Stateless MCP has recaptured my interest(simonwillison.net) |
Stateless MCP has recaptured my interest(simonwillison.net) |
Like a tool where the AI can only call out to certain APIs based on a config file the agent cannot change.
That way you can leverage all the shell knowledge agents already have while still limiting what network calls they can make, and you wouldn't have to set up a server to use an agent.
As a result, everything compounds. The work I do doesn't need to be re-derived by the work you do. Typed models keep everything repeatable and deterministic. Huge reduction in token spend and huge increase in speed.
I love this question because of the diverse answers given and we learn from each other.
The idea could be that the agent runs as a unix user. That user has execute access to these scripts but not read or write access.
So the agent can only do what those scripts allow, the scripts present an API. You could let agents call the scripts with -h to get instructions, and just put some text into context saying like "to access helper scripts call ./showHelp".
From my experience, instructions in `.md` files aren't enough. In fact, Claude seems to prefer the shell over even its own built-in tools (e.g. `read`, `edit` etc.) See: https://github.com/anthropics/claude-code/issues/19649. It's like an old dog that needs to be tricked to learn new tricks.
[1] Recently: Opus 4.7/4.8 and Kimi K3
If I understand correctly, you’re not answering my question, and you’re not talking about MCP either. Have you considered that maybe you’re in the wrong thread?
What? I never realized it was the case. I was calling my handcrafted MCP server without the header all the time!
Is that roughly accurate? We are talking about a technology that would allow SaaS API vendors to publish a second set of documentation (or adapt the same one to both human and LLM targetted channels)?
I'm being a bit cynical, but here's my minimal naïve alternative solution, not proposing it be used, but just saying that this would be equivalent:
1- Publish a well known endpoint like /docs_url , which could return something like "docs.domain".
2- Support an alternative content-type header, where instead of 'text/html' the client could ask for 'text/plain', or 'text/markdown'
2b- Alternatively, changing the extension type could achieve the same result, where requesting docs.domain/endpoint.txt would return the docs in text instead of in html
So now I ask, how is MCP better than the solution I proposed? Did I grossly misunderstand something? Or am I on track to avoiding hundreds of engineering hours due to accidental complexity by recognizing and avoiding a privately funded protocol that serves to increase vendor lock instead of reducing complexity of our systems?
In this specific post the SQL example gets around this by being super low-level, inherently composable, and low context consumption itself. I’m not sure this works for everyone.
Looking at the Linear MCP for example, they basically just want to expose an API surface and provide 32 MCP tools as a result of that. My agents do run into composability problems here all the time and fall back to the tool they included which allows you to run arbitrary GraphQL. This takes a couple tries usually. I don’t really know what the sensible alternative to this is.
Cloudflare switched to “code mode” which basically provides 2 MCP tools: search and execute. Both take a TS arrow function, the first allows you to programmatically search a TS api spec, the second to compose and run the methods in that spec.
I think this is a very interesting approach, and certainly better than providing the ~1000 actions as MCP tools but I feel the jury is still out on exactly how well this pans out.
Code mode is great but not needed anymore in 95% of use-cases.
Of course they do. Do you really think that there's a magical bag of holding for the potentially infinite amount of skills?
I would love to see a proper engineering post-mortem for how this happened.
> curl -s -X POST "https://mcpclock.firasd.workers.dev/mcp" -H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" -d '{"jsonrpc":"2.0","id": 1,"method":"tools/call","params":{"name":"clock_get","arguments":{}}}' | grep '^data:' | sed 's/^data: //'| jq
{"result": {"content": [{"type": "text",
"text": "[\n {\n \"timezone\": \"UTC\",\n \"iso\": \"2026-08-05T04:44:41.707Z\",\n \"unixtime\": 1785905081\n },\n {\n \"timezone\": \"Alphadec\",\n \"alphadec\": \"2026_P4A0_466322\"\n }\n]"
}]},"jsonrpc": "2.0", "id": 1}
The "just use a CLI" crowd is implicitly assuming:1) You're a developer 2) On a laptop 3) With a shell open inside an agentic coding harness (Claude Code, Codex CLI, Cursor) 4) Working on a software project
That's maybe 2% of AI usage.
The other 98% is: Someone on the ChatGPT iOS app asking a question on the subway; Someone in Claude.ai web chatting about their calendar; Someone using ChatGPT Desktop to summarize their Notion; A non-developer using AI in a browser at work; Voice mode on a phone; An embedded chat widget on some company's website...
This essentially makes MCP just another REST API endpoint, and lets you use the same infrastructure you already have set up for REST APIs (like load balancers, API gateways, progressive rollouts, etc).
Might break the caching, perhaps this can be solved with snapshot ids or cache ids like "Replacing context line 434-500 with hint; checking last request before that context even was added and running that cache before"
Not sure whats the downside of this approach is but its serving us well so far.
Companies can implement Individual Oauth with DCR (which makes it as easy as "log in with Google"), but many don't
Isn't MCP _an API_?
disable-model-invocation: true
Most MCP servers still bloat up my context even when I am not using them at all. Why can't they be configured to allow me to keep them out unless I really need to use them?
Modern harnesses don't do that, MCP is also progressive disclosure now. MCP tool descriptions aren't included anymore, and have to be found via tool_search. I actually find that to be a regression. Sometimes the models will start coding a python script for something that is already available via a pre-compiled MCP tool call.
In Claude Code you can enable / disable MCP servers at will, just use the /mcp command. You can also lock down permissions in claude -p headless mode to individual tools within an MCP server (eg maybe you only want to allow the reading tools, and none of the write-enabled tools for your automated session).
Last time I checked, and it may be different now, Claude Code used tool search only if context usage was greater than 10%.
Also, even with tool search, MCP uses about 1000 to 3000 tokens. A skill frontmatter on the other hand can be around 50.
And an mcp tool call result gets dumped into context in its entirety. A cli too can be filtered with grep/sed etc.
Skills are just a lot more efficient for most use cases.
I am guessing keeping them all in every request is cache efficient though.
> code_execution tool - uses monty to run an interpreter that has all other tools available as async functions. Maki uses it to filter / summarize / transform / pipe data to other tools as input, without it ever reaching and polluting the context window. Sandbox limited by time & memory.
Failing that, you can just disable native MCP in your harness and have your agent use a CLI tool that calls MCP, like mcp-cli [1], and then the agent can use regular unix pipes to filter or transform the output. Here, rather than raw grep or sed (which are line oriented), the agent would probably use things like jq.
What about mcp-inspector? It’s a nice tool, can be used interactively, can be used as a CLI.
npx @modelcontextprotocol/inspector --cli \
https://agentic-mermaid.dev/mcp \
--method tools/call \
--tool-name render_svg \
--tool-args-json '{"source":"graph TD; A-->B","options":{"padding":24}}'
Equivalent with my mcp-explorer tool: uvx mcp-explorer call \
https://agentic-mermaid.dev/mcp render_svg \
-a source 'graph TD; A-->B' \
-a options '{"padding":24}'
So yeah, they're pretty similar.Then for my list command:
uvx mcp-explorer list https://agentic-mermaid.dev/mcp
With the inspector one you would do: npx @modelcontextprotocol/inspector --cli \
https://agentic-mermaid.dev/mcp \
--method tools/list
Mine returns a human-readable list (unless you add --json), the inspector one returns a big dump of raw JSON.you can specify --url, --npx and include --auth $TOKEN on the command line. you can also interactively connect with /connect, and pixel peep transport details https://fast-agent.ai/mcp/mcp-inspect-transport/
If we're talking protocols, it's useful to know the distinction.
There have been many flame wars around REST vs RPC and whether most "RESTful" implementations out there are actually RPC with a funny nose.
Now that the models are getting better, you can trust them to book barber appointments, order groceries, manage your calendar, etc etc. It's significantly lazier to ask an agent to do something than manually doing it. Especially with voice to text.
I'm predicting that chat bots will auto-discover MCPs from trusted websites and make life much easier.
Now when I ask ChatGPT for a product, it does a web_search tool call and then gives me a link to a webshop product page. But next step would be automatically using the MCP of that webshop to embed checkout inside chat.
I propose AII - AI Interface. Lets make it happen!
a few months ago I tried to implement an MCP server from scratch in python (instead of using the existing reference implementation) and I could not get it to work reliably across clients
I thought it was The Browser Company’s invention?
https://claude.com/blog/skills
> Introducing Agent Skills
> October 16, 2025
I can find articles and Reddit posts about Dia Browser’s Skills from July 2025.
Wrote about this back in March: https://chrlschn.dev/blog/2026/03/mcp-is-dead-long-live-mcp/
MCP is going to be a foundational piece of enterprise agent infra.
Nowadays, these agents are more capable and I think you can replace MCP (which is a pain on macOS), with simple CLI tools and expose them to agents via system prompt, skills, or other API documentation.
The actual implementation may have been very janky but the idea itself is sound: If you want to interact with arbitrary services then providing one single protocol (ideally not JSONRPC but hey) with a standard api discovery method and standard starting method call means your super-intelligent machine god can connect to any MCP service with just a URL and figure everything out from there.
The reason is that these "folks getting paid several hundred thousand dollars a year" are mostly youngsters without any care to learn about computing history, which is why IT is a fashion industry.
See HTMX as another example, that is basically ASP.NET Ajax or JSF Ajax.
Someone should have said to the llm coming up with it -- "that sounds like an API with extra steps ... how about we keep these things (tool descriptions) and get rid of all this irrelevant - this will look good in my promotion meeting - overengineering (everything else)"
You are absolutely right is what it would have replied with.
Only when my AI and its subagents are modelled through a graph of producing agents, consuming agents, and agentic mailboxes, will Claude Code truly shine.
It's a terrible protocol with zero value beyond bigtech sponsorship. We're all worse off for it.
But I wouldn't discount how uphill it was to probably even argue for something like MCP cause the labs still very scientist-driven and focused on solving everything in token space. The idea that tool use could be super transformative probably wasn't obvious from inside before Claude Code
All those teams, including ChatGPT and Claude.ai, have figured out that you will eventually need to give your agent a small sandbox Linux environment to unlock the same level of “intelligence“ those coding harness exhibit. Stitching together the results of a cli command through scripting or coding gives the agent a ton more flexibility in what it can do as it can utilize its text generation capability into executable logic. toolcalls mostly work for actions rather than complex and novel problem solving. You are making the agent represent a programming control flow through toolcalls while carrying the context between them in a lossy, nondeterministic, wasteful, slow and rigid way.
It’s one thing if you want to artificially limit that agent to a very strict set of available APIs that it must use in a specific way while transferring context between them through the LLM and you don’t want to incur the cost of the extra sandbox compute. But coding harnesses have demonstrated that letting the agent write a small shell or python script can let the agents solve problems that you haven’t even really anticipated in your toolcall approach or that tool calls make prohibitively expensive or not even possible.
But also the token cost tends to dwarf the sandbox compute cost, so why not pay the $0.05/hour to have a sandbox where the agent can run free when you are already paying orders of magnitude more for the tokens
But you can have both: rund the agent outside the vm/sandbox and orchestrate work on it, either directly via shell calls or kicking off an ephemeral subagent on the box.
This makes the agent and session that runs outside the vm more durable and opens new orchestration pattern.
I’m building the oss version of this here: https://github.com/smartcomputer-ai/lightspeed
All of those use cases you mentioned benefit from the agent having access to a temporary virtual machine with a set of standard CLI tools and the ability to write and execute arbitrary code.
Most already do. ChatGPT has been running Python in the cloud to answer questions before we even had functional coding harnesses.
So why not augment their repertoire of CLI tools instead of a completely new protocol?
- CLI tools need to access your secrets in your .env. With MCP the model never sees the secrets and refresh tokens.
- CLI tools don't have auditing and telemetry. MCP provides all this for free.
- CLI tools imply full bash access, a nightmare in terms of security and very costly if you have to spin up a machine for every request for billions of users.
- etc
But let’s take my MCP clock for example if you ask ChatGPT what’s the time in Tokyo it’s not even gonna think of booting up the code interpreter. It’s gonna just do web search and give you the wrong time (I just tried it and there may be an OpenAI built in widget it pops up now—but again that’s a specific tool call with an iframe output not arbitrary code)
This doesn't cover the case when you're talking to an LLM from web, or via Slack or Linear, etc. There, you will want MCP so the LLM can use services on your behalf as you. That's portability.
When you talk to an LLM on the web, the harnesses spin up a fresh environment (I would hope it’s a VM…) so that the LLM can do stuff like run arbitrary Python and Bash scripts to complete the task you asked it for.
There’s no reason why you shouldn’t be able to customize this environment to add whatever CLI tools and credentials you need for the agent to act on your behalf.
The UX would be exactly the same.
As for the "just use a CLI" crowd, stateless MCP servers should satisfy us too - it means providing an mcp CLI tool that provides all the benefits of a CLI with access to all the API's exposed over MCP has just become easier.
They’re essentially provisioning you a temporary vm, so it’s morally equivalent to running cc on ur laptop and remote-controlling from the app, except worse
So if the LLM behind the scene has its own compute environment anyway, why not just use a cli?
This is imo what the cli crowd is actually assuming
your messages causing your LLM (harness) to run CLIs on your computer? charming, thrilling, great fun.
other people’s messages causing your LLM to run CLIs on your (cloud) computer? terrifying, awful, sickening, no fun at all
I learnt this with Sun RPC and the whole "The network is the computer".
Somehow this keeps having to be relearnt.
It’s not true. It’s not totally untrue, but don’t hide your head in the sand.
1. a REST-like API
2. with a spec (OpenAPI, introspection, etc.)
3. with harness-level authz (the ability to allow / deny specific methods in the harness)
The only part that's actually new is #3, but it seems like it should be fairly easy to provide similar support for REST APIs. There might already be a Pi extension that allows you to allow / deny particular method / path patterns for particular sites. Regardless, there's always service-level authz with service accounts, at least for the more sophisticated APIs.
So ya, I don't see why we need a whole new set of standards and protocols just to have harness-level fine-grained API permissions. We could have just done it all with REST.
I maintain https://endara.ai and you can very quickly introduce a ton of capabilities for Claude/Cursor/ChatGPT by configuring all the stuff you use in a single place (and slicing them with profiles if needed).
It's was simpler to design/implement the AI tool if these interactions had state. When this was generalized to internet interactions this no longer made sense and was gradually cleaned up.
I think at the same time, the AI tool "protocol" to interact with the local system moved away from MCP model.
And then the model will randomly decide to load the .md file that is the "skill" randomly at any point. Again polluting the context.
There's no magic.
Maybe Microsoft should bring back Biztalk for agentic workflows.
MCP serves a single purpose: If you ask some company for API support for some stuff you can do via web UI but not API, they might say no for many reasons. But when you ask for MCP, suddenly you trigger AI FOMO and it will be available right away.
Previously, it was an API with a weird stateful standard; now it is essentially a bog-standard stateless JSON-RPC API with conventions for tool discovery, long-running tasks and multi-round-trip requests
If you wrote your own JSON-RPC API for an LLM, you would probably eventually end up with something that looks very much like the standard they're describing.
But your answer is the first one that seems to be relevant, so thank you.
The point is that even web search should be a CLI tool, and all ChatGPT would know to do other than talk to you is how interact with a shell.
Then if you ask it what’s the time in Tokyo, it would likely reach for the POSIX date command, instead of web search, because both would be equally visible.
The difference is that we already have Bash :)
EPERM, POSIX ACLs and execute bits.
> CLI tools need to access your secrets in your .env
They can access whichever secret store you want. There are dozens of projects offering valid solutions nowadays for the local tool call space, and they’re equally useful in this scenario.
> CLI tools don’t have auditing and telemetry
They’ve had it for much earlier than MCP.
> A nightmare in terms of security
We know how to secure shells. Just offer a limited toolset. No escape, unless there’s a vulnerability, which is just as likely with MCP.
> Very costly
It’s a rounding error in comparison to token costs.
- Standardized input and output schemas for each tool
- Standardized annotations that indicate how potentially dangerous each tool can be (e.g. read-only, idempotent, publishes data visible to third parties)
- Extensions for UI widgets
All of the main agent CLI's provides a demonstration that it is possible because they're all callable as a CLI. Several of them, like Codex, Kimi CLI, Pi, OpenCode are open source and so you could obviously strip out the MCP host and client from them and turn them into a CLI. Doing so in a way that keeps auth outside the agents sandbox is trickier and you might end up with a proxy which partly defeats the point but at least still keeps the composability of a CLI.
Most chat products will just have a directory of connectors anyways - so for non technical folks they won't even here the term MCP or REST, they'll just "Install the Gmail plugin".
I think that’s a future plenty of people see or want. But simpler and composable Linux commands are far more flexible than pre-defined Windows or Mac “Settings App”. The barrier to entry for the former was knowledge and familiarity, while the latter optimizes for simpler user experience. Agents don’t have to worry about knowledge or familiarity the way users do.
Ok, now you have customers_aggregate(‘topspenders’), but the prompt to the agent was “Get me the top spenders that are public companies and show me ones that increase their spend proportionally to their revenue increase and ones that don’t”. If the agent had access to the needed APIs (or CLIs) for your data and a finance API provider, it can generate a script that crunches down the top 10 or 10,000 for all that data. You could obviously come up with a toolcall that encapsulates that or rely on multiple toolcalls and on the LLM to fill in data correctly between them then do the math using inference.
Edit: even if you look at that customers_aggregate(‘topspenders’) tool, you wouldn’t have needed that if the agent could execute a SQL query. It can also express anything the data can do. You obviously trade the control and correctness guarantees you can give for a `customers_aggregate(‘topspenders’)` vs a potentially wrong SQL query generated by the LLM with the infinite flexibility of a SQL query to be able to express far more than you could in a customers_aggregate call.
In my view, the MCP should be part of the specification. It should expresses intended actions and/or restrictions while the code generated and executed by the LLM is an implementation detail.
This distinction is completely obvious in some cases such as using an external payment gateway. But let's consider a more borderline case.
Say your database is multi-tenant and every query absolutely must include a tenant_id. Now that doesn't necessarily mean you have to use MCP. It could be some command line tool that requires a tenant_id parameter.
But what you can't do is ask the LLM politely to please include a where clause for the tenant_id in any and all SQL statements it decides to generate. You have to impose some things deterministically and MCP is one way to do that.
An MCP tool call that returns the top N spenders puts the result in the agent’s context window.
A CLI tool call on the other hand… The agent can pipe their results into other completely unrelated CLI tools. It can write a loop that calls multiple tools.
These things are not possible with MCP but are trivial with CLIs.
The problem is often that OpenAPI schemas are often incomplete, since they used to be useful to humans even in an incomplete state.
MCP basically requires you to do the equivalent work of getting your OpenAPI schema complete. It’s just much easier to sell to your managers.
This is easy to see when you think in terms of a client: It is pretty much impossible to build an opinionated UI for any kind of RESTful API, while creating one for an MCP server is fairly self-explanatory: MCP servers offer a mandatory and complete runtime introspection endpoint (you can retrieve a listing of available tools/resources/prompts etc. along with their parameter and return type schemas). So that means clients have a way to exhaustively describe everything an MCP server is able to do with a vocabulary that carries over exactly to other servers - a tool is a tool everywhere.
This is easy to see when you think in terms of a developer. The natural way to adapt an existing REST API into an MCP server while keeping the two synchronized is to simply write a basic translation tool that takes your OpenAPI spec and turns it into an MCP manifest, mapping each HTTP endpoint to an MCP method, and copying over all the request / response type signatures and descriptions for everything. Then, all of the sudden, it hits you: there is no need for this translation layer.
There are multiple products that turn OpenAPI schemas into front-ends nowadays.
But there's a pretty large body of RFCs that define what makes a good REST API. For instance POST should create things, PUT should re-define them, PATCH should incrementally update them, GET must be side-effect-free, etc. And there's a ton of standard HTTP headers. Pair it with OpenAPI and the whole thing is self-documenting. You can achieve the same effect with gRPC + reflection, or GraphQL + reflection, and there's plenty of mature tooling and interoperability for each system.
By comparison, MCP actually has relatively few conventions. LLMs are very familiar with REST and OpenAPI already. Just use that.
Edit: I read it. Yep.
We have text interfaces refined by humans for decades and there's an endless sea of training data for them, but they imagine these amateur-hour homegrown solutions will ever outdo an agent with shell access?
How do you send a big read only query? A POST? A GET hoping nothings logs it or swallows half of it?
How do you deal with delayed answers? Things that most HTTP middleware will timeout long before the result is achieved?
If you have a long running search, the resource is the search. Create a search. Fetch search results. Delete the search when you’re done.
Similarly, if you have a sufficiently complex command, the resource is the command itself. Maybe it requires async work, and you can later fetch the command results.
Anecdotally, this seems to be too big of a leap in mental model for developers, mostly because all of the REST learning path does is teach you how to use it in CRUD domains.