Conversation
Introduces compile-time annotation processing for the Model Context Protocol (MCP), generating highly optimized, reflection-free dispatchers that bridge untyped JSON-RPC arguments to strongly-typed Jooby controllers. Key implementations: - Add `@McpServer`, `@McpTool`, `@McpPrompt`, `@McpResource`, and `@McpCompletion` annotations for defining MCP capabilities. - Add `@McpParam` to customize schema parameter names and provide LLM descriptions (with planned Javadoc fallback). - Update `MvcRoute` to classify MCP components during construction and isolate them into a dedicated `mcpRoutes` list in `MvcRouter` to prevent REST generator conflicts. - Define `McpService` interface enforcing a strict execution contract with `McpSyncServerExchange` for Context resolution. - Implement `getMcpSourceCode` in `MvcRouter` to generate `*McpServer_` classes featuring: - Zero-reflection `switch/case` routing for tools, prompts, resources, templates, and completions. - Safe extraction and primitive casting from untyped argument maps. - Strict Jackson 3 (`tools.jackson`) registry lookup for complex POJO conversions. - Native Jooby dependency injection support using the existing `constructors()` AST utility. - Interface compliance by throwing `UnsupportedOperationException` for unused MCP capabilities.
- normalize: constructor generation
Implement full Annotation Processor (APT) support for generating MCP server specifications and handlers, bridging Jooby controllers with the Model Context Protocol SDK. Key implementations: * Annotations: Add code generation for @mcptool, @McpPrompt, @McpResource, and @McpCompletion. * Completion Routing: Implement "Group and Route" architecture. Groups multiple argument-specific completion methods under a single MCP Reference and routes them at runtime using compile-time generated enhanced switch/when expressions. * Dynamic Resources: Auto-detect URI placeholders (`{}`) to dynamically register as Resource Templates or static Resources. * Template Variables: Integrate `DefaultMcpUriTemplateManager` to safely extract and bind path variables from `req.uri()` into controller args. * Schema Generation: Optimize Jackson 3 (`tools.jackson`) schema builder to automatically omit output schemas for primitives, standard java.lang types, and internal MCP classes. * Idiomatic Output: Generate clean, modern Java (var, enhanced switch) and Kotlin (val, when) with strict null-safety checks and safe default fallbacks for unmatched requests.
- into: rest, jsonrpc, trpc and mcp
- get back tests passed
This commit fixes an issue where `@McpCompletion` requests were failing with a "-32601 Missing handler" error because the `completions()` capability was omitted from the generated `ServerCapabilities`. It also introduces a major refactor to the APT generation to eliminate duplicate code. Details: * Added `capabilities.completions()` to the generated `capabilities()` method. * Safely stripped lingering AST quotes from `@McpCompletion` annotation values to ensure precise router matching. * Refactored `McpRoute` to generate a single, unified handler method accepting both `McpSyncServerExchange` and `McpTransportContext` alongside the request, dynamically extracting `io.jooby.Context` from the available transport. * Updated `McpRouter` to register tools, prompts, resources, and completions using clean lambda adapters that map the current server type (stateful vs. stateless) to the unified handler.
- add web-socket transport
This commit refactors the transport layer by removing the redundant "Jooby" prefix and "Server" identifiers from the class names, as their context is already clear within the `io.jooby.mcp.transport` package. To provide a consistent and predictable API, all transport implementations now share the `TransportProvider` suffix. Renames: * JoobySseTransportProvider -> SseTransportProvider * JoobyStatelessServerTransport -> StatelessTransportProvider * JoobyStreamableServerTransportProvider -> StreamableTransportProvider * JoobyWebSocketServerTransportProvider -> WebSocketTransportProvider
…e transport
This commit fixes a race condition where the Streamable HTTP transport
would randomly return an empty payload ("") instead of the tool execution
result. The failure occurred because the Jooby I/O thread was being blocked,
causing the server to tear down the TCP socket before the final SSE chunk
could be flushed to the network.
Details:
* Replaced `.block()` calls inside `ctx.upgrade()` with non-blocking
Reactor `.subscribe()` chains to prevent I/O thread deadlocks.
* Refactored `notifyClients`, `closeGracefully`, and the `lastId` replay
loop to use native Reactor `Flux` chains instead of blocking Java
`.parallelStream().forEach()` loops.
* Added a 50ms `Mono.delay` to `JoobyStreamableMcpSessionTransport.closeGracefully()`.
This guarantees the underlying server (e.g., Undertow) has a sufficient
buffer window to physically flush the final SSE event to the network
layer before the connection is destroyed.
This commit significantly expands the `@McpResource` annotation to fully support the Model Context Protocol (MCP) Resource specification, and updates the annotation processor to map these new fields into the SDK constructors. Details: * Renamed the default `value` attribute to `uri` for better clarity. * Added support for `title`, `description`, `mimeType`, and `size` metadata. * Introduced a nested `@McpAnnotations` interface to support advanced resource metadata, including `audience` roles, `priority`, and `lastModified` dates. * Updated the `McpRoute` APT generator to extract the new attributes and map them correctly into `McpSchema.Resource` and `McpSchema.ResourceTemplate`. * Implemented string-based parsing for nested annotations in `McpRoute` to safely generate the `ResourceAnnotations` object for Java and Kotlin, bypassing the need for heavy `ElementVisitor` boilerplate.
- simplify mcp description lookup
This commit expands the `@McpTool` annotation to fully support the latest Model Context Protocol (MCP) tool specification, allowing developers to provide richer metadata to the LLM. Details: * Added support for the `title` attribute in `@McpTool` for human-readable display names. * Introduced nested `@McpAnnotations` for tools to support execution hints: `readOnlyHint`, `destructiveHint`, `idempotentHint`, and `openWorldHint`. * Updated the `McpRoute` APT generator to parse the nested tool annotations from their string representation, falling back to spec defaults when omitted. * Fixed compilation errors in the code generator by correctly aligning the constructor arguments for `McpSchema.ToolAnnotations`.
This commit completely decouples the MCP core and APT code generator from Jackson and victools, resolving classpath conflicts between Jackson 2 and Jackson 3. It introduces dedicated integration modules to cleanly handle JSON serialization and JSON Schema generation based on the user's runtime environment. Details: * Refactored the APT generator (`McpRoute`, `McpRouter`) to build schemas using standard `java.util.LinkedHashMap` and `java.util.ArrayList` instead of Jackson's `ObjectNode` and `ArrayNode`. * Removed hardcoded `victools` configuration from the generated `install` methods. The generated router now dynamically requires the `SchemaGenerator` from the Jooby application registry. * Delegated the final schema map conversion strictly to the abstracted `McpJsonMapper` interface. * Introduced the `jooby-mcp-jackson2` module to provide bindings for Jackson 2 and `victools` 4.x. * Introduced the `jooby-mcp-jackson3` module to provide bindings for Jackson 3 and `victools` 5.x.
- move transport as internal package - add mcp-inspector - add automatic-module-name - fix error for missing completions
Member
Author
|
/cc @kliushnichenko |
Since the routing lambdas now directly inject the transport context
for both stateful and stateless flows, the `transportContext` parameter
is guaranteed to be non-null in the generated handler methods.
This commit cleans up the APT generator (`McpRoute`, `McpRouter`) by:
* Removing redundant null-checks and ternary operators associated with
the transport context.
* Eliminating Kotlin safe-calls (`?.`) in method signatures and
variable extractions.
* Streamlining Jooby `Context` extraction to use direct, safe casts
(e.g., `(io.jooby.Context) transportContext.get("CTX")`).
* Significantly reducing the boilerplate and branching in all generated
tool, prompt, resource, and completion handlers.
MCP tool, prompt, resource, and completion calls. It centralizes
execution logic, exception handling, and protocol error mapping, while
providing developers a clean hook to inject custom telemetry, tracing,
or MDC context propagation.
Details:
* Added `McpInvoker` interface and `DefaultMcpInvoker` implementation.
* Integrated Jooby's `Router.errorCode()` to seamlessly map standard
framework exceptions (e.g., 400 Bad Request) to standard MCP
JSON-RPC errors (e.g., -32602 Invalid Params).
* Implemented LLM "self-healing" for tools: Unhandled business exceptions
are now caught and returned as a `CallToolResult` with `isError=true`.
This prevents protocol aborts and feeds the error text directly back
into the LLM context so it can self-correct.
* Updated the APT generator (`McpRouter`) to dynamically resolve the
`McpInvoker` from the Jooby application registry using local variables,
ensuring the generated router remains completely stateless.
* Wrapped all routing lambdas in `invoker.invoke(operationId, action)`,
passing contextual operation IDs (e.g., `tools/add_numbers` or
`resources/calculator://history/{user}`).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
This commit introduces first-class support for the Model Context Protocol (MCP) in Jooby, built natively on top of the official MCP Java SDK. It provides a highly efficient, annotation-driven architecture for exposing server capabilities to LLM clients.
Key Features & Architecture:
io.jooby.annotation.mcpnamespace to define server capabilities.@McpTool(description="...")) to standard Javadoc comments, ensuring LLMs get the context they need without forcing developers to duplicate documentation.*Mcp_routing class that implementsio.jooby.mcp.McpService, ensuring fast startup without runtime reflection.McpModuleis completely decoupled from JSON serialization. Developers must explicitly register eitherMcpJackson2Module(for Jackson 2) orMcpJackson3Module(for Jackson 3) alongside their respective Jooby Jackson modules.@McpServerannotation.McpInspectorModuleto facilitate easy testing and debugging of MCP endpoints during development.Example
Input:
Output: