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

Simon Willison

ステートレスMCPが再び私の興味を捉えた(そしてmcp-explorerとdatasette-mcpを生んだ)

原文は Simon Willison により に公開されました。 このブログを購読する

火曜日はStateless MCPデーだった――MCP 2.0、あるいはより形式的だが覚えにくい名前で呼べば2026-07-28版 Model Context Protocol仕様のローンチだ。これはMCP仕様が最初に登場して以来、最も大きな変更であり、私のこのプロトコルへの関心を再び呼び起こしてくれた。

背景を説明しておくと、MCPはModel Context Protocolの略で、LLMを搭載したエージェントフレームワークに新しいツールを公開するための標準的な方法を定めたものだ。Anthropicによって2024年11月に導入され、2025年の大半を通じてとてつもない盛り上がりを見せたが、ターミナルとcurlにアクセスできるエージェントハーネスがあればMCPでできることのほとんどをより柔軟に実現できると分かってからは、(同じくAnthropicが生み出した)Skillsにやや影が薄くなった時期もあった。この件については2025年の振り返りで書いている。

私は今、再びMCPに注目している。インターネットにアクセスできるシェル環境をエージェントに与えることはリスクに満ちており、その環境をうまく操れる高性能なモデルを必要とする。MCPツールは監査や制御がしやすく、ノートPCで動くような小型モデルでも十分に扱えるほどシンプルだ。

新しいステートレスMCP仕様は、プロトコルのクライアントとサーバーの両方の実装の複雑さも大幅に下げてくれる。今週、私はその実装を3つ作った!

ステートレスMCPで何が簡単になったか

新旧の違いを最もよく示しているのは、新仕様のRCを発表した5月21日のブログ投稿だ。そこには分かりやすいビフォーアフターの例が載っている。

古いステートフルなMCP(ここでは「レガシーMCP」と呼ぶことにする)では、2回の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"
    }
  }
}

新しいステートレス方式では、1回の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"
      }
    }
  }
}

クライアント側から見てもサーバー側から見ても、これははるかにすっきりしている。スケーラブルなウェブアプリケーションを構築する上でも適しており、セッション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スキーマなど、大量の情報が出力される。

そのツールを呼び出して引数を渡すには。

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を付け足せばいい。私はこの画像を得られた。

AのボックスからBのボックスへ矢印が伸びているSVG

READMEには他にもいくつかコマンドが載っているが、だいたいの雰囲気はつかめるだろう。このようなCLIツールを作るのは、たとえ実際のコードのほとんどをエージェントが書いたとしても、仕様に親しむ上で非常に生産的な方法だと感じている。

datasette-mcp

2つ目のプロジェクトはdatasette-mcp、あらゆるDatasetteインスタンスに/-/mcpエンドポイントを追加するDatasetteプラグインだ。

このプラグインを作ろうとするのはおそらく4回目だが、新しいステートレスMCP仕様のおかげで、ようやくリリースする気になれる出来のものができた。

提供するツールはたった3つ、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統合がずっと待たれていた。新しいアルファ版の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 has prompt injection security problemsという記事を書いた。そこでは、エンドユーザーがツールを自由に組み合わせるというパターンが、データ流出攻撃を防ぐ責任をユーザー自身に押し付けてしまうと指摘した。当時はまだLethal Trifectaという言葉を作る前だったが、まさにそのことを念頭に置いていた。

その後、任意のシェルやcurlへのアクセスを持つ汎用エージェントが登場したが、そちらの方がはるかにセキュアに保つのが難しい!

MCPについて私が評価するようになったのは、開放的なネットワーク環境での任意のコマンド実行――今日の多くの汎用エージェントやコーディングエージェントツールのデフォルトだ――と比べて、エージェントに何ができ、何が問題になりうるかを推論しやすいことだ。

今後、LLMの上にセンシティブなアプリケーションを構築する際には、MCPをもっと積極的に活用していくつもりだ。

この記事は「muse-spark-1.2-contributor」を使用して翻訳されました。

コメント