This is an automated email from the ASF dual-hosted git repository.

jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git

commit 3420e01ccb7894080c9d26e92d48029e2ad2bc72
Author: James Bognar <[email protected]>
AuthorDate: Tue Jul 28 17:21:46 2026 -0400

    refactor: replace McpDispatcher/Mcp with the McpRevision SPI
    
    Moves the JSON-RPC method table verbatim into Mcp20250618Revision in the new
    2025-06-18 adapter, rewires McpRestServlet/McpEndpoint to dispatch through
    revision() with an McpExchange, and deletes McpDispatcher and the Mcp static
    facade. Handler interfaces and McpServerConfig still speak wire types; the
    neutral model lands next. Characterization fixtures replay byte-identical.
    
    Co-authored-by: Cursor <[email protected]>
---
 .../juneau/rest/server/mcp/McpCursorCtx_Test.java  |  47 ++--
 .../Mcp20250618Revision_Test.java}                 |  59 +++--
 .../{ => v20250618}/McpHandlerDefaults_Test.java   |  28 +--
 .../McpRestServlet20250618_Test.java}              |  13 +-
 .../mcp/{ => v20250618}/McpServerConfig_Test.java  |   3 +-
 .../mcp/{ => v20250618}/McpTypedHandlers_Test.java |  45 ++--
 .../server/mcp/v20250618/Mcp20250618Revision.java  | 230 +++++++++++++++++
 .../server/mcp/v20250618/McpEndpoint20250618.java  |  66 +++++
 .../mcp/v20250618/McpRestServlet20250618.java      |  75 ++++++
 .../server/mcp/v20250618}/McpTypedHandlers.java    |   9 +-
 .../mcp/v20250618}/McpTypedPromptHandler.java      |   3 +-
 .../server/mcp/v20250618}/McpTypedToolHandler.java |   3 +-
 .../mcp/v20250618}/Characterization_Test.java      |  15 +-
 .../mcp/v20250618/Mcp20250618Revision_Test.java}   |  59 +++--
 .../mcp/v20250618}/McpHandlerDefaults_Test.java    |  28 +--
 .../v20250618/McpRestServlet20250618_Test.java}    |  13 +-
 .../mcp/v20250618}/McpServerConfig_Test.java       |   3 +-
 .../mcp/v20250618}/McpTypedHandlers_Test.java      |  45 ++--
 .../characterization/CAPS-initialize.request.json  |   0
 .../characterization/CAPS-initialize.response.json |   0
 .../EMPTY-empty-method.request.json                |   0
 .../EMPTY-empty-method.response.json               |   0
 .../characterization/EMPTY-initialize.request.json |   0
 .../EMPTY-initialize.response.json                 |   0
 .../EMPTY-missing-method.request.json              |   0
 .../EMPTY-missing-method.response.json             |   0
 .../EMPTY-notification-ping.request.json           |   0
 .../EMPTY-notification-ping.response.json          |   0
 .../EMPTY-notification-unknown.request.json        |   0
 .../EMPTY-notification-unknown.response.json       |   0
 .../characterization/EMPTY-ping.request.json       |   0
 .../characterization/EMPTY-ping.response.json      |   0
 .../EMPTY-prompts-get-missing-name.request.json    |   0
 .../EMPTY-prompts-get-missing-name.response.json   |   0
 .../EMPTY-prompts-get-unknown.request.json         |   0
 .../EMPTY-prompts-get-unknown.response.json        |   0
 .../EMPTY-prompts-list.request.json                |   0
 .../EMPTY-prompts-list.response.json               |   0
 .../EMPTY-resources-list.request.json              |   0
 .../EMPTY-resources-list.response.json             |   0
 .../EMPTY-resources-read-missing-uri.request.json  |   0
 .../EMPTY-resources-read-missing-uri.response.json |   0
 .../EMPTY-resources-read-unknown.request.json      |   0
 .../EMPTY-resources-read-unknown.response.json     |   0
 .../EMPTY-tools-call-missing-name.request.json     |   0
 .../EMPTY-tools-call-missing-name.response.json    |   0
 .../EMPTY-tools-call-unknown-tool.request.json     |   0
 .../EMPTY-tools-call-unknown-tool.response.json    |   0
 .../characterization/EMPTY-tools-list.request.json |   0
 .../EMPTY-tools-list.response.json                 |   0
 .../EMPTY-unknown-method.request.json              |   0
 .../EMPTY-unknown-method.response.json             |   0
 .../characterization/FULL-initialize.request.json  |   0
 .../characterization/FULL-initialize.response.json |   0
 .../characterization/FULL-prompts-get.request.json |   0
 .../FULL-prompts-get.response.json                 |   0
 .../FULL-prompts-list.request.json                 |   0
 .../FULL-prompts-list.response.json                |   0
 .../FULL-resources-list.request.json               |   0
 .../FULL-resources-list.response.json              |   0
 .../FULL-resources-read.request.json               |   0
 .../FULL-resources-read.response.json              |   0
 .../characterization/FULL-string-id.request.json   |   0
 .../characterization/FULL-string-id.response.json  |   0
 .../FULL-tools-call-arguments-not-map.request.json |   0
 ...FULL-tools-call-arguments-not-map.response.json |   0
 .../FULL-tools-call-error-flag.request.json        |   0
 .../FULL-tools-call-error-flag.response.json       |   0
 .../FULL-tools-call-mixed-content.request.json     |   0
 .../FULL-tools-call-mixed-content.response.json    |   0
 .../FULL-tools-call-no-arguments.request.json      |   0
 .../FULL-tools-call-no-arguments.response.json     |   0
 .../FULL-tools-call-params-not-map.request.json    |   0
 .../FULL-tools-call-params-not-map.response.json   |   0
 .../FULL-tools-call-text.request.json              |   0
 .../FULL-tools-call-text.response.json             |   0
 .../characterization/FULL-tools-list.request.json  |   0
 .../characterization/FULL-tools-list.response.json |   0
 .../PAGED-tools-list-first.request.json            |   0
 .../PAGED-tools-list-first.response.json           |   0
 .../PAGED-tools-list-second.request.json           |   0
 .../PAGED-tools-list-second.response.json          |   0
 .../THROW-mcp-exception.request.json               |   0
 .../THROW-mcp-exception.response.json              |   0
 .../characterization/THROW-no-message.request.json |   0
 .../THROW-no-message.response.json                 |   0
 .../THROW-runtime-exception.request.json           |   0
 .../THROW-runtime-exception.response.json          |   0
 .../org/apache/juneau/rest/server/mcp/Mcp.java     |  59 -----
 .../juneau/rest/server/mcp/McpDispatcher.java      | 271 ---------------------
 .../apache/juneau/rest/server/mcp/McpEndpoint.java |  23 +-
 .../juneau/rest/server/mcp/McpPromptHandler.java   |   5 +-
 .../juneau/rest/server/mcp/McpResourceHandler.java |   5 +-
 .../juneau/rest/server/mcp/McpRestServlet.java     |  21 +-
 .../juneau/rest/server/mcp/McpServerConfig.java    |   6 +-
 .../juneau/rest/server/mcp/McpToolHandler.java     |   5 +-
 .../juneau/rest/server/mcp/McpCursorCtx_Test.java} |  47 ++--
 97 files changed, 610 insertions(+), 576 deletions(-)

diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpTypedPromptHandler.java
 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/mcp/McpCursorCtx_Test.java
similarity index 53%
copy from 
juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpTypedPromptHandler.java
copy to 
juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/mcp/McpCursorCtx_Test.java
index 524737d38a..4ee720fd73 100644
--- 
a/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpTypedPromptHandler.java
+++ 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/mcp/McpCursorCtx_Test.java
@@ -16,36 +16,29 @@
  */
 package org.apache.juneau.rest.server.mcp;
 
-import org.apache.juneau.bean.mcp.v20250618.*;
+import static org.junit.jupiter.api.Assertions.*;
+
 import org.apache.juneau.commons.inject.*;
+import org.junit.jupiter.api.*;
 
 /**
- * Typed variant of {@link McpPromptHandler} where MCP {@code prompts/get} 
arguments bind into a Juneau bean.
- *
- * @param <A> Argument bean type.
+ * Coverage for {@link McpCursor} bean-store passthrough.
  */
-public interface McpTypedPromptHandler<A> {
-
-       /**
-        * Returns the static descriptor for this prompt.
-        *
-        * @return The prompt descriptor. Never {@code null}.
-        */
-       Prompt descriptor();
-
-       /**
-        * Returns the runtime argument class for binding.
-        *
-        * @return The argument class. Never {@code null}.
-        */
-       Class<A> argumentType();
+class McpCursorCtx_Test {
 
-       /**
-        * Renders the prompt.
-        *
-        * @param arguments Bound argument bean (may be {@code null} when no 
arguments are supplied).
-        * @param ctx Per-request bean store.
-        * @return The rendered prompt.
-        */
-       GetPromptResult get(A arguments, BeanStore ctx);
+       @Test
+       void cursor_passes_ctx_to_strategy() {
+               // Verify ctx parameter reaches the cursor (covers the 
BeanStore parameter passthrough).
+               var bs = new BasicBeanStore();
+               var got = new Object[1];
+               McpCursor c = new McpCursor() {
+                       @Override
+                       public <T> McpPage<T> page(java.util.List<T> all, 
String cursor, BeanStore ctx) {
+                               got[0] = ctx;
+                               return new McpPage<>(all, null);
+                       }
+               };
+               c.page(java.util.List.of(), null, bs);
+               assertSame(bs, got[0]);
+       }
 }
diff --git 
a/juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/mcp/McpDispatcher_Test.java
 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/mcp/v20250618/Mcp20250618Revision_Test.java
similarity index 82%
rename from 
juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/mcp/McpDispatcher_Test.java
rename to 
juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/mcp/v20250618/Mcp20250618Revision_Test.java
index f2773d7d6d..5deec891fa 100644
--- 
a/juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/mcp/McpDispatcher_Test.java
+++ 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/mcp/v20250618/Mcp20250618Revision_Test.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.juneau.rest.server.mcp;
+package org.apache.juneau.rest.server.mcp.v20250618;
 
 import static org.apache.juneau.test.bct.BctAssertions.*;
 import static org.junit.jupiter.api.Assertions.*;
@@ -25,14 +25,14 @@ import org.apache.juneau.bean.jsonrpc.*;
 import org.apache.juneau.bean.mcp.v20250618.*;
 import org.apache.juneau.commons.inject.*;
 import org.apache.juneau.marshall.collections.*;
+import org.apache.juneau.rest.server.mcp.*;
 import org.junit.jupiter.api.*;
 
 /**
- * Coverage for {@link McpDispatcher}.
+ * Coverage for {@link Mcp20250618Revision}.
  */
-class McpDispatcher_Test {
+class Mcp20250618Revision_Test {
 
-       private final McpDispatcher dispatcher = new McpDispatcher();
        private final BeanStore ctx = new BasicBeanStore();
 
        private static McpToolHandler tool(String name, 
java.util.function.Function<Map<String,Object>,CallToolResult> fn) {
@@ -82,7 +82,7 @@ class McpDispatcher_Test {
        }
 
        private JsonRpcResponse send(McpServerConfig config, JsonRpcRequest r) {
-               return dispatcher.dispatch(r, config, ctx);
+               return new Mcp20250618Revision(null).dispatch(new 
McpExchange(r, n -> null), config, ctx);
        }
 
        @Test
@@ -99,7 +99,7 @@ class McpDispatcher_Test {
                assertNotNull(result.getCapabilities().getTools());
                assertNotNull(result.getCapabilities().getPrompts());
                assertNotNull(result.getCapabilities().getResources());
-               assertString(McpDispatcher.DEFAULT_SERVER_NAME, 
result.getServerInfo().getName());
+               assertString(Mcp20250618Revision.DEFAULT_SERVER_NAME, 
result.getServerInfo().getName());
        }
 
        @Test
@@ -123,19 +123,19 @@ class McpDispatcher_Test {
        @Test
        void method_notFound() {
                var resp = send(new McpServerConfig(), req(1, "no/such/method", 
null));
-               assertEquals(McpDispatcher.CODE_METHOD_NOT_FOUND, 
resp.getError().getCode());
+               assertEquals(Mcp20250618Revision.CODE_METHOD_NOT_FOUND, 
resp.getError().getCode());
        }
 
        @Test
        void missing_method() {
                var resp = send(new McpServerConfig(), req(1, null, null));
-               assertEquals(McpDispatcher.CODE_INVALID_REQUEST, 
resp.getError().getCode());
+               assertEquals(Mcp20250618Revision.CODE_INVALID_REQUEST, 
resp.getError().getCode());
        }
 
        @Test
        void empty_method_string() {
                var resp = send(new McpServerConfig(), req(1, "", null));
-               assertEquals(McpDispatcher.CODE_INVALID_REQUEST, 
resp.getError().getCode());
+               assertEquals(Mcp20250618Revision.CODE_INVALID_REQUEST, 
resp.getError().getCode());
        }
 
        @Test
@@ -183,8 +183,8 @@ class McpDispatcher_Test {
 
        @Test
        void nullEnvelope_returnsInvalidRequest() {
-               var resp = dispatcher.dispatch(null, new McpServerConfig(), 
ctx);
-               assertEquals(McpDispatcher.CODE_INVALID_REQUEST, 
resp.getError().getCode());
+               var resp = new Mcp20250618Revision(null).dispatch(new 
McpExchange(null, n -> null), new McpServerConfig(), ctx);
+               assertEquals(Mcp20250618Revision.CODE_INVALID_REQUEST, 
resp.getError().getCode());
        }
 
        // -------- tools/list ---------
@@ -230,27 +230,27 @@ class McpDispatcher_Test {
        @Test
        void tools_call_missingName_invalidParams() {
                var resp = send(new McpServerConfig(), req(1, 
McpMethods.TOOLS_CALL, JsonMap.of("arguments", JsonMap.of())));
-               assertEquals(McpDispatcher.CODE_INVALID_PARAMS, 
resp.getError().getCode());
+               assertEquals(Mcp20250618Revision.CODE_INVALID_PARAMS, 
resp.getError().getCode());
        }
 
        @Test
        void tools_call_unknownTool_methodNotFound() {
                var resp = send(new McpServerConfig(), req(1, 
McpMethods.TOOLS_CALL, JsonMap.of("name", "missing")));
-               assertEquals(McpDispatcher.CODE_METHOD_NOT_FOUND, 
resp.getError().getCode());
+               assertEquals(Mcp20250618Revision.CODE_METHOD_NOT_FOUND, 
resp.getError().getCode());
        }
 
        @Test
        void tools_call_argumentsNotObject_throwsInvalidParams() {
                var config = new McpServerConfig().addTool(tool("e", a -> new 
CallToolResult()));
                var resp = send(config, req(1, McpMethods.TOOLS_CALL, 
JsonMap.of("name", "e", "arguments", "string-not-map")));
-               assertEquals(McpDispatcher.CODE_INVALID_PARAMS, 
resp.getError().getCode());
+               assertEquals(Mcp20250618Revision.CODE_INVALID_PARAMS, 
resp.getError().getCode());
        }
 
        @Test
        void tools_call_paramsNotMap_invalidParams() {
                var config = new McpServerConfig().addTool(tool("e", a -> new 
CallToolResult()));
                var resp = send(config, req(1, McpMethods.TOOLS_CALL, 
"not-a-map"));
-               assertEquals(McpDispatcher.CODE_INVALID_PARAMS, 
resp.getError().getCode());
+               assertEquals(Mcp20250618Revision.CODE_INVALID_PARAMS, 
resp.getError().getCode());
        }
 
        @Test
@@ -270,7 +270,7 @@ class McpDispatcher_Test {
                        throw new RuntimeException("boom");
                }));
                var resp = send(config, req(1, McpMethods.TOOLS_CALL, 
JsonMap.of("name", "e")));
-               assertEquals(McpDispatcher.CODE_INTERNAL_ERROR, 
resp.getError().getCode());
+               assertEquals(Mcp20250618Revision.CODE_INTERNAL_ERROR, 
resp.getError().getCode());
                assertString("boom", resp.getError().getMessage());
        }
 
@@ -280,7 +280,7 @@ class McpDispatcher_Test {
                        throw new IllegalStateException();
                }));
                var resp = send(config, req(1, McpMethods.TOOLS_CALL, 
JsonMap.of("name", "e")));
-               assertEquals(McpDispatcher.CODE_INTERNAL_ERROR, 
resp.getError().getCode());
+               assertEquals(Mcp20250618Revision.CODE_INTERNAL_ERROR, 
resp.getError().getCode());
                assertString("IllegalStateException", 
resp.getError().getMessage());
        }
 
@@ -298,13 +298,13 @@ class McpDispatcher_Test {
        @Test
        void prompts_get_missingName_invalidParams() {
                var resp = send(new McpServerConfig(), req(1, 
McpMethods.PROMPTS_GET, JsonMap.of()));
-               assertEquals(McpDispatcher.CODE_INVALID_PARAMS, 
resp.getError().getCode());
+               assertEquals(Mcp20250618Revision.CODE_INVALID_PARAMS, 
resp.getError().getCode());
        }
 
        @Test
        void prompts_get_unknown_methodNotFound() {
                var resp = send(new McpServerConfig(), req(1, 
McpMethods.PROMPTS_GET, JsonMap.of("name", "missing")));
-               assertEquals(McpDispatcher.CODE_METHOD_NOT_FOUND, 
resp.getError().getCode());
+               assertEquals(Mcp20250618Revision.CODE_METHOD_NOT_FOUND, 
resp.getError().getCode());
        }
 
        // -------- resources ---------
@@ -321,13 +321,13 @@ class McpDispatcher_Test {
        @Test
        void resources_read_missingUri_invalidParams() {
                var resp = send(new McpServerConfig(), req(1, 
McpMethods.RESOURCES_READ, JsonMap.of()));
-               assertEquals(McpDispatcher.CODE_INVALID_PARAMS, 
resp.getError().getCode());
+               assertEquals(Mcp20250618Revision.CODE_INVALID_PARAMS, 
resp.getError().getCode());
        }
 
        @Test
        void resources_read_unknown_methodNotFound() {
                var resp = send(new McpServerConfig(), req(1, 
McpMethods.RESOURCES_READ, JsonMap.of("uri", "ghost://")));
-               assertEquals(McpDispatcher.CODE_METHOD_NOT_FOUND, 
resp.getError().getCode());
+               assertEquals(Mcp20250618Revision.CODE_METHOD_NOT_FOUND, 
resp.getError().getCode());
        }
 
        // -------- pagination cursor passthrough ---------
@@ -341,12 +341,19 @@ class McpDispatcher_Test {
                assertString("1", resp.getNextCursor());
        }
 
-       // -------- Mcp facade ---------
+       // -------- error code table ---------
 
        @Test
-       void facade_dispatches() {
-               var config = new McpServerConfig();
-               var resp = Mcp.handle(req(1, McpMethods.PING, null), config, 
ctx);
-               assertNotNull(resp);
+       void errorCode_tableIsComplete() {
+               var a = new Mcp20250618Revision(null);
+               assertEquals(-32600, a.errorCode(McpErrorKind.INVALID_REQUEST));
+               assertEquals(-32601, a.errorCode(McpErrorKind.UNKNOWN_METHOD));
+               assertEquals(-32601, a.errorCode(McpErrorKind.TOOL_NOT_FOUND), 
"known-wrong mapping, preserved deliberately");
+               assertEquals(-32601, 
a.errorCode(McpErrorKind.PROMPT_NOT_FOUND), "known-wrong mapping, preserved 
deliberately");
+               assertEquals(-32601, 
a.errorCode(McpErrorKind.RESOURCE_NOT_FOUND), "known-wrong mapping, preserved 
deliberately");
+               assertEquals(-32602, a.errorCode(McpErrorKind.INVALID_PARAMS));
+               assertEquals(-32603, a.errorCode(McpErrorKind.INTERNAL_ERROR));
+               assertEquals(-32700, a.errorCode(McpErrorKind.PARSE_ERROR));
+               assertEquals("2025-06-18", a.protocolVersion());
        }
 }
diff --git 
a/juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/mcp/McpHandlerDefaults_Test.java
 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/mcp/v20250618/McpHandlerDefaults_Test.java
similarity index 75%
rename from 
juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/mcp/McpHandlerDefaults_Test.java
rename to 
juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/mcp/v20250618/McpHandlerDefaults_Test.java
index 94f4a309cd..55b78d910e 100644
--- 
a/juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/mcp/McpHandlerDefaults_Test.java
+++ 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/mcp/v20250618/McpHandlerDefaults_Test.java
@@ -14,13 +14,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.juneau.rest.server.mcp;
+package org.apache.juneau.rest.server.mcp.v20250618;
 
 import static org.apache.juneau.test.bct.BctAssertions.*;
 import static org.junit.jupiter.api.Assertions.*;
 
 import org.apache.juneau.bean.mcp.v20250618.*;
 import org.apache.juneau.commons.inject.*;
+import org.apache.juneau.rest.server.mcp.*;
 import org.junit.jupiter.api.*;
 
 /**
@@ -57,29 +58,4 @@ class McpHandlerDefaults_Test {
                        assertNotNull(ctor.newInstance());
                });
        }
-
-       @Test
-       void mcp_facade_constructor_isPrivate() {
-               assertDoesNotThrow(() -> {
-                       var ctor = Mcp.class.getDeclaredConstructor();
-                       ctor.setAccessible(true);
-                       assertNotNull(ctor.newInstance());
-               });
-       }
-
-       @Test
-       void cursor_passes_ctx_to_strategy() {
-               // Verify ctx parameter reaches the cursor (covers the 
BeanStore parameter passthrough).
-               var bs = new BasicBeanStore();
-               var got = new Object[1];
-               McpCursor c = new McpCursor() {
-                       @Override
-                       public <T> McpPage<T> page(java.util.List<T> all, 
String cursor, BeanStore ctx) {
-                               got[0] = ctx;
-                               return new McpPage<>(all, null);
-                       }
-               };
-               c.page(java.util.List.of(), null, bs);
-               assertSame(bs, got[0]);
-       }
 }
diff --git 
a/juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/mcp/McpRestServlet_Test.java
 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/mcp/v20250618/McpRestServlet20250618_Test.java
similarity index 93%
rename from 
juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/mcp/McpRestServlet_Test.java
rename to 
juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/mcp/v20250618/McpRestServlet20250618_Test.java
index 53518668c1..ca289b1eaf 100644
--- 
a/juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/mcp/McpRestServlet_Test.java
+++ 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/mcp/v20250618/McpRestServlet20250618_Test.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.juneau.rest.server.mcp;
+package org.apache.juneau.rest.server.mcp.v20250618;
 
 import static org.apache.juneau.test.bct.BctAssertions.*;
 import static org.junit.jupiter.api.Assertions.*;
@@ -28,15 +28,16 @@ import org.apache.juneau.commons.inject.*;
 import org.apache.juneau.marshall.json.*;
 import org.apache.juneau.rest.mock.classic.*;
 import org.apache.juneau.rest.server.*;
+import org.apache.juneau.rest.server.mcp.*;
 import org.junit.jupiter.api.*;
 
 /**
- * End-to-end coverage for {@link McpRestServlet} via {@link MockRestClient}.
+ * End-to-end coverage for {@link McpRestServlet20250618} via {@link 
MockRestClient}.
  */
 @SuppressWarnings({
        "resource" // Test helpers return Closeables; Eclipse JDT @Owning 
warning is by design.
 })
-class McpRestServlet_Test extends TestBase {
+class McpRestServlet20250618_Test extends TestBase {
 
        private static final JsonParser PAR = JsonParser.create()
                .typePropertyName(Content.class, "type")
@@ -44,7 +45,7 @@ class McpRestServlet_Test extends TestBase {
                .build();
 
        @Rest(serializers = JsonSerializer.class, parsers = JsonParser.class, 
defaultAccept = "application/json")
-       public static class A extends McpRestServlet {
+       public static class A extends McpRestServlet20250618 {
                private static final long serialVersionUID = 1L;
 
                @Override
@@ -115,7 +116,7 @@ class McpRestServlet_Test extends TestBase {
 
        @Rest(path = "/api", serializers = JsonSerializer.class, parsers = 
JsonParser.class, defaultAccept = "application/json")
        @org.apache.juneau.marshall.serializer.SerializerConfig(addBeanTypes = 
"true")
-       public static class B extends 
org.apache.juneau.rest.server.servlet.BasicRestServlet implements McpEndpoint {
+       public static class B extends 
org.apache.juneau.rest.server.servlet.BasicRestServlet implements 
McpEndpoint20250618 {
                private static final long serialVersionUID = 1L;
 
                @Override
@@ -146,7 +147,7 @@ class McpRestServlet_Test extends TestBase {
        // -------- failure modes --------
 
        @Rest(serializers = JsonSerializer.class, parsers = JsonParser.class, 
defaultAccept = "application/json")
-       public static class C extends McpRestServlet {
+       public static class C extends McpRestServlet20250618 {
                private static final long serialVersionUID = 1L;
                @Override
                protected McpServerConfig createMcpConfig() {
diff --git 
a/juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/mcp/McpServerConfig_Test.java
 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/mcp/v20250618/McpServerConfig_Test.java
similarity index 97%
rename from 
juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/mcp/McpServerConfig_Test.java
rename to 
juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/mcp/v20250618/McpServerConfig_Test.java
index b0000ea739..155c7798a7 100644
--- 
a/juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/mcp/McpServerConfig_Test.java
+++ 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/mcp/v20250618/McpServerConfig_Test.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.juneau.rest.server.mcp;
+package org.apache.juneau.rest.server.mcp.v20250618;
 
 import static org.apache.juneau.test.bct.BctAssertions.*;
 import static org.junit.jupiter.api.Assertions.*;
@@ -23,6 +23,7 @@ import java.util.*;
 
 import org.apache.juneau.bean.mcp.v20250618.*;
 import org.apache.juneau.commons.inject.*;
+import org.apache.juneau.rest.server.mcp.*;
 import org.junit.jupiter.api.*;
 
 /**
diff --git 
a/juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/mcp/McpTypedHandlers_Test.java
 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/mcp/v20250618/McpTypedHandlers_Test.java
similarity index 90%
rename from 
juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/mcp/McpTypedHandlers_Test.java
rename to 
juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/mcp/v20250618/McpTypedHandlers_Test.java
index 7c2ac864ac..72fcf1da66 100644
--- 
a/juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/mcp/McpTypedHandlers_Test.java
+++ 
b/juneau-integration-tests/src/test/java/org/apache/juneau/rest/server/mcp/v20250618/McpTypedHandlers_Test.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.juneau.rest.server.mcp;
+package org.apache.juneau.rest.server.mcp.v20250618;
 
 import static org.apache.juneau.test.bct.BctAssertions.*;
 import static org.junit.jupiter.api.Assertions.*;
@@ -25,6 +25,7 @@ import org.apache.juneau.bean.jsonrpc.*;
 import org.apache.juneau.bean.mcp.v20250618.*;
 import org.apache.juneau.commons.inject.*;
 import org.apache.juneau.marshall.collections.*;
+import org.apache.juneau.rest.server.mcp.*;
 import org.junit.jupiter.api.*;
 
 /**
@@ -50,7 +51,11 @@ class McpTypedHandlers_Test {
        }
 
        private final BeanStore ctx = new BasicBeanStore();
-       private final McpDispatcher dispatcher = new McpDispatcher();
+       private final Mcp20250618Revision revision = new 
Mcp20250618Revision(null);
+
+       private JsonRpcResponse dispatch(JsonRpcRequest req, McpServerConfig 
config) {
+               return revision.dispatch(new McpExchange(req, n -> null), 
config, ctx);
+       }
 
        @Test
        void typedTool_argsBound_andResultWrappedAsText() {
@@ -78,7 +83,7 @@ class McpTypedHandlers_Test {
                        .setId(1)
                        .setMethod(McpMethods.TOOLS_CALL)
                        .setParams(JsonMap.of("name", "echo", "arguments", 
JsonMap.of("message", "hi", "repeat", 3)));
-               var resp = dispatcher.dispatch(req, config, ctx);
+               var resp = dispatch(req, config);
                var ctr = (CallToolResult) resp.getResult();
                var text = ((TextContent) ctr.getContent().get(0)).getText();
                assertContains("\"text\":\"hi:3\"", text);
@@ -109,7 +114,7 @@ class McpTypedHandlers_Test {
                        .setId(1)
                        .setMethod(McpMethods.TOOLS_CALL)
                        .setParams(JsonMap.of("name", "d"));
-               var resp = dispatcher.dispatch(req, config, ctx);
+               var resp = dispatch(req, config);
                assertSame(ctr, resp.getResult());
        }
 
@@ -124,9 +129,9 @@ class McpTypedHandlers_Test {
                        public String call(EchoArgs args, BeanStore ctx) { 
return "hello"; }
                };
                var config = new 
McpServerConfig().addTool(McpTypedHandlers.adaptTool(typed));
-               var resp = dispatcher.dispatch(new JsonRpcRequest()
+               var resp = dispatch(new JsonRpcRequest()
                        .setJsonrpc(McpProtocol.JSON_RPC_2_0).setId(1)
-                       
.setMethod(McpMethods.TOOLS_CALL).setParams(JsonMap.of("name", "s")), config, 
ctx);
+                       
.setMethod(McpMethods.TOOLS_CALL).setParams(JsonMap.of("name", "s")), config);
                var ctr = (CallToolResult) resp.getResult();
                assertString("hello", ((TextContent) 
ctr.getContent().get(0)).getText());
        }
@@ -142,9 +147,9 @@ class McpTypedHandlers_Test {
                        public EchoResult call(EchoArgs args, BeanStore ctx) { 
return null; }
                };
                var config = new 
McpServerConfig().addTool(McpTypedHandlers.adaptTool(typed));
-               var resp = dispatcher.dispatch(new JsonRpcRequest()
+               var resp = dispatch(new JsonRpcRequest()
                        .setJsonrpc(McpProtocol.JSON_RPC_2_0).setId(1)
-                       
.setMethod(McpMethods.TOOLS_CALL).setParams(JsonMap.of("name", "n")), config, 
ctx);
+                       
.setMethod(McpMethods.TOOLS_CALL).setParams(JsonMap.of("name", "n")), config);
                var ctr = (CallToolResult) resp.getResult();
                assertString("", ((TextContent) 
ctr.getContent().get(0)).getText());
        }
@@ -162,9 +167,9 @@ class McpTypedHandlers_Test {
                        }
                };
                var config = new 
McpServerConfig().addTool(McpTypedHandlers.adaptTool(typed));
-               var resp = dispatcher.dispatch(new JsonRpcRequest()
+               var resp = dispatch(new JsonRpcRequest()
                        .setJsonrpc(McpProtocol.JSON_RPC_2_0).setId(1)
-                       
.setMethod(McpMethods.TOOLS_CALL).setParams(JsonMap.of("name", "z")), config, 
ctx);
+                       
.setMethod(McpMethods.TOOLS_CALL).setParams(JsonMap.of("name", "z")), config);
                var ctr = (CallToolResult) resp.getResult();
                assertString("null", ((TextContent) 
ctr.getContent().get(0)).getText());
        }
@@ -181,11 +186,11 @@ class McpTypedHandlers_Test {
                };
                var config = new 
McpServerConfig().addTool(McpTypedHandlers.adaptTool(typed));
                // Bad: 'repeat' should be int, supply a non-numeric value to 
trigger parser failure.
-               var resp = dispatcher.dispatch(new JsonRpcRequest()
+               var resp = dispatch(new JsonRpcRequest()
                        .setJsonrpc(McpProtocol.JSON_RPC_2_0).setId(1)
                        .setMethod(McpMethods.TOOLS_CALL)
-                       .setParams(JsonMap.of("name", "x", "arguments", 
JsonMap.of("repeat", "not-an-int"))), config, ctx);
-               assertEquals(McpDispatcher.CODE_INVALID_PARAMS, 
resp.getError().getCode());
+                       .setParams(JsonMap.of("name", "x", "arguments", 
JsonMap.of("repeat", "not-an-int"))), config);
+               assertEquals(Mcp20250618Revision.CODE_INVALID_PARAMS, 
resp.getError().getCode());
        }
 
        @Test
@@ -202,9 +207,9 @@ class McpTypedHandlers_Test {
                };
                var raw = McpTypedHandlers.adaptPrompt(typed);
                var config = new McpServerConfig().addPrompt(raw);
-               var resp = dispatcher.dispatch(new JsonRpcRequest()
+               var resp = dispatch(new JsonRpcRequest()
                        .setJsonrpc(McpProtocol.JSON_RPC_2_0).setId(1)
-                       
.setMethod(McpMethods.PROMPTS_GET).setParams(JsonMap.of("name", "p")), config, 
ctx);
+                       
.setMethod(McpMethods.PROMPTS_GET).setParams(JsonMap.of("name", "p")), config);
                var pr = (GetPromptResult) resp.getResult();
                assertString("null", pr.getDescription());
        }
@@ -260,10 +265,10 @@ class McpTypedHandlers_Test {
                        public Unserializable call(EchoArgs args, BeanStore 
ctx) { return new Unserializable(); }
                };
                var config = new 
McpServerConfig().addTool(McpTypedHandlers.adaptTool(typed));
-               var resp = dispatcher.dispatch(new JsonRpcRequest()
+               var resp = dispatch(new JsonRpcRequest()
                        .setJsonrpc(McpProtocol.JSON_RPC_2_0).setId(1)
-                       
.setMethod(McpMethods.TOOLS_CALL).setParams(JsonMap.of("name", "u")), config, 
ctx);
-               assertEquals(McpDispatcher.CODE_INTERNAL_ERROR, 
resp.getError().getCode());
+                       
.setMethod(McpMethods.TOOLS_CALL).setParams(JsonMap.of("name", "u")), config);
+               assertEquals(Mcp20250618Revision.CODE_INTERNAL_ERROR, 
resp.getError().getCode());
        }
 
        @Test
@@ -280,10 +285,10 @@ class McpTypedHandlers_Test {
                };
                var raw = McpTypedHandlers.adaptPrompt(typed);
                var config = new McpServerConfig().addPrompt(raw);
-               var resp = dispatcher.dispatch(new JsonRpcRequest()
+               var resp = dispatch(new JsonRpcRequest()
                        .setJsonrpc(McpProtocol.JSON_RPC_2_0).setId(1)
                        .setMethod(McpMethods.PROMPTS_GET)
-                       .setParams(JsonMap.of("name", "p", "arguments", 
JsonMap.of("message", "hello"))), config, ctx);
+                       .setParams(JsonMap.of("name", "p", "arguments", 
JsonMap.of("message", "hello"))), config);
                var pr = (GetPromptResult) resp.getResult();
                assertString("hello", pr.getDescription());
        }
diff --git 
a/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/main/java/org/apache/juneau/rest/server/mcp/v20250618/Mcp20250618Revision.java
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/main/java/org/apache/juneau/rest/server/mcp/v20250618/Mcp20250618Revision.java
new file mode 100644
index 0000000000..09da0a545c
--- /dev/null
+++ 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/main/java/org/apache/juneau/rest/server/mcp/v20250618/Mcp20250618Revision.java
@@ -0,0 +1,230 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.rest.server.mcp.v20250618;
+
+import static org.apache.juneau.commons.utils.AssertionUtils.*;
+import static org.apache.juneau.commons.utils.Shorts.*;
+import static org.apache.juneau.commons.utils.StringUtils.*;
+
+import java.util.*;
+
+import org.apache.juneau.bean.jsonrpc.*;
+import org.apache.juneau.bean.mcp.v20250618.*;
+import org.apache.juneau.commons.inject.*;
+import org.apache.juneau.marshall.collections.*;
+import org.apache.juneau.rest.server.mcp.*;
+
+/**
+ * {@link McpRevision} implementation for MCP revision {@code 2025-06-18}.
+ *
+ * <p>
+ * Owns this revision's JSON-RPC method table and error-code table. Note the 
error-code table
+ * deliberately reproduces a known-wrong mapping: {@link 
McpErrorKind#UNKNOWN_METHOD},
+ * {@link McpErrorKind#TOOL_NOT_FOUND}, {@link McpErrorKind#PROMPT_NOT_FOUND} 
and
+ * {@link McpErrorKind#RESOURCE_NOT_FOUND} all report {@code -32601}, which is 
only actually correct
+ * for the first. Preserving that is intentional — the corrective fix is 
tracked separately and is
+ * not part of this re-layering.
+ *
+ * <p>
+ * <b>Constructed per binding, not shared as a singleton (Correction C8).</b> 
The bound servlet or
+ * mixin builds a fresh instance on every {@code revision()} call, passing its 
own
+ * {@code capabilities()} hook result into the constructor — see {@link 
McpRestServlet20250618} and
+ * {@link McpEndpoint20250618}. That is how this revision learns which 
explicit capabilities (if any)
+ * to advertise, with no channel back into core: this class is otherwise 
entirely stateless, so
+ * constructing a new one per call costs nothing.
+ */
+public final class Mcp20250618Revision implements McpRevision {
+
+       private final ServerCapabilities capabilities;
+
+       /**
+        * Constructor.
+        *
+        * @param capabilities Explicit capabilities to advertise on {@code 
initialize}, or <jk>null</jk>
+        *      to auto-derive from the registered tool/prompt/resource lists 
(today's default behavior).
+        */
+       public Mcp20250618Revision(ServerCapabilities capabilities) {
+               this.capabilities = capabilities;
+       }
+
+       /** JSON-RPC error code: parse error. Never reported by this revision 
(see {@link McpErrorKind#PARSE_ERROR}). */
+       public static final int CODE_PARSE_ERROR = -32700;
+
+       /** JSON-RPC error code: invalid request. */
+       public static final int CODE_INVALID_REQUEST = -32600;
+
+       /** JSON-RPC error code: method not found. */
+       public static final int CODE_METHOD_NOT_FOUND = -32601;
+
+       /** JSON-RPC error code: invalid params. */
+       public static final int CODE_INVALID_PARAMS = -32602;
+
+       /** JSON-RPC error code: internal error. */
+       public static final int CODE_INTERNAL_ERROR = -32603;
+
+       /** Default server name reported by {@code initialize} when the config 
supplies no server identity. */
+       public static final String DEFAULT_SERVER_NAME = 
"juneau-rest-server-mcp";
+
+       @Override /* McpRevision */
+       public String protocolVersion() {
+               return McpProtocol.VERSION_2025_06_18;
+       }
+
+       @Override /* McpRevision */
+       public int errorCode(McpErrorKind kind) {
+               return switch (kind) {
+                       case INVALID_REQUEST -> CODE_INVALID_REQUEST;
+                       case UNKNOWN_METHOD, TOOL_NOT_FOUND, PROMPT_NOT_FOUND, 
RESOURCE_NOT_FOUND -> CODE_METHOD_NOT_FOUND;
+                       case INVALID_PARAMS -> CODE_INVALID_PARAMS;
+                       case INTERNAL_ERROR -> CODE_INTERNAL_ERROR;
+                       case PARSE_ERROR -> CODE_PARSE_ERROR;
+               };
+       }
+
+       @Override /* McpRevision */
+       public JsonRpcResponse dispatch(McpExchange exchange, McpServerConfig 
config, BeanStore ctx) {
+               assertArgNotNull("exchange", exchange);
+               assertArgNotNull("config", config);
+               assertArgNotNull("ctx", ctx);
+
+               var req = exchange.request();
+               if (req == null)
+                       return JsonRpcResponse.errorResponse(null, 
errorCode(McpErrorKind.INVALID_REQUEST), "Request envelope is null");
+
+               var id = req.getId();
+               var method = req.getMethod();
+
+               if (isEmpty(method))
+                       return JsonRpcResponse.notification(id) ? null
+                               : JsonRpcResponse.errorResponse(id, 
errorCode(McpErrorKind.INVALID_REQUEST), "Missing method");
+
+               try {
+                       var result = invoke(method, req.getParams(), config, 
ctx);
+                       return JsonRpcResponse.notification(id) ? null : 
JsonRpcResponse.ok(id, result);
+               } catch (McpException e) {
+                       return JsonRpcResponse.notification(id) ? null : new 
JsonRpcResponse()
+                               .setJsonrpc(McpProtocol.JSON_RPC_2_0)
+                               .setId(id)
+                               .setError(e.toJsonRpcError());
+               } catch (Exception e) {
+                       if (JsonRpcResponse.notification(id))
+                               return null;
+                       var message = e.getMessage() == null ? cns(e) : 
e.getMessage();
+                       return JsonRpcResponse.errorResponse(id, 
errorCode(McpErrorKind.INTERNAL_ERROR), message, JsonMap.of("type", cn(e)));
+               }
+       }
+
+       private Object invoke(String method, Object params, McpServerConfig 
config, BeanStore ctx) {
+               switch (method) {
+                       case McpMethods.INITIALIZE:
+                               return initialize(config);
+                       case McpMethods.PING:
+                               return new JsonMap();
+                       case McpMethods.TOOLS_LIST:
+                               return listTools(config, params, ctx);
+                       case McpMethods.TOOLS_CALL:
+                               return callTool(config, params, ctx);
+                       case McpMethods.PROMPTS_LIST:
+                               return listPrompts(config, params, ctx);
+                       case McpMethods.PROMPTS_GET:
+                               return getPrompt(config, params, ctx);
+                       case McpMethods.RESOURCES_LIST:
+                               return listResources(config, params, ctx);
+                       case McpMethods.RESOURCES_READ:
+                               return readResource(config, params, ctx);
+                       default:
+                               throw new 
McpException(errorCode(McpErrorKind.UNKNOWN_METHOD), "Method not found: " + 
method);
+               }
+       }
+
+       private InitializeResult initialize(McpServerConfig config) {
+               var caps = config.getCapabilities();
+               if (caps == null) {
+                       caps = new ServerCapabilities();
+                       if (! config.getTools().isEmpty())
+                               caps.setTools(new ToolCapability());
+                       if (! config.getPrompts().isEmpty())
+                               caps.setPrompts(new PromptCapability());
+                       if (! config.getResources().isEmpty())
+                               caps.setResources(new ResourceCapability());
+               }
+               var info = config.getServerInfo();
+               if (info == null)
+                       info = new 
Implementation().setName(DEFAULT_SERVER_NAME).setVersion("unknown");
+               return new InitializeResult()
+                       .setProtocolVersion(config.getProtocolVersion())
+                       .setCapabilities(caps)
+                       .setServerInfo(info)
+                       .setInstructions(config.getInstructions());
+       }
+
+       private static ListToolsResult listTools(McpServerConfig config, Object 
params, BeanStore ctx) {
+               var descriptors = 
config.getTools().stream().map(McpToolHandler::descriptor).toList();
+               var page = config.getCursor().page(descriptors, 
McpCursor.cursorOf(params), ctx);
+               return new 
ListToolsResult().setTools(page.items()).setNextCursor(page.nextCursor());
+       }
+
+       private CallToolResult callTool(McpServerConfig config, Object params, 
BeanStore ctx) {
+               var p = McpParamUtils.asMap(params);
+               var name = McpParamUtils.strParam(p, "name");
+               if (name == null)
+                       throw new 
McpException(errorCode(McpErrorKind.INVALID_PARAMS), "Missing tool name");
+               var handler = config.getTools().stream()
+                       .filter(h -> name.equals(h.descriptor().getName()))
+                       .findFirst()
+                       .orElseThrow(() -> new 
McpException(errorCode(McpErrorKind.TOOL_NOT_FOUND), "Tool not found: " + 
name));
+               var args = McpParamUtils.mapParam(p, "arguments");
+               return handler.call(args, ctx);
+       }
+
+       private static ListPromptsResult listPrompts(McpServerConfig config, 
Object params, BeanStore ctx) {
+               var descriptors = 
config.getPrompts().stream().map(McpPromptHandler::descriptor).toList();
+               var page = config.getCursor().page(descriptors, 
McpCursor.cursorOf(params), ctx);
+               return new 
ListPromptsResult().setPrompts(page.items()).setNextCursor(page.nextCursor());
+       }
+
+       private GetPromptResult getPrompt(McpServerConfig config, Object 
params, BeanStore ctx) {
+               var p = McpParamUtils.asMap(params);
+               var name = McpParamUtils.strParam(p, "name");
+               if (name == null)
+                       throw new 
McpException(errorCode(McpErrorKind.INVALID_PARAMS), "Missing prompt name");
+               var handler = config.getPrompts().stream()
+                       .filter(h -> name.equals(h.descriptor().getName()))
+                       .findFirst()
+                       .orElseThrow(() -> new 
McpException(errorCode(McpErrorKind.PROMPT_NOT_FOUND), "Prompt not found: " + 
name));
+               var args = McpParamUtils.mapParam(p, "arguments");
+               return handler.get(args, ctx);
+       }
+
+       private static ListResourcesResult listResources(McpServerConfig 
config, Object params, BeanStore ctx) {
+               var descriptors = 
config.getResources().stream().map(McpResourceHandler::descriptor).toList();
+               var page = config.getCursor().page(descriptors, 
McpCursor.cursorOf(params), ctx);
+               return new 
ListResourcesResult().setResources(page.items()).setNextCursor(page.nextCursor());
+       }
+
+       private ReadResourceResult readResource(McpServerConfig config, Object 
params, BeanStore ctx) {
+               var p = McpParamUtils.asMap(params);
+               var uri = McpParamUtils.strParam(p, "uri");
+               if (uri == null)
+                       throw new 
McpException(errorCode(McpErrorKind.INVALID_PARAMS), "Missing resource uri");
+               var handler = config.getResources().stream()
+                       .filter(h -> uri.equals(h.descriptor().getUri()))
+                       .findFirst()
+                       .orElseThrow(() -> new 
McpException(errorCode(McpErrorKind.RESOURCE_NOT_FOUND), "Resource not found: " 
+ uri));
+               return handler.read(uri, ctx);
+       }
+}
diff --git 
a/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/main/java/org/apache/juneau/rest/server/mcp/v20250618/McpEndpoint20250618.java
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/main/java/org/apache/juneau/rest/server/mcp/v20250618/McpEndpoint20250618.java
new file mode 100644
index 0000000000..bcc0134840
--- /dev/null
+++ 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/main/java/org/apache/juneau/rest/server/mcp/v20250618/McpEndpoint20250618.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.rest.server.mcp.v20250618;
+
+import org.apache.juneau.bean.mcp.v20250618.*;
+import org.apache.juneau.rest.server.mcp.*;
+
+/**
+ * Mixin interface that exposes a {@code 2025-06-18} MCP endpoint at {@code 
POST /mcp} on any Juneau
+ * REST resource.
+ *
+ * <p>
+ * The mixin path and the servlet-subclass path
+ * ({@link McpRestServlet20250618}) are at parity: both bind the revision at 
compile time and both
+ * expose the same {@link #capabilities()} hook.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <pre>
+ * @Rest(path="/api")
+ * public class MyResource extends BasicRestServlet implements 
McpEndpoint20250618 {
+ *     @Override
+ *     public McpServerConfig getMcpConfig() {
+ *         return new McpServerConfig().addTool(new MyEchoTool());
+ *     }
+ * }
+ * </pre>
+ */
+public interface McpEndpoint20250618 extends McpEndpoint {
+
+       @Override /* McpEndpoint */
+       default McpRevision revision() {
+               return new Mcp20250618Revision(capabilities());
+       }
+
+       /**
+        * Optional explicit capabilities advertisement for {@code initialize}.
+        *
+        * <p>
+        * Returning <jk>null</jk> (the default) leaves capabilities 
auto-derived from the registered
+        * tool / prompt / resource lists; a non-<jk>null</jk> value bypasses 
auto-derivation.
+        *
+        * <p>
+        * Stays a {@code default} interface method (Correction C8), for the 
same reason
+        * {@link McpRestServlet20250618#capabilities()} stays {@code 
protected}: only {@link #revision()}
+        * above, declared on this same interface, ever calls it.
+        *
+        * @return The explicit capabilities, or <jk>null</jk> to auto-derive.
+        */
+       default ServerCapabilities capabilities() {
+               return null;
+       }
+}
diff --git 
a/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/main/java/org/apache/juneau/rest/server/mcp/v20250618/McpRestServlet20250618.java
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/main/java/org/apache/juneau/rest/server/mcp/v20250618/McpRestServlet20250618.java
new file mode 100644
index 0000000000..ab83cfbfee
--- /dev/null
+++ 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/main/java/org/apache/juneau/rest/server/mcp/v20250618/McpRestServlet20250618.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.rest.server.mcp.v20250618;
+
+import org.apache.juneau.bean.mcp.v20250618.*;
+import org.apache.juneau.rest.server.mcp.*;
+
+/**
+ * Abstract MCP servlet bound to protocol revision {@code 2025-06-18}.
+ *
+ * <p>
+ * Subclass this (rather than {@link McpRestServlet}) to expose a {@code 
2025-06-18} endpoint; the
+ * revision binding is a compile-time choice made by which class you extend.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <pre>
+ * @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());
+ *     }
+ * }
+ * </pre>
+ *
+ * @serial exclude
+ */
+public abstract class McpRestServlet20250618 extends McpRestServlet {
+       private static final long serialVersionUID = 1L;
+
+       @Override /* McpRestServlet */
+       protected McpRevision revision() {
+               return new Mcp20250618Revision(capabilities());
+       }
+
+       /**
+        * Optional explicit capabilities advertisement for {@code initialize}.
+        *
+        * <p>
+        * Returning <jk>null</jk> (the default) leaves capabilities 
auto-derived from the registered
+        * tool / prompt / resource lists. Returning a non-<jk>null</jk> value 
bypasses auto-derivation
+        * entirely and advertises exactly what is returned — the way to 
advertise
+        * {@code resources.subscribe}, {@code logging}, {@code listChanged}, 
or {@code experimental},
+        * none of which are derivable from a handler registry.
+        *
+        * <p>
+        * This hook is typed against this revision's wire beans on purpose: 
capabilities diverge between
+        * MCP revisions, so a neutral carrier in the core would accept data 
that is nonsense for the
+        * bound revision and fail only at serialization time.
+        *
+        * <p>
+        * Stays {@code protected} (Correction C8): {@link #revision()} above 
is the only caller, and it
+        * lives in this same class, so there is never a need to reach this 
hook from outside this
+        * class's own hierarchy — in particular, {@code Mcp20250618Revision} 
never calls back into it.
+        *
+        * @return The explicit capabilities, or <jk>null</jk> to auto-derive.
+        */
+       protected ServerCapabilities capabilities() {
+               return null;
+       }
+}
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpTypedHandlers.java
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/main/java/org/apache/juneau/rest/server/mcp/v20250618/McpTypedHandlers.java
similarity index 91%
rename from 
juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpTypedHandlers.java
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/main/java/org/apache/juneau/rest/server/mcp/v20250618/McpTypedHandlers.java
index 82146efe5d..1d24b73a35 100644
--- 
a/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpTypedHandlers.java
+++ 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/main/java/org/apache/juneau/rest/server/mcp/v20250618/McpTypedHandlers.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.juneau.rest.server.mcp;
+package org.apache.juneau.rest.server.mcp.v20250618;
 
 import static org.apache.juneau.commons.utils.AssertionUtils.*;
 
@@ -24,10 +24,11 @@ import org.apache.juneau.bean.jsonrpc.*;
 import org.apache.juneau.bean.mcp.v20250618.*;
 import org.apache.juneau.commons.inject.*;
 import org.apache.juneau.marshall.marshaller.*;
+import org.apache.juneau.rest.server.mcp.*;
 
 /**
  * Adapters that convert {@link McpTypedToolHandler} / {@link 
McpTypedPromptHandler} into the raw
- * {@link McpToolHandler} / {@link McpPromptHandler} interfaces consumed by 
{@link McpDispatcher}.
+ * {@link McpToolHandler} / {@link McpPromptHandler} interfaces consumed by 
{@link Mcp20250618Revision}.
  */
 public final class McpTypedHandlers {
 
@@ -102,7 +103,7 @@ public final class McpTypedHandlers {
                        var json = Json.of(arguments);
                        return Json.to(json, type);
                } catch (Exception e) {
-                       throw new 
McpException(McpDispatcher.CODE_INVALID_PARAMS, "Failed to bind arguments to " 
+ type.getName() + ": " + e.getMessage());
+                       throw new 
McpException(Mcp20250618Revision.CODE_INVALID_PARAMS, "Failed to bind arguments 
to " + type.getName() + ": " + e.getMessage());
                }
        }
 
@@ -118,7 +119,7 @@ public final class McpTypedHandlers {
                        try {
                                text = Json.of(result);
                        } catch (Exception e) {
-                               throw new 
McpException(McpDispatcher.CODE_INTERNAL_ERROR, "Failed to serialize tool 
result: " + e.getMessage());
+                               throw new 
McpException(Mcp20250618Revision.CODE_INTERNAL_ERROR, "Failed to serialize tool 
result: " + e.getMessage());
                        }
                }
                return new CallToolResult().setContent(List.of(new 
TextContent().setText(text)));
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpTypedPromptHandler.java
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/main/java/org/apache/juneau/rest/server/mcp/v20250618/McpTypedPromptHandler.java
similarity index 94%
copy from 
juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpTypedPromptHandler.java
copy to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/main/java/org/apache/juneau/rest/server/mcp/v20250618/McpTypedPromptHandler.java
index 524737d38a..ff1686f7c7 100644
--- 
a/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpTypedPromptHandler.java
+++ 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/main/java/org/apache/juneau/rest/server/mcp/v20250618/McpTypedPromptHandler.java
@@ -14,10 +14,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.juneau.rest.server.mcp;
+package org.apache.juneau.rest.server.mcp.v20250618;
 
 import org.apache.juneau.bean.mcp.v20250618.*;
 import org.apache.juneau.commons.inject.*;
+import org.apache.juneau.rest.server.mcp.*;
 
 /**
  * Typed variant of {@link McpPromptHandler} where MCP {@code prompts/get} 
arguments bind into a Juneau bean.
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpTypedToolHandler.java
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/main/java/org/apache/juneau/rest/server/mcp/v20250618/McpTypedToolHandler.java
similarity index 95%
rename from 
juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpTypedToolHandler.java
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/main/java/org/apache/juneau/rest/server/mcp/v20250618/McpTypedToolHandler.java
index 6db25a864d..23e4f69d77 100644
--- 
a/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpTypedToolHandler.java
+++ 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/main/java/org/apache/juneau/rest/server/mcp/v20250618/McpTypedToolHandler.java
@@ -14,10 +14,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.juneau.rest.server.mcp;
+package org.apache.juneau.rest.server.mcp.v20250618;
 
 import org.apache.juneau.bean.mcp.v20250618.*;
 import org.apache.juneau.commons.inject.*;
+import org.apache.juneau.rest.server.mcp.*;
 
 /**
  * Typed variant of {@link McpToolHandler} where MCP {@code tools/call} 
arguments bind into a Juneau bean.
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/java/org/apache/juneau/rest/server/mcp/Characterization_Test.java
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/java/org/apache/juneau/rest/server/mcp/v20250618/Characterization_Test.java
similarity index 94%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/java/org/apache/juneau/rest/server/mcp/Characterization_Test.java
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/java/org/apache/juneau/rest/server/mcp/v20250618/Characterization_Test.java
index 7b5a597526..207f4ed696 100644
--- 
a/juneau-rest/juneau-rest-server-mcp/src/test/java/org/apache/juneau/rest/server/mcp/Characterization_Test.java
+++ 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/java/org/apache/juneau/rest/server/mcp/v20250618/Characterization_Test.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.juneau.rest.server.mcp;
+package org.apache.juneau.rest.server.mcp.v20250618;
 
 import static org.junit.jupiter.api.Assertions.*;
 
@@ -29,6 +29,7 @@ import org.apache.juneau.marshall.collections.*;
 import org.apache.juneau.marshall.json.*;
 import org.apache.juneau.rest.mock.classic.*;
 import org.apache.juneau.rest.server.*;
+import org.apache.juneau.rest.server.mcp.*;
 import org.junit.jupiter.params.*;
 import org.junit.jupiter.params.provider.*;
 
@@ -44,7 +45,7 @@ import org.junit.jupiter.params.provider.*;
  * <p>
  * Regenerate the {@code *.response.json} files (only ever against known-good 
code) with:
  * <p>
- * {@code mvn test -Drat.skip=true -pl juneau-rest/juneau-rest-server-mcp 
-Dtest=Characterization_Test -Djuneau.mcp.characterization.write=true}
+ * {@code mvn test -Drat.skip=true -pl 
juneau-rest/juneau-rest-server-mcp-2025-06-18 -Dtest=Characterization_Test 
-Djuneau.mcp.characterization.write=true}
  */
 @SuppressWarnings({
        "resource" // MockRestClient is a Closeable test helper; lifetime is 
bounded by the test method.
@@ -57,13 +58,13 @@ class Characterization_Test {
        // --- fixture servlets 
-------------------------------------------------------------------
 
        @Rest(serializers = JsonSerializer.class, parsers = JsonParser.class, 
defaultAccept = "application/json")
-       public static class F_Empty extends McpRestServlet {
+       public static class F_Empty extends McpRestServlet20250618 {
                private static final long serialVersionUID = 1L;
                @Override protected McpServerConfig createMcpConfig() { return 
new McpServerConfig(); }
        }
 
        @Rest(serializers = JsonSerializer.class, parsers = JsonParser.class, 
defaultAccept = "application/json")
-       public static class F_Full extends McpRestServlet {
+       public static class F_Full extends McpRestServlet20250618 {
                private static final long serialVersionUID = 1L;
                @Override protected McpServerConfig createMcpConfig() {
                        return new McpServerConfig()
@@ -86,7 +87,7 @@ class Characterization_Test {
        }
 
        @Rest(serializers = JsonSerializer.class, parsers = JsonParser.class, 
defaultAccept = "application/json")
-       public static class F_Caps extends McpRestServlet {
+       public static class F_Caps extends McpRestServlet20250618 {
                private static final long serialVersionUID = 1L;
                @Override protected McpServerConfig createMcpConfig() {
                        return new McpServerConfig()
@@ -98,7 +99,7 @@ class Characterization_Test {
        }
 
        @Rest(serializers = JsonSerializer.class, parsers = JsonParser.class, 
defaultAccept = "application/json")
-       public static class F_Paged extends McpRestServlet {
+       public static class F_Paged extends McpRestServlet20250618 {
                private static final long serialVersionUID = 1L;
                @Override protected McpServerConfig createMcpConfig() {
                        return new McpServerConfig()
@@ -109,7 +110,7 @@ class Characterization_Test {
        }
 
        @Rest(serializers = JsonSerializer.class, parsers = JsonParser.class, 
defaultAccept = "application/json")
-       public static class F_Throw extends McpRestServlet {
+       public static class F_Throw extends McpRestServlet20250618 {
                private static final long serialVersionUID = 1L;
                @Override protected McpServerConfig createMcpConfig() {
                        return new McpServerConfig()
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/java/org/apache/juneau/rest/server/mcp/McpDispatcher_Test.java
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/java/org/apache/juneau/rest/server/mcp/v20250618/Mcp20250618Revision_Test.java
similarity index 82%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/java/org/apache/juneau/rest/server/mcp/McpDispatcher_Test.java
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/java/org/apache/juneau/rest/server/mcp/v20250618/Mcp20250618Revision_Test.java
index 0120bc06ab..fda7de737f 100644
--- 
a/juneau-rest/juneau-rest-server-mcp/src/test/java/org/apache/juneau/rest/server/mcp/McpDispatcher_Test.java
+++ 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/java/org/apache/juneau/rest/server/mcp/v20250618/Mcp20250618Revision_Test.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.juneau.rest.server.mcp;
+package org.apache.juneau.rest.server.mcp.v20250618;
 
 import static org.apache.juneau.test.bct.BctAssertions.*;
 import static org.junit.jupiter.api.Assertions.*;
@@ -25,14 +25,14 @@ import org.apache.juneau.bean.jsonrpc.*;
 import org.apache.juneau.bean.mcp.v20250618.*;
 import org.apache.juneau.commons.inject.*;
 import org.apache.juneau.marshall.collections.*;
+import org.apache.juneau.rest.server.mcp.*;
 import org.junit.jupiter.api.*;
 
 /**
- * Coverage for {@link McpDispatcher}.
+ * Coverage for {@link Mcp20250618Revision}.
  */
-class McpDispatcher_Test {
+class Mcp20250618Revision_Test {
 
-       private final McpDispatcher dispatcher = new McpDispatcher();
        private final BeanStore ctx = new BasicBeanStore();
 
        private static McpToolHandler tool(String name, 
java.util.function.Function<Map<String,Object>,CallToolResult> fn) {
@@ -82,7 +82,7 @@ class McpDispatcher_Test {
        }
 
        private JsonRpcResponse send(McpServerConfig config, JsonRpcRequest r) {
-               return dispatcher.dispatch(r, config, ctx);
+               return new Mcp20250618Revision(null).dispatch(new 
McpExchange(r, n -> null), config, ctx);
        }
 
        @Test
@@ -99,7 +99,7 @@ class McpDispatcher_Test {
                assertNotNull(result.getCapabilities().getTools());
                assertNotNull(result.getCapabilities().getPrompts());
                assertNotNull(result.getCapabilities().getResources());
-               assertString(McpDispatcher.DEFAULT_SERVER_NAME, 
result.getServerInfo().getName());
+               assertString(Mcp20250618Revision.DEFAULT_SERVER_NAME, 
result.getServerInfo().getName());
        }
 
        @Test
@@ -123,19 +123,19 @@ class McpDispatcher_Test {
        @Test
        void a04_method_notFound() {
                var resp = send(new McpServerConfig(), req(1, "no/such/method", 
null));
-               assertEquals(McpDispatcher.CODE_METHOD_NOT_FOUND, 
resp.getError().getCode());
+               assertEquals(Mcp20250618Revision.CODE_METHOD_NOT_FOUND, 
resp.getError().getCode());
        }
 
        @Test
        void a05_missing_method() {
                var resp = send(new McpServerConfig(), req(1, null, null));
-               assertEquals(McpDispatcher.CODE_INVALID_REQUEST, 
resp.getError().getCode());
+               assertEquals(Mcp20250618Revision.CODE_INVALID_REQUEST, 
resp.getError().getCode());
        }
 
        @Test
        void a06_empty_method_string() {
                var resp = send(new McpServerConfig(), req(1, "", null));
-               assertEquals(McpDispatcher.CODE_INVALID_REQUEST, 
resp.getError().getCode());
+               assertEquals(Mcp20250618Revision.CODE_INVALID_REQUEST, 
resp.getError().getCode());
        }
 
        @Test
@@ -183,8 +183,8 @@ class McpDispatcher_Test {
 
        @Test
        void a08_nullEnvelope_returnsInvalidRequest() {
-               var resp = dispatcher.dispatch(null, new McpServerConfig(), 
ctx);
-               assertEquals(McpDispatcher.CODE_INVALID_REQUEST, 
resp.getError().getCode());
+               var resp = new Mcp20250618Revision(null).dispatch(new 
McpExchange(null, n -> null), new McpServerConfig(), ctx);
+               assertEquals(Mcp20250618Revision.CODE_INVALID_REQUEST, 
resp.getError().getCode());
        }
 
        // -------- tools/list ---------
@@ -230,27 +230,27 @@ class McpDispatcher_Test {
        @Test
        void d02_tools_call_missingName_invalidParams() {
                var resp = send(new McpServerConfig(), req(1, 
McpMethods.TOOLS_CALL, JsonMap.of("arguments", JsonMap.of())));
-               assertEquals(McpDispatcher.CODE_INVALID_PARAMS, 
resp.getError().getCode());
+               assertEquals(Mcp20250618Revision.CODE_INVALID_PARAMS, 
resp.getError().getCode());
        }
 
        @Test
        void d03_tools_call_unknownTool_methodNotFound() {
                var resp = send(new McpServerConfig(), req(1, 
McpMethods.TOOLS_CALL, JsonMap.of("name", "missing")));
-               assertEquals(McpDispatcher.CODE_METHOD_NOT_FOUND, 
resp.getError().getCode());
+               assertEquals(Mcp20250618Revision.CODE_METHOD_NOT_FOUND, 
resp.getError().getCode());
        }
 
        @Test
        void d04_tools_call_argumentsNotObject_throwsInvalidParams() {
                var config = new McpServerConfig().addTool(tool("e", a -> new 
CallToolResult()));
                var resp = send(config, req(1, McpMethods.TOOLS_CALL, 
JsonMap.of("name", "e", "arguments", "string-not-map")));
-               assertEquals(McpDispatcher.CODE_INVALID_PARAMS, 
resp.getError().getCode());
+               assertEquals(Mcp20250618Revision.CODE_INVALID_PARAMS, 
resp.getError().getCode());
        }
 
        @Test
        void d05_tools_call_paramsNotMap_invalidParams() {
                var config = new McpServerConfig().addTool(tool("e", a -> new 
CallToolResult()));
                var resp = send(config, req(1, McpMethods.TOOLS_CALL, 
"not-a-map"));
-               assertEquals(McpDispatcher.CODE_INVALID_PARAMS, 
resp.getError().getCode());
+               assertEquals(Mcp20250618Revision.CODE_INVALID_PARAMS, 
resp.getError().getCode());
        }
 
        @Test
@@ -270,7 +270,7 @@ class McpDispatcher_Test {
                        throw new RuntimeException("boom");
                }));
                var resp = send(config, req(1, McpMethods.TOOLS_CALL, 
JsonMap.of("name", "e")));
-               assertEquals(McpDispatcher.CODE_INTERNAL_ERROR, 
resp.getError().getCode());
+               assertEquals(Mcp20250618Revision.CODE_INTERNAL_ERROR, 
resp.getError().getCode());
                assertString("boom", resp.getError().getMessage());
        }
 
@@ -280,7 +280,7 @@ class McpDispatcher_Test {
                        throw new IllegalStateException();
                }));
                var resp = send(config, req(1, McpMethods.TOOLS_CALL, 
JsonMap.of("name", "e")));
-               assertEquals(McpDispatcher.CODE_INTERNAL_ERROR, 
resp.getError().getCode());
+               assertEquals(Mcp20250618Revision.CODE_INTERNAL_ERROR, 
resp.getError().getCode());
                assertString("IllegalStateException", 
resp.getError().getMessage());
        }
 
@@ -298,13 +298,13 @@ class McpDispatcher_Test {
        @Test
        void e02_prompts_get_missingName_invalidParams() {
                var resp = send(new McpServerConfig(), req(1, 
McpMethods.PROMPTS_GET, JsonMap.of()));
-               assertEquals(McpDispatcher.CODE_INVALID_PARAMS, 
resp.getError().getCode());
+               assertEquals(Mcp20250618Revision.CODE_INVALID_PARAMS, 
resp.getError().getCode());
        }
 
        @Test
        void e03_prompts_get_unknown_methodNotFound() {
                var resp = send(new McpServerConfig(), req(1, 
McpMethods.PROMPTS_GET, JsonMap.of("name", "missing")));
-               assertEquals(McpDispatcher.CODE_METHOD_NOT_FOUND, 
resp.getError().getCode());
+               assertEquals(Mcp20250618Revision.CODE_METHOD_NOT_FOUND, 
resp.getError().getCode());
        }
 
        // -------- resources ---------
@@ -321,13 +321,13 @@ class McpDispatcher_Test {
        @Test
        void f02_resources_read_missingUri_invalidParams() {
                var resp = send(new McpServerConfig(), req(1, 
McpMethods.RESOURCES_READ, JsonMap.of()));
-               assertEquals(McpDispatcher.CODE_INVALID_PARAMS, 
resp.getError().getCode());
+               assertEquals(Mcp20250618Revision.CODE_INVALID_PARAMS, 
resp.getError().getCode());
        }
 
        @Test
        void f03_resources_read_unknown_methodNotFound() {
                var resp = send(new McpServerConfig(), req(1, 
McpMethods.RESOURCES_READ, JsonMap.of("uri", "ghost://")));
-               assertEquals(McpDispatcher.CODE_METHOD_NOT_FOUND, 
resp.getError().getCode());
+               assertEquals(Mcp20250618Revision.CODE_METHOD_NOT_FOUND, 
resp.getError().getCode());
        }
 
        // -------- pagination cursor passthrough ---------
@@ -341,12 +341,19 @@ class McpDispatcher_Test {
                assertString("1", resp.getNextCursor());
        }
 
-       // -------- Mcp facade ---------
+       // -------- error code table ---------
 
        @Test
-       void h01_facade_dispatches() {
-               var config = new McpServerConfig();
-               var resp = Mcp.handle(req(1, McpMethods.PING, null), config, 
ctx);
-               assertNotNull(resp);
+       void h01_errorCode_tableIsComplete() {
+               var a = new Mcp20250618Revision(null);
+               assertEquals(-32600, a.errorCode(McpErrorKind.INVALID_REQUEST));
+               assertEquals(-32601, a.errorCode(McpErrorKind.UNKNOWN_METHOD));
+               assertEquals(-32601, a.errorCode(McpErrorKind.TOOL_NOT_FOUND), 
"known-wrong mapping, preserved deliberately");
+               assertEquals(-32601, 
a.errorCode(McpErrorKind.PROMPT_NOT_FOUND), "known-wrong mapping, preserved 
deliberately");
+               assertEquals(-32601, 
a.errorCode(McpErrorKind.RESOURCE_NOT_FOUND), "known-wrong mapping, preserved 
deliberately");
+               assertEquals(-32602, a.errorCode(McpErrorKind.INVALID_PARAMS));
+               assertEquals(-32603, a.errorCode(McpErrorKind.INTERNAL_ERROR));
+               assertEquals(-32700, a.errorCode(McpErrorKind.PARSE_ERROR));
+               assertEquals("2025-06-18", a.protocolVersion());
        }
 }
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/java/org/apache/juneau/rest/server/mcp/McpHandlerDefaults_Test.java
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/java/org/apache/juneau/rest/server/mcp/v20250618/McpHandlerDefaults_Test.java
similarity index 75%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/java/org/apache/juneau/rest/server/mcp/McpHandlerDefaults_Test.java
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/java/org/apache/juneau/rest/server/mcp/v20250618/McpHandlerDefaults_Test.java
index 5a623eda45..dbbb8e883e 100644
--- 
a/juneau-rest/juneau-rest-server-mcp/src/test/java/org/apache/juneau/rest/server/mcp/McpHandlerDefaults_Test.java
+++ 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/java/org/apache/juneau/rest/server/mcp/v20250618/McpHandlerDefaults_Test.java
@@ -14,13 +14,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.juneau.rest.server.mcp;
+package org.apache.juneau.rest.server.mcp.v20250618;
 
 import static org.apache.juneau.test.bct.BctAssertions.*;
 import static org.junit.jupiter.api.Assertions.*;
 
 import org.apache.juneau.bean.mcp.v20250618.*;
 import org.apache.juneau.commons.inject.*;
+import org.apache.juneau.rest.server.mcp.*;
 import org.junit.jupiter.api.*;
 
 /**
@@ -57,29 +58,4 @@ class McpHandlerDefaults_Test {
                        assertNotNull(ctor.newInstance());
                });
        }
-
-       @Test
-       void c02_mcp_facade_constructor_isPrivate() {
-               assertDoesNotThrow(() -> {
-                       var ctor = Mcp.class.getDeclaredConstructor();
-                       ctor.setAccessible(true);
-                       assertNotNull(ctor.newInstance());
-               });
-       }
-
-       @Test
-       void d01_cursor_passes_ctx_to_strategy() {
-               // Verify ctx parameter reaches the cursor (covers the 
BeanStore parameter passthrough).
-               var bs = new BasicBeanStore();
-               var got = new Object[1];
-               McpCursor c = new McpCursor() {
-                       @Override
-                       public <T> McpPage<T> page(java.util.List<T> all, 
String cursor, BeanStore ctx) {
-                               got[0] = ctx;
-                               return new McpPage<>(all, null);
-                       }
-               };
-               c.page(java.util.List.of(), null, bs);
-               assertSame(bs, got[0]);
-       }
 }
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/java/org/apache/juneau/rest/server/mcp/McpRestServlet_Test.java
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/java/org/apache/juneau/rest/server/mcp/v20250618/McpRestServlet20250618_Test.java
similarity index 93%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/java/org/apache/juneau/rest/server/mcp/McpRestServlet_Test.java
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/java/org/apache/juneau/rest/server/mcp/v20250618/McpRestServlet20250618_Test.java
index a129f1979e..53dd178d03 100644
--- 
a/juneau-rest/juneau-rest-server-mcp/src/test/java/org/apache/juneau/rest/server/mcp/McpRestServlet_Test.java
+++ 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/java/org/apache/juneau/rest/server/mcp/v20250618/McpRestServlet20250618_Test.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.juneau.rest.server.mcp;
+package org.apache.juneau.rest.server.mcp.v20250618;
 
 import static org.apache.juneau.test.bct.BctAssertions.*;
 import static org.junit.jupiter.api.Assertions.*;
@@ -28,15 +28,16 @@ import org.apache.juneau.commons.inject.*;
 import org.apache.juneau.marshall.json.*;
 import org.apache.juneau.rest.mock.classic.*;
 import org.apache.juneau.rest.server.*;
+import org.apache.juneau.rest.server.mcp.*;
 import org.junit.jupiter.api.*;
 
 /**
- * End-to-end coverage for {@link McpRestServlet} via {@link MockRestClient}.
+ * End-to-end coverage for {@link McpRestServlet20250618} via {@link 
MockRestClient}.
  */
 @SuppressWarnings({
        "resource" // Test helpers return Closeables; Eclipse JDT @Owning 
warning is by design.
 })
-class McpRestServlet_Test extends TestBase {
+class McpRestServlet20250618_Test extends TestBase {
 
        private static final JsonParser PAR = JsonParser.create()
                .typePropertyName(Content.class, "type")
@@ -44,7 +45,7 @@ class McpRestServlet_Test extends TestBase {
                .build();
 
        @Rest(serializers = JsonSerializer.class, parsers = JsonParser.class, 
defaultAccept = "application/json")
-       public static class A extends McpRestServlet {
+       public static class A extends McpRestServlet20250618 {
                private static final long serialVersionUID = 1L;
 
                @Override
@@ -115,7 +116,7 @@ class McpRestServlet_Test extends TestBase {
 
        @Rest(path = "/api", serializers = JsonSerializer.class, parsers = 
JsonParser.class, defaultAccept = "application/json")
        @org.apache.juneau.marshall.serializer.SerializerConfig(addBeanTypes = 
"true")
-       public static class B extends 
org.apache.juneau.rest.server.servlet.BasicRestServlet implements McpEndpoint {
+       public static class B extends 
org.apache.juneau.rest.server.servlet.BasicRestServlet implements 
McpEndpoint20250618 {
                private static final long serialVersionUID = 1L;
 
                @Override
@@ -146,7 +147,7 @@ class McpRestServlet_Test extends TestBase {
        // -------- failure modes --------
 
        @Rest(serializers = JsonSerializer.class, parsers = JsonParser.class, 
defaultAccept = "application/json")
-       public static class C extends McpRestServlet {
+       public static class C extends McpRestServlet20250618 {
                private static final long serialVersionUID = 1L;
                @Override
                protected McpServerConfig createMcpConfig() {
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/java/org/apache/juneau/rest/server/mcp/McpServerConfig_Test.java
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/java/org/apache/juneau/rest/server/mcp/v20250618/McpServerConfig_Test.java
similarity index 97%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/java/org/apache/juneau/rest/server/mcp/McpServerConfig_Test.java
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/java/org/apache/juneau/rest/server/mcp/v20250618/McpServerConfig_Test.java
index 57437172fc..6ec4f9fbd6 100644
--- 
a/juneau-rest/juneau-rest-server-mcp/src/test/java/org/apache/juneau/rest/server/mcp/McpServerConfig_Test.java
+++ 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/java/org/apache/juneau/rest/server/mcp/v20250618/McpServerConfig_Test.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.juneau.rest.server.mcp;
+package org.apache.juneau.rest.server.mcp.v20250618;
 
 import static org.apache.juneau.test.bct.BctAssertions.*;
 import static org.junit.jupiter.api.Assertions.*;
@@ -23,6 +23,7 @@ import java.util.*;
 
 import org.apache.juneau.bean.mcp.v20250618.*;
 import org.apache.juneau.commons.inject.*;
+import org.apache.juneau.rest.server.mcp.*;
 import org.junit.jupiter.api.*;
 
 /**
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/java/org/apache/juneau/rest/server/mcp/McpTypedHandlers_Test.java
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/java/org/apache/juneau/rest/server/mcp/v20250618/McpTypedHandlers_Test.java
similarity index 90%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/java/org/apache/juneau/rest/server/mcp/McpTypedHandlers_Test.java
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/java/org/apache/juneau/rest/server/mcp/v20250618/McpTypedHandlers_Test.java
index 50f488eb59..7c5f2c2157 100644
--- 
a/juneau-rest/juneau-rest-server-mcp/src/test/java/org/apache/juneau/rest/server/mcp/McpTypedHandlers_Test.java
+++ 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/java/org/apache/juneau/rest/server/mcp/v20250618/McpTypedHandlers_Test.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.juneau.rest.server.mcp;
+package org.apache.juneau.rest.server.mcp.v20250618;
 
 import static org.apache.juneau.test.bct.BctAssertions.*;
 import static org.junit.jupiter.api.Assertions.*;
@@ -25,6 +25,7 @@ import org.apache.juneau.bean.jsonrpc.*;
 import org.apache.juneau.bean.mcp.v20250618.*;
 import org.apache.juneau.commons.inject.*;
 import org.apache.juneau.marshall.collections.*;
+import org.apache.juneau.rest.server.mcp.*;
 import org.junit.jupiter.api.*;
 
 /**
@@ -50,7 +51,11 @@ class McpTypedHandlers_Test {
        }
 
        private final BeanStore ctx = new BasicBeanStore();
-       private final McpDispatcher dispatcher = new McpDispatcher();
+       private final Mcp20250618Revision revision = new 
Mcp20250618Revision(null);
+
+       private JsonRpcResponse dispatch(JsonRpcRequest req, McpServerConfig 
config) {
+               return revision.dispatch(new McpExchange(req, n -> null), 
config, ctx);
+       }
 
        @Test
        void a01_typedTool_argsBound_andResultWrappedAsText() {
@@ -78,7 +83,7 @@ class McpTypedHandlers_Test {
                        .setId(1)
                        .setMethod(McpMethods.TOOLS_CALL)
                        .setParams(JsonMap.of("name", "echo", "arguments", 
JsonMap.of("message", "hi", "repeat", 3)));
-               var resp = dispatcher.dispatch(req, config, ctx);
+               var resp = dispatch(req, config);
                var ctr = (CallToolResult) resp.getResult();
                var text = ((TextContent) ctr.getContent().get(0)).getText();
                assertContains("\"text\":\"hi:3\"", text);
@@ -109,7 +114,7 @@ class McpTypedHandlers_Test {
                        .setId(1)
                        .setMethod(McpMethods.TOOLS_CALL)
                        .setParams(JsonMap.of("name", "d"));
-               var resp = dispatcher.dispatch(req, config, ctx);
+               var resp = dispatch(req, config);
                assertSame(ctr, resp.getResult());
        }
 
@@ -124,9 +129,9 @@ class McpTypedHandlers_Test {
                        public String call(EchoArgs args, BeanStore ctx) { 
return "hello"; }
                };
                var config = new 
McpServerConfig().addTool(McpTypedHandlers.adaptTool(typed));
-               var resp = dispatcher.dispatch(new JsonRpcRequest()
+               var resp = dispatch(new JsonRpcRequest()
                        .setJsonrpc(McpProtocol.JSON_RPC_2_0).setId(1)
-                       
.setMethod(McpMethods.TOOLS_CALL).setParams(JsonMap.of("name", "s")), config, 
ctx);
+                       
.setMethod(McpMethods.TOOLS_CALL).setParams(JsonMap.of("name", "s")), config);
                var ctr = (CallToolResult) resp.getResult();
                assertString("hello", ((TextContent) 
ctr.getContent().get(0)).getText());
        }
@@ -142,9 +147,9 @@ class McpTypedHandlers_Test {
                        public EchoResult call(EchoArgs args, BeanStore ctx) { 
return null; }
                };
                var config = new 
McpServerConfig().addTool(McpTypedHandlers.adaptTool(typed));
-               var resp = dispatcher.dispatch(new JsonRpcRequest()
+               var resp = dispatch(new JsonRpcRequest()
                        .setJsonrpc(McpProtocol.JSON_RPC_2_0).setId(1)
-                       
.setMethod(McpMethods.TOOLS_CALL).setParams(JsonMap.of("name", "n")), config, 
ctx);
+                       
.setMethod(McpMethods.TOOLS_CALL).setParams(JsonMap.of("name", "n")), config);
                var ctr = (CallToolResult) resp.getResult();
                assertString("", ((TextContent) 
ctr.getContent().get(0)).getText());
        }
@@ -162,9 +167,9 @@ class McpTypedHandlers_Test {
                        }
                };
                var config = new 
McpServerConfig().addTool(McpTypedHandlers.adaptTool(typed));
-               var resp = dispatcher.dispatch(new JsonRpcRequest()
+               var resp = dispatch(new JsonRpcRequest()
                        .setJsonrpc(McpProtocol.JSON_RPC_2_0).setId(1)
-                       
.setMethod(McpMethods.TOOLS_CALL).setParams(JsonMap.of("name", "z")), config, 
ctx);
+                       
.setMethod(McpMethods.TOOLS_CALL).setParams(JsonMap.of("name", "z")), config);
                var ctr = (CallToolResult) resp.getResult();
                assertString("null", ((TextContent) 
ctr.getContent().get(0)).getText());
        }
@@ -181,11 +186,11 @@ class McpTypedHandlers_Test {
                };
                var config = new 
McpServerConfig().addTool(McpTypedHandlers.adaptTool(typed));
                // Bad: 'repeat' should be int, supply a non-numeric value to 
trigger parser failure.
-               var resp = dispatcher.dispatch(new JsonRpcRequest()
+               var resp = dispatch(new JsonRpcRequest()
                        .setJsonrpc(McpProtocol.JSON_RPC_2_0).setId(1)
                        .setMethod(McpMethods.TOOLS_CALL)
-                       .setParams(JsonMap.of("name", "x", "arguments", 
JsonMap.of("repeat", "not-an-int"))), config, ctx);
-               assertEquals(McpDispatcher.CODE_INVALID_PARAMS, 
resp.getError().getCode());
+                       .setParams(JsonMap.of("name", "x", "arguments", 
JsonMap.of("repeat", "not-an-int"))), config);
+               assertEquals(Mcp20250618Revision.CODE_INVALID_PARAMS, 
resp.getError().getCode());
        }
 
        @Test
@@ -202,9 +207,9 @@ class McpTypedHandlers_Test {
                };
                var raw = McpTypedHandlers.adaptPrompt(typed);
                var config = new McpServerConfig().addPrompt(raw);
-               var resp = dispatcher.dispatch(new JsonRpcRequest()
+               var resp = dispatch(new JsonRpcRequest()
                        .setJsonrpc(McpProtocol.JSON_RPC_2_0).setId(1)
-                       
.setMethod(McpMethods.PROMPTS_GET).setParams(JsonMap.of("name", "p")), config, 
ctx);
+                       
.setMethod(McpMethods.PROMPTS_GET).setParams(JsonMap.of("name", "p")), config);
                var pr = (GetPromptResult) resp.getResult();
                assertString("null", pr.getDescription());
        }
@@ -260,10 +265,10 @@ class McpTypedHandlers_Test {
                        public Unserializable call(EchoArgs args, BeanStore 
ctx) { return new Unserializable(); }
                };
                var config = new 
McpServerConfig().addTool(McpTypedHandlers.adaptTool(typed));
-               var resp = dispatcher.dispatch(new JsonRpcRequest()
+               var resp = dispatch(new JsonRpcRequest()
                        .setJsonrpc(McpProtocol.JSON_RPC_2_0).setId(1)
-                       
.setMethod(McpMethods.TOOLS_CALL).setParams(JsonMap.of("name", "u")), config, 
ctx);
-               assertEquals(McpDispatcher.CODE_INTERNAL_ERROR, 
resp.getError().getCode());
+                       
.setMethod(McpMethods.TOOLS_CALL).setParams(JsonMap.of("name", "u")), config);
+               assertEquals(Mcp20250618Revision.CODE_INTERNAL_ERROR, 
resp.getError().getCode());
        }
 
        @Test
@@ -280,10 +285,10 @@ class McpTypedHandlers_Test {
                };
                var raw = McpTypedHandlers.adaptPrompt(typed);
                var config = new McpServerConfig().addPrompt(raw);
-               var resp = dispatcher.dispatch(new JsonRpcRequest()
+               var resp = dispatch(new JsonRpcRequest()
                        .setJsonrpc(McpProtocol.JSON_RPC_2_0).setId(1)
                        .setMethod(McpMethods.PROMPTS_GET)
-                       .setParams(JsonMap.of("name", "p", "arguments", 
JsonMap.of("message", "hello"))), config, ctx);
+                       .setParams(JsonMap.of("name", "p", "arguments", 
JsonMap.of("message", "hello"))), config);
                var pr = (GetPromptResult) resp.getResult();
                assertString("hello", pr.getDescription());
        }
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/CAPS-initialize.request.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/CAPS-initialize.request.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/CAPS-initialize.request.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/CAPS-initialize.request.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/CAPS-initialize.response.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/CAPS-initialize.response.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/CAPS-initialize.response.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/CAPS-initialize.response.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-empty-method.request.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-empty-method.request.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-empty-method.request.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-empty-method.request.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-empty-method.response.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-empty-method.response.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-empty-method.response.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-empty-method.response.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-initialize.request.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-initialize.request.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-initialize.request.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-initialize.request.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-initialize.response.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-initialize.response.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-initialize.response.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-initialize.response.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-missing-method.request.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-missing-method.request.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-missing-method.request.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-missing-method.request.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-missing-method.response.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-missing-method.response.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-missing-method.response.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-missing-method.response.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-notification-ping.request.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-notification-ping.request.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-notification-ping.request.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-notification-ping.request.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-notification-ping.response.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-notification-ping.response.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-notification-ping.response.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-notification-ping.response.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-notification-unknown.request.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-notification-unknown.request.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-notification-unknown.request.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-notification-unknown.request.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-notification-unknown.response.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-notification-unknown.response.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-notification-unknown.response.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-notification-unknown.response.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-ping.request.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-ping.request.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-ping.request.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-ping.request.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-ping.response.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-ping.response.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-ping.response.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-ping.response.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-prompts-get-missing-name.request.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-prompts-get-missing-name.request.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-prompts-get-missing-name.request.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-prompts-get-missing-name.request.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-prompts-get-missing-name.response.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-prompts-get-missing-name.response.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-prompts-get-missing-name.response.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-prompts-get-missing-name.response.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-prompts-get-unknown.request.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-prompts-get-unknown.request.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-prompts-get-unknown.request.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-prompts-get-unknown.request.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-prompts-get-unknown.response.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-prompts-get-unknown.response.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-prompts-get-unknown.response.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-prompts-get-unknown.response.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-prompts-list.request.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-prompts-list.request.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-prompts-list.request.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-prompts-list.request.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-prompts-list.response.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-prompts-list.response.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-prompts-list.response.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-prompts-list.response.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-resources-list.request.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-resources-list.request.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-resources-list.request.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-resources-list.request.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-resources-list.response.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-resources-list.response.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-resources-list.response.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-resources-list.response.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-resources-read-missing-uri.request.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-resources-read-missing-uri.request.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-resources-read-missing-uri.request.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-resources-read-missing-uri.request.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-resources-read-missing-uri.response.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-resources-read-missing-uri.response.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-resources-read-missing-uri.response.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-resources-read-missing-uri.response.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-resources-read-unknown.request.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-resources-read-unknown.request.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-resources-read-unknown.request.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-resources-read-unknown.request.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-resources-read-unknown.response.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-resources-read-unknown.response.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-resources-read-unknown.response.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-resources-read-unknown.response.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-tools-call-missing-name.request.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-tools-call-missing-name.request.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-tools-call-missing-name.request.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-tools-call-missing-name.request.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-tools-call-missing-name.response.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-tools-call-missing-name.response.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-tools-call-missing-name.response.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-tools-call-missing-name.response.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-tools-call-unknown-tool.request.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-tools-call-unknown-tool.request.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-tools-call-unknown-tool.request.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-tools-call-unknown-tool.request.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-tools-call-unknown-tool.response.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-tools-call-unknown-tool.response.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-tools-call-unknown-tool.response.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-tools-call-unknown-tool.response.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-tools-list.request.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-tools-list.request.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-tools-list.request.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-tools-list.request.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-tools-list.response.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-tools-list.response.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-tools-list.response.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-tools-list.response.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-unknown-method.request.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-unknown-method.request.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-unknown-method.request.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-unknown-method.request.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-unknown-method.response.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-unknown-method.response.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/EMPTY-unknown-method.response.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/EMPTY-unknown-method.response.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-initialize.request.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-initialize.request.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-initialize.request.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-initialize.request.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-initialize.response.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-initialize.response.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-initialize.response.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-initialize.response.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-prompts-get.request.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-prompts-get.request.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-prompts-get.request.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-prompts-get.request.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-prompts-get.response.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-prompts-get.response.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-prompts-get.response.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-prompts-get.response.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-prompts-list.request.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-prompts-list.request.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-prompts-list.request.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-prompts-list.request.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-prompts-list.response.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-prompts-list.response.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-prompts-list.response.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-prompts-list.response.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-resources-list.request.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-resources-list.request.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-resources-list.request.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-resources-list.request.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-resources-list.response.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-resources-list.response.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-resources-list.response.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-resources-list.response.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-resources-read.request.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-resources-read.request.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-resources-read.request.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-resources-read.request.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-resources-read.response.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-resources-read.response.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-resources-read.response.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-resources-read.response.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-string-id.request.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-string-id.request.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-string-id.request.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-string-id.request.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-string-id.response.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-string-id.response.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-string-id.response.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-string-id.response.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-tools-call-arguments-not-map.request.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-tools-call-arguments-not-map.request.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-tools-call-arguments-not-map.request.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-tools-call-arguments-not-map.request.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-tools-call-arguments-not-map.response.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-tools-call-arguments-not-map.response.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-tools-call-arguments-not-map.response.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-tools-call-arguments-not-map.response.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-tools-call-error-flag.request.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-tools-call-error-flag.request.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-tools-call-error-flag.request.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-tools-call-error-flag.request.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-tools-call-error-flag.response.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-tools-call-error-flag.response.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-tools-call-error-flag.response.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-tools-call-error-flag.response.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-tools-call-mixed-content.request.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-tools-call-mixed-content.request.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-tools-call-mixed-content.request.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-tools-call-mixed-content.request.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-tools-call-mixed-content.response.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-tools-call-mixed-content.response.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-tools-call-mixed-content.response.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-tools-call-mixed-content.response.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-tools-call-no-arguments.request.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-tools-call-no-arguments.request.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-tools-call-no-arguments.request.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-tools-call-no-arguments.request.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-tools-call-no-arguments.response.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-tools-call-no-arguments.response.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-tools-call-no-arguments.response.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-tools-call-no-arguments.response.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-tools-call-params-not-map.request.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-tools-call-params-not-map.request.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-tools-call-params-not-map.request.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-tools-call-params-not-map.request.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-tools-call-params-not-map.response.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-tools-call-params-not-map.response.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-tools-call-params-not-map.response.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-tools-call-params-not-map.response.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-tools-call-text.request.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-tools-call-text.request.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-tools-call-text.request.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-tools-call-text.request.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-tools-call-text.response.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-tools-call-text.response.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-tools-call-text.response.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-tools-call-text.response.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-tools-list.request.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-tools-list.request.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-tools-list.request.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-tools-list.request.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-tools-list.response.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-tools-list.response.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/FULL-tools-list.response.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/FULL-tools-list.response.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/PAGED-tools-list-first.request.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/PAGED-tools-list-first.request.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/PAGED-tools-list-first.request.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/PAGED-tools-list-first.request.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/PAGED-tools-list-first.response.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/PAGED-tools-list-first.response.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/PAGED-tools-list-first.response.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/PAGED-tools-list-first.response.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/PAGED-tools-list-second.request.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/PAGED-tools-list-second.request.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/PAGED-tools-list-second.request.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/PAGED-tools-list-second.request.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/PAGED-tools-list-second.response.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/PAGED-tools-list-second.response.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/PAGED-tools-list-second.response.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/PAGED-tools-list-second.response.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/THROW-mcp-exception.request.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/THROW-mcp-exception.request.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/THROW-mcp-exception.request.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/THROW-mcp-exception.request.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/THROW-mcp-exception.response.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/THROW-mcp-exception.response.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/THROW-mcp-exception.response.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/THROW-mcp-exception.response.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/THROW-no-message.request.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/THROW-no-message.request.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/THROW-no-message.request.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/THROW-no-message.request.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/THROW-no-message.response.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/THROW-no-message.response.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/THROW-no-message.response.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/THROW-no-message.response.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/THROW-runtime-exception.request.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/THROW-runtime-exception.request.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/THROW-runtime-exception.request.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/THROW-runtime-exception.request.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/THROW-runtime-exception.response.json
 
b/juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/THROW-runtime-exception.response.json
similarity index 100%
rename from 
juneau-rest/juneau-rest-server-mcp/src/test/resources/characterization/THROW-runtime-exception.response.json
rename to 
juneau-rest/juneau-rest-server-mcp-2025-06-18/src/test/resources/characterization/THROW-runtime-exception.response.json
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/Mcp.java
 
b/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/Mcp.java
deleted file mode 100644
index 9c388cd109..0000000000
--- 
a/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/Mcp.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.juneau.rest.server.mcp;
-
-import org.apache.juneau.bean.jsonrpc.*;
-import org.apache.juneau.bean.mcp.v20250618.*;
-import org.apache.juneau.commons.inject.*;
-
-/**
- * Static façade over {@link McpDispatcher} for embedders that want a 
single-line dispatch call.
- *
- * <p>
- * Use this from inside a custom Juneau REST resource that wants to expose an 
MCP endpoint without
- * subclassing {@link McpRestServlet}:
- *
- * <pre>
- * @Rest(path="/api")
- * public class MyResource extends BasicRestServlet {
- *     @RestPost(path="/mcp")
- *     public JsonRpcResponse mcp(JsonRpcRequest req, RestRequest rr) {
- *         return Mcp.handle(req, getMcpConfig(), 
rr.getContext().getBeanStore());
- *     }
- *
- *     private McpServerConfig getMcpConfig() { ... }
- * }
- * </pre>
- */
-public final class Mcp {
-
-       private static final McpDispatcher DISPATCHER = new McpDispatcher();
-
-       private Mcp() {}
-
-       /**
-        * Dispatches a JSON-RPC request through the shared {@link 
McpDispatcher} instance.
-        *
-        * @param req JSON-RPC request envelope. May be {@code null} (treated 
as an invalid request).
-        * @param config Server config. Never {@code null}.
-        * @param ctx Per-request bean store. Never {@code null}.
-        * @return The response, or {@code null} for notification requests.
-        */
-       public static JsonRpcResponse handle(JsonRpcRequest req, 
McpServerConfig config, BeanStore ctx) {
-               return DISPATCHER.dispatch(req, config, ctx);
-       }
-}
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpDispatcher.java
 
b/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpDispatcher.java
deleted file mode 100644
index 20c1a0c363..0000000000
--- 
a/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpDispatcher.java
+++ /dev/null
@@ -1,271 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.juneau.rest.server.mcp;
-
-import static org.apache.juneau.commons.utils.AssertionUtils.*;
-import static org.apache.juneau.commons.utils.Shorts.*;
-import static org.apache.juneau.commons.utils.StringUtils.*;
-
-import java.util.*;
-
-import org.apache.juneau.bean.jsonrpc.*;
-import org.apache.juneau.bean.mcp.v20250618.*;
-import org.apache.juneau.commons.inject.*;
-import org.apache.juneau.marshall.collections.*;
-
-/**
- * Transport-agnostic JSON-RPC dispatcher for the MCP wire protocol.
- *
- * <p>
- * The dispatcher contains <em>zero</em> HTTP plumbing; it accepts a parsed 
{@link JsonRpcRequest},
- * routes by {@code method}, and returns a {@link JsonRpcResponse}. {@link 
McpRestServlet} is a thin
- * adapter that wraps this dispatcher in a Juneau REST {@code @RestPost} 
method, but embedders are
- * free to call {@link #dispatch(JsonRpcRequest, McpServerConfig, BeanStore)} 
directly from
- * tests or from a non-Juneau servlet.
- *
- * <p>
- * Notification requests (those with a {@code null} {@link 
JsonRpcRequest#getId() id}) return
- * {@code null}; HTTP adapters should map this to {@code 204 No Content}.
- */
-public class McpDispatcher {
-
-       /** JSON-RPC error code: parse error. */
-       public static final int CODE_PARSE_ERROR = -32700;
-
-       /** JSON-RPC error code: invalid request. */
-       public static final int CODE_INVALID_REQUEST = -32600;
-
-       /** JSON-RPC error code: method not found. */
-       public static final int CODE_METHOD_NOT_FOUND = -32601;
-
-       /** JSON-RPC error code: invalid params. */
-       public static final int CODE_INVALID_PARAMS = -32602;
-
-       /** JSON-RPC error code: internal error. */
-       public static final int CODE_INTERNAL_ERROR = -32603;
-
-       /**
-        * Default server name used when {@link 
McpServerConfig#getServerInfo()} is {@code null}.
-        */
-       public static final String DEFAULT_SERVER_NAME = 
"juneau-rest-server-mcp";
-
-       /**
-        * Dispatch the supplied JSON-RPC request against the supplied 
configuration.
-        *
-        * @param req JSON-RPC request envelope. Never {@code null}.
-        * @param config Server config (handler registry, capabilities, cursor 
strategy). Never {@code null}.
-        * @param ctx Per-request bean store (passed through to handlers). 
Never {@code null}.
-        * @return The response, or {@code null} for notification requests.
-        */
-       public JsonRpcResponse dispatch(JsonRpcRequest req, McpServerConfig 
config, BeanStore ctx) {
-               assertArgNotNull("config", config);
-               assertArgNotNull("ctx", ctx);
-               if (req == null)
-                       return errorResponse(null, CODE_INVALID_REQUEST, 
"Request envelope is null", null);
-
-               var id = req.getId();
-               var method = req.getMethod();
-
-               if (isEmpty(method))
-                       return notification(id) ? null : errorResponse(id, 
CODE_INVALID_REQUEST, "Missing method", null);
-
-               try {
-                       var result = invoke(method, req.getParams(), config, 
ctx);
-                       return notification(id) ? null : ok(id, result);
-               } catch (McpException e) {
-                       return notification(id) ? null : new JsonRpcResponse()
-                               .setJsonrpc(McpProtocol.JSON_RPC_2_0)
-                               .setId(id)
-                               .setError(e.toJsonRpcError());
-               } catch (Exception e) {
-                       if (notification(id))
-                               return null;
-                       var message = e.getMessage() == null ? cns(e) : 
e.getMessage();
-                       return errorResponse(id, CODE_INTERNAL_ERROR, message, 
JsonMap.of("type", cn(e)));
-               }
-       }
-
-       private static boolean notification(Object id) {
-               return id == null;
-       }
-
-       private Object invoke(String method, Object params, McpServerConfig 
config, BeanStore ctx) {
-               switch (method) {
-                       case McpMethods.INITIALIZE:
-                               return initialize(config);
-                       case McpMethods.PING:
-                               return new JsonMap();
-                       case McpMethods.TOOLS_LIST:
-                               return listTools(config, params, ctx);
-                       case McpMethods.TOOLS_CALL:
-                               return callTool(config, params, ctx);
-                       case McpMethods.PROMPTS_LIST:
-                               return listPrompts(config, params, ctx);
-                       case McpMethods.PROMPTS_GET:
-                               return getPrompt(config, params, ctx);
-                       case McpMethods.RESOURCES_LIST:
-                               return listResources(config, params, ctx);
-                       case McpMethods.RESOURCES_READ:
-                               return readResource(config, params, ctx);
-                       default:
-                               throw new McpException(CODE_METHOD_NOT_FOUND, 
"Method not found: " + method);
-               }
-       }
-
-       // 
-------------------------------------------------------------------------------------------
-       // initialize / capabilities
-       // 
-------------------------------------------------------------------------------------------
-
-       private static InitializeResult initialize(McpServerConfig config) {
-               var caps = config.getCapabilities();
-               if (caps == null) {
-                       caps = new ServerCapabilities();
-                       if (! config.getTools().isEmpty())
-                               caps.setTools(new ToolCapability());
-                       if (! config.getPrompts().isEmpty())
-                               caps.setPrompts(new PromptCapability());
-                       if (! config.getResources().isEmpty())
-                               caps.setResources(new ResourceCapability());
-               }
-               var info = config.getServerInfo();
-               if (info == null)
-                       info = new 
Implementation().setName(DEFAULT_SERVER_NAME).setVersion("unknown");
-               return new InitializeResult()
-                       .setProtocolVersion(config.getProtocolVersion())
-                       .setCapabilities(caps)
-                       .setServerInfo(info)
-                       .setInstructions(config.getInstructions());
-       }
-
-       // 
-------------------------------------------------------------------------------------------
-       // tools
-       // 
-------------------------------------------------------------------------------------------
-
-       private static ListToolsResult listTools(McpServerConfig config, Object 
params, BeanStore ctx) {
-               var descriptors = 
config.getTools().stream().map(McpToolHandler::descriptor).toList();
-               var page = config.getCursor().page(descriptors, 
cursorOf(params), ctx);
-               return new 
ListToolsResult().setTools(page.items()).setNextCursor(page.nextCursor());
-       }
-
-       private static CallToolResult callTool(McpServerConfig config, Object 
params, BeanStore ctx) {
-               var p = asMap(params);
-               var name = strParam(p, "name");
-               if (name == null)
-                       throw new McpException(CODE_INVALID_PARAMS, "Missing 
tool name");
-               var handler = config.getTools().stream()
-                       .filter(h -> name.equals(h.descriptor().getName()))
-                       .findFirst()
-                       .orElseThrow(() -> new 
McpException(CODE_METHOD_NOT_FOUND, "Tool not found: " + name));
-               var args = mapParam(p, "arguments");
-               return handler.call(args, ctx);
-       }
-
-       // 
-------------------------------------------------------------------------------------------
-       // prompts
-       // 
-------------------------------------------------------------------------------------------
-
-       private static ListPromptsResult listPrompts(McpServerConfig config, 
Object params, BeanStore ctx) {
-               var descriptors = 
config.getPrompts().stream().map(McpPromptHandler::descriptor).toList();
-               var page = config.getCursor().page(descriptors, 
cursorOf(params), ctx);
-               return new 
ListPromptsResult().setPrompts(page.items()).setNextCursor(page.nextCursor());
-       }
-
-       private static GetPromptResult getPrompt(McpServerConfig config, Object 
params, BeanStore ctx) {
-               var p = asMap(params);
-               var name = strParam(p, "name");
-               if (name == null)
-                       throw new McpException(CODE_INVALID_PARAMS, "Missing 
prompt name");
-               var handler = config.getPrompts().stream()
-                       .filter(h -> name.equals(h.descriptor().getName()))
-                       .findFirst()
-                       .orElseThrow(() -> new 
McpException(CODE_METHOD_NOT_FOUND, "Prompt not found: " + name));
-               var args = mapParam(p, "arguments");
-               return handler.get(args, ctx);
-       }
-
-       // 
-------------------------------------------------------------------------------------------
-       // resources
-       // 
-------------------------------------------------------------------------------------------
-
-       private static ListResourcesResult listResources(McpServerConfig 
config, Object params, BeanStore ctx) {
-               var descriptors = 
config.getResources().stream().map(McpResourceHandler::descriptor).toList();
-               var page = config.getCursor().page(descriptors, 
cursorOf(params), ctx);
-               return new 
ListResourcesResult().setResources(page.items()).setNextCursor(page.nextCursor());
-       }
-
-       private static ReadResourceResult readResource(McpServerConfig config, 
Object params, BeanStore ctx) {
-               var p = asMap(params);
-               var uri = strParam(p, "uri");
-               if (uri == null)
-                       throw new McpException(CODE_INVALID_PARAMS, "Missing 
resource uri");
-               var handler = config.getResources().stream()
-                       .filter(h -> uri.equals(h.descriptor().getUri()))
-                       .findFirst()
-                       .orElseThrow(() -> new 
McpException(CODE_METHOD_NOT_FOUND, "Resource not found: " + uri));
-               return handler.read(uri, ctx);
-       }
-
-       // 
-------------------------------------------------------------------------------------------
-       // helpers
-       // 
-------------------------------------------------------------------------------------------
-
-       private static String cursorOf(Object params) {
-               return strParam(asMap(params), "cursor");
-       }
-
-       @SuppressWarnings({
-               "unchecked" // Cast is safe: type parameter verified by MCP 
protocol contract.
-       })
-       private static Map<String,Object> asMap(Object params) {
-               if (params == null)
-                       return Map.of();
-               if (params instanceof Map)
-                       return (Map<String,Object>) params;
-               throw new McpException(CODE_INVALID_PARAMS, "Params must be an 
object");
-       }
-
-       private static String strParam(Map<String,Object> p, String key) {
-               var v = p.get(key);
-               return v == null ? null : v.toString();
-       }
-
-       @SuppressWarnings({
-               "unchecked" // Cast is safe: type parameter verified by MCP 
protocol contract.
-       })
-       private static Map<String,Object> mapParam(Map<String,Object> p, String 
key) {
-               var v = p.get(key);
-               if (v == null)
-                       return Map.of();
-               if (v instanceof Map)
-                       return (Map<String,Object>) v;
-               throw new McpException(CODE_INVALID_PARAMS, "Param '" + key + 
"' must be an object");
-       }
-
-       private static JsonRpcResponse ok(Object id, Object result) {
-               return new JsonRpcResponse()
-                       .setJsonrpc(McpProtocol.JSON_RPC_2_0)
-                       .setId(id)
-                       .setResult(result);
-       }
-
-       private static JsonRpcResponse errorResponse(Object id, int code, 
String message, Object data) {
-               return new JsonRpcResponse()
-                       .setJsonrpc(McpProtocol.JSON_RPC_2_0)
-                       .setId(id)
-                       .setError(new 
JsonRpcError().setCode(code).setMessage(message).setData(data));
-       }
-}
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpEndpoint.java
 
b/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpEndpoint.java
index 2302bf5fd3..1df1bd99a6 100644
--- 
a/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpEndpoint.java
+++ 
b/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpEndpoint.java
@@ -17,7 +17,6 @@
 package org.apache.juneau.rest.server.mcp;
 
 import org.apache.juneau.bean.jsonrpc.*;
-import org.apache.juneau.bean.mcp.v20250618.*;
 import org.apache.juneau.commons.inject.*;
 import org.apache.juneau.http.Content;
 import org.apache.juneau.rest.server.*;
@@ -26,14 +25,14 @@ import org.apache.juneau.rest.server.*;
  * Mixin interface that exposes an MCP JSON-RPC endpoint at {@code POST /mcp} 
on any Juneau REST resource.
  *
  * <p>
- * Implementing classes provide their {@link McpServerConfig} by implementing 
{@link #getMcpConfig()}; the
- * default {@link #handleMcpRequest(JsonRpcRequest, RestRequest)} method 
dispatches incoming requests through
- * {@link Mcp#handle(JsonRpcRequest, McpServerConfig, BeanStore)}.
+ * Implementing classes supply their {@link McpServerConfig} via {@link 
#getMcpConfig()} and their
+ * protocol revision via {@link #revision()}. In practice a consumer 
implements a revision-specific
+ * sub-interface (for example {@code McpEndpoint20250618}), which supplies 
{@link #revision()} for them.
  *
  * <h5 class='section'>Example:</h5>
  * <pre>
  * @Rest(path="/api")
- * public class MyResource extends BasicRestServlet implements McpEndpoint {
+ * public class MyResource extends BasicRestServlet implements 
McpEndpoint20250618 {
  *     @Override
  *     public McpServerConfig getMcpConfig() {
  *         return new McpServerConfig().addTool(new MyEchoTool());
@@ -50,12 +49,19 @@ public interface McpEndpoint {
         */
        McpServerConfig getMcpConfig();
 
+       /**
+        * The MCP protocol revision this endpoint speaks.
+        *
+        * @return The bound revision. Never {@code null}.
+        */
+       McpRevision revision();
+
        /**
         * Default MCP JSON-RPC endpoint handler.
         *
         * <p>
-        * Implementations may override this method to customize routing (path 
/ annotations) but must still
-        * call {@link Mcp#handle(JsonRpcRequest, McpServerConfig, BeanStore)} 
to dispatch.
+        * Implementations may override this method to customize routing (path 
/ annotations) but must
+        * still dispatch through {@link #revision()}.
         *
         * @param req JSON-RPC request envelope.
         * @param restReq The current REST request.
@@ -68,6 +74,7 @@ public interface McpEndpoint {
        default JsonRpcResponse handleMcpRequest(@Content JsonRpcRequest req, 
RestRequest restReq) {
                var bs = new BasicBeanStore(restReq.getContext().getBeanStore())
                        .addBean(RestRequest.class, restReq);
-               return Mcp.handle(req, getMcpConfig(), bs);
+               var exchange = new McpExchange(req, n -> 
restReq.getHeaderParam(n).asString().orElse(null));
+               return revision().dispatch(exchange, getMcpConfig(), bs);
        }
 }
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpPromptHandler.java
 
b/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpPromptHandler.java
index 9b91d6a049..e8612f8d8a 100644
--- 
a/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpPromptHandler.java
+++ 
b/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpPromptHandler.java
@@ -35,8 +35,9 @@ public interface McpPromptHandler {
         * Returns the static descriptor for this prompt.
         *
         * <p>
-        * The {@link Prompt#getName() name} value is used by {@link 
McpDispatcher} to route incoming
-        * {@code prompts/get} requests, so each handler in an {@link 
McpServerConfig} must use a unique name.
+        * The {@link Prompt#getName() name} value is used by the bound {@link 
McpRevision} to route
+        * incoming {@code prompts/get} requests, so each handler in an {@link 
McpServerConfig} must use a
+        * unique name.
         *
         * @return The prompt descriptor. Never {@code null}.
         */
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpResourceHandler.java
 
b/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpResourceHandler.java
index 08eed9aeaa..db02fe3e77 100644
--- 
a/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpResourceHandler.java
+++ 
b/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpResourceHandler.java
@@ -33,8 +33,9 @@ public interface McpResourceHandler {
         * Returns the static descriptor for this resource.
         *
         * <p>
-        * The {@link Resource#getUri() uri} value is used by {@link 
McpDispatcher} to route incoming
-        * {@code resources/read} requests, so each handler in an {@link 
McpServerConfig} must use a unique URI.
+        * The {@link Resource#getUri() uri} value is used by the bound {@link 
McpRevision} to route
+        * incoming {@code resources/read} requests, so each handler in an 
{@link McpServerConfig} must use
+        * a unique URI.
         *
         * @return The resource descriptor. Never {@code null}.
         */
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpRestServlet.java
 
b/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpRestServlet.java
index 8414192cd4..3475544b92 100644
--- 
a/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpRestServlet.java
+++ 
b/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpRestServlet.java
@@ -37,7 +37,7 @@ import org.apache.juneau.rest.server.servlet.*;
  * <h5 class='section'>Example:</h5>
  * <pre>
  * @Rest(path="/mcp")
- * public class MyMcpServlet extends McpRestServlet {
+ * public class MyMcpServlet extends McpRestServlet20250618 {
  *     @Override
  *     protected McpServerConfig createMcpConfig() {
  *         return new McpServerConfig()
@@ -48,8 +48,9 @@ import org.apache.juneau.rest.server.servlet.*;
  * </pre>
  *
  * <p>
- * The servlet enables {@code addBeanTypes} on its serializer so {@link 
Content} and {@link ResourceContents}
- * polymorphic types are tagged with their {@code type} discriminator on the 
wire.
+ * The servlet enables {@code addBeanTypes} on its serializer so that 
polymorphic wire types a
+ * revision emits (content blocks, resource payloads) are tagged with their 
{@code type}
+ * discriminator.
  *
  * @serial exclude
  */
@@ -98,6 +99,17 @@ public abstract class McpRestServlet extends 
BasicRestServlet {
         */
        protected abstract McpServerConfig createMcpConfig();
 
+       /**
+        * The MCP protocol revision this servlet speaks.
+        *
+        * <p>
+        * Supplied by the revision-specific subclass a consumer extends (for 
example
+        * {@code McpRestServlet20250618}); consumers do not normally implement 
this themselves.
+        *
+        * @return The bound revision. Never {@code null}.
+        */
+       protected abstract McpRevision revision();
+
        /**
         * MCP JSON-RPC endpoint.
         *
@@ -113,6 +125,7 @@ public abstract class McpRestServlet extends 
BasicRestServlet {
        public JsonRpcResponse handleMcp(@Content JsonRpcRequest req, 
RestRequest restReq) {
                var bs = new BasicBeanStore(restReq.getContext().getBeanStore())
                        .addBean(RestRequest.class, restReq);
-               return Mcp.handle(req, getMcpConfig(), bs);
+               var exchange = new McpExchange(req, n -> 
restReq.getHeaderParam(n).asString().orElse(null));
+               return revision().dispatch(exchange, getMcpConfig(), bs);
        }
 }
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpServerConfig.java
 
b/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpServerConfig.java
index 8ddae0d0ad..da8fd2ac99 100644
--- 
a/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpServerConfig.java
+++ 
b/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpServerConfig.java
@@ -23,7 +23,7 @@ import java.util.*;
 import org.apache.juneau.bean.mcp.v20250618.*;
 
 /**
- * Aggregate configuration consumed by {@link McpDispatcher}.
+ * Aggregate configuration consumed by the bound {@link McpRevision}.
  *
  * <p>
  * Applications register a single {@link McpServerConfig} (typically as a bean 
in their {@code RestContext}
@@ -45,7 +45,7 @@ public class McpServerConfig {
         * Server identity reported in {@code initialize}.
         *
         * <p>
-        * If {@code null}, {@link McpDispatcher} fills in {@code 
"juneau-rest-server-mcp" / "unknown"}.
+        * If {@code null}, the bound {@link McpRevision} fills in a default 
identity.
         *
         * @return The server info, or {@code null} if not set.
         */
@@ -201,7 +201,7 @@ public class McpServerConfig {
         * Optional explicit capabilities advertisement.
         *
         * <p>
-        * When {@code null}, {@link McpDispatcher} synthesizes one from the 
registered handler lists.
+        * When {@code null}, the bound {@link McpRevision} synthesizes one 
from the registered handler lists.
         *
         * @return The override, or {@code null} if auto-derived.
         */
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpToolHandler.java
 
b/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpToolHandler.java
index 3d547976ff..6d0169d020 100644
--- 
a/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpToolHandler.java
+++ 
b/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpToolHandler.java
@@ -39,8 +39,9 @@ public interface McpToolHandler {
         * Returns the static descriptor for this tool.
         *
         * <p>
-        * The {@link Tool#getName() name} value is used by {@link 
McpDispatcher} to route incoming
-        * {@code tools/call} requests, so each handler in an {@link 
McpServerConfig} must use a unique name.
+        * The {@link Tool#getName() name} value is used by the bound {@link 
McpRevision} to route
+        * incoming {@code tools/call} requests, so each handler in an {@link 
McpServerConfig} must use a
+        * unique name.
         *
         * @return The tool descriptor. Never {@code null}.
         */
diff --git 
a/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpTypedPromptHandler.java
 
b/juneau-rest/juneau-rest-server-mcp/src/test/java/org/apache/juneau/rest/server/mcp/McpCursorCtx_Test.java
similarity index 53%
rename from 
juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpTypedPromptHandler.java
rename to 
juneau-rest/juneau-rest-server-mcp/src/test/java/org/apache/juneau/rest/server/mcp/McpCursorCtx_Test.java
index 524737d38a..8df4f8ca2d 100644
--- 
a/juneau-rest/juneau-rest-server-mcp/src/main/java/org/apache/juneau/rest/server/mcp/McpTypedPromptHandler.java
+++ 
b/juneau-rest/juneau-rest-server-mcp/src/test/java/org/apache/juneau/rest/server/mcp/McpCursorCtx_Test.java
@@ -16,36 +16,29 @@
  */
 package org.apache.juneau.rest.server.mcp;
 
-import org.apache.juneau.bean.mcp.v20250618.*;
+import static org.junit.jupiter.api.Assertions.*;
+
 import org.apache.juneau.commons.inject.*;
+import org.junit.jupiter.api.*;
 
 /**
- * Typed variant of {@link McpPromptHandler} where MCP {@code prompts/get} 
arguments bind into a Juneau bean.
- *
- * @param <A> Argument bean type.
+ * Coverage for {@link McpCursor} bean-store passthrough.
  */
-public interface McpTypedPromptHandler<A> {
-
-       /**
-        * Returns the static descriptor for this prompt.
-        *
-        * @return The prompt descriptor. Never {@code null}.
-        */
-       Prompt descriptor();
-
-       /**
-        * Returns the runtime argument class for binding.
-        *
-        * @return The argument class. Never {@code null}.
-        */
-       Class<A> argumentType();
+class McpCursorCtx_Test {
 
-       /**
-        * Renders the prompt.
-        *
-        * @param arguments Bound argument bean (may be {@code null} when no 
arguments are supplied).
-        * @param ctx Per-request bean store.
-        * @return The rendered prompt.
-        */
-       GetPromptResult get(A arguments, BeanStore ctx);
+       @Test
+       void d01_cursor_passes_ctx_to_strategy() {
+               // Verify ctx parameter reaches the cursor (covers the 
BeanStore parameter passthrough).
+               var bs = new BasicBeanStore();
+               var got = new Object[1];
+               McpCursor c = new McpCursor() {
+                       @Override
+                       public <T> McpPage<T> page(java.util.List<T> all, 
String cursor, BeanStore ctx) {
+                               got[0] = ctx;
+                               return new McpPage<>(all, null);
+                       }
+               };
+               c.page(java.util.List.of(), null, bs);
+               assertSame(bs, got[0]);
+       }
 }

Reply via email to