无状态 MCP 重新点燃了我的兴趣(并催生了 mcp-explorer 和 datasette-mcp)
原文由 Simon Willison 于 发布,订阅该博客
周二是无状态 MCP 日——MCP 2.0 正式发布的日子,或者用更正式但没那么好记的名字来说,就是2026-07-28 版 Model Context Protocol 规范。这是该规范自发布以来最重大的变更,也让我个人对这一协议重新燃起了兴趣。
先补充一下背景:MCP 即 Model Context Protocol(模型上下文协议),它定义了一种向基于 LLM 的智能体框架开放新工具的标准方式。它由 Anthropic 早在2024 年 11 月推出,在 2025 年的大部分时间里热度飙升,随后却在一定程度上被Skills(同样是 Anthropic 的另一项发明)盖过了风头——因为人们发现,只要给智能体容器提供终端和 curl 的访问能力,就能以更灵活的方式完成 MCP 所做的绝大部分事情。我在2025 年回顾中写过这件事。
现在我又重新关注起 MCP 了。给智能体一个能够联网的 shell 环境风险重重,而且需要足够强大的模型才能有效驾驭这样的环境。MCP 工具则更容易审计和管控,也足够简单,即便是能在笔记本电脑上运行的小模型,也能较好地驱动它们。
新的无状态 MCP 规范还大幅降低了实现该协议客户端和服务端的复杂度。这周我就做了三个!
无状态 MCP 带来了哪些简化
展示有状态与无状态 MCP 差异的最佳例子,来自这篇5 月 21 日的博客文章,它介绍了新规范的候选发布版(RC)。文章中给出了一个清晰的前后对比示例。
旧版有状态的 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) - Execute Mermaid SDK code
Run JavaScript in an isolated sandbox; return a value.
describe_sdk(family: string, detail?: string) - Describe Mermaid SDK operations
Return version-matched mutation operations for one diagram family.
render_svg(source: string, options?: object) - Render Mermaid as SVG
Render a Mermaid source string to themeable SVG. Returns { ok, svg }.
render_ascii(source: string, useAscii?: boolean, targetWidth?: integer, options?: object) - Render Mermaid as text
Render a Mermaid source string to text. Returns { ok, text }.
render_png(source: string, scale?: number, background?: string, fitTo?: object, options?: object) - Render Mermaid as PNG
Rasterize a Mermaid source string to PNG. Returns { 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 里还有更多命令,但大致就是这样。我发现,像这样构建 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 会话,我在其中问了它:
list tables in simonwillison.net
然后:
what has Simon said recently about MCP?
它运行了 7 次独立的 SQL 查询才得出答案。
llm-mcp-client
我的 LLM 工具早就该有官方的 MCP 集成了。新的 alpha 版 llm-mcp-client 插件正是我对此的尝试:
llm install llm-mcp-client
llm -T 'MCP("https://datasette.simonwillison.net/-/mcp")' 'count the notes'以下是输出(包含推理过程,我用的是 LLM 0.32rc2):
正在思考笔记数量
我看到“count the notes”这个问题很可能是在问博客笔记的总数。它也可能指已发布的笔记或草稿,所以存在一定的歧义。我需要算出笔记的总数,很可能要通过查询已发布笔记和草稿的数量来得出明确的答案。来执行这个统计吧!
共有 151 条笔记。
等它完全成熟后,我考虑将其直接并入 LLM 核心。我也很期待在 Datasette Agent 和 llm-coding-agent 中尝试 MCP。
MCP 是更安全的智能体构建方式
在 MCP 首次发布几个月后,我写过一篇《Model Context Protocol 存在提示注入安全问题》,在文中我指出,让终端用户自行混搭工具的模式,把防范数据外泄攻击的责任推给了用户自己。那时我还没有提出“致命三要素”(the Lethal Trifecta)这个概念,但想的完全就是这回事。
随后,拥有任意 shell 和 curl 访问能力的通用智能体出现了,而要保障这类智能体的安全要困难得多!
关于 MCP,我愈发体会到的一点是,相比于在开放网络环境中执行任意命令——这正是当今大多数通用和编程智能体工具的默认方式——MCP 让人们更容易推断智能体的能力边界以及可能出现的问题。
今后在基于 LLM 构建敏感应用时,我打算更多地倚重 MCP。
随机一篇博客
评论
登录后参与讨论