Every coding-agent conversation ends the same way. The component you spent an hour getting exactly right, the fix you never want to derive again, the pattern that finally worked: all of it stays behind in a chat log you will never open again. Developers are producing huge amounts of valuable, hard-won work this way, and almost none of it survives the conversation that produced it. Grabbing those assets and giving them somewhere permanent to live is the biggest unlock I see in how we work with these tools.
That belief is what I am building with Sirro: a hosted memory layer for AI coding agents, served over MCP on streamable HTTP. Agents like Cursor, Claude Code, and Codex save the work that earned persistence and pull it back in a different project, in a different agent, three weeks later. You stop letting the AI guess from zero every session and start composing from building blocks you already trust.
Running it in production taught me four things the docs never mentioned. It started the week I put the server on a public directory and watched the listing bounce every client that was not Cursor, Claude Code, or Codex. The error users saw was "Unknown agent", which is a terrible thing to tell someone who just tried to install your product. The bug was not in my protocol handling. It was in a decision I had made early on, to allowlist OAuth clients by name, and fixing it forced me to learn how the MCP ecosystem actually works.
When you build an MCP server over stdio, authentication is someone else's problem. The moment you host it, OAuth 2.1 with dynamic client registration becomes the front door, and every client walks through it differently.
My mistake was assuming clients identify themselves cleanly. They do not. Cursor, Claude Code, and Codex each present different client names, some use loopback redirect URIs, and hosted gateways like Smithery sit between your server and the user's actual client, so the identity you see is the gateway's, not the user's. An allowlist by client name works until the ecosystem ships a new client, which it does constantly. Every new client was a support ticket waiting to happen.
The fix that actually holds: accept loopback redirect URIs and dynamic client registration as the default path, and gate only the parts you genuinely must gate. The spec already tells you how to do this. What it does not tell you is how many real clients deviate from what you assumed, so build the permissive path first and add restrictions only when you have a concrete reason.
Agents read your tool responses into their context window, which means your API output competes with the user's actual work for the scarcest resource in the system. This changed how I design responses.
A naive list endpoint returns full bodies. That is fine for a REST API serving a frontend. It is careless for an MCP server, because dumping forty assets into a context window to answer "do I have something like this" burns the user's budget on data the agent will mostly discard. So list returns search snippets and metadata, get returns one full asset on demand, and a compose tool does assembly server-side instead of making the agent do it in-context.
I also cap assets at 64KB. Anything larger is a file, not a memory, and it belongs in the repo, not in the memory layer.
The industry currently answers the forgetting problem with files: CLAUDE.md, cursor rules, AGENTS.md. These help and they half solve it. They are write-heavy, they are read on faith at session start, and nobody curates them, so they rot. Nathan Marz put it better than I could in a reply to me: persisted corrections are right, but CLAUDE.md-style files only half-solve the problem.
The reason they half-solve it is that they confuse the container with the mechanism. A context window is session state. It dies when the thread ends, it gets truncated when it fills, and it carries no semantics about what deserves to survive. Memory needs explicit save and retrieve: the developer decides what earned persistence, and the agent retrieves it when it is relevant, not because it was appended to a file months ago. That is the difference between an agent that starts every project empty and one that starts with everything you already figured out.
A memory layer only gets used if the economics work at small scale, so I designed against a concrete target: thousands of users, each storing dozens of assets, on boring infrastructure. That constraint shaped real choices. Postgres full-text search with trigram indexes is enough at this scale, so there is no vector database to operate. Search returns snippets because full bodies are both a context problem and an egress cost. Asset size caps keep storage predictable.
The pattern here generalizes: pick your scale assumption early, price it, and let it veto features. The boring stack that fits the budget beats the impressive one that does not.
Coding agents made generating software cheap, which moved the bottleneck from creation to memory. A conversation is where an asset gets created, but on its own it has no memory beyond the thread it lives in. What compounds is the loop after the conversation: save what earned it, then compose from it next time. Building blocks instead of fresh guesses. A developer who has spent six months working with coding agents should never open a new project with an empty toolbox, and giving all those closed conversations somewhere permanent to live is how we get there.
That is what I am building with Sirro, and these are the constraints it runs under today. If you are building in the MCP ecosystem, I would genuinely like to hear what broke for you first.