Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

mcp-stateless-http-demo

基于 MCP 2.0 SDK无状态 Streamable HTTP 最小可运行示例,并落地了 2026-07-28 协议大版本 的关键新特性。

约束遵循 项目开发文档.md(SDD):无 stdio、无 SSE、无 session-id、无 LLM。

一、项目定位:新版本要解决什么

MCP 协议在 2026-07-28 迎来了一次大版本升级(协议 era 从 2024/2025 演进到 2026-07-28)。本项目是这次升级的最小落地示范,用一套可运行的 Server + Client 验证"现代协议时代"的完整交互流程,重点回答三件事:

  • 协议怎么变 —— 移除 initialize 握手、改走 server/discover,请求自动携带协议版本头;
  • 架构怎么变 —— 从"有状态会话"变为"无状态请求自包含",服务端零会话存储;
  • 生产能力补了什么 —— 工具列表缓存、W3C Trace Context 分布式追踪、单行请求/响应日志、MCP Apps 交互式 UI。
能力 解决的问题 以前 MCP(传统模式) 新方向(Server/Capability 模式) 对 Agent / Planner 的影响 ChatBI 示例
1. 动态发现(Dynamic Discovery) 工具和服务越来越多,不想全部写死 Client 先知道有哪些 Server,然后调用 tools/list 获取工具 Client 可以先发现业务能力 / Server,再发现 Server 下的 Tool Planner 不需要维护所有工具清单,可以根据任务找到对应能力 用户问“分析销售下降原因”,先发现 Sales Analytics Server,再选择 analyze_growth
核心变化 发现范围扩大 Server → Tools Capability → Server → Tools 从“找工具”变成“找解决问题的能力” 从找 query_sales,变成找“销售分析能力”
2. Server 自描述能力增强(Server Self Description) Agent 不知道一个 Server 能干什么 Tool 有 namedescriptioninputSchema Server 自己描述领域、业务对象、能力范围、权限、工具集合 Planner 可以理解业务边界,不只是看到工具名字 Sales MCP Server 描述:负责销售领域,支持收入、订单、客户分析
核心变化 描述粒度提升 Tool 级描述 Server / Domain 级描述 LLM 可以先理解“这个服务适合解决什么问题” 不只是看到 query_sales,而知道“这是销售分析领域能力”
3. 对 Agent Planner 更友好(Planner-oriented) Tool 数量爆炸,Prompt 塞不下 Planner Prompt 中维护大量工具列表 Planner 根据当前任务加载相关能力和工具 Planner 从“工具选择”升级为“能力选择 + 工具选择” 先选择 Sales Analytics 能力,再选择 query_sales、analyze_growth

一句话总结新版本的价值:

旧版本演示"MCP 怎么跑起来";新版本演示"MCP 在 2026-07-28 时代该怎么设计"——无状态、可观测、可缓存、可交互。

二、新版本特性总览(功能 + 意义)

能力 说明 意义
MCP Server / Client 分别位于 server/client/ 一个仓库同时示范两端,闭环验证协议
Streamable HTTP http://localhost:8000/mcp,仅 POST 单一 HTTP 端点,对网关/防火墙友好
Stateless(无状态) 服务端不保存 session、不产生 mcp-session-id,每个请求自包含 无共享状态 → 天然支持水平扩展、容器化/边缘部署,故障恢复简单
现代协议 era(2026-07-28) 客户端用 server/discover 握手,移除 initialize,自动发送 Mcp-Method / Mcp-Name / Mcp-Protocol-Version 单请求完成握手、请求自描述,连接更快、更贴近 REST 语义
Tools add multiply query_order list_orders get_user semantic_query 覆盖算术、业务查询、语义查询(§13 Phase 2 预览)
Resource resource://company/profile(静态)、resource://orders/{order_id}(模板) 数据资源化访问,支持动态参数
Prompt welcomeorder_analysis(order_id) 提示词模板化,便于复用 LLM 引导
MCP Apps(2026-07-28) orders_table 工具绑定 ui://orders/table HTML 资源 工具输出从"纯文本"升级为"可交互界面"
tools/list 缓存(2026-07-28) 响应带 _meta.ttlMs / cacheScope 减少重复拉取,降低 LLM 应用的延迟与带宽
W3C Trace Context(2026-07-28) OpenTelemetry 分布式追踪,客户端 span 跨进程级联到服务端 端到端链路可观测,生产排障必备
请求/响应日志 每个 JSON-RPC 请求打印 [REQ] / [RESP](单行、可 grep) 审计友好,无需额外中间件即可排查问题

三、与旧版本对比:优势一目了然

旧时代 = 2024/2025 协议(基于 initialize + 会话);现代时代 = 2026-07-28 协议。

维度 旧时代(2024/2025) 现代时代(2026-07-28) 新版本优势
握手方式 initialize 协商,需维护握手状态 server/discover 单请求完成 少一轮往返、更简单,接近普通 HTTP 调用
会话模型 有状态,携带 mcp-session-id 无状态,每个请求自包含 可水平扩展、部署/运维简化、无粘性会话需求
传输格式 默认 SSE 流式响应 Streamable HTTP,支持 json_response=True 返回纯 JSON 客户端实现简单,任何 HTTP 栈都能对接
方法路由 靠请求体 method 字段识别 自动带 Mcp-Method / Mcp-Name / Mcp-Protocol-Version 网关/代理可直接按头路由,无需解析 body
工具列表 每次全量拉取 ttlMs / cacheScope 缓存提示 减少重复请求,LLM 应用首屏响应更快
UI 能力 仅文本/结构化输出 MCP Apps(ui:// 交互式 HTML) 工具结果可渲染成可操作界面,而非静态文本
可观测性 无标准追踪 W3C Trace Context(OTel) 跨进程分布式追踪,一个 trace_id 贯穿两端
请求审计 [REQ] / [RESP] 单行中间件 可直接 grep、可审计,无需额外部署

核心结论:新版本不是在旧模型上打补丁,而是把 MCP 的传输层、握手层、能力层全部升级——从"会话式 RPC"变成"无状态可观测的 HTTP 风格协议",这让 MCP Server 可以像普通 Web 服务一样部署、扩容、排障。

目录结构

.
├── 项目开发文档.md          # SDD 规范(§5/6/7/8 为实现依据)
├── pyproject.toml          # 依赖(mcp[cli]>=2.0.0、fastapi、uvicorn、opentelemetry-sdk)
├── README.md
├── server/                 # MCP Server
│   ├── main.py             # 入口 + McpServerLauncher 启动类
│   ├── tools.py            # 工具 / 资源 / 提示词 / MCP Apps 定义
│   ├── logging_middleware.py  # 请求/响应日志中间件(ServerMiddleware)
│   └── tracing.py          # OpenTelemetry 追踪配置(Console/OTLP 导出)
└── client/                 # MCP Client
    ├── main.py             # discover → list → call 流程演示
    └── tracing.py          # 客户端追踪 + W3C traceparent 注入

快速开始

环境:Python 3.12+,包管理用 uv

# 1. 安装依赖
uv sync

# 2. 启动服务端(终端 1)
.venv/bin/python server/main.py
#   -> Uvicorn running on http://127.0.0.1:8000
#   或使用启动类自定义端口:
#   .venv/bin/python -c "from server.main import McpServerLauncher; McpServerLauncher(port=9000).run()"

# 3. 运行客户端(终端 2)
.venv/bin/python client/main.py

客户端预期输出:

[Connected] modern protocol, supported=2026-07-28
  client root span: 337fa235c2870945c7d857160f4dd58b
[Available Tools] orders_table, add, multiply, query_order, list_orders, get_user, semantic_query
  [cache] tools/list ttlMs=300000 cacheScope=public
[Available Resources] ui://orders/table, resource://company/profile
[Available Prompts] welcome, order_analysis
[call] add(10, 20) -> 30
[call] query_order('10001') -> {...}
[resource] company/profile -> ...
[mcp-app] ui://orders/table -> <!DOCTYPE html>...
[prompt] order_analysis('10003') -> ...
[error] unknown tool -> is_error=True: Unknown tool: unknown

注意输出中的 [cache] tools/list ttlMs=300000 cacheScope=public[mcp-app] ui://orders/table——这两行就是 2026-07-28 新特性(缓存提示、MCP Apps)的实际效果。

架构与协议流程

MCP Client(modern era)
   │  discover(server/discover) ── Mcp-Method / Mcp-Protocol-Version 头,无会话
   │  list_tools / call_tool / read_resource / get_prompt
   ▼  (每个请求自包含:capabilities/clientInfo 放 _meta,带 traceparent)
MCP Server(stateless_http=True, json_response=True)
   │  OpenTelemetryMiddleware 提取 traceparent 建 span
   │  RequestResponseLoggingMiddleware 打印 [REQ]/[RESP]
   ▼
Tools / Resources / Prompts / MCP Apps(MCPServer 注册)

协议时代说明:SDK 2.0 支持"双时代"——首个请求是 initialize 走旧协议(2024/2025 版本), 首个请求是 server/discover(或带 2026 封装的请求)则进入现代 era(2026-07-28)。 本 demo 客户端显式走现代 era。

2026-07-28 新特性实现细节

1. 无状态化 + 移除 initialize 握手

  • 服务端:streamable_http_app(stateless_http=True) —— 每次请求新建 transport,零会话存储。
  • 客户端:session.discover() 替代 session.initialize() —— 请求自动携带 Mcp-Protocol-Version / Mcp-Method / Mcp-Name 头。
  • 验证:裸 curl -Dmcp-session-id 响应头;连续请求各自独立。

2. tools/list 缓存(ttlMs / cacheScope)

server/tools.py 中:

cache_hints={
    "tools/list": CacheHint(ttl_ms=300_000, scope="public"),
    "server/discover": CacheHint(ttl_ms=60_000, scope="public"),
}

响应会带上 cacheScope/ttlMs;客户端可据此缓存 tools/list 结果 5 分钟。

3. MCP Apps(交互式 HTML 工具)

apps = Apps()
@apps.tool(resource_uri="ui://orders/table")
async def orders_table() -> list[dict]: ...
apps.add_html_resource("ui://orders/table", html, ...)
mcp = MCPServer(..., extensions=[apps])

宿主(如 Claude Desktop)在沙箱 iframe 渲染 ui:// HTML,经 postMessage + JSON-RPC 双向通信,把工具返回的数据渲染成可交互表格。本 demo 是文本客户端,只能读取 HTML 源文件, 不渲染界面。

4. W3C Trace Context(OTel)

  • 服务端:SDK 内置 OpenTelemetryMiddleware 为每个请求建 span (mcp.method.name / mcp.protocol.version / gen_ai.tool.name)。
  • 客户端:trace_meta()opentelemetry.propagate.injecttraceparent 写进每个请求的 _meta,服务端 extract 出上下文 → 服务端 span 挂到客户端 span 下。
  • 观察:客户端与服务端日志中 span 共享同一 trace_id,且 parent_id 指向客户端对应请求 span。
  • 导出器:默认 ConsoleSpanExporter(打印 span);设置环境变量 OTEL_EXPORTER_OTLP_ENDPOINT 可切换为 OTLP(需另装 opentelemetry-exporter-otlp-proto-http)。

请求/响应日志

server/logging_middleware.py 基于 SDK 的 ServerMiddleware 协议实现, 在每个 JSON-RPC 请求前后记录方法与参数/结果(单行、长内容截断到 500 字符):

08/01 15:54:43 INFO    [REQ ] id=5 method=tools/call params={"name": "add", "arguments": {"a": 10, "b": 20}, "_meta": {...}}
08/01 15:54:43 INFO    [RESP] id=5 method=tools/call result={"content": [{"text": "30", "type": "text"}], "isError": false, ...}

处理失败时记 [RESP] ... error=... 后原样抛出。

验证清单(AC)

  • AC-001 服务端启动:python server/main.pyUvicorn running on http://127.0.0.1:8000
  • AC-002 客户端连接:[Connected] ...
  • AC-003 工具发现:add, multiply, query_order, list_orders, get_user, semantic_query(+ orders_table)
  • AC-004 工具调用:add(10, 20) -> 30
  • AC-005 无状态验证:无 mcp-session-id,连续请求独立
  • §10 错误场景:未知工具/非法参数以 is_error=True 结果返回
  • 2026 新特性:现代协议 era、tools/list 缓存、MCP Apps、W3C Trace Context

注意事项

  1. 文本客户端无法渲染 MCP Apps 界面;想看交互式 UI 需使用支持 MCP Apps 的宿主。
  2. OTel span 由 BatchSpanProcessor 异步导出,打印可能晚于请求执行。
  3. 客户端 span 输出会混在演示输出里(结尾统一 flush),属正常现象。
  4. _ORDERS / _USERS 为只读演示数据(相当于 mock 数据库),不违反无状态要求。

About

MCP协议新特性

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages