Tachyon MCP is a Java 21 Model Context Protocol (MCP) server built on Netty. It implements the MCP 2025-11-25 and partially MCP 2026-07-28 Streamable HTTP transport and runs stateless by default. It passes all official conformance tests for the latest protocol version and partially passes conformance tests for the Draft protocol.
π« 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.11</version> </dependency> -
Create MCP server:
import dev.tachyonmcp.server.TachyonServer; import dev.tachyonmcp.server.features.tools.ToolHandler; import dev.tachyonmcp.server.features.tools.ToolResult; public class WeatherMcpServer { public static void main(String... args) { 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), client-initiated 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 |
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; client-initiated 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.
No comments yet
Be the first to share your take.