Tachyon MCP is a Java 21 Model Context Protocol (MCP) server built on Netty. It implements the 2025-11-25 Streamable HTTP transport, passes all official conformance tests, and runs stateless by default.
๐ซ Why Tachyon?
๐งต Synchronous code, asynchronous runtime โ write blocking handlers; Java 21 virtual threads run them off the Netty event loop. No thread pools, reactive pipelines, or CompletableFuture boilerplate. Coroutine-first Kotlin DSL included.
๐ก๏ธ Stable APIs across spec changes โ domain types (ToolHandler, ResourceHandler, PromptHandler, tasks) sit behind an internal protocol mapper. Spec upgrades change the mapper, not your handlers.
โ๏ธ Serverless by default โ stateless request handling works out of the box on AWS Lambda and similar. Opt into sessions (.session(s -> s.enabled(true))) for SSE resumability, Last-Event-ID replay, and TTL cleanup.
๐ Production transport โ Netty with backpressure, graceful shutdown, DNS rebinding protection, and native transport auto-detection (io_uring โ epoll โ kqueue โ NIO).
TL;DR
-
Add dependency:
<dependency> <groupId>dev.tachyonmcp</groupId> <artifactId>tachyon-server</artifactId> <version>1.0.0-beta.8</version> </dependency>
-
Create MCP server:
import dev.tachyonmcp.server.TachyonServer; import dev.tachyonmcp.server.features.tools.ToolHandler; import dev.tachyonmcp.server.features.tools.ToolResult; void main() { TachyonServer.builder() .name("weather-mcp") .tool(ToolHandler.of( b -> b.name("get_forecast") .description("Get weather forecast") .inputSchema(""" {"type":"object","properties":{"city":{"type":"string"}},"required":["city"]} """), (ctx, args) -> ToolResult.text("โ๏ธ 22ยฐC"))) .port(8080) .start(); }
Documentation
| Guide | Description |
|---|---|
| Quickstart | Build a working server in 5 minutes |
| Configuration | Network, I/O engine (native transports), sessions, CORS |
| Tools | Sync/async handlers, input schema, ToolResult |
| Resources | Static URIs, dynamic handlers, URI templates |
| Tasks | Long-running operations, state machine, TasksExtension |
| Extensions | Custom protocol extensions, negotiation |
| Kotlin DSL | Coroutine-first DSL, TachyonServer { }, scope reference |
| Kotlin module | tachyon-server-kotlin module overview |
Agent Skill
Add agent skill to write better code using this SDK:
npx skills add kpavlov/tachyon --skill tachyon-mcp
The skill includes compilable Java and Kotlin example sources under .agents/skills/tachyon-mcp/resources/.
They are compiled as extra source roots of the e2e module during mvn test to keep them valid.
Check out Skills CLI for more options.
Features
Full 2025-11-25 MCP surface over Streamable HTTP, verified by the official conformance suite:
| Area | What you get |
|---|---|
| Tools | Sync & async handlers, JSON Schema 2020-12 input/output validation, outputSchema, annotations, per-tool taskSupport, list_changed |
| Resources | Static + dynamic handlers, URI templates, subscribe/unsubscribe with updated notifications, text & blob content |
| Prompts | List/get with resolver handlers, input-required (MRTR) flow, list_changed |
| Tasks | Full tasks/* lifecycle with enforced state machine, status broadcast, stale-task janitor, TasksExtension (SEP-1686) |
| Client calls | sampling/createMessage, elicitation (form and URL modes), bidirectional cancelled |
| Sessions | Stateless by default; opt-in SSE resumability, Last-Event-ID replay, TTL janitor, pluggable store/ID generator |
| Transport | Native transport auto-detect, backpressure watermarks, graceful drain-on-shutdown, CORS + origin/DNS-rebinding protection |
| Extensions | Negotiable protocol extensions (SEP-2133) with extension-gated tool visibility |
Detailed method-by-method breakdown
Core โ JSON-RPC 2.0; Streamable HTTP (POST/GET-SSE/DELETE/OPTIONS); lifecycle initialize โ initialized โ ACTIVE; cursor pagination on all list methods; strict Accept validation (406); pending-request timeout.
Tools โ tools/list (paginated), tools/call (isError), outputSchema + annotations, sync/async handlers, name validation (SEP-986), inline notifications/logging mid-call, JSON Schema 2020-12 validation (SEP-1613), notifications/tools/list_changed.
Resources โ resources/list, resources/read (text & blob), resources/templates/list, subscribe/unsubscribe, list_changed + updated notifications, dynamic ResourceHandler.
Prompts โ prompts/list (paginated), prompts/get, input-required flow, list_changed.
Tasks โ tasks/list|get|cancel|result; state machine SUBMITTED โ WORKING โ INPUT_REQUIRED โ COMPLETED/FAILED/CANCELLED (+ REJECTED/AUTH_REQUIRED); notifications/tasks/status on every transition; stale-task janitor; per-tool execution.taskSupport; TasksExtension (SEP-1686) exposing create_task + task://{id}, hidden from clients that don't negotiate it.
Logging & client calls โ logging/setLevel per session, notifications/message above threshold, progress notifications; sampling/createMessage; elicitation form + URL modes; bidirectional notifications/cancelled.
Transport & sessions โ Netty 4.2, io_uring/epoll/kqueue/NIO auto-detect, platform-thread event loops + virtual-thread handlers, writability backpressure, configurable idle timeouts; stateless or in-memory sessions, 5s janitor / 30s TTL, SSE disconnect survives (event-log replay on reconnect), graceful drain (shutdownGracePeriod, default 5s) before force-interrupt.
Quick Start
See docs/quickstart.md for a full walkthrough with Java and Kotlin examples, curl test, and next-step links.
TasksExtension (SEP-1686)
var handle = TachyonServer.builder() .extension(TasksExtension.instance()) // exposes create_task tool + task://{id} resource .port(8080) .start();
Clients that include "extensions": {"io.modelcontextprotocol/tasks": {}} in their initialize capabilities receive the extension's tool and resource template. Clients that don't negotiate it see standard tasks/* methods. See docs/tasks.md.
Protocol isolation
Handler interfaces (ToolHandler, ResourceHandler, PromptHandler) and descriptor types use stable domain types. When Tachyon upgrades to a new protocol version, only the internal mapper layer changes; handler implementations are unaffected. Domain types track the 2026-07-28 spec shape where it improves on 2025-11-25 (e.g. Annotations.lastModified, ResourceLink in ContentBlock).
Performance
- Native transports โ
io_uringโepollโkqueueโ NIO auto-detect - Write-buffer watermarks โ 32 KB low / 128 KB high, backpressure wired end to end
- Batch flushing โ
ctx.write()accumulates, onectx.flush()per boundary - Sharable handlers โ
@Sharablepipeline handlers, no per-request allocation - Virtual threads โ handlers offloaded from the event loop, no manual pools
- Streaming JSON-RPC โ Jackson streaming codec, no or limited
ObjectMappertree round-trips
Not yet supported
- HTTP/2 โ transport is HTTP/1.1
- Rate limiting
- 2026-07-28 protocol version โ domain types already track its shape; negotiation pending
FAQ
Can I deploy to AWS Lambda?
Yes. Servers are stateless by default, so each invocation processes one request independently. Enable sessions with .session(cfg -> cfg.enabled(true)) when you need SSE resumability or replay.
How do I write a tool?
See docs/tools.md โ covers lambda and class-based handlers, input schema, and ToolResult factories.
How do I expose a resource?
See docs/resources.md โ covers static URIs, dynamic handlers, URI templates, and subscriptions.
License
Tachyon MCP is available under the terms of the Apache 2.0.
