fastmcp做mcp工具服务

  1. 端到端通信与时序
Host(IDE/Agent)                    fastmcp 进程(STDIO)
─────────────── spawn ─────────────►  启动(附着 stdin/stdout 管道)
               Content-Length 帧化 + JSON-RPC 2.0

1) initialize ─────────────────────►  解析能力、返回 capabilities
2) initialized ◄────────────────────  Host 通知“完成初始化”
3) tools/list  ─────────────────────►  列出工具 (name, schema, desc)
4) resources/list(optional) ───────►  列出资源(如文件、URI模板)
5) tools/call  ─────────────────────►  执行工具函数(你的业务逻辑)
6)   result    ◄────────────────────  返回结果(contents 数组)
…(可多次调用)
7) shutdown    ─────────────────────►  优雅关闭
8) exit        ◄────────────────────  进程退出
•	传输层:MCP 规定可用多种传输;其中 STDIO 是标准且推荐的本地传输,客户端通过子进程与服务器的 stdin/stdout 双向通信。 
•	fastmcp 的默认运行方式:调用 run() 不带参数即走 STDIO;客户端通常“每会话拉起一个子进程”。 
•	编码与线格式:消息为 JSON-RPC 2.0,并用 Content-Length 头 + \r\n\r\n + JSON 做帧边界(和 LSP 一致)。例如:
Content-Length: 61\r\n
\r\n
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}

(务必计算字节长度、用 CRLF,日志不要写到 stdout) 。    

  1. Host ↔ fastmcp 的核心调用语义
    • initialize / initialized:握手阶段,协商协议版本与能力。之后 Host 常会立即做一次工具发现。 
    • tools/list → tools/call:发现工具、调用工具(带参数),是最常见主路径。各 SDK 在客户端侧提供 list_tools() / call_tool()。  
    • resources/list / read_resource:用于暴露数据源(本地文件、HTTP、数据库等)并读取内容。 

  1. 可运行的最小示例(服务器:STDIO)

下面是一个 fastmcp 2.0 风格的最小服务器。直接运行后,它就等着 Host 通过 STDIO 连接、列工具、调工具。fastmcp 在 2.0 版本对 MCP 生态做了“更 Pythonic 的封装”。  

server.py

from typing import Annotated
from mcp.server.fastmcp import FastMCP, run

mcp = FastMCP("demo")

@mcp.tool()
def add(a: Annotated[int, "加数 A"], b: Annotated[int, "加数 B"]) -> str:
    """返回 a + b 的结果。"""
    return str(a + b)

if __name__ == "__main__":
    # 不带参数:默认用 STDIO 传输
    run(mcp)

要点:
• 不用自己处理 Content-Length,SDK 已内置;只专注写业务函数(工具)。 
• 不要 print 到 stdout(会破坏帧);日志写 stderr。 

  1. 客户端(调试/自测):用官方 Python SDK 走 STDIO

client.py

import asyncio
from mcp.client.stdio import stdio_client, StdioServerParameters
from mcp.client.session import ClientSession

async def main():
    params = StdioServerParameters(command="python", args=["server.py"])
    async with stdio_client(params) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()
            tools = await session.list_tools()
            print("Tools:", [t.name for t in tools.tools])
            result = await session.call_tool("add", {"a": 7, "b": 13})
            # MCP 工具结果通常是 contents 列表(文本/JSON等)
            print("Result:", result.content[0].text)

if __name__ == "__main__":
    asyncio.run(main())

这个示例演示了 握手 → 列工具 → 调工具 的完整流程。方法名与握手顺序与文档一致。 

  1. 手工诊断(原始帧)

你也可以在命令行手工发帧验证服务器是否按 STDIO 规范读消息:

发送 initialize(注意 \r\n\r\n、字节数、不要多空格)

printf 'Content-Length: 61\r\n\r\n{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' \
 | python server.py

这是调试 STDIO 帧化最直观的方法(和文档示例一致)。 

  1. 生产化落地的关键细节

    1. 帧化与日志

    • Content-Length 必须是 UTF-8 字节长度;分隔必须 \r\n\r\n;
    • 严禁把业务日志写到 stdout(会和 JSON-RPC 混流导致解帧失败),请写到 stderr。

    1. 并发与重入

    • 客户端可能并行 call_tool;尽量让工具函数是无副作用/线程安全;
    • 某些 SDK 在内部会触发隐式的 list_tools()(曾引发竞争条件),建议使用较新的 SDK 版本并避免在工具执行期间动态重置能力表。

    1. 长任务与返回体

    • MCP 工具返回是内容列表(text/json 等),超大结果建议分页或资源化(resource://… 再 read_resource)。

    1. 何时选 STDIO

    • 本地 IDE、桌面客户端(如 Claude Desktop/Cursor 等)优先 STDIO;
    • 远程或需要浏览器接入时,用 Streamable HTTP / SSE 更合适;客户端与服务端都能无缝切换传输。

    1. 为什么 fastmcp 默认 STDIO

    • 本地子进程最简单(无需端口/防火墙);
    • 跨语言/跨平台(一切语言都有 stdin/stdout);
    • 低开销高安全(无网络面)。这些正是 fastmcp 的默认选择与官方建议。  

  1. 扩展示例:把“汇率查询”作为 MCP 工具(可接你之前的需求)

下面是一个“查询即期/历史汇率”的工具骨架(以 httpx 查询公共 API 为例);在真实环境里换成你企业内部汇率源即可。

fx_server.py

from typing import Annotated, Optional
from datetime import date
import httpx
from mcp.server.fastmcp import FastMCP, run

mcp = FastMCP("fx")

FX_BASE = "https://api.exchangerate.host"  # 仅示例

@mcp.tool()
def fx_rate(
    base: Annotated[str, "基准货币,如 USD"],
    quote: Annotated[str, "标价货币,如 CNY"],
    on: Annotated[Optional[str], "日期 YYYY-MM-DD,留空为今日"] = None,
) -> str:
    """查询即期/历史汇率:返回 1 {base} = ? {quote}"""
    d = on or date.today().isoformat()
    endpoint = f"{FX_BASE}/{d}"
    with httpx.Client(timeout=10) as client:
        r = client.get(endpoint, params={"base": base.upper(), "symbols": quote.upper()})
        r.raise_for_status()
        data = r.json()
        rate = data["rates"][quote.upper()]
        return f"1 {base.upper()} = {rate} {quote.upper()} @ {d}"

if __name__ == "__main__":
    run(mcp)  # 默认 STDIO
•	仍然通过 run(mcp) 走 STDIO;Host 侧 tools/list 能自动读到 fx_rate 的名称、入参 schema 和文档字符串。 

小结
• STDIO:MCP 的标准传输之一,fastmcp 默认使用,因其简单、通用、安全、低延迟。
• 线格式:Content-Length 帧 + JSON-RPC 2.0;严格遵守字节长度与 CRLF;日志走 stderr。
• 实现:fastmcp 让你专注于“写工具函数”,其余交给 SDK(握手、发现、调度、STDIO 管道)。

<think>我们正在讨论FastMCP如何调用外部MCP服务。根据引用[3]和[5],FastMCP主要是一个用于构建MCP服务器的框架。然而,用户的问题是关于如何调用外部的MCP服务(即作为客户端)。 在MCP架构中(引用[3]): - MCP Host: 使用AI的应用程序(如Claude客户端) - MCP Client: 负责与MCP Server通信的组件 - MCP Server: 提供功能接口的组件 FastMCP主要用于构建MCP Server(引用[2][5])。但是,调用外部MCP服务需要扮演MCP Client的角色。 然而,查看引用[5]的示例代码,它只展示了如何创建MCP Server(使用`FastMCP`类,定义工具和资源,然后运行)。并没有直接展示如何作为客户端调用外部服务。 但是,我们注意到引用[3]中提到“MCP Client:与MCP Server建立连接的组件,负责处理通信细节”。因此,要调用外部MCP服务,我们需要一个MCP客户端。 由于FastMCP的文档和引用中主要关注服务端,可能没有直接提供客户端库。但我们可以根据MCP协议(一种基于JSON的协议)来实现客户端调用。 步骤: 1. 了解MCP协议:MCP协议通常通过标准输入输出(stdio)或HTTP等传输层进行通信。消息是JSON格式。 2. 作为客户端,我们需要向MCP服务器发送请求并接收响应。 例如,如果我们有一个运行在HTTP上的MCP服务器,我们可以发送HTTP POST请求到服务器的端点,请求体为JSON格式的MCP请求。 但是,引用[5]中的示例使用的是`stdio`传输(即标准输入输出),这意味着它设计用于与父进程通过管道通信。在这种情况下,调用外部MCP服务(作为另一个进程)需要以子进程方式启动它,并通过管道进行通信。 然而,用户的问题可能是:在FastMCP构建的服务中,如何调用另一个外部的MCP服务?即我们构建的FastMCP服务器需要作为客户端去调用其他MCP服务器。 由于FastMCP主要关注服务端,它可能没有内置的客户端功能。因此,我们需要自己实现一个MCP客户端。 实现MCP客户端的基本步骤(以HTTP传输为例): 1. 确定外部MCP服务的端点(URL)。 2. 根据MCP协议构造请求(JSON格式)。 3. 发送HTTP POST请求。 4. 解析响应。 但是,如果外部MCP服务使用stdio传输(如引用[5]的示例),则调用方式不同:我们需要启动该外部服务作为子进程,并通过标准输入输出与其通信。 由于MCP协议的具体消息格式在引用中没有给出,我们可以参考MCP的官方文档或标准。但根据常见实现,一个简单的MCP请求可能如下: 请求: ```json { "id": "unique-request-id", "method": "方法名", "params": { "param1": value1, "param2": value2 } } ``` 响应: ```json { "id": "unique-request-id", "result": ..., "error": null } ``` 因此,如果我们使用HTTP传输,可以这样调用(使用Python的requests库): ```python import requests url = "http://external-mcp-server/endpoint" payload = { "id": "1", "method": "add", # 假设外部服务有一个add方法 "params": { "a": 2, "b": 3 } } response = requests.post(url, json=payload) result = response.json()['result'] ``` 如果是stdio传输,则更复杂,需要管理子进程的输入输出。例如: ```python import subprocess import json # 启动外部MCP服务进程(假设它是一个可以通过命令行启动的程序) process = subprocess.Popen(['python', 'external_mcp_server.py'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, text=True) # 构造请求 request = { "id": "1", "method": "add", "params": { "a": 2, "b": 3 } } # 发送请求 process.stdin.write(json.dumps(request) + '\n') process.stdin.flush() # 获取响应 response_line = process.stdout.readline() response = json.loads(response_line) result = response['result'] ``` 注意:stdio模式下,通常以换行符分隔的JSON消息进行通信。 在FastMCP构建的服务中,如果我们想要调用外部服务,我们可以在FastMCP定义的工具函数中实现上述客户端逻辑。例如: ```python from mcp.server.fastmcp import FastMCP import requests mcp = FastMCP("My Server") @mcp.tool() def call_external_service(a: int, b: int) -> int: """调用外部MCP服务的add方法""" # 假设外部服务是HTTP传输 url = "http://localhost:8000/mcp" payload = { "id": "1", "method": "add", "params": {"a": a, "b": b} } response = requests.post(url, json=payload) if response.status_code == 200: return response.json()['result'] else: raise Exception("调用外部服务失败") # ... 其他工具和资源 ``` 总结:FastMCP本身没有提供调用外部MCP服务的客户端库,但我们可以根据传输方式(HTTP或stdio)使用常规的HTTP客户端或子进程管理来实现。 注意:在实现客户端时,需要了解目标MCP服务的具体协议细节(方法名、参数、传输方式等)。 因此,回答用户问题:FastMCP如何调用外部MCP服务?我们可以通过自行实现MCP客户端(使用HTTP请求或子进程通信)来调用。</think>### FastMCP 调用外部 MCP 服务的技术实现 在 FastMCP 中调用外部 MCP 服务需要遵循 MCP 协议的客户端-服务器架构。以下是实现步骤和技术要点: #### 1. **理解 MCP 架构角色** - **MCP Host**:发起请求的应用程序(如 AI 客户端)[^3] - **MCP Client**:负责连接外部服务的组件(需自行实现) - **MCP Server**:外部服务提供方(已存在) ```mermaid graph LR A[MCP Host] --> B[MCP Client] B --> C[外部 MCP Server] ``` #### 2. **实现 MCP 客户端** FastMCP 主要提供**服务开发能力**,调用外部服务需自行构建客户端逻辑。核心步骤: ```python import requests import json class MCPClient: def __init__(self, server_url: str): self.base_url = server_url # 外部服务地址,如 http://external-mcp:8000 def call_tool(self, tool_name: str, params: dict) -> dict: """调用外部服务工具""" payload = { "method": tool_name, "params": params, "id": "req_123" # 唯一请求ID } response = requests.post( f"{self.base_url}/invoke", json=payload, headers={"Content-Type": "application/json"} ) return response.json() ``` #### 3. **在 FastMCP 服务中集成调用** 在 FastMCP 定义的工具中嵌入客户端调用: ```python from mcp.server.fastmcp import FastMCP mcp = FastMCP("GatewayService") # 创建客户端实例 external_client = MCPClient("http://external-service:8000") @mcp.tool() def combined_calculation(a: int, b: int) -> dict: """调用外部服务的加法工具并本地处理""" # 调用外部服务 external_res = external_client.call_tool("add", {"a": a, "b": b}) # 本地处理 local_calc = a * b return { "external_sum": external_res["result"], "local_product": local_calc } ``` #### 4. **通信协议要点 - **传输格式**:JSON-RPC 风格协议[^3] - **典型请求**: ```json { "method": "add", "params": {"a": 5, "b": 3}, "id": "req_2023" } ``` - **响应结构**: ```json { "result": 8, "error": null, "id": "req_2023" } ``` #### 5. **高级场景处理 - **异步调用**:使用 `asyncio` + `aiohttp` 实现非阻塞 - **错误处理**: ```python try: result = external_client.call_tool("divide", {"x": 10, "y": 0}) except MCPError as e: return {"error": f"External service failed: {e.code}"} ``` - **安全认证**:在请求头添加 JWT 或 API Key ```python headers={"Authorization": "Bearer <TOKEN>"} ``` ### 关键注意事项 1. **协议兼容性**:确保外部服务遵循标准 MCP 协议规范 2. **传输层适配**:根据目标服务支持 `HTTP`/`WebSocket`/`stdio`[^5] 3. **超时控制**:设置合理超时(建议 3-5 秒) 4. **服务发现**:复杂系统需集成服务注册中心(如 Consul) > 提示:对于复杂集成场景,建议封装可复用的 MCP Client SDK,统一处理序列化、重试和监控逻辑[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MonkeyKing.sun

对你有帮助的话,可以打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值