DA-技术分享
← 返回文章列表

Claude Desktop App 连接本地 LLM 完整配置指南

2026-05-06 7 阅读 AI

Claude Desktop App 连接本地 LLM 完整配置指南

环境:macOS (Apple Silicon)、Claude Desktop App、LM Studio、Claude Code Router (ccr)

最后更新:2026-04-26

1. 架构概览

Claude Desktop App
    │
    ▼ HTTP (127.0.0.1:3457)
ccr-compat-proxy (补充 /v1/models 端点 + 剥离 attribution header)
    │
    ▼ HTTP (127.0.0.1:3456)
Claude Code Router (ccr) — 协议转换 + 路由
    │
    ▼ HTTP (192.168.31.127:1234/v1/chat/completions)
LM Studio (本地 LLM 推理服务)

为什么需要三层?

组件作用为什么不能省
ccr将 Anthropic Messages API 格式转换为 OpenAI Chat Completions 格式Claude Desktop 只发送 /v1/messages 格式,本地模型只接受 /v1/chat/completions
ccr-compat-proxy补充 /v1/models 端点 + 剥离 x-anthropic-billing-headerClaude Desktop 启动时会请求 /v1/models,ccr 没有这个端点会返回 404
LM Studio运行本地模型,提供 OpenAI 兼容 API实际的 LLM 推理引擎

2. 前置条件

  • LM Studio 已安装并在远程/本机运行,开启了 Server 模式
  • Node.js (v18+) 已安装
  • Claude Desktop App 已安装

3. 安装 Claude Code Router (ccr)

3.1 独立安装(推荐)

将 ccr 安装到用户目录,避免被其他应用升级覆盖:

# 安装到 ~/.npm-global/
npm install -g @musistudio/claude-code-router --prefix ~/.npm-global

# 添加到 PATH(加到 ~/.zshrc)
echo 'export PATH="$HOME/.npm-global/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc

# 验证安装
ccr --version

3.2 创建启动脚本

mkdir -p ~/.local/bin
cat > ~/.local/bin/ccr << 'EOF'
#!/bin/bash
exec /opt/homebrew/bin/node ~/.npm-global/lib/node_modules/@musistudio/claude-code-router/dist/cli.js "$@"
EOF
chmod +x ~/.local/bin/ccr

4. 配置 ccr

4.1 创建配置文件

mkdir -p ~/.claude-code-router

编辑 ~/.claude-code-router/config.json

{
  "LOG": true,
  "LOG_LEVEL": "info",
  "API_TIMEOUT_MS": 600000,
  "Providers": [
    {
      "name": "lmstudio",
      "api_base_url": "http://192.168.31.127:1234/v1/chat/completions",
      "api_key": "your-lmstudio-api-key",
      "models": ["qwen/qwen3.6-35b-a3b"]
    }
  ],
  "Router": {
    "default": "lmstudio,qwen/qwen3.6-35b-a3b"
  }
}

字段说明:

字段说明
Providers[].api_base_urlLM Studio 的 OpenAI 兼容 API 地址
Providers[].api_keyLM Studio 的 Bearer Token(可在 LM Studio Server 设置中配置)
Providers[].modelsLM Studio 中已加载的模型名称(必须与 LM Studio 显示的完全一致)
Router.default格式为 provider名,模型名,指定默认使用的模型
API_TIMEOUT_MS上游请求超时时间(毫秒),建议 600000(10 分钟)

切换模型:修改 Providers[].modelsRouter.default 中的模型名,然后重启 ccr。

5. 创建 ccr-compat-proxy

Claude Desktop App 启动时会请求 GET /v1/models,ccr 没有这个端点。同时 Claude Code 客户端会在请求 body 中注入 x-anthropic-billing-header,需要剥离。

5.1 安装依赖

cd ~/.claude-code-router
npm init -y
npm install http-proxy

5.2 创建代理脚本

创建 ~/.claude-code-router/ccr-compat-proxy.js

#!/usr/bin/env node
const http = require('http');

const CONFIG_PATH = process.env.HOME + '/.claude-code-router/config.json';
const PROXY_PORT = 3457;
const CCR_PORT = 3456;

function loadModels() {
  try {
    const config = JSON.parse(require('fs').readFileSync(CONFIG_PATH, 'utf-8'));
    const models = [];
    for (const provider of (config.Providers || [])) {
      for (const model of (provider.models || [])) {
        models.push({ id: model, object: 'model', created: Date.now(), owned_by: provider.name });
      }
    }
    return models;
  } catch (e) {
    return [];
  }
}

function stripAttributionHeader(bodyStr) {
  try {
    const body = JSON.parse(bodyStr);
    let modified = false;

    if (Array.isArray(body.system)) {
      body.system = body.system.filter(block => {
        if (block.type === 'text' && block.text && block.text.startsWith('x-anthropic-billing-header:')) {
          modified = true;
          return false;
        }
        return true;
      });
    }

    if (Array.isArray(body.messages)) {
      for (const msg of body.messages) {
        if (Array.isArray(msg.content)) {
          const before = msg.content.length;
          msg.content = msg.content.filter(block => {
            if (block.type === 'text' && block.text && block.text.startsWith('x-anthropic-billing-header:')) {
              return false;
            }
            return true;
          });
          if (msg.content.length !== before) modified = true;
        }
      }
    }

    return modified ? JSON.stringify(body) : bodyStr;
  } catch (e) {
    return bodyStr;
  }
}

const server = http.createServer((req, res) => {
  if (req.method === 'GET' && req.url.match(/^\/v1\/models/)) {
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ object: 'list', data: loadModels() }));
    return;
  }

  let bodyChunks = [];
  req.on('data', chunk => bodyChunks.push(chunk));
  req.on('end', () => {
    const cleaned = stripAttributionHeader(Buffer.concat(bodyChunks).toString('utf-8'));
    const buf = Buffer.from(cleaned);

    const headers = { ...req.headers, 'content-length': buf.length };
    delete headers['transfer-encoding'];

    const ccrReq = http.request({
      hostname: '127.0.0.1', port: CCR_PORT, path: req.url, method: req.method, headers
    }, (ccrRes) => {
      res.writeHead(ccrRes.statusCode, ccrRes.headers);
      ccrRes.pipe(res);
    });

    ccrReq.on('error', (err) => {
      if (!res.headersSent) res.writeHead(502, { 'Content-Type': 'application/json' });
      res.end(JSON.stringify({ error: { message: 'CCR upstream error: ' + err.message } }));
    });

    res.on('close', () => ccrReq.destroy());
    ccrReq.write(buf);
    ccrReq.end();
  });
});

server.listen(PROXY_PORT, '127.0.0.1', () => {
  console.log(`CCR compat proxy listening on http://127.0.0.1:${PROXY_PORT}`);
  console.log(`Stripping x-anthropic-billing-header from POST bodies`);
});

6. 配置开机自启动 (launchd)

6.1 ccr 服务

创建 ~/Library/LaunchAgents/com.user.ccr.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.user.ccr</string>
    <key>ProgramArguments</key>
    <array>
        <string>/opt/homebrew/bin/node</string>
        <string>/Users/mac/.npm-global/lib/node_modules/@musistudio/claude-code-router/dist/cli.js</string>
    </array>
    <key>WorkingDirectory</key>
    <string>/Users/mac/.claude-code-router</string>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
    <key>StandardOutPath</key>
    <string>/Users/mac/.claude-code-router/logs/ccr-stdout.log</string>
    <key>StandardErrorPath</key>
    <string>/Users/mac/.claude-code-router/logs/ccr-stderr.log</string>
    <key>EnvironmentVariables</key>
    <dict>
        <key>PATH</key>
        <string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
        <key>HOME</key>
        <string>/Users/mac</string>
    </dict>
</dict>
</plist>

6.2 ccr-compat-proxy 服务

创建 ~/Library/LaunchAgents/com.user.ccr-proxy.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.user.ccr-proxy</string>
    <key>ProgramArguments</key>
    <array>
        <string>/opt/homebrew/bin/node</string>
        <string>/Users/mac/.claude-code-router/ccr-compat-proxy.js</string>
    </array>
    <key>WorkingDirectory</key>
    <string>/Users/mac/.claude-code-router</string>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
    <key>StandardOutPath</key>
    <string>/Users/mac/.claude-code-router/logs/proxy-stdout.log</string>
    <key>StandardErrorPath</key>
    <string>/Users/mac/.claude-code-router/logs/proxy-stderr.log</string>
    <key>EnvironmentVariables</key>
    <dict>
        <key>PATH</key>
        <string>/opt/homebrew/bin:/Users/mac/.npm-global/bin:/usr/local/bin:/usr/bin:/bin</string>
        <key>HOME</key>
        <string>/Users/mac</string>
    </dict>
</dict>
</plist>

6.3 启动服务

# 创建日志目录
mkdir -p ~/.claude-code-router/logs

# 加载服务
launchctl load ~/Library/LaunchAgents/com.user.ccr.plist
launchctl load ~/Library/LaunchAgents/com.user.ccr-proxy.plist

# 验证
lsof -i :3456  # ccr 应该在监听
lsof -i :3457  # proxy 应该在监听

7. 配置 Claude Desktop App

编辑 ~/Library/Application Support/Claude/claude_desktop_config.json

{
  "preferences": {
    "coworkWebSearchEnabled": true
  }
}

在 Claude Desktop App 中:

  1. 打开设置 (Settings)
  2. 找到 API ProviderCustom API 部分
  3. 设置 API Base URLhttp://127.0.0.1:3457
  4. 输入 LM Studio 的 API Key(如果有的话)

注意:Claude Desktop App 要求 API 地址必须使用 httpshttp://127.0.0.1,不能使用其他 IP 地址。这就是为什么需要 ccr-compat-proxy 在 127.0.0.1:3457 监听 — LM Studio 如果运行在远程 IP(如 192.168.31.127),不能直接被 Claude Desktop App 引用。

8. 环境变量配置

~/.claude/settings.json 中配置(对 Claude Desktop App 和 CLI 都生效):

{
  "permissions": {
    "dangerouslySkipPermissions": true
  },
  "env": {
    "CLAUDE_CODE_ATTRIBUTION_HEADER": "0",
    "CLAUDE_CODE_ENABLE_TELEMETRY": "0",
    "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1"
  }
}
环境变量作用
CLAUDE_CODE_ATTRIBUTION_HEADER设为 0 尝试关闭 HTTP 层的 attribution header(body 中的需要 proxy 剥离)
CLAUDE_CODE_ENABLE_TELEMETRY设为 0 禁用遥测数据上报
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC设为 1 禁用非必要的网络请求

9. Patch:延长上游请求超时

ccr 使用 Node.js 内置的 undici 作为 HTTP 客户端,bodyTimeout 硬编码为 3e5(5 分钟)。如果本地模型推理较慢,需要 patch 延长超时。

9.1 手动 patch

编辑 ~/.npm-global/lib/node_modules/@musistudio/claude-code-router/dist/cli.js,搜索并替换:

# 将 bodyTimeout 从 5 分钟改为 10 分钟
qe??3e5  →  qe??6e5

9.2 自动 patch 脚本

已提供脚本 ~/.claude-code-router/patch-body-timeout.sh

# 默认 patch 到 600s (10 分钟)
bash ~/.claude-code-router/patch-body-timeout.sh

# 自定义超时
bash ~/.claude-code-router/patch-body-timeout.sh 1200

脚本会自动备份原文件、执行替换、重启 ccr。

注意:ccr 版本更新(npm update)后此 patch 会丢失,需要重新运行脚本。

10. 目录结构总览

~/.claude-code-router/
├── config.json                  # ccr 配置(模型、API 地址等)
├── ccr-compat-proxy.js          # Claude Desktop 兼容代理
├── patch-body-timeout.sh        # bodyTimeout 自动 patch 脚本
├── node_modules/
│   └── http-proxy/              # proxy 依赖
└── logs/
    ├── ccr-stdout.log           # ccr 标准输出
    ├── ccr-stderr.log           # ccr 错误输出
    ├── ccr-*.log                # ccr 运行日志(含请求详情)
    ├── proxy-stdout.log         # proxy 标准输出
    └── proxy-stderr.log         # proxy 错误输出

~/.npm-global/lib/node_modules/@musistudio/claude-code-router/
└── dist/cli.js                  # ccr 主程序(已 patch bodyTimeout)

~/.local/bin/
└── ccr                          # ccr 启动脚本

~/Library/LaunchAgents/
├── com.user.ccr.plist           # ccr 开机自启动配置
└── com.user.ccr-proxy.plist     # proxy 开机自启动配置

~/.claude/
├── settings.json                # Claude Code 通用设置(含环境变量)
└── claude.json                  # Claude Code 本地缓存(feature flags 等)

11. 常见问题排查

请求发不出去

# 检查服务是否运行
lsof -i :3456  # ccr
lsof -i :3457  # proxy

# 查看日志
tail -50 ~/.claude-code-router/logs/ccr-*.log
tail -50 ~/.claude-code-router/logs/proxy-stderr.log

ccr 日志显示 ERR_STREAM_PREMATURE_CLOSE

proxy 的 stream 处理有问题,确保 ccr-compat-proxy.js 中删除了 transfer-encoding header 并使用 content-length

切换模型后请求的模型名没变

ccr 缓存了旧配置,需要重启:

launchctl unload ~/Library/LaunchAgents/com.user.ccr.plist
launchctl load ~/Library/LaunchAgents/com.user.ccr.plist

5 分钟超时

undici 的 bodyTimeout 硬编码为 5 分钟,需要 patch cli.js(参见第 9 节)。

x-anthropic-billing-header 仍然出现在请求中

  1. 确认 ccr-compat-proxy 正在运行(lsof -i :3457
  2. 确认 Claude Desktop 连接的是 127.0.0.1:3457(proxy)而不是 3456(ccr)
  3. 只有经过 proxy 的请求才会剥离 attribution header

LM Studio 报错 connection refused

确认 LM Studio 的 Server 模式已开启,且 API 地址和端口配置正确:

curl http://192.168.31.127:1234/v1/models
AI
← 告别重复输入密码:SSK — 一行命令搞定 SSH 免密登录 将 无官方订阅的Claude Code 接入飞书:cc-connect 配置踩坑全记录 →