{"slug": "java-mcp-server-with-streamable-http-netty-transports-and-stateless-deployment", "title": "Java MCP server with Streamable HTTP, Netty transports, and stateless deployment", "summary": "Tachyon MCP, a Java 21 Model Context Protocol server built on Netty 4.2, has been released with full support for the MCP 2025-11-25 spec including Streamable HTTP transport, session lifecycle, native I/O transports, and a stateless mode for serverless deployments. The server implements JSON-RPC 2.0, pagination, CORS, DNS rebinding protection, and features like tools, resources, prompts, tasks, and logging with extensible handlers.", "body_md": "**Tachyon MCP** — A Java 21 [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server built on [Netty 4.2](https://netty.io).\nFully implements MCP spec **2025-11-25** Streamable HTTP transport, session lifecycle, native I/O transports, and a stateless mode for serverless deployments.\n\n**TL;DR**\n\n``` python\nimport dev.tachyonmcp.server.TachyonMcpServer;\nimport dev.tachyonmcp.server.features.tools.AbstractSyncToolHandler;\nimport dev.tachyonmcp.server.features.tools.ToolDescriptor;\nimport dev.tachyonmcp.server.features.tools.ToolResult;\nimport dev.tachyonmcp.server.session.McpContext;\nimport tools.jackson.databind.node.JsonNodeFactory;\n\nvoid main() {\n    var schema = JsonNodeFactory.instance.objectNode();\n    schema.put(\"type\", \"object\");\n    schema.putObject(\"properties\").putObject(\"city\").put(\"type\", \"string\");\n\n    TachyonMcpServer.builder()\n        .name(\"weather-mcp\")\n        .tool(new AbstractSyncToolHandler(\n            ToolDescriptor.builder(\"get_forecast\")\n                .description(\"Get weather forecast\")\n                .inputSchema(schema)\n                .build()) {\n            @Override\n            public Object handle(McpContext ctx, Object args) {\n                return ToolResult.text(\"☀️ 22°C\");\n            }\n        })\n        .stateless(true)\n        .port(8080)\n        .bind();\n}\n```\n\n- JSON-RPC 2.0 — request/response/error/notification\n- Streamable HTTP — POST, GET (SSE), DELETE, OPTIONS\n- Lifecycle — initialize → initialized → ACTIVE\n- Pagination — cursor-based across all list methods\n- Session state machine — INITIALIZING → ACTIVE → CLOSED\n- CORS & origin validation\n- DNS rebinding protection\n- Accept header strict validation (406)\n- Pending request timeout (60s)\n- SSE resumability via Last-Event-ID\n\n`tools/list`\n\n— paginated with`nextCursor`\n\n`tools/call`\n\n— returns`CallToolResult`\n\nwith`isError`\n\n`outputSchema`\n\nin listing`annotations`\n\nfield`execution.taskSupport`\n\n(forbidden/optional/required)- Synchronous & asynchronous handler interfaces\n- Name validation (1–128 chars)\n`notifications/tools/list_changed`\n\non add/remove- Inline notifications + logging during tool call\n- Input schema validation\n\n`resources/list`\n\n— paginated`resources/read`\n\n— text & blob content`resources/templates/list`\n\n— URI templates`resources/subscribe`\n\n/`unsubscribe`\n\n`notifications/resources/list_changed`\n\n`notifications/resources/updated`\n\nto subscribers- Dynamic content via\n`ResourceHandler`\n\ninterface\n\n`prompts/list`\n\n— paginated with`nextCursor`\n\n`prompts/get`\n\n— invokes prompt resolver`notifications/prompts/list_changed`\n\n`tasks/list`\n\n,`tasks/get`\n\n,`tasks/cancel`\n\n,`tasks/result`\n\n- State machine enforcement — SUBMITTED → WORKING → COMPLETED/FAILED/CANCELLED\n`notifications/tasks/status`\n\nbroadcast on every transition- Task Janitor for stale tasks\n`execution.taskSupport`\n\nper tool (forbidden/optional/required)`TasksExtension`\n\n([SEP-1686](https://modelcontextprotocol.io/seps/1686-tasks)) — negotiable extension exposing`create_task`\n\ntool +`task://{id}`\n\nresource template- Extension-gated tool visibility (hidden from un-negotiated clients)\n\n`logging/setLevel`\n\nper session`notifications/message`\n\nemitted above threshold- Progress notifications\n\n`sampling/createMessage`\n\n— server → client request- Elicitation — ✅ form mode; ❌ url mode\n`notifications/cancelled`\n\n— bidirectional`notifications/tasks/status`\n\nfrom client\n\n- Netty 4.2\n- io_uring / epoll / kqueue / nio auto-detection\n- Platform-thread event loops + virtual-thread handlers\n- TCP_NODELAY, SO_KEEPALIVE\n- Channel writability backpressure (\n`setAutoRead`\n\n) - Configurable idle timeouts (reader/writer)\n\n**Stateless mode**— skip sessions for serverless- IN_MEMORY session store (ConcurrentHashMap)\n- Session Janitor — 5s sweep, 30s TTL\n- SSE disconnect ≠ session removal (supports reconnect)\n- Event log replay on reconnection\n\n**Requirements**: JDK 21+\n\n```\n<dependency>\n    <groupId>dev.tachyonmcp</groupId>\n    <artifactId>tachyon-server</artifactId>\n    <version>1.0.0-alpha.2</version>\n</dependency>\ngit clone https://github.com/kpavlov/tachyon.git\ncd tachyon\nmvn install -pl tachyon-mcp-server -DskipTests\npython\nimport dev.tachyonmcp.server.TachyonMcpServer;\nimport dev.tachyonmcp.server.features.tools.AbstractSyncToolHandler;\nimport dev.tachyonmcp.server.features.tools.ToolDescriptor;\nimport dev.tachyonmcp.server.features.tools.ToolResult;\nimport dev.tachyonmcp.server.session.McpContext;\nimport tools.jackson.databind.node.JsonNodeFactory;\n\nvoid main() {\n    var schema = JsonNodeFactory.instance.objectNode();\n    schema.put(\"type\", \"object\");\n    schema.putObject(\"properties\").putObject(\"city\").put(\"type\", \"string\");\n\n    TachyonMcpServer.builder()\n        .name(\"weather-mcp\")\n        .tool(new AbstractSyncToolHandler(\n            ToolDescriptor.builder(\"get_forecast\")\n                .description(\"Get weather forecast\")\n                .inputSchema(schema)\n                .build()) {\n            @Override\n            public Object handle(McpContext ctx, Object args) {\n                return ToolResult.text(\"☀️ 22°C\");\n            }\n        })\n        .stateless(true) // start in stateless node (no sessions)\n        .port(8080) // bind to 1270.0.1:8080\n        .bind();\n}\n```\n\n### With TasksExtension (negotiable) - [SEP-1686](https://modelcontextprotocol.io/seps/1686-tasks)\n\n``` js\nvar handle = TachyonMcpServer.builder()\n    .extension(new TasksExtension())   // exposes create_task tool + task://{id} resource\n    .port(8080)\n    .bind();\n```\n\nClients that include `\"extensions\": {\"io.modelcontextprotocol/tasks\": {}}`\n\nin their\n`initialize`\n\ncapabilities receive the extension's tool and resource template.\nClients that don't negotiate the extension see standard MCP tasks via `tasks/list`\n\n/ `tasks/get`\n\n.\n\nHandler interfaces (`ToolHandler`\n\n, `ResourceHandler`\n\n, `PromptHandler`\n\n) and descriptor types use stable domain types.\nWhen Tachyon upgrades to a new protocol version, only the internal mapper layer changes;\nhandler implementations are unaffected. Domain types track the 2026-07-28 spec shape where it improves on 2025-11-25 (e.g. `Annotations.lastModified`\n\n, `ResourceLink`\n\nin `ContentBlock`\n\n).\n\n**Native transports**— io_uring > epoll > kqueue > NIO auto-detect** Write buffer watermarks**— 32 KB low / 128 KB high, backpressure wired** Batch flushing**—`ctx.write()`\n\naccumulates, single`ctx.flush()`\n\non boundary**Minimal allocations**—`McpEndpointHandler`\n\nis`@Sharable`\n\n, no per-request handler creation**Virtual threads**— handlers offloaded from event loop, no manual thread pools** JSON-RPC**— Jackson streaming codec, no ObjectMapper.\n\n-\n**Rate limiting**— Not yet implemented -\n**URL elicitation mode / -32042 error**— Form mode works, URL mode missing -\n**2026-07-28 draft protocol version**— Not negotiable; version-gated features ready -\n**Stale session on re-initialize**— 30s TTL lingering, affects reconnect only\n\nYes. Use `stateless(true)`\n\nto skip session persistence. Each invocation processes one request independently.\n\nNot yet. The current pipeline targets HTTP/1.1; HTTP/2 upgrade is a pipeline configuration change comming soon.\n\nExtend `AbstractSyncToolHandler`\n\nor `AbstractAsyncToolHandler`\n\n, passing a `ToolDescriptor`\n\n:\n\n```\nclass MyTool extends AbstractSyncToolHandler {\n    MyTool() {\n        super(ToolDescriptor.builder(\"my_tool\")\n                .description(\"Does something useful\")\n                .inputSchema(buildSchema())\n                .build());\n    }\n\n    @Override\n    public Object handle(McpContext ctx, Object args) throws Exception {\n        return CallToolResult.ofText(\"done\");\n    }\n\n    private static JsonNode buildSchema() {\n        var s = JsonNodeFactory.instance.objectNode();\n        s.put(\"type\", \"object\");\n        return s;\n    }\n}\nserver.resources().add(\n    ResourceDescriptor.of(\"custom://data\"),\n    (ctx, req) -> new TextResourceContents(\"content\", req.uri(), \"text/plain\", null));\n```\n\n**Tachyon MCP** is available under the terms of the [Apache 2.0](/kpavlov/tachyon/blob/main/LICENSE).", "url": "https://wpnews.pro/news/java-mcp-server-with-streamable-http-netty-transports-and-stateless-deployment", "canonical_source": "https://github.com/kpavlov/tachyon", "published_at": "2026-06-25 10:20:44+00:00", "updated_at": "2026-06-25 10:44:06.588134+00:00", "lang": "en", "topics": ["developer-tools", "ai-tools", "ai-infrastructure"], "entities": ["Tachyon MCP", "Netty", "Model Context Protocol", "JDK 21"], "alternates": {"html": "https://wpnews.pro/news/java-mcp-server-with-streamable-http-netty-transports-and-stateless-deployment", "markdown": "https://wpnews.pro/news/java-mcp-server-with-streamable-http-netty-transports-and-stateless-deployment.md", "text": "https://wpnews.pro/news/java-mcp-server-with-streamable-http-netty-transports-and-stateless-deployment.txt", "jsonld": "https://wpnews.pro/news/java-mcp-server-with-streamable-http-netty-transports-and-stateless-deployment.jsonld"}}