Simon Willison · 博客

无状态MCP重燃我的兴趣(催生mcp-explorer与datasette-mcp)

Stateless MCP has recaptured my interest (and inspired mcp-explorer and datasette-mcp)

二〇二六年八月一日 · 英文原文

2026-07-28版Model Context Protocol(MCP)规范发布,引入无状态MCP,将传统两阶段HTTP请求(initialize获取会话ID后调用工具)简化为单请求,通过`MCP-Protocol-Version`和`Mcp-Method`头传递协议版本与工具调用信息,降低客户端与服务端实现复杂度。作者构建了三个工具:mcp-explorer(交互式探测MCP服务器的CLI)、datasette-mcp(为Datasette实例添加`/-/mcp`端点,提供三个只读工具)、llm-mcp-client(LLM工具的MCP集成插件)。作者认为MCP比开放shell环境更易审计和控制,计划在敏感应用中更多依赖。

周二是无状态 MCP 日——MCP 2.0 的发布,或者用更正式但更难记的名字来说,是 2026-07-28 版 Model Context Protocol 规范。这是 MCP 规范自首次推出以来最重大的一次变更,也重新点燃了我个人对该协议的兴趣。背景说明:MCP 是 Model Context Protocol(模型上下文协议),它描述了一种向 LLM 驱动的 agent 框架暴露新工具的标准方式。该协议由 Anthropic 于 2024 年 11 月推出,在 2025 年大部分时间里引发了巨大关注,随后在人们发现一个能访问终端和 curl 的 agent 框架可以用更灵活的方式完成 MCP 的大部分功能后,它逐渐被 Skills(Anthropic 的另一项发明)所掩盖。我在 2025 年回顾中写过这一点。现在我又回到了 MCP。给 agent 一个能访问互联网的 shell 环境充满风险,并且需要一个足够强大的模型来有效驱动这样的环境。MCP 工具更容易审计和控制,而且足够简单,以至于能在笔记本电脑上运行的较小模型也能较好地驱动它们。新的无状态 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

我找不到一个很好的 CLI 工具来交互式地探测 MCP 服务器,所以让 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 ..."}

要只获取原始 SVG,可以在命令后加上 | jq .svg -r。我得到了这张图片。README 中还有几个其他命令,但你已经了解大致用法了。我发现构建这样的 CLI 工具是熟悉规范的一种非常高效的方式,即使大部分实际代码是由 agent 编写的。

datasette-mcp

第二个项目是 datasette-mcp,一个 Datasette 插件,它为任何 Datasette 实例添加一个 /-/mcp 端点。这大概是我第四次尝试构建这个插件,但得益于新的无状态 MCP 规范,我终于有了一个感觉可以发布的版本。它只提供三个工具:list_databases()get_database_schema(database_name)execute_sql(database_name, sql)。它们的功能正如你所预期——不过 execute_sql() 目前是只读的。将这些工具接入 agent,或像 ChatGPT 或 Claude 这样的聊天工具,它们就能对你托管的 Datasette 实例运行 SQL 查询。目前我正把它运行在我博客的 Datasette 镜像上,地址是 datasette.simonwillison.net/-/mcp。我花了一些功夫才弄清楚如何把它挂到 ChatGPT 和 Claude 上,但最终还是搞定了。这里有一个新的 TIL 展示了具体做法。这是一个共享的 Claude 会话,我问了它:列出 simonwillison.net 中的表,然后问:Simon 最近对 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):

Considering note count
I see the question "count the notes" is probably asking me to tally up blog notes.
It could also mean published notes or drafts, so there's some ambiguity there.
I'll need to figure out the total number of notes, likely by querying the count for both published notes and drafts to get a clear answer.
Let's execute that count!
There are 151 notes.

以及该提示的 llm logs 输出。一旦这个插件完全成熟,我考虑直接把它集成到 LLM 核心中。我也很期待在 Datasette Agent 和 llm-coding-agent 中尝试 MCP。

MCP 是用 agent 构建的更安全方式

在 MCP 首次发布几个月后,我写了《Model Context Protocol has prompt injection security problems》一文,指出让最终用户混搭工具的模式将避免数据外泄攻击的责任推给了用户自己。我当时还没有提出“致命三重奏”这个说法,但那绝对是我心中所想。随后,具有任意 shell 和 curl 访问权限的通用 agent 出现了,这更难保持安全!我对 MCP 逐渐欣赏的一点是,与在开放网络环境中执行任意命令相比(这是当今大多数通用和编码 agent 工具的默认方式),它更容易推理 agent 的能力以及可能出错的地方。我计划在基于 LLM 构建敏感应用时更多地依赖 MCP。

标签:projects、ai、datasette、mermaid、generative-ai、llms、llm、anthropic、model-context-protocol

译自 Simon Willison · 博客 · 录于 二〇二六年八月一日