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

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


The following commit(s) were added to refs/heads/docs by this push:
     new 953dc29d7b MCP: document the 2026-07-28 MRTR / input_required surface 
(TODO-318)
953dc29d7b is described below

commit 953dc29d7b4f857673f4c4ebf787ba527b422390
Author: James Bognar <[email protected]>
AuthorDate: Mon Aug 3 09:45:38 2026 -0700

    MCP: document the 2026-07-28 MRTR / input_required surface (TODO-318)
    
    Documents InputRequiredResult, the free-form inputRequests map, 
requestState,
    inputResponses, and the elicitation client capability (no dedicated MRTR 
flag),
    plus the server dispatch surface: throw-to-pause McpInputRequiredSignal, the
    BeanStore-injected resume/capability contexts, RequestStateCodec /
    AeadRequestStateCodec, McpMrtrConfig, the three error codes, and the note 
that
    MRTR is synchronous request/response, not SSE.
    
    Co-authored-by: Cursor <[email protected]>
---
 pages/topics/05.07.JuneauBeanMcp.md    | 31 ++++++++++++++++
 pages/topics/11.JuneauRestServerMcp.md | 64 ++++++++++++++++++++++++++++++++++
 2 files changed, 95 insertions(+)

diff --git a/pages/topics/05.07.JuneauBeanMcp.md 
b/pages/topics/05.07.JuneauBeanMcp.md
index f41750a501..059d6e11a5 100644
--- a/pages/topics/05.07.JuneauBeanMcp.md
+++ b/pages/topics/05.07.JuneauBeanMcp.md
@@ -322,6 +322,37 @@ A representative `tools/call` round trip showing the full 
`_meta` shape on both
 
 Trace-context propagation (the `traceparent`/`tracestate`/`baggage` keys 
above) is a REST-server-adapter behavior, not a bean-level one — see 
[Trace-Context 
Propagation](/docs/topics/JuneauRestServerMcp#trace-context-propagation-mcp-2026-07-28)
 on the `juneau-rest-server-mcp` topic and 
[Observability](/docs/topics/RestServerObservability) for the full mechanism.
 
+## MCP `2026-07-28`: Multi-Round-Trip Requests
+
+`juneau-bean-mcp-v20260728` adds the wire beans for MCP `2026-07-28` SEP-2322 
Multi-Round-Trip Requests: a `tools/call`, `prompts/get`, or `resources/read` 
handler can *pause* to ask the caller for more input, and the caller *resumes* 
by re-sending the same request with its answers. These beans model only the 
wire shape — the server-side pause/resume dispatch, the sealing of 
`requestState`, and capability gating are `juneau-rest-server-mcp-v20260728` 
concerns, covered in [juneau-rest-ser [...]
+
+### Pause result
+
+| Bean | Purpose |
+|---|---|
+| `InputRequiredResult` | A `Result<InputRequiredResult>` subtype whose 
constructor pins `resultType` to `"input_required"`. Carries a 
server-assigned-id-keyed `inputRequests` (`Map<String,JsonMap>`) and/or an 
opaque `requestState` (`String`). Its `validate()` method enforces the 
pinned-schema invariant that **at least one of the two** is present. |
+
+`inputRequests` values are **free-form and lossless**: each entry is a raw 
`JsonMap` sub-request carried to the wire byte-for-byte, with no synthetic 
`{type, payload}` envelope — the concrete sub-request shapes (sampling, 
elicitation) are owned by future consumers and out of scope here. There is 
deliberately **no** dedicated per-entry bean; the map value is a bare 
`JsonMap`, mirroring the equally free-form request-side `inputResponses`. 
`requestState` is opaque ciphertext to a client — m [...]
+
+### Resume fields
+
+`CallToolRequest`, `GetPromptRequest`, and `ReadResourceRequest` each gain two 
optional, nullable fields for the resume leg:
+
+| Field | Type | Purpose |
+|---|---|---|
+| `requestState` | `String` | The opaque token echoed back from a prior 
`InputRequiredResult`, identifying the paused operation to the server. |
+| `inputResponses` | `Map<String,Object>` | The caller's collected answers, 
keyed by the same ids the pause's `inputRequests` used. Free-form, mirroring 
`inputRequests`. |
+
+Both are omitted from the wire entirely when unset, so an ordinary one-shot 
call is byte-identical to before this addition.
+
+### Capability
+
+| Bean | Purpose |
+|---|---|
+| `ElicitationCapability` | An empty marker bean (serializes as `{}`), exposed 
as the new nullable `elicitation` field on `ClientCapabilities` 
(`getElicitation()`/`setElicitation(...)`), matching `RootsCapability`'s 
presence-checkable shape. |
+
+**There is no dedicated `input_required` capability flag.** Gating reuses the 
existing `elicitation` client capability: a client that advertises 
`clientCapabilities.elicitation` is signalling it can handle an 
`input_required` pause. This is a deliberate divergence from a separate 
per-feature flag — the pinned `2026-07-28` schema defines no such flag, so none 
is modelled here.
+
 ## Related Modules
 
 - **[juneau-bean-jsonrpc](/docs/topics/JuneauBeanJsonRpc)** — The 
revision-neutral JSON-RPC 2.0 envelope beans that carry MCP traffic on the wire.
diff --git a/pages/topics/11.JuneauRestServerMcp.md 
b/pages/topics/11.JuneauRestServerMcp.md
index 7c9eb9ca72..eb8105e1b9 100644
--- a/pages/topics/11.JuneauRestServerMcp.md
+++ b/pages/topics/11.JuneauRestServerMcp.md
@@ -625,6 +625,70 @@ Because a dated MCP adapter always answers over HTTP `200` 
— even for a JSON-R
 
 All of the above attribute names and span-naming choices are pinned to a 
specific, **experimental** OpenTelemetry GenAI semantic-conventions snapshot — 
see [Observability — Micrometer + 
OpenTelemetry](/docs/topics/RestServerObservability#mcp-trace-context-propagation-2026-07-28)
 for the exact pin, the full attribute reference, and the neutral seam these 
classes are built on.
 
+## Multi-Round-Trip Requests (MCP `2026-07-28`, SEP-2322)
+
+**v2-only.** The `2026-07-28` adapter 
(`org.apache.juneau.rest.server.mcp.v20260728`) adds server-side support for 
SEP-2322 Multi-Round-Trip Requests: a handler for `tools/call`, `prompts/get`, 
or the **exact-resource** `resources/read` path can *pause* to ask the caller 
for more input, returning an 
[`InputRequiredResult`](/docs/topics/JuneauBeanMcp#mcp-2026-07-28-multi-round-trip-requests);
 the caller *resumes* by re-sending the same request with its answers plus the 
echoed `requestStat [...]
+
+The resource-*template* read path, `completion/complete`, and the 
list/discover/ping paths are **not** MRTR-wired — a pause signal thrown from 
one of them is not caught and surfaces as the `-32603` internal-error 
fail-safe, by design.
+
+### Pausing from a handler
+
+A handler signals a pause by throwing `McpInputRequiredSignal` from its 
ordinary `call`/`get`/`read` body — a **v2-only unchecked exception**, not an 
`McpException` (it becomes a *successful* `input_required` result, not a 
JSON-RPC error). This throw-to-pause mechanism is a v2-adapter addition; the 
Phase 0 design deliberately kept it off the neutral core, so no 
revision-neutral type changed:
+
+```java
+import org.apache.juneau.rest.server.mcp.v20260728.*;
+
+// inside McpToolHandler.call(...)
+throw new McpInputRequiredSignal(
+    Map.of("q1", Map.of("type", "elicitation")),   // inputRequests: at least 
one, free-form sub-requests
+    "my-continuation");                              // opaque, handler-owned 
continuation (may be null)
+```
+
+The dispatcher catches the signal, seals the continuation (plus the method, a 
1-based round counter, and an expiry) into an opaque `requestState`, and 
returns an `InputRequiredResult` carrying the handler's `inputRequests` map and 
that `requestState`.
+
+### Resuming: the two BeanStore-injected handler contexts
+
+On a resume call the same handler runs again; two immutable beans in the 
per-request `BeanStore` let it detect and react to the round:
+
+- **`McpMrtrResumeContext`** — stashed **only** on a resume call. Carries 
`continuation()` (the decoded value, as generic JSON — use 
`continuationAs(Class)` to recover a typed view) and `inputResponses()` (the 
caller's answers). A handler detects resume via 
`ctx.getBean(McpMrtrResumeContext.class)` returning a present `Optional`; 
absent means a first-round call.
+- **`McpMrtrCapabilityContext`** — stashed on **every** in-scope call 
(first-round and resume), exposing `elicitationSupported()` so a handler can 
pre-check and degrade gracefully (return a normal result or a domain error) 
instead of throwing a pause into a client that can't handle it.
+
+Only the sealed `continuation` is tamper-evident across rounds; the per-round 
`arguments` are re-sent by the client and fully client-controlled, so anything 
a handler must trust across rounds belongs *inside* the continuation, never 
re-read from the arguments.
+
+### Sealing `requestState`: the codec SPI
+
+`RequestStateCodec` is the pluggable seal/unseal SPI (mirroring the 
client-side `McpResponseCache` "SPI + built-in default" shape). The built-in 
default, `AeadRequestStateCodec`, is **AES-256-GCM with a per-process ephemeral 
key**: it is deliberately **not restart-durable and not shareable across 
process instances** — a token sealed by one instance can never be unsealed by 
another, or by the same process after a restart. Operators who need 
cross-restart or multi-instance resumption suppl [...]
+
+### Configuring MRTR: `McpMrtrConfig`
+
+`McpMrtrConfig` is the adapter-owned configuration bean, placed exactly like 
`McpCacheConfig` (v2-adapter-only, never on the neutral core). It carries the 
`RequestStateCodec`, a `requestState` TTL (default 5 minutes), and a max-rounds 
cap (default 10). Supply it via the servlet's `createMrtrConfig()` hook (or the 
mixin's `mrtrConfig()`):
+
+```java
+@Override
+protected McpMrtrConfig createMrtrConfig() {
+    return new McpMrtrConfig()
+        .setCodec(new AeadRequestStateCodec())   // or a shared/rotating-key 
implementation
+        .setTtlMs(5 * 60 * 1000L)
+        .setMaxRounds(10);
+}
+```
+
+The config is memoized at binding time and treated as read-only afterward 
(each `AeadRequestStateCodec` holds a distinct random key, so exactly one 
instance must be published per binding).
+
+### Error codes
+
+Alongside the standard JSON-RPC codes, MRTR dispatch adds three v2-only codes 
(constants on `org.apache.juneau.rest.server.mcp.v20260728.McpRevision`):
+
+| Code | Constant | Condition |
+|---|---|---|
+| `-32021` | `CODE_MISSING_REQUIRED_CLIENT_CAPABILITY` | A handler tried to 
pause, but the request's `_meta.clientCapabilities` did not advertise 
`elicitation`. Gating reuses the existing `elicitation` client capability — 
there is no dedicated `input_required` capability flag. |
+| `-32022` | `CODE_REQUEST_STATE_EXPIRED` | The echoed `requestState` unsealed 
successfully but its sealed expiry has passed. |
+| `-32023` | `CODE_MAX_ROUNDS_EXCEEDED` | The resume chain exceeded the 
configured max-rounds cap. |
+
+A `requestState` that fails to unseal — tampered, truncated, sealed for a 
different method, or otherwise invalid — is the standard `-32602` 
invalid-params code (the codec returns empty rather than throwing; the 
dispatcher maps that to `-32602`).
+
+See 
[juneau-bean-mcp-v20260728](/docs/topics/JuneauBeanMcp#mcp-2026-07-28-multi-round-trip-requests)
 for the wire-bean side (`InputRequiredResult`, the 
`inputResponses`/`requestState` request fields, and `ElicitationCapability`).
+
 ## Related Modules
 
 - **[juneau-bean-mcp-v20250618](/docs/topics/JuneauBeanMcp)** — The 
`2025-06-18` wire beans consumed by the adapter module, plus the 
revision-neutral JSON-RPC envelope beans (`juneau-bean-jsonrpc`) both modules 
build on.

Reply via email to