Claude Code SDK #8:MCP 集成全解——三种接入方式 × 工具命名规范 × Tool Search 懒加载,把 Agent 能力边界推到任意外部服务

Claude Code SDK #8:MCP 集成全解——三种接入方式 × 工具命名规范 × Tool Search 懒加载,把 Agent 能力边界推到任意外部服务

MCP 是 Claude Code SDK 把 Agent 能力边界从「内置 11 工具」扩展到任意外部服务的核心机制。本篇完整拆解三种传输方式的选型逻辑(stdio/HTTP-SSE/SDK内嵌)、工具命名规范 mcp__<server>__<tool>、allowedTools 与 permissionMode 的对比、三种认证方式、连接状态检测,以及大量工具时的 Tool Search 懒加载机制,附 Python/TypeScript 完整代码示例和五条实践建议。

Claude Code SDK 每日技术拆解
June 1, 2026 · 9:03 AM
3 subscriptions · 42 items

Research Brief

一个 Agent,内置 11 种工具——Read、Write、Bash、WebSearch……已经够强了。但如果你需要查 GitHub Issues、查 Postgres 数据库、操控浏览器?
不需要自己写工具执行器。Claude Code SDK 支持 MCP(Model Context Protocol),一个开放标准,让你用几行配置接入数百个现成服务。1
本篇拆解 SDK 的 MCP 集成机制:三种传输方式的选型逻辑、容易踩坑的工具命名规范、权限控制与 permissionMode 的对比,以及大量工具时必须理解的 Tool Search 懒加载机制。

为什么 MCP 不是"可选插件"

内置工具覆盖了文件系统和 shell,但 Agent 真正要自主处理业务时,外部服务调用几乎是刚需:查数据库、提 PR、发通知、操控 UI。
如果用 Client SDK 手写这些工具,你需要实现完整的工具循环——拦截 tool_use 块、调用外部 API、把结果塞回 tool_result。MCP 把这套胶水代码变成了一个标准接口:你只需要配置服务器地址,SDK 自动处理工具发现和执行。2
目前 MCP 服务目录里已有数十个官方维护的服务器,涵盖 GitHub、Postgres、Slack、Filesystem 等主流场景,直接 npx 启动即用。

工具命名规范:最容易忽略的细节

MCP 工具在 SDK 里遵循固定命名格式:
mcp__<服务器名>__<工具名>
比如,你把服务器命名为 github,它提供了 list_issues 工具,那这个工具在 allowedTools 里的写法就是 mcp__github__list_issues
这个命名规则直接决定了权限配置能否生效。如果服务器名拼错一个字符,Claude 能看到工具但调用时权限不通过——这是很多人初次接入 MCP 时卡住的地方。
支持通配符:mcp__github__* 表示开放该服务器的全部工具,不需要逐一列举。

三种传输方式

MCP 服务器通过三种方式与你的 Agent 通信,选哪种取决于服务器的部署形式。3
服务器机房中闪烁的 LED 指示灯与数据机柜
MCP 服务器可以运行在本地机器、远程云端或直接嵌入应用进程 3
判断依据很简单:
  • 文档给你的是命令行命令(比如 npx @modelcontextprotocol/server-github)→ 用 stdio
  • 文档给你的是URL→ 用 HTTP 或 SSE
  • 你想直接在代码里定义工具函数→ 用 SDK 内嵌服务器

stdio:本地子进程

最常见的形式,MCP 服务器作为本地进程运行,通过 stdin/stdout 通信:
# Python
from claude_agent_sdk import query, ClaudeAgentOptions

async for message in query(
    prompt="列出 anthropics/claude-code 最近 3 个 Issues",
    options=ClaudeAgentOptions(
        mcp_servers={
            "github": {
                "command": "npx",
                "args": ["-y", "@modelcontextprotocol/server-github"],
                "env": {"GITHUB_TOKEN": os.environ["GITHUB_TOKEN"]}
            }
        },
        allowed_tools=["mcp__github__list_issues"]
    )
):
    ...
// TypeScript
for await (const message of query({
    prompt: "列出最近 3 个 Issues",
    options: {
        mcpServers: {
            github: {
                command: "npx",
                args: ["-y", "@modelcontextprotocol/server-github"],
                env: { GITHUB_TOKEN: process.env.GITHUB_TOKEN }
            }
        },
        allowedTools: ["mcp__github__list_issues"]
    }
})) { ... }

HTTP / SSE:云端服务

适合云托管的 MCP 服务和远程 API:
options=ClaudeAgentOptions(
    mcp_servers={
        "remote-api": {
            "type": "sse",    # 或 "http" 用于 Streamable HTTP
            "url": "https://api.example.com/mcp/sse",
            "headers": {
                "Authorization": f"Bearer {os.environ['API_TOKEN']}"
            }
        }
    },
    allowed_tools=["mcp__remote-api__*"]
)
注意:JSON 配置文件(.mcp.json)里可以用 "streamable-http" 作为 "http" 的别名,但代码里只接受 "http"。混淆这两者会导致连接静默失败。

SDK 内嵌服务器(Python 专属)

@tool 装饰器直接在 Python 代码里定义工具,不需要启动额外进程——开发调试时非常实用:
from claude_agent_sdk import tool, create_sdk_mcp_server

@tool("query_db", "按自然语言查询数据库", {"question": str})
async def query_db(args):
    sql = translate_to_sql(args["question"])
    result = await db.execute(sql)
    return {"content": [{"type": "text", "text": str(result)}]}

my_server = create_sdk_mcp_server(
    name="db-tools",
    tools=[query_db]
)

options = ClaudeAgentOptions(
    mcp_servers={"db": my_server},
    allowed_tools=["mcp__db__query_db"]
)
Python SDK 里 create_sdk_mcp_server() 返回 McpSdkServerConfigtype="sdk"),直接传给 mcp_servers 即可。

权限控制:为什么不应该依赖 permissionMode

很多人的第一反应是把 permission_mode 设成 acceptEditsbypassPermissions 来「放通」MCP 工具。但官方文档明确指出这两种方式都有问题:4
网络安全盾牌与锁图标,深蓝背景
工具权限控制是 MCP 集成安全的核心,推荐用 allowedTools 精确授权而非宽泛的权限模式 4
方式对 MCP 的实际效果问题
acceptEdits不会自动放通 MCP 工具只作用于文件编辑和 Bash 命令
bypassPermissions会放通 MCP 工具同时跳过所有其他安全检查,范围太大
allowedTools 通配符精准放通目标服务器✅ 推荐方式
正确做法是用 allowedTools 配合通配符:
allowed_tools=["mcp__github__*"]   # 只开放 github 服务器
allowed_tools=["mcp__db__query"]   # 只开放 db 服务器的 query 工具
Claude 不会无限制地调用工具——它在 system init 消息里看到所有可用工具,但只有 allowedTools 里列出的才能不经审批直接调用。

认证:三种方式

环境变量(stdio)

给 stdio 服务器注入密钥的标准方式。在 .mcp.json 里可以用 ${VAR_NAME} 语法引用运行时环境变量:
{
    "mcpServers": {
        "github": {
            "command": "npx",
            "args": ["-y", "@modelcontextprotocol/server-github"],
            "env": {
                "GITHUB_TOKEN": "${GITHUB_TOKEN}"
            }
        }
    }
}

HTTP Header(HTTP/SSE)

远程服务通过 headers 字段传认证信息:
mcp_servers={
    "secure-api": {
        "type": "http",
        "url": "https://api.example.com/mcp",
        "headers": {"Authorization": f"Bearer {token}"}
    }
}

OAuth2

SDK 不自动处理 OAuth 流程,但支持在 OAuth 完成后把 access token 塞进 header:
access_token = await complete_oauth_flow()
mcp_servers={
    "oauth-api": {
        "type": "http",
        "url": "https://api.example.com/mcp",
        "headers": {"Authorization": f"Bearer {access_token}"}
    }
}
OAuth 流程本身由你的应用负责,SDK 只处理 token 之后的通信。

连接状态检测与错误处理

MCP 服务器在每次 query() 开始时初始化。连接结果通过 system init 消息返回,是唯一可靠的状态检查入口:5
async for message in query(prompt="...", options=options):
    # 检查连接状态
    if hasattr(message, 'subtype') and message.subtype == "init":
        servers = message.data.get("mcp_servers", [])
        failed = [s for s in servers if s.get("status") != "connected"]
        if failed:
            print(f"连接失败: {failed}")
            break

if hasattr(message, 'result'):
        print(message.result)
TypeScript 侧写法一致:
if (message.type === "system" && message.subtype === "init") {
    const failed = message.mcp_servers.filter(s => s.status !== "connected");
    if (failed.length > 0) console.warn("Failed:", failed);
}
ClaudeSDKClient 还提供了两个动态方法:
  • get_mcp_status():随时获取所有服务器的连接状态
  • reconnect_mcp_server(name):对失败的服务器触发重连
  • toggle_mcp_server(name, enabled):在会话中动态启停某个服务器
常见失败原因:环境变量未设置(stdio 最常见)、npm 包未安装、远程 URL 不可达、网络防火墙拦截。

Loading content card…

Tool Search:上百工具时的上下文管理

接入多个 MCP 服务器后,工具数量可能迅速膨胀。50 个工具定义大约占 10-20k tokens 上下文,工具选择准确率也会在超过 30-50 个工具时明显下降。6
SDK 默认开启 Tool Search:不把所有工具定义一次性塞进上下文,而是按需检索。工作流程是:
  1. Agent 遇到需要外部能力的任务
  2. 向工具目录发起语义搜索
  3. 加载最相关的 3-5 个工具定义
  4. 这些工具在后续轮次中保持可用
这比每次加载所有定义多一个「搜索」round-trip,但对大型工具集来说,减小的上下文开销通常值得。
可以通过环境变量精细控制行为:
ClaudeAgentOptions(
    mcp_servers={ ... },
    env={
        # auto:5 = 工具定义超过上下文窗口 5% 时启动懒加载
        "ENABLE_TOOL_SEARCH": "auto:5"
    }
)
ENABLE_TOOL_SEARCH效果
未设置(默认)开启,在 Vertex AI 和非官方代理上回退为全量加载
true强制开启(即使在 Vertex AI)
auto超过上下文 10% 时自动开启
auto:N超过 N% 时开启
false关闭,全量加载所有工具定义
工具少于 10 个时,全量加载通常更快;超过 30 个时建议评估是否启用懒加载。
工具描述质量直接影响搜索召回search_slack_messagesquery_slack 匹配范围更广,描述写"按关键词、频道或日期搜索 Slack 消息"比"查询 Slack"精确得多。

发现可用工具

不知道一个 MCP 服务器提供哪些工具?从 system init 消息里读:
async for message in query(prompt="...", options=options):
    if hasattr(message, 'subtype') and message.subtype == "init":
        mcp_info = message.data.get("mcp_servers", [])
        for server in mcp_info:
            print(f"服务器: {server['name']}")
            for tool in server.get('tools', []):
                print(f"  - {tool['name']}: {tool.get('description', '')}")
ClaudeSDKClient 还可以在会话中随时调用 get_mcp_status() 获取每个服务器的工具列表和连接状态。

实践建议

1. 工具权限最小化
不要用 mcp__*__* 通配符一次放通所有 MCP 工具。对每个服务器单独配置通配符(mcp__github__*),或精确到工具名(mcp__db__query),便于审计和限制意外调用。
2. 总是检查 init 消息
MCP 连接失败是静默的——Claude 只是无法调用这些工具。在生产代码里,总应该在 init 消息里检查连接状态,而不是等到任务失败时才发现服务器没接上。
3. 分离只读和写操作
通过 allowedTools 的精细配置,可以让 Agent 只拥有读权限的工具(比如 mcp__db__query),而不开放写操作(mcp__db__execute)。这和上一篇子 Agent 里工具最小化的原则一致。
4. 调试时用 stdin 打印 mcp_servers
接入新服务器时,先把 print(message.data.get("mcp_servers")) 加进去,确认服务器已连接、工具列表符合预期,再开始测试实际功能。
5. 超过 30 个工具时评估 Tool Search
工具多了之后,先用 ENABLE_TOOL_SEARCH=auto 跑一下,观察是否自动触发。如果工具定义较重,适当降低阈值(auto:5)。

下期 #9:自定义工具(Custom Tools)——用 @tool 装饰器在 SDK 内嵌入你自己的工具函数,以及如何用 in-process MCP 服务器在代码里定义完整工具集。文档入口:https://docs.anthropic.com/en/docs/agent-sdk/custom-tools

Add more perspectives or context around this Post.

  • Sign in to comment.