以Streamable HTTP方式访问mcp server的过程

发布于:2025-07-21 ⋅ 阅读:(19) ⋅ 点赞:(0)

一、mcp server 部署

使用fastmcp框架 部署 mcp server, 以下是源代码

# 引入 fastmcp 依赖包
from fastmcp import FastMCP

# 新建fastmcp实例, 名字叫做 weather
mcp = FastMCP("weather")


@mcp.tool(name="weather", tags={"weather"})
def weather(city: str) -> str:
    """获取制定城市的天气信息
    :param city: 城市名称
    :return:    天气信息
    """
    print(f"正在获取 {city} 的天气信息...")
    return f"{city} 的天气是晴天,温度 25 度。"


if __name__ == '__main__':
    # mcp.run(transport="stdio")
    mcp.run(transport="streamable-http", host="0.0.0.0", port=8000, path="/weather")

运行python文件,启动 mcp server , 以下是启动成功界面
在这里插入图片描述

二、理解mcp服务调用原理

模型上下文协议 (MCP) 为客户端-服务器连接定义了严格的生命周期,以确保适当的功能协商和状态管理。

  • 初始化 :能力协商和协议版本协议
  • 操作 :正常协议通信
  • Shutdown:正常终止连接

在这里插入图片描述

初始化

初始化阶段必须是客户端和服务器之间的第一次交互。在此阶段,客户端和服务器:

  • 建立协议版本兼容性
  • 交换和获取功能
  • 获取实现详情

客户端必须通过发送包含以下内容的 initialize 请求来启动此阶段:

  • 支持的协议版本
  • 客户端功能
  • 客户端实现信息
initialize request
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize",
  "params": {
    "protocolVersion": "2025-03-26",
    "capabilities": {
      "roots": {
        "listChanged": true
      },
      "sampling": {}
    },
    "clientInfo": {
      "name": "ExampleClient",
      "version": "1.0.0"
    }
  }
}

initialize 请求不可以作为 JSON-RPC 的一部分 batch,因为在初始化完成之前,其他请求和通知是不可能的。这还允许向后兼容未明确支持 JSON-RPC 批处理的先前协议版本。

服务器必须使用自己的功能和信息进行响应:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "protocolVersion": "2025-03-26",
    "capabilities": {
      "logging": {},
      "prompts": {
        "listChanged": true
      },
      "resources": {
        "subscribe": true,
        "listChanged": true
      },
      "tools": {
        "listChanged": true
      }
    },
    "serverInfo": {
      "name": "ExampleServer",
      "version": "1.0.0"
    },
    "instructions": "Optional instructions for the client"
  }
}

成功初始化后,客户端必须发送初始化通知,以指示它已准备好开始正常作:

initialize notifications
{
  "jsonrpc": "2.0",
  "method": "notifications/initialized"
}

注意:

客户端 一定不能 在 服务器 响应 initialize 请求之前发送 ping 以外的请求。

服务器在接收初始化通知之前不应发送 ping 和 logging 以外的请求。

版本协商

在 initialize 请求中,客户端必须发送它支持的协议版本。这应该是客户端支持的最新版本 。
如果服务器支持请求的协议版本,则它必须使用相同的版本进行响应。否则,服务器必须使用它支持的另一个协议版本进行响应。这应该是服务器支持的最新版本 。

如果客户端不支持服务器响应中的版本,则它应该 断开。

能力协商

客户端和服务器功能确定在会话期间哪些可选协议功能将可用。
主要功能包括:

  • roots: 提供文件系统 root 能力
  • sampling: 支持 LLM采样请求
  • experimental: 描述对非标准实验性功能的支持
  • prompts: 提供提示模板
  • resources: 提供可读资源
  • tools: 公开可调用工具
  • logging: 发出结构化日志消息
  • completions: 支持参数自动补全
  • experimental: 描述对非标准实验性功能的支持
  • subscribe:支持订阅单个项目的更改(仅限资源)

功能对象可以描述子功能,例如:
listChanged:支持列表更改通知(用于提示、资源和工具)
subscribe:支持订阅单个项目的更改(仅限资源)

Operation

在操作阶段,客户端和服务端根据协商的能力进行消息交换。
双方都应该 :

  • 遵循协商的协议版本
  • 仅使用已成功协商的功能

Shutdown

在 shutdown 阶段,一端(通常是 client)干净地终止协议连接。没有定义特定的关闭消息,而是使用底层传输机制来发出连接终止信号:

stdio

对于 stdio 传输 ,客户端应通过以下方式启动关闭:

  • 首先,关闭子进程(服务器)的输入流
  • 等待服务器退出,如果服务器未在合理时间内退出,则发送 SIGTERM
  • 如果服务器在 SIGTERM 之后的合理时间内未退出,则发送 SIGKILL

服务器可以通过关闭其对 Client 端的输出流并退出来启动关闭。

HTTP 协议

对于 HTTP 传输 ,通过关闭关联的 HTTP 连接来指示关闭。

Timeouts

实现应为所有发送的请求建立超时,以防止连接挂起和资源耗尽。当请求在超时期限内未收到成功或错误响应时,发送者应针对该请求发出取消通知并停止等待响应。

SDK 和其他中间件应允许按请求配置这些超时。

实现可以选择在收到与请求相对应的进度通知时重置超时时钟,因为这意味着工作实际上正在进行。但是,无论进度通知如何,实现都应始终强制执行最大超时,以限制行为异常的客户端或服务器的影响。

Error Handling

实现应该准备好处理这些错误情况:

  • 协议版本不匹配
  • 无法协商所需的功能
  • 请求超时

初始化错误示例:

{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Unsupported protocol version",
    "data": {
      "supported": ["2024-11-05"],
      "requested": "1.0.0"
    }
  }
}

三、请求案例

1. 首先发送初始化请求

调用工具之前, 首先需要初始化连接:

  • 新增请求头: Accept: application/json, text/event-stream
  • 构建请求体
{
    "jsonrpc": "2.0",
    "method": "initialize",
    "params": {
    "protocolVersion": "2025-07-17",
    "capabilities": {},
    "clientInfo": {
      "name": "example-client",
      "version": "1.0.0"
    }
  },
    "id": 0
}
  • 请求测试
    在这里插入图片描述
    从相应头中,可以拿到 mcp-session-id:
    在这里插入图片描述

2. 响应初始化

  • 新增请求头: mcp-session-id:18251f0529ec4bef90da4d3ffb34b81b

  • 构建请求体:

{
  "jsonrpc": "2.0",
  "method": "notifications/initialized"
}

-检查响应, 服务器端返回的内容为空,正确
在这里插入图片描述

3. 在当前session下进行基本操作测试

3.1 获取可用工具列表
  • 请求体:
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/list",
  "params": {}
}
  • 响应内容
event: message
data: {"jsonrpc":"2.0","id":1,"result":{"tools":[{"name":"weather","description":"获取制定城市的天气信息\n:param city: 城市名称\n:return:    天气信息","inputSchema":{"properties":{"city":{"title":"City","type":"string"}},"required":["city"],"type":"object"},"outputSchema":{"properties":{"result":{"title":"Result","type":"string"}},"required":["result"],"title":"_WrappedResult","type":"object","x-fastmcp-wrap-result":true}}]}}
3.2 调用工具
  • 请求体:
{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "weather",
    "arguments": {
      "city": "深圳"
    }
  }
}
}
  • 响应内容
event: message
data: {"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"深圳 的天气是晴天,温度 25 度。"}],"structuredContent":{"result":"深圳 的天气是晴天,温度 25 度。"},"isError":false}}

常见错误

1. Not Acceptable: Client must accept both application/json and text/event-stream

报错信息
{
    "jsonrpc": "2.0",
    "id": "server-error",
    "error": {
        "code": -32600,
        "message": "Not Acceptable: Client must accept both application/json and text/event-stream"
    }
}
原因

客户端请求头没有支持 application/json 和 text/event-stream

解决

新增请求头: Accept: application/json, text/event-stream
在这里插入图片描述

2. Bad Request: Missing session ID

报错信息

{
“jsonrpc”: “2.0”,
“id”: “server-error”,
“error”: {
“code”: -32600,
“message”: “Bad Request: Missing session ID”
}
}

原因

客户端请求头没有 session id

解决

请求头新增字段:mcp-session-id , 这个 session id 需要 请求 初始化方法, 从相应头中获取

在这里插入图片描述

3. Invalid request parameters

报错信息
event: message
data: {"jsonrpc":"2.0","id":1,"error":{"code":-32602,"message":"Invalid request parameters","data":""}}
原因

没有 调用相应初始化方法

解决

请求响应初始化方法

  • 构建请求体:
{
  "jsonrpc": "2.0",
  "method": "notifications/initialized"
}


网站公告

今日签到

点亮在社区的每一天
去签到