跳转到内容

MCP 客户端集成:连接生命周期与工具 URI 路由

MCP 客户端集成:连接生命周期与工具 URI 路由

学习目标

本章要解决的核心问题:当 AI 编程助手需要连接多个外部 MCP 服务器时,如何管理连接的生命周期、路由工具调用、处理认证并支持配置热更新?

你将学到:

  • MCPManagerSingleton 单例模式的多连接管理
  • mcp:// URI 协议的工具路由机制
  • MCP OAuth 认证流程
  • 配置变更时的智能连接热更新

前置知识

本章涉及 MCP 协议的通用原理,建议先阅读:

下文假设你已理解 MCP 的 Stdio/SSE 传输层和 JSON-RPC 2.0 消息协议,直接聚焦 Continue 的客户端实现。


项目实践

MCPManagerSingleton:多连接中枢

Continue 使用单例模式管理所有 MCP 连接:

class MCPManagerSingleton {
private static instance: MCPManagerSingleton;
public connections: Map<string, MCPConnection> = new Map();
createConnection(id: string, options: InternalMcpOptions): MCPConnection
getConnection(id: string): MCPConnection | undefined
async refreshConnection(id: string): Promise<void>
async setConnections(servers: InternalMcpOptions[], forceRefresh: boolean): void
async shutdown(): void
}

核心职责:

  • 创建连接:首次访问时创建,已存在则复用
  • 连接追踪:通过 id 索引所有连接
  • 批量同步setConnections 比对新旧服务器列表,删除不再需要的连接,创建新的连接
  • 状态管理:维护每个服务器的运行状态(connected, disconnected, error)
  • 生命周期shutdown 时中止所有连接并清理资源

连接热更新策略

当用户修改 Continue 配置中的 MCP 服务器列表时,setConnections 执行智能更新:

新的服务器列表(从配置读取)
遍历现有连接 → 如果不在新列表中 → 断开并删除
遍历新列表 → 如果不在现有连接中 → 创建新连接
如果已存在但配置变更 → 更新选项元数据
触发 onConnectionsRefreshed 回调 → 重载配置

关键优化:连接选项比对。Continue 在判断是否需要重建连接时,比较传输层选项(transport type、command、args 等)。只有传输层选项变化时才重建连接,仅名称或图标变化时只更新元数据。

mcp:// URI 路由

Continue 定义了一套自定义 URI 协议来路由 MCP 工具调用:

mcp://{serverId}/{toolName}

路由流程:

核心函数:

function encodeMCPToolUri(mcpId: string, toolName: string): string {
return `mcp://${encodeURIComponent(mcpId)}/${encodeURIComponent(toolName)}`;
}
function decodeMCPToolUri(uri: string): [string, string] | null {
const url = new URL(uri);
if (url.protocol !== "mcp:") return null;
return [
decodeURIComponent(url.hostname),
decodeURIComponent(url.pathname).slice(1),
];
}

callTool.ts 中,三种工具调用方式统一处理:

协议调用方式
http:// / https://HTTP POST 到外部工具服务
mcp://通过 MCPManager 路由到对应 MCP client
内置工具名直接调用 implementations/ 中的实现

OAuth 认证流程

MCP 服务器可能需要 OAuth 认证。Continue 的认证流程:

用户启用需要认证的 MCP 服务器
调用 mcp/startAuthentication
MCPManager 设置状态为 "authenticating"
启动 OAuth 流程(打开浏览器 → 用户授权 → 回调)
收到授权码 → 交换 access token
token 存储(通过 IDE 的 SecretStorage)
refreshConnection → 使用新 token 重建连接
状态更新为 "connected"

认证解除:

用户调用 mcp/removeAuthentication
清除存储的 token
refreshConnection → 以未认证状态重建连接

问题与规避

陷阱 1:MCP 连接失败导致整个配置加载失败

现象:某个 MCP 服务器不可用,Continue 的配置加载卡住。

规避:MCPManager 的连接操作都有超时和错误处理。setConnections 中使用 Promise.allSettled 批量关闭连接,确保一个连接失败不影响其他连接。refreshConnection 也有独立的 AbortController 可中止。

陷阱 2:MCP 工具调用延迟

现象:通过 MCP 调用外部工具比内置工具慢很多。

原因:MCP 通常使用 stdio 传输层,需要启动子进程。首次调用时进程启动可能有 1-2 秒延迟。

规避:MCPConnection 保持连接(进程存活),后续调用复用已有进程。但用户首次体验可能感知到启动延迟。

陷阱 3:OAuth token 过期

现象:认证过的 MCP 服务器过一段时间后连接失败。

规避:需要在连接层实现 token 刷新逻辑。如果 MCP SDK 支持 refresh token,自动刷新;否则需要用户重新认证。


设计取舍

为什么用单例模式

MCP 连接是全局资源——所有子系统(配置加载、工具调用、上下文提供者)都需要访问。单例确保:

  • 连接不会被重复创建
  • 状态变更对所有消费者可见
  • 配置重载时统一清理

替代方案:依赖注入(DI)容器。Continue 选择了更简单的单例,因为 MCPManager 的生命周期与整个应用一致,不需要更细粒度的作用域控制。

mcp:// URI vs 直接工具名引用

Continue 选择用 URI 协议来标识 MCP 工具,而非在工具表中存储 serverId + toolName 的组合。好处是:

  • 统一的工具路由:callTool 只需解析一个 URI 字符串
  • 可扩展:未来可以添加新的协议(如 http-tool://)而不改变路由逻辑
  • 序列化友好:URI 是字符串,可安全地存储在历史记录中

参考来源