拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

快速手搓一个MCP服务指南(九):FastMCP 服务器组合技术:构建模块化AI应用的终极方案

快速手搓一个MCP服务指南(九):FastMCP 服务器组合技术:构建模块化AI应用的终极方案

1. 为什么要把多个 MCP 服务拼成一个入口

如果你已经手搓过几个 MCP 服务,大概率会遇到一个尴尬局面:天气服务一个进程、数据库查询一个进程、文本处理又一个进程,每个都要单独配一遍客户端、单独填一遍 Key、单独维护一份启动脚本。客户端那边更麻烦,Claude Desktop 或 Cline 里要挂四五个 server 条目,改一个端口就得全量重启。

FastMCP 的服务器组合(Server Composition)就是来解决这件事的。它提供两种把子服务器拼进主服务器的方式:import_server做静态复制,mount做动态链接。拼完之后,你对外只暴露一个 MCP 入口,客户端只认一个地址、一份配置,内部却可以按功能域拆成任意多个模块。适合谁?适合已经把 MCP 玩到第二个、第三个服务,开始觉得「配置比代码还多」的开发者;也适合团队里不同人负责不同工具域,最后要合成一个统一入口的场景。

我试过把三个独立服务合成一个主服务器,客户端配置从 60 行缩到 12 行,重启次数直接砍半。这篇就按「先讲清两种组合的差别 → 给出可复制的骨架 → 用 TaoToken 统一通道验证工具列表和调用」的顺序走,每一步都能跟着敲。

核心检索词先摆出来:FastMCP 服务器组合、import_server 静态导入、mount 动态挂载、MCP 统一入口。这四个词贯穿全文,你照着搜也能找到对应文档。

在动手之前,先把两种机制的边界划清楚,否则很容易选错:

import_server是「复制」。调用那一刻,子服务器的工具、资源、提示词被拷贝进主服务器,之后子服务器再怎么改,主服务器都不受影响。工具名会加上前缀,比如weather_get_forecast。它适合固化的、不常变的组件,比如封装好的第三方 API、稳定的算法工具。

mount是「链接」。主服务器只保留一个引用,运行时收到带前缀的请求再转发给子服务器。子服务器新增工具,主服务器立刻能看到。它适合需要持续迭代、或者跨进程跨实例的模块。

一句话决策:组件稳定用 import,组件会活用 mount。下面进入实操。

2. TaoToken 前置:一份 Key 打通组合后的统一通道

组合完服务器,下一个问题就是「谁来调用」。本地调试可以用 stdio,但一旦你想让组合后的主服务器对外提供 HTTP 入口,或者想让多个客户端共用一套鉴权,就需要一个统一的 API 通道。TaoToken 在这里扮演的角色是:给你一个统一的 Base URL 和一把 Key,模型对话、工具调用都走同一个出口,不用每个子服务单独配一套凭证。

先把地址记清楚,后面配置里要用:

  • 官网入口:https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=
  • API 根地址:https://taotoken.net/api (这个不加 UTM,配置里填这个)

你需要提前准备两样东西:一把 API Key,以及确认你要用的 Model ID。Key 在控制台的 API Keys 页面生成,路径是 https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite 。生成后先复制存好,页面刷新就不再完整显示。

Model ID 这块要注意,不同客户端填法不一样。如果你用的是 Claude Code 这类 Anthropic 协议客户端,走的是 https://taotoken.net/api 这个根地址加对应模型名;如果是 OpenAI 兼容协议的客户端,同样填这个根地址,模型名按你实际开通的填。别把两个协议的路径混用,这是后面 401 和 404 的高发区。

为什么组合服务器要配 TaoToken?因为组合后的主服务器往往要同时处理「模型推理」和「工具调用」两类请求。如果工具走本地、模型走另一个通道,鉴权和日志就分裂了。统一到一套 Base URL + Key + Model ID,排障时只看一个出口,日志一条线,省心很多。

这里给一个最小验证思路:先用模型对话页面确认 Key 和模型名是通的,地址是 https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite 。确认能正常返回,再去配 MCP 服务器。顺序反了的话,你会分不清是 Key 错还是服务器组合错。

如果你打算长期跑编码类 Agent,或者要把组合后的服务器接到自动化流程里,可以看下 Coding Plan:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite 。它解决的是「长期、高频、多工具」场景下的额度与稳定性,不是必须,但组合服务器一旦上量就会用到。

前置准备就这些:一把 Key、一个确认可用的 Model ID、一个根地址。接下来进配置。

3. 可复制配置:import_server 与 mount 骨架 + config.toml

这一节是全文最该动手的部分。我按「子服务器 → 主服务器 → 客户端配置」三层给你骨架,路径和字段名保持和 FastMCP 一致,你直接改名字就能用。

先看两个子服务器。第一个是天气服务,第二个是文本处理服务,都放在servers/目录下:

# servers/weather_server.py from fastmcp import FastMCP weather_mcp = FastMCP(name="WeatherService") @weather_mcp.tool def get_forecast(city: str) -> dict: """返回指定城市的天气预报""" return {"city": city, "forecast": "Sunny", "temp_c": 26} @weather_mcp.tool def get_alert(city: str) -> dict: """返回指定城市的天气预警""" return {"city": city, "alert": "none"}
# servers/text_server.py from fastmcp import FastMCP text_mcp = FastMCP(name="TextService") @text_mcp.tool def word_count(text: str) -> dict: """统计文本词数""" return {"words": len(text.split())} @text_mcp.tool def to_upper(text: str) -> dict: """转大写""" return {"result": text.upper()}

现在写主服务器。这里同时演示两种组合方式:天气服务用import_server静态导入(它稳定),文本服务用mount动态挂载(它可能加新工具):

# main_server.py import asyncio from fastmcp import FastMCP from servers.weather_server import weather_mcp from servers.text_server import text_mcp main_mcp = FastMCP(name="MainApp") async def build(): # 静态导入:工具名变成 weather_get_forecast / weather_get_alert await main_mcp.import_server(weather_mcp, prefix="weather") # 动态挂载:工具名变成 text_word_count / text_to_upper main_mcp.mount(text_mcp, prefix="text") return main_mcp if __name__ == "__main__": app = asyncio.run(build()) app.run(transport="http", host="127.0.0.1", port=8765)

注意import_server是 async 的,必须 await;mount是同步的,直接调。这是新手最容易踩的一个坑,漏了 await 会得到一个协程对象而不是导入结果。

接下来是客户端侧的config.toml。如果你用的是支持 TOML 配置的 MCP 客户端,把组合后的主服务器和 TaoToken 通道一起写进去:

# config.toml [mcp_servers.main_app] command = "python" args = ["main_server.py"] transport = "http" url = "http://127.0.0.1:8765" [llm.taotoken] base_url = "https://taotoken.net/api" api_key = "sk-你的Key" model = "你的ModelID"

如果你用的是 JSON 配置的客户端(比如某些 IDE 插件),等价片段是这样:

{ "mcpServers": { "main_app": { "url": "http://127.0.0.1:8765", "transport": "http" } }, "llm": { "baseUrl": "https://taotoken.net/api", "apiKey": "sk-你的Key", "model": "你的ModelID" } }

三件套再强调一次:Base URL 填https://taotoken.net/api,Key 填控制台生成的那把,Model ID 填你确认可用的那个。这三个字段在 Claude Code、Cline、Codex 的 auth.json 里都是必填项,缺一个就连不上。

资源前缀格式也顺手配一下。FastMCP 支持两种前缀格式,推荐用 path 格式,避免 URI 协议限制:

# 全局配置 import fastmcp fastmcp.settings.resource_prefix_format = "path" # 或者单服务器配置 main_mcp = FastMCP(name="MainApp", resource_prefix_format="path")

也可以用环境变量:FASTMCP_RESOURCE_PREFIX_FORMAT=path。新项目直接上 path 格式,老系统迁移再考虑协议格式。

配置写完,先别急着接客户端,下一节先本地验证工具列表能不能拉出来。

4. 验证请求:拉取工具列表并完成一次调用

配置对不对,跑一次就知道。分两步:先拉工具列表,确认组合生效;再实际调用一个工具,确认前缀和路由都对。

启动主服务器:

python main_server.py

看到监听 8765 的日志后,另开一个终端,用 curl 拉工具列表。MCP over HTTP 的列表请求大致长这样:

curl -s http://127.0.0.1:8765/mcp \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

预期返回里应该能看到四个工具,名字带前缀:

{ "jsonrpc": "2.0", "id": 1, "result": { "tools": [ {"name": "weather_get_forecast"}, {"name": "weather_get_alert"}, {"name": "text_word_count"}, {"name": "text_to_upper"} ] } }

如果weather_和text_两个前缀都在,说明 import 和 mount 都生效了。这一步是整个组合技术的验收点,前缀没出来,后面全白搭。

接着调用一个工具,验证路由:

curl -s http://127.0.0.1:8765/mcp \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"weather_get_forecast","arguments":{"city":"Hangzhou"}}}'

预期返回:

{ "jsonrpc": "2.0", "id": 2, "result": { "content": [{"type": "text", "text": "{\"city\": \"Hangzhou\", \"forecast\": \"Sunny\", \"temp_c\": 26}"}] } }

到这里,组合服务器本身已经通了。再验证动态挂载的「实时性」:不重启主服务器,往text_server.py里加一个新工具to_lower,然后重新拉一次工具列表。因为mount是动态链接,理论上新工具应该出现。实测下来,直接挂载模式下同进程内确实能立刻看到;如果你用的是as_proxy=True的代理挂载,需要子服务器那边也刷新。

最后把 TaoToken 通道接进来做一次端到端验证。用模型对话页面发一条会触发工具调用的指令,比如「帮我查一下 Hangzhou 的天气,并统计这句话的词数」。如果模型能正确调用weather_get_forecast和text_word_count并返回结果,说明「组合服务器 + 统一通道」整条链路是通的。模型对话入口:https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite 。

验证顺序建议固定成:本地 tools/list → 本地 tools/call → 接 TaoToken 端到端。哪一步断了就停在哪一步排查,别跳。

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

组合服务器 + 统一通道这套组合,报错集中在四个地方。我按真实遇到的顺序列出来,对照着查。

401 Unauthorized。九成是 Key 的问题。先确认config.toml或 JSON 里的api_key没有多余空格,再确认这把 Key 是在 https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite 生成的、且没过期。还有一种情况是 Base URL 写成了带路径的完整地址,正确写法是根地址https://taotoken.net/api,不要自己拼/v1/chat/completions之类。401 出现时,先用模型对话页面单独测 Key,能通就说明是 MCP 配置里字段名写错了。

local proxy failed。这个通常出现在代理挂载(as_proxy=True)或者客户端走本地代理转发时。排查三步:一看子服务器进程是否还活着,代理挂载依赖子服务器生命周期;二看端口有没有被占用,lsof -i :8765确认;三看mount时前缀是否和请求里的前缀一致,前缀对不上会走到空路由。如果是跨进程代理挂载,还要确认子服务器的启动命令路径是绝对路径,相对路径在代理模式下经常找不到。

reading choices 相关报错。这类报错一般出现在模型返回体解析阶段,根因是返回结构和你客户端预期的协议不一致。比如你用 Anthropic 协议的客户端去请求了 OpenAI 兼容格式的返回,或者 Model ID 填错导致返回体里没有choices字段。解决方式:确认客户端协议和 Base URL 匹配,确认 Model ID 是实际开通的。如果返回体里字段名对不上,先别改代码,先用模型对话页面看原始返回长什么样。

OAuth 报错。如果你在 Claude Code 或类似客户端里看到 OAuth 相关提示,多半是客户端在尝试走它默认的登录流程,而不是用你配的 Key。这时候要检查配置里是否显式写了apiKey字段,以及是否把认证方式设成了 API Key 模式。有些客户端需要你在设置里手动切换认证方式,光填 Key 不够。Codex 的auth.json里要确保OPENAI_API_KEY或对应字段填的是 TaoToken 的 Key,而不是残留的旧值。

再补一个组合技术特有的坑:import_server漏写await。表现是工具列表里完全没有子服务器的工具,但也不报错。看到「导入成功但工具没出现」,第一反应就是检查 await。

排查完记得回到验证顺序:tools/list 通了再测 tools/call,本地通了再接 TaoToken。跳步排查会浪费大量时间。

6. 组合策略怎么选,以及统一入口的长期价值

把两种机制的选择标准再收拢一下,方便你以后直接查:

场景推荐方式原因
第三方 API 封装、稳定算法import_server静态复制,不受子服务变更影响
实时数据服务、频繁加工具mount动态链接,子服务更新即时可见
跨进程、跨节点集成mount + as_proxy=True保留子服务生命周期,走客户端接口通信
需要统一鉴权和日志组合 + TaoToken 通道一个 Base URL、一把 Key、一条日志线

前缀命名建议按功能域来,比如weather_、text_、db_、ml_,别用s1_、s2_这种无语义前缀。import 的时候记一下子服务器的版本,方便回溯。同进程高频调用用直接挂载,跨进程用代理挂载,这是性能上的分水岭。

长期看,组合服务器的价值不只是「少配几个条目」。它把 MCP 从「一个服务一个入口」推进到「一个入口多个模块」,这才是模块化 AI 应用该有的样子。你后面要加新工具域,只需要写一个新的子服务器,import 或 mount 进主服务器,客户端那边一行都不用改。配合 TaoToken 的统一通道,鉴权、模型、日志都收敛到一个出口,维护成本会随着服务数量增加而摊薄,而不是线性上涨。

如果你还没生成 Key,现在就可以去 https://taotoken.net/console/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite 拿一把,把上面那份config.toml里的占位符替换掉,跑一遍 tools/list。接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,遇到协议细节可以对照查。先把一个主服务器 + 两个子服务器跑通,再往上叠模块,这条路会顺很多。

返回列表