无状态MCP重燃兴趣:三个新工具与Agent构建的思考
2026年7月31日
上周二是「无状态MCP日」——MCP 2.0正式推出,它的官方全称是《2026-07-28模型上下文协议规范》,虽更严谨却远不如前者好记。这是MCP自问世以来最重大的一次规范更新,也重新点燃了我对这个协议的兴趣。
先做个背景介绍:MCP即模型上下文协议(Model Context Protocol),它定义了一套标准,用于向大语言模型(LLM)驱动的智能体框架接入新工具。该协议由Anthropic于2024年11月推出,2025年曾引发广泛关注,后来却被Anthropic另一项发明「Skills」盖过风头——因为人们发现,只要给智能体配备终端和curl工具,就能以更灵活的方式实现MCP的大部分功能。我曾在《2025年度回顾》一文中探讨过这一点。
如今我重新关注起MCP。给智能体开放带联网权限的Shell环境风险极高,还需要性能强劲的模型才能驾驭;而MCP工具更易于审计和管控,且实现简单,哪怕是笔记本电脑上运行的小型模型也能较好地调用。
新的无状态MCP规范还大幅降低了协议客户端与服务端的实现复杂度——我这周就一口气搭建了三个相关项目!
无状态MCP的便捷之处
要理解有状态与无状态MCP的差异,最佳参考是今年5月21日发布的新规范候选版博客文章,其中给出了清晰的前后对比示例。
旧版有状态MCP(下称「传统MCP」)需要两次HTTP请求:第一次初始化会话并获取Mcp-Session-Id,第二次才真正调用工具:
POST /mcp HTTP/1.1
Content-Type: application/json
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-11-25",
"capabilities": {
},
"clientInfo": {
"name": "my-app",
"version": "1.0"
}
}
}
POST /mcp HTTP/1.1
Mcp-Session-Id: 1868a90c-3a3f-4f5b
Content-Type: application/json
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "search",
"arguments": {
"q": "otters"
}
}
}
而新的无状态模式只需一次HTTP请求:
POST /mcp HTTP/1.1
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: search
Content-Type: application/json
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "search",
"arguments": {
"q": "otters"
},
"_meta": {
"io.modelcontextprotocol/clientInfo": {
"name": "my-app",
"version": "1.0"
}
}
}
}
无论从客户端还是服务端的实现角度看,这种方式都简洁得多,也更适合构建可扩展的Web应用——无需在服务端维护会话ID状态,也不用操心将同一会话路由到同一后端服务器的问题。
mcp-explorer
我没找到能交互式调试MCP服务端的好用CLI工具,于是借助Codex开发了一款自己的工具——mcp-explorer。它是一款无状态Python CLI工具,无需安装即可通过uvx直接运行:
uvx mcp-explorer list https://agentic-mermaid.dev/mcp
这条命令会查询Ade Oshineye搭建的agentic-mermaid.dev演示MCP服务,返回的工具列表如下:
execute(code: string, timeoutMs?: integer) - 执行Mermaid SDK代码
在隔离沙箱中运行JavaScript并返回结果。
describe_sdk(family: string, detail?: string) - 描述Mermaid SDK操作
返回对应图表家族的版本匹配型变更操作。
render_svg(source: string, options?: object) - 将Mermaid渲染为SVG
将Mermaid源码字符串渲染为可自定义主题的SVG,返回{ ok, svg }。
render_ascii(source: string, useAscii?: boolean, targetWidth?: integer, options?: object) - 将Mermaid渲染为文本
将Mermaid源码字符串渲染为文本,返回{ ok, text }。
render_png(source: string, scale?: number, background?: string, fitTo?: object, options?: object) - 将Mermaid渲染为PNG
将Mermaid源码字符串栅格化为PNG,返回{ ok, png_base64 }。
...
查看工具详情可执行:
uvx mcp-explorer inspect render_svg
该命令会输出工具的完整信息,包括输入输出的JSON Schema。
调用工具并传入参数:
uvx mcp-explorer call \
https://agentic-mermaid.dev/mcp \
render_svg \
-a source 'graph TD; A-->B' \
-a options '{"padding":24}'
返回结果如下:
{"ok":true,"svg":"<svg xmlns=\"http://www.w3.org/2000/svg\" width=...
如果只想获取原始SVG内容,可在命令后追加| jq .svg -r,最终得到的就是这张图。
README里还有更多命令,核心用法大致如此。我发现,哪怕大部分代码由AI生成,开发这类CLI工具仍是熟悉技术规范的高效方式。
datasette-mcp
第二个项目是datasette-mcp,这是一款Datasette插件,可为任意Datasette实例添加/-/mcp端点。
这大概是我第四次尝试开发这款插件了,多亏新的无状态MCP规范,我终于做出了满意的可发布版本。它仅提供三个工具:list_databases()、get_database_schema(database_name)和execute_sql(database_name, sql),功能一目了然——目前execute_sql()仅支持只读操作。
将这些工具接入智能体,或是ChatGPT、Claude等聊天工具,就能让它们对托管的Datasette实例执行SQL查询。
目前我已在个人博客的Datasette镜像站点启用该插件,地址是datasette.simonwillison.net/-/mcp。我花了些功夫才搞清楚如何将其接入ChatGPT和Claude,最终成功实现,还专门写了一篇TIL(今日新知)记录具体步骤。
在这个共享Claude会话中,我先后提问:
列出simonwillison.net的所有表格
以及
Simon最近关于MCP说了些什么?
Claude总共执行了7次独立SQL查询才得出答案。
llm-mcp-client
我的LLM工具早就该接入官方MCP了,新推出的alpha版llm-mcp-client插件正是为此而生:
llm install llm-mcp-client
llm -T 'MCP("https://datasette.simonwillison.net/-/mcp")' '统计笔记数量'
以下是输出结果(包含推理过程,我使用的是LLM 0.32rc2):
思考笔记统计方式 用户问的「统计笔记数量」应该是指统计博客笔记总数,但可能包含已发布和草稿两种状态,存在歧义。我需要分别查询已发布和草稿笔记的数量,再汇总得到清晰结果。现在执行统计!
共有151篇笔记。
对应的llm日志输出可查看详细过程。
等这个插件成熟后,我打算将其直接整合进LLM核心功能,还计划在Datasette Agent和llm-coding-agent中测试MCP的应用场景,对此我充满期待。
MCP是构建智能体的更安全方案
MCP刚发布几个月时,我曾撰文《模型上下文协议存在提示注入安全风险》,指出让终端用户自行组合工具的模式,会将防范数据泄露攻击的责任转嫁给用户。当时我还未提出「致命三重奏」(Lethal Trifecta)的概念,但这正是我当时担忧的核心问题。
后来出现了支持任意Shell和curl调用的通用智能体,安全管控难度更是陡增!
如今我越发意识到,相比在开放网络环境中执行任意命令(当前多数通用智能体和代码智能体的默认模式),MCP的优势在于能更清晰地界定智能体的能力范围,预判潜在风险。
未来在基于LLM构建敏感应用时,我会更多采用MCP方案。