Files
claude-code/docs/slash-command-mcp-routing.md
unraid 95fece4b51 feat: 整合功能恢复与技能学习闭环(含 ECC v2.1 parity + Opus 4.7 接入 + prompt 工程优化)
主要变更:
- Skill Learning 闭环系统 (9/9 AC)
- Opus 4.7 模型层接入 + adaptive thinking
- Prompt 工程优化 (64 审计测试)
- Agent Teams 简化门控 (默认启用)
- Windows Terminal 后端修复 (EncodedCommand/WT_SESSION)
- TF-IDF 技能搜索精准化 (字段加权/CJK 优化)
- Autonomy 系统 (/autonomy 命令)
- ACP 协议完整实现
- mock.module 泄漏修复 (CI 全绿)
- 152+ lint/type 修复
2026-04-22 16:07:42 +08:00

280 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# `/mcp` 斜杠命令路由机制
本文档描述用户在 REPL 交互模式下输入 `/mcp` 时,命令如何被解析、查找、分发,以及如何通过 React 状态机渲染交互式子项界面。
## 架构概览
```
用户输入 /mcp [args]
┌─────────────────────────────────┐
│ 第一层:斜杠命令解析 │
│ slashCommandParsing.ts │
│ parseSlashCommand() │
│ → commandName + args 拆分 │
└──────────────┬──────────────────┘
┌─────────────────────────────────┐
│ 第二层:命令查找与加载 │
│ commands.ts → findCommand() │
│ commands/mcp/index.ts │
│ → 懒加载 mcp.tsx 模块 │
└──────────────┬──────────────────┘
┌─────────────────────────────────┐
│ 第三层:命令处理器分发 │
│ commands/mcp/mcp.tsx → call() │
│ → 根据 args 决定渲染哪个组件 │
└──────────────┬──────────────────┘
┌─────────────────────────────────┐
│ 第四层:交互式 UI 状态机 │
│ MCPSettings → viewState 切换 │
│ MCPListPanel → 列表导航 │
│ MCPStdioServerMenu / │
│ MCPRemoteServerMenu → 操作菜单 │
└─────────────────────────────────┘
```
## 第一层:斜杠命令解析
**文件**: `src/utils/slashCommandParsing.ts`
`parseSlashCommand()` 负责将用户的原始输入拆分为命令名和参数:
```typescript
parseSlashCommand('/mcp')
// → { commandName: 'mcp', args: '', isMcp: false }
parseSlashCommand('/mcp enable sorftime')
// → { commandName: 'mcp', args: 'enable sorftime', isMcp: false }
parseSlashCommand('/mcp:tool (MCP) arg1')
// → { commandName: 'mcp:tool (MCP)', args: 'arg1', isMcp: true }
```
解析规则:
-`/` 后的第一个词作为 `commandName`
- 剩余部分整体作为 `args` 字符串
- 如果第二个词是 `(MCP)`,则拼入 `commandName` 并标记 `isMcp: true`
- 解析器**不处理子命令层级**,子命令路由由各命令处理器自行实现
## 第二层:命令查找与加载
### 命令注册
**文件**: `src/commands/mcp/index.ts`
```typescript
const mcp = {
type: 'local-jsx', // 本地 JSX 组件命令,不经过 AI
name: 'mcp',
description: 'Manage MCP servers',
immediate: true, // 直接执行,不需要 AI 处理
argumentHint: '[enable|disable [server-name]]',
load: () => import('./mcp.js'), // 懒加载处理器
} satisfies Command
```
### 命令查找
**文件**: `src/commands.ts`
`findCommand()` 在全局 `COMMANDS` 列表中按 `name``aliases` 精确匹配:
```typescript
export function findCommand(commandName: string, commands: Command[]): Command | undefined {
return commands.find(
_ => _.name === commandName ||
getCommandName(_) === commandName ||
_.aliases?.includes(commandName),
);
}
```
全局命令列表由 `COMMANDS()` 函数memoized构建`mcp` 是其中之一。
### 命令执行入口
**文件**: `src/utils/processUserInput/processSlashCommand.tsx`
`processSlashCommand` 调用 `findCommand` 找到命令后:
1.`local-jsx` 类型命令,调用 `load()` 懒加载模块
2. 调用模块导出的 `call(onDone, context, args)` 函数
3. 返回的 React 节点由 Ink 渲染到终端
## 第三层:命令处理器分发
**文件**: `src/commands/mcp/mcp.tsx`
`call()` 函数根据 `args` 参数手动路由到不同的子功能:
```typescript
export async function call(onDone, _context, args?: string): Promise<React.ReactNode> {
if (args) {
const parts = args.trim().split(/\s+/);
// /mcp no-redirect → 绕过 ant 用户重定向,直接显示 MCP 设置
if (parts[0] === 'no-redirect') {
return <MCPSettings onComplete={onDone} />;
}
// /mcp reconnect <server-name> → 重连指定服务器
if (parts[0] === 'reconnect' && parts[1]) {
return <MCPReconnect serverName={parts.slice(1).join(' ')} onComplete={onDone} />;
}
// /mcp enable [server-name|all] → 启用服务器
// /mcp disable [server-name|all] → 禁用服务器
if (parts[0] === 'enable' || parts[0] === 'disable') {
return <MCPToggle
action={parts[0]}
target={parts.length > 1 ? parts.slice(1).join(' ') : 'all'}
onComplete={onDone}
/>;
}
}
// /mcp (无参数) → ant 用户重定向到 /plugins其他用户显示 MCPSettings
if (process.env.USER_TYPE === 'ant') {
return <PluginSettings onComplete={onDone} args="manage" showMcpRedirectMessage />;
}
return <MCPSettings onComplete={onDone} />;
}
```
### 子命令映射表
| 输入 | 路由目标 | 说明 |
|------|---------|------|
| `/mcp` | `<MCPSettings>` | 交互式服务器管理 UI |
| `/mcp no-redirect` | `<MCPSettings>` | 绕过 ant 重定向 |
| `/mcp reconnect <name>` | `<MCPReconnect>` | 重连指定服务器 |
| `/mcp enable [name]` | `<MCPToggle action="enable">` | 启用服务器(默认 all |
| `/mcp disable [name]` | `<MCPToggle action="disable">` | 禁用服务器(默认 all |
### MCPToggle 组件
`MCPToggle` 是一个无 UI 的效果组件(返回 `null`),通过 `useEffect` 执行一次性操作:
1.`appState.mcp.clients` 中筛选目标服务器(排除 `ide`
2. 调用 `toggleMcpServer(name)` 切换启用状态
3. 通过 `onComplete` 回调返回结果消息
## 第四层:交互式 UI 状态机
### MCPSettings — 视图控制器
**文件**: `src/components/mcp/MCPSettings.tsx`
`MCPSettings` 是整个交互式界面的控制器,用 React state 驱动一个 5 状态的视图状态机:
```typescript
type MCPViewState =
| { type: 'list'; defaultTab?: string }
| { type: 'server-menu'; server: ServerInfo }
| { type: 'server-tools'; server: ServerInfo }
| { type: 'server-tool-detail'; server: ServerInfo; toolIndex: number }
| { type: 'agent-server-menu'; agentServer: AgentMcpServerInfo }
```
状态转换图:
```
list ──(选中普通服务器)──→ server-menu ──(查看工具)──→ server-tools ──(选中工具)──→ server-tool-detail
│ │ │ │
│ └──(Esc/返回)──→ list └──(返回)──→ server-menu └──(返回)──→ server-tools
└──(选中 Agent 服务器)──→ agent-server-menu
└──(Esc/返回)──→ list
```
### MCPSettings 数据准备
组件启动时:
1.`appState.mcp.clients` 获取所有 MCP 客户端,过滤掉 `ide` 类型
2. 按传输类型stdio/sse/http/claudeai-proxy分类
3. 对远程服务器检查 OAuth 认证状态
4.`appState.agentDefinitions` 提取 Agent 专属 MCP 服务器
5. 若无任何服务器,直接调用 `onComplete` 显示提示信息
### MCPListPanel — 服务器列表
**文件**: `src/components/mcp/MCPListPanel.tsx`
这是用户看到的"子项选择"界面,负责:
**分组与排序**
```
Project MCPs (.mcp.json) ← scope: project
Local MCPs (settings.local.json) ← scope: local
User MCPs (settings.json) ← scope: user
Enterprise MCPs ← scope: enterprise
claude.ai ← type: claudeai-proxy
Agent MCPs ← 来自 agent 定义
Built-in MCPs (always available) ← scope: dynamic
```
**状态图标**
| 状态 | 图标 | 文字 |
|------|------|------|
| `connected` | ✓ (绿色) | connected |
| `disabled` | ○ (灰色) | disabled |
| `pending` | ○ (灰色) | connecting… / reconnecting (n/m)… |
| `needs-auth` | △ (黄色) | needs authentication |
| `failed` | ✗ (红色) | failed |
**键盘交互**
- `↑↓` — 在扁平列表中上下移动光标(`selectedIndex`
- `Enter` — 选中当前项,触发 `onSelectServer(server)``setViewState({ type: 'server-menu', server })`
- `Esc` — 退出,调用 `onComplete('MCP dialog dismissed')`
### 子菜单组件
选中某个服务器后,根据传输类型渲染不同的操作菜单:
| 传输类型 | 组件 | 可用操作 |
|---------|------|---------|
| `stdio` | `MCPStdioServerMenu` | 启用/禁用、重连、查看工具、删除 |
| `sse` / `http` | `MCPRemoteServerMenu` | 认证、启用/禁用、重连、查看工具、删除 |
| Agent | `MCPAgentServerMenu` | 查看 Agent 配置信息 |
## 与 CLI 模式的对比
REPL 斜杠命令和 CLI 参数模式对 `mcp` 子命令的处理方式完全不同:
| 维度 | REPL `/mcp` | CLI `claude mcp` |
|------|------------|-----------------|
| 定义位置 | `commands/mcp/index.ts` + `mcp.tsx` | `main.tsx:4677-4757` (Commander.js) |
| 子命令路由 | `call()` 内手动 `args.split()` | Commander.js `.command()` 链式注册 |
| 子命令集合 | enable, disable, reconnect, no-redirect | serve, add, remove, list, get, add-json, add-from-claude-desktop, reset-project-choices |
| 交互方式 | Ink React 组件(键盘导航) | 一次性执行并退出 |
| 处理器 | React 组件 (`MCPSettings`, `MCPToggle`) | async handler 函数 (`cli/handlers/mcp.tsx`) |
两套子命令几乎没有重叠——REPL 侧重运行时交互(启用/禁用/浏览CLI 侧重配置管理(添加/删除/列出)。
## 关键文件索引
| 文件 | 职责 |
|------|------|
| `src/utils/slashCommandParsing.ts` | 斜杠命令输入解析 |
| `src/utils/processUserInput/processSlashCommand.tsx` | 斜杠命令执行入口 |
| `src/commands.ts` | 全局命令注册与查找 (`findCommand`) |
| `src/commands/mcp/index.ts` | `/mcp` 命令定义type, name, load |
| `src/commands/mcp/mcp.tsx` | `/mcp` 处理器args 分发 + MCPToggle 组件 |
| `src/components/mcp/MCPSettings.tsx` | 交互式 UI 状态机控制器 |
| `src/components/mcp/MCPListPanel.tsx` | 服务器列表与键盘导航 |
| `src/components/mcp/MCPStdioServerMenu.tsx` | stdio 服务器操作菜单 |
| `src/components/mcp/MCPRemoteServerMenu.tsx` | 远程服务器操作菜单 |
| `src/components/mcp/MCPAgentServerMenu.tsx` | Agent MCP 服务器菜单 |
| `src/components/mcp/MCPToolListView.tsx` | 工具列表视图 |
| `src/components/mcp/MCPToolDetailView.tsx` | 工具详情视图 |
| `src/main.tsx:4677-4757` | CLI 模式 `claude mcp` 子命令注册 |
| `src/cli/handlers/mcp.tsx` | CLI 模式 handler 实现 |