Tachyon MCP: A Spec-Forward Java Runtime for the MCP Ecosystem

Tachyon MCP: A Spec-Forward Java Runtime for the MCP Ecosystem

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:

xml
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:

java
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:

java
 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:

java
 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 Accept header validation returning 406 on mismatch.
  • Tools, Resources, Prompts, Completions — paginated list endpoints with nextCursor, tools/call with isError and structured output, resources/subscribe/unsubscribe with live update notifications, prompts/get with argument resolution.
  • 2025-11-25 session management — SSE disconnect doesn’t remove the session. Clients reconnect with Last-Event-ID and 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) with tasks/cancel, tasks/result, and notifications/tasks/status broadcast 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:

java
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:

java
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:

bash
1git clone https://github.com/kpavlov/tachyon.git
2cd tachyon
3mvn install -pl tachyon-server -DskipTests

What’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.

tachyon-icon.png

Konstantin Pavlov

Konstantin Pavlov

Software Engineer working with Java, Kotlin, Swift, and AI. Focusing on software architecture and building AI-infused apps. Passionate about testing and Open-Source projects.