{"slug": "mcp-is-going-stateless-what-changed-and-how-i-migrated-my-currency-converter", "title": "MCP Is Going Stateless: What Changed and How I Migrated My Currency Converter Server", "summary": "The Model Context Protocol (MCP) is moving toward a stateless protocol model, allowing requests to reach any server instance without sticky routing or shared session state. A developer recently migrated their MCP currency converter server to align with this change and the new split TypeScript SDK packages, highlighting the benefits for horizontally scalable HTTP services. The developer notes that stateless MCP does not mean the entire application must be stateless, as application state can be represented explicitly via data like cart IDs.", "body_md": "The Model Context Protocol (MCP) has been evolving quickly.\n\nOne of the most interesting changes in the latest MCP specification is the move toward a **stateless protocol model**.\n\nI recently updated my MCP currency converter server to work with the newer stateless behavior and the new split TypeScript SDK packages, particularly `@modelcontextprotocol/server`\n\n.\n\nIn this article, I'll explain:\n\nIf you're new to MCP, the **Model Context Protocol** is a standard for connecting AI applications to external tools, resources, and data.\n\nInstead of building custom integrations between every AI application and every external service, MCP provides a common protocol.\n\nFor example, an AI assistant can use an MCP server exposing a tool like:\n\n```\nconvert_currency\n```\n\nThe model can then request:\n\n```\nConvert 100 USD to EUR.\n```\n\nThe MCP client communicates with the MCP server, which performs the actual operation and returns the result.\n\nMCP servers can expose several primitives, including tools, resources, and prompts.\n\nFor my example, the server is intentionally simple: it exposes currency-conversion functionality.\n\nBefore the stateless changes, Streamable HTTP could maintain a protocol-level session.\n\nConceptually, the flow looked something like this:\n\n```\nClient\n   |\n   | POST /mcp\n   | initialize\n   v\nMCP Server\n   |\n   | Mcp-Session-Id\n   v\nClient\n   |\n   | POST /mcp\n   | Mcp-Session-Id: abc123\n   v\nMCP Server\n```\n\nThe server creates a session during initialization.\n\nSubsequent requests contain the session identifier.\n\nThat means the server can associate requests with the session that was established earlier.\n\nThis isn't necessarily bad.\n\nSession state can be useful when an application genuinely needs conversational or connection-level state.\n\nBut it creates an architectural problem when we want MCP servers to behave like ordinary horizontally scalable HTTP services.\n\nImagine deploying three MCP server instances:\n\n```\n                    ┌─────────────┐\n                    │ Load        │\n                    │ Balancer    │\n                    └──────┬──────┘\n                           |\n             ┌─────────────┼─────────────┐\n             |             |             |\n             v             v             v\n          Server A      Server B      Server C\n```\n\nSuppose the initialization request reaches Server A.\n\nThe session now belongs to Server A.\n\nWhat happens when the next request reaches Server B?\n\nServer B doesn't necessarily know anything about that session.\n\nYou now need mechanisms such as:\n\nThat adds complexity to the deployment architecture.\n\nThe latest MCP direction removes this protocol-level session dependency.\n\nThe MCP team describes the goal as allowing requests to reach any server instance without requiring sticky routing or a shared protocol session store. ([Model Context Protocol Blog](https://blog.modelcontextprotocol.io/posts/2026-07-28-release-candidate/?utm_source=chatgpt.com))\n\nThe key idea is surprisingly simple:\n\nEach request should be understandable independently.\n\nInstead of relying on a protocol-level session established earlier, the request contains the information necessary for the server to process it.\n\nConceptually:\n\n```\nRequest 1\n   |\n   v\nServer A\n\nRequest 2\n   |\n   v\nServer C\n\nRequest 3\n   |\n   v\nServer B\n```\n\nThere is no requirement that all three requests reach the same instance.\n\nThis is a much more natural fit for cloud-native architectures.\n\nThis is an important distinction.\n\n**Stateless MCP does not mean your entire application must be stateless.**\n\nSuppose you have a shopping-cart tool:\n\n```\ncreate_cart()\n```\n\nThe server can return:\n\n```\n{\n  \"cart_id\": \"cart_123\"\n}\n```\n\nThe next tool call can explicitly provide:\n\n```\n{\n  \"cart_id\": \"cart_123\",\n  \"product_id\": \"product_456\"\n}\n```\n\nThe application still has state.\n\nBut that state is represented explicitly by application data rather than being hidden inside an MCP protocol session.\n\nThe MCP team explicitly recommends this type of application-level state when state needs to persist between calls. ([Model Context Protocol Blog](https://blog.modelcontextprotocol.io/posts/2026-07-28-release-candidate/?utm_source=chatgpt.com))\n\nThat's a subtle but important architectural improvement.\n\nStreamable HTTP is the important transport for remote MCP servers.\n\nThe 2026-07-28 specification changed its behavior significantly.\n\nThe new model removes:\n\n`Mcp-Session-Id`\n\nInstead, the server exposes a single MCP endpoint that accepts POST requests.\n\nEach JSON-RPC request is sent as its own HTTP request.\n\nThe server can return either:\n\n```\napplication/json\n```\n\nor an SSE stream associated with that request.\n\n([GitHub](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2026-07-28/basic/transports/streamable-http.mdx?utm_source=chatgpt.com))\n\nSo the mental model becomes:\n\n```\nPOST /mcp\n      |\n      v\n+----------------+\n| MCP Server     |\n|                |\n| Process request|\n|                |\n| Return result  |\n+----------------+\n```\n\nRather than:\n\n```\nInitialize session\n        |\n        v\nMaintain session\n        |\n        v\nProcess requests\n        |\n        v\nEventually close session\n```\n\nTo understand the change practically, I used my currency converter MCP server.\n\nThe server exposes currency conversion as an MCP tool.\n\nThe architecture is roughly:\n\n```\n               AI Application\n                     |\n                     | MCP\n                     v\n              ┌───────────────┐\n              │ Currency MCP  │\n              │ Server        │\n              └───────┬───────┘\n                      |\n                      v\n              Exchange Rate API\n```\n\nThe server doesn't need conversational state.\n\nFor example:\n\n```\nConvert 100 USD to EUR\n```\n\nis completely independent from:\n\n```\nConvert 500 GBP to USD\n```\n\nThere is no reason for the server to maintain a protocol session between these requests.\n\nThat makes the currency converter a good candidate for a stateless MCP server.\n\nAnother change I encountered while updating the project was the move to the split TypeScript SDK packages.\n\nInstead of relying on the older monolithic package structure, the SDK now provides separate packages such as:\n\n```\n@modelcontextprotocol/server\n@modelcontextprotocol/client\n```\n\nThe server package is responsible for building MCP servers, while the client package provides client functionality. ([GitHub](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/README.md?utm_source=chatgpt.com))\n\nFor my server, I use:\n\n```\nnpm install @modelcontextprotocol/server\n```\n\nalong with the required schema library.\n\nThis separation makes the dependency boundaries much clearer.\n\nOne of the important concepts when working with Streamable HTTP is the absence of a session ID generator.\n\nIn the earlier SDK transport model, a stateful server could configure something like:\n\n``` js\nconst transport = new StreamableHTTPServerTransport({\n  sessionIdGenerator: () => randomUUID(),\n});\n```\n\nThe transport then generates and manages a session identifier.\n\nFor stateless operation, the session ID generator is omitted:\n\n``` js\nconst transport = new StreamableHTTPServerTransport({\n  sessionIdGenerator: undefined,\n});\n```\n\nThe SDK documentation describes this as stateless mode: no session ID is returned and no session validation is performed. ([GitHub](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/packages/middleware/node/src/streamableHttp.ts?utm_source=chatgpt.com))\n\nHowever, there is an important nuance here.\n\n**Don't confuse this older SDK configuration with the new 2026 protocol itself.**\n\nThe 2026 specification removes protocol-level sessions altogether.\n\nThe SDK is evolving to provide higher-level APIs around the new protocol behavior as well. ([GitHub](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/docs/migration/support-2026-07-28.md?utm_source=chatgpt.com))\n\nThis is probably the biggest reason I find this change interesting.\n\nConsider a Kubernetes deployment:\n\n```\n                  Load Balancer\n                       |\n        ┌──────────────┼──────────────┐\n        |              |              |\n        v              v              v\n      Pod 1          Pod 2          Pod 3\n```\n\nWith protocol-level sessions, routing could become:\n\n```\nClient A ───────────────> Pod 1\nClient A ───────────────> Pod 1\nClient A ───────────────> Pod 1\n```\n\nbecause the session belongs to Pod 1.\n\nWith a stateless protocol, requests can be distributed naturally:\n\n```\nClient A ───────────────> Pod 1\nClient A ───────────────> Pod 3\nClient A ───────────────> Pod 2\nClient A ───────────────> Pod 1\n```\n\nThe load balancer doesn't need to understand MCP session ownership.\n\nThis makes horizontal scaling significantly simpler.\n\nThis is another area where the change becomes interesting.\n\nConsider AWS Lambda.\n\nA typical architecture might look like:\n\n```\nMCP Client\n    |\n    v\nAPI Gateway\n    |\n    v\nLambda\n    |\n    v\nExchange Rate API\n```\n\nWith stateless requests, each invocation can independently process an MCP request.\n\nThere is less reason to keep a warm process specifically because it owns a particular MCP session.\n\nThis aligns nicely with the serverless execution model.\n\nOf course, long-running streaming operations and application-specific state can still introduce additional considerations.\n\nStateless doesn't magically eliminate all stateful requirements.\n\nSuppose I later extend my currency server with something like:\n\n```\ncreate_conversion_watch\n```\n\nA user might create a watch:\n\n```\ncreate_conversion_watch(\n  from = USD,\n  to = EUR,\n  threshold = 0.95\n)\n```\n\nThe server could return:\n\n```\n{\n  \"watch_id\": \"watch_123\"\n}\n```\n\nThe watch itself could be persisted in:\n\n```\nPostgreSQL\n```\n\nor:\n\n```\nRedis\n```\n\nor another durable store.\n\nLater:\n\n```\nget_conversion_watch(\n  watch_id = \"watch_123\"\n)\n```\n\nThe MCP protocol doesn't need to maintain a session.\n\nThe **application** owns the state.\n\nThat's a much cleaner separation of responsibilities:\n\n```\nMCP\n |\n | Protocol communication\n |\n v\nApplication\n |\n | Business state\n |\n v\nDatabase\n```\n\nThis is another misconception worth addressing.\n\nAn MCP request can still contain metadata and application parameters.\n\nThe difference is that we're no longer relying on an implicit protocol session to reconstruct the context.\n\nThe newer protocol explicitly moves toward per-request information.\n\nThe TypeScript SDK documentation describes the 2026 protocol as carrying request-level information such as protocol version and client information rather than relying on an initialization-scoped identity. ([GitHub](https://github.com/modelcontextprotocol/typescript-sdk/blob/main/docs/protocol-versions.md?utm_source=chatgpt.com))\n\nThis makes each request more self-contained.\n\nAfter migrating my currency converter server, the biggest architectural benefit I see is **simplification**.\n\nInstead of thinking:\n\n```\nWhere does this client's MCP session live?\n```\n\nI can think:\n\n```\nCan this request be processed independently?\n```\n\nFor many tools, the answer is yes.\n\nThat makes MCP much easier to reason about in distributed environments.\n\nIt also fits familiar backend architecture principles:\n\nIn other words, MCP starts feeling much more like a conventional cloud-native protocol.\n\nStateless doesn't remove architectural responsibilities.\n\nYou still need to consider:\n\nEvery request must still be authenticated and authorized appropriately.\n\nIf a tool performs a side effect, you may need idempotency protection.\n\nFor example:\n\n```\ncreate_payment()\n```\n\nshould not accidentally execute twice because a request was retried.\n\nIf your application needs state, store it explicitly.\n\nFor example:\n\n```\nMCP request\n     |\n     v\nApplication\n     |\n     v\nRedis / PostgreSQL / DynamoDB\n```\n\nStreamable HTTP servers need appropriate security controls.\n\nThe MCP specification specifically calls out validating the `Origin`\n\nheader to mitigate DNS rebinding attacks and recommends authentication for connections. ([Model Context Protocol](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports?utm_source=chatgpt.com))\n\nStateless does not mean \"everything must be a simple JSON response.\"\n\nThe newer Streamable HTTP transport can still use SSE for streaming data associated with a request. ([GitHub](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2026-07-28/basic/transports/streamable-http.mdx?utm_source=chatgpt.com))\n\nThe easiest way to summarize the architectural difference is:\n\n```\n             Initialize\n                 |\n                 v\n        ┌─────────────────┐\n        │ MCP Session     │\n        │                 │\n        │ Session ID      │\n        │ Session State   │\n        └────────┬────────┘\n                 |\n        ┌────────┼────────┐\n        v        v        v\n      Req 1    Req 2    Req 3\n```\n\nThe protocol session is part of the architecture.\n\n```\n      Request 1 ───────> Server\n      Request 2 ───────> Server\n      Request 3 ───────> Server\n      Request 4 ───────> Server\n```\n\nEach request can be independently routed and processed.\n\nIf state is required:\n\n``` php\nRequest\n   |\n   v\nMCP Server\n   |\n   +----> Application State\n             |\n             v\n          Database\n```\n\nThe state belongs to the application rather than the MCP transport.\n\nThe move toward a stateless MCP protocol might initially look like a relatively small protocol change.\n\nI think its implications are much bigger.\n\nRemoving protocol-level sessions makes MCP easier to deploy in environments where stateless HTTP is already the default architectural model.\n\nIt reduces the need for:\n\nAnd it encourages a cleaner separation:\n\n```\nMCP\n ↓\nCommunication protocol\n\nApplication\n ↓\nBusiness logic\n\nDatabase\n ↓\nApplication state\n```\n\nMy currency converter server was a relatively simple project, but it was a useful way to understand this change in practice.\n\nIf you're building remote MCP servers today, I think it's worth understanding the new stateless model—not just from an MCP perspective, but from a **distributed systems and deployment architecture perspective**.\n\nThe interesting question is no longer:\n\n\"How do I maintain an MCP session?\"\n\nIt becomes:\n\n\"Can I design my MCP server so that every request can be independently processed?\"\n\nFor many MCP tools, the answer is yes.\n\nAnd when it is, the resulting architecture can be considerably simpler.\n\nI implemented the stateless behavior in my **currency converter MCP server** as a practical example of the concepts discussed in this article.\n\nThe project is available on GitHub:\n\nIn NPM repository:\n\nIf you're learning MCP, I recommend starting with a small tool server like this and then experimenting with:\n\nThat's where the protocol changes become much more interesting from an architecture perspective.", "url": "https://wpnews.pro/news/mcp-is-going-stateless-what-changed-and-how-i-migrated-my-currency-converter", "canonical_source": "https://dev.to/dilumdarshana/mcp-is-going-stateless-what-changed-and-how-i-migrated-my-currency-converter-server-olm", "published_at": "2026-08-17 03:21:38+00:00", "updated_at": "2026-08-17 03:41:44.253332+00:00", "lang": "en", "topics": ["developer-tools", "ai-infrastructure", "ai-agents"], "entities": ["Model Context Protocol", "MCP", "TypeScript", "@modelcontextprotocol/server"], "alternates": {"html": "https://wpnews.pro/news/mcp-is-going-stateless-what-changed-and-how-i-migrated-my-currency-converter", "markdown": "https://wpnews.pro/news/mcp-is-going-stateless-what-changed-and-how-i-migrated-my-currency-converter.md", "text": "https://wpnews.pro/news/mcp-is-going-stateless-what-changed-and-how-i-migrated-my-currency-converter.txt", "jsonld": "https://wpnews.pro/news/mcp-is-going-stateless-what-changed-and-how-i-migrated-my-currency-converter.jsonld"}}