Stateless 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 도구는 감사하고 통제하기가 더 쉽고, 노트북에서 실행되는 작은 모델도 충분히 잘 다룰 수 있을 만큼 단순하다.
새로운 stateless MCP 스펙은 프로토콜의 클라이언트와 서버를 구현하는 복잡도도 크게 낮춘다. 나는 이번 주에만 세 가지를 만들었다!
stateless MCP에서 더 쉬워진 점
stateful과 stateless MCP의 차이를 가장 잘 보여주는 예시는 새로운 스펙의 RC를 소개한 5월 21일자 블로그 포스트에 있다. 거기에는 전후를 비교하는 명확한 예시가 담겨 있다.
기존의 stateful 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"
}
}
}새로운 stateless 방식은 단 한 번의 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가 그 결과물이다. stateless 파이썬 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를 추가하면 된다. 나는 이 이미지를 받았다:
README에 몇 가지 명령어가 더 소개되어 있지만, 대략적인 감은 잡았을 것이다. 나는 이렇게 CLI 도구를 직접 만들어 보는 것이, 실제 코드 대부분을 에이전트가 작성하더라도 스펙에 익숙해지는 데 정말 생산적인 방법이라고 생각한다.
datasette-mcp
두 번째 프로젝트는 어떤 Datasette 인스턴스에도 /-/mcp 엔드포인트를 추가하는 Datasette 플러그인인 datasette-mcp다.
이 플러그인을 만들려고 시도한 게 아마 네 번째쯤 되는데, 새로운 stateless 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?
Claude는 답을 찾기 위해 총 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 Agent와 llm-coding-agent에서도 MCP를 실험해 볼 생각에 기대가 크다.
MCP는 에이전트를 더 안전하게 만드는 방법이다
MCP가 처음 출시된 지 몇 달 뒤, 나는 Model Context Protocol에는 프롬프트 인젝션 보안 문제가 있다라는 글을 썼다. 그 글에서 최종 사용자가 도구를 조합해 쓰는 패턴이 데이터 유출 공격을 막을 책임을 사용자 개인에게 떠넘긴다고 지적했다. 당시에는 아직 Lethal Trifecta라는 말을 만들지 않았지만, 정확히 그 개념을 염두에 둔 것이었다.
그러다 임의의 셸과 curl 접근 권한을 가진 범용 에이전트가 등장했는데, 그런 환경은 보안을 유지하기가 훨씬 더 어렵다!
MCP에 대해 내가 점점 높이 평가하게 된 점은, 개방된 네트워크 환경에서의 임의 명령 실행—오늘날 대부분의 범용 및 코딩 에이전트 도구의 기본값—보다 에이전트의 역량과 무엇이 잘못될 수 있는지를 훨씬 더 쉽게 추론할 수 있다는 것이다.
앞으로 LLM 위에 민감한 애플리케이션을 구축할 때는 MCP를 훨씬 더 적극적으로 활용할 계획이다.
글을 무작위로 읽기
댓글
로그인하고 댓글 남기기