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 d04f52c9b0 docs: OAuth 2.1-secured MCP example (juneau-examples-mcp)
d04f52c9b0 is described below

commit d04f52c9b0a4be6e705df172b858dd0a4a236ad8
Author: James Bognar <[email protected]>
AuthorDate: Thu Aug 6 11:15:34 2026 -0700

    docs: OAuth 2.1-secured MCP example (juneau-examples-mcp)
    
    Co-authored-by: Cursor <[email protected]>
---
 pages/release-notes/10.0.0.md             | 10 ++++++++++
 pages/topics/11.03.JuneauMcpRecipes.md    |  2 ++
 pages/topics/11.04.JuneauRestServerMcp.md |  2 ++
 pages/topics/11.05.JuneauRestClientMcp.md |  2 ++
 4 files changed, 16 insertions(+)

diff --git a/pages/release-notes/10.0.0.md b/pages/release-notes/10.0.0.md
index 32942e6cfd..6cc19567fd 100644
--- a/pages/release-notes/10.0.0.md
+++ b/pages/release-notes/10.0.0.md
@@ -1002,6 +1002,16 @@ A new example module, `juneau-examples-mcp` (under 
`juneau-examples/`, alongside
 
 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` — OAuth 2.1-secured variant of `juneau-examples-mcp` 
(`secured/` package)
+
+The example module above gets a second, fully **offline-runnable** variant 
demonstrating the OAuth 2.1 authorization baseline (TODO-312f) end to end: 
`org.apache.juneau.examples.mcp.secured` wraps the identical notes service in a 
live resource-server gate, with a demo authorization server standing in for a 
real IdP so the whole walkthrough needs no external network access.
+
+- **`SecuredExampleMcpServer extends ExampleMcpServer`** — the entire security 
story is one `createMcpOptions()` override layering `McpResourceServerConfig` 
on top of the inherited notes surface: RS auth enabled, `setResource(...)` (the 
RFC 9728 resource identity / RFC 8707 default audience), a `JwtTokenValidator` 
checking `iss`/`aud`/`exp`/`nbf` against the offline AS, 
`addAuthorizationServer(...)`, a baseline `addRequiredScope("mcp.read")`, and 
SEP-2350 per-operation step-up (`addOpera [...]
+- **`OfflineAuthorizationServer`** — a clearly-marked DEMO-ONLY stand-in AS: a 
fresh self-generated RSA signing key per run, an offline `JWKSource` handed 
straight to the validator (no JWKS HTTP fetch), a real RFC 6749 §4.4 
client-credentials `/token` endpoint gated by a fixed scope allowlist 
(`mcp.read`/`mcp.write`, else `invalid_scope`), and an RFC 8414 
`/.well-known/oauth-authorization-server` metadata document (issuer = its own 
loopback base URL, plus `token_endpoint`) enabling genui [...]
+- **`SecuredExampleServer`/`SecuredExampleClient`** — a launcher plus a real, 
discovery-driven end-to-end client walkthrough: an unauthenticated call 
rejected with `401` + `WWW-Authenticate: Bearer`, RFC 9728 PRM discovery 
(`authorization_servers`, `scopes_supported`), RFC 8414 AS-metadata discovery 
of the `token_endpoint`, a client-credentials token acquisition via 
`juneau-rest-client-mcp-auth`'s `McpTokenProvider`, a successful `mcp.read` 
read, the same token rejected with `403 insuffi [...]
+
+See the [OAuth 2.1 
recipe](/docs/topics/JuneauMcpRecipes#securing-an-mcp-client-and-server-with-oauth-21-v2-only)
 for the full walkthrough, 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.
diff --git a/pages/topics/11.03.JuneauMcpRecipes.md 
b/pages/topics/11.03.JuneauMcpRecipes.md
index 5613f58f42..f2dfacb5a4 100644
--- a/pages/topics/11.03.JuneauMcpRecipes.md
+++ b/pages/topics/11.03.JuneauMcpRecipes.md
@@ -448,6 +448,8 @@ try (var client = 
McpClient.connect("http://localhost:8080/mcp";)) {
 
 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.
 
+**Prefer to start from running code?** The `secured/` package inside 
[`juneau-examples-mcp`](#full-example-juneau-examples-mcp) is the canonical, 
copy-me, fully offline realization of this entire recipe — and it ships as part 
of the same downloadable project zip in the Apache release. 
`SecuredExampleMcpServer` layers the resource-server config below onto the 
plain `ExampleMcpServer` with a single `createMcpOptions()` override (baseline 
`mcp.read`, plus a SEP-2350 per-operation step-up re [...]
+
 **Dependencies.** On the server, add a token-validator module alongside 
`juneau-rest-server-mcp-v20260728`: `juneau-rest-server-auth-jwt` (JWT/JWKS 
validation) or `juneau-rest-server-auth-oauth` (RFC 7662 introspection). On the 
client, add `juneau-rest-client-mcp-auth` alongside 
`juneau-rest-client-mcp-v20260728`:
 
 ```xml
diff --git a/pages/topics/11.04.JuneauRestServerMcp.md 
b/pages/topics/11.04.JuneauRestServerMcp.md
index de3c122c73..33a6298df8 100644
--- a/pages/topics/11.04.JuneauRestServerMcp.md
+++ b/pages/topics/11.04.JuneauRestServerMcp.md
@@ -621,6 +621,8 @@ WWW-Authenticate: Bearer realm="mcp", 
error="insufficient_scope", scope="repo.de
 
 All of the operation's required scopes are emitted in a single `scope=` param 
(the SEP-2350 "single challenge" SHOULD). Sufficiency is **hierarchy-aware** 
per the server MUST: a broader granted scope implies its narrower children 
(granted `repo` satisfies required `repo.delete` / `repo:read`, but granted 
`repo.read` does *not* satisfy required `repo`). Operations with no configured 
per-operation scope are unaffected — the baseline alone governs them. The 
client half of the flow (union th [...]
 
+**See it wired end to end.** `SecuredExampleMcpServer` in the `secured/` 
package of 
[`juneau-examples-mcp`](/docs/topics/JuneauMcpRecipes#securing-an-mcp-client-and-server-with-oauth-21-v2-only)
 applies exactly this pattern with one `createMcpOptions()` override: a 
baseline `addRequiredScope("mcp.read")` plus `addOperationScope("tools/call", 
"publishNote", "mcp.write")` (and `deleteNote`) for the step-up, running fully 
offline against a demo authorization server.
+
 ## Cache Hints (MCP `2026-07-28`, SEP-2549)
 
 The `2026-07-28` adapter (`org.apache.juneau.rest.server.mcp.v20260728`) adds 
two capabilities on top of the neutral core: SEP-2549 cache hints on five 
list/read results, and a `resources/templates/list` endpoint. Both are 
configured statically, at server-construction time — there is no 
dynamic/per-request cache callback, and neither concept exists on the neutral 
`juneau-rest-server-mcp` core or the `2025-06-18` adapter.
diff --git a/pages/topics/11.05.JuneauRestClientMcp.md 
b/pages/topics/11.05.JuneauRestClientMcp.md
index dc2a061dde..f82476ee29 100644
--- a/pages/topics/11.05.JuneauRestClientMcp.md
+++ b/pages/topics/11.05.JuneauRestClientMcp.md
@@ -145,6 +145,8 @@ Fetches an authorization server's metadata as an 
`OidcMetadata` record (`issuer`
 The full SEP-2351 path-insertion well-known-URI construction (path-bearing 
issuers, ordered fallbacks) is a deferred enhancement; `OidcDiscoveryClient` 
currently implements the two-endpoint (RFC 8414 → OIDC) fallback the baseline 
requires. An OIDC ID token returned alongside an access token is surfaced 
verbatim on `OAuthToken.idToken()` but is **not validated** in this baseline — 
do not trust its claims for auth decisions.
 :::
 
+**See it wired end to end.** `SecuredExampleClient` in the `secured/` package 
of 
[`juneau-examples-mcp`](/docs/topics/JuneauMcpRecipes#securing-an-mcp-client-and-server-with-oauth-21-v2-only)
 drives this exact discovery chain with nothing hardcoded — from a `401` 
challenge through `McpProtectedResourceMetadataClient` (RFC 9728) and 
`discoverAuthorizationServer(...)` (RFC 8414) to a discovered `token_endpoint` 
it hands straight to `McpTokenProvider.clientCredentials()`.
+
 ### Dynamic Client Registration (SEP-837)
 
 When you have no pre-registered `client_id`, `McpDynamicClientRegistrar` 
performs RFC 7591 / OIDC Dynamic Client Registration against an authorization 
server's `registration_endpoint`, built via 
`McpDynamicClientRegistrar.create()`. `register()` POSTs the client metadata 
and returns an immutable `McpClientRegistration` (`clientId`, `clientSecret`, 
`clientSecretExpiresAt`, `registrationAccessToken`, `registrationClientUri`, 
`issuer`, `redirectUris`, `applicationType`, `extras`) whose `toS [...]

Reply via email to