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 an McpExtension, 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.4</version>
5</dependency>(check Maven Central for latest version)
Start your MCP server with your tool:
1var handle = TachyonServer.builder()
2 .name("weather-mcp")
3 .tool(myWeatherTool)
4 .port(8080)
5 .start();That’s 2025-11-25 spec-compliant MCP server: JSON-RPC 2.0, Streamable HTTP (POST + SSE + DELETE + OPTIONS), session lifecycle, 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 AbstractSyncToolHandler for simple request/response logic:
1class GetWeatherTool extends AbstractSyncToolHandler {
2
3 GetWeatherTool() {
4 super(ToolDescriptor.builder("get_weather", """
5 {
6 "type": "object",
7 "properties": {
8 "city": { "type": "string", "description": "City name" }
9 },
10 "required": ["city"]
11 }
12 """, null)
13 .description("Get current weather for a city")
14 .build());
15 }
16
17 @Override
18 public ToolResult handle(InteractionContext ctx, ToolArgs args) {
19 var city = args.string("city");
20 return ToolResult.text("🌤️ 22°C in " + city);
21 }
22}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 AbstractSyncToolHandler {
2
3 ForecastApiTool() {
4 super(ToolDescriptor.builder("get_forecast", """
5 {
6 "type": "object",
7 "properties": {
8 "city": { "type": "string", "description": "City name" }
9 },
10 "required": ["city"]
11 }
12 """, null)
13 .description("Get current weather forecast")
14 .build());
15 }
16
17 @Override
18 public ToolResult handle(InteractionContext ctx, ToolArgs args) {
19 var city = args.string("city");
20 var result = weatherApi.fetch(city); // blocking call — safe on a virtual thread
21 return ToolResult.text(result.summary());
22 }
23}AsyncToolHandler exists for integrating clients that already return a CompletionStage (Reactor, Vert.x, async HTTP clients). If you’re writing new code, sync is simpler and costs nothing extra.
What’s already spec-compliant
Tachyon passes 46 out of 46 tests on the official @modelcontextprotocol/[email protected] runner against MCP spec 2025-11-25. The conformance suite covers 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 — paginated list endpoints with
nextCursor,tools/callwithisErrorand structured output,resources/subscribe/unsubscribewith live update notifications,prompts/getwith argument resolution. - 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 suite, Tachyon also implements features the runner does 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, they become a negotiable extension (SEP-1686)— clients must opt in at the initialize handshake.
Tachyon supports both models simultaneously.
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 handle = TachyonServer.builder()
2 .extension(TasksExtension.instance())
3 .port(8080)
4 .start();Clients that include "io.modelcontextprotocol/tasks" in their initialize capabilities get the extension’s tools and resources automatically. Clients that don’t fall back to standard task endpoints. Tachyon works correctly with clients built against either spec version, today.
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 handle = 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, 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 — full support for the 2026-07-28 draft is the immediate priority: updated protocol version negotiation, and any new SEPs that 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.
