本章总览
/mcp 命令是 Claude Code MCP(Model Context Protocol)集成的用户入口:local-jsx 类型、immediate: true,默认打开 MCPSettings Ink 面板;子命令 enable/disable/reconnect 走无 UI 路径;ANT 构建下 base /mcp 重定向到 PluginSettings。CLI 侧 commands/mcp/addCommand.ts 提供 claude mcp add 非交互注册。本章连接 commands 层与 services/mcp/ 连接管理。
学完本章你应该能
- 说明 index.ts 与 mcp.tsx call 的参数路由
- 解释 MCPToggle 如何用 useMcpToggleEnabled 批量开关服务器
- 描述 ant 用户 /mcp → /plugins 重定向策略
- 理解 MCPReconnect 与 MCPSettings 组件职责
- 区分 slash /mcp 与 CLI claude mcp add 的配置写入路径
核心概念(先读懂这些)
immediate 绕过输入队列
/mcp 标记 immediate:true,用户输入后立即执行而不等待当前 model turn 结束——MCP 配置属于会话基础设施,与 /model 类似需要低延迟响应。
IDE MCP 排除于 bulk toggle
MCPToggle filter c.name !== "ide":IDE 集成服务器不参与 enable all / disable all,避免误关编辑器桥接。
MCP prompt 命令不在 getCommands 主池
MCP 服务器暴露的 prompt-type skills 存 AppState.mcp.commands,经 getMcpSkillCommands 单独注入 SkillTool——与 /mcp 管理 UI 是不同数据流。
建议学习步骤
- 阅读源码块 A:index.ts 定义
- 阅读源码块 B:call 参数路由
- 阅读源码块 C:MCPToggle effect
- 阅读源码块 D:addCommand CLI 注册
- 阅读源码块 E:getMcpSkillCommands
- 对照 MCPConnectionManager 与 config scope
常见误区
注意
no-redirect 子命令仅测试用,绕过 ant 重定向
注意
reconnect 服务器名含空格时用 slice(1).join
注意
toggleMcpServer 来自 hook,TODO 注释称理想状态应进 global state
在架构中的位置
MCP 在 Claude Code 中分三层:
services/mcp/ — 连接、transport、OAuth、tool 注册
components/mcp/ — MCPSettings、MCPReconnect UI
commands/mcp/ — /mcp 斜杠命令入口
cli mcp add — 非 TUI 配置写入(addCommand.ts)
用户 /mcp 打开设置修改 AppState.mcp.clients;useMcpToggleEnabled 持久化到 mcp config JSON(local/user/project scope)。连接成功后 fetchToolsForClient 把工具以 mcp__server__tool 名注入 ToolUseContext。
/mcp enable foo 不重启 REPL——toggle 触发 ConnectionManager 重连序列。
index.ts:Command 壳
commands/mcp/index.ts 极简:
const mcp = {
type: 'local-jsx',
name: 'mcp',
description: 'Manage MCP servers',
immediate: true,
argumentHint: '[enable|disable [server-name]]',
load: () => import('./mcp.js'),
} satisfies Command
无 isEnabled gate——MCP 管理始终可用(除非整个 REPL remote 过滤掉非 REMOTE_SAFE 命令;/mcp 不在 remote 白名单,remote 模式不可用)。
argumentHint 指导 typeahead 显示子命令语法。
源码引用: src/commands/mcp/index.ts · 第 1–12 行(共 13 行)
1| import type { Command } from '../../commands.js'
2|
3| const mcp = {
4| type: 'local-jsx',
5| name: 'mcp',
6| description: 'Manage MCP servers',
7| immediate: true,
8| argumentHint: '[enable|disable [server-name]]',
9| load: () => import('./mcp.js'),
10| } satisfies Command
11|
12| export default mcp
call:参数路由表
mcp.tsx export async function call(onDone, _context, args?) 决策树:
| 条件 | 渲染 |
|---|---|
args 含 no-redirect | MCPSettings(测试绕过) |
reconnect <name> | MCPReconnect serverName |
enable / disable [target] | MCPToggle action + target |
| 无 args,build==ant | PluginSettings manage + showMcpRedirectMessage |
| 默认 | MCPSettings |
parts = args.trim().split(/\s+/) 解析;target 默认 'all' 用于 bulk enable/disable。
ANT 重定向:MCP 管理 UI 合并进 plugins _installed tab,减少重复入口;no-redirect 保留旧路径供 E2E。
源码引用: src/commands/mcp/mcp.tsx · 第 63–84 行(共 106 行)
63| args?: string,
64| ): Promise<React.ReactNode> {
65| if (args) {
66| const parts = args.trim().split(/\s+/)
67|
68| // Allow /mcp no-redirect to bypass the redirect for testing
69| if (parts[0] === 'no-redirect') {
70| return <MCPSettings onComplete={onDone} />
71| }
72|
73| if (parts[0] === 'reconnect' && parts[1]) {
74| return (
75| <MCPReconnect
76| serverName={parts.slice(1).join(' ')}
77| onComplete={onDone}
78| />
79| )
80| }
81|
82| if (parts[0] === 'enable' || parts[0] === 'disable') {
83| return (
84| <MCPToggle
MCPToggle:批量开关
MCPToggle 是 hack 组件:因 useMcpToggleEnabled 是 React hook,必须在组件内调用(注释 TODO:理想迁入 global state)。
useEffect 单次执行(didRun ref):
isEnabling = action === 'enable'- clients = mcpClients.filter(name !== 'ide')
- toToggle = target==='all' ? 按 type disabled/enabled 过滤 : name===target
- 空集 → onComplete 友好消息(already enabled / not found)
- 循环
toggleMcpServer(s.name)
onComplete 文本区分 all vs 单服务器、enable vs disable。无 Ink 视觉输出,仅 system 消息反馈——适合脚本式 /mcp disable all。
源码引用: src/commands/mcp/mcp.tsx · 第 10–56 行(共 106 行)
10| // Ideally, all MCP state and functions would be in global state.
11| function MCPToggle({
12| action,
13| target,
14| onComplete,
15| }: {
16| action: 'enable' | 'disable'
17| target: string
18| onComplete: (result: string) => void
19| }): null {
20| const mcpClients = useAppState(s => s.mcp.clients)
21| const toggleMcpServer = useMcpToggleEnabled()
22| const didRun = useRef(false)
23|
24| useEffect(() => {
25| if (didRun.current) return
26| didRun.current = true
27|
28| const isEnabling = action === 'enable'
29| const clients = mcpClients.filter(c => c.name !== 'ide')
30| const toToggle =
31| target === 'all'
32| ? clients.filter(c =>
33| isEnabling ? c.type === 'disabled' : c.type !== 'disabled',
34| )
35| : clients.filter(c => c.name === target)
36|
37| if (toToggle.length === 0) {
38| onComplete(
39| target === 'all'
40| ? `All MCP servers are already ${isEnabling ? 'enabled' : 'disabled'}`
41| : `MCP server "${target}" not found`,
42| )
43| return
44| }
45|
46| for (const s of toToggle) {
47| void toggleMcpServer(s.name)
48| }
49|
50| onComplete(
51| target === 'all'
52| ? `${isEnabling ? 'Enabled' : 'Disabled'} ${toToggle.length} MCP server(s)`
53| : `MCP server "${target}" ${isEnabling ? 'enabled' : 'disabled'}`,
54| )
55| }, [action, target, mcpClients, toggleMcpServer, onComplete])
56|
MCPSettings 与 MCPReconnect
默认路径 <MCPSettings onComplete={onDone} />(components/mcp/index.js)提供:
- 服务器列表、transport 类型、连接状态
- 添加/编辑/删除配置项
- OAuth 流程触发
MCPReconnect 专用于 /mcp reconnect <name> 强制重 handshake,处理 sticky 错误连接而不改 config。
PluginSettings(ant 重定向)args="manage" 打开插件管理 tab 并 showMcpRedirectMessage 提示用户 MCP 入口迁移。
onComplete/onDone 统一 LocalJSXCommandOnDone 签名,关闭 dialog 后写 transcript。
源码引用: src/commands/mcp/mcp.tsx · 第 1–9 行(共 106 行)
1| import React, { useEffect, useRef } from 'react'
2| import { MCPSettings } from '../../components/mcp/index.js'
3| import { MCPReconnect } from '../../components/mcp/MCPReconnect.js'
4| import { useMcpToggleEnabled } from '../../services/mcp/MCPConnectionManager.js'
5| import { useAppState } from '../../state/AppState.js'
6| import type { LocalJSXCommandOnDone } from '../../types/command.js'
7| import { PluginSettings } from '../plugin/PluginSettings.js'
8|
9| // TODO: This is a hack to get the context value from toggleMcpServer (useContext only works in a component)
CLI:registerMcpAddCommand
commands/mcp/addCommand.ts 服务 claude mcp add(Commander.js),非 REPL 斜杠:
- 子命令
add <name> <commandOrUrl> [args...] - options:--scope local|user|project、--transport stdio|sse|http、-e env、-H header、OAuth client-id/secret、--xaa(SEP-990 gated)
- 调用 addMcpConfig、saveMcpClientSecret、readClientSecret
- 示例在 description 中内联(HTTP、stdio、header)
xaaIdpCommand.ts Companion:XAA IdP setup 流程。
Slash /mcp 与 CLI mcp add 最终都写同一 mcp config 文件(describeMcpConfigFilePath),但 UX 与 validation 路径分离——便于 headless CI 预置服务器。
源码引用: src/commands/mcp/addCommand.ts · 第 30–80 行(共 281 行)
30| /**
31| * Registers the `mcp add` subcommand on the given Commander command.
32| */
33| export function registerMcpAddCommand(mcp: Command): void {
34| mcp
35| .command('add <name> <commandOrUrl> [args...]')
36| .description(
37| 'Add an MCP server to Claude Code.\n\n' +
38| 'Examples:\n' +
39| ' # Add HTTP server:\n' +
40| ' claude mcp add --transport http sentry https://mcp.sentry.dev/mcp\n\n' +
41| ' # Add HTTP server with headers:\n' +
42| ' claude mcp add --transport http corridor https://app.corridor.dev/api/mcp --header "Authorization: Bearer ..."\n\n' +
43| ' # Add stdio server with environment variables:\n' +
44| ' claude mcp add -e API_KEY=xxx my-server -- npx my-mcp-server\n\n' +
45| ' # Add stdio server with subprocess flags:\n' +
46| ' claude mcp add my-server -- my-command --some-flag arg1',
47| )
48| .option(
49| '-s, --scope <scope>',
50| 'Configuration scope (local, user, or project)',
51| 'local',
52| )
53| .option(
54| '-t, --transport <transport>',
55| 'Transport type (stdio, sse, http). Defaults to stdio if not specified.',
56| )
57| .option(
58| '-e, --env <env...>',
59| 'Set environment variables (e.g. -e KEY=value)',
60| )
61| .option(
62| '-H, --header <header...>',
63| 'Set WebSocket headers (e.g. -H "X-Api-Key: abc123" -H "X-Custom: value")',
64| )
65| .option('--client-id <clientId>', 'OAuth client ID for HTTP/SSE servers')
66| .option(
67| '--client-secret',
68| 'Prompt for OAuth client secret (or set MCP_CLIENT_SECRET env var)',
69| )
70| .option(
71| '--callback-port <port>',
72| 'Fixed port for OAuth callback (for servers requiring pre-registered redirect URIs)',
73| )
74| .helpOption('-h, --help', 'Display help for command')
75| .addOption(
76| new Option(
77| '--xaa',
78| "Enable XAA (SEP-990) for this server. Requires 'claude mcp xaa setup' first. Also requires --client-id and --client-secret (for the MCP server's AS).",
79| ).hideHelp(!isXaaEnabled()),
80| )
getMcpSkillCommands 与 SkillTool
commands.ts getMcpSkillCommands(mcpCommands):
当 MCP_SKILLS feature 开启,filter:
- type === 'prompt'
- loadedFrom === 'mcp'
- !disableModelInvocation
这些 command 对象由 MCP 服务器 prompts/list 或等价能力填充,不在 getCommands(cwd) 返回数组中。SkillTool 组装 schema 时需 merge 主池 + MCP skills。
用户 /mcp 管理的是 connection;MCP prompt 命令随连接动态出现/消失。reload plugins / reconnect 后应 clearCommandMemoizationCaches 刷新 typeahead。
源码引用: src/commands.ts · 第 541–559 行(共 755 行)
541| /**
542| * Filter AppState.mcp.commands to MCP-provided skills (prompt-type,
543| * model-invocable, loaded from MCP). These live outside getCommands() so
544| * callers that need MCP skills in their skill index thread them through
545| * separately.
546| */
547| export function getMcpSkillCommands(
548| mcpCommands: readonly Command[],
549| ): readonly Command[] {
550| if (feature('MCP_SKILLS')) {
551| return mcpCommands.filter(
552| cmd =>
553| cmd.type === 'prompt' &&
554| cmd.loadedFrom === 'mcp' &&
555| !cmd.disableModelInvocation,
556| )
557| }
558| return []
559| }
权限与 hooks 交叉点
MCP 工具执行走 hooks/useCanUseTool + permissions 规则引擎:
- 工具名
mcp__server__tool三段式命名约定(server 名 sanitize 后嵌入) - /mcp disable 后客户端 type=disabled,getTools 过滤掉其工具
- PreToolUse hook 仍可拦截单个 MCP tool call
/mcp 本身不触发 tool use;但 MCPSettings 内「Test connection」可能 spawn 子进程。Remote 模式不含 /mcp——mobile 用户无法 mid-flight 改 MCP 配置,需在 desktop session 完成。
与 commands/plugin/ 重叠:ant 构建将 MCP 并入 plugin 管理,反映产品策略「扩展统一入口」。
配置 scope 与多文件合并
MCP 配置按 scope 分层写入(local / user / project),与 Claude settings 体系一致:
| scope | 典型路径语义 |
|---|---|
| local | 当前工作目录 .mcp.json,不提交 git |
| user | ~/.claude.json 内 mcpServers 段 |
| project | 仓库内 .mcp.json 或 claude 项目配置 |
claude mcp add -s project 与 /mcp UI 编辑同一底层 addMcpConfig。启动时 MCPConnectionManager 合并各 scope,后加载覆盖先加载的同名 server。OAuth 回调端口 --callback-port 解决预注册 redirect URI 场景;client secret 可读 MCP_CLIENT_SECRET env 避免交互 prompt。
读连接失败 bug 时:先 /mcp reconnect,再查 transport(stdio vs http)与 header 是否随 config 持久化;slash enable 只改 enabled flag,不改 command 行。IDE 类型 server 永远不参与 bulk toggle,避免误关编辑器桥接。详见 services 章 mcp-client。
源码缺失
File not found: c:\Users\123\Desktop\jd\Claude code源码\claude-code-complete\claude-code-complete\src-readable\services\mcp\config.js
本章小结与延伸
/mcp = MCP 连接的用户控制面。下一章 compact-memory-commands,读上下文压缩与 CLAUDE.md 记忆编辑。 继续学习: