Model Context Protocol (MCP) is the REST API of the AI era — the universal interface between agents and the tools they use. The protocol is also evolving fast: new drafts, experimental features, and Specification Enhancement Proposals (SEPs) are landing regularly. For teams building on the JVM, keeping up means staying close to the spec.
The Java ecosystem already has good options, and they stack in layers. At the base, the official MCP Java SDK is a clean protocol library you can embed anywhere. On top of it, Spring AI MCP gives you a full server integrated with the Spring ecosystem, and the Quarkus team has built a polished MCP extension for the Quarkus stack. Each is the right tool in its context.
I built Tachyon MCP Runtime to sit in a different spot in that stack: a standalone, pure-Java MCP server runtime — more than a protocol library, but without pulling in a framework. It focuses on tracking the emerging spec closely and giving you an extensible engine for AI workloads.
The goal: stay current with the spec
MCP is not a stable protocol yet. The 2025-11-25 spec introduced tasks as an experimental feature. The upcoming 2026-07-28 draft promotes Tasks to a negotiable extension. SEPs like elicitation (SEP-1034, SEP-1330) add new interaction patterns. A server that’s correct today may lag behind by next quarter.
Tachyon is designed to track these changes quickly. The extension mechanism is first-class — adding support for a new SEP or draft feature means registering a ServerExtension, not forking the core. My goal is for Tachyon to be a place where emerging MCP capabilities land early.
Five lines to a running server
Add Maven dependency:
1<dependency>
2 <groupId>dev.tachyonmcp</groupId>
3 <artifactId>tachyon-server</artifactId>
4 <version>1.0.0-beta.13</version>
5</dependency>(check Maven Central for latest version)
Start your MCP server with your tool:
1var server = TachyonServer.builder()
2 .name("weather-mcp")
3 .tool(myWeatherTool)
4 .port(8080)
5 .start();That’s an MCP server conforming to both 2025-11-25 and the upcoming 2026-07-28 specification: JSON-RPC 2.0, Streamable HTTP, DNS rebinding protection, and CORS — all configured with sensible defaults. No framework, no annotation processing, no dependency injection container required.
Writing your first tool
Tools are the primary unit of work. Extend AbstractToolHandler for simple request/response logic:
1class GetWeatherTool extends AbstractToolHandler {
2
3 GetWeatherTool() {
4 super(ToolDescriptor.builder()
5 .name("get_weather")
6 .inputSchema("""
7 {
8 "type": "object",
9 "properties": {
10 "city": { "type": "string", "description": "City name" }
11 },
12 "required": ["city"]
13 }
14 """)
15 .description("Get current weather for a city")
16 .build());
17 }
18
19 @Override
20 public ToolResult handle(InteractionContext ctx, Args args) {
21 var city = args.stringValue("city");
22 return ToolResult.text("🌤️ 22°C in " + city);
23 }
24}For tools that call downstream APIs or run database queries, the sync handler is still the right choice — blocking is fine because Tachyon runs every handler on a JDK 21 virtual thread, not on a Netty I/O thread. A Thread.sleep, a JDBC query, or a synchronous HTTP call parks the virtual thread without touching the event loop:
1class ForecastApiTool extends AbstractToolHandler {
2
3 ForecastApiTool() {
4 super(ToolDescriptor.builder()
5 .name("get_forecast")
6 .inputSchema("""
7 {
8 "type": "object",
9 "properties": {
10 "city": { "type": "string", "description": "City name" }
11 },
12 "required": ["city"]
13 }
14 """)
15 .description("Get current weather forecast")
16 .build());
17 }
18
19 @Override
20 public ToolResult handle(InteractionContext ctx, Args args) {
21 var city = args.stringValue("city");
22 var result = weatherApi.fetch(city); // blocking call — safe on a virtual thread
23 return ToolResult.text(result.summary());
24 }
25}For clients that already return a CompletionStage (Reactor, Vert.x, async HTTP clients), use ToolHandler.ofAsync(...) or override handleAsync(...) on AbstractToolHandler. If you’re writing new code, sync is simpler and costs nothing extra.
What’s already spec-compliant
Tachyon passes all official conformance tests for MCP 2025-11-25 and the upcoming MCP 2026-07-28 specification. The conformance suites cover the core protocol:
- Protocol basics — JSON-RPC 2.0, protocol version negotiation, pending request timeout, max request body (1 MB), and strict
Acceptheader validation returning406on mismatch. - Tools, Resources, Prompts, Completions — paginated list endpoints with
nextCursor,tools/callwithisErrorand structured output,resources/subscribe/unsubscribewith live update notifications,prompts/getwith argument resolution. - 2025-11-25 session management — SSE disconnect doesn’t remove the session. Clients reconnect with
Last-Event-IDand the server replays the event log from that point. Session TTL is configurable (default 30s). - Security — DNS rebinding protection and origin validation out of the box.
- Input validation — JSON Schema 2020-12 validation on tool and prompt arguments before your handler is called.
Beyond the conformance suites, Tachyon also implements features the runners do not yet test:
- Tasks — the 2025-11-25 spec defines tasks as experimental. Tachyon implements the full state machine (
SUBMITTED → WORKING → COMPLETED/FAILED/CANCELLED) withtasks/cancel,tasks/result, andnotifications/tasks/statusbroadcast on every transition. - Elicitation — lets the server request structured input from the user mid-conversation.
Tasks: bridging two specs
The MCP spec is moving fast. In 2025-11-25, tasks are an experimental first-class concept. In the upcoming 2026-07-28 draft, SEP-1686 defines them as a negotiable extension rather than an initialize capability.
Tachyon implements the core task lifecycle and the optional TasksExtension.
The core task system (tasks/list, tasks/get, tasks/cancel, tasks/result) is always available. If a client negotiates the io.modelcontextprotocol/tasks extension, it additionally receives a create_task tool and a task://{id} resource template:
1var server = TachyonServer.builder()
2 .extension(TasksExtension.instance())
3 .port(8080)
4 .start();2025-11-25 clients that include "io.modelcontextprotocol/tasks" in their initialize capabilities get the extension’s tools and resources automatically. Clients that don’t negotiate it use the standard task endpoints.
Stateless mode for serverless
Session state is a problem on ephemeral infrastructure — AWS Lambda, Cloud Run, or any autoscaling setup where a request may hit a different instance on reconnect. The tradeoff is explicit: stateless mode handles request/response tools and prompts, but gives up session-bound features, like SSE replay via Last-Event-ID. For serverless tool-calling workloads that don’t need those, you trade them for zero distributed session storage and no sticky-routing requirement.
Tachyon’s stateless mode is enabled by default, but you can switch to sessions on:
1var server = TachyonServer.builder()
2 .tool(myTool)
3 .session(s -> s.enabled(true))
4 .port(8080)
5 .start();Architecture: a non-blocking I/O foundation
AI agent traffic — multiplexed, long-lived SSE sessions and bursty tool calls — is exactly what non-blocking I/O is for. That’s why Tachyon is built on Netty 4.2: native transports (io_uring on Linux, kqueue on macOS, NIO everywhere else), and virtual threads for handler execution so blocking tool implementations stay off the event loop.
No benchmarks yet — I’d rather show numbers than promise them. The work is on the roadmap.
Getting started
Add dev.tachyonmcp:tachyon-server from Maven Central — Apache 2.0, JDK 21+, no framework dependencies.
Or clone and build yourself:
1git clone https://github.com/kpavlov/tachyon.git
2cd tachyon
3mvn install -pl tachyon-server -DskipTestsWhat’s next
A word on status first: Tachyon is beta, very close to 1.0, and a solo project moving fast to keep up with the spec. The API will shift before 1.0. It’s a solid choice for experimenting with MCP and staying ahead of the protocol — not yet a production commitment I’d ask you to make blind. With that said, two tracks drive the roadmap.
Spec alignment — maintaining conformance with 2025-11-25 and the upcoming 2026-07-28 specification remains the immediate priority as new SEPs land. The extension mechanism exists precisely to absorb these changes without touching the core.
Running at scale — rate limiting, HTTP/2, and deeper observability hooks are planned for teams running Tachyon in production.
The source is at github.com/kpavlov/tachyon under the Apache 2.0 licence. If you’re building MCP tooling on the JVM and want to stay ahead of the spec, give Tachyon a try — and open an issue if something doesn’t work the way you expect.

