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 ef3c6e9d03 docs: MCP example module (juneau-examples-mcp) + v2 
adoption conveniences
ef3c6e9d03 is described below

commit ef3c6e9d035d58e101f0f36a6f525dc961189786
Author: James Bognar <[email protected]>
AuthorDate: Thu Aug 6 04:49:23 2026 -0700

    docs: MCP example module (juneau-examples-mcp) + v2 adoption conveniences
    
    Co-authored-by: Cursor <[email protected]>
---
 artifact-paths.json                       |  2 ++
 pages/release-notes/10.0.0.md             | 27 +++++++++++++++++++++++++++
 pages/topics/11.03.JuneauMcpRecipes.md    | 20 ++++++++++++++++----
 pages/topics/11.04.JuneauRestServerMcp.md |  8 +++++++-
 pages/topics/11.05.JuneauRestClientMcp.md |  4 ++++
 5 files changed, 56 insertions(+), 5 deletions(-)

diff --git a/artifact-paths.json b/artifact-paths.json
index 30f2a7b50b..8a0787658e 100644
--- a/artifact-paths.json
+++ b/artifact-paths.json
@@ -22,6 +22,7 @@
   "juneau-distrib": "juneau-distrib",
   "juneau-examples": "juneau-examples",
   "juneau-examples-core": "juneau-examples/juneau-examples-core/build-overlay",
+  "juneau-examples-mcp": "juneau-examples/juneau-examples-mcp/build-overlay",
   "juneau-integration-tests": "juneau-integration-tests",
   "juneau-marshall": "juneau-core/juneau-marshall",
   "juneau-marshall-rdf": "juneau-core/juneau-marshall-rdf",
@@ -45,6 +46,7 @@
   "juneau-rest-client-classic": "juneau-rest/juneau-rest-client-classic",
   "juneau-rest-client-jetty": "juneau-rest/juneau-rest-client-jetty",
   "juneau-rest-client-mcp": "juneau-rest/juneau-rest-client-mcp",
+  "juneau-rest-client-mcp-auth": "juneau-rest/juneau-rest-client-mcp-auth",
   "juneau-rest-client-mcp-v20250618": 
"juneau-rest/juneau-rest-client-mcp-v20250618",
   "juneau-rest-client-mcp-v20260728": 
"juneau-rest/juneau-rest-client-mcp-v20260728",
   "juneau-rest-client-okhttp": "juneau-rest/juneau-rest-client-okhttp",
diff --git a/pages/release-notes/10.0.0.md b/pages/release-notes/10.0.0.md
index 40f427abfa..32942e6cfd 100644
--- a/pages/release-notes/10.0.0.md
+++ b/pages/release-notes/10.0.0.md
@@ -987,6 +987,33 @@ See 
[juneau-rest-server-mcp](/docs/topics/JuneauRestServerMcp#replay-protection-
 
 See the [OAuth 2.1 
recipe](/docs/topics/JuneauMcpRecipes#securing-an-mcp-client-and-server-with-oauth-21-v2-only)
 (end-to-end client + server), the [DCR + step-up 
recipes](/docs/topics/JuneauMcpRecipes#dynamic-client-registration-and-step-up-v2-only),
 the [juneau-rest-client-mcp auth 
reference](/docs/topics/JuneauRestClientMcp#oauth-21-authorization-juneau-rest-client-mcp-auth),
 and the [juneau-rest-server-mcp resource-server 
reference](/docs/topics/JuneauRestServerMcp#oauth-21-resource- [...]
 
+### MCP `2026-07-28` — first-party runnable example module 
(`juneau-examples-mcp`) (TODO-312)
+
+A new example module, `juneau-examples-mcp` (under `juneau-examples/`, 
alongside `juneau-examples-core`), ships the single highest-value adoption 
artifact for the now-complete MCP framework above: a **runnable, copy-me 
reference implementation** of both the MCP server and client sides, built 
against the `2026-07-28` revision. One cohesive in-memory "notes" service 
(`NoteStore`, a `title -> body` map) is exercised end to end by every headline 
surface:
+
+- **tool** — `publishNote(title, body)`, which stores a note and pushes a 
`resources/updated` change to subscribers.
+- **tool + elicitation/MRTR** — `deleteNote(title)`, which pauses with 
`input_required` to confirm before resuming to delete.
+- **prompt + completion** — `summarize(title)`, whose `title` argument offers 
`completion/complete` suggestions from existing note titles.
+- **resource** — the fixed `note:///index` resource, listing all note titles.
+- **resource template + completion** — `note:///{title}`, reading one note by 
title, with the same title-completion behavior on its `{title}` variable.
+- **subscription (SEP-2575)** — `subscriptions/listen` held-open SSE, 
delivering the `resources/updated` change `publishNote` fires.
+
+`ExampleMcpServer` (a v2 `McpRestServlet`) registers every surface above; 
`ExampleServer` is an embedded-Jetty launcher (`Microservice` + 
`JettyConfiguration`, `main(...)` plus a `start(port)` handle); `ExampleClient` 
is a guided, numbered `McpClient` walkthrough printing each step's 
request/response top-to-bottom like a tutorial; and a Spring Boot variant 
(`SpringExampleMcpServer`/`SpringExampleApplication`) uses 
`SpringMcpRestServlet` to resolve a Spring-managed `GreetingService` throu [...]
+
+See the [MCP 
Recipes](/docs/topics/JuneauMcpRecipes#full-example-juneau-examples-mcp), 
[juneau-rest-server-mcp](/docs/topics/JuneauRestServerMcp), and 
[juneau-rest-client-mcp](/docs/topics/JuneauRestClientMcp) topics for pointers 
to the module, and the module's own `README.md` for run instructions.
+
+### MCP `2026-07-28` — client/server adoption convenience methods
+
+Four small, additive convenience methods land alongside the example module 
above, each replacing a small piece of repetitive code the example (and 
real-world adopters) would otherwise repeat at every call site. None deprecates 
or replaces the lower-level API it sits on top of.
+
+- **`CallToolResult.firstText()`** (`juneau-bean-mcp-v20260728`) — returns the 
text of the first `TextContent` block found while scanning the result's 
`content` list in order, or `null` if the list is empty/unset or contains no 
`TextContent` block. Kills the `((TextContent) 
result.getContent().get(i)).getText()` cast-and-scan a caller who only wants 
the tool's text result would otherwise repeat everywhere.
+- **`McpClient.callToolText(String name, Map<String,Object> arguments)`** 
(`juneau-rest-client-mcp-v20260728`) — convenience for `callTool(...)` followed 
by `CallToolResult.firstText()`; null-safe if `callTool(...)` itself returns a 
`null` result.
+- **`ServerDiscoverResult.getServerInfo()`** (`juneau-bean-mcp-v20260728`) — 
null-safe shortcut for `getMeta() == null ? null : getMeta().getServerInfo()`, 
so a caller doesn't have to null-check the intermediate `_meta` envelope just 
to reach the server's `Implementation` identity. `@BeanIgnore`d so it does not 
add a second, top-level `serverInfo` member to the `server/discover` wire 
format.
+- **`McpMrtrResumeContext.continuationAsString()`** 
(`juneau-rest-server-mcp-v20260728`) — shorthand for 
`continuationAs(String.class)`, for the common case of a plain `String` 
continuation.
+- **`ElicitationResponses.getBoolean(ctx, id, field)` / `getString(ctx, id, 
field)`** (`juneau-rest-server-mcp-v20260728`) — decline/omission-safe typed 
reads of a single field from an elicitation answer: a missing id, a missing 
field, or a non-`ACCEPT` action (declined/cancelled) all resolve to a safe 
default (`false` for `getBoolean`, `null` for `getString`) instead of requiring 
the caller to null-check `get(...)` and its content map by hand.
+
+See the [confirm-delete 
recipe](/docs/topics/JuneauMcpRecipes#elicitation--multi-round-trip-requests-v2-only)
 and the [client 
recipe](/docs/topics/JuneauMcpRecipes#client-call-read-and-handle-errors) in 
MCP Recipes, the 
[juneau-rest-server-mcp](/docs/topics/JuneauRestServerMcp#elicitation-mcp-2026-07-28-sep-2322)
 elicitation reference, and the 
[juneau-rest-client-mcp](/docs/topics/JuneauRestClientMcp) client reference for 
these methods in context.
+
 ### MCP `2025-06-18` — resource-not-found error code corrected to `-32002` 
(TODO-332)
 
 `2025-06-18`-only bug fix. A `resources/read` for a URI the server does not 
serve now returns the JSON-RPC error code **`-32002`** ("Resource not found") — 
the `2025-06-18` spec's dedicated missing-resource code — instead of the 
previous generic **`-32601`** ("method not found"). This was one of the four 
"not found" kinds that the re-layered `2025-06-18` `McpRevision` initially 
collapsed onto `-32601` (see the `juneau-rest-server-mcp-v20250618` module 
notes above); resource-not-found is  [...]
diff --git a/pages/topics/11.03.JuneauMcpRecipes.md 
b/pages/topics/11.03.JuneauMcpRecipes.md
index f03d77e76e..5613f58f42 100644
--- a/pages/topics/11.03.JuneauMcpRecipes.md
+++ b/pages/topics/11.03.JuneauMcpRecipes.md
@@ -5,6 +5,7 @@ slug: JuneauMcpRecipes
 
 Copy-pasteable snippets for common MCP tasks, targeting revision `2026-07-28` 
unless noted. Each recipe is self-contained: assume `config` is an 
`McpServerConfig` you're building up (as in the 
[Quickstart](/docs/topics/JuneauMcpQuickstart)), and paste imports as shown.
 
+- [Full example: `juneau-examples-mcp`](#full-example-juneau-examples-mcp)
 - [Writing a tool](#writing-a-tool)
 - [Writing a prompt](#writing-a-prompt)
 - [Serving a resource](#serving-a-resource)
@@ -17,6 +18,10 @@ Copy-pasteable snippets for common MCP tasks, targeting 
revision `2026-07-28` un
 - [Securing an MCP client and server with OAuth 2.1 (v2 
only)](#securing-an-mcp-client-and-server-with-oauth-21-v2-only)
 - [Dynamic client registration and step-up (v2 
only)](#dynamic-client-registration-and-step-up-v2-only)
 
+## Full example: `juneau-examples-mcp`
+
+Before diving into individual recipes, consider starting from 
[`juneau-examples-mcp`](https://github.com/apache/juneau/tree/master/juneau-examples/juneau-examples-mcp)
 — a complete, runnable "notes" service exercising every recipe on this page 
(and more) in one small, copy-me Maven module targeting revision `2026-07-28`: 
a `publishNote` tool, a `deleteNote` tool that walks a full elicitation/MRTR 
confirm-then-resume round-trip, a `summarize` prompt with argument completion, 
a `note:///in [...]
+
 ## Writing a tool
 
 **Raw, via `McpToolHandler.of(...)`** — full control over the input schema:
@@ -216,8 +221,7 @@ McpToolHandler confirmDeleteTool = McpToolHandler.of(
             var question = new ElicitRequest().setMessage("Proceed with 
deletion?").setRequestedSchema(schema);
             throw ElicitationRequests.of("confirm", question, 
"confirm-delete-continuation");
         }
-        var answer = ElicitationResponses.get(resume.get(), "confirm");
-        if (answer == null || answer.getAction() != ElicitAction.ACCEPT || ! 
Boolean.TRUE.equals(answer.getContent().get("confirm")))
+        if (! ElicitationResponses.getBoolean(resume.get(), "confirm", 
"confirm"))
             return McpToolOutcome.text("Deletion cancelled.");
         // ... perform the deletion here ...
         return McpToolOutcome.text("Deleted.");
@@ -226,8 +230,8 @@ McpToolHandler confirmDeleteTool = McpToolHandler.of(
 ```
 
 - On the first round, `ctx.getBean(McpMrtrResumeContext.class)` is empty, so 
the handler throws `ElicitationRequests.of(id, request, continuation)` — an 
`McpInputRequiredSignal` (a `RuntimeException`) that the dispatcher turns into 
an `input_required` pause.
-- The client answers, then re-sends the call; on that round, 
`McpMrtrResumeContext` is present, and `ElicitationResponses.get(ctx, id)` 
decodes the typed `ElicitResult` keyed by the same `id` you posed the question 
under.
-- `ElicitResult.getContent()` is populated only when `getAction() == 
ElicitAction.ACCEPT`; a `DECLINE`/`CANCEL` answer carries no content.
+- The client answers, then re-sends the call; on that round, 
`McpMrtrResumeContext` is present. `ElicitationResponses.getBoolean(ctx, id, 
field)` reads a single boolean field decline/omission-safely in one call — a 
missing id, a missing field, or a non-`ACCEPT` action (declined/cancelled) all 
resolve to `false`, so there's no need to null-check `get(...)` and its content 
map by hand for a plain confirm/deny question. 
`ElicitationResponses.getString(ctx, id, field)` is the equivalent for  [...]
+- If a handler needs to carry state across rounds (e.g. which record to 
delete), stash it in the `continuation` argument to 
`ElicitationRequests.of(...)` and recover it on resume via 
`resume.get().continuationAsString()` (or `continuationAs(Class)` for a 
non-`String` shape) — see [MRTR resume 
contexts](/docs/topics/JuneauRestServerMcp#resuming-the-two-beanstore-injected-handler-contexts)
 in the server reference.
 
 This requires no special server config beyond a v2 servlet/mixin — MRTR 
support is on by default (see `McpOptions.mrtr(Consumer<McpMrtrConfig>)` in the 
[server reference](/docs/topics/JuneauRestServerMcp) to customize the codec, 
TTL, or round cap).
 
@@ -415,12 +419,18 @@ import org.apache.juneau.bean.jsonrpc.McpException;
 import org.apache.juneau.rest.client.mcp.v20260728.McpClient;
 
 try (var client = McpClient.connect("http://localhost:8080/mcp";)) {
+    System.out.println("Connected to: " + 
client.discoveredServer().getServerInfo());
+
     var tools = client.listTools();
     System.out.println(tools);
 
     var callResult = client.callTool("weather", Map.of("city", "Seattle"));
     System.out.println(callResult.getContent());
 
+    // Just want the tool's text? callToolText(...) is callTool(...) + 
CallToolResult.firstText() in one
+    // call -- no manual ((TextContent) 
callResult.getContent().get(0)).getText() cast-and-scan needed.
+    System.out.println(client.callToolText("weather", Map.of("city", 
"Seattle")));
+
     var resource = client.readResource("file:///readme.txt");
     System.out.println(resource.getContents());
 } catch (McpException e) {
@@ -432,6 +442,8 @@ try (var client = 
McpClient.connect("http://localhost:8080/mcp";)) {
 
 `McpClient` is `Closeable`, so try-with-resources closes the underlying 
transport when the block exits, on either the success or error path. Every 
typed call (`callTool`, `getPrompt`, `readResource`, `listTools`, 
`listPrompts`, `listResources`, `listResourceTemplates`, `complete`) throws 
`McpException` for a JSON-RPC error response and `IOException` for a 
transport/(de)serialization failure — there's no single unified exception type, 
so a caller that needs both should catch both, as shown.
 
+`ServerDiscoverResult.getServerInfo()` and `McpClient.callToolText(...)` are 
additive convenience methods: the former is a null-safe shortcut to the 
server's `Implementation` identity off the `server/discover` result that 
`connect(...)` (and `serverDiscover()`) already cache, and the latter folds 
`callTool(...)` + `CallToolResult.firstText()` into one call for the common 
case of wanting just a tool's rendered text. Neither replaces the lower-level 
`getContent()`/`getMeta()` access shown  [...]
+
 ## Securing an MCP client and server with OAuth 2.1 (v2 only)
 
 This recipe walks both sides of an OAuth 2.1 / MCP `2026-07-28` deployment as 
one story: turn a v2 MCP endpoint into a protected resource server, then 
acquire a token on the client and call it. Authorization is **v2-only** and 
**off by default** — an endpoint that doesn't opt in behaves exactly as before.
diff --git a/pages/topics/11.04.JuneauRestServerMcp.md 
b/pages/topics/11.04.JuneauRestServerMcp.md
index 987735473d..de3c122c73 100644
--- a/pages/topics/11.04.JuneauRestServerMcp.md
+++ b/pages/topics/11.04.JuneauRestServerMcp.md
@@ -6,6 +6,10 @@ slug: JuneauRestServerMcp
 
 New to MCP in Juneau? Start with the [MCP 
Quickstart](/docs/topics/JuneauMcpQuickstart) and [MCP 
Recipes](/docs/topics/JuneauMcpRecipes) — this page is the deep reference for 
lookup once you're past the basics.
 
+:::tip Want a runnable, end-to-end example instead?
+[`juneau-examples-mcp`](https://github.com/apache/juneau/tree/master/juneau-examples/juneau-examples-mcp)
 is a complete, copy-me Maven module exercising every major surface on this 
page — tools, elicitation/MRTR, prompts + completion, resources + resource 
templates, and subscriptions — against a tiny in-memory "notes" service, 
targeting revision `2026-07-28`. It ships as a downloadable project zip in the 
Apache release. See [MCP 
Recipes](/docs/topics/JuneauMcpRecipes#full-example-juneau- [...]
+:::
+
 `juneau-rest-server-mcp` is a **revision-neutral core** for exposing a [Model 
Context Protocol (MCP)](https://modelcontextprotocol.io/) JSON-RPC endpoint on 
`juneau-rest-server`. On its own it has zero compile-time knowledge of any MCP 
protocol revision; a protocol revision is supplied by a separate adapter 
module. Today that's **`juneau-rest-server-mcp-v20250618`**, which binds the 
core to the `2025-06-18` wire beans in 
[`juneau-bean-mcp-v20250618`](/docs/topics/JuneauBeanMcp). This pag [...]
 
 ## Overview
@@ -763,7 +767,7 @@ The dispatcher catches the signal, seals the continuation 
(plus the method, a 1-
 
 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.
+- **`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. For the common case of a `String` 
continuation, `continuationAsString()` is a shorthand for 
`continuationAs(String.class)`.
 - **`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.
@@ -877,6 +881,8 @@ var answer = ElicitationResponses.get(resume.get(), 
"confirm");
 
 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.
 
+For the common case of reading a single typed field — a boolean confirm/deny 
or a string answer — `ElicitationResponses.getBoolean(ctx, id, field)` and 
`getString(ctx, id, field)` skip the `get(...)` + null-check + content-map 
dance: a missing id, a missing field, or a non-`ACCEPT` action 
(declined/cancelled) all resolve to a safe default (`false` for `getBoolean`, 
`null` for `getString`) rather than requiring the caller to null-check by hand. 
See the [confirm-delete recipe](/docs/topics [...]
+
 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
diff --git a/pages/topics/11.05.JuneauRestClientMcp.md 
b/pages/topics/11.05.JuneauRestClientMcp.md
index dd73b00d8f..dc2a061dde 100644
--- a/pages/topics/11.05.JuneauRestClientMcp.md
+++ b/pages/topics/11.05.JuneauRestClientMcp.md
@@ -6,6 +6,10 @@ slug: JuneauRestClientMcp
 
 New to MCP in Juneau? Start with the [MCP 
Quickstart](/docs/topics/JuneauMcpQuickstart) and [MCP 
Recipes](/docs/topics/JuneauMcpRecipes) — this page is the deep reference for 
lookup once you're past the basics.
 
+:::tip Want a runnable, end-to-end example instead?
+[`juneau-examples-mcp`](https://github.com/apache/juneau/tree/master/juneau-examples/juneau-examples-mcp)
 is a complete, copy-me Maven module pairing a `2026-07-28` MCP server with a 
guided `McpClient` walkthrough (`ExampleClient`) that exercises every client 
call covered by this reference — discovery, tool calls, resource/template 
reads, completions, prompts, subscriptions, and an elicitation round-trip. It 
ships as a downloadable project zip in the Apache release. See [MCP 
Recipes](/do [...]
+:::
+
 `juneau-rest-client-mcp` is the revision-neutral MCP client core built on 
`juneau-rest-client`, with two dated adapters: 
`juneau-rest-client-mcp-v20250618` and `juneau-rest-client-mcp-v20260728`.
 
 ## Overview

Reply via email to