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 328273744b docs: MCP 2026-07-28 sampling + elicitation (TODO-328)
328273744b is described below

commit 328273744b52c4317c9f8387093464333298a5ae
Author: James Bognar <[email protected]>
AuthorDate: Mon Aug 3 20:07:23 2026 -0700

    docs: MCP 2026-07-28 sampling + elicitation (TODO-328)
    
    Document the SEP-2322 elicitation surface (ElicitRequest/ElicitResult/
    ElicitAction wire beans, the ElicitSchema restricted-schema builder, and
    the server/client helpers ElicitationRequests/ElicitationResponses/
    ElicitationAccess) riding the existing MRTR input_required pause/resume
    loop with zero mechanism changes.
    
    Document full-fidelity SEP-2577 sampling support (CreateMessageRequest/
    CreateMessageResult/SamplingMessage/ModelPreferences/ModelHint/
    SamplingCapability, the new AudioContent content type, and the hand-driven
    duplex exchange pattern), plus a note that Juneau does not mark the
    Sampling/Roots/Logging Java types @Deprecated even though SEP-2577
    deprecates them at the protocol level.
    
    Co-authored-by: Cursor <[email protected]>
---
 pages/topics/05.07.JuneauBeanMcp.md       | 53 +++++++++++++++++++++++++++++--
 pages/topics/11.01.JuneauRestClientMcp.md | 25 +++++++++++++++
 pages/topics/11.JuneauRestServerMcp.md    | 29 +++++++++++++++++
 3 files changed, 105 insertions(+), 2 deletions(-)

diff --git a/pages/topics/05.07.JuneauBeanMcp.md 
b/pages/topics/05.07.JuneauBeanMcp.md
index 059d6e11a5..eb1cddd219 100644
--- a/pages/topics/05.07.JuneauBeanMcp.md
+++ b/pages/topics/05.07.JuneauBeanMcp.md
@@ -332,7 +332,7 @@ Trace-context propagation (the 
`traceparent`/`tracestate`/`baggage` keys above)
 |---|---|
 | `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 [...]
+`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 are owned by their 
eventual consumers — elicitation's shape (`ElicitRequest`) is covered below in 
[MCP `2026-07-28`: Elicitation](#mcp-2026-07-28-elicitation); sampling's is a 
separate, fully-supported mechanism (see [MCP Sampling](#mcp-sampling) further 
down). There is deliberatel [...]
 
 ### Resume fields
 
@@ -349,10 +349,59 @@ Both are omitted from the wire entirely when unset, so an 
ordinary one-shot call
 
 | 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. |
+| `ElicitationCapability` | An empty marker bean (serializes as `{}`), exposed 
as the new nullable `elicitation` field on `ClientCapabilities` 
(`getElicitation()`/`setElicitation(...)`), matching `SamplingCapability`'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.
 
+## MCP `2026-07-28`: Elicitation
+
+`juneau-bean-mcp-v20260728` adds the wire beans for MCP `2026-07-28` SEP-2322 
elicitation: a handler can ask the end user a schema-shaped question mid-call, 
riding the same Multi-Round-Trip Requests pause/resume loop described above 
with zero changes to that mechanism. These beans model only the wire shape and 
the schema-building helper — the server-side placement/parsing helpers are a 
`juneau-rest-server-mcp-v20260728` concern, and the client-side typed accessors 
are a `juneau-rest-clie [...]
+
+| Bean | Purpose |
+|---|---|
+| `ElicitRequest` | One question: `message` (the prompt shown to the end user) 
and `requestedSchema` (a restricted JSON Schema `JsonMap` built via 
`ElicitSchema`, below). Placed into an `input_required` pause's `inputRequests` 
map under a server-assigned id. |
+| `ElicitResult` | The end user's answer: `action` (`ElicitAction`) and 
`content` (a dynamic `Map<String,Object>`, present only when `action` is 
`ACCEPT` — its shape is whatever `requestedSchema` described, so it is 
deliberately left untyped). Echoed back keyed by the same id in the resume 
call's `inputResponses`. |
+| `ElicitAction` | A closed three-value wire enum: `accept` / `decline` / 
`cancel`. |
+
+### `ElicitSchema`: the restricted-schema builder
+
+Elicitation's requested-answer schema is deliberately restricted to 
primitive-typed, non-nested top-level properties — no objects, arrays, or 
`$ref`. `ElicitSchema` enforces this by construction: there is no 
`objectField`/`arrayField` method at all, so a nested schema is a compile-time 
impossibility, not a runtime-rejected one.
+
+```java
+JsonMap schema = ElicitSchema.create()
+    
.stringField("email").title("Email").format("email").minLength(3).maxLength(120)
+    .integerField("age").min(0).max(150)
+    .enumField("plan", "free", "pro").enumNames("Free", "Pro")
+    .required("email", "plan")
+    .build();
+// {"type":"object","properties":{...},"required":["email","plan"]}
+```
+
+Each `xxxField(name)` call 
(`stringField`/`numberField`/`integerField`/`booleanField`/`enumField`) starts 
a new property and becomes the target of every subsequent per-field modifier 
(`title`, `description`, `format`, `minLength`/`maxLength` — string fields only 
— `min`/`max`, `defaultValue`, `enumNames`) until the next `xxxField(...)` 
call. `required(String...)` is different: it's a builder-level, not per-field, 
call — like every other modifier it returns `this`, so chaining continues n 
[...]
+
+## MCP Sampling
+
+MCP SEP-2577 deprecates the Sampling capability (along with Roots and Logging) 
at the protocol level in favor of newer interaction patterns — elicitation 
(above) and the Multi-Round-Trip Requests loop it rides are this codebase's 
newer, actively-recommended mechanism for interactive mid-call input. Juneau 
continues to fully support Sampling, though: `juneau-bean-mcp-v20260728` ships 
full-fidelity typed beans for `sampling/createMessage`, since the beans are 
useful today wherever a caller [...]
+
+| Bean | Purpose |
+|---|---|
+| `CreateMessageRequest` | `sampling/createMessage`'s parameters: `messages` 
(`List<SamplingMessage>`), `modelPreferences`, `systemPrompt`, 
`includeContext`, `temperature`, `maxTokens`, `stopSequences`, `metadata`. |
+| `CreateMessageResult` | The result: `role` (`Role`), `content` (`Content`), 
`model`, `stopReason`. |
+| `SamplingMessage` | One conversation turn: `role` (`Role`, reusing the 
existing enum) and `content` (`Content` — a single block, not a list). |
+| `ModelPreferences` | Model-selection hints: `hints` (`List<ModelHint>`), 
`costPriority`/`speedPriority`/`intelligencePriority` (each `Double`, 
`0.0`-`1.0`). |
+| `ModelHint` | A single fuzzy model-family/name hint (`name`). |
+| `SamplingCapability` | An empty marker bean (serializes as `{}`), the typed 
value of `ClientCapabilities.getSampling()`/`setSampling(...)` (replacing the 
prior opaque `Map<String,Object>`). |
+
+A hand-driven sampling exchange needs no framework changes — build a 
`JsonRpcRequest` with `method = McpMethods.SAMPLING_CREATE_MESSAGE` and 
`params` set to a serialized `CreateMessageRequest`, dispatch it via the 
existing `McpDuplexDispatcher`/`McpServerRequestHandler` seam, and decode the 
handler's typed `CreateMessageResult` return value. See 
[juneau-rest-client-mcp](/docs/topics/JuneauRestClientMcp#sse-and-duplex-seam) 
for the duplex mechanism itself, which is entirely unchanged by t [...]
+
+### `Content` gains `AudioContent`
+
+`Content`'s polymorphic dictionary 
(`TextContent`/`ImageContent`/`EmbeddedResourceContent`) gains a fourth member, 
`AudioContent` (`type: "audio"`, `data`/`mimeType`, mirroring `ImageContent`'s 
exact shape) — completing genuine `text`/`image`/`audio`/`resource` fidelity 
for `SamplingMessage.content`/`CreateMessageResult.content` against the real 
MCP sampling schema. Like the other three members of the `Content` dictionary, 
`AudioContent` is general-purpose `Content` vocabulary usable any [...]
+
+### SEP-2577 and the Roots/Logging capabilities
+
+SEP-2577 groups three capabilities together — Sampling, Roots, and Logging — 
but Sampling is the new surface introduced above; only the other two were 
already shipping. `RootsCapability` (and 
`ClientCapabilities.getRoots()`/`setRoots(...)`) and `LoggingCapability` (and 
`ServerCapabilities.getLogging()`/`setLogging(...)`) remain ordinary, 
fully-supported beans — SEP-2577 deprecates these capabilities at the protocol 
level, but Juneau does not mark the corresponding Java types/members `@De [...]
+
 ## 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.01.JuneauRestClientMcp.md 
b/pages/topics/11.01.JuneauRestClientMcp.md
index 7d313a77c4..cbf206afd7 100644
--- a/pages/topics/11.01.JuneauRestClientMcp.md
+++ b/pages/topics/11.01.JuneauRestClientMcp.md
@@ -25,6 +25,31 @@ The duplex loop uses the general-purpose SSE capability on 
`juneau-rest-client`:
 
 Inbound server->client requests are dispatched through 
`McpServerRequestHandler` as raw `JsonRpcRequest` payloads; params remain 
generic `JsonMap`/`Object` and are not typed or rewritten in the seam.
 
+The illustrative server-initiated method name used elsewhere in this 
codebase's Javadoc/tests for this seam is `sampling/createMessage` (see 
[juneau-bean-mcp-v20260728](/docs/topics/JuneauBeanMcp#mcp-sampling)) — 
elicitation, by contrast, resumes over the ordinary Multi-Round-Trip Requests 
HTTP re-call path below, never over this duplex channel.
+
+## Elicitation (MCP `2026-07-28`, SEP-2322)
+
+**v2-only.** `org.apache.juneau.rest.client.mcp.v20260728.ElicitationAccess` 
is a static helper for driving elicitation over `McpClient.callRaw(...)`'s raw 
`Map<String,Object>` result — the typed `callTool`/`getPrompt`/`readResource` 
methods cannot represent a paused `input_required` result or set 
`requestState`/`inputResponses` on a resume call, so a caller must go through 
`callRaw(...)` for the resumable leg of the loop.
+
+```java
+import org.apache.juneau.bean.mcp.v20260728.*;
+import org.apache.juneau.rest.client.mcp.v20260728.*;
+
+var paused = client.callRaw(McpMethods.TOOLS_CALL, new 
CallToolRequest().setName("confirm").setArguments(Map.of()));
+if (ElicitationAccess.isInputRequired(paused)) {
+    var questions = ElicitationAccess.requests(paused);   // Map<String, 
ElicitRequest>
+    // ... show questions.get("confirm").getMessage() to the end user, collect 
an answer ...
+    var answer = new 
ElicitResult().setAction(ElicitAction.ACCEPT).putContent("confirm", true);
+    var completed = client.callRaw(McpMethods.TOOLS_CALL, new 
CallToolRequest().setName("confirm")
+        .setRequestState(ElicitationAccess.requestState(paused))
+        .setInputResponses(ElicitationAccess.toInputResponse("confirm", 
answer)));
+}
+```
+
+`ElicitationAccess` works at the raw map/JSON level rather than exposing typed 
overloads per concrete request bean, because 
`CallToolRequest`/`GetPromptRequest`/`ReadResourceRequest` share no common 
"has-`inputResponses`-and-`requestState`" interface — a caller resuming a 
specific one of the three makes its own final 
`.setInputResponses(...).setRequestState(...)` call on the concrete bean it 
already knows it holds. 
`ElicitationAccess.toInputResponses(Map<String,ElicitResult>)` encodes se [...]
+
+See 
[juneau-rest-server-mcp](/docs/topics/JuneauRestServerMcp#elicitation-mcp-2026-07-28-sep-2322)
 for the server-side helper half of this loop.
+
 ## Auth seam
 
 `McpAuthInterceptor` is the client-side auth seam. It injects `Authorization: 
Bearer ...` from a supplier callback at request-init time. OAuth/OIDC flows 
plug in through the supplier implementation.
diff --git a/pages/topics/11.JuneauRestServerMcp.md 
b/pages/topics/11.JuneauRestServerMcp.md
index eb8105e1b9..af8a7dac2e 100644
--- a/pages/topics/11.JuneauRestServerMcp.md
+++ b/pages/topics/11.JuneauRestServerMcp.md
@@ -689,6 +689,35 @@ A `requestState` that fails to unseal — tampered, 
truncated, sealed for a diff
 
 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`).
 
+## Elicitation (MCP `2026-07-28`, SEP-2322)
+
+**v2-only.** Elicitation rides the Multi-Round-Trip Requests loop above with 
**zero mechanism changes** — no new dispatcher hook, no new capability gate 
(the existing `elicitationSupported()` gate was already elicitation-named, 
since C6 was its intended concrete consumer from the start), no new `BeanStore` 
bean type. Two static helper classes in 
`org.apache.juneau.rest.server.mcp.v20260728` add typed convenience on top of 
the plain `Map<String,Object>` contract `McpInputRequiredSignal`/` [...]
+
+| Helper | Purpose |
+|---|---|
+| `ElicitationRequests` | Builds an `McpInputRequiredSignal` from one or more 
typed 
[`ElicitRequest`](/docs/topics/JuneauBeanMcp#mcp-2026-07-28-elicitation)s, 
serializing each to the `Map`-shaped value the signal's constructor requires. |
+| `ElicitationResponses` | Parses typed 
[`ElicitResult`](/docs/topics/JuneauBeanMcp#mcp-2026-07-28-elicitation)s back 
out of an `McpMrtrResumeContext`'s `inputResponses()`. |
+
+```java
+import org.apache.juneau.bean.mcp.v20260728.*;
+import org.apache.juneau.rest.server.mcp.v20260728.*;
+
+// inside McpToolHandler.call(...)
+var resume = ctx.getBean(McpMrtrResumeContext.class);
+if (resume.isEmpty()) {
+    var question = new ElicitRequest()
+        .setMessage("Proceed with deletion?")
+        
.setRequestedSchema(ElicitSchema.create().booleanField("confirm").title("Confirm").build());
+    throw ElicitationRequests.of("confirm", question, "my-continuation");
+}
+var answer = ElicitationResponses.get(resume.get(), "confirm");
+// answer.getAction() is ACCEPT / DECLINE / CANCEL; answer.getContent() is 
populated only on ACCEPT
+```
+
+Both helpers support the multi-question case identically: 
`ElicitationRequests.of(Map<String,ElicitRequest>, continuation)` places 
several questions in one round, and `ElicitationResponses.all(resume.get())` 
returns every answer keyed the same way — no extra round trip is needed to ask 
(or answer) more than one question at once.
+
+See 
[juneau-bean-mcp-v20260728](/docs/topics/JuneauBeanMcp#mcp-2026-07-28-elicitation)
 for `ElicitRequest`/`ElicitResult`/`ElicitAction`/`ElicitSchema`'s wire shapes 
and the `ElicitSchema` builder API in full.
+
 ## 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