GitHub - kpavlov/tachyon: ๐Ÿ’ซ Tachyon MCP Runtime for JVM - a Java Model Context Protocol (MCP) server with Streamable HTTP, native Netty transports, tasks, resources, prompts, resumable sessions, and stateless deployment support.

5 min read Original article โ†—

Maven Central Java 21+ Build Conformance: v0.1.16 codecov Docs Ask DeepWiki

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

  1. Add dependency:

    <dependency>
        <groupId>dev.tachyonmcp</groupId>
        <artifactId>tachyon-server</artifactId>
        <version>1.0.0-beta.8</version>
    </dependency>
  2. 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, one ctx.flush() per boundary
  • Sharable handlers โ€” @Sharable pipeline 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 ObjectMapper tree 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.