fix: discover MCP servers from CLI when not in settings.json - #43

Closed
gy212 wants to merge 5 commits into
op7418:mainfrom
gy212:fix/mcp-cli-discovery
Closed

fix: discover MCP servers from CLI when not in settings.json#43
gy212 wants to merge 5 commits into
op7418:mainfrom
gy212:fix/mcp-cli-discovery

Conversation

@gy212

@gy212gy212 commented Feb 9, 2026

Copy link
Copy Markdown
Contributor

问题

通过 claude mcp add -s user 配置的 MCP 服务器在 CodePilot 中无法被发现和使用。扩展页面显示"未配置 MCP 服务器",聊天中也无法使用 MCP 工具。

根本原因:Claude Code CLI 新版本将 MCP 配置存储在内部位置,而非 ~/.claude/settings.jsonmcpServers 字段。CodePilot 只从 settings.json 读取,因此找不到任何 CLI 配置的服务器。

环境:Windows 11

Closes#42

修改内容

1. src/lib/mcp-config.ts — 新增 CLI 发现能力

  • 新增 discoverCliMcpServers():调用 claude mcp list 解析输出,提取服务器名称和命令
  • 使用 findClaudeBinary()findGitBash() 确保跨平台兼容
  • 结果缓存 60 秒,避免频繁调用 CLI
  • 失败时静默返回空对象
  • 修改 getMergedMcpServers():合并三个来源(CLI < settings.json < .mcp.json)

2. src/app/api/plugins/mcp/route.ts — 扩展页面展示 CLI 服务器

  • GET 处理器合并 CLI 发现的服务器与 settings.json 中的服务器
  • 标记 source: 'cli' | 'settings' 方便前端区分

3. src/app/api/chat/route.ts — 聊天时传递 MCP 配置

  • 导入 getMergedMcpServers,将合并后的 MCP 配置传递给 streamClaude()

4. src/app/api/settings/route.ts — 防止设置保存覆盖 MCP 配置

  • PUT 处理器改为合并写入,保留前端未管理的字段(如 mcpServers

验证方式

  1. TypeScript 编译通过
  2. 启动应用,扩展页面能看到 CLI 配置的 MCP 服务器
  3. 聊天中 MCP 工具出现在 init 系统消息的 tools 列表中
  4. 保存设置后 MCP 配置未丢失

Claude Code CLI stores MCP configs in an internal location that
CodePilot cannot read from ~/.claude/settings.json. This adds CLI
discovery via `claude mcp list` with 60s caching, merges CLI/user/
project MCP sources for both the extensions page and chat sessions,
and changes settings PUT to merge instead of overwrite to prevent
losing CLI-configured MCP servers.
Closes#42
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gy212

gy212 commented Feb 9, 2026

Copy link
Copy Markdown
ContributorAuthor

不是哥们,提交完发现又更新了

1 similar comment
@gy212

gy212 commented Feb 9, 2026

Copy link
Copy Markdown
ContributorAuthor

不是哥们,提交完发现又更新了

@gy212

gy212 commented Feb 9, 2026

Copy link
Copy Markdown
ContributorAuthor

基于0.6.4,可能与0.7存在冲突。

@op7418op7418 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR! The approach of discovering MCP servers from claude mcp list is sound and addresses a real gap. A few issues to address before merging:

Medium:

  1. execFileSync blocks the event loopdiscoverCliMcpServers() uses execFileSync with a 10s timeout, called from the chat API route. The first call (or any after cache expiry) will block ALL concurrent requests. Please switch to async execFile (with util.promisify) and make the call chain async.

  2. Brittle CLI output parsing — The regex ^(\S+):\s+(.+?)\s+-\s+[✓✗] parses human-readable output from claude mcp list. This will break if the CLI changes its format, server names contain spaces/colons, or status indicators change. Consider checking if claude mcp list --json is available, or at minimum document this as a known limitation.

Low:

  1. Shallow merge in PUT handler (settings/route.ts:52-54) — { ...existing, ...settings } only works if the frontend omits fields it doesn't manage. If it sends mcpServers: {}, it will overwrite existing config. Consider a more targeted merge.

  2. source field type mismatch — The source property added to MCP configs in the GET handler isn't reflected in the MCPConfigResponse type.

  3. No cache invalidation — The 60s TTL is reasonable, but there's no way to force-refresh after adding a new MCP server via CLI. Consider adding a manual refresh mechanism.

Code quality is otherwise good — defensive error handling, proper use of execFileSync (not exec) for security, and sensible merge priority ordering.

gy212and others added 2 commits February 10, 2026 18:23
- Convert execFileSync to async execFile to avoid blocking the event loop
- Add forceRefresh param and invalidateCliMcpCache() for manual cache refresh
- Support ?refresh=true query param on MCP GET endpoint
- Wrap per-line CLI output parsing in try-catch for robustness
- Add comments noting claude mcp list --json is not yet available
- Fix settings PUT to skip empty objects, preventing accidental mcpServers wipe
- Add source field to MCPServerConfig type definition
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gy212

Copy link
Copy Markdown
ContributorAuthor

已按本轮 review 意见完成修复,补充说明如下:

✅ 1) CLI MCP 服务器改为“只显示,不落盘”

  • /api/plugins/mcp GET 仍会合并返回:CLI + settings(settings 同名优先)。
  • /api/plugins/mcp PUT 会过滤 source: 'cli',只持久化 settings 服务器,避免把 CLI 发现结果写回 ~/.claude/settings.json
  • 前端 MCP 列表把 CLI 项标记为 CLI (read-only),并禁用编辑/删除按钮。
  • MCP 页面新增 Refresh 按钮(?refresh=true)以手动刷新 CLI 发现缓存。

✅ 2) /api/settings PUT 恢复覆盖语义

  • 改回“提交什么就保存什么”,不再 merge + 跳过空对象。
  • 这样 JSON 模式可正常删除字段/清空对象,不会出现“删不掉”的行为回归。

✅ 3) 修复 CLI 输出解析脆弱性

  • 新增 parseCliMcpListLine + 引号感知拆分逻辑,正确处理带空格路径/参数与引号内容。
  • 解析状态分隔改为使用最后一个 " - ",避免命令参数里包含 - 时被截断。

✅ 附加修正

  • MCP POST 入参校验更严格:
    • stdio 必须有 command
    • sse/http 必须有 url

🧪 验证

  • lint:通过(改动文件)
  • 单测:新增 mcp-cli-parser.test.ts 并通过
    • 覆盖普通行、带引号/空格参数、命令含 -、非法行等场景

本次提交:02e35d1

@gy212
gy212 requested a review from op7418February 10, 2026 11:16
gy212and others added 2 commits February 10, 2026 21:52
`claude mcp list` fails on Windows due to Git Bash detection issues,
so CLI discovery returns empty. Add direct file read from ~/.claude.json
(where `claude mcp add` stores configs) as a reliable fallback source.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gy212

Copy link
Copy Markdown
ContributorAuthor

已完成本次适配与复核,结论如下:

本地验证:

px tsx --test src/tests/unit/mcp-cli-parser.test.ts ✅

px tsx --test src/tests/unit/mcp-config.test.ts ✅

px eslint(针对本 PR 涉及文件)✅

Review 结论:当前版本无 blocker/high 问题,可继续走合并流程。
建议后续补一组集成测试覆盖 MCP 多来源合并优先级(CLI / ~/.claude.json / settings / .mcp.json)以降低未来回归风险。

@op7418op7418 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well-designed MCP CLI discovery feature. Multi-source merging logic is clean, 60s cache is sensible, read-only UI for CLI servers is good UX. CLI output parsing is inherently fragile but mitigated by the ~/.claude.json fallback.

Please rebase onto current main before merge.

@op7418

Copy link
Copy Markdown
Owner

你好 @gy212,感谢你提交这个 MCP 发现的修复!

MCP 服务器的读取问题已经在主分支通过以下 commit 修复了:

  • a5cff79 fix: read MCP servers from both ~/.claude.json and ~/.claude/settings.json

该 commit 支持从多个配置文件位置读取 MCP 服务器配置,解决了 CLI 配置的 MCP 服务器无法被发现的问题。

因此先关闭这个 PR。再次感谢你的贡献!

@op7418op7418 closed this Feb 23, 2026
@gy212
gy212 deleted the fix/mcp-cli-discovery branch March 5, 2026 01:32
op7418 added a commit that referenced this pull request Jun 28, 2026
…ed (#632)
Signal — Codex 复审 #632 P1+item1 后给三点:
- [P2] resolveEffectiveAnthropicBaseUrl 在 provider 存在但 hasCredentials=false 时仍优先返回
provider.base_url,与 toClaudeCodeEnv 不一致(该状态两分支都不跑、SDK 只继承 ambient
ANTHROPIC_BASE_URL)。若用户选了无 key 的 DB provider + 环境有第三方 ANTHROPIC_BASE_URL,
gate 可能按错地址误信窗口(同 GLM 同类漏口)。
- [P3] 存量第三方会话首屏可能在 provider models 加载前(undefined→trusted)短暂闪历史 200K。
- [P3] tech-debt 新条目用了 #36,与既有 #36 撞车。
Triage —
- P2:helper 只镜像了 toClaudeCodeEnv 两态(有凭据 provider / 无 provider),漏第三态(有
provider 无凭据):该态 provider 分支 gated on hasCredentials、env 分支 gated on !provider,
两者都不跑 → env 保持 ambient process.env.ANTHROPIC_BASE_URL,provider.base_url 不注入、
settings 不读。
- P3 闪回:undefined 同时表示"加载中"与"非 anthropic 组未标注",前者应 fail-closed。
- P3 编号:tracker 非严格连续,实际最大 42 → 下一个可用 43。
Fix —
- provider-resolver:helper 改三态镜像。`provider && !hasCredentials` → 返回
process.env.ANTHROPIC_BASE_URL(忠实镜像 SDK 继承的 ambient env,不读 provider.base_url /
settings)。doc 写明三态对应关系。
- ChatView:fail-closed —— providerFetchState !== 'loaded' 传 false;loaded 时用 group flag
(found 必有标注;not-found stale provider → ?? true 向后兼容)。第一方代价 = 首屏短暂只显
已用、百分比后补的渐进式诚实显示,绝不闪错数。
- tech-debt #36#43(plan doc / memory 引用同步)。
Verify — npm run test 3387/3387(typecheck clean)。新增 P2 回归 2 例:无凭据 provider 背后
第三方 env → untrust;无凭据 provider + clean env → 不误 untrust。
Guardrail — provider-resolver.test.ts 加 P2 三态行为测试;context-window-trusted.test.ts 的
ChatView pin 更新为锁定 fail-closed 语义(providerFetchState === 'loaded' ? (… ?? true) : false)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
youcho2 pushed a commit to youcho2/CodePilot that referenced this pull request Aug 11, 2026
…ed (op7418#632)
Signal — Codex 复审 op7418#632 P1+item1 后给三点:
- [P2] resolveEffectiveAnthropicBaseUrl 在 provider 存在但 hasCredentials=false 时仍优先返回
provider.base_url,与 toClaudeCodeEnv 不一致(该状态两分支都不跑、SDK 只继承 ambient
ANTHROPIC_BASE_URL)。若用户选了无 key 的 DB provider + 环境有第三方 ANTHROPIC_BASE_URL,
gate 可能按错地址误信窗口(同 GLM 同类漏口)。
- [P3] 存量第三方会话首屏可能在 provider models 加载前(undefined→trusted)短暂闪历史 200K。
- [P3] tech-debt 新条目用了 op7418#36,与既有 op7418#36 撞车。
Triage —
- P2:helper 只镜像了 toClaudeCodeEnv 两态(有凭据 provider / 无 provider),漏第三态(有
provider 无凭据):该态 provider 分支 gated on hasCredentials、env 分支 gated on !provider,
两者都不跑 → env 保持 ambient process.env.ANTHROPIC_BASE_URL,provider.base_url 不注入、
settings 不读。
- P3 闪回:undefined 同时表示"加载中"与"非 anthropic 组未标注",前者应 fail-closed。
- P3 编号:tracker 非严格连续,实际最大 42 → 下一个可用 43。
Fix —
- provider-resolver:helper 改三态镜像。`provider && !hasCredentials` → 返回
process.env.ANTHROPIC_BASE_URL(忠实镜像 SDK 继承的 ambient env,不读 provider.base_url /
settings)。doc 写明三态对应关系。
- ChatView:fail-closed —— providerFetchState !== 'loaded' 传 false;loaded 时用 group flag
(found 必有标注;not-found stale provider → ?? true 向后兼容)。第一方代价 = 首屏短暂只显
已用、百分比后补的渐进式诚实显示,绝不闪错数。
- tech-debt op7418#36op7418#43(plan doc / memory 引用同步)。
Verify — npm run test 3387/3387(typecheck clean)。新增 P2 回归 2 例:无凭据 provider 背后
第三方 env → untrust;无凭据 provider + clean env → 不误 untrust。
Guardrail — provider-resolver.test.ts 加 P2 三态行为测试;context-window-trusted.test.ts 的
ChatView pin 更新为锁定 fail-closed 语义(providerFetchState === 'loaded' ? (… ?? true) : false)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MCP servers configured via CLI not discovered by CodePilot

2 participants

@gy212@op7418
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix: discover MCP servers from CLI when not in settings.json - #43

Closed
gy212 wants to merge 5 commits into
op7418:mainfrom
gy212:fix/mcp-cli-discovery
Closed

fix: discover MCP servers from CLI when not in settings.json#43
gy212 wants to merge 5 commits into
op7418:mainfrom
gy212:fix/mcp-cli-discovery

Conversation

@gy212

@gy212gy212 commented Feb 9, 2026

Copy link
Copy Markdown
Contributor

问题

通过 claude mcp add -s user 配置的 MCP 服务器在 CodePilot 中无法被发现和使用。扩展页面显示"未配置 MCP 服务器",聊天中也无法使用 MCP 工具。

根本原因:Claude Code CLI 新版本将 MCP 配置存储在内部位置,而非 ~/.claude/settings.jsonmcpServers 字段。CodePilot 只从 settings.json 读取,因此找不到任何 CLI 配置的服务器。

环境:Windows 11

Closes#42

修改内容

1. src/lib/mcp-config.ts — 新增 CLI 发现能力

  • 新增 discoverCliMcpServers():调用 claude mcp list 解析输出,提取服务器名称和命令
  • 使用 findClaudeBinary()findGitBash() 确保跨平台兼容
  • 结果缓存 60 秒,避免频繁调用 CLI
  • 失败时静默返回空对象
  • 修改 getMergedMcpServers():合并三个来源(CLI < settings.json < .mcp.json)

2. src/app/api/plugins/mcp/route.ts — 扩展页面展示 CLI 服务器

  • GET 处理器合并 CLI 发现的服务器与 settings.json 中的服务器
  • 标记 source: 'cli' | 'settings' 方便前端区分

3. src/app/api/chat/route.ts — 聊天时传递 MCP 配置

  • 导入 getMergedMcpServers,将合并后的 MCP 配置传递给 streamClaude()

4. src/app/api/settings/route.ts — 防止设置保存覆盖 MCP 配置

  • PUT 处理器改为合并写入,保留前端未管理的字段(如 mcpServers

验证方式

  1. TypeScript 编译通过
  2. 启动应用,扩展页面能看到 CLI 配置的 MCP 服务器
  3. 聊天中 MCP 工具出现在 init 系统消息的 tools 列表中
  4. 保存设置后 MCP 配置未丢失

Claude Code CLI stores MCP configs in an internal location that
CodePilot cannot read from ~/.claude/settings.json. This adds CLI
discovery via `claude mcp list` with 60s caching, merges CLI/user/
project MCP sources for both the extensions page and chat sessions,
and changes settings PUT to merge instead of overwrite to prevent
losing CLI-configured MCP servers.
Closes#42
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gy212

gy212 commented Feb 9, 2026

Copy link
Copy Markdown
ContributorAuthor

不是哥们,提交完发现又更新了

1 similar comment
@gy212

gy212 commented Feb 9, 2026

Copy link
Copy Markdown
ContributorAuthor

不是哥们,提交完发现又更新了

@gy212

gy212 commented Feb 9, 2026

Copy link
Copy Markdown
ContributorAuthor

基于0.6.4,可能与0.7存在冲突。

@op7418op7418 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR! The approach of discovering MCP servers from claude mcp list is sound and addresses a real gap. A few issues to address before merging:

Medium:

  1. execFileSync blocks the event loopdiscoverCliMcpServers() uses execFileSync with a 10s timeout, called from the chat API route. The first call (or any after cache expiry) will block ALL concurrent requests. Please switch to async execFile (with util.promisify) and make the call chain async.

  2. Brittle CLI output parsing — The regex ^(\S+):\s+(.+?)\s+-\s+[✓✗] parses human-readable output from claude mcp list. This will break if the CLI changes its format, server names contain spaces/colons, or status indicators change. Consider checking if claude mcp list --json is available, or at minimum document this as a known limitation.

Low:

  1. Shallow merge in PUT handler (settings/route.ts:52-54) — { ...existing, ...settings } only works if the frontend omits fields it doesn't manage. If it sends mcpServers: {}, it will overwrite existing config. Consider a more targeted merge.

  2. source field type mismatch — The source property added to MCP configs in the GET handler isn't reflected in the MCPConfigResponse type.

  3. No cache invalidation — The 60s TTL is reasonable, but there's no way to force-refresh after adding a new MCP server via CLI. Consider adding a manual refresh mechanism.

Code quality is otherwise good — defensive error handling, proper use of execFileSync (not exec) for security, and sensible merge priority ordering.

gy212and others added 2 commits February 10, 2026 18:23
- Convert execFileSync to async execFile to avoid blocking the event loop
- Add forceRefresh param and invalidateCliMcpCache() for manual cache refresh
- Support ?refresh=true query param on MCP GET endpoint
- Wrap per-line CLI output parsing in try-catch for robustness
- Add comments noting claude mcp list --json is not yet available
- Fix settings PUT to skip empty objects, preventing accidental mcpServers wipe
- Add source field to MCPServerConfig type definition
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gy212

Copy link
Copy Markdown
ContributorAuthor

已按本轮 review 意见完成修复,补充说明如下:

✅ 1) CLI MCP 服务器改为“只显示,不落盘”

  • /api/plugins/mcp GET 仍会合并返回:CLI + settings(settings 同名优先)。
  • /api/plugins/mcp PUT 会过滤 source: 'cli',只持久化 settings 服务器,避免把 CLI 发现结果写回 ~/.claude/settings.json
  • 前端 MCP 列表把 CLI 项标记为 CLI (read-only),并禁用编辑/删除按钮。
  • MCP 页面新增 Refresh 按钮(?refresh=true)以手动刷新 CLI 发现缓存。

✅ 2) /api/settings PUT 恢复覆盖语义

  • 改回“提交什么就保存什么”,不再 merge + 跳过空对象。
  • 这样 JSON 模式可正常删除字段/清空对象,不会出现“删不掉”的行为回归。

✅ 3) 修复 CLI 输出解析脆弱性

  • 新增 parseCliMcpListLine + 引号感知拆分逻辑,正确处理带空格路径/参数与引号内容。
  • 解析状态分隔改为使用最后一个 " - ",避免命令参数里包含 - 时被截断。

✅ 附加修正

  • MCP POST 入参校验更严格:
    • stdio 必须有 command
    • sse/http 必须有 url

🧪 验证

  • lint:通过(改动文件)
  • 单测:新增 mcp-cli-parser.test.ts 并通过
    • 覆盖普通行、带引号/空格参数、命令含 -、非法行等场景

本次提交:02e35d1

@gy212
gy212 requested a review from op7418February 10, 2026 11:16
gy212and others added 2 commits February 10, 2026 21:52
`claude mcp list` fails on Windows due to Git Bash detection issues,
so CLI discovery returns empty. Add direct file read from ~/.claude.json
(where `claude mcp add` stores configs) as a reliable fallback source.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gy212

Copy link
Copy Markdown
ContributorAuthor

已完成本次适配与复核,结论如下:

本地验证:

px tsx --test src/tests/unit/mcp-cli-parser.test.ts ✅

px tsx --test src/tests/unit/mcp-config.test.ts ✅

px eslint(针对本 PR 涉及文件)✅

Review 结论:当前版本无 blocker/high 问题,可继续走合并流程。
建议后续补一组集成测试覆盖 MCP 多来源合并优先级(CLI / ~/.claude.json / settings / .mcp.json)以降低未来回归风险。

@op7418op7418 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well-designed MCP CLI discovery feature. Multi-source merging logic is clean, 60s cache is sensible, read-only UI for CLI servers is good UX. CLI output parsing is inherently fragile but mitigated by the ~/.claude.json fallback.

Please rebase onto current main before merge.

@op7418

Copy link
Copy Markdown
Owner

你好 @gy212,感谢你提交这个 MCP 发现的修复!

MCP 服务器的读取问题已经在主分支通过以下 commit 修复了:

  • a5cff79 fix: read MCP servers from both ~/.claude.json and ~/.claude/settings.json

该 commit 支持从多个配置文件位置读取 MCP 服务器配置,解决了 CLI 配置的 MCP 服务器无法被发现的问题。

因此先关闭这个 PR。再次感谢你的贡献!

@op7418op7418 closed this Feb 23, 2026
@gy212
gy212 deleted the fix/mcp-cli-discovery branch March 5, 2026 01:32
op7418 added a commit that referenced this pull request Jun 28, 2026
…ed (#632)
Signal — Codex 复审 #632 P1+item1 后给三点:
- [P2] resolveEffectiveAnthropicBaseUrl 在 provider 存在但 hasCredentials=false 时仍优先返回
provider.base_url,与 toClaudeCodeEnv 不一致(该状态两分支都不跑、SDK 只继承 ambient
ANTHROPIC_BASE_URL)。若用户选了无 key 的 DB provider + 环境有第三方 ANTHROPIC_BASE_URL,
gate 可能按错地址误信窗口(同 GLM 同类漏口)。
- [P3] 存量第三方会话首屏可能在 provider models 加载前(undefined→trusted)短暂闪历史 200K。
- [P3] tech-debt 新条目用了 #36,与既有 #36 撞车。
Triage —
- P2:helper 只镜像了 toClaudeCodeEnv 两态(有凭据 provider / 无 provider),漏第三态(有
provider 无凭据):该态 provider 分支 gated on hasCredentials、env 分支 gated on !provider,
两者都不跑 → env 保持 ambient process.env.ANTHROPIC_BASE_URL,provider.base_url 不注入、
settings 不读。
- P3 闪回:undefined 同时表示"加载中"与"非 anthropic 组未标注",前者应 fail-closed。
- P3 编号:tracker 非严格连续,实际最大 42 → 下一个可用 43。
Fix —
- provider-resolver:helper 改三态镜像。`provider && !hasCredentials` → 返回
process.env.ANTHROPIC_BASE_URL(忠实镜像 SDK 继承的 ambient env,不读 provider.base_url /
settings)。doc 写明三态对应关系。
- ChatView:fail-closed —— providerFetchState !== 'loaded' 传 false;loaded 时用 group flag
(found 必有标注;not-found stale provider → ?? true 向后兼容)。第一方代价 = 首屏短暂只显
已用、百分比后补的渐进式诚实显示,绝不闪错数。
- tech-debt #36#43(plan doc / memory 引用同步)。
Verify — npm run test 3387/3387(typecheck clean)。新增 P2 回归 2 例:无凭据 provider 背后
第三方 env → untrust;无凭据 provider + clean env → 不误 untrust。
Guardrail — provider-resolver.test.ts 加 P2 三态行为测试;context-window-trusted.test.ts 的
ChatView pin 更新为锁定 fail-closed 语义(providerFetchState === 'loaded' ? (… ?? true) : false)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
youcho2 pushed a commit to youcho2/CodePilot that referenced this pull request Aug 11, 2026
…ed (op7418#632)
Signal — Codex 复审 op7418#632 P1+item1 后给三点:
- [P2] resolveEffectiveAnthropicBaseUrl 在 provider 存在但 hasCredentials=false 时仍优先返回
provider.base_url,与 toClaudeCodeEnv 不一致(该状态两分支都不跑、SDK 只继承 ambient
ANTHROPIC_BASE_URL)。若用户选了无 key 的 DB provider + 环境有第三方 ANTHROPIC_BASE_URL,
gate 可能按错地址误信窗口(同 GLM 同类漏口)。
- [P3] 存量第三方会话首屏可能在 provider models 加载前(undefined→trusted)短暂闪历史 200K。
- [P3] tech-debt 新条目用了 op7418#36,与既有 op7418#36 撞车。
Triage —
- P2:helper 只镜像了 toClaudeCodeEnv 两态(有凭据 provider / 无 provider),漏第三态(有
provider 无凭据):该态 provider 分支 gated on hasCredentials、env 分支 gated on !provider,
两者都不跑 → env 保持 ambient process.env.ANTHROPIC_BASE_URL,provider.base_url 不注入、
settings 不读。
- P3 闪回:undefined 同时表示"加载中"与"非 anthropic 组未标注",前者应 fail-closed。
- P3 编号:tracker 非严格连续,实际最大 42 → 下一个可用 43。
Fix —
- provider-resolver:helper 改三态镜像。`provider && !hasCredentials` → 返回
process.env.ANTHROPIC_BASE_URL(忠实镜像 SDK 继承的 ambient env,不读 provider.base_url /
settings)。doc 写明三态对应关系。
- ChatView:fail-closed —— providerFetchState !== 'loaded' 传 false;loaded 时用 group flag
(found 必有标注;not-found stale provider → ?? true 向后兼容)。第一方代价 = 首屏短暂只显
已用、百分比后补的渐进式诚实显示,绝不闪错数。
- tech-debt op7418#36op7418#43(plan doc / memory 引用同步)。
Verify — npm run test 3387/3387(typecheck clean)。新增 P2 回归 2 例:无凭据 provider 背后
第三方 env → untrust;无凭据 provider + clean env → 不误 untrust。
Guardrail — provider-resolver.test.ts 加 P2 三态行为测试;context-window-trusted.test.ts 的
ChatView pin 更新为锁定 fail-closed 语义(providerFetchState === 'loaded' ? (… ?? true) : false)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MCP servers configured via CLI not discovered by CodePilot

2 participants

@gy212@op7418
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix: discover MCP servers from CLI when not in settings.json - #43

Closed
gy212 wants to merge 5 commits into
op7418:mainfrom
gy212:fix/mcp-cli-discovery
Closed

fix: discover MCP servers from CLI when not in settings.json#43
gy212 wants to merge 5 commits into
op7418:mainfrom
gy212:fix/mcp-cli-discovery

Conversation

@gy212

@gy212gy212 commented Feb 9, 2026

Copy link
Copy Markdown
Contributor

问题

通过 claude mcp add -s user 配置的 MCP 服务器在 CodePilot 中无法被发现和使用。扩展页面显示"未配置 MCP 服务器",聊天中也无法使用 MCP 工具。

根本原因:Claude Code CLI 新版本将 MCP 配置存储在内部位置,而非 ~/.claude/settings.jsonmcpServers 字段。CodePilot 只从 settings.json 读取,因此找不到任何 CLI 配置的服务器。

环境:Windows 11

Closes#42

修改内容

1. src/lib/mcp-config.ts — 新增 CLI 发现能力

  • 新增 discoverCliMcpServers():调用 claude mcp list 解析输出,提取服务器名称和命令
  • 使用 findClaudeBinary()findGitBash() 确保跨平台兼容
  • 结果缓存 60 秒,避免频繁调用 CLI
  • 失败时静默返回空对象
  • 修改 getMergedMcpServers():合并三个来源(CLI < settings.json < .mcp.json)

2. src/app/api/plugins/mcp/route.ts — 扩展页面展示 CLI 服务器

  • GET 处理器合并 CLI 发现的服务器与 settings.json 中的服务器
  • 标记 source: 'cli' | 'settings' 方便前端区分

3. src/app/api/chat/route.ts — 聊天时传递 MCP 配置

  • 导入 getMergedMcpServers,将合并后的 MCP 配置传递给 streamClaude()

4. src/app/api/settings/route.ts — 防止设置保存覆盖 MCP 配置

  • PUT 处理器改为合并写入,保留前端未管理的字段(如 mcpServers

验证方式

  1. TypeScript 编译通过
  2. 启动应用,扩展页面能看到 CLI 配置的 MCP 服务器
  3. 聊天中 MCP 工具出现在 init 系统消息的 tools 列表中
  4. 保存设置后 MCP 配置未丢失

Claude Code CLI stores MCP configs in an internal location that
CodePilot cannot read from ~/.claude/settings.json. This adds CLI
discovery via `claude mcp list` with 60s caching, merges CLI/user/
project MCP sources for both the extensions page and chat sessions,
and changes settings PUT to merge instead of overwrite to prevent
losing CLI-configured MCP servers.
Closes#42
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gy212

gy212 commented Feb 9, 2026

Copy link
Copy Markdown
ContributorAuthor

不是哥们,提交完发现又更新了

1 similar comment
@gy212

gy212 commented Feb 9, 2026

Copy link
Copy Markdown
ContributorAuthor

不是哥们,提交完发现又更新了

@gy212

gy212 commented Feb 9, 2026

Copy link
Copy Markdown
ContributorAuthor

基于0.6.4,可能与0.7存在冲突。

@op7418op7418 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR! The approach of discovering MCP servers from claude mcp list is sound and addresses a real gap. A few issues to address before merging:

Medium:

  1. execFileSync blocks the event loopdiscoverCliMcpServers() uses execFileSync with a 10s timeout, called from the chat API route. The first call (or any after cache expiry) will block ALL concurrent requests. Please switch to async execFile (with util.promisify) and make the call chain async.

  2. Brittle CLI output parsing — The regex ^(\S+):\s+(.+?)\s+-\s+[✓✗] parses human-readable output from claude mcp list. This will break if the CLI changes its format, server names contain spaces/colons, or status indicators change. Consider checking if claude mcp list --json is available, or at minimum document this as a known limitation.

Low:

  1. Shallow merge in PUT handler (settings/route.ts:52-54) — { ...existing, ...settings } only works if the frontend omits fields it doesn't manage. If it sends mcpServers: {}, it will overwrite existing config. Consider a more targeted merge.

  2. source field type mismatch — The source property added to MCP configs in the GET handler isn't reflected in the MCPConfigResponse type.

  3. No cache invalidation — The 60s TTL is reasonable, but there's no way to force-refresh after adding a new MCP server via CLI. Consider adding a manual refresh mechanism.

Code quality is otherwise good — defensive error handling, proper use of execFileSync (not exec) for security, and sensible merge priority ordering.

gy212and others added 2 commits February 10, 2026 18:23
- Convert execFileSync to async execFile to avoid blocking the event loop
- Add forceRefresh param and invalidateCliMcpCache() for manual cache refresh
- Support ?refresh=true query param on MCP GET endpoint
- Wrap per-line CLI output parsing in try-catch for robustness
- Add comments noting claude mcp list --json is not yet available
- Fix settings PUT to skip empty objects, preventing accidental mcpServers wipe
- Add source field to MCPServerConfig type definition
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gy212

Copy link
Copy Markdown
ContributorAuthor

已按本轮 review 意见完成修复,补充说明如下:

✅ 1) CLI MCP 服务器改为“只显示,不落盘”

  • /api/plugins/mcp GET 仍会合并返回:CLI + settings(settings 同名优先)。
  • /api/plugins/mcp PUT 会过滤 source: 'cli',只持久化 settings 服务器,避免把 CLI 发现结果写回 ~/.claude/settings.json
  • 前端 MCP 列表把 CLI 项标记为 CLI (read-only),并禁用编辑/删除按钮。
  • MCP 页面新增 Refresh 按钮(?refresh=true)以手动刷新 CLI 发现缓存。

✅ 2) /api/settings PUT 恢复覆盖语义

  • 改回“提交什么就保存什么”,不再 merge + 跳过空对象。
  • 这样 JSON 模式可正常删除字段/清空对象,不会出现“删不掉”的行为回归。

✅ 3) 修复 CLI 输出解析脆弱性

  • 新增 parseCliMcpListLine + 引号感知拆分逻辑,正确处理带空格路径/参数与引号内容。
  • 解析状态分隔改为使用最后一个 " - ",避免命令参数里包含 - 时被截断。

✅ 附加修正

  • MCP POST 入参校验更严格:
    • stdio 必须有 command
    • sse/http 必须有 url

🧪 验证

  • lint:通过(改动文件)
  • 单测:新增 mcp-cli-parser.test.ts 并通过
    • 覆盖普通行、带引号/空格参数、命令含 -、非法行等场景

本次提交:02e35d1

@gy212
gy212 requested a review from op7418February 10, 2026 11:16
gy212and others added 2 commits February 10, 2026 21:52
`claude mcp list` fails on Windows due to Git Bash detection issues,
so CLI discovery returns empty. Add direct file read from ~/.claude.json
(where `claude mcp add` stores configs) as a reliable fallback source.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gy212

Copy link
Copy Markdown
ContributorAuthor

已完成本次适配与复核,结论如下:

本地验证:

px tsx --test src/tests/unit/mcp-cli-parser.test.ts ✅

px tsx --test src/tests/unit/mcp-config.test.ts ✅

px eslint(针对本 PR 涉及文件)✅

Review 结论:当前版本无 blocker/high 问题,可继续走合并流程。
建议后续补一组集成测试覆盖 MCP 多来源合并优先级(CLI / ~/.claude.json / settings / .mcp.json)以降低未来回归风险。

@op7418op7418 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well-designed MCP CLI discovery feature. Multi-source merging logic is clean, 60s cache is sensible, read-only UI for CLI servers is good UX. CLI output parsing is inherently fragile but mitigated by the ~/.claude.json fallback.

Please rebase onto current main before merge.

@op7418

Copy link
Copy Markdown
Owner

你好 @gy212,感谢你提交这个 MCP 发现的修复!

MCP 服务器的读取问题已经在主分支通过以下 commit 修复了:

  • a5cff79 fix: read MCP servers from both ~/.claude.json and ~/.claude/settings.json

该 commit 支持从多个配置文件位置读取 MCP 服务器配置,解决了 CLI 配置的 MCP 服务器无法被发现的问题。

因此先关闭这个 PR。再次感谢你的贡献!

@op7418op7418 closed this Feb 23, 2026
@gy212
gy212 deleted the fix/mcp-cli-discovery branch March 5, 2026 01:32
op7418 added a commit that referenced this pull request Jun 28, 2026
…ed (#632)
Signal — Codex 复审 #632 P1+item1 后给三点:
- [P2] resolveEffectiveAnthropicBaseUrl 在 provider 存在但 hasCredentials=false 时仍优先返回
provider.base_url,与 toClaudeCodeEnv 不一致(该状态两分支都不跑、SDK 只继承 ambient
ANTHROPIC_BASE_URL)。若用户选了无 key 的 DB provider + 环境有第三方 ANTHROPIC_BASE_URL,
gate 可能按错地址误信窗口(同 GLM 同类漏口)。
- [P3] 存量第三方会话首屏可能在 provider models 加载前(undefined→trusted)短暂闪历史 200K。
- [P3] tech-debt 新条目用了 #36,与既有 #36 撞车。
Triage —
- P2:helper 只镜像了 toClaudeCodeEnv 两态(有凭据 provider / 无 provider),漏第三态(有
provider 无凭据):该态 provider 分支 gated on hasCredentials、env 分支 gated on !provider,
两者都不跑 → env 保持 ambient process.env.ANTHROPIC_BASE_URL,provider.base_url 不注入、
settings 不读。
- P3 闪回:undefined 同时表示"加载中"与"非 anthropic 组未标注",前者应 fail-closed。
- P3 编号:tracker 非严格连续,实际最大 42 → 下一个可用 43。
Fix —
- provider-resolver:helper 改三态镜像。`provider && !hasCredentials` → 返回
process.env.ANTHROPIC_BASE_URL(忠实镜像 SDK 继承的 ambient env,不读 provider.base_url /
settings)。doc 写明三态对应关系。
- ChatView:fail-closed —— providerFetchState !== 'loaded' 传 false;loaded 时用 group flag
(found 必有标注;not-found stale provider → ?? true 向后兼容)。第一方代价 = 首屏短暂只显
已用、百分比后补的渐进式诚实显示,绝不闪错数。
- tech-debt #36#43(plan doc / memory 引用同步)。
Verify — npm run test 3387/3387(typecheck clean)。新增 P2 回归 2 例:无凭据 provider 背后
第三方 env → untrust;无凭据 provider + clean env → 不误 untrust。
Guardrail — provider-resolver.test.ts 加 P2 三态行为测试;context-window-trusted.test.ts 的
ChatView pin 更新为锁定 fail-closed 语义(providerFetchState === 'loaded' ? (… ?? true) : false)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
youcho2 pushed a commit to youcho2/CodePilot that referenced this pull request Aug 11, 2026
…ed (op7418#632)
Signal — Codex 复审 op7418#632 P1+item1 后给三点:
- [P2] resolveEffectiveAnthropicBaseUrl 在 provider 存在但 hasCredentials=false 时仍优先返回
provider.base_url,与 toClaudeCodeEnv 不一致(该状态两分支都不跑、SDK 只继承 ambient
ANTHROPIC_BASE_URL)。若用户选了无 key 的 DB provider + 环境有第三方 ANTHROPIC_BASE_URL,
gate 可能按错地址误信窗口(同 GLM 同类漏口)。
- [P3] 存量第三方会话首屏可能在 provider models 加载前(undefined→trusted)短暂闪历史 200K。
- [P3] tech-debt 新条目用了 op7418#36,与既有 op7418#36 撞车。
Triage —
- P2:helper 只镜像了 toClaudeCodeEnv 两态(有凭据 provider / 无 provider),漏第三态(有
provider 无凭据):该态 provider 分支 gated on hasCredentials、env 分支 gated on !provider,
两者都不跑 → env 保持 ambient process.env.ANTHROPIC_BASE_URL,provider.base_url 不注入、
settings 不读。
- P3 闪回:undefined 同时表示"加载中"与"非 anthropic 组未标注",前者应 fail-closed。
- P3 编号:tracker 非严格连续,实际最大 42 → 下一个可用 43。
Fix —
- provider-resolver:helper 改三态镜像。`provider && !hasCredentials` → 返回
process.env.ANTHROPIC_BASE_URL(忠实镜像 SDK 继承的 ambient env,不读 provider.base_url /
settings)。doc 写明三态对应关系。
- ChatView:fail-closed —— providerFetchState !== 'loaded' 传 false;loaded 时用 group flag
(found 必有标注;not-found stale provider → ?? true 向后兼容)。第一方代价 = 首屏短暂只显
已用、百分比后补的渐进式诚实显示,绝不闪错数。
- tech-debt op7418#36op7418#43(plan doc / memory 引用同步)。
Verify — npm run test 3387/3387(typecheck clean)。新增 P2 回归 2 例:无凭据 provider 背后
第三方 env → untrust;无凭据 provider + clean env → 不误 untrust。
Guardrail — provider-resolver.test.ts 加 P2 三态行为测试;context-window-trusted.test.ts 的
ChatView pin 更新为锁定 fail-closed 语义(providerFetchState === 'loaded' ? (… ?? true) : false)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MCP servers configured via CLI not discovered by CodePilot

2 participants

@gy212@op7418
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix: discover MCP servers from CLI when not in settings.json - #43

Closed
gy212 wants to merge 5 commits into
op7418:mainfrom
gy212:fix/mcp-cli-discovery
Closed

fix: discover MCP servers from CLI when not in settings.json#43
gy212 wants to merge 5 commits into
op7418:mainfrom
gy212:fix/mcp-cli-discovery

Conversation

@gy212

@gy212gy212 commented Feb 9, 2026

Copy link
Copy Markdown
Contributor

问题

通过 claude mcp add -s user 配置的 MCP 服务器在 CodePilot 中无法被发现和使用。扩展页面显示"未配置 MCP 服务器",聊天中也无法使用 MCP 工具。

根本原因:Claude Code CLI 新版本将 MCP 配置存储在内部位置,而非 ~/.claude/settings.jsonmcpServers 字段。CodePilot 只从 settings.json 读取,因此找不到任何 CLI 配置的服务器。

环境:Windows 11

Closes#42

修改内容

1. src/lib/mcp-config.ts — 新增 CLI 发现能力

  • 新增 discoverCliMcpServers():调用 claude mcp list 解析输出,提取服务器名称和命令
  • 使用 findClaudeBinary()findGitBash() 确保跨平台兼容
  • 结果缓存 60 秒,避免频繁调用 CLI
  • 失败时静默返回空对象
  • 修改 getMergedMcpServers():合并三个来源(CLI < settings.json < .mcp.json)

2. src/app/api/plugins/mcp/route.ts — 扩展页面展示 CLI 服务器

  • GET 处理器合并 CLI 发现的服务器与 settings.json 中的服务器
  • 标记 source: 'cli' | 'settings' 方便前端区分

3. src/app/api/chat/route.ts — 聊天时传递 MCP 配置

  • 导入 getMergedMcpServers,将合并后的 MCP 配置传递给 streamClaude()

4. src/app/api/settings/route.ts — 防止设置保存覆盖 MCP 配置

  • PUT 处理器改为合并写入,保留前端未管理的字段(如 mcpServers

验证方式

  1. TypeScript 编译通过
  2. 启动应用,扩展页面能看到 CLI 配置的 MCP 服务器
  3. 聊天中 MCP 工具出现在 init 系统消息的 tools 列表中
  4. 保存设置后 MCP 配置未丢失

Claude Code CLI stores MCP configs in an internal location that
CodePilot cannot read from ~/.claude/settings.json. This adds CLI
discovery via `claude mcp list` with 60s caching, merges CLI/user/
project MCP sources for both the extensions page and chat sessions,
and changes settings PUT to merge instead of overwrite to prevent
losing CLI-configured MCP servers.
Closes#42
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gy212

gy212 commented Feb 9, 2026

Copy link
Copy Markdown
ContributorAuthor

不是哥们,提交完发现又更新了

1 similar comment
@gy212

gy212 commented Feb 9, 2026

Copy link
Copy Markdown
ContributorAuthor

不是哥们,提交完发现又更新了

@gy212

gy212 commented Feb 9, 2026

Copy link
Copy Markdown
ContributorAuthor

基于0.6.4,可能与0.7存在冲突。

@op7418op7418 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR! The approach of discovering MCP servers from claude mcp list is sound and addresses a real gap. A few issues to address before merging:

Medium:

  1. execFileSync blocks the event loopdiscoverCliMcpServers() uses execFileSync with a 10s timeout, called from the chat API route. The first call (or any after cache expiry) will block ALL concurrent requests. Please switch to async execFile (with util.promisify) and make the call chain async.

  2. Brittle CLI output parsing — The regex ^(\S+):\s+(.+?)\s+-\s+[✓✗] parses human-readable output from claude mcp list. This will break if the CLI changes its format, server names contain spaces/colons, or status indicators change. Consider checking if claude mcp list --json is available, or at minimum document this as a known limitation.

Low:

  1. Shallow merge in PUT handler (settings/route.ts:52-54) — { ...existing, ...settings } only works if the frontend omits fields it doesn't manage. If it sends mcpServers: {}, it will overwrite existing config. Consider a more targeted merge.

  2. source field type mismatch — The source property added to MCP configs in the GET handler isn't reflected in the MCPConfigResponse type.

  3. No cache invalidation — The 60s TTL is reasonable, but there's no way to force-refresh after adding a new MCP server via CLI. Consider adding a manual refresh mechanism.

Code quality is otherwise good — defensive error handling, proper use of execFileSync (not exec) for security, and sensible merge priority ordering.

gy212and others added 2 commits February 10, 2026 18:23
- Convert execFileSync to async execFile to avoid blocking the event loop
- Add forceRefresh param and invalidateCliMcpCache() for manual cache refresh
- Support ?refresh=true query param on MCP GET endpoint
- Wrap per-line CLI output parsing in try-catch for robustness
- Add comments noting claude mcp list --json is not yet available
- Fix settings PUT to skip empty objects, preventing accidental mcpServers wipe
- Add source field to MCPServerConfig type definition
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gy212

Copy link
Copy Markdown
ContributorAuthor

已按本轮 review 意见完成修复,补充说明如下:

✅ 1) CLI MCP 服务器改为“只显示,不落盘”

  • /api/plugins/mcp GET 仍会合并返回:CLI + settings(settings 同名优先)。
  • /api/plugins/mcp PUT 会过滤 source: 'cli',只持久化 settings 服务器,避免把 CLI 发现结果写回 ~/.claude/settings.json
  • 前端 MCP 列表把 CLI 项标记为 CLI (read-only),并禁用编辑/删除按钮。
  • MCP 页面新增 Refresh 按钮(?refresh=true)以手动刷新 CLI 发现缓存。

✅ 2) /api/settings PUT 恢复覆盖语义

  • 改回“提交什么就保存什么”,不再 merge + 跳过空对象。
  • 这样 JSON 模式可正常删除字段/清空对象,不会出现“删不掉”的行为回归。

✅ 3) 修复 CLI 输出解析脆弱性

  • 新增 parseCliMcpListLine + 引号感知拆分逻辑,正确处理带空格路径/参数与引号内容。
  • 解析状态分隔改为使用最后一个 " - ",避免命令参数里包含 - 时被截断。

✅ 附加修正

  • MCP POST 入参校验更严格:
    • stdio 必须有 command
    • sse/http 必须有 url

🧪 验证

  • lint:通过(改动文件)
  • 单测:新增 mcp-cli-parser.test.ts 并通过
    • 覆盖普通行、带引号/空格参数、命令含 -、非法行等场景

本次提交:02e35d1

@gy212
gy212 requested a review from op7418February 10, 2026 11:16
gy212and others added 2 commits February 10, 2026 21:52
`claude mcp list` fails on Windows due to Git Bash detection issues,
so CLI discovery returns empty. Add direct file read from ~/.claude.json
(where `claude mcp add` stores configs) as a reliable fallback source.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gy212

Copy link
Copy Markdown
ContributorAuthor

已完成本次适配与复核,结论如下:

本地验证:

px tsx --test src/tests/unit/mcp-cli-parser.test.ts ✅

px tsx --test src/tests/unit/mcp-config.test.ts ✅

px eslint(针对本 PR 涉及文件)✅

Review 结论:当前版本无 blocker/high 问题,可继续走合并流程。
建议后续补一组集成测试覆盖 MCP 多来源合并优先级(CLI / ~/.claude.json / settings / .mcp.json)以降低未来回归风险。

@op7418op7418 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well-designed MCP CLI discovery feature. Multi-source merging logic is clean, 60s cache is sensible, read-only UI for CLI servers is good UX. CLI output parsing is inherently fragile but mitigated by the ~/.claude.json fallback.

Please rebase onto current main before merge.

@op7418

Copy link
Copy Markdown
Owner

你好 @gy212,感谢你提交这个 MCP 发现的修复!

MCP 服务器的读取问题已经在主分支通过以下 commit 修复了:

  • a5cff79 fix: read MCP servers from both ~/.claude.json and ~/.claude/settings.json

该 commit 支持从多个配置文件位置读取 MCP 服务器配置,解决了 CLI 配置的 MCP 服务器无法被发现的问题。

因此先关闭这个 PR。再次感谢你的贡献!

@op7418op7418 closed this Feb 23, 2026
@gy212
gy212 deleted the fix/mcp-cli-discovery branch March 5, 2026 01:32
op7418 added a commit that referenced this pull request Jun 28, 2026
…ed (#632)
Signal — Codex 复审 #632 P1+item1 后给三点:
- [P2] resolveEffectiveAnthropicBaseUrl 在 provider 存在但 hasCredentials=false 时仍优先返回
provider.base_url,与 toClaudeCodeEnv 不一致(该状态两分支都不跑、SDK 只继承 ambient
ANTHROPIC_BASE_URL)。若用户选了无 key 的 DB provider + 环境有第三方 ANTHROPIC_BASE_URL,
gate 可能按错地址误信窗口(同 GLM 同类漏口)。
- [P3] 存量第三方会话首屏可能在 provider models 加载前(undefined→trusted)短暂闪历史 200K。
- [P3] tech-debt 新条目用了 #36,与既有 #36 撞车。
Triage —
- P2:helper 只镜像了 toClaudeCodeEnv 两态(有凭据 provider / 无 provider),漏第三态(有
provider 无凭据):该态 provider 分支 gated on hasCredentials、env 分支 gated on !provider,
两者都不跑 → env 保持 ambient process.env.ANTHROPIC_BASE_URL,provider.base_url 不注入、
settings 不读。
- P3 闪回:undefined 同时表示"加载中"与"非 anthropic 组未标注",前者应 fail-closed。
- P3 编号:tracker 非严格连续,实际最大 42 → 下一个可用 43。
Fix —
- provider-resolver:helper 改三态镜像。`provider && !hasCredentials` → 返回
process.env.ANTHROPIC_BASE_URL(忠实镜像 SDK 继承的 ambient env,不读 provider.base_url /
settings)。doc 写明三态对应关系。
- ChatView:fail-closed —— providerFetchState !== 'loaded' 传 false;loaded 时用 group flag
(found 必有标注;not-found stale provider → ?? true 向后兼容)。第一方代价 = 首屏短暂只显
已用、百分比后补的渐进式诚实显示,绝不闪错数。
- tech-debt #36#43(plan doc / memory 引用同步)。
Verify — npm run test 3387/3387(typecheck clean)。新增 P2 回归 2 例:无凭据 provider 背后
第三方 env → untrust;无凭据 provider + clean env → 不误 untrust。
Guardrail — provider-resolver.test.ts 加 P2 三态行为测试;context-window-trusted.test.ts 的
ChatView pin 更新为锁定 fail-closed 语义(providerFetchState === 'loaded' ? (… ?? true) : false)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
youcho2 pushed a commit to youcho2/CodePilot that referenced this pull request Aug 11, 2026
…ed (op7418#632)
Signal — Codex 复审 op7418#632 P1+item1 后给三点:
- [P2] resolveEffectiveAnthropicBaseUrl 在 provider 存在但 hasCredentials=false 时仍优先返回
provider.base_url,与 toClaudeCodeEnv 不一致(该状态两分支都不跑、SDK 只继承 ambient
ANTHROPIC_BASE_URL)。若用户选了无 key 的 DB provider + 环境有第三方 ANTHROPIC_BASE_URL,
gate 可能按错地址误信窗口(同 GLM 同类漏口)。
- [P3] 存量第三方会话首屏可能在 provider models 加载前(undefined→trusted)短暂闪历史 200K。
- [P3] tech-debt 新条目用了 op7418#36,与既有 op7418#36 撞车。
Triage —
- P2:helper 只镜像了 toClaudeCodeEnv 两态(有凭据 provider / 无 provider),漏第三态(有
provider 无凭据):该态 provider 分支 gated on hasCredentials、env 分支 gated on !provider,
两者都不跑 → env 保持 ambient process.env.ANTHROPIC_BASE_URL,provider.base_url 不注入、
settings 不读。
- P3 闪回:undefined 同时表示"加载中"与"非 anthropic 组未标注",前者应 fail-closed。
- P3 编号:tracker 非严格连续,实际最大 42 → 下一个可用 43。
Fix —
- provider-resolver:helper 改三态镜像。`provider && !hasCredentials` → 返回
process.env.ANTHROPIC_BASE_URL(忠实镜像 SDK 继承的 ambient env,不读 provider.base_url /
settings)。doc 写明三态对应关系。
- ChatView:fail-closed —— providerFetchState !== 'loaded' 传 false;loaded 时用 group flag
(found 必有标注;not-found stale provider → ?? true 向后兼容)。第一方代价 = 首屏短暂只显
已用、百分比后补的渐进式诚实显示,绝不闪错数。
- tech-debt op7418#36op7418#43(plan doc / memory 引用同步)。
Verify — npm run test 3387/3387(typecheck clean)。新增 P2 回归 2 例:无凭据 provider 背后
第三方 env → untrust;无凭据 provider + clean env → 不误 untrust。
Guardrail — provider-resolver.test.ts 加 P2 三态行为测试;context-window-trusted.test.ts 的
ChatView pin 更新为锁定 fail-closed 语义(providerFetchState === 'loaded' ? (… ?? true) : false)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MCP servers configured via CLI not discovered by CodePilot

2 participants

@gy212@op7418
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix: discover MCP servers from CLI when not in settings.json - #43

Closed
gy212 wants to merge 5 commits into
op7418:mainfrom
gy212:fix/mcp-cli-discovery
Closed

fix: discover MCP servers from CLI when not in settings.json#43
gy212 wants to merge 5 commits into
op7418:mainfrom
gy212:fix/mcp-cli-discovery

Conversation

@gy212

@gy212gy212 commented Feb 9, 2026

Copy link
Copy Markdown
Contributor

问题

通过 claude mcp add -s user 配置的 MCP 服务器在 CodePilot 中无法被发现和使用。扩展页面显示"未配置 MCP 服务器",聊天中也无法使用 MCP 工具。

根本原因:Claude Code CLI 新版本将 MCP 配置存储在内部位置,而非 ~/.claude/settings.jsonmcpServers 字段。CodePilot 只从 settings.json 读取,因此找不到任何 CLI 配置的服务器。

环境:Windows 11

Closes#42

修改内容

1. src/lib/mcp-config.ts — 新增 CLI 发现能力

  • 新增 discoverCliMcpServers():调用 claude mcp list 解析输出,提取服务器名称和命令
  • 使用 findClaudeBinary()findGitBash() 确保跨平台兼容
  • 结果缓存 60 秒,避免频繁调用 CLI
  • 失败时静默返回空对象
  • 修改 getMergedMcpServers():合并三个来源(CLI < settings.json < .mcp.json)

2. src/app/api/plugins/mcp/route.ts — 扩展页面展示 CLI 服务器

  • GET 处理器合并 CLI 发现的服务器与 settings.json 中的服务器
  • 标记 source: 'cli' | 'settings' 方便前端区分

3. src/app/api/chat/route.ts — 聊天时传递 MCP 配置

  • 导入 getMergedMcpServers,将合并后的 MCP 配置传递给 streamClaude()

4. src/app/api/settings/route.ts — 防止设置保存覆盖 MCP 配置

  • PUT 处理器改为合并写入,保留前端未管理的字段(如 mcpServers

验证方式

  1. TypeScript 编译通过
  2. 启动应用,扩展页面能看到 CLI 配置的 MCP 服务器
  3. 聊天中 MCP 工具出现在 init 系统消息的 tools 列表中
  4. 保存设置后 MCP 配置未丢失

Claude Code CLI stores MCP configs in an internal location that
CodePilot cannot read from ~/.claude/settings.json. This adds CLI
discovery via `claude mcp list` with 60s caching, merges CLI/user/
project MCP sources for both the extensions page and chat sessions,
and changes settings PUT to merge instead of overwrite to prevent
losing CLI-configured MCP servers.
Closes#42
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gy212

gy212 commented Feb 9, 2026

Copy link
Copy Markdown
ContributorAuthor

不是哥们,提交完发现又更新了

1 similar comment
@gy212

gy212 commented Feb 9, 2026

Copy link
Copy Markdown
ContributorAuthor

不是哥们,提交完发现又更新了

@gy212

gy212 commented Feb 9, 2026

Copy link
Copy Markdown
ContributorAuthor

基于0.6.4,可能与0.7存在冲突。

@op7418op7418 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR! The approach of discovering MCP servers from claude mcp list is sound and addresses a real gap. A few issues to address before merging:

Medium:

  1. execFileSync blocks the event loopdiscoverCliMcpServers() uses execFileSync with a 10s timeout, called from the chat API route. The first call (or any after cache expiry) will block ALL concurrent requests. Please switch to async execFile (with util.promisify) and make the call chain async.

  2. Brittle CLI output parsing — The regex ^(\S+):\s+(.+?)\s+-\s+[✓✗] parses human-readable output from claude mcp list. This will break if the CLI changes its format, server names contain spaces/colons, or status indicators change. Consider checking if claude mcp list --json is available, or at minimum document this as a known limitation.

Low:

  1. Shallow merge in PUT handler (settings/route.ts:52-54) — { ...existing, ...settings } only works if the frontend omits fields it doesn't manage. If it sends mcpServers: {}, it will overwrite existing config. Consider a more targeted merge.

  2. source field type mismatch — The source property added to MCP configs in the GET handler isn't reflected in the MCPConfigResponse type.

  3. No cache invalidation — The 60s TTL is reasonable, but there's no way to force-refresh after adding a new MCP server via CLI. Consider adding a manual refresh mechanism.

Code quality is otherwise good — defensive error handling, proper use of execFileSync (not exec) for security, and sensible merge priority ordering.

gy212and others added 2 commits February 10, 2026 18:23
- Convert execFileSync to async execFile to avoid blocking the event loop
- Add forceRefresh param and invalidateCliMcpCache() for manual cache refresh
- Support ?refresh=true query param on MCP GET endpoint
- Wrap per-line CLI output parsing in try-catch for robustness
- Add comments noting claude mcp list --json is not yet available
- Fix settings PUT to skip empty objects, preventing accidental mcpServers wipe
- Add source field to MCPServerConfig type definition
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gy212

Copy link
Copy Markdown
ContributorAuthor

已按本轮 review 意见完成修复,补充说明如下:

✅ 1) CLI MCP 服务器改为“只显示,不落盘”

  • /api/plugins/mcp GET 仍会合并返回:CLI + settings(settings 同名优先)。
  • /api/plugins/mcp PUT 会过滤 source: 'cli',只持久化 settings 服务器,避免把 CLI 发现结果写回 ~/.claude/settings.json
  • 前端 MCP 列表把 CLI 项标记为 CLI (read-only),并禁用编辑/删除按钮。
  • MCP 页面新增 Refresh 按钮(?refresh=true)以手动刷新 CLI 发现缓存。

✅ 2) /api/settings PUT 恢复覆盖语义

  • 改回“提交什么就保存什么”,不再 merge + 跳过空对象。
  • 这样 JSON 模式可正常删除字段/清空对象,不会出现“删不掉”的行为回归。

✅ 3) 修复 CLI 输出解析脆弱性

  • 新增 parseCliMcpListLine + 引号感知拆分逻辑,正确处理带空格路径/参数与引号内容。
  • 解析状态分隔改为使用最后一个 " - ",避免命令参数里包含 - 时被截断。

✅ 附加修正

  • MCP POST 入参校验更严格:
    • stdio 必须有 command
    • sse/http 必须有 url

🧪 验证

  • lint:通过(改动文件)
  • 单测:新增 mcp-cli-parser.test.ts 并通过
    • 覆盖普通行、带引号/空格参数、命令含 -、非法行等场景

本次提交:02e35d1

@gy212
gy212 requested a review from op7418February 10, 2026 11:16
gy212and others added 2 commits February 10, 2026 21:52
`claude mcp list` fails on Windows due to Git Bash detection issues,
so CLI discovery returns empty. Add direct file read from ~/.claude.json
(where `claude mcp add` stores configs) as a reliable fallback source.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gy212

Copy link
Copy Markdown
ContributorAuthor

已完成本次适配与复核,结论如下:

本地验证:

px tsx --test src/tests/unit/mcp-cli-parser.test.ts ✅

px tsx --test src/tests/unit/mcp-config.test.ts ✅

px eslint(针对本 PR 涉及文件)✅

Review 结论:当前版本无 blocker/high 问题,可继续走合并流程。
建议后续补一组集成测试覆盖 MCP 多来源合并优先级(CLI / ~/.claude.json / settings / .mcp.json)以降低未来回归风险。

@op7418op7418 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well-designed MCP CLI discovery feature. Multi-source merging logic is clean, 60s cache is sensible, read-only UI for CLI servers is good UX. CLI output parsing is inherently fragile but mitigated by the ~/.claude.json fallback.

Please rebase onto current main before merge.

@op7418

Copy link
Copy Markdown
Owner

你好 @gy212,感谢你提交这个 MCP 发现的修复!

MCP 服务器的读取问题已经在主分支通过以下 commit 修复了:

  • a5cff79 fix: read MCP servers from both ~/.claude.json and ~/.claude/settings.json

该 commit 支持从多个配置文件位置读取 MCP 服务器配置,解决了 CLI 配置的 MCP 服务器无法被发现的问题。

因此先关闭这个 PR。再次感谢你的贡献!

@op7418op7418 closed this Feb 23, 2026
@gy212
gy212 deleted the fix/mcp-cli-discovery branch March 5, 2026 01:32
op7418 added a commit that referenced this pull request Jun 28, 2026
…ed (#632)
Signal — Codex 复审 #632 P1+item1 后给三点:
- [P2] resolveEffectiveAnthropicBaseUrl 在 provider 存在但 hasCredentials=false 时仍优先返回
provider.base_url,与 toClaudeCodeEnv 不一致(该状态两分支都不跑、SDK 只继承 ambient
ANTHROPIC_BASE_URL)。若用户选了无 key 的 DB provider + 环境有第三方 ANTHROPIC_BASE_URL,
gate 可能按错地址误信窗口(同 GLM 同类漏口)。
- [P3] 存量第三方会话首屏可能在 provider models 加载前(undefined→trusted)短暂闪历史 200K。
- [P3] tech-debt 新条目用了 #36,与既有 #36 撞车。
Triage —
- P2:helper 只镜像了 toClaudeCodeEnv 两态(有凭据 provider / 无 provider),漏第三态(有
provider 无凭据):该态 provider 分支 gated on hasCredentials、env 分支 gated on !provider,
两者都不跑 → env 保持 ambient process.env.ANTHROPIC_BASE_URL,provider.base_url 不注入、
settings 不读。
- P3 闪回:undefined 同时表示"加载中"与"非 anthropic 组未标注",前者应 fail-closed。
- P3 编号:tracker 非严格连续,实际最大 42 → 下一个可用 43。
Fix —
- provider-resolver:helper 改三态镜像。`provider && !hasCredentials` → 返回
process.env.ANTHROPIC_BASE_URL(忠实镜像 SDK 继承的 ambient env,不读 provider.base_url /
settings)。doc 写明三态对应关系。
- ChatView:fail-closed —— providerFetchState !== 'loaded' 传 false;loaded 时用 group flag
(found 必有标注;not-found stale provider → ?? true 向后兼容)。第一方代价 = 首屏短暂只显
已用、百分比后补的渐进式诚实显示,绝不闪错数。
- tech-debt #36#43(plan doc / memory 引用同步)。
Verify — npm run test 3387/3387(typecheck clean)。新增 P2 回归 2 例:无凭据 provider 背后
第三方 env → untrust;无凭据 provider + clean env → 不误 untrust。
Guardrail — provider-resolver.test.ts 加 P2 三态行为测试;context-window-trusted.test.ts 的
ChatView pin 更新为锁定 fail-closed 语义(providerFetchState === 'loaded' ? (… ?? true) : false)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
youcho2 pushed a commit to youcho2/CodePilot that referenced this pull request Aug 11, 2026
…ed (op7418#632)
Signal — Codex 复审 op7418#632 P1+item1 后给三点:
- [P2] resolveEffectiveAnthropicBaseUrl 在 provider 存在但 hasCredentials=false 时仍优先返回
provider.base_url,与 toClaudeCodeEnv 不一致(该状态两分支都不跑、SDK 只继承 ambient
ANTHROPIC_BASE_URL)。若用户选了无 key 的 DB provider + 环境有第三方 ANTHROPIC_BASE_URL,
gate 可能按错地址误信窗口(同 GLM 同类漏口)。
- [P3] 存量第三方会话首屏可能在 provider models 加载前(undefined→trusted)短暂闪历史 200K。
- [P3] tech-debt 新条目用了 op7418#36,与既有 op7418#36 撞车。
Triage —
- P2:helper 只镜像了 toClaudeCodeEnv 两态(有凭据 provider / 无 provider),漏第三态(有
provider 无凭据):该态 provider 分支 gated on hasCredentials、env 分支 gated on !provider,
两者都不跑 → env 保持 ambient process.env.ANTHROPIC_BASE_URL,provider.base_url 不注入、
settings 不读。
- P3 闪回:undefined 同时表示"加载中"与"非 anthropic 组未标注",前者应 fail-closed。
- P3 编号:tracker 非严格连续,实际最大 42 → 下一个可用 43。
Fix —
- provider-resolver:helper 改三态镜像。`provider && !hasCredentials` → 返回
process.env.ANTHROPIC_BASE_URL(忠实镜像 SDK 继承的 ambient env,不读 provider.base_url /
settings)。doc 写明三态对应关系。
- ChatView:fail-closed —— providerFetchState !== 'loaded' 传 false;loaded 时用 group flag
(found 必有标注;not-found stale provider → ?? true 向后兼容)。第一方代价 = 首屏短暂只显
已用、百分比后补的渐进式诚实显示,绝不闪错数。
- tech-debt op7418#36op7418#43(plan doc / memory 引用同步)。
Verify — npm run test 3387/3387(typecheck clean)。新增 P2 回归 2 例:无凭据 provider 背后
第三方 env → untrust;无凭据 provider + clean env → 不误 untrust。
Guardrail — provider-resolver.test.ts 加 P2 三态行为测试;context-window-trusted.test.ts 的
ChatView pin 更新为锁定 fail-closed 语义(providerFetchState === 'loaded' ? (… ?? true) : false)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MCP servers configured via CLI not discovered by CodePilot

2 participants

@gy212@op7418
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix: discover MCP servers from CLI when not in settings.json - #43

Closed
gy212 wants to merge 5 commits into
op7418:mainfrom
gy212:fix/mcp-cli-discovery
Closed

fix: discover MCP servers from CLI when not in settings.json#43
gy212 wants to merge 5 commits into
op7418:mainfrom
gy212:fix/mcp-cli-discovery

Conversation

@gy212

@gy212gy212 commented Feb 9, 2026

Copy link
Copy Markdown
Contributor

问题

通过 claude mcp add -s user 配置的 MCP 服务器在 CodePilot 中无法被发现和使用。扩展页面显示"未配置 MCP 服务器",聊天中也无法使用 MCP 工具。

根本原因:Claude Code CLI 新版本将 MCP 配置存储在内部位置,而非 ~/.claude/settings.jsonmcpServers 字段。CodePilot 只从 settings.json 读取,因此找不到任何 CLI 配置的服务器。

环境:Windows 11

Closes#42

修改内容

1. src/lib/mcp-config.ts — 新增 CLI 发现能力

  • 新增 discoverCliMcpServers():调用 claude mcp list 解析输出,提取服务器名称和命令
  • 使用 findClaudeBinary()findGitBash() 确保跨平台兼容
  • 结果缓存 60 秒,避免频繁调用 CLI
  • 失败时静默返回空对象
  • 修改 getMergedMcpServers():合并三个来源(CLI < settings.json < .mcp.json)

2. src/app/api/plugins/mcp/route.ts — 扩展页面展示 CLI 服务器

  • GET 处理器合并 CLI 发现的服务器与 settings.json 中的服务器
  • 标记 source: 'cli' | 'settings' 方便前端区分

3. src/app/api/chat/route.ts — 聊天时传递 MCP 配置

  • 导入 getMergedMcpServers,将合并后的 MCP 配置传递给 streamClaude()

4. src/app/api/settings/route.ts — 防止设置保存覆盖 MCP 配置

  • PUT 处理器改为合并写入,保留前端未管理的字段(如 mcpServers

验证方式

  1. TypeScript 编译通过
  2. 启动应用,扩展页面能看到 CLI 配置的 MCP 服务器
  3. 聊天中 MCP 工具出现在 init 系统消息的 tools 列表中
  4. 保存设置后 MCP 配置未丢失

Claude Code CLI stores MCP configs in an internal location that
CodePilot cannot read from ~/.claude/settings.json. This adds CLI
discovery via `claude mcp list` with 60s caching, merges CLI/user/
project MCP sources for both the extensions page and chat sessions,
and changes settings PUT to merge instead of overwrite to prevent
losing CLI-configured MCP servers.
Closes#42
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gy212

gy212 commented Feb 9, 2026

Copy link
Copy Markdown
ContributorAuthor

不是哥们,提交完发现又更新了

1 similar comment
@gy212

gy212 commented Feb 9, 2026

Copy link
Copy Markdown
ContributorAuthor

不是哥们,提交完发现又更新了

@gy212

gy212 commented Feb 9, 2026

Copy link
Copy Markdown
ContributorAuthor

基于0.6.4,可能与0.7存在冲突。

@op7418op7418 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR! The approach of discovering MCP servers from claude mcp list is sound and addresses a real gap. A few issues to address before merging:

Medium:

  1. execFileSync blocks the event loopdiscoverCliMcpServers() uses execFileSync with a 10s timeout, called from the chat API route. The first call (or any after cache expiry) will block ALL concurrent requests. Please switch to async execFile (with util.promisify) and make the call chain async.

  2. Brittle CLI output parsing — The regex ^(\S+):\s+(.+?)\s+-\s+[✓✗] parses human-readable output from claude mcp list. This will break if the CLI changes its format, server names contain spaces/colons, or status indicators change. Consider checking if claude mcp list --json is available, or at minimum document this as a known limitation.

Low:

  1. Shallow merge in PUT handler (settings/route.ts:52-54) — { ...existing, ...settings } only works if the frontend omits fields it doesn't manage. If it sends mcpServers: {}, it will overwrite existing config. Consider a more targeted merge.

  2. source field type mismatch — The source property added to MCP configs in the GET handler isn't reflected in the MCPConfigResponse type.

  3. No cache invalidation — The 60s TTL is reasonable, but there's no way to force-refresh after adding a new MCP server via CLI. Consider adding a manual refresh mechanism.

Code quality is otherwise good — defensive error handling, proper use of execFileSync (not exec) for security, and sensible merge priority ordering.

gy212and others added 2 commits February 10, 2026 18:23
- Convert execFileSync to async execFile to avoid blocking the event loop
- Add forceRefresh param and invalidateCliMcpCache() for manual cache refresh
- Support ?refresh=true query param on MCP GET endpoint
- Wrap per-line CLI output parsing in try-catch for robustness
- Add comments noting claude mcp list --json is not yet available
- Fix settings PUT to skip empty objects, preventing accidental mcpServers wipe
- Add source field to MCPServerConfig type definition
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gy212

Copy link
Copy Markdown
ContributorAuthor

已按本轮 review 意见完成修复,补充说明如下:

✅ 1) CLI MCP 服务器改为“只显示,不落盘”

  • /api/plugins/mcp GET 仍会合并返回:CLI + settings(settings 同名优先)。
  • /api/plugins/mcp PUT 会过滤 source: 'cli',只持久化 settings 服务器,避免把 CLI 发现结果写回 ~/.claude/settings.json
  • 前端 MCP 列表把 CLI 项标记为 CLI (read-only),并禁用编辑/删除按钮。
  • MCP 页面新增 Refresh 按钮(?refresh=true)以手动刷新 CLI 发现缓存。

✅ 2) /api/settings PUT 恢复覆盖语义

  • 改回“提交什么就保存什么”,不再 merge + 跳过空对象。
  • 这样 JSON 模式可正常删除字段/清空对象,不会出现“删不掉”的行为回归。

✅ 3) 修复 CLI 输出解析脆弱性

  • 新增 parseCliMcpListLine + 引号感知拆分逻辑,正确处理带空格路径/参数与引号内容。
  • 解析状态分隔改为使用最后一个 " - ",避免命令参数里包含 - 时被截断。

✅ 附加修正

  • MCP POST 入参校验更严格:
    • stdio 必须有 command
    • sse/http 必须有 url

🧪 验证

  • lint:通过(改动文件)
  • 单测:新增 mcp-cli-parser.test.ts 并通过
    • 覆盖普通行、带引号/空格参数、命令含 -、非法行等场景

本次提交:02e35d1

@gy212
gy212 requested a review from op7418February 10, 2026 11:16
gy212and others added 2 commits February 10, 2026 21:52
`claude mcp list` fails on Windows due to Git Bash detection issues,
so CLI discovery returns empty. Add direct file read from ~/.claude.json
(where `claude mcp add` stores configs) as a reliable fallback source.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gy212

Copy link
Copy Markdown
ContributorAuthor

已完成本次适配与复核,结论如下:

本地验证:

px tsx --test src/tests/unit/mcp-cli-parser.test.ts ✅

px tsx --test src/tests/unit/mcp-config.test.ts ✅

px eslint(针对本 PR 涉及文件)✅

Review 结论:当前版本无 blocker/high 问题,可继续走合并流程。
建议后续补一组集成测试覆盖 MCP 多来源合并优先级(CLI / ~/.claude.json / settings / .mcp.json)以降低未来回归风险。

@op7418op7418 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well-designed MCP CLI discovery feature. Multi-source merging logic is clean, 60s cache is sensible, read-only UI for CLI servers is good UX. CLI output parsing is inherently fragile but mitigated by the ~/.claude.json fallback.

Please rebase onto current main before merge.

@op7418

Copy link
Copy Markdown
Owner

你好 @gy212,感谢你提交这个 MCP 发现的修复!

MCP 服务器的读取问题已经在主分支通过以下 commit 修复了:

  • a5cff79 fix: read MCP servers from both ~/.claude.json and ~/.claude/settings.json

该 commit 支持从多个配置文件位置读取 MCP 服务器配置,解决了 CLI 配置的 MCP 服务器无法被发现的问题。

因此先关闭这个 PR。再次感谢你的贡献!

@op7418op7418 closed this Feb 23, 2026
@gy212
gy212 deleted the fix/mcp-cli-discovery branch March 5, 2026 01:32
op7418 added a commit that referenced this pull request Jun 28, 2026
…ed (#632)
Signal — Codex 复审 #632 P1+item1 后给三点:
- [P2] resolveEffectiveAnthropicBaseUrl 在 provider 存在但 hasCredentials=false 时仍优先返回
provider.base_url,与 toClaudeCodeEnv 不一致(该状态两分支都不跑、SDK 只继承 ambient
ANTHROPIC_BASE_URL)。若用户选了无 key 的 DB provider + 环境有第三方 ANTHROPIC_BASE_URL,
gate 可能按错地址误信窗口(同 GLM 同类漏口)。
- [P3] 存量第三方会话首屏可能在 provider models 加载前(undefined→trusted)短暂闪历史 200K。
- [P3] tech-debt 新条目用了 #36,与既有 #36 撞车。
Triage —
- P2:helper 只镜像了 toClaudeCodeEnv 两态(有凭据 provider / 无 provider),漏第三态(有
provider 无凭据):该态 provider 分支 gated on hasCredentials、env 分支 gated on !provider,
两者都不跑 → env 保持 ambient process.env.ANTHROPIC_BASE_URL,provider.base_url 不注入、
settings 不读。
- P3 闪回:undefined 同时表示"加载中"与"非 anthropic 组未标注",前者应 fail-closed。
- P3 编号:tracker 非严格连续,实际最大 42 → 下一个可用 43。
Fix —
- provider-resolver:helper 改三态镜像。`provider && !hasCredentials` → 返回
process.env.ANTHROPIC_BASE_URL(忠实镜像 SDK 继承的 ambient env,不读 provider.base_url /
settings)。doc 写明三态对应关系。
- ChatView:fail-closed —— providerFetchState !== 'loaded' 传 false;loaded 时用 group flag
(found 必有标注;not-found stale provider → ?? true 向后兼容)。第一方代价 = 首屏短暂只显
已用、百分比后补的渐进式诚实显示,绝不闪错数。
- tech-debt #36#43(plan doc / memory 引用同步)。
Verify — npm run test 3387/3387(typecheck clean)。新增 P2 回归 2 例:无凭据 provider 背后
第三方 env → untrust;无凭据 provider + clean env → 不误 untrust。
Guardrail — provider-resolver.test.ts 加 P2 三态行为测试;context-window-trusted.test.ts 的
ChatView pin 更新为锁定 fail-closed 语义(providerFetchState === 'loaded' ? (… ?? true) : false)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
youcho2 pushed a commit to youcho2/CodePilot that referenced this pull request Aug 11, 2026
…ed (op7418#632)
Signal — Codex 复审 op7418#632 P1+item1 后给三点:
- [P2] resolveEffectiveAnthropicBaseUrl 在 provider 存在但 hasCredentials=false 时仍优先返回
provider.base_url,与 toClaudeCodeEnv 不一致(该状态两分支都不跑、SDK 只继承 ambient
ANTHROPIC_BASE_URL)。若用户选了无 key 的 DB provider + 环境有第三方 ANTHROPIC_BASE_URL,
gate 可能按错地址误信窗口(同 GLM 同类漏口)。
- [P3] 存量第三方会话首屏可能在 provider models 加载前(undefined→trusted)短暂闪历史 200K。
- [P3] tech-debt 新条目用了 op7418#36,与既有 op7418#36 撞车。
Triage —
- P2:helper 只镜像了 toClaudeCodeEnv 两态(有凭据 provider / 无 provider),漏第三态(有
provider 无凭据):该态 provider 分支 gated on hasCredentials、env 分支 gated on !provider,
两者都不跑 → env 保持 ambient process.env.ANTHROPIC_BASE_URL,provider.base_url 不注入、
settings 不读。
- P3 闪回:undefined 同时表示"加载中"与"非 anthropic 组未标注",前者应 fail-closed。
- P3 编号:tracker 非严格连续,实际最大 42 → 下一个可用 43。
Fix —
- provider-resolver:helper 改三态镜像。`provider && !hasCredentials` → 返回
process.env.ANTHROPIC_BASE_URL(忠实镜像 SDK 继承的 ambient env,不读 provider.base_url /
settings)。doc 写明三态对应关系。
- ChatView:fail-closed —— providerFetchState !== 'loaded' 传 false;loaded 时用 group flag
(found 必有标注;not-found stale provider → ?? true 向后兼容)。第一方代价 = 首屏短暂只显
已用、百分比后补的渐进式诚实显示,绝不闪错数。
- tech-debt op7418#36op7418#43(plan doc / memory 引用同步)。
Verify — npm run test 3387/3387(typecheck clean)。新增 P2 回归 2 例:无凭据 provider 背后
第三方 env → untrust;无凭据 provider + clean env → 不误 untrust。
Guardrail — provider-resolver.test.ts 加 P2 三态行为测试;context-window-trusted.test.ts 的
ChatView pin 更新为锁定 fail-closed 语义(providerFetchState === 'loaded' ? (… ?? true) : false)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MCP servers configured via CLI not discovered by CodePilot

2 participants

@gy212@op7418
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix: discover MCP servers from CLI when not in settings.json - #43

Closed
gy212 wants to merge 5 commits into
op7418:mainfrom
gy212:fix/mcp-cli-discovery
Closed

fix: discover MCP servers from CLI when not in settings.json#43
gy212 wants to merge 5 commits into
op7418:mainfrom
gy212:fix/mcp-cli-discovery

Conversation

@gy212

@gy212gy212 commented Feb 9, 2026

Copy link
Copy Markdown
Contributor

问题

通过 claude mcp add -s user 配置的 MCP 服务器在 CodePilot 中无法被发现和使用。扩展页面显示"未配置 MCP 服务器",聊天中也无法使用 MCP 工具。

根本原因:Claude Code CLI 新版本将 MCP 配置存储在内部位置,而非 ~/.claude/settings.jsonmcpServers 字段。CodePilot 只从 settings.json 读取,因此找不到任何 CLI 配置的服务器。

环境:Windows 11

Closes#42

修改内容

1. src/lib/mcp-config.ts — 新增 CLI 发现能力

  • 新增 discoverCliMcpServers():调用 claude mcp list 解析输出,提取服务器名称和命令
  • 使用 findClaudeBinary()findGitBash() 确保跨平台兼容
  • 结果缓存 60 秒,避免频繁调用 CLI
  • 失败时静默返回空对象
  • 修改 getMergedMcpServers():合并三个来源(CLI < settings.json < .mcp.json)

2. src/app/api/plugins/mcp/route.ts — 扩展页面展示 CLI 服务器

  • GET 处理器合并 CLI 发现的服务器与 settings.json 中的服务器
  • 标记 source: 'cli' | 'settings' 方便前端区分

3. src/app/api/chat/route.ts — 聊天时传递 MCP 配置

  • 导入 getMergedMcpServers,将合并后的 MCP 配置传递给 streamClaude()

4. src/app/api/settings/route.ts — 防止设置保存覆盖 MCP 配置

  • PUT 处理器改为合并写入,保留前端未管理的字段(如 mcpServers

验证方式

  1. TypeScript 编译通过
  2. 启动应用,扩展页面能看到 CLI 配置的 MCP 服务器
  3. 聊天中 MCP 工具出现在 init 系统消息的 tools 列表中
  4. 保存设置后 MCP 配置未丢失

Claude Code CLI stores MCP configs in an internal location that
CodePilot cannot read from ~/.claude/settings.json. This adds CLI
discovery via `claude mcp list` with 60s caching, merges CLI/user/
project MCP sources for both the extensions page and chat sessions,
and changes settings PUT to merge instead of overwrite to prevent
losing CLI-configured MCP servers.
Closes#42
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gy212

gy212 commented Feb 9, 2026

Copy link
Copy Markdown
ContributorAuthor

不是哥们,提交完发现又更新了

1 similar comment
@gy212

gy212 commented Feb 9, 2026

Copy link
Copy Markdown
ContributorAuthor

不是哥们,提交完发现又更新了

@gy212

gy212 commented Feb 9, 2026

Copy link
Copy Markdown
ContributorAuthor

基于0.6.4,可能与0.7存在冲突。

@op7418op7418 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR! The approach of discovering MCP servers from claude mcp list is sound and addresses a real gap. A few issues to address before merging:

Medium:

  1. execFileSync blocks the event loopdiscoverCliMcpServers() uses execFileSync with a 10s timeout, called from the chat API route. The first call (or any after cache expiry) will block ALL concurrent requests. Please switch to async execFile (with util.promisify) and make the call chain async.

  2. Brittle CLI output parsing — The regex ^(\S+):\s+(.+?)\s+-\s+[✓✗] parses human-readable output from claude mcp list. This will break if the CLI changes its format, server names contain spaces/colons, or status indicators change. Consider checking if claude mcp list --json is available, or at minimum document this as a known limitation.

Low:

  1. Shallow merge in PUT handler (settings/route.ts:52-54) — { ...existing, ...settings } only works if the frontend omits fields it doesn't manage. If it sends mcpServers: {}, it will overwrite existing config. Consider a more targeted merge.

  2. source field type mismatch — The source property added to MCP configs in the GET handler isn't reflected in the MCPConfigResponse type.

  3. No cache invalidation — The 60s TTL is reasonable, but there's no way to force-refresh after adding a new MCP server via CLI. Consider adding a manual refresh mechanism.

Code quality is otherwise good — defensive error handling, proper use of execFileSync (not exec) for security, and sensible merge priority ordering.

gy212and others added 2 commits February 10, 2026 18:23
- Convert execFileSync to async execFile to avoid blocking the event loop
- Add forceRefresh param and invalidateCliMcpCache() for manual cache refresh
- Support ?refresh=true query param on MCP GET endpoint
- Wrap per-line CLI output parsing in try-catch for robustness
- Add comments noting claude mcp list --json is not yet available
- Fix settings PUT to skip empty objects, preventing accidental mcpServers wipe
- Add source field to MCPServerConfig type definition
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gy212

Copy link
Copy Markdown
ContributorAuthor

已按本轮 review 意见完成修复,补充说明如下:

✅ 1) CLI MCP 服务器改为“只显示,不落盘”

  • /api/plugins/mcp GET 仍会合并返回:CLI + settings(settings 同名优先)。
  • /api/plugins/mcp PUT 会过滤 source: 'cli',只持久化 settings 服务器,避免把 CLI 发现结果写回 ~/.claude/settings.json
  • 前端 MCP 列表把 CLI 项标记为 CLI (read-only),并禁用编辑/删除按钮。
  • MCP 页面新增 Refresh 按钮(?refresh=true)以手动刷新 CLI 发现缓存。

✅ 2) /api/settings PUT 恢复覆盖语义

  • 改回“提交什么就保存什么”,不再 merge + 跳过空对象。
  • 这样 JSON 模式可正常删除字段/清空对象,不会出现“删不掉”的行为回归。

✅ 3) 修复 CLI 输出解析脆弱性

  • 新增 parseCliMcpListLine + 引号感知拆分逻辑,正确处理带空格路径/参数与引号内容。
  • 解析状态分隔改为使用最后一个 " - ",避免命令参数里包含 - 时被截断。

✅ 附加修正

  • MCP POST 入参校验更严格:
    • stdio 必须有 command
    • sse/http 必须有 url

🧪 验证

  • lint:通过(改动文件)
  • 单测:新增 mcp-cli-parser.test.ts 并通过
    • 覆盖普通行、带引号/空格参数、命令含 -、非法行等场景

本次提交:02e35d1

@gy212
gy212 requested a review from op7418February 10, 2026 11:16
gy212and others added 2 commits February 10, 2026 21:52
`claude mcp list` fails on Windows due to Git Bash detection issues,
so CLI discovery returns empty. Add direct file read from ~/.claude.json
(where `claude mcp add` stores configs) as a reliable fallback source.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gy212

Copy link
Copy Markdown
ContributorAuthor

已完成本次适配与复核,结论如下:

本地验证:

px tsx --test src/tests/unit/mcp-cli-parser.test.ts ✅

px tsx --test src/tests/unit/mcp-config.test.ts ✅

px eslint(针对本 PR 涉及文件)✅

Review 结论:当前版本无 blocker/high 问题,可继续走合并流程。
建议后续补一组集成测试覆盖 MCP 多来源合并优先级(CLI / ~/.claude.json / settings / .mcp.json)以降低未来回归风险。

@op7418op7418 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well-designed MCP CLI discovery feature. Multi-source merging logic is clean, 60s cache is sensible, read-only UI for CLI servers is good UX. CLI output parsing is inherently fragile but mitigated by the ~/.claude.json fallback.

Please rebase onto current main before merge.

@op7418

Copy link
Copy Markdown
Owner

你好 @gy212,感谢你提交这个 MCP 发现的修复!

MCP 服务器的读取问题已经在主分支通过以下 commit 修复了:

  • a5cff79 fix: read MCP servers from both ~/.claude.json and ~/.claude/settings.json

该 commit 支持从多个配置文件位置读取 MCP 服务器配置,解决了 CLI 配置的 MCP 服务器无法被发现的问题。

因此先关闭这个 PR。再次感谢你的贡献!

@op7418op7418 closed this Feb 23, 2026
@gy212
gy212 deleted the fix/mcp-cli-discovery branch March 5, 2026 01:32
op7418 added a commit that referenced this pull request Jun 28, 2026
…ed (#632)
Signal — Codex 复审 #632 P1+item1 后给三点:
- [P2] resolveEffectiveAnthropicBaseUrl 在 provider 存在但 hasCredentials=false 时仍优先返回
provider.base_url,与 toClaudeCodeEnv 不一致(该状态两分支都不跑、SDK 只继承 ambient
ANTHROPIC_BASE_URL)。若用户选了无 key 的 DB provider + 环境有第三方 ANTHROPIC_BASE_URL,
gate 可能按错地址误信窗口(同 GLM 同类漏口)。
- [P3] 存量第三方会话首屏可能在 provider models 加载前(undefined→trusted)短暂闪历史 200K。
- [P3] tech-debt 新条目用了 #36,与既有 #36 撞车。
Triage —
- P2:helper 只镜像了 toClaudeCodeEnv 两态(有凭据 provider / 无 provider),漏第三态(有
provider 无凭据):该态 provider 分支 gated on hasCredentials、env 分支 gated on !provider,
两者都不跑 → env 保持 ambient process.env.ANTHROPIC_BASE_URL,provider.base_url 不注入、
settings 不读。
- P3 闪回:undefined 同时表示"加载中"与"非 anthropic 组未标注",前者应 fail-closed。
- P3 编号:tracker 非严格连续,实际最大 42 → 下一个可用 43。
Fix —
- provider-resolver:helper 改三态镜像。`provider && !hasCredentials` → 返回
process.env.ANTHROPIC_BASE_URL(忠实镜像 SDK 继承的 ambient env,不读 provider.base_url /
settings)。doc 写明三态对应关系。
- ChatView:fail-closed —— providerFetchState !== 'loaded' 传 false;loaded 时用 group flag
(found 必有标注;not-found stale provider → ?? true 向后兼容)。第一方代价 = 首屏短暂只显
已用、百分比后补的渐进式诚实显示,绝不闪错数。
- tech-debt #36#43(plan doc / memory 引用同步)。
Verify — npm run test 3387/3387(typecheck clean)。新增 P2 回归 2 例:无凭据 provider 背后
第三方 env → untrust;无凭据 provider + clean env → 不误 untrust。
Guardrail — provider-resolver.test.ts 加 P2 三态行为测试;context-window-trusted.test.ts 的
ChatView pin 更新为锁定 fail-closed 语义(providerFetchState === 'loaded' ? (… ?? true) : false)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
youcho2 pushed a commit to youcho2/CodePilot that referenced this pull request Aug 11, 2026
…ed (op7418#632)
Signal — Codex 复审 op7418#632 P1+item1 后给三点:
- [P2] resolveEffectiveAnthropicBaseUrl 在 provider 存在但 hasCredentials=false 时仍优先返回
provider.base_url,与 toClaudeCodeEnv 不一致(该状态两分支都不跑、SDK 只继承 ambient
ANTHROPIC_BASE_URL)。若用户选了无 key 的 DB provider + 环境有第三方 ANTHROPIC_BASE_URL,
gate 可能按错地址误信窗口(同 GLM 同类漏口)。
- [P3] 存量第三方会话首屏可能在 provider models 加载前(undefined→trusted)短暂闪历史 200K。
- [P3] tech-debt 新条目用了 op7418#36,与既有 op7418#36 撞车。
Triage —
- P2:helper 只镜像了 toClaudeCodeEnv 两态(有凭据 provider / 无 provider),漏第三态(有
provider 无凭据):该态 provider 分支 gated on hasCredentials、env 分支 gated on !provider,
两者都不跑 → env 保持 ambient process.env.ANTHROPIC_BASE_URL,provider.base_url 不注入、
settings 不读。
- P3 闪回:undefined 同时表示"加载中"与"非 anthropic 组未标注",前者应 fail-closed。
- P3 编号:tracker 非严格连续,实际最大 42 → 下一个可用 43。
Fix —
- provider-resolver:helper 改三态镜像。`provider && !hasCredentials` → 返回
process.env.ANTHROPIC_BASE_URL(忠实镜像 SDK 继承的 ambient env,不读 provider.base_url /
settings)。doc 写明三态对应关系。
- ChatView:fail-closed —— providerFetchState !== 'loaded' 传 false;loaded 时用 group flag
(found 必有标注;not-found stale provider → ?? true 向后兼容)。第一方代价 = 首屏短暂只显
已用、百分比后补的渐进式诚实显示,绝不闪错数。
- tech-debt op7418#36op7418#43(plan doc / memory 引用同步)。
Verify — npm run test 3387/3387(typecheck clean)。新增 P2 回归 2 例:无凭据 provider 背后
第三方 env → untrust;无凭据 provider + clean env → 不误 untrust。
Guardrail — provider-resolver.test.ts 加 P2 三态行为测试;context-window-trusted.test.ts 的
ChatView pin 更新为锁定 fail-closed 语义(providerFetchState === 'loaded' ? (… ?? true) : false)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MCP servers configured via CLI not discovered by CodePilot

2 participants

@gy212@op7418
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix: discover MCP servers from CLI when not in settings.json - #43

Closed
gy212 wants to merge 5 commits into
op7418:mainfrom
gy212:fix/mcp-cli-discovery
Closed

fix: discover MCP servers from CLI when not in settings.json#43
gy212 wants to merge 5 commits into
op7418:mainfrom
gy212:fix/mcp-cli-discovery

Conversation

@gy212

@gy212gy212 commented Feb 9, 2026

Copy link
Copy Markdown
Contributor

问题

通过 claude mcp add -s user 配置的 MCP 服务器在 CodePilot 中无法被发现和使用。扩展页面显示"未配置 MCP 服务器",聊天中也无法使用 MCP 工具。

根本原因:Claude Code CLI 新版本将 MCP 配置存储在内部位置,而非 ~/.claude/settings.jsonmcpServers 字段。CodePilot 只从 settings.json 读取,因此找不到任何 CLI 配置的服务器。

环境:Windows 11

Closes#42

修改内容

1. src/lib/mcp-config.ts — 新增 CLI 发现能力

  • 新增 discoverCliMcpServers():调用 claude mcp list 解析输出,提取服务器名称和命令
  • 使用 findClaudeBinary()findGitBash() 确保跨平台兼容
  • 结果缓存 60 秒,避免频繁调用 CLI
  • 失败时静默返回空对象
  • 修改 getMergedMcpServers():合并三个来源(CLI < settings.json < .mcp.json)

2. src/app/api/plugins/mcp/route.ts — 扩展页面展示 CLI 服务器

  • GET 处理器合并 CLI 发现的服务器与 settings.json 中的服务器
  • 标记 source: 'cli' | 'settings' 方便前端区分

3. src/app/api/chat/route.ts — 聊天时传递 MCP 配置

  • 导入 getMergedMcpServers,将合并后的 MCP 配置传递给 streamClaude()

4. src/app/api/settings/route.ts — 防止设置保存覆盖 MCP 配置

  • PUT 处理器改为合并写入,保留前端未管理的字段(如 mcpServers

验证方式

  1. TypeScript 编译通过
  2. 启动应用,扩展页面能看到 CLI 配置的 MCP 服务器
  3. 聊天中 MCP 工具出现在 init 系统消息的 tools 列表中
  4. 保存设置后 MCP 配置未丢失

Claude Code CLI stores MCP configs in an internal location that
CodePilot cannot read from ~/.claude/settings.json. This adds CLI
discovery via `claude mcp list` with 60s caching, merges CLI/user/
project MCP sources for both the extensions page and chat sessions,
and changes settings PUT to merge instead of overwrite to prevent
losing CLI-configured MCP servers.
Closes#42
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gy212

gy212 commented Feb 9, 2026

Copy link
Copy Markdown
ContributorAuthor

不是哥们,提交完发现又更新了

1 similar comment
@gy212

gy212 commented Feb 9, 2026

Copy link
Copy Markdown
ContributorAuthor

不是哥们,提交完发现又更新了

@gy212

gy212 commented Feb 9, 2026

Copy link
Copy Markdown
ContributorAuthor

基于0.6.4,可能与0.7存在冲突。

@op7418op7418 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR! The approach of discovering MCP servers from claude mcp list is sound and addresses a real gap. A few issues to address before merging:

Medium:

  1. execFileSync blocks the event loopdiscoverCliMcpServers() uses execFileSync with a 10s timeout, called from the chat API route. The first call (or any after cache expiry) will block ALL concurrent requests. Please switch to async execFile (with util.promisify) and make the call chain async.

  2. Brittle CLI output parsing — The regex ^(\S+):\s+(.+?)\s+-\s+[✓✗] parses human-readable output from claude mcp list. This will break if the CLI changes its format, server names contain spaces/colons, or status indicators change. Consider checking if claude mcp list --json is available, or at minimum document this as a known limitation.

Low:

  1. Shallow merge in PUT handler (settings/route.ts:52-54) — { ...existing, ...settings } only works if the frontend omits fields it doesn't manage. If it sends mcpServers: {}, it will overwrite existing config. Consider a more targeted merge.

  2. source field type mismatch — The source property added to MCP configs in the GET handler isn't reflected in the MCPConfigResponse type.

  3. No cache invalidation — The 60s TTL is reasonable, but there's no way to force-refresh after adding a new MCP server via CLI. Consider adding a manual refresh mechanism.

Code quality is otherwise good — defensive error handling, proper use of execFileSync (not exec) for security, and sensible merge priority ordering.

gy212and others added 2 commits February 10, 2026 18:23
- Convert execFileSync to async execFile to avoid blocking the event loop
- Add forceRefresh param and invalidateCliMcpCache() for manual cache refresh
- Support ?refresh=true query param on MCP GET endpoint
- Wrap per-line CLI output parsing in try-catch for robustness
- Add comments noting claude mcp list --json is not yet available
- Fix settings PUT to skip empty objects, preventing accidental mcpServers wipe
- Add source field to MCPServerConfig type definition
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gy212

Copy link
Copy Markdown
ContributorAuthor

已按本轮 review 意见完成修复,补充说明如下:

✅ 1) CLI MCP 服务器改为“只显示,不落盘”

  • /api/plugins/mcp GET 仍会合并返回:CLI + settings(settings 同名优先)。
  • /api/plugins/mcp PUT 会过滤 source: 'cli',只持久化 settings 服务器,避免把 CLI 发现结果写回 ~/.claude/settings.json
  • 前端 MCP 列表把 CLI 项标记为 CLI (read-only),并禁用编辑/删除按钮。
  • MCP 页面新增 Refresh 按钮(?refresh=true)以手动刷新 CLI 发现缓存。

✅ 2) /api/settings PUT 恢复覆盖语义

  • 改回“提交什么就保存什么”,不再 merge + 跳过空对象。
  • 这样 JSON 模式可正常删除字段/清空对象,不会出现“删不掉”的行为回归。

✅ 3) 修复 CLI 输出解析脆弱性

  • 新增 parseCliMcpListLine + 引号感知拆分逻辑,正确处理带空格路径/参数与引号内容。
  • 解析状态分隔改为使用最后一个 " - ",避免命令参数里包含 - 时被截断。

✅ 附加修正

  • MCP POST 入参校验更严格:
    • stdio 必须有 command
    • sse/http 必须有 url

🧪 验证

  • lint:通过(改动文件)
  • 单测:新增 mcp-cli-parser.test.ts 并通过
    • 覆盖普通行、带引号/空格参数、命令含 -、非法行等场景

本次提交:02e35d1

@gy212
gy212 requested a review from op7418February 10, 2026 11:16
gy212and others added 2 commits February 10, 2026 21:52
`claude mcp list` fails on Windows due to Git Bash detection issues,
so CLI discovery returns empty. Add direct file read from ~/.claude.json
(where `claude mcp add` stores configs) as a reliable fallback source.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gy212

Copy link
Copy Markdown
ContributorAuthor

已完成本次适配与复核,结论如下:

本地验证:

px tsx --test src/tests/unit/mcp-cli-parser.test.ts ✅

px tsx --test src/tests/unit/mcp-config.test.ts ✅

px eslint(针对本 PR 涉及文件)✅

Review 结论:当前版本无 blocker/high 问题,可继续走合并流程。
建议后续补一组集成测试覆盖 MCP 多来源合并优先级(CLI / ~/.claude.json / settings / .mcp.json)以降低未来回归风险。

@op7418op7418 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well-designed MCP CLI discovery feature. Multi-source merging logic is clean, 60s cache is sensible, read-only UI for CLI servers is good UX. CLI output parsing is inherently fragile but mitigated by the ~/.claude.json fallback.

Please rebase onto current main before merge.

@op7418

Copy link
Copy Markdown
Owner

你好 @gy212,感谢你提交这个 MCP 发现的修复!

MCP 服务器的读取问题已经在主分支通过以下 commit 修复了:

  • a5cff79 fix: read MCP servers from both ~/.claude.json and ~/.claude/settings.json

该 commit 支持从多个配置文件位置读取 MCP 服务器配置,解决了 CLI 配置的 MCP 服务器无法被发现的问题。

因此先关闭这个 PR。再次感谢你的贡献!

@op7418op7418 closed this Feb 23, 2026
@gy212
gy212 deleted the fix/mcp-cli-discovery branch March 5, 2026 01:32
op7418 added a commit that referenced this pull request Jun 28, 2026
…ed (#632)
Signal — Codex 复审 #632 P1+item1 后给三点:
- [P2] resolveEffectiveAnthropicBaseUrl 在 provider 存在但 hasCredentials=false 时仍优先返回
provider.base_url,与 toClaudeCodeEnv 不一致(该状态两分支都不跑、SDK 只继承 ambient
ANTHROPIC_BASE_URL)。若用户选了无 key 的 DB provider + 环境有第三方 ANTHROPIC_BASE_URL,
gate 可能按错地址误信窗口(同 GLM 同类漏口)。
- [P3] 存量第三方会话首屏可能在 provider models 加载前(undefined→trusted)短暂闪历史 200K。
- [P3] tech-debt 新条目用了 #36,与既有 #36 撞车。
Triage —
- P2:helper 只镜像了 toClaudeCodeEnv 两态(有凭据 provider / 无 provider),漏第三态(有
provider 无凭据):该态 provider 分支 gated on hasCredentials、env 分支 gated on !provider,
两者都不跑 → env 保持 ambient process.env.ANTHROPIC_BASE_URL,provider.base_url 不注入、
settings 不读。
- P3 闪回:undefined 同时表示"加载中"与"非 anthropic 组未标注",前者应 fail-closed。
- P3 编号:tracker 非严格连续,实际最大 42 → 下一个可用 43。
Fix —
- provider-resolver:helper 改三态镜像。`provider && !hasCredentials` → 返回
process.env.ANTHROPIC_BASE_URL(忠实镜像 SDK 继承的 ambient env,不读 provider.base_url /
settings)。doc 写明三态对应关系。
- ChatView:fail-closed —— providerFetchState !== 'loaded' 传 false;loaded 时用 group flag
(found 必有标注;not-found stale provider → ?? true 向后兼容)。第一方代价 = 首屏短暂只显
已用、百分比后补的渐进式诚实显示,绝不闪错数。
- tech-debt #36#43(plan doc / memory 引用同步)。
Verify — npm run test 3387/3387(typecheck clean)。新增 P2 回归 2 例:无凭据 provider 背后
第三方 env → untrust;无凭据 provider + clean env → 不误 untrust。
Guardrail — provider-resolver.test.ts 加 P2 三态行为测试;context-window-trusted.test.ts 的
ChatView pin 更新为锁定 fail-closed 语义(providerFetchState === 'loaded' ? (… ?? true) : false)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
youcho2 pushed a commit to youcho2/CodePilot that referenced this pull request Aug 11, 2026
…ed (op7418#632)
Signal — Codex 复审 op7418#632 P1+item1 后给三点:
- [P2] resolveEffectiveAnthropicBaseUrl 在 provider 存在但 hasCredentials=false 时仍优先返回
provider.base_url,与 toClaudeCodeEnv 不一致(该状态两分支都不跑、SDK 只继承 ambient
ANTHROPIC_BASE_URL)。若用户选了无 key 的 DB provider + 环境有第三方 ANTHROPIC_BASE_URL,
gate 可能按错地址误信窗口(同 GLM 同类漏口)。
- [P3] 存量第三方会话首屏可能在 provider models 加载前(undefined→trusted)短暂闪历史 200K。
- [P3] tech-debt 新条目用了 op7418#36,与既有 op7418#36 撞车。
Triage —
- P2:helper 只镜像了 toClaudeCodeEnv 两态(有凭据 provider / 无 provider),漏第三态(有
provider 无凭据):该态 provider 分支 gated on hasCredentials、env 分支 gated on !provider,
两者都不跑 → env 保持 ambient process.env.ANTHROPIC_BASE_URL,provider.base_url 不注入、
settings 不读。
- P3 闪回:undefined 同时表示"加载中"与"非 anthropic 组未标注",前者应 fail-closed。
- P3 编号:tracker 非严格连续,实际最大 42 → 下一个可用 43。
Fix —
- provider-resolver:helper 改三态镜像。`provider && !hasCredentials` → 返回
process.env.ANTHROPIC_BASE_URL(忠实镜像 SDK 继承的 ambient env,不读 provider.base_url /
settings)。doc 写明三态对应关系。
- ChatView:fail-closed —— providerFetchState !== 'loaded' 传 false;loaded 时用 group flag
(found 必有标注;not-found stale provider → ?? true 向后兼容)。第一方代价 = 首屏短暂只显
已用、百分比后补的渐进式诚实显示,绝不闪错数。
- tech-debt op7418#36op7418#43(plan doc / memory 引用同步)。
Verify — npm run test 3387/3387(typecheck clean)。新增 P2 回归 2 例:无凭据 provider 背后
第三方 env → untrust;无凭据 provider + clean env → 不误 untrust。
Guardrail — provider-resolver.test.ts 加 P2 三态行为测试;context-window-trusted.test.ts 的
ChatView pin 更新为锁定 fail-closed 语义(providerFetchState === 'loaded' ? (… ?? true) : false)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MCP servers configured via CLI not discovered by CodePilot

2 participants

@gy212@op7418