This is an automated email from the ASF dual-hosted git repository.
jamesbognar pushed a commit to branch docs
in repository https://gitbox.apache.org/repos/asf/juneau.git
The following commit(s) were added to refs/heads/docs by this push:
new a331b55a76 docs: MCP re-layering — move release notes to 10.0.0,
refresh topic pages, add juneau-bean-jsonrpc page
a331b55a76 is described below
commit a331b55a76900ee7ad7b6e2d23ba5ff95442bb92
Author: James Bognar <[email protected]>
AuthorDate: Wed Jul 29 13:11:30 2026 -0400
docs: MCP re-layering — move release notes to 10.0.0, refresh topic pages,
add juneau-bean-jsonrpc page
- move MCP release-notes content from 9.5.0 to 10.0.0, rewritten for the
four-module architecture
- rewrite JuneauBeanMcp / JuneauRestServerMcp topic pages (neutral core +
2025-06-18 adapter; drop deleted McpDispatcher/Mcp facade; setName/setVersion
config)
- add dedicated JuneauBeanJsonRpc topic page + sidebar entry (resolves the
source package-info doclink)
- refresh ecosystem-overview, WhyJuneau, JuneauBean index, and
JuneauShadedAll module lists
Co-authored-by: Cursor <[email protected]>
---
pages/release-notes/10.0.0.md | 190 +++++++++++++++++++++++++-
pages/release-notes/9.5.0.md | 115 +---------------
pages/topics/01.00.JuneauEcosystemOverview.md | 2 +-
pages/topics/01.02.WhyJuneau.md | 2 +-
pages/topics/05.00.JuneauBean.md | 3 +-
pages/topics/05.07.JuneauBeanMcp.md | 58 ++++----
pages/topics/05.12.JuneauBeanJsonRpc.md | 131 ++++++++++++++++++
pages/topics/11.JuneauRestServerMcp.md | 167 +++++++++++++---------
pages/topics/23.05.JuneauShadedAll.md | 6 +-
sidebars.ts | 7 +-
10 files changed, 463 insertions(+), 218 deletions(-)
diff --git a/pages/release-notes/10.0.0.md b/pages/release-notes/10.0.0.md
index 1f9bf843b1..6dbfdae6d3 100644
--- a/pages/release-notes/10.0.0.md
+++ b/pages/release-notes/10.0.0.md
@@ -687,6 +687,194 @@ These replace the former `Json5.DEFAULT_READABLE`,
`Ini.DEFAULT_READABLE`, and `
constants (see Breaking Changes below). The
[Marshallers](/docs/topics/Marshallers), [JSON5](/docs/topics/Json5),
[Hjson](/docs/topics/Hjson), and [Ini](/docs/topics/Ini) topic pages were
updated accordingly.
+### MCP (Model Context Protocol) support (new modules)
+
+Juneau's first [Model Context Protocol](https://modelcontextprotocol.io/)
support lands in 10.0.0 across four new modules. (An earlier MCP write-up
appeared in the draft, unreleased `9.5.0` notes describing a single-revision
`juneau-bean-mcp` / `juneau-rest-server-mcp` pair; MCP itself had not shipped
in any released Juneau version at that point, so that content has been
withdrawn from `9.5.0` and replaced by this re-layered design, which lands for
the first time here.) The implementatio [...]
+
+- **`juneau-bean-jsonrpc`** — revision-neutral JSON-RPC 2.0 envelope beans.
+- **`juneau-rest-server-mcp`** — revision-neutral REST-server core
(tool/prompt/resource registry, dispatch contract, pagination, the two HTTP
entry points), with zero compile-time knowledge of any MCP protocol revision.
+- **`juneau-bean-mcp-2025-06-18`** — MCP revision `2025-06-18` wire beans
(renamed from the withdrawn draft's `juneau-bean-mcp`).
+- **`juneau-rest-server-mcp-2025-06-18`** — the `2025-06-18` REST-server
adapter that binds the neutral core to those wire beans; this is the module
application code depends on to actually expose an MCP endpoint today.
+
+### `juneau-bean-jsonrpc` (new module)
+
+A new bean module, `juneau-bean-jsonrpc`, models the revision-neutral JSON-RPC
2.0 envelope as Juneau `@Marshalled` POJOs. It carries no MCP-specific
knowledge — it depends on `juneau-marshall` only — and is the shared framing
layer beneath protocol-specific bean modules such as
`juneau-bean-mcp-2025-06-18`.
+
+### Coverage
+
+- **`JsonRpcRequest`** — `jsonrpc`, `id`, `method`, `params`.
+- **`JsonRpcResponse`** — `jsonrpc`, `id`, `result`, `error`. Also carries
three `public static` helpers used by dispatch implementations: `ok(id,
result)` and `errorResponse(id, code, message[, data])` (both response
factories), plus `notification(id)` (a `boolean` predicate testing whether an
id represents a JSON-RPC notification, i.e. `id == null`).
+- **`JsonRpcError`** — `code`, `message`, `data`.
+- **`McpException`** — a `RuntimeException` carrying JSON-RPC error fields
(`code`, `data`) for handler-side propagation, with `toJsonRpcError()`
converting it to a `JsonRpcError`.
+
+### Example
+
+```java
+import org.apache.juneau.bean.jsonrpc.*;
+import org.apache.juneau.json.*;
+
+JsonRpcRequest req = new JsonRpcRequest()
+ .setJsonrpc("2.0")
+ .setId(1)
+ .setMethod("tools/list");
+
+String wire = JsonSerializer.DEFAULT.serialize(req);
+JsonRpcRequest back = JsonParser.DEFAULT.parse(wire, JsonRpcRequest.class);
+
+JsonRpcResponse ok = JsonRpcResponse.ok(1, "pong");
+JsonRpcResponse err = JsonRpcResponse.errorResponse(1, -32601, "Method not
found");
+```
+
+### `juneau-rest-server-mcp` (re-layered into a revision-neutral core)
+
+`juneau-rest-server-mcp` has been re-layered from a single-revision
implementation into a **revision-neutral core** with zero compile-time
knowledge of any MCP protocol revision — enforced by a `maven-enforcer`
banned-dependency rule that fails the build if this module ever depends on
`juneau-bean-mcp-*` or `juneau-rest-server-mcp-*`. A protocol revision is
supplied by an `McpRevision` implementation living in its own adapter module
(see `juneau-rest-server-mcp-2025-06-18` below); a cons [...]
+
+### New Classes
+
+- **`McpRevision`** — the 3-method SPI a protocol revision implements:
`protocolVersion()`, `dispatch(McpExchange, McpServerConfig, BeanStore)`, and
`errorCode(McpErrorKind)`.
+- **`McpExchange`** — the inbound JSON-RPC envelope plus request-header
access, with no servlet or HTTP types attached.
+- **`McpErrorKind`** — revision-neutral classification of a dispatch failure
(`INVALID_REQUEST`, `UNKNOWN_METHOD`, `TOOL_NOT_FOUND`, `PROMPT_NOT_FOUND`,
`RESOURCE_NOT_FOUND`, `INVALID_PARAMS`, `INTERNAL_ERROR`, `PARSE_ERROR`); each
revision maps kinds to its own JSON-RPC error codes via
`McpRevision.errorCode(McpErrorKind)`.
+- **`McpServerConfig`** — re-shaped aggregate registry of tools, prompts,
resources, `name`/`version`, `instructions`, and pagination strategy
(`cursor`). No longer holds `protocolVersion` (now owned by
`McpRevision.protocolVersion()`) or `capabilities` (now owned by a
revision-specific hook — see the `2025-06-18` adapter below);
`setProtocolVersion(String)` has no replacement.
+- **`McpRestServlet`** — abstract `BasicRestServlet` subclass exposing `POST
/`. Subclasses implement `createMcpConfig()`; a new abstract `revision()`
method supplies the bound protocol revision.
+- **`McpEndpoint`** — mixin interface exposing `POST /mcp` on any `@Rest`
resource, re-shaped the same way (`getMcpConfig()` plus an abstract
`revision()`).
+- **Neutral model** — `McpToolSpec`/`McpToolOutcome`,
`McpPromptSpec`/`McpPromptArgument`/`McpPromptMessage`/`McpPromptOutcome`,
`McpResourceSpec`/`McpResourceOutcome`, `McpContentBlock`,
`McpResourceContents`, `McpRole`, and the unconstrained JSON-object carrier
`McpSchema`. This is what a handler is written against, so a handler compiles
against exactly one protocol revision's worth of assumptions: none.
+- **`McpToolHandler`**, **`McpPromptHandler`**, **`McpResourceHandler`** — raw
`@FunctionalInterface` handlers, now typed against the neutral spec/outcome
types above (previously typed against wire beans). Each keeps its
default-throwing `descriptor()`, so a lambda implementing only the
call/get/read method still compiles.
+- **`McpCursor`**, **`McpPage<T>`** — pagination strategy seam, unchanged:
`McpCursor.SINGLE_PAGE` (default, returns everything in one page) and
`McpCursor.fixedSize(n)` (opaque integer-offset paging).
+- **`McpParamUtils`** — shared `params`-coercion plumbing
(`asMap`/`strParam`/`mapParam`) used by revision `dispatch()` implementations.
+
+**Removed:** `McpDispatcher` and the static `Mcp` façade are gone with no
drop-in replacement class — their method-table logic now lives in each
revision's own `McpRevision.dispatch()` implementation (see
`Mcp20250618Revision` below), and their internal plumbing helpers were
redistributed onto `JsonRpcResponse`, `McpCursor`, and the new `McpParamUtils`.
+
+See [juneau-rest-server-mcp](/docs/topics/JuneauRestServerMcp) for the full
topic.
+
+### `juneau-bean-mcp-2025-06-18` (renamed from `juneau-bean-mcp`)
+
+The bean module has been renamed from `juneau-bean-mcp` to
`juneau-bean-mcp-2025-06-18` (package `org.apache.juneau.bean.mcp.v20250618`)
as part of the re-layering above — the name change makes room for a future
sibling module under the same `org.apache.juneau.bean.mcp` namespace should a
later MCP protocol revision need one. The module still models the MCP
`2025-06-18` wire format as Juneau `@Marshalled` POJOs that round-trip cleanly
through any Juneau serializer/parser (JSON, JSON5, Me [...]
+
+### Coverage
+
+- **Initialization** — `InitializeRequest`, `InitializeResult`,
`Implementation`, `ClientCapabilities`, `ServerCapabilities`, and the
per-feature capability beans (`ToolCapability`, `PromptCapability`,
`ResourceCapability`, `LoggingCapability`, `RootsCapability`).
+- **Tools** — `Tool`, `CallToolRequest`, `CallToolResult`, `ListToolsResult`,
plus the `JsonSchema` argument-schema bean.
+- **Prompts** — `Prompt`, `PromptArgument`, `GetPromptRequest`,
`GetPromptResult`, `ListPromptsResult`, `PromptMessage`, `Role`.
+- **Resources** — `Resource`, `ReadResourceRequest`, `ReadResourceResult`,
`ListResourcesResult`, plus polymorphic `ResourceContents`
(`TextResourceContents`, `BlobResourceContents`).
+- **Polymorphic content blocks** — `Content` interface with `TextContent`,
`ImageContent`, `EmbeddedResourceContent` discriminated by a `type` property.
+- **Constants** — `McpMethods` (JSON-RPC method names) and `McpProtocol`
(version constants, including the `JSON_RPC_2_0` literal).
+
+### Polymorphic Serialization
+
+`Content` and `ResourceContents` use `@Marshalled(typePropertyName="type",
dictionary={...})` to discriminate at the wire level. Configure your serializer
with `addBeanTypes()` to emit the discriminator on output:
+
+```java
+JsonSerializer ser = JsonSerializer.create()
+ .addBeanTypes()
+ .typePropertyName(Content.class, "type")
+ .typePropertyName(ResourceContents.class, "type")
+ .build();
+```
+
+### Example
+
+```java
+import org.apache.juneau.bean.jsonrpc.*;
+import org.apache.juneau.bean.mcp.v20250618.*;
+import org.apache.juneau.json.*;
+
+JsonRpcRequest req = new JsonRpcRequest()
+ .setJsonrpc(McpProtocol.JSON_RPC_2_0)
+ .setId(1)
+ .setMethod(McpMethods.TOOLS_LIST);
+
+String wire = JsonSerializer.DEFAULT.serialize(req);
+JsonRpcRequest back = JsonParser.DEFAULT.parse(wire, JsonRpcRequest.class);
+```
+
+See [juneau-bean-mcp-2025-06-18](/docs/topics/JuneauBeanMcp) for the full
topic.
+
+### `juneau-rest-server-mcp-2025-06-18` (new module)
+
+A new adapter module, `juneau-rest-server-mcp-2025-06-18` (package
`org.apache.juneau.rest.server.mcp.v20250618`), binds the revision-neutral
`juneau-rest-server-mcp` core to the `2025-06-18` wire beans in
`juneau-bean-mcp-2025-06-18`.
+
+### New Classes
+
+- **`Mcp20250618Revision`** — the `McpRevision` implementation for protocol
revision `2025-06-18`. Replaces the old `McpDispatcher`/`Mcp` façade: owns the
JSON-RPC method table (`initialize`, `ping`, `tools/list|call`,
`prompts/list|get`, `resources/list|read`) and the error-code table. The
error-code table intentionally preserves a known-wrong mapping carried over
unmodified from the pre-re-layering dispatcher — unknown-method,
tool-not-found, prompt-not-found, and resource-not-found al [...]
+- **`McpRestServlet20250618`** — concrete abstract servlet for this revision;
subclass it (instead of the core `McpRestServlet`) and implement
`createMcpConfig()`. Exposes a `protected ServerCapabilities capabilities()`
hook (returns `null` by default) for explicitly overriding the `initialize`
capabilities advertisement instead of relying on auto-derivation from the
registered tool/prompt/resource lists.
+- **`McpEndpoint20250618`** — mixin interface for this revision, at parity
with `McpRestServlet20250618`: implement `getMcpConfig()`, and optionally
override its `default ServerCapabilities capabilities()` hook.
+- **`McpTypedToolHandler<A,R>`**, **`McpTypedPromptHandler<A>`**,
**`McpTypedHandlers`** — the typed sugar layer, moved here (package-renamed
only) since it's revision-specific by construction: it binds arguments into
this revision's wire-bean argument types and adapts the result to the neutral
`McpToolHandler`/`McpPromptHandler` raw interfaces the core registry holds.
+
+**New behavior:** on a given `McpServerConfig`'s first dispatch, this revision
validates that every registered tool's `McpSchema` uses only the 6 JSON Schema
keywords its wire `JsonSchema` bean can represent (`type`, `properties`,
`required`, `additionalProperties`, `items`, `$defs`) — throwing
`IllegalArgumentException` naming the offending tool and keyword if not, rather
than silently dropping an unsupported keyword on the wire.
+
+### Drop-in servlet
+
+```java
+@Rest(path="/mcp")
+public class MyMcpServlet extends McpRestServlet20250618 {
+ @Override
+ protected McpServerConfig createMcpConfig() {
+ return new
McpServerConfig().setName("my-server").setVersion("1.0.0").addTool(new
MyEchoTool());
+ }
+}
+```
+
+### Mixin on an existing resource
+
+```java
+@Rest(path="/api")
+public class MyResource extends BasicRestServlet implements
McpEndpoint20250618 {
+ @Override
+ public McpServerConfig getMcpConfig() {
+ return new McpServerConfig().addTool(new MyEchoTool());
+ }
+}
+```
+
+### Typed handlers
+
+```java
+public class WeatherArgs { String city; String unit; /* getters/setters */ }
+
+McpTypedToolHandler<WeatherArgs, String> typed = new McpTypedToolHandler<>() {
+ @Override public Tool descriptor() { return new Tool().setName("weather");
}
+ @Override public Class<WeatherArgs> argumentType() { return
WeatherArgs.class; }
+ @Override public String call(WeatherArgs a, BeanStore ctx) { return
"sunny"; }
+};
+
+config.addTool(McpTypedHandlers.adaptTool(typed));
+```
+
+A non-`CallToolResult` return value (like the `String` above) is
JSON-serialized and wrapped in a single-`TextContent` `CallToolResult`;
returning a `CallToolResult` directly passes it through unchanged.
+
+### Pagination
+
+```java
+config.setCursor(McpCursor.fixedSize(50));
+```
+
+`McpCursor` lives in the neutral core (above) and is unchanged: it receives
the full descriptor list and the inbound cursor token, and returns an
`McpPage<T>` containing the slice plus an opaque `nextCursor`. Custom
strategies plug in via the `McpCursor` functional interface.
+
+### Capability override
+
+```java
+@Rest(path="/mcp")
+public class MyMcpServlet extends McpRestServlet20250618 {
+ @Override
+ protected McpServerConfig createMcpConfig() {
+ return new McpServerConfig().addTool(new MyEchoTool());
+ }
+
+ @Override
+ protected ServerCapabilities capabilities() {
+ return new ServerCapabilities().setLogging(new LoggingCapability());
+ }
+}
+```
+
+Returning `null` (the default) leaves capabilities auto-derived from the
registered tool/prompt/resource lists. Returning a non-`null` value bypasses
auto-derivation entirely — the way to advertise `resources.subscribe`,
`logging`, `listChanged`, or `experimental`, none of which are derivable from a
handler registry. `McpEndpoint20250618` exposes the equivalent hook as a
`default` interface method for the mixin path.
+
+### Notifications
+
+Requests without an `id` are treated as JSON-RPC notifications: handlers run,
exceptions are swallowed, and `dispatch()` returns `null`. The REST
servlet/mixin writes an empty body so transports can map this to `204 No
Content`.
+
+See [juneau-rest-server-mcp-2025-06-18](/docs/topics/JuneauRestServerMcp) for
the full topic.
+
### Bug Fixes
- **Fixed RRPC method calls never dispatching over POST.** Every HTTP POST to
an `@RestOp(method="RRPC")` operation previously returned a 404 instead of
reaching the target method. `RrpcRestOpSession` derived the RRPC method key by
splitting the request path on the last `/`, but RRPC keys are of the form
`methodName/(paramTypes)` and themselves contain a `/`, so the method name was
stripped off and the lookup always fell through to `NotFound`. The key is now
derived from the already-comp [...]
@@ -800,7 +988,7 @@ constants (see Breaking Changes below). The
[Marshallers](/docs/topics/Marshalle
- **Typed-`View` template names in the FreeMarker / Mustache / Thymeleaf view
bridges are now path-traversal-gated (behavioral change).** The
`juneau-rest-server-view-freemarker`, `-mustache`, and `-thymeleaf` bridges
previously applied `FileUtils.resolveVirtualPathSafely(...)` only on the raw
`/{engine}/*` mount, while a typed `View` return value
(`FreemarkerView.of(name)` / `MustacheView.of(name)` /
`ThymeleafView.of(name)`) passed its template name straight to the engine. As
of 10.0.0 [...]
- **`juneau-bean` DTO collection/array getters no longer expose live internal
state (behavioral change).** Getters on the DTO beans that previously returned
the bean's live internal collection or array now return a defensive, immutable
view so that caller-side mutation can no longer corrupt bean state:
- - **Collection / `Map` / `Set` getters** in `juneau-bean-jsonapi`
(`JsonApiResource`, `JsonApiDocument`, `JsonApiResourceIdentifier`,
`JsonApiLink`, `JsonApiVersion`, `JsonApiRelationship`, `JsonApiError`),
`juneau-bean-jsonschema` (`JsonSchema`), `juneau-bean-mcp`
(`ListPromptsResult`, `ListToolsResult`, `ListResourcesResult`,
`ReadResourceResult`, `CallToolResult`, `GetPromptResult`, `Prompt`,
`CallToolRequest`, `GetPromptRequest`, `ServerCapabilities`,
`ClientCapabilities`, `JsonSch [...]
+ - **Collection / `Map` / `Set` getters** in `juneau-bean-jsonapi`
(`JsonApiResource`, `JsonApiDocument`, `JsonApiResourceIdentifier`,
`JsonApiLink`, `JsonApiVersion`, `JsonApiRelationship`, `JsonApiError`),
`juneau-bean-jsonschema` (`JsonSchema`), `juneau-bean-mcp-2025-06-18`
(`ListPromptsResult`, `ListToolsResult`, `ListResourcesResult`,
`ReadResourceResult`, `CallToolResult`, `GetPromptResult`, `Prompt`,
`CallToolRequest`, `GetPromptRequest`, `ServerCapabilities`,
`ClientCapabilities [...]
- **Array getters** in `juneau-bean-atom` (`CommonEntry.getAuthors()` /
`getCategories()` / `getContributors()` / `getLinks()`, `Feed.getEntries()`)
and the mutable-`Calendar` getter `CommonEntry.getUpdated()` now return a
**defensive copy** (array `clone()` / `Calendar` clone) rather than the live
field, and the corresponding setters store a defensive copy of their argument.
Mutating a returned array/`Calendar` (or an array previously handed to a
setter) no longer affects the bean. Ge [...]
- **Migration:** callers that mutated a value returned from one of these
getters must instead go through the bean's setter/adder API. No source changes
are required for read-only usage.
diff --git a/pages/release-notes/9.5.0.md b/pages/release-notes/9.5.0.md
index f6b1e57942..238c8835b1 100644
--- a/pages/release-notes/9.5.0.md
+++ b/pages/release-notes/9.5.0.md
@@ -6,7 +6,7 @@ title: "Release 9.5.0"
**Date:** TBD
-Juneau 9.5.0 is a minor release with native OpenAPI 3.1 emission (alongside
Swagger v2, composed via the new four-class `org.apache.juneau.rest.docs` mixin
pack — `SwaggerMixin` / `SwaggerUiMixin` / `OpenApiMixin` / `RedocMixin` — that
replaces the previously-considered `apiFormat` string knob), native TOML and
YAML support, BSON (Binary JSON) support for MongoDB-interoperable binary
serialization, CBOR (Concise Binary Object Representation) per RFC 8949 for IoT
and constrained environme [...]
+Juneau 9.5.0 is a minor release with native OpenAPI 3.1 emission (alongside
Swagger v2, composed via the new four-class `org.apache.juneau.rest.docs` mixin
pack — `SwaggerMixin` / `SwaggerUiMixin` / `OpenApiMixin` / `RedocMixin` — that
replaces the previously-considered `apiFormat` string knob), native TOML and
YAML support, BSON (Binary JSON) support for MongoDB-interoperable binary
serialization, CBOR (Concise Binary Object Representation) per RFC 8949 for IoT
and constrained environme [...]
### Security
@@ -4477,49 +4477,6 @@ RestClient client =
RestClient.builder().transport(transport).build();
// (no code change needed — ServiceLoader picks up the higher-priority
provider)
```
-### juneau-bean-mcp (new module)
-
-A new bean module, `juneau-bean-mcp`, models the [Model Context
Protocol](https://modelcontextprotocol.io/) wire format as Juneau `@Bean`
POJOs. The beans round-trip cleanly through any Juneau serializer/parser (JSON,
JSON5, MessagePack, CBOR, YAML, etc.) and were the foundation for
`juneau-rest-server-mcp`.
-
-### Coverage
-
-- **JSON-RPC envelopes** — `JsonRpcRequest`, `JsonRpcResponse`,
`JsonRpcError`, plus an `McpException` carrying JSON-RPC fields for
handler-side propagation.
-- **Initialization** — `InitializeRequest`, `InitializeResult`,
`Implementation`, `ClientCapabilities`, `ServerCapabilities`, and the
per-feature capability beans (`ToolCapability`, `PromptCapability`,
`ResourceCapability`, `LoggingCapability`, `RootsCapability`).
-- **Tools** — `Tool`, `CallToolRequest`, `CallToolResult`, `ListToolsResult`,
plus the `JsonSchema` argument-schema bean.
-- **Prompts** — `Prompt`, `PromptArgument`, `GetPromptRequest`,
`GetPromptResult`, `ListPromptsResult`, `PromptMessage`, `Role`.
-- **Resources** — `Resource`, `ReadResourceRequest`, `ReadResourceResult`,
`ListResourcesResult`, plus polymorphic `ResourceContents`
(`TextResourceContents`, `BlobResourceContents`).
-- **Polymorphic content blocks** — `Content` interface with `TextContent`,
`ImageContent`, `EmbeddedResourceContent` discriminated by a `type` property.
-- **Constants** — `McpMethods` (JSON-RPC method names) and `McpProtocol`
(version constants and `JSON_RPC_2_0` literal).
-
-### Polymorphic Serialization
-
-`Content` and `ResourceContents` use `@Bean(typePropertyName="type",
dictionary={...})` to discriminate at the wire level. Configure your serializer
with `addBeanTypes()` to emit the discriminator on output:
-
-```java
-JsonSerializer ser = JsonSerializer.create()
- .addBeanTypes()
- .typePropertyName(Content.class, "type")
- .typePropertyName(ResourceContents.class, "type")
- .build();
-```
-
-### Example
-
-```java
-import org.apache.juneau.bean.mcp.*;
-import org.apache.juneau.json.*;
-
-JsonRpcRequest req = new JsonRpcRequest()
- .setJsonrpc(McpProtocol.JSON_RPC_2_0)
- .setId(1)
- .setMethod(McpMethods.TOOLS_LIST);
-
-String wire = JsonSerializer.DEFAULT.serialize(req);
-JsonRpcRequest back = JsonParser.DEFAULT.parse(wire, JsonRpcRequest.class);
-```
-
-See [juneau-bean-mcp](/docs/topics/JuneauBeanMcp) for the full topic.
-
### juneau-rest-server-auth-jwt (new module)
### `jwksCacheTtl` migrated to `@Value` (TODO-92)
@@ -4731,76 +4688,6 @@ A new opt-in REST module,
`juneau-rest-server-auth-oidc-rp`, turns a Juneau REST
</dependency>
```
-### juneau-rest-server-mcp (new module)
-
-A new REST module, `juneau-rest-server-mcp`, exposes a stateless MCP JSON-RPC
endpoint built on `juneau-rest-server` and the `juneau-bean-mcp` wire beans.
The implementation is transport-agnostic at its core (a pure dispatcher seam)
with two REST adapters: a drop-in servlet, and an interface mixin that mounts
the endpoint on any existing `@Rest` resource.
-
-### New Classes
-
-- **`McpDispatcher`** - Transport-agnostic JSON-RPC dispatcher. Routes every
MCP method (`initialize`, `ping`, `tools/list|call`, `prompts/list|get`,
`resources/list|read`), maps `McpException` to JSON-RPC errors, and silently
suppresses responses for notifications (`id == null`).
-- **`McpServerConfig`** - Aggregate registry of tools, prompts, resources,
server identity, protocol version, instructions, capabilities, and pagination
strategy. Typically registered as a bean in your `RestContext` bean store.
-- **`McpRestServlet`** - Drop-in `BasicRestServlet` subclass exposing `POST /`
as the MCP endpoint. Subclasses implement `createMcpConfig()` and the servlet
handles dispatch + serialization (with `@SerializerConfig(addBeanTypes="true")`
so polymorphic content carries its discriminator).
-- **`McpEndpoint`** - Interface mixin with a default `@RestPost("/mcp")`
method, letting users add an MCP endpoint to any existing `@Rest` class without
subclassing `McpRestServlet`.
-- **`Mcp`** - Static façade exposing `Mcp.handle(req, config, beanStore)` for
embedders that want a one-line dispatch from inside their own REST methods.
-- **`McpToolHandler`**, **`McpPromptHandler`**, **`McpResourceHandler`** - Raw
`@FunctionalInterface` handlers that receive `Map<String, Object>` arguments
and a per-request `BasicBeanStore`.
-- **`McpTypedToolHandler<A,R>`**, **`McpTypedPromptHandler<A>`**,
**`McpTypedHandlers`** - Optional sugar layer for binding incoming arguments
into Juneau beans and wrapping non-`CallToolResult` returns as a
single-`TextContent` result.
-- **`McpCursor`**, **`McpPage<T>`** - Pagination strategy seam. Built-in
implementations: `McpCursor.SINGLE_PAGE` (default, returns everything in one
page) and `McpCursor.fixedSize(n)` (opaque integer-offset paging).
-
-### Drop-in servlet
-
-```java
-@Rest(path="/mcp")
-public class MyMcpServlet extends McpRestServlet {
- @Override
- protected McpServerConfig createMcpConfig() {
- return new McpServerConfig()
- .setServerInfo(new
Implementation().setName("my-server").setVersion("1.0.0"))
- .addTool(new MyEchoTool());
- }
-}
-```
-
-### Mixin on an existing resource
-
-```java
-@Rest(path="/api")
-public class MyResource extends BasicRestServlet implements McpEndpoint {
- @Override
- public McpServerConfig getMcpConfig() {
- return new McpServerConfig().addTool(new MyEchoTool());
- }
-}
-```
-
-### Typed handlers
-
-```java
-public class WeatherArgs { String city; String unit; /* getters/setters */ }
-public class WeatherResult { String summary; double temp; /* getters/setters
*/ }
-
-McpTypedToolHandler<WeatherArgs, WeatherResult> typed = new
McpTypedToolHandler<>() {
- @Override public Tool descriptor() { return new Tool().setName("weather");
}
- @Override public Class<WeatherArgs> argumentType() { return
WeatherArgs.class; }
- @Override public WeatherResult call(WeatherArgs a, BasicBeanStore ctx) {
/* ... */ }
-};
-
-config.addTool(McpTypedHandlers.adaptTool(typed));
-```
-
-### Pagination
-
-```java
-config.setCursor(McpCursor.fixedSize(50));
-```
-
-The cursor receives the full descriptor list and the inbound cursor token, and
returns an `McpPage<T>` containing the slice plus an opaque `nextCursor`.
Custom strategies plug in via the `McpCursor` functional interface.
-
-### Notifications
-
-Requests without an `id` are treated as JSON-RPC notifications: handlers run,
exceptions are swallowed, and the dispatcher returns `null`. The REST servlet
writes an empty body so transports can map this to `204 No Content`.
-
-See [juneau-rest-server-mcp](/docs/topics/JuneauRestServerMcp) for the full
topic.
-
### juneau-rest-server-metrics-micrometer (new module)
A new opt-in REST module, `juneau-rest-server-metrics-micrometer`, bridges the
new `MetricsRecorder` SPI (see [juneau-rest-server](#juneau-rest-server)) into
a Micrometer `MeterRegistry` so a Juneau REST service can drop into existing
Prometheus / StatsD / JMX scrape pipelines with no hand-rolled instrumentation.
Engine-agnostic POM stance (TODO-67 resolved decision #1, mirroring TODO-68 /
TODO-78 / TODO-82 / TODO-83 / TODO-84): `io.micrometer:micrometer-core` is
declared in `provided` s [...]
diff --git a/pages/topics/01.00.JuneauEcosystemOverview.md
b/pages/topics/01.00.JuneauEcosystemOverview.md
index 00ba837c78..7a7048f96f 100644
--- a/pages/topics/01.00.JuneauEcosystemOverview.md
+++ b/pages/topics/01.00.JuneauEcosystemOverview.md
@@ -54,7 +54,7 @@ The Juneau ecosystem consists of the following parts:
| [juneau‑bean‑jsonapi](/docs/topics/JuneauBeanJsonApi) | DTO beans for the
JSON:API format | • *None* |
| [juneau‑bean‑jsonpatch](/docs/topics/JuneauBeanJsonPatch) | DTO beans for
JSON Patch (RFC 6902) | • *None* |
| [juneau‑bean‑jsonschema](/docs/topics/JuneauBeanJsonSchema) | DTO beans for
JSON Schema | • *None* |
-| [juneau‑bean‑mcp](/docs/topics/JuneauBeanMcp) | DTO beans for the Model
Context Protocol (MCP) | • *None* |
+| [juneau‑bean‑mcp‑2025‑06‑18](/docs/topics/JuneauBeanMcp) | DTO beans for the
Model Context Protocol (MCP) revision `2025-06-18` | • *None* |
| [juneau‑bean‑openapi‑v3](/docs/topics/JuneauBeanOpenApi3) | DTO beans for
OpenAPI v3 | • *None* |
| [juneau‑bean‑rfc7807](/docs/topics/JuneauBeanRfc7807) | DTO beans for RFC
7807 Problem Details | • *None* |
| [juneau‑bean‑swagger‑v2](/docs/topics/JuneauBeanSwagger2) | DTO beans for
Swagger / OpenAPI v2 | • *None* |
diff --git a/pages/topics/01.02.WhyJuneau.md b/pages/topics/01.02.WhyJuneau.md
index 51a6ea5775..2d3587245e 100644
--- a/pages/topics/01.02.WhyJuneau.md
+++ b/pages/topics/01.02.WhyJuneau.md
@@ -216,7 +216,7 @@ try (TokenReader r = Json.DEFAULT.readTokens(inputStream)) {
- **Automatic Documentation:** Swagger UI generated automatically from your
code
- **Content Negotiation:** Support multiple formats with zero additional
configuration
- **Type Safety:** Compile-time checking for REST client interfaces
-- **MCP Support:** `juneau-rest-server-mcp` exposes any `@Rest` resource as a
Model Context Protocol (MCP) endpoint, enabling LLM tool-calling with no
additional framework
+- **MCP Support:** `juneau-rest-server-mcp` (revision-neutral core) plus the
`juneau-rest-server-mcp-2025-06-18` adapter expose any `@Rest` resource as a
Model Context Protocol (MCP) endpoint, enabling LLM tool-calling with no
additional framework
## When to Choose Juneau
diff --git a/pages/topics/05.00.JuneauBean.md b/pages/topics/05.00.JuneauBean.md
index 0acdfa00ce..f1da8ffeb9 100644
--- a/pages/topics/05.00.JuneauBean.md
+++ b/pages/topics/05.00.JuneauBean.md
@@ -26,7 +26,8 @@ The `juneau-bean` group is split into one module per document
type. Every module
| [juneau-bean-openapi-v3](/docs/topics/JuneauBeanOpenApi3) | OpenAPI 3.0
document and UI beans. |
| [juneau-bean-common](/docs/topics/JuneauBeanCommon) | Shared general-purpose
DTOs (`LinkString`, `ResultSetList`). |
| [juneau-bean-swagger-v2](/docs/topics/JuneauBeanSwagger2) | Swagger 2.0
document and UI beans. |
-| [juneau-bean-mcp](/docs/topics/JuneauBeanMcp) | Model Context Protocol (MCP)
JSON-RPC wire beans. |
+| [juneau-bean-mcp-2025-06-18](/docs/topics/JuneauBeanMcp) | Model Context
Protocol (MCP) `2025-06-18` wire beans (JSON-RPC envelope lives in
`juneau-bean-jsonrpc`). |
+| [juneau-bean-jsonrpc](/docs/topics/JuneauBeanJsonRpc) | Revision-neutral
JSON-RPC 2.0 envelope beans (`JsonRpcRequest`, `JsonRpcResponse`,
`JsonRpcError`, `McpException`). |
| [juneau-bean-rfc7807](/docs/topics/JuneauBeanRfc7807) | RFC 7807 Problem
Details (`application/problem+json`) beans. |
| [juneau-bean-hal](/docs/topics/JuneauBeanHal) | HAL hypermedia
(`application/hal+json`) beans. |
| [juneau-bean-jsonapi](/docs/topics/JuneauBeanJsonApi) | JSON:API v1.1
(`application/vnd.api+json`) beans. |
diff --git a/pages/topics/05.07.JuneauBeanMcp.md
b/pages/topics/05.07.JuneauBeanMcp.md
index 4ecfa2323d..0c3b1917df 100644
--- a/pages/topics/05.07.JuneauBeanMcp.md
+++ b/pages/topics/05.07.JuneauBeanMcp.md
@@ -1,35 +1,33 @@
---
-title: "juneau-bean-mcp"
+title: "juneau-bean-mcp-2025-06-18"
slug: JuneauBeanMcp
---
-The `juneau-bean-mcp` module provides Java beans modelling the [Model Context
Protocol (MCP)](https://modelcontextprotocol.io/) wire format.
+The `juneau-bean-mcp-2025-06-18` module provides Java beans modelling the
[Model Context Protocol (MCP)](https://modelcontextprotocol.io/) `2025-06-18`
wire format. It's paired with a separate, revision-neutral module,
`juneau-bean-jsonrpc`, which carries the underlying JSON-RPC 2.0 envelope.
## Overview
-MCP is a JSON-RPC 2.0 protocol used by AI assistants and agents to interact
with external tools, prompts, and resources. This module provides a complete
set of Juneau `@Marshalled`-annotated POJOs covering the MCP HTTP wire surface,
so you can build MCP servers and clients using any Juneau serializer/parser
(JSON, JSON5, MessagePack, CBOR, YAML, and more).
+MCP is a JSON-RPC 2.0 protocol used by AI assistants and agents to interact
with external tools, prompts, and resources. Two bean modules cover the wire
format:
-The module ships only the wire types — no transport, no dispatch logic, no
HTTP plumbing. For a stateless JSON-RPC endpoint built on `juneau-rest-server`
and these beans, see [juneau-rest-server-mcp](/docs/topics/JuneauRestServerMcp).
+- **[juneau-bean-jsonrpc](/docs/topics/JuneauBeanJsonRpc)** (package
`org.apache.juneau.bean.jsonrpc`) — the revision-neutral JSON-RPC 2.0 envelope
(`JsonRpcRequest`, `JsonRpcResponse`, `JsonRpcError`, `McpException`). It
carries no MCP-specific knowledge and depends only on `juneau-marshall`. See
[juneau-bean-jsonrpc](/docs/topics/JuneauBeanJsonRpc) for full coverage.
+- **`juneau-bean-mcp-2025-06-18`** (package
`org.apache.juneau.bean.mcp.v20250618`) — the MCP `2025-06-18` wire beans
covered by this page: tool/prompt/resource descriptors, capability beans, and
the polymorphic content types. Renamed from the earlier `juneau-bean-mcp` /
`org.apache.juneau.bean.mcp` as part of a re-layering that split the JSON-RPC
envelope out into its own module — the name change leaves room for a future
sibling module under the same `org.apache.juneau.bean.mcp` namespa [...]
+
+Both modules ship Juneau `@Marshalled`-annotated POJOs, so you can build MCP
servers and clients using any Juneau serializer/parser (JSON, JSON5,
MessagePack, CBOR, YAML, and more).
+
+Neither module ships transport or dispatch logic — no HTTP plumbing. For a
stateless JSON-RPC endpoint built on `juneau-rest-server` and these beans, see
[juneau-rest-server-mcp](/docs/topics/JuneauRestServerMcp), which is itself
split into a revision-neutral core plus a `2025-06-18` adapter.
### Key Features
- **Round-trip serialization** — Every bean parses back from its serialized
form into an identical object across JSON, JSON5, MessagePack, CBOR, YAML, and
other Juneau formats.
- **Polymorphic content blocks** — `Content` and `ResourceContents` use
`@Marshalled(typePropertyName="type", dictionary={...})` to discriminate
subtype on the wire.
- **Fluent setters** — Every bean uses `setX(...)` returning `this` for
ergonomic construction.
-- **No external dependencies** — Only depends on `juneau-marshall`.
+- **No external dependencies** — Both modules depend only on `juneau-marshall`
(`juneau-bean-mcp-2025-06-18` additionally has a *test-scope* dependency on
`juneau-bean-jsonrpc`, to exercise the envelope types its own round-trip tests
still reference).
- **Zero protocol logic** — Pure DTOs; no validation, no state machines, no
dispatch.
## Coverage
-### JSON-RPC envelopes
-
-| Bean | Purpose |
-|---|---|
-| `JsonRpcRequest` | Inbound JSON-RPC request envelope (`jsonrpc`, `id`,
`method`, `params`). |
-| `JsonRpcResponse` | Outbound JSON-RPC response envelope (`jsonrpc`, `id`,
`result`, `error`). |
-| `JsonRpcError` | JSON-RPC error structure (`code`, `message`, `data`). |
-| `McpException` | `RuntimeException` carrying JSON-RPC error fields for
handler-side propagation. |
+The JSON-RPC 2.0 envelope beans (`JsonRpcRequest`, `JsonRpcResponse`,
`JsonRpcError`, `McpException`) that carry MCP traffic on the wire live in the
separate [juneau-bean-jsonrpc](/docs/topics/JuneauBeanJsonRpc) module — see
that page for full coverage of the envelope, including the `JsonRpcResponse`
static factories and error-mapping helpers.
### Initialization & capabilities
@@ -48,7 +46,7 @@ The module ships only the wire types — no transport, no
dispatch logic, no HTT
| `Tool` | Tool descriptor (`name`, `description`, `inputSchema`). |
| `ListToolsResult` | Result of `tools/list`. |
| `CallToolRequest` / `CallToolResult` | `tools/call` request/response. |
-| `JsonSchema` | Lightweight JSON Schema bean for tool argument schemas. |
+| `JsonSchema` | Lightweight JSON Schema bean for tool argument schemas (six
keywords: `type`, `properties`, `required`, `additionalProperties`, `items`,
`$defs`). |
### Prompts
@@ -59,13 +57,13 @@ The module ships only the wire types — no transport, no
dispatch logic, no HTT
| `ListPromptsResult` | Result of `prompts/list`. |
| `GetPromptRequest` / `GetPromptResult` | `prompts/get` request/response. |
| `PromptMessage` | A rendered prompt message. |
-| `Role` | Enum of `user`, `assistant`. |
+| `Role` | Enum of `user`, `assistant`, `system`, `tool`. |
### Resources
| Bean | Purpose |
|---|---|
-| `Resource` | Resource descriptor (`uri`, `name`, `description`, `mimeType`).
|
+| `Resource` | Resource descriptor (`uri`, `name`, `title`, `description`,
`mimeType`, `size`). |
| `ListResourcesResult` | Result of `resources/list`. |
| `ReadResourceRequest` / `ReadResourceResult` | `resources/read`
request/response. |
| `ResourceContents` | Polymorphic interface for the body of a resource. |
@@ -92,7 +90,7 @@ The module ships only the wire types — no transport, no
dispatch logic, no HTT
`Content` and `ResourceContents` declare `@Marshalled(typePropertyName="type",
dictionary={...})`, so the parser automatically discovers the right subtype
when reading. For *serialization*, enable `addBeanTypes` on the serializer so
the `type` discriminator is actually written out:
```java
-import org.apache.juneau.bean.mcp.*;
+import org.apache.juneau.bean.mcp.v20250618.*;
import org.apache.juneau.marshall.json.*;
JsonSerializer ser = JsonSerializer.create().addBeanTypes().build();
@@ -107,14 +105,18 @@ String wire = ser.write(result);
CallToolResult back = JsonParser.DEFAULT.read(wire, CallToolResult.class);
```
-When MCP responses are sent through `juneau-rest-server-mcp`, the servlet sets
`addBeanTypes="true"` for you via `@SerializerConfig`.
+When MCP responses are sent through
[juneau-rest-server-mcp](/docs/topics/JuneauRestServerMcp), the servlet sets
`addBeanTypes="true"` for you via `@SerializerConfig`.
## Basic Usage
### Building a JSON-RPC request
+`JsonRpcRequest` lives in the neutral
[juneau-bean-jsonrpc](/docs/topics/JuneauBeanJsonRpc) module;
`McpMethods`/`McpProtocol` stay in this module, since they're MCP-specific:
+
```java
-import org.apache.juneau.bean.mcp.*;
+import org.apache.juneau.bean.jsonrpc.*;
+import org.apache.juneau.bean.mcp.v20250618.*;
+import org.apache.juneau.marshall.collections.*;
import org.apache.juneau.marshall.json.*;
JsonRpcRequest req = new JsonRpcRequest()
@@ -183,18 +185,7 @@ String wire = ser.write(readResult);
### Mapping handler errors to JSON-RPC errors
-`McpException` is a `RuntimeException` carrying JSON-RPC `code`, `message`,
and optional `data`. Handlers can throw it directly; calling code can convert
via `toJsonRpcError()`:
-
-```java
-try {
- throw new McpException(-32602, "Invalid arguments", JsonMap.of("field",
"name"));
-} catch (McpException e) {
- JsonRpcResponse resp = new JsonRpcResponse()
- .setJsonrpc(McpProtocol.JSON_RPC_2_0)
- .setId(1)
- .setError(e.toJsonRpcError());
-}
-```
+`McpException` and the `JsonRpcResponse` error factories live in
[juneau-bean-jsonrpc](/docs/topics/JuneauBeanJsonRpc#mapping-handler-errors-to-json-rpc-errors)
— see that page for how to convert a thrown `McpException` into a
`JsonRpcError`/`JsonRpcResponse`.
## Multiple Wire Formats
@@ -202,8 +193,8 @@ Because the beans are pure POJOs with `@Marshalled`
annotations, every Juneau se
```java
import org.apache.juneau.marshall.cbor.*;
+import org.apache.juneau.marshall.marshaller.*;
import org.apache.juneau.marshall.msgpack.*;
-import org.apache.juneau.marshall.yaml.*;
CallToolResult r = new CallToolResult().setContent(List.of(new
TextContent().setText("hi")));
@@ -216,7 +207,8 @@ Set `addBeanTypes` and the discriminator property names on
the corresponding bui
## Related Modules
-- **[juneau-rest-server-mcp](/docs/topics/JuneauRestServerMcp)** — Builds an
MCP JSON-RPC HTTP endpoint on top of these beans plus `juneau-rest-server`.
+- **[juneau-bean-jsonrpc](/docs/topics/JuneauBeanJsonRpc)** — The
revision-neutral JSON-RPC 2.0 envelope beans that carry MCP traffic on the wire.
+- **[juneau-rest-server-mcp](/docs/topics/JuneauRestServerMcp)** — The
revision-neutral MCP REST-server core plus the `2025-06-18` adapter that builds
an MCP JSON-RPC HTTP endpoint on top of these beans.
## Resources
diff --git a/pages/topics/05.12.JuneauBeanJsonRpc.md
b/pages/topics/05.12.JuneauBeanJsonRpc.md
new file mode 100644
index 0000000000..a6af5ce8e7
--- /dev/null
+++ b/pages/topics/05.12.JuneauBeanJsonRpc.md
@@ -0,0 +1,131 @@
+---
+title: "juneau-bean-jsonrpc"
+slug: JuneauBeanJsonRpc
+---
+
+
+The `juneau-bean-jsonrpc` module provides Java beans modelling the
revision-neutral [JSON-RPC 2.0](https://www.jsonrpc.org/specification)
envelope: request/response/error framing plus a runtime exception for
handler-side error propagation. It carries no protocol-specific knowledge and
depends only on `juneau-marshall`, making it the shared framing layer beneath
protocol-specific bean modules such as
[juneau-bean-mcp-2025-06-18](/docs/topics/JuneauBeanMcp).
+
+## Overview
+
+JSON-RPC 2.0 is a stateless, lightweight remote procedure call protocol
encoded as JSON. This module ships the envelope shapes needed to read and write
JSON-RPC requests and responses, without any knowledge of what methods exist or
what their parameters mean — that's left to higher-level modules like
[juneau-bean-mcp-2025-06-18](/docs/topics/JuneauBeanMcp).
+
+The beans are Juneau `@Marshalled`-annotated POJOs, so they round-trip through
any Juneau serializer/parser (JSON, JSON5, MessagePack, CBOR, YAML, and more).
+
+### Key Features
+
+- **Round-trip serialization** — Every bean parses back from its
serialized form into an identical object across JSON, JSON5, MessagePack, CBOR,
YAML, and other Juneau formats.
+- **Static factories** — `JsonRpcResponse.ok(id, result)` and
`JsonRpcResponse.errorResponse(id, code, message[, data])` build
correctly-shaped responses (including the `jsonrpc` version token) in one call.
+- **Fluent setters** — Every bean uses `setX(...)` returning `this` for
ergonomic construction.
+- **No external dependencies** — Depends only on `juneau-marshall`.
+- **Zero protocol logic** — Pure DTOs; no validation, no method
dispatch, no transport.
+
+## Coverage
+
+| Bean | Purpose |
+|---|---|
+| `JsonRpcRequest` | Inbound JSON-RPC request envelope (`jsonrpc`, `id`,
`method`, `params`). |
+| `JsonRpcResponse` | Outbound JSON-RPC response envelope (`jsonrpc`, `id`,
`result`, `error`). Also carries three `public static` helpers used by dispatch
code: `ok(id, result)` and `errorResponse(id, code, message[, data])` (response
factories, which set the `jsonrpc` version token for you), plus
`notification(id)` (a `boolean` predicate testing `id == null`). |
+| `JsonRpcError` | JSON-RPC error structure (`code`, `message`, `data`). |
+| `McpException` | `RuntimeException` carrying JSON-RPC error fields (`code`,
`data`) for handler-side propagation; `toJsonRpcError()` converts it to a
`JsonRpcError`. |
+
+This module carries no MCP-specific knowledge — it's the shared framing
layer beneath protocol-specific bean modules such as
[juneau-bean-mcp-2025-06-18](/docs/topics/JuneauBeanMcp).
+
+## Basic Usage
+
+### Building a JSON-RPC request
+
+```java
+import org.apache.juneau.bean.jsonrpc.*;
+import org.apache.juneau.marshall.collections.*;
+import org.apache.juneau.marshall.json.*;
+
+JsonRpcRequest req = new JsonRpcRequest()
+ .setJsonrpc("2.0")
+ .setId(1)
+ .setMethod("tools/call")
+ .setParams(JsonMap.of(
+ "name", "echo",
+ "arguments", JsonMap.of("text", "hello")
+ ));
+
+String wire = JsonSerializer.DEFAULT.write(req);
+JsonRpcRequest back = JsonParser.DEFAULT.read(wire, JsonRpcRequest.class);
+```
+
+### Building success and error responses
+
+`JsonRpcResponse.ok(...)` and `JsonRpcResponse.errorResponse(...)` set the
`jsonrpc` version token automatically:
+
+```java
+import org.apache.juneau.bean.jsonrpc.*;
+import org.apache.juneau.marshall.collections.*;
+import org.apache.juneau.marshall.json.*;
+
+JsonRpcResponse success = JsonRpcResponse.ok(1, JsonMap.of("echoed", "hello"));
+String wire = JsonSerializer.DEFAULT.write(success);
+// {"jsonrpc":"2.0","id":1,"result":{"echoed":"hello"}}
+
+JsonRpcResponse failure = JsonRpcResponse.errorResponse(1, -32602, "Invalid
arguments", JsonMap.of("field", "name"));
+// {"jsonrpc":"2.0","id":1,"error":{"code":-32602,"message":"Invalid
arguments","data":{"field":"name"}}}
+```
+
+### Detecting notifications
+
+A JSON-RPC request with no `id` is a *notification* — the server
performs the work and returns no response body.
`JsonRpcResponse.notification(id)` tests for this:
+
+```java
+if (JsonRpcResponse.notification(req.getId())) {
+ // no response should be sent
+}
+```
+
+### Mapping handler errors to JSON-RPC errors
+
+`McpException` is a `RuntimeException` carrying JSON-RPC `code`, `message`,
and optional `data`. Handlers can throw it directly; calling code can convert
via `toJsonRpcError()`:
+
+```java
+import org.apache.juneau.bean.jsonrpc.*;
+import org.apache.juneau.marshall.collections.*;
+
+try {
+ throw new McpException(-32602, "Invalid arguments", JsonMap.of("field",
"name"));
+} catch (McpException e) {
+ JsonRpcResponse resp = new JsonRpcResponse()
+ .setJsonrpc("2.0")
+ .setId(1)
+ .setError(e.toJsonRpcError());
+}
+```
+
+Or, more directly, via the static factory on `JsonRpcResponse` itself:
+
+```java
+JsonRpcResponse resp = JsonRpcResponse.errorResponse(1, -32602, "Invalid
arguments", JsonMap.of("field", "name"));
+```
+
+## Multiple Wire Formats
+
+Because the beans are pure POJOs with `@Marshalled` annotations, every Juneau
serializer/parser works:
+
+```java
+import org.apache.juneau.bean.jsonrpc.*;
+import org.apache.juneau.marshall.cbor.*;
+import org.apache.juneau.marshall.marshaller.*;
+import org.apache.juneau.marshall.msgpack.*;
+
+JsonRpcResponse resp = JsonRpcResponse.ok(1, "pong");
+
+byte[] cbor = Cbor.of(resp);
+byte[] msgpack = MsgPackSerializer.DEFAULT.write(resp);
+String yaml = Yaml.of(resp);
+```
+
+## Related Modules
+
+- **[juneau-bean-mcp-2025-06-18](/docs/topics/JuneauBeanMcp)** — The
Model Context Protocol `2025-06-18` wire beans, which build their JSON-RPC
traffic on top of this envelope.
+- **[juneau-rest-server-mcp](/docs/topics/JuneauRestServerMcp)** — A
stateless JSON-RPC REST-server endpoint built on `juneau-rest-server` and these
beans.
+
+## Resources
+
+- [JSON-RPC 2.0 Specification](https://www.jsonrpc.org/specification)
diff --git a/pages/topics/11.JuneauRestServerMcp.md
b/pages/topics/11.JuneauRestServerMcp.md
index 1633a5aece..7d10893a8a 100644
--- a/pages/topics/11.JuneauRestServerMcp.md
+++ b/pages/topics/11.JuneauRestServerMcp.md
@@ -5,68 +5,72 @@ slug: JuneauRestServerMcp
---
-The `juneau-rest-server-mcp` module exposes a stateless [Model Context
Protocol (MCP)](https://modelcontextprotocol.io/) JSON-RPC endpoint built on
`juneau-rest-server` and the [`juneau-bean-mcp`](/docs/topics/JuneauBeanMcp)
wire beans.
+`juneau-rest-server-mcp` is a **revision-neutral core** for exposing a [Model
Context Protocol (MCP)](https://modelcontextprotocol.io/) JSON-RPC endpoint on
`juneau-rest-server`. On its own it has zero compile-time knowledge of any MCP
protocol revision; a protocol revision is supplied by a separate adapter
module. Today that's **`juneau-rest-server-mcp-2025-06-18`**, which binds the
core to the `2025-06-18` wire beans in
[`juneau-bean-mcp-2025-06-18`](/docs/topics/JuneauBeanMcp). This p [...]
## Overview
-MCP is a JSON-RPC 2.0 protocol that lets AI assistants discover and invoke
external **tools**, **prompts**, and **resources**. This module provides:
+MCP is a JSON-RPC 2.0 protocol that lets AI assistants discover and invoke
external **tools**, **prompts**, and **resources**. Together, the two modules
provide:
-- A transport-agnostic **dispatcher** that routes incoming JSON-RPC methods.
-- A drop-in **servlet** (`McpRestServlet`) for the common case of "expose an
MCP endpoint at `POST /mcp`".
-- An **interface mixin** (`McpEndpoint`) for embedding an MCP endpoint inside
an existing `@Rest` resource.
-- Functional **handler interfaces** for tools/prompts/resources, plus a typed
sugar layer that does argument binding and result wrapping for you.
-- A pluggable **pagination** seam for `*\/list` methods.
+- A **revision SPI** (`McpRevision`) that a protocol revision implements — the
neutral core dispatches through it without knowing what's on the other side.
+- A drop-in **servlet** (`McpRestServlet`, concretely
`McpRestServlet20250618`) for the common case of "expose an MCP endpoint at
`POST /mcp`".
+- An **interface mixin** (`McpEndpoint`, concretely `McpEndpoint20250618`) for
embedding an MCP endpoint inside an existing `@Rest` resource.
+- Functional **handler interfaces** for tools/prompts/resources, typed against
a revision-neutral model, plus a typed sugar layer (in the adapter) that does
argument binding and result wrapping for you.
+- A pluggable **pagination** seam for `*/list` methods.
-The implementation is stateless — every request is dispatched against the
`McpServerConfig` supplied by the REST adapter (via `createMcpConfig()`, an
`McpEndpoint` implementation, or an `McpServerConfig` bean in the REST
`BeanStore`), and per-request state (such as the underlying `RestRequest`) is
handed to handlers as a `BeanStore`. The adapters populate a `BasicBeanStore`
with the `RestRequest` for that purpose.
+The split exists so that a future MCP protocol revision can be added as a
second adapter module without touching the JSON-RPC envelope or this neutral
core — enforced by a `maven-enforcer-plugin` banned-dependency rule on the core
module's `pom.xml` that fails the build if `juneau-rest-server-mcp` ever
depends on `juneau-bean-mcp-*` or `juneau-rest-server-mcp-*`. There's
deliberately no `ServiceLoader` auto-discovery (unlike the transport-provider
pattern in `juneau-rest-client-apache-ht [...]
+
+The implementation is stateless — every request is dispatched against the
`McpServerConfig` supplied by the REST adapter (via `createMcpConfig()` or an
`McpEndpoint` implementation), and per-request state (such as the underlying
`RestRequest`) is handed to handlers as a `BeanStore`.
### Architecture
```
-+------------------------+ +------------------+
+-----------------+
-| McpRestServlet -or- | ---> | McpDispatcher | ---> | Tool/Prompt/
|
-| McpEndpoint mixin | | (pure JSON-RPC) | | Resource
|
-| (POST handler) | | | | Handlers
|
-+------------------------+ +------------------+
+-----------------+
- | |
- v v
- juneau-rest-server juneau-bean-mcp
- (@Rest, @RestPost) (wire beans + JSON-RPC envelopes)
++----------------------------+ +-------------------------+
+-----------------+
+| McpRestServlet20250618 | | Mcp20250618Revision | |
Tool/Prompt/ |
+| -or- McpEndpoint20250618 | ---> | (this revision's | ---> |
Resource |
+| (POST handler, adapter) | | JSON-RPC method | |
Handlers |
++----------------------------+ | table + error codes) | |
(neutral) |
+ | +-------------------------+
+-----------------+
+ v |
+ juneau-rest-server-mcp v
+ (core: McpServerConfig, juneau-bean-mcp-2025-06-18
+ McpRevision SPI, neutral model) (wire beans, via Mcp20250618Wire)
```
-`McpDispatcher` is the single seam containing all protocol logic. The two REST
adapters (`McpRestServlet` and `McpEndpoint`) only translate from `RestRequest`
to a `BasicBeanStore` and call `Mcp.handle(...)`.
+`Mcp20250618Revision` implements `McpRevision` and owns this revision's
JSON-RPC method table and error-code table — it replaces the earlier,
now-deleted `McpDispatcher`/`Mcp` façade. The adapter servlet/mixin only
translates from `RestRequest` to an `McpExchange` + `BeanStore`, then calls
`revision().dispatch(...)`.
## Getting Started
### Add the dependency
+Application code normally depends on the `2025-06-18` adapter, which
transitively pulls in the core plus the `2025-06-18` wire beans:
+
```xml
<dependency>
<groupId>org.apache.juneau</groupId>
- <artifactId>juneau-rest-server-mcp</artifactId>
+ <artifactId>juneau-rest-server-mcp-2025-06-18</artifactId>
<version>${juneau.version}</version>
</dependency>
```
-This module transitively pulls in `juneau-rest-server` and `juneau-bean-mcp`.
+This module transitively pulls in `juneau-rest-server-mcp` (the core) and
`juneau-bean-mcp-2025-06-18`.
### Drop-in servlet
-Subclass `McpRestServlet` and supply your config in `createMcpConfig()`. The
base class wires up `@Rest`, `@SerializerConfig(addBeanTypes="true")`, and a
`POST /` handler:
+Subclass `McpRestServlet20250618` (not the abstract core `McpRestServlet`
directly) and supply your config in `createMcpConfig()`. The base class wires
up `@Rest`, `@SerializerConfig(addBeanTypes="true")`, and a `POST /` handler:
```java
-import org.apache.juneau.bean.mcp.*;
import org.apache.juneau.rest.server.*;
import org.apache.juneau.rest.server.mcp.*;
+import org.apache.juneau.rest.server.mcp.v20250618.*;
@Rest(path="/mcp")
-public class MyMcpServlet extends McpRestServlet {
+public class MyMcpServlet extends McpRestServlet20250618 {
@Override
protected McpServerConfig createMcpConfig() {
return new McpServerConfig()
- .setServerInfo(new Implementation()
- .setName("my-server")
- .setVersion("1.0.0"))
+ .setName("my-server")
+ .setVersion("1.0.0")
.addTool(new EchoTool());
}
}
@@ -76,11 +80,11 @@ Mount the servlet through your microservice / Spring Boot
config like any other
### Interface mixin
-If you already have a `@Rest` resource and want to expose MCP at `POST /mcp`
next to your other endpoints, implement `McpEndpoint`:
+If you already have a `@Rest` resource and want to expose MCP at `POST /mcp`
next to your other endpoints, implement `McpEndpoint20250618`:
```java
@Rest(path="/api")
-public class MyResource extends BasicRestServlet implements McpEndpoint {
+public class MyResource extends BasicRestServlet implements
McpEndpoint20250618 {
@Override
public McpServerConfig getMcpConfig() {
@@ -89,55 +93,63 @@ public class MyResource extends BasicRestServlet implements
McpEndpoint {
}
```
-The default `handleMcpRequest(...)` method on `McpEndpoint` (annotated
`@RestPost("/mcp")`) takes care of dispatch.
-
-### Static façade
-
-For full custom routing, call `Mcp.handle(...)` from your own `@RestPost`
method:
-
-```java
-@RestPost(path="/custom-mcp")
-public JsonRpcResponse mcp(@Content JsonRpcRequest req, RestRequest restReq) {
- var bs = new BasicBeanStore(restReq.getContext().getBeanStore())
- .addBean(RestRequest.class, restReq);
- return Mcp.handle(req, getMcpConfig(), bs);
-}
-```
+The default `handleMcpRequest(...)` method on `McpEndpoint` (annotated
`@RestPost("/mcp")`) takes care of dispatch, via `McpEndpoint20250618`'s
`revision()` override.
## Writing Handlers
### Raw tool handler
-`McpToolHandler` is the lowest-level handler interface. It receives a parsed
`Map<String, Object>` of arguments and a per-request `BeanStore`:
+`McpToolHandler` is the lowest-level handler interface, and is
revision-neutral — it's typed against `McpToolSpec`/`McpToolOutcome`, not any
revision's wire beans. It receives a parsed `Map<String, Object>` of arguments
and a per-request `BeanStore`:
```java
-import org.apache.juneau.bean.mcp.*;
+import java.util.*;
+
import org.apache.juneau.commons.inject.*;
import org.apache.juneau.rest.server.mcp.*;
public class EchoTool implements McpToolHandler {
@Override
- public Tool descriptor() {
- return new Tool()
+ public McpToolSpec descriptor() {
+ return new McpToolSpec()
.setName("echo")
.setDescription("Echoes the supplied text back.");
}
@Override
- public CallToolResult call(Map<String, Object> arguments, BeanStore ctx) {
+ public McpToolOutcome call(Map<String, Object> arguments, BeanStore ctx) {
String text = (String) arguments.getOrDefault("text", "");
- return new CallToolResult().setContent(List.of(
- new TextContent().setText(text)
- ));
+ return McpToolOutcome.text(text);
}
}
```
+A tool's `inputSchema` is an `McpSchema` — an unconstrained JSON-object
carrier, built from a `JsonMap`:
+
+```java
+import org.apache.juneau.marshall.collections.*;
+import org.apache.juneau.rest.server.mcp.*;
+
+McpToolSpec echo = new McpToolSpec()
+ .setName("echo")
+ .setDescription("Echoes the input text back.")
+ .setInputSchema(McpSchema.of(JsonMap.of(
+ "type", "object",
+ "properties", JsonMap.of("text", JsonMap.of("type", "string")),
+ "required", List.of("text")
+ )));
+```
+
+The `2025-06-18` adapter's wire `JsonSchema` bean only supports six keywords
(`type`, `properties`, `required`, `additionalProperties`, `items`, `$defs`) —
see "Tool schema validation" below.
+
### Typed tool handler (sugar)
-When you'd rather receive a Juneau bean for arguments and let the framework
wrap your result, use `McpTypedToolHandler` and adapt it via
`McpTypedHandlers.adaptTool(...)`:
+When you'd rather receive a Juneau bean for arguments and let the framework
wrap your result, use `McpTypedToolHandler` and adapt it via
`McpTypedHandlers.adaptTool(...)`. These two types are revision-specific (they
bind to this revision's wire-bean argument/return types), so they live in the
`v20250618` adapter package, not the core:
```java
+import org.apache.juneau.bean.mcp.v20250618.*;
+import org.apache.juneau.commons.inject.*;
+import org.apache.juneau.rest.server.mcp.v20250618.*;
+
public class WeatherArgs {
public String city;
public String unit = "C";
@@ -175,26 +187,30 @@ The adapter:
2. Invokes your typed `call(...)`.
3. If the return value is a `CallToolResult`, passes it through unchanged.
4. Otherwise, JSON-serializes the return and wraps it in a
single-`TextContent` `CallToolResult`.
+5. Converts the whole thing (descriptor and outcome) to the neutral
`McpToolSpec`/`McpToolOutcome` types the core registry holds — this
wire→neutral mapping is the mirror image of the neutral→wire mapping the
revision performs when handling a raw `McpToolHandler`.
-If binding fails, the dispatcher emits a JSON-RPC `-32602 Invalid params`
error.
+If binding fails, the adapter emits a JSON-RPC `-32602 Invalid params` error.
### Prompts and resources
-`McpPromptHandler`, `McpResourceHandler`, and `McpTypedPromptHandler<A>`
follow the same shape. Register them via `addPrompt(...)` and
`addResource(...)` on `McpServerConfig`.
+`McpPromptHandler`, `McpResourceHandler` (core, neutral) and
`McpTypedPromptHandler<A>` (adapter, typed sugar) follow the same shape.
Register raw/adapted handlers via `addPrompt(...)` and `addResource(...)` on
`McpServerConfig`. There is no `McpTypedResourceHandler` — this asymmetry
pre-exists and applies to both the pre- and post-re-layering shape.
### Reporting errors
-Throw `McpException` from any handler to surface a structured JSON-RPC error:
+Throw `McpException` (from `juneau-bean-jsonrpc`) from any handler to surface
a structured JSON-RPC error:
```java
-throw new McpException(McpDispatcher.CODE_INVALID_PARAMS, "Missing required
argument 'city'");
+import org.apache.juneau.bean.jsonrpc.*;
+import org.apache.juneau.rest.server.mcp.v20250618.*;
+
+throw new McpException(Mcp20250618Revision.CODE_INVALID_PARAMS, "Missing
required argument 'city'");
```
-`McpException` carries the JSON-RPC `code`, `message`, and optional `data`
fields. The dispatcher converts other unchecked exceptions into `-32603
Internal error` automatically.
+`McpException` carries the JSON-RPC `code`, `message`, and optional `data`
fields. `Mcp20250618Revision` converts other unchecked exceptions into `-32603
Internal error` automatically.
## Pagination
-`McpServerConfig.setCursor(McpCursor)` controls how `tools/list`,
`prompts/list`, and `resources/list` paginate.
+`McpServerConfig.setCursor(McpCursor)` controls how `tools/list`,
`prompts/list`, and `resources/list` paginate. `McpCursor`/`McpPage` are
revision-neutral and live in the core, unchanged by the re-layering.
### Built-in strategies
@@ -225,30 +241,53 @@ config.setCursor(myCursor);
## Notifications
-JSON-RPC requests with `id == null` are *notifications*. The dispatcher
invokes the handler, **silently swallows any exception**, and returns `null`.
The REST servlet renders this as an empty response body, so transports can map
this to `204 No Content`.
+JSON-RPC requests with `id == null` are *notifications*.
`Mcp20250618Revision.dispatch()` invokes the handler, **silently swallows any
exception**, and returns `null`. The REST servlet/mixin renders this as an
empty response body, so transports can map this to `204 No Content`.
## Capabilities
-`McpServerConfig.setCapabilities(...)` lets you advertise an explicit
`ServerCapabilities` bean. When omitted, `McpDispatcher` synthesizes one from
the registered handler lists (advertising `tools`, `prompts`, and/or
`resources` only when at least one matching handler is registered).
+Capabilities are revision-owned, not part of the neutral `McpServerConfig` —
`ServerCapabilities` is a `2025-06-18` wire type, and capability shape is
expected to diverge across future MCP revisions. `McpRestServlet20250618` and
`McpEndpoint20250618` each expose a `capabilities()` hook:
+
+```java
+@Rest(path="/mcp")
+public class MyMcpServlet extends McpRestServlet20250618 {
+ @Override
+ protected McpServerConfig createMcpConfig() {
+ return new McpServerConfig().addTool(new EchoTool());
+ }
+
+ @Override
+ protected ServerCapabilities capabilities() {
+ return new ServerCapabilities().setLogging(new LoggingCapability());
+ }
+}
+```
+
+Returning `null` (the default) leaves capabilities auto-derived from the
registered tool/prompt/resource lists — `Mcp20250618Revision` synthesizes a
bare `ServerCapabilities` advertising `tools`/`prompts`/`resources` only when
at least one matching handler is registered, exactly as the earlier
`McpDispatcher` did. Returning a non-`null` value bypasses auto-derivation
entirely — the way to advertise `resources.subscribe`, `logging`,
`listChanged`, or `experimental`, none of which are deri [...]
## Server Info Defaults
-If `setServerInfo(...)` is not provided, the dispatcher reports:
+`McpServerConfig` has plain `name`/`version` `String` fields
(`setName(...)`/`setVersion(...)`) rather than a dedicated identity bean. If
neither is set, `Mcp20250618Revision` reports:
- `name` = `"juneau-rest-server-mcp"`
- `version` = `"unknown"`
-Always set your own `Implementation` so MCP clients can identify and version
your service.
+Always set your own name/version so MCP clients can identify and version your
service.
+
+## Tool schema validation
+
+**New behavior added by the re-layering.** On a given `McpServerConfig`'s
first dispatch, `Mcp20250618Revision` validates that every registered tool's
`McpSchema` uses only the six JSON Schema keywords its wire `JsonSchema` bean
can represent (`type`, `properties`, `required`, `additionalProperties`,
`items`, `$defs`). A schema using an unsupported keyword (`oneOf`, `$ref`,
etc.) throws `IllegalArgumentException`, naming both the offending tool and the
keyword, rather than silently dropp [...]
+
+The check is memoized per `McpServerConfig` instance (an identity-keyed weak
set), so it runs once — on that config's first routed request — not on every
request. A server with an inexpressible schema still comes up healthy; only the
first request against that config fails.
## Polymorphic Wire Format
-MCP returns polymorphic content (`Content`, `ResourceContents`) discriminated
by a `type` property. `McpRestServlet` enables `addBeanTypes` on its serializer
via `@SerializerConfig(addBeanTypes="true")` so the discriminator is emitted on
the wire. If you build your own `RestServlet`, apply the same configuration.
+MCP returns polymorphic content (`Content`, `ResourceContents`) discriminated
by a `type` property. The core `McpRestServlet` enables `addBeanTypes` on its
serializer via `@SerializerConfig(addBeanTypes="true")` so the discriminator is
emitted on the wire — this carries over unchanged onto
`McpRestServlet20250618`. If you build your own `RestServlet` (bypassing both),
apply the same configuration.
-See [juneau-bean-mcp](/docs/topics/JuneauBeanMcp) for the full wire-bean
catalog.
+See [juneau-bean-mcp-2025-06-18](/docs/topics/JuneauBeanMcp) for the full
wire-bean catalog.
## Related Modules
-- **[juneau-bean-mcp](/docs/topics/JuneauBeanMcp)** — The MCP wire beans
consumed by this module.
+- **[juneau-bean-mcp-2025-06-18](/docs/topics/JuneauBeanMcp)** — The
`2025-06-18` wire beans consumed by the adapter module, plus the
revision-neutral JSON-RPC envelope beans (`juneau-bean-jsonrpc`) both modules
build on.
- **[juneau-rest-server](/docs/topics/JuneauRestServer)** — The base REST
server framework.
## Resources
diff --git a/pages/topics/23.05.JuneauShadedAll.md
b/pages/topics/23.05.JuneauShadedAll.md
index fc6d22eeea..ec0285a902 100644
--- a/pages/topics/23.05.JuneauShadedAll.md
+++ b/pages/topics/23.05.JuneauShadedAll.md
@@ -21,14 +21,16 @@ This artifact includes **everything**:
- **juneau-rest-client-classic** - Legacy REST client built on Apache
HttpClient 4.5
- **juneau-rest-server** - REST server API
- **juneau-rest-server-springboot** - Spring Boot integration
-- **juneau-rest-server-mcp** - Model Context Protocol (MCP) REST server
endpoint
+- **juneau-rest-server-mcp** - Model Context Protocol (MCP) REST server
endpoint (revision-neutral core)
+- **juneau-rest-server-mcp-2025-06-18** - MCP `2025-06-18` REST server adapter
### Bean DTOs
- **juneau-bean-common** - Common bean utilities
- **juneau-bean-atom** - ATOM feed beans
- **juneau-bean-html5** - HTML5 element beans
- **juneau-bean-jsonschema** - JSON Schema beans (Draft 2020-12)
-- **juneau-bean-mcp** - Model Context Protocol (MCP) beans
+- **juneau-bean-jsonrpc** - JSON-RPC 2.0 envelope beans
+- **juneau-bean-mcp-2025-06-18** - Model Context Protocol (MCP) `2025-06-18`
beans
- **juneau-bean-openapi-v3** - OpenAPI 3.0 beans
- **juneau-bean-swagger-v2** - Swagger 2.0 beans
diff --git a/sidebars.ts b/sidebars.ts
index c09bf5a19a..86bf19bc00 100644
--- a/sidebars.ts
+++ b/sidebars.ts
@@ -943,7 +943,7 @@ const sidebars: SidebarsConfig = {
{
type: 'doc',
id:
'topics/05.07.JuneauBeanMcp',
- label: '5.7.
juneau-bean-mcp',
+ label: '5.7.
juneau-bean-mcp-2025-06-18',
},
{
type: 'doc',
@@ -965,6 +965,11 @@ const sidebars: SidebarsConfig = {
id:
'topics/05.11.JuneauBeanJsonPatch',
label: '5.11.
juneau-bean-jsonpatch',
},
+ {
+ type: 'doc',
+ id:
'topics/05.12.JuneauBeanJsonRpc',
+ label: '5.12.
juneau-bean-jsonrpc',
+ },
],
link: {
type: 'doc',