1. FastMCP 写 MCP 服务时,属性、端口、IP 到底卡在哪
用 FastMCP 起一个 MCP 服务,代码量确实少得让人怀疑人生:一个FastMCP("名字"),加一个@mcp.tool()装饰器,mcp.run()一跑就完事。但真正落到本地开发和内网联调场景,问题往往不在工具函数本身,而在三个地方:属性没生效、端口被占用、IP 绑错网卡。这三个坑单独看都不复杂,凑在一起就会让你对着日志反复怀疑“我明明改了配置”。
FastMCP 是 MCP 官方 Python SDK 里封装度较高的一层,它把底层Settings对象、传输方式、路由注册都收进了一个实例里。你写的mcp.settings.host、mcp.settings.port这些属性,最终会决定服务监听在哪个地址、哪个端口、以什么调试级别输出日志。问题在于:这些属性有默认值,而且不同版本对“什么时候读取属性”这件事处理不一样。你在mcp.run()之前改,和之后改,效果完全不同。
这篇面向的是本地开发与内网联调:你在一台开发机上写 MCP 服务,想让同一局域网的另一台机器或另一个进程调用它;或者你想让服务监听0.0.0.0方便容器访问,结果发现只绑到了127.0.0.1。我会把属性、端口、IP 三类配置拆开讲,给出可复制的config.toml/settings.json骨架,再配一套 TaoToken 统一 Key/API 通道的接入示例,最后用端口占用检测、IP 绑定验证、属性生效确认三步动作,帮你一次跑通服务启动与调用。
先说结论:FastMCP 的配置优先级是“代码里显式赋值 > 环境变量 > 默认值”,而端口和 IP 属于Settings对象,必须在run()之前完成赋值。很多人踩的坑就是把settings建好了却没挂回mcp.settings,或者挂回去了但run()用的是旧引用。下面按步骤来。
2. TaoToken 前置:统一 Key 与 API 通道怎么准备
在讲配置骨架之前,先把调用侧的事情理清楚。MCP 服务本身是“被调用方”,但你在本地调试时,往往需要一个稳定的模型通道来验证工具调用是否真的通了。TaoToken 在这里的角色是统一 Key 和 API 通道:你不需要在每台机器、每个客户端里分别配不同厂商的 Key,而是用一套 Key 走同一个 API 入口。
官网入口是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 基址是 https://taotoken.net/api 。注意 API 地址不带 UTM 参数,配置里写干净的这个就行。你需要先去控制台拿 Key,地址是 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,Key 管理页在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。
拿到 Key 之后,你要区分两个概念:一个是 MCP 服务自己的监听配置(host/port),另一个是 MCP 服务内部去调用模型时用的 API 配置(Base URL + Key + Model ID)。这两者经常被混在一起,导致“服务起来了但工具调用报 401”。我的建议是把它们分到两个配置文件里:config.toml管服务监听,settings.json管模型通道。
如果你用的是 Claude Code 这类客户端,接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,里面有 Base URL 和 Key 的填写位置。想先验证模型通不通,可以直接用模型对话页 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 发一条消息,确认 Key 有效再往下配。长期做编码或 Agent 的话,Coding Plan 页面在 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,可以先了解额度模型。
这里要强调一点:TaoToken 是统一通道,不是让你把 MCP 服务直接连生产库。MCP 工具函数里该做的参数校验、权限判断,一个都不能省。统一 Key 只是解决了“调用侧配置分散”的问题,不解决业务安全。
3. 可复制配置:config.toml 与 settings.json 骨架
这一节是核心,直接给可复制的片段。先看服务监听侧的config.toml。FastMCP 本身不强制你用 TOML,但把 host/port/debug 抽出来放配置文件,比硬编码在 Python 里好维护,尤其是内网联调要频繁换 IP 的时候。
# config.toml —— MCP 服务监听配置 [mcp] name = "time-machine" host = "0.0.0.0" port = 8088 debug = true transport = "sse" [mcp.logging] level = "DEBUG" format = "%(asctime)s %(levelname)s %(name)s %(message)s"对应的 Python 读取逻辑,注意settings必须挂回mcp.settings:
import tomllib from mcp.server.fastmcp import FastMCP from mcp.server.fastmcp.server import Settings with open("config.toml", "rb") as f: cfg = tomllib.load(f)["mcp"] mcp = FastMCP(cfg["name"]) settings = Settings() settings.host = cfg["host"] settings.port = cfg["port"] settings.debug = cfg["debug"] # 关键一步:挂回实例,否则 run() 读的还是默认值 mcp.settings = settings @mcp.tool() def now() -> str: """返回当前时间字符串""" from datetime import datetime return datetime.now().isoformat() if __name__ == "__main__": mcp.run(transport=cfg["transport"])再看模型通道侧的settings.json,这个文件通常给客户端或 SDK 读取,路径按你实际项目放,比如项目根目录的.mcp/settings.json:
{ "base_url": "https://taotoken.net/api", "api_key": "sk-你的Key", "model_id": "claude-sonnet-4-20250514", "timeout": 60, "max_retries": 2 }三件套必须写全:Base URL 是https://taotoken.net/api,Key 从 API Keys 页拿,Model ID 按你实际要用的模型填。少任何一个,调用侧都会报错。如果你用 Cline MCP 或 CC Switch 这类工具,它们的配置项名称可能不同,但本质都是这三样。
关于属性生效,有个容易忽略的点:Settings()的默认host是127.0.0.1,默认port是8000。你不改,它就只监听本机回环。内网联调时另一台机器访问127.0.0.1是访问它自己的回环,当然连不上。所以要么绑0.0.0.0,要么绑具体网卡 IP。
4. 验证请求:端口占用检测、IP 绑定验证、属性生效确认
配置写完不算完,得验证。我按三步走,每步都有具体命令和预期结果。
第一步,端口占用检测。在启动服务前先确认 8088 没被占:
# Linux / macOS lsof -i :8088 # 或 ss -ltnp | grep 8088 # Windows netstat -ano | findstr :8088如果输出里有进程,要么换端口,要么先停掉那个进程。我试过直接启动然后报Address already in use,回头查才发现是上次没退干净的进程还在。
第二步,IP 绑定验证。服务启动后,用ss或netstat看监听地址:
ss -ltnp | grep 8088预期看到0.0.0.0:8088或192.168.x.x:8088。如果看到的是127.0.0.1:8088,说明你的host没生效,回去检查mcp.settings = settings那行。然后从另一台机器或另一个终端发请求:
curl -N http://192.168.1.20:8088/sse能建立连接并收到事件流,说明 IP 绑定正确。连不上先 ping 一下确认网络通,再查防火墙。
第三步,属性生效确认。在服务里加一行日志,或者直接在 Python 里打印:
print("host=", mcp.settings.host, "port=", mcp.settings.port, "debug=", mcp.settings.debug)启动时看输出是否和你配置文件一致。如果打印出来是默认值,说明赋值时机不对——必须在mcp.run()之前,且挂回mcp.settings。
模型通道的验证,用模型对话页发一条消息最快:https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。能正常返回,说明 Key 和 Base URL 没问题,再去查 MCP 工具调用。
5. 本篇常见错排查:401、local proxy failed、reading choices、OAuth
这一节对照真实报错来。第一个,401 Unauthorized。绝大多数情况是 Key 没填、填错,或者 Base URL 写成了带路径的地址。检查settings.json里的api_key是否以sk-开头,base_url是否是https://taotoken.net/api,不要多加/v1之类的后缀。如果 Key 是从别处复制的,注意有没有多余空格。
第二个,local proxy failed。这个通常出现在客户端配置了本地代理但代理没起来,或者代理地址写错。排查顺序:先确认代理进程在跑,再确认端口对得上,最后确认 MCP 服务的 host/port 和代理转发目标一致。内网联调时如果服务绑的是127.0.0.1,代理从另一台机器转发过来就会失败,改成0.0.0.0再试。
第三个,reading choices相关报错。这类多半是响应体解析失败,常见原因是模型返回了非预期格式,或者model_id填了一个不存在的模型。去模型对话页确认你用的 Model ID 可用,再回填到settings.json。另外timeout设太短也会导致读到一半断开,内网联调建议先设 60 秒。
第四个,OAuth相关报错。如果你用的是 Claude Code 或类似客户端,它可能默认走 OAuth 流程。这时候要么按接入文档完成 OAuth 配置,要么改用 API Key 方式。文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。用 API Key 方式时,Base URL、Key、Model ID 三件套一个都不能少。
还有一个隐蔽的坑:mcp.settings被覆盖。如果你在代码里先mcp.settings = settings,后面又调用了某个会重置 settings 的方法,属性就丢了。排查方法就是在run()前再打印一次。另外,环境变量MCP_HOST、MCP_PORT如果设了,可能会覆盖你的代码赋值,检查一下 shell 里有没有残留。
6. 语义一致 CTA:把配置跑通之后
配置跑通之后,你手里应该有一个监听在正确 IP 和端口上的 MCP 服务,以及一套能用的模型通道。接下来最该做的是把 Key 管理规范化:去 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 建一个专用 Key,别和别的项目混用。接入细节看文档 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。
如果你还在验证阶段,模型对话页 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 是最快的确认方式。长期做编码或 Agent 开发,Coding Plan 页面 https://taotoken.net/coding-plan?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_medium=csdn&utm_campaign=rewrite&utm_content= ,Key 和用量都在那里。
最后留一个我踩过的坑:改完config.toml后忘了重启服务,对着旧进程调了半天。FastMCP 不会热加载配置,改完必须重启。还有,0.0.0.0只适合内网联调,别直接暴露到公网。