☰
第一个MCP服务:用TaoToken统一Key跑通fastmcp最小闭环
2026/10/1 15:08:05 网站建设 项目流程

1. 从零理解 MCP 与 fastmcp:第一个 MCP 服务到底在解决什么问题

如果你最近在折腾 AI 应用开发,大概率被 MCP 这个词刷屏了。MCP 全称 Model Context Protocol,直白点说,它就是一套让大模型能"调用外部工具"的标准协议。你可以把它想象成 USB 接口:以前每个 AI 应用想连数据库、连文件系统、连第三方 API,都得自己写一套私有对接逻辑;现在有了 MCP,工具提供方按统一协议暴露能力,AI 客户端按统一协议去发现和调用,双方解耦。

那 fastmcp 又是什么?它是 Python 生态里把 MCP 服务端封装得最轻量的框架之一。你不需要手写 JSON-RPC 的握手细节,只要用装饰器把普通 Python 函数标记成@mcp.tool(),fastmcp 就帮你生成符合 MCP 规范的服务描述、参数 schema 和调用入口。对第一次接触 MCP 的开发者来说,fastmcp 是理解"一个 MCP 服务长什么样"的最短路径。

这篇文章面向的就是第一次搭 MCP 服务的你。我会带你走完一条完整闭环:装环境、写一个带两个工具的 fastmcp 服务、本地用 STDIO 和 HTTP 两种模式启动、用 curl 和 MCP 客户端验证调用成功。同时,因为 MCP 服务里经常要调用大模型能力(比如让工具内部再去做一次语义处理),我会把 TaoToken 的统一 Key 和 API 通道接进来,这样你后面无论换哪个模型,都不用改代码里的鉴权逻辑。

先说清楚适合谁:会一点 Python、装过 pip 包、能看懂命令行输出,就足够了。不需要你懂 MCP 协议细节,也不需要你有 GPU。整个流程在一台普通开发机上十几分钟能跑通。跑通之后你会得到一个可复用的最小骨架,后面加工具就是往文件里再写几个函数的事。

我试过把 MCP 服务想复杂,结果卡在"协议到底怎么握手"上半天。后来发现 fastmcp 已经把这一层吃掉了,你真正要关心的只有两件事:工具函数写对没有,服务启动模式选对没有。剩下的交给框架。

2. TaoToken 前置准备:统一 Key 与 API 通道怎么配

在写服务代码之前,先把"模型调用通道"这件事定下来。MCP 服务本身不强制你连大模型,但真实场景里,工具函数经常需要调用 LLM,比如做一个"总结文本"的工具、一个"翻译"的工具。如果每个工具里都硬编码不同厂商的 Key 和 Base URL,维护起来会很痛苦。TaoToken 的价值就在这里:它提供一个统一的 API 入口和统一 Key,你换模型只改一个 Model ID,鉴权部分不动。

你需要准备三样东西,我把它叫"三件套":Base URL、API Key、Model ID。这三样在后面的配置片段里会反复出现,务必先拿到。

第一步,打开 TaoToken 官网 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 注册并登录。登录后进入控制台,地址是 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite 。在控制台里你能看到账户额度和可用的模型列表。

第二步,生成 API Key。进入 API Keys 页面 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite ,点新建,复制那串以sk-开头的 Key。注意:这串 Key 只显示一次,复制后先存到本地环境变量里,别直接写进代码提交到 Git。

第三步,确认 Base URL。TaoToken 的 API 入口是 https://taotoken.net/api ,注意这个地址后面不加任何 UTM 参数,直接用它作为 OpenAI 兼容的 base_url。Model ID 则从控制台的模型列表里挑一个,比如你常用某个对话模型,就把它对应的 ID 记下来。

配置方式我推荐用环境变量,这样代码里读os.environ就行,不泄露密钥。在 Linux/macOS 的~/.bashrc或~/.zshrc里加:

export TAOTOKEN_API_KEY="sk-你的Key" export TAOTOKEN_BASE_URL="https://taotoken.net/api" export TAOTOKEN_MODEL_ID="你选的模型ID"

Windows PowerShell 用户用:

$env:TAOTOKEN_API_KEY="sk-你的Key" $env:TAOTOKEN_BASE_URL="https://taotoken.net/api" $env:TAOTOKEN_MODEL_ID="你选的模型ID"

改完记得source ~/.zshrc或重开终端,然后echo $TAOTOKEN_API_KEY确认能打印出来。这一步别跳过,后面服务里读不到环境变量会直接报 401。

注意:不要把 Key 写进任何会提交到公开仓库的文件。如果你用.env文件,记得把.env加进.gitignore。

到这里前置就完成了。你手里有了统一 Key、统一 Base URL 和一个 Model ID,接下来写服务代码时,模型调用部分就靠这三样。

3. 可复制配置:fastmcp 服务代码与 settings 片段

现在进入正题,写第一个 fastmcp 服务。先装依赖:

pip install fastmcp openai

fastmcp提供服务框架,openai用来调用 TaoToken 的兼容接口。装完可以pip show fastmcp看下版本,确认装上了。

新建一个文件demo_mcp.py,把下面这段完整代码复制进去。这段代码定义了两个工具:greet做简单问候,summarize调用 TaoToken 的模型做文本总结,正好演示"工具内部调 LLM"的典型写法。

import os import asyncio import sys import fastmcp from fastmcp import FastMCP from openai import OpenAI # 全局设置:HTTP/SSE 模式监听的地址和端口 fastmcp.settings.host = "0.0.0.0" fastmcp.settings.port = 8001 # 创建 MCP 实例 mcp = FastMCP("demo.mcp") # 从环境变量读取 TaoToken 三件套 TAOTOKEN_API_KEY = os.environ.get("TAOTOKEN_API_KEY") TAOTOKEN_BASE_URL = os.environ.get("TAOTOKEN_BASE_URL", "https://taotoken.net/api") TAOTOKEN_MODEL_ID = os.environ.get("TAOTOKEN_MODEL_ID") # 初始化 OpenAI 兼容客户端,指向 TaoToken 统一通道 client = OpenAI( api_key=TAOTOKEN_API_KEY, base_url=TAOTOKEN_BASE_URL, ) @mcp.tool() def greet(name: str) -> str: """简单的问候工具""" return f"Hello, {name}" @mcp.tool() def summarize(text: str) -> str: """调用 TaoToken 统一通道的模型,对输入文本做一句话总结""" resp = client.chat.completions.create( model=TAOTOKEN_MODEL_ID, messages=[ {"role": "system", "content": "你是一个简洁的总结助手,只输出一句话总结。"}, {"role": "user", "content": text}, ], temperature = 0.3, ) return resp.choices[0].message.content async def start_server(mode: str = "stdio"): """根据指定模式启动服务""" print(f"启动MCP服务({mode}模式)...") print(f"服务名称: demo.mcp") if mode == "stdio": print("===== STDIO模式 =====") await mcp.run_stdio_async() elif mode == "http": print(f"HTTP服务地址: http://{fastmcp.settings.host}:{fastmcp.settings.port}") await mcp.run_http_async() elif mode == "sse": print(f"SSE服务地址: http://{fastmcp.settings.host}:{fastmcp.settings.port}/sse") await mcp.run_sse_async() elif mode == "streamable": print(f"Streamable HTTP服务地址: http://{fastmcp.settings.host}:{fastmcp.settings.port}") await mcp.run_streamable_http_async() else: print(f"错误: 不支持的模式 '{mode}'") print("支持的模式: stdio, http, sse, streamable") return print("按Ctrl+C停止服务") if __name__ == "__main__": mode = "stdio" if len(sys.argv) > 1: mode = sys.argv[1].lower() asyncio.run(start_server(mode))

几个关键点解释一下。fastmcp.settings.host和port是全局设置,只有 HTTP/SSE/streamable 模式才用得上,STDIO 模式忽略它们。FastMCP("demo.mcp")里的字符串是服务名,客户端连接时会看到。@mcp.tool()装饰器把普通函数变成 MCP 工具,函数签名和 docstring 会自动转成工具的参数描述,所以 docstring 要写清楚,客户端展示给模型看的就是它。

summarize里用OpenAI客户端指向 TaoToken 的 Base URL,这就是统一通道的用法。你以后想换模型,只改环境变量TAOTOKEN_MODEL_ID,代码一行不动。

如果你更习惯用配置文件管理,可以建一个settings.toml:

[taotoken] base_url = "https://taotoken.net/api" model_id = "你选的模型ID" [fastmcp] host = "0.0.0.0" port = 8001

然后在代码里用tomllib读进来替换环境变量。两种方式都行,环境变量更适合本地快速验证,配置文件更适合团队共享结构。

4. 验证请求:STDIO 与 HTTP 两种模式跑通成功结果

代码写好了,先跑 STDIO 模式,这是 MCP 客户端最常用的本地连接方式。在终端执行:

python demo_mcp.py stdio

你会看到类似输出:

启动MCP服务(stdio模式)... 服务名称: demo.mcp ===== STDIO模式 =====

此时服务在等待标准输入上的 MCP 消息。STDIO 模式不适合直接用 curl 测,因为它走的是标准输入输出而不是网络端口。要验证它,最直接的方式是用一个 MCP 客户端连上来。你可以用 Claude Code 或任何支持 MCP 的客户端,在配置里加一个 stdio server,命令填python /绝对路径/demo_mcp.py stdio。连上后客户端会列出greet和summarize两个工具,调用greet传{"name": "MCP"},返回Hello, MCP就说明通了。

不过对第一次搭服务的人来说,HTTP 模式更容易用 curl 直接验证。另开一个终端,启动 HTTP 模式:

python demo_mcp.py http

输出:

启动MCP服务(http模式)... 服务名称: demo.mcp HTTP服务地址: http://0.0.0.0:8001

服务监听在 8001 端口。fastmcp 的 HTTP 模式走的是 MCP 的 streamable HTTP 传输,请求体是 JSON-RPC 格式。先用 curl 做一次初始化握手:

curl -s -X POST http://127.0.0.1:8001/mcp \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "protocolVersion": "2024-11-05", "capabilities": {}, "clientInfo": {"name": "curl-client", "version": "1.0"} } }'

如果返回里带serverInfo和capabilities,说明握手成功。接着列出工具:

curl -s -X POST http://127.0.0.1:8001/mcp \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -d '{ "jsonrpc": "2.0", "id": 2, "method": "tools/list", "params": {} }'

你应该能看到greet和summarize两个工具的定义,包括参数 schema。最后调用greet:

curl -s -X POST http://127.0.0.1:8001/mcp \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -d '{ "jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": { "name": "greet", "arguments": {"name": "MCP"} } }'

返回里result.content[0].text是Hello, MCP,闭环就跑通了。再测summarize,传一段长文本进去,它会走 TaoToken 通道调模型返回一句话总结。这一步成功,说明你的 MCP 服务既能暴露工具,又能通过统一 Key 调模型。

提示:如果 curl 返回空或连接被拒,先确认服务进程还在前台运行,且端口没被占用。lsof -i :8001可以查端口占用。

5. 本篇常见错排查:401、local proxy failed、reading choices 与 OAuth

第一次跑,报错几乎必然。下面这几个是我和身边人踩过的,对照着看。

401 Unauthorized。这个最常见,基本是 TaoToken 的 Key 没读到或写错。先echo $TAOTOKEN_API_KEY确认环境变量在当前终端可见。如果你在 IDE 里跑,IDE 可能没继承 shell 的环境变量,需要在运行配置里手动加。还有一种情况是 Key 复制时带了空格或换行,strip()一下再传。401 也可能出现在summarize工具里,因为那是唯一真正发起模型请求的地方,greet不碰网络,所以如果greet通而summarize报 401,问题一定在 Key 或 Base URL。

local proxy failed。这个报错通常出现在客户端连接 MCP 服务时,意思是客户端尝试连本地服务但连不上。检查三件事:服务是否真的在跑、地址端口是否和客户端配置一致、防火墙是否拦了本地回环。STDIO 模式下不会出现这个错,因为它不走网络;HTTP 模式下如果客户端配的是localhost:8001而服务监听在0.0.0.0:8001,一般没问题,但如果你把 host 改成了某个具体网卡地址,客户端就得跟着改。

reading choices 相关报错。这个出现在summarize里,典型信息是'NoneType' object has no attribute 'choices'或list index out of range。原因通常是模型返回结构和你预期不一致,或者TAOTOKEN_MODEL_ID是空的导致请求根本没发出去。先打印resp看原始返回,确认resp.choices存在。如果 Model ID 写错,有些通道会返回错误对象而不是抛异常,所以加一层判断更稳:

if not resp.choices: return f"模型调用失败: {resp}" return resp.choices[0].message.content

OAuth 相关报错。如果你用的是 Claude Code 这类客户端,连接 MCP 时可能提示 OAuth 或鉴权失败。这通常不是 MCP 服务本身的问题,而是客户端侧的登录态过期。重新在客户端里完成一次登录,或者检查客户端的 MCP 配置里有没有多余的 auth 字段。MCP 服务本身在 STDIO 模式下不需要 OAuth,HTTP 模式如果没开鉴权也不需要,所以看到 OAuth 报错先怀疑客户端配置而不是服务代码。

Codex auth.json 场景。如果你在用 Codex 类工具,它的鉴权信息存在auth.json里。当 MCP 服务和 Codex 共用模型通道时,确保auth.json里的 base_url 指向 TaoToken 的 API 入口,Key 和 Model ID 与本文三件套一致。三件套缺一不可:Base URL 决定请求发到哪,Key 决定能不能过鉴权,Model ID 决定用哪个模型。任何一件写错,表现都是调用失败,但报错信息各不相同,对照上面几条定位。

排查顺序建议固定下来:先确认服务进程活着,再确认端口/命令对,再确认三件套环境变量,最后看模型返回结构。按这个顺序,九成问题能在两分钟内定位。

6. 继续往下走:把最小闭环扩展成你的工具集

跑通这个闭环之后,你手里其实已经有了一个可复用的骨架。加新工具就是往demo_mcp.py里再写几个带@mcp.tool()的函数,docstring 写清楚参数含义,重启服务,客户端重新连接就能看到。如果你想让工具内部调用模型,直接复用那个client对象,三件套不用再配一遍。

下一步可以试的方向:把summarize换成更具体的业务工具,比如"从一段日志里提取错误码";或者给工具加上参数校验,让 schema 更严格;再或者把 STDIO 模式接进你日常用的编码客户端,让它在写代码时能直接调你的工具。这些都不需要改协议层,fastmcp 已经处理好了。

如果你打算长期做 MCP 服务和 Agent 相关开发,建议了解一下 Coding Plan,它更适合需要持续调用模型、跑长任务的场景:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite 。想直接在网页里试模型效果,可以用模型对话:https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_content=model-chat&utm_campaign=rewrite 。接入细节和参数说明都在文档里:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 。需要新建或管理 Key 就去 API Keys 页面:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 。

最后留一个实用技巧:把启动命令写成一个run.sh,里面先source环境变量再启动服务,这样每次不用手动 export。服务跑起来后,先用greet这种不依赖网络的工具确认链路通,再测summarize这种依赖模型的工具,能把"服务问题"和"模型通道问题"分开定位。这个习惯能帮你省下大量排查时间。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询