ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

自己动手写一个联网MCP工具:用TaoToken统一Key打通fastmcp与duckduckgo-search

自己动手写一个联网MCP工具:用TaoToken统一Key打通fastmcp与duckduckgo-search 1. 从零构建联网 MCP 工具为什么选 stdio fastmcp duckduckgo-searchMCPModel Context Protocol是让大模型调用外部工具的开放协议而联网搜索是它最实用的落地场景之一。你可能会问模型本身不是能联网吗问题在于大多数本地模型或第三方 API 通道默认没有实时检索能力回答里全是训练截止日期之前的旧信息。自己写一个联网 MCP 工具等于给模型装上一双能实时看网页的眼睛。这个工具适合谁适合正在用 Claude Desktop、Cline、Cursor 这类支持 MCP 客户端的开发者也适合想把本地 Python 脚本升级成模型可调用工具的进阶玩家。整个方案的技术栈很轻用 fastmcp 定义工具函数用 duckduckgo-search 做免费检索后端用 stdio 作为通信方式再通过 TaoToken 统一 Key 打通模型调用通道。不需要服务器、不需要域名、不需要备案本地跑通即可。为什么是 stdio 而不是 SSE 或 HTTPstdio 是本地开发最省事的方式——客户端直接以子进程方式启动你的 Python 脚本通过标准输入输出交换 JSON-RPC 消息。没有端口占用、没有跨域、没有鉴权调试时甚至能直接看进程日志。对于个人工具来说stdio 的启动延迟几乎为零稳定性也最好。为什么用 duckduckgo-search因为它不需要 API Key安装即用对入门者极其友好。虽然它的结果质量和稳定性不如商业搜索 API但作为跑通一次真实联网问答的目标它完全够用。等你验证完整个链路再换成 SerpAPI 或自建检索服务只需要改web_search函数内部几行代码。这里有个容易踩的坑很多人以为 MCP 工具写完就能被模型调用其实中间还差一层模型通道。MCP 客户端负责把工具描述发给模型模型决定调用哪个工具但模型本身得先能连上。如果你用的是第三方 API 通道就需要一个统一的 Key 和 Base URL 来承接模型请求。TaoToken 在这里扮演的就是这个角色——一个 Key 同时覆盖模型对话和工具调用链路省去在多个平台之间来回切换配置的麻烦。我试过把 MCP 工具和模型通道分开配置结果调试时经常分不清是工具报错还是模型没连上。统一通道之后排障路径清晰很多先确认模型能正常对话再确认工具能被列出最后确认工具能被调用。这个顺序能帮你省下大量时间。接下来的内容会按环境准备 → 写 server.py → 配置模型通道 → 本地联调验证 → 常见报错排查的顺序展开每一步都给出可复制的命令和配置。目标很明确让你在本地跑通一次模型自主决定搜索 → 拿到实时结果 → 生成带来源的回答的完整流程。2. TaoToken 前置准备统一 Key 与 API 通道配置在写 MCP 工具之前先把模型通道准备好。这一步经常被跳过导致后面工具写完了却不知道怎么让模型用上。TaoToken 的作用是提供一个统一的 API 入口你只需要一个 Key就能同时完成模型对话和工具调用链路的对接。先注册并拿到 Key。打开官网 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 完成账号注册后进入控制台。控制台地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 在API Keys页面创建一个新的 Key。创建时建议给它起个能认出来的名字比如mcp-search-dev方便后续区分不同用途的 Key。拿到 Key 之后你需要记住两个核心信息Base URL 和 Key 本身。Base URL 是https://taotoken.net/api注意这个地址不带任何查询参数直接作为 API 根路径使用。Key 的格式通常是一串以sk-开头的字符串复制后先存到本地环境变量里不要硬编码进代码。在终端里设置环境变量macOS 和 Linux 用export TAOTOKEN_API_KEYsk-你的实际Key export TAOTOKEN_BASE_URLhttps://taotoken.net/apiWindows PowerShell 用$env:TAOTOKEN_API_KEYsk-你的实际Key $env:TAOTOKEN_BASE_URLhttps://taotoken.net/api设置完可以用echo $TAOTOKEN_API_KEYWindows 用echo $env:TAOTOKEN_API_KEY确认一下是否生效。如果输出为空说明环境变量没设置成功检查一下是否在正确的终端会话里执行。接下来验证模型通道是否可用。用 curl 发一个最简单的对话请求curl https://taotoken.net/api/v1/chat/completions \ -H Content-Type: application/json \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -d { model: claude-3-5-sonnet-20241022, messages: [{role: user, content: 回复两个字收到}], max_tokens: 20 }如果返回的 JSON 里有choices字段且内容包含收到说明模型通道已经打通。如果返回 401说明 Key 不对或没带上如果返回 404检查 Base URL 是否写成了https://taotoken.net/api而不是带/v1的完整路径——不同客户端对路径拼接的处理方式不同这一点后面配置 MCP 客户端时还会遇到。模型 ID 的选择上建议先用一个你熟悉的模型跑通链路比如claude-3-5-sonnet-20241022或gpt-4o。等工具联调成功后再换成你日常用的模型。模型 ID 的完整列表可以在接入文档里查到 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。这里有个细节值得注意MCP 工具本身不直接调用模型它只负责被模型调用。模型通道的配置是在 MCP 客户端那一侧完成的。所以你现在配置的 TaoToken Key 和 Base URL后面要填到 Claude Desktop 或 Cline 的配置文件里而不是填到 server.py 里。server.py 只关心搜索逻辑不关心模型是谁。如果你打算长期做编码类 Agent 开发可以考虑 Coding Plan 方案它针对高频工具调用场景做了通道优化 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。不过对于本篇的入门目标按量付费的 API Key 就足够了。3. 可复制配置server.py 与依赖清单完整实现现在进入核心部分写 MCP 服务器。先建项目目录并安装依赖。mkdir my_mcp_search cd my_mcp_search python -m venv .venv source .venv/bin/activate # Windows 用 .venv\Scripts\activate pip install mcp[cli] fastmcp duckduckgo-search依赖说明mcp[cli]是官方 Python SDK带 CLI 工具方便调试fastmcp提供更简洁的工具注册装饰器duckduckgo-search是免费检索后端。三个包都不大安装通常在一分钟内完成。创建server.py完整代码如下from mcp.server.fastmcp import FastMCP from duckduckgo_search import DDGS mcp FastMCP(My Web Search) mcp.tool() async def web_search(query: str, max_results: int 3) - str: 在互联网上执行实时搜索并返回摘要结果。 Args: query (str): 用户的搜索关键词。 max_results (int): 返回的最大结果数量默认3。 Returns: str: 格式化的搜索结果摘要。 try: with DDGS() as ddgs: results ddgs.text(query, max_resultsmax_results) formatted_results [] for i, result in enumerate(results, 1): title result.get(title, N/A) body result.get(body, N/A) href result.get(href, #) formatted_results.append( f{i}. **{title}**\n {body}\n [来源]({href}) ) if not formatted_results: return 未找到相关搜索结果。 return \n\n.join(formatted_results) except Exception as e: return f搜索时发生错误: {str(e)} if __name__ __main__: mcp.run(transportstdio)这段代码有几个关键点。mcp.tool()装饰器把web_search注册为 MCP 工具客户端能自动读取函数签名和文档字符串生成工具描述发给模型。query: str和max_results: int 3的类型注解很重要——MCP 会据此做参数序列化和反序列化类型写错会导致调用失败。文档字符串里的 Args 和 Returns 部分会被模型读到帮助它判断何时调用这个工具。transportstdio指定通信方式为标准输入输出。注意mcp.run()会阻塞进程这是正常的——它进入事件循环等待客户端消息。不要在它后面写任何代码否则不会执行。如果你用的是较新版本的 fastmcp导入路径可能是from fastmcp import FastMCP。两个包都装了的话优先用mcp.server.fastmcp它是官方 SDK 自带的兼容性更稳。接下来配置 MCP 客户端。以 Claude Desktop 为例配置文件路径macOS:~/Library/Application Support/Claude/claude_desktop_config.jsonWindows:%APPDATA%\Claude\claude_desktop_config.json在mcpServers字段下加入你的服务器配置{ mcpServers: { my-web-search: { command: /absolute/path/to/.venv/bin/python, args: [/absolute/path/to/server.py], env: { TAOTOKEN_API_KEY: sk-你的实际Key, TAOTOKEN_BASE_URL: https://taotoken.net/api } } } }三个关键字段必须写全command指向虚拟环境里的 Python 解释器绝对路径args指向 server.py 的绝对路径env里放 TaoToken 的 Key 和 Base URL。路径必须用绝对路径相对路径在客户端启动子进程时会解析失败。如果你用的是 Cline 或 Cursor配置结构类似但字段名可能不同。Cline 的 MCP 配置在设置面板里格式是{ mcpServers: { my-web-search: { command: python, args: [/absolute/path/to/server.py], disabled: false, autoApprove: [web_search] } } }autoApprove字段可以让web_search免确认直接执行调试时很方便但生产环境建议关掉避免模型频繁调用消耗额度。模型通道的配置在客户端另一处。以 Cline 为例在 API Provider 设置里选 OpenAI CompatibleBase URL 填https://taotoken.net/apiAPI Key 填你的 TaoToken KeyModel ID 填claude-3-5-sonnet-20241022。这三件套Base URL Key Model ID必须同时正确缺一个都会导致模型无法调用工具。4. 验证请求本地 stdio 联调与成功结果确认配置写完后先别急着开客户端用官方 CLI 工具单独验证 server.py 能否正常工作。这是排障的关键一步——如果 CLI 都跑不通客户端里更不可能跑通。mcp dev server.py第一次运行会提示是否安装modelcontextprotocol/inspector输入y确认。安装完成后会自动打开浏览器地址是http://127.0.0.1:6274。这是 MCP Inspector 界面。在 Inspector 里点击 Tools → List Tools你应该能看到web_search工具及其参数描述。点击web_search在表单里填入{ query: Python 3.13 新特性, max_results: 2 }点击 Execute右侧会返回搜索结果。如果看到带标题、摘要和来源链接的格式化文本说明 server.py 本身没问题。接下来写一个 Python 客户端做端到端验证。创建test_client.pyimport asyncio from mcp import ClientSession, StdioServerParameters from mcp.client.stdio import stdio_client async def main(): server_params StdioServerParameters( commandpython, args[server.py], ) async with stdio_client(server_params) as (read, write): async with ClientSession(read, write) as session: await session.initialize() tools await session.list_tools() print(可用工具:, [t.name for t in tools.tools]) result await session.call_tool( web_search, arguments{query: MCP 协议是什么, max_results: 2}, ) print(搜索结果:\n, result.content[0].text) if __name__ __main__: asyncio.run(main())运行python test_client.py。预期输出分两部分先打印可用工具: [web_search]再打印两条搜索结果。如果工具列表为空说明mcp.tool()装饰器没生效或导入路径有问题如果调用时报错看错误信息里是搜索库的问题还是参数序列化的问题。端到端验证的最后一步是在真实客户端里测试。重启 Claude Desktop 或 Cline在对话里输入帮我搜索一下最近 Python 生态有什么值得关注的新库并给出信息来源。如果配置正确模型会先输出一段我来搜索一下之类的过渡语然后触发web_search工具调用拿到结果后生成带来源的回答。整个过程你能在客户端的工具调用面板里看到web_search的执行记录。成功的关键标志有三个工具出现在客户端的工具列表里、模型主动决定调用工具而不是直接回答、返回结果里包含实时信息。三个都满足说明整条链路——从模型通道到 MCP 工具到搜索后端——全部打通。如果模型没有调用工具而是直接回答通常是工具描述不够清晰。检查web_search的文档字符串是否说明了实时搜索和互联网这两个关键词模型靠这些判断何时该用工具。另外确认客户端的模型确实支持 function calling部分小模型不支持工具调用。5. 本篇常见错排查401、local proxy failed、reading choices 与 OAuth排障时按模型通道 → 工具注册 → 搜索后端的顺序排查能最快定位问题。401 Unauthorized模型通道鉴权失败。检查三处TaoToken Key 是否复制完整有没有漏掉尾部字符、Base URL 是否写成https://taotoken.net/api不要带/v1客户端会自动拼、请求头是否是Authorization: Bearer sk-xxx格式。如果 Key 是在环境变量里设置的确认客户端启动时能读到——Claude Desktop 的env字段就是干这个的别只在本机 shell 里 export 而忘了写进配置。local proxy failed / connection refused客户端连不上模型通道。先确认网络能访问https://taotoken.net/api用 curl 测一下。如果 curl 通但客户端不通检查客户端是否配置了额外的网络设置导致请求被拦截。另一个常见原因是 Base URL 末尾多了斜杠比如https://taotoken.net/api/某些客户端拼接路径时会变成//v1/chat/completions导致 404 而非 401报错信息可能被包装成连接失败。Error reading choices / choices field missing模型返回了非预期格式。通常是 Model ID 写错了客户端请求了一个不存在的模型通道返回错误 JSON客户端解析choices时失败。对照接入文档确认 Model ID 拼写注意大小写和日期后缀。另一个可能是max_tokens设得太小模型还没输出完整 JSON 就被截断。OAuth / authentication flow required某些客户端默认走 OAuth 流程但 TaoToken 用的是 API Key 鉴权。在客户端设置里把认证方式从 OAuth 改成 API Key或者选择 OpenAI Compatible 这类通用接口模式。Claude Code 的配置在~/.claude/settings.json需要显式指定apiKey和baseURL字段。工具列表为空server.py 启动了但工具没注册。检查mcp.tool()装饰器是否在mcp FastMCP(...)之后、mcp.run()之前。如果用了if __name__ __main__保护确认客户端启动的确实是这个文件。还有一种情况是虚拟环境路径写错客户端用系统 Python 启动找不到安装的包进程直接退出工具列表自然为空。搜索返回空结果duckduckgo-search 被限流或查询词太生僻。换一个常见查询词试试比如Python tutorial。如果持续为空可能是网络环境导致 DuckDuckGo 不可达这种情况需要换检索后端但那是另一个话题了。stdio 进程启动后立即退出看客户端日志里子进程的 stderr 输出。常见原因是mcp.run()之前有语法错误或者依赖没装全。在终端里直接python server.py跑一下如果报ModuleNotFoundError说明虚拟环境没激活或包装错了地方。排查时养成看日志的习惯。Claude Desktop 的日志在~/Library/Logs/Claude/macOSCline 在 VS Code 的输出面板里选 Cline 频道。日志里会打印子进程的启动命令和 stderr大部分问题看一眼日志就能定位。6. 继续深入从跑通到好用下一步可以做什么跑通一次联网问答只是起点。接下来你可以从三个方向继续打磨这个工具。第一换检索后端。duckduckgo-search 适合入门但结果质量和稳定性有限。把web_search函数内部的DDGS()换成 SerpAPI、Bing Search API 或自建检索服务接口签名保持不变模型侧完全无感知。这就是 MCP 协议的好处——工具实现和模型调用解耦。第二加更多工具。除了搜索你还可以加fetch_url抓取指定网页正文、get_news按关键词拉新闻摘要、search_github搜代码仓库。每个工具就是一个带mcp.tool()的异步函数注册完重启客户端就能用。工具多了之后模型会自动根据用户意图选择调用哪个。第三优化工具描述。模型决定调不调用工具全靠文档字符串。把在互联网上执行实时搜索改成当用户询问最新事件、实时数据或需要外部信息时调用此工具搜索互联网能显著提升触发准确率。参数描述也可以写得更具体比如max_results注明建议 3-5过多会稀释关键信息。如果你打算把这个工具用在长期编码或 Agent 场景建议把模型通道切到 Coding Plan它在高频工具调用下的稳定性和成本控制更好 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。日常调试和验证模型行为时用模型对话页面快速测试更顺手 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite 。最后提醒一个实用技巧把 server.py 里的搜索逻辑和格式化逻辑拆成两个函数。搜索函数只负责拿原始结果格式化函数负责转成模型友好的文本。这样换后端时只改搜索函数格式化逻辑复用测试也更容易写。工具函数本身保持薄薄一层只做参数校验和调用编排复杂逻辑下沉到独立模块。这个结构在工具数量增长后优势会非常明显。
返回列表