无状态 MCP 重新激发了我的兴趣。
Stateless MCP has recaptured my interest

原始链接: https://simonwillison.net/2026/Jul/31/stateless-mcp/

2026年7月发布的模型上下文协议(MCP)2.0,即“无状态 MCP”,标志着智能体互操作性的一次重大演进。通过从有状态的多请求握手转向简化的单请求模型,此次更新大幅降低了实现复杂度,并提升了 Web 应用的可扩展性。 作者指出,作为一种更安全、更易审计的替代方案,MCP 重新获得了关注,以取代赋予 LLM 智能体不受限制的 Shell 或互联网访问权限。尽管通用的智能体框架功能强大,但它们存在重大的安全风险;而 MCP 工具提供了一个受控接口,即使是较小的本地模型也能有效地进行管理。 为了探索这一更新后的规范,作者开发了三个项目: 1. **mcp-explorer**:一个用于交互式探测和调用 MCP 服务器工具的无状态命令行工具。 2. **datasette-mcp**:一个允许针对 Datasette 实例进行基于 SQL 查询的插件,使智能体能够安全地与实时数据进行交互。 3. **llm-mcp-client**:LLM 命令行工具的集成插件,使智能体能够直接与 MCP 服务器交互。 最终,作者认为无状态 MCP 是构建安全、稳健应用程序的更优基础,这标志着向更加可控和可预测的智能体生态系统迈进。

最近的 Hacker News 讨论凸显了围绕模型上下文协议(MCP)日益激烈的争论,特别是其向无状态架构的转变。 **主要议题:** * **无状态与有状态:** 许多参与者认为最初的有状态 MCP 设计过度复杂。转向无状态模型使其与标准的 REST/JSON-RPC 实践保持一致,从而更具可扩展性,并更易于与 API 网关和负载均衡器等现有基础设施集成。 * **“CLI 与 MCP”之争:** 很大一部分人认为,简单的 CLI 工具或 Shell 访问优于 MCP。他们认为 CLI 工具更灵活、更具可组合性(例如使用管道),且 LLM 对其理解更好;而有些人则将 MCP 视为一种“厂商锁定”的协议,是在重造 CORBA 或 REST 等现有标准。 * **上下文管理:** 批评者指出,MCP 可能导致“上下文膨胀”,即未使用的工具描述会消耗宝贵的 Token 额度。支持者则认为,现代的“渐进式披露”(如工具搜索或开关工具)已在很大程度上解决了这一问题,尽管仍有人坚持认为“技能”(按需调用)效率更高。 * **企业价值:** 支持者捍卫 MCP,认为它是必要的“标准化”层,允许 LLM 安全地与私有企业系统交互,在标准 REST API 可能缺乏必要 AI 友好元数据的情况下,提供细粒度的身份验证和发现功能。
相关文章

原文

31st July 2026

Tuesday was Stateless MCP day—the rollout of MCP 2.0, or the 2026-07-28 Model Context Protocol specification to use the more formal but less memorable name. This is the most significant change to the MCP spec since it first launched, and has also served to reignite my personal interest in the protocol.

For background: MCP is the Model Context Protocol, which describes a standard way to expose new tools to LLM-powered agent frameworks. It was introduced by Anthropic back in November 2024, had a huge spike of interest through much of 2025, and then became somewhat eclipsed by Skills (another Anthropic invention) when it became apparent that an agent harness with access to a terminal and curl could do most of what MCP did in a more flexible way. I wrote about that in my review of 2025.

I’m coming back around to MCP now. Giving an agent a shell environment with the ability to access the internet is fraught with risk, and requires a strong model that is capable of effectively driving such an environment. MCP tools are easier to audit and control, and simple enough that smaller models that run on a laptop can still drive them reasonably well.

The new stateless MCP specification also greatly decreases the complexity of implementing both clients and servers for the protocol. I built three of those this week!

What’s easier with stateless MCP

The best demonstration of the difference between stateful and stateless MCP is in this May 21st blog post that introduced the RC for the new specification. It included a clear before-and-after example.

The older stateful MCP (I’m going to call it “legacy MCP”) required two HTTP requests—the first to initialize a session and obtain a Mcp-Session-Id, and the second to actually call the tool:

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"
    }
  }
}

The new stateless way uses a single HTTP request which looks like this:

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"
      }
    }
  }
}

This is so much cleaner from both a client- and server-side implementation perspective. It’s also a better fit for building scalable web applications, since now you don’t need to maintain server-side state to keep track of those session IDs, or worry about routing the same session to the same backend machine.

mcp-explorer

I couldn’t find a great CLI tool for interactively probing an MCP server, so I had Codex help build my own.

mcp-explorer is the result. It’s a stateless Python CLI tool, so you don’t even need to install it to try it out—it works with uvx like this:

uvx mcp-explorer list https://agentic-mermaid.dev/mcp

This queries Ade Oshineye’s agentic-mermaid.dev demo MCP. The above command returns the following list of tools:

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 }.
...

Then to inspect a tool:

uvx mcp-explorer inspect render_svg

This outputs a whole bunch of information, including the JSON schema of the inputs and outputs.

To call that tool and pass arguments to it:

uvx mcp-explorer call \
  https://agentic-mermaid.dev/mcp \
  render_svg \
  -a source 'graph TD; A-->B' \
  -a options '{"padding":24}'

Which returns:

{"ok":true,"svg":"<svg xmlns=\"http://www.w3.org/2000/svg\" width=...

To get just the raw SVG try adding | jq .svg -r to that command. I got back this image:

SVG of as A box on top of a B box with an arrow from A to B

There are a few more commands in the README, but you get the general idea. I find building CLI tools like this to be a really productive way to get familiar with a specification, even if an agent writes most of the actual code.

datasette-mcp

The second project is datasette-mcp, a Datasette plugin which adds a /-/mcp endpoint to any Datasette instance.

This is probably the fourth time I’ve tried building this plugin, but thanks to the new stateless MCP specification I finally have a version that feels good to release.

It provides just three tools: list_databases(), get_database_schema(database_name), and execute_sql(database_name, sql). They do exactly what you would expect them to do—though execute_sql() is read-only for the moment.

Wire these into an agent, or a chat tool like ChatGPT or Claude, and they’ll gain the ability to run SQL queries against your hosted Datasette instance.

So far I’m running it on the Datasette mirror of my blog, at datasette.simonwillison.net/-/mcp. It took a bit of fiddling to figure out how to attach that to ChatGPT and Claude, but I got there in the end. Here’s a new TIL showing exactly how to do that.

Here’s a shared Claude session where I asked it:

list tables in simonwillison.net

And then:

what has Simon said recently about MCP?

It ran 7 separate SQL queries to figure out the answer.

llm-mcp-client

My LLM tool is long overdue for an official MCP integration. The new alpha llm-mcp-client plugin is my attempt at exactly that:

llm install llm-mcp-client
llm -T 'MCP("https://datasette.simonwillison.net/-/mcp")' 'count the notes'

Here’s the output (including reasoning trace, I’m using 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.

And the output of llm logs for that prompt.

Once this is fully baked, I’m considering bringing it directly into LLM core. I’m excited to experiment with MCP in Datasette Agent and llm-coding-agent as well.

MCP is a safer way to build with agents

A few months after MCP was first released, I wrote Model Context Protocol has prompt injection security problems, where I noted that the pattern of having end users mix and match tools pushed responsibility for avoiding data exfiltration attacks out to the users themselves. I hadn’t coined the Lethal Trifecta yet, but that was absolutely what I had in mind.

Then general agents with arbitrary shell and curl access came along, and that’s so much harder to keep secure!

Something I’ve come to appreciate about MCP is that it’s much easier to reason about agent capabilities and what might go wrong than with arbitrary command execution in an open network environment—the default for most of today’s general and coding agent tools.

I plan to lean into MCP a whole lot more when I’m building sensitive applications on top of LLMs.

联系我们 contact @ memedata.com