§08 自定义工具与 MCP:把外部能力交给 Agent

有回产品经理想让 Agent 直接查订单库、发站内信。内置工具做不到,我第一次认真翻 MCP 文档。才发现它是给 Agent 装外挂的标准插槽。这章手把手接一个。

08.1 MCP 是给 Agent 装外挂

内置工具够用,但不够。你想让 Agent 查数据库、发邮件、调你的内部 API?

这些靠 MCP(Model Context Protocol)接进来。SDK 里用 createSdkMcpServer 把你的函数包成一个"服务器",传给 query 就行。

我管 MCP 叫"外挂插槽"。插上,Agent 就会用。

08.2 三步定义一个工具

# weather_tool.py —— 给 Agent 一个查天气的工具
from claude_agent_sdk import tool, create_sdk_mcp_server

@tool(
    "get_temperature",                      # 工具名
    "查某个位置的当前温度",                  # 描述,Claude 靠它决定何时调用
    {"latitude": float, "longitude": float}, # 输入参数
)
async def get_temperature(args):
    # 这行是关键:真正去调天气 API
    data = await fetch_api(args["latitude"], args["longitude"])
    return {"content": [{"type": "text", "text": f"温度:{data}°F"}]}

# 包成服务器
weather_server = create_sdk_mcp_server(
    name="weather", version="1.0.0", tools=[get_temperature]
)

工具的描述特别重要。Claude 靠描述判断"这件事该不该调这个工具"。写清楚"什么时候用",它才调得准。

08.3 工具名的固定格式

接入后,工具全名长这样:

mcp__<服务器名>__<工具名>

上面的例子,全名是 mcp__weather__get_temperature。你要把它写进 allowedTools 才会自动批准。

注意:自定义工具名带前缀 mcp__

文档第 1 条坑:很多人 allowedTools 里直接写 get_temperature,结果调不动。必须写全名 mcp__weather__get_temperature,或用通配 mcp__weather__*

08.4 出错别抛异常

工具处理函数如果直接抛异常,整个 query 会挂掉。正确做法:返回 is_error: true,让 Agent 自己决定重试或换方法。

# 捕获错误,把失败当数据返回
try:
    resp = await httpx.get(args["endpoint"])
    if resp.status_code != 200:
        return {"content": [{"type": "text", "text": f"API 错误:{resp.status_code}"}],
                "is_error": True}  # 这行是关键:标记失败,Loop 继续
except Exception as e:
    return {"content": [{"type": "text", "text": f"请求失败:{e}"}], "is_error": True}

工具会接了。想让多个 Agent 一起干活?下一章讲子代理。