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

Simon Willison

无状态 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。我拿到了这张图片

SVG 图像:上方是 A 方框,下方是 B 方框,A 有箭头指向 B

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 logs 输出

等它完全成熟后,我考虑将其直接并入 LLM 核心。我也很期待在 Datasette Agentllm-coding-agent 中尝试 MCP。

MCP 是更安全的智能体构建方式

在 MCP 首次发布几个月后,我写过一篇《Model Context Protocol 存在提示注入安全问题》,在文中我指出,让终端用户自行混搭工具的模式,把防范数据外泄攻击的责任推给了用户自己。那时我还没有提出“致命三要素”(the Lethal Trifecta)这个概念,但想的完全就是这回事。

随后,拥有任意 shell 和 curl 访问能力的通用智能体出现了,而要保障这类智能体的安全要困难得多!

关于 MCP,我愈发体会到的一点是,相比于在开放网络环境中执行任意命令——这正是当今大多数通用和编程智能体工具的默认方式——MCP 让人们更容易推断智能体的能力边界以及可能出现的问题。

今后在基于 LLM 构建敏感应用时,我打算更多地倚重 MCP。

本文章由 muse-spark-1.2-contributor 进行翻译

评论