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 2558c9fa83 feat: add MCP 2026-07-28 revision server (new bean +
adapter modules)
2558c9fa83 is described below
commit 2558c9fa83ab7bdf71ac4d3eb756d5418e8cf643
Author: James Bognar <[email protected]>
AuthorDate: Thu Jul 30 14:20:36 2026 -0400
feat: add MCP 2026-07-28 revision server (new bean + adapter modules)
Adds juneau-bean-mcp-2026-07-28 and juneau-rest-server-mcp-2026-07-28
implementing the stateless 2026-07-28 MCP revision on the neutral core:
server/discover capability discovery, opaque _meta on the JSON-RPC
envelopes, SEP-2243 Mcp-Method/Mcp-Name header routing, and JsonSchema-typed
tool input/output schemas. De-versions the 2025-06-18 adapter binding class
names (package-only differentiation) to match.
---
artifact-packages.json | 1 -
artifact-paths.json | 6 +++-
pages/release-notes/10.0.0.md | 16 +++++-----
pages/topics/11.JuneauRestServerMcp.md | 54 +++++++++++++++++-----------------
4 files changed, 40 insertions(+), 37 deletions(-)
diff --git a/artifact-packages.json b/artifact-packages.json
index 84cd8d0702..571b61309b 100644
--- a/artifact-packages.json
+++ b/artifact-packages.json
@@ -6,7 +6,6 @@
"juneau-bean-jsonapi": "org/apache/juneau/bean/jsonapi",
"juneau-bean-jsonpatch": "org/apache/juneau/bean/jsonpatch",
"juneau-bean-jsonschema": "org/apache/juneau/bean/jsonschema",
- "juneau-bean-mcp": "org/apache/juneau/bean/mcp",
"juneau-bean-openapi-v3": "org/apache/juneau/bean/openapi3",
"juneau-bean-rfc7807": "org/apache/juneau/bean/rfc7807",
"juneau-bean-swagger-v2": "org/apache/juneau/bean/swagger",
diff --git a/artifact-paths.json b/artifact-paths.json
index 28a4f07251..a5be25db64 100644
--- a/artifact-paths.json
+++ b/artifact-paths.json
@@ -7,8 +7,10 @@
"juneau-bean-html5": "juneau-bean/juneau-bean-html5",
"juneau-bean-jsonapi": "juneau-bean/juneau-bean-jsonapi",
"juneau-bean-jsonpatch": "juneau-bean/juneau-bean-jsonpatch",
+ "juneau-bean-jsonrpc": "juneau-bean/juneau-bean-jsonrpc",
"juneau-bean-jsonschema": "juneau-bean/juneau-bean-jsonschema",
- "juneau-bean-mcp": "juneau-bean/juneau-bean-mcp",
+ "juneau-bean-mcp-2025-06-18": "juneau-bean/juneau-bean-mcp-2025-06-18",
+ "juneau-bean-mcp-2026-07-28": "juneau-bean/juneau-bean-mcp-2026-07-28",
"juneau-bean-openapi-v3": "juneau-bean/juneau-bean-openapi-v3",
"juneau-bean-rfc7807": "juneau-bean/juneau-bean-rfc7807",
"juneau-bean-swagger-v2": "juneau-bean/juneau-bean-swagger-v2",
@@ -53,6 +55,8 @@
"juneau-rest-server-auth-saml": "juneau-rest/juneau-rest-server-auth-saml",
"juneau-rest-server-management-logging":
"juneau-rest/juneau-rest-server-management-logging",
"juneau-rest-server-mcp": "juneau-rest/juneau-rest-server-mcp",
+ "juneau-rest-server-mcp-2025-06-18":
"juneau-rest/juneau-rest-server-mcp-2025-06-18",
+ "juneau-rest-server-mcp-2026-07-28":
"juneau-rest/juneau-rest-server-mcp-2026-07-28",
"juneau-rest-server-metrics-micrometer":
"juneau-rest/juneau-rest-server-metrics-micrometer",
"juneau-rest-server-rdf": "juneau-rest/juneau-rest-server-rdf",
"juneau-rest-server-reactive": "juneau-rest/juneau-rest-server-reactive",
diff --git a/pages/release-notes/10.0.0.md b/pages/release-notes/10.0.0.md
index 6dbfdae6d3..c58929b894 100644
--- a/pages/release-notes/10.0.0.md
+++ b/pages/release-notes/10.0.0.md
@@ -742,7 +742,7 @@ JsonRpcResponse err = JsonRpcResponse.errorResponse(1,
-32601, "Method not found
- **`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`.
+**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
`org.apache.juneau.rest.server.mcp.v20250618.McpRevision` 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.
@@ -795,9 +795,9 @@ A new adapter module, `juneau-rest-server-mcp-2025-06-18`
(package `org.apache.j
### 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.
+- **`org.apache.juneau.rest.server.mcp.v20250618.McpRevision`** — 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- [...]
+- **`org.apache.juneau.rest.server.mcp.v20250618.McpRestServlet`** — 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.
+- **`org.apache.juneau.rest.server.mcp.v20250618.McpEndpoint`** — mixin
interface for this revision, at parity with
`org.apache.juneau.rest.server.mcp.v20250618.McpRestServlet`: 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.
@@ -806,7 +806,7 @@ A new adapter module, `juneau-rest-server-mcp-2025-06-18`
(package `org.apache.j
```java
@Rest(path="/mcp")
-public class MyMcpServlet extends McpRestServlet20250618 {
+public class MyMcpServlet extends McpRestServlet {
@Override
protected McpServerConfig createMcpConfig() {
return new
McpServerConfig().setName("my-server").setVersion("1.0.0").addTool(new
MyEchoTool());
@@ -818,7 +818,7 @@ public class MyMcpServlet extends McpRestServlet20250618 {
```java
@Rest(path="/api")
-public class MyResource extends BasicRestServlet implements
McpEndpoint20250618 {
+public class MyResource extends BasicRestServlet implements McpEndpoint {
@Override
public McpServerConfig getMcpConfig() {
return new McpServerConfig().addTool(new MyEchoTool());
@@ -854,7 +854,7 @@ config.setCursor(McpCursor.fixedSize(50));
```java
@Rest(path="/mcp")
-public class MyMcpServlet extends McpRestServlet20250618 {
+public class MyMcpServlet extends McpRestServlet {
@Override
protected McpServerConfig createMcpConfig() {
return new McpServerConfig().addTool(new MyEchoTool());
@@ -867,7 +867,7 @@ public class MyMcpServlet extends McpRestServlet20250618 {
}
```
-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.
+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. `McpEndpoint` exposes the equivalent hook as a `default`
interface method for the mixin path.
### Notifications
diff --git a/pages/topics/11.JuneauRestServerMcp.md
b/pages/topics/11.JuneauRestServerMcp.md
index 7d10893a8a..eb605e216c 100644
--- a/pages/topics/11.JuneauRestServerMcp.md
+++ b/pages/topics/11.JuneauRestServerMcp.md
@@ -12,8 +12,8 @@ slug: JuneauRestServerMcp
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 **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.
+- A drop-in **servlet** (`McpRestServlet`, concretely
`org.apache.juneau.rest.server.mcp.v20250618.McpRestServlet`) for the common
case of "expose an MCP endpoint at `POST /mcp`".
+- An **interface mixin** (`McpEndpoint`, concretely
`org.apache.juneau.rest.server.mcp.v20250618.McpEndpoint`) 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.
@@ -24,19 +24,19 @@ The implementation is stateless — every request is
dispatched against the `Mcp
### Architecture
```
-+----------------------------+ +-------------------------+
+-----------------+
-| 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)
++------------------------------------------------+
+------------------------------------------------+ +-----------------+
+| org.apache.juneau.rest.server.mcp.v20250618 | |
org.apache.juneau.rest.server.mcp.v20250618 | | Tool/Prompt/ |
+| .McpRestServlet -or- .McpEndpoint | ---> | .McpRevision
(this revision's JSON-RPC | ---> | Resource |
+| (POST handler, adapter) | | method table +
error codes) | | Handlers |
++------------------------------------------------+
+------------------------------------------------+ +-----------------+
+ | |
+ v v
+ juneau-rest-server-mcp
juneau-bean-mcp-2025-06-18
+ (core: McpServerConfig, (wire beans,
via
+ McpRevision SPI, neutral model)
org.apache.juneau.rest.server.mcp.v20250618.McpWire)
```
-`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(...)`.
+`org.apache.juneau.rest.server.mcp.v20250618.McpRevision` 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
@@ -56,7 +56,7 @@ This module transitively pulls in `juneau-rest-server-mcp`
(the core) and `junea
### Drop-in servlet
-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:
+Subclass `org.apache.juneau.rest.server.mcp.v20250618.McpRestServlet` (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.rest.server.*;
@@ -64,7 +64,7 @@ import org.apache.juneau.rest.server.mcp.*;
import org.apache.juneau.rest.server.mcp.v20250618.*;
@Rest(path="/mcp")
-public class MyMcpServlet extends McpRestServlet20250618 {
+public class MyMcpServlet extends
org.apache.juneau.rest.server.mcp.v20250618.McpRestServlet {
@Override
protected McpServerConfig createMcpConfig() {
@@ -80,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 `McpEndpoint20250618`:
+If you already have a `@Rest` resource and want to expose MCP at `POST /mcp`
next to your other endpoints, implement `McpEndpoint`:
```java
@Rest(path="/api")
-public class MyResource extends BasicRestServlet implements
McpEndpoint20250618 {
+public class MyResource extends BasicRestServlet implements McpEndpoint {
@Override
public McpServerConfig getMcpConfig() {
@@ -93,7 +93,7 @@ public class MyResource extends BasicRestServlet implements
McpEndpoint20250618
}
```
-The default `handleMcpRequest(...)` method on `McpEndpoint` (annotated
`@RestPost("/mcp")`) takes care of dispatch, via `McpEndpoint20250618`'s
`revision()` override.
+The default `handleMcpRequest(...)` method on `McpEndpoint` (annotated
`@RestPost("/mcp")`) takes care of dispatch, via
`org.apache.juneau.rest.server.mcp.v20250618.McpEndpoint`'s `revision()`
override.
## Writing Handlers
@@ -203,10 +203,10 @@ Throw `McpException` (from `juneau-bean-jsonrpc`) from
any handler to surface a
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'");
+throw new McpException(McpRevision.CODE_INVALID_PARAMS, "Missing required
argument 'city'");
```
-`McpException` carries the JSON-RPC `code`, `message`, and optional `data`
fields. `Mcp20250618Revision` converts other unchecked exceptions into `-32603
Internal error` automatically.
+`McpException` carries the JSON-RPC `code`, `message`, and optional `data`
fields. `McpRevision` converts other unchecked exceptions into `-32603 Internal
error` automatically.
## Pagination
@@ -241,15 +241,15 @@ config.setCursor(myCursor);
## Notifications
-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`.
+JSON-RPC requests with `id == null` are *notifications*.
`McpRevision.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
-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:
+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. `McpRestServlet` and
`McpEndpoint` each expose a `capabilities()` hook:
```java
@Rest(path="/mcp")
-public class MyMcpServlet extends McpRestServlet20250618 {
+public class MyMcpServlet extends McpRestServlet {
@Override
protected McpServerConfig createMcpConfig() {
return new McpServerConfig().addTool(new EchoTool());
@@ -262,11 +262,11 @@ public class MyMcpServlet extends McpRestServlet20250618 {
}
```
-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 [...]
+Returning `null` (the default) leaves capabilities auto-derived from the
registered tool/prompt/resource lists — `McpRevision` 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 derivable fr [...]
## Server Info Defaults
-`McpServerConfig` has plain `name`/`version` `String` fields
(`setName(...)`/`setVersion(...)`) rather than a dedicated identity bean. If
neither is set, `Mcp20250618Revision` reports:
+`McpServerConfig` has plain `name`/`version` `String` fields
(`setName(...)`/`setVersion(...)`) rather than a dedicated identity bean. If
neither is set, `McpRevision` reports:
- `name` = `"juneau-rest-server-mcp"`
- `version` = `"unknown"`
@@ -275,13 +275,13 @@ Always set your own name/version so MCP clients can
identify and version your se
## 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 [...]
+**New behavior added by the re-layering.** On a given `McpServerConfig`'s
first dispatch, `McpRevision` 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 dropping it o [...]
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. 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.
+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
`org.apache.juneau.rest.server.mcp.v20250618.McpRestServlet`. If you build your
own `RestServlet` (bypassing both), apply the same configuration.
See [juneau-bean-mcp-2025-06-18](/docs/topics/JuneauBeanMcp) for the full
wire-bean catalog.