Prepare SDK Tier 2 assessment submission
- Add CHANGELOG.md with release lines and history - Refresh ROADMAP.md: 2.x stable line, 3.0.x for 2026-07-28 spec (first milestones Sep 2026), Tier 2 commitment, 1.x/0.x security-patches-only - Refresh VALIDATION_RESULTS.md: 2026-08-17 run on conformance suite 0.2.0-alpha.11 (server 73/73, auth 193/193, client 3/4 w/ sse-retry baselined) - Bump conformance CI action pins v0.1.11 -> v0.1.16 - Update docs with missing features Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>
This commit is contained in:
parent
8ee8ccbc17
commit
2fd159a79a
7 changed files with 432 additions and 105 deletions
4
.github/workflows/conformance.yml
vendored
4
.github/workflows/conformance.yml
vendored
|
|
@ -27,7 +27,7 @@ jobs:
|
||||||
timeout 30 bash -c 'until curl -s http://localhost:8080/mcp > /dev/null 2>&1; do sleep 0.5; done'
|
timeout 30 bash -c 'until curl -s http://localhost:8080/mcp > /dev/null 2>&1; do sleep 0.5; done'
|
||||||
|
|
||||||
- name: Run conformance tests
|
- name: Run conformance tests
|
||||||
uses: modelcontextprotocol/conformance@v0.1.11
|
uses: modelcontextprotocol/conformance@v0.1.16
|
||||||
with:
|
with:
|
||||||
mode: server
|
mode: server
|
||||||
url: http://localhost:8080/mcp
|
url: http://localhost:8080/mcp
|
||||||
|
|
@ -54,7 +54,7 @@ jobs:
|
||||||
run: mvn clean install -DskipTests
|
run: mvn clean install -DskipTests
|
||||||
|
|
||||||
- name: Run conformance test
|
- name: Run conformance test
|
||||||
uses: modelcontextprotocol/conformance@v0.1.11
|
uses: modelcontextprotocol/conformance@v0.1.16
|
||||||
with:
|
with:
|
||||||
mode: client
|
mode: client
|
||||||
command: 'java -jar conformance-tests/client-jdk-http-client/target/client-jdk-http-client-*-SNAPSHOT.jar'
|
command: 'java -jar conformance-tests/client-jdk-http-client/target/client-jdk-http-client-*-SNAPSHOT.jar'
|
||||||
|
|
|
||||||
64
CHANGELOG.md
Normal file
64
CHANGELOG.md
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to the MCP Java SDK are documented in the
|
||||||
|
[GitHub Releases](https://github.com/modelcontextprotocol/java-sdk/releases),
|
||||||
|
which serve as the canonical, detailed changelog for every version. This file
|
||||||
|
summarizes the release history and the currently maintained release lines.
|
||||||
|
|
||||||
|
Versioning follows [Semantic Versioning](https://semver.org/); see
|
||||||
|
[VERSIONING.md](VERSIONING.md) for the breaking-change policy and
|
||||||
|
[SECURITY.md](SECURITY.md) for the supported-versions security policy.
|
||||||
|
|
||||||
|
## Release lines
|
||||||
|
|
||||||
|
| Line | Latest | Spec revision | Status |
|
||||||
|
|------|--------|---------------|--------|
|
||||||
|
| 2.x | [2.0.0](https://github.com/modelcontextprotocol/java-sdk/releases/tag/v2.0.0) (2026-06-11) | 2025-11-25 | Active development |
|
||||||
|
| 1.1.x | [1.1.3](https://github.com/modelcontextprotocol/java-sdk/releases/tag/v1.1.3) (2026-05-21) | 2025-06-18 | Security patches only |
|
||||||
|
| 1.0.x | [1.0.2](https://github.com/modelcontextprotocol/java-sdk/releases/tag/v1.0.2) (2026-05-21) | 2025-06-18 | Security patches only |
|
||||||
|
| 0.18.x | [0.18.3](https://github.com/modelcontextprotocol/java-sdk/releases/tag/v0.18.3) (2026-06-09) | 2025-06-18 | Security patches only |
|
||||||
|
|
||||||
|
## 2.0.0 — 2026-06-11
|
||||||
|
|
||||||
|
First major release since 1.x, tracking the **2025-11-25** MCP specification.
|
||||||
|
Upgrading from 1.x? See the [v2 migration guide](MIGRATION-2.0.md).
|
||||||
|
|
||||||
|
- New JSON compatibility foundation for forward/backward wire compatibility,
|
||||||
|
with pluggable Jackson 2 / Jackson 3 serialization modules
|
||||||
|
- Spec-accurate schema: enforced required fields, lenient wire deserialization
|
||||||
|
- End-to-end validation of tool inputs and embedded JSON Schema documents
|
||||||
|
(JSON Schema 2020-12, SEP-1613)
|
||||||
|
- Richer elicitation: client-side schema defaults (SEP-1034), URL elicitation
|
||||||
|
(SEP-1036), form-based elicitation schemas
|
||||||
|
- Icons and metadata support (SEP-973)
|
||||||
|
- Streamable HTTP first: SSE transports deprecated in favor of Streamable HTTP
|
||||||
|
- Module restructuring: `mcp-core`, `mcp-json-jackson2`, `mcp-json-jackson3`,
|
||||||
|
`mcp-bom`
|
||||||
|
|
||||||
|
Full notes: [v2.0.0 release](https://github.com/modelcontextprotocol/java-sdk/releases/tag/v2.0.0)
|
||||||
|
(preceded by milestones [M1](https://github.com/modelcontextprotocol/java-sdk/releases/tag/v2.0.0-M1),
|
||||||
|
[M2](https://github.com/modelcontextprotocol/java-sdk/releases/tag/v2.0.0-M2),
|
||||||
|
[M3](https://github.com/modelcontextprotocol/java-sdk/releases/tag/v2.0.0-M3),
|
||||||
|
[RC1](https://github.com/modelcontextprotocol/java-sdk/releases/tag/v2.0.0-RC1)).
|
||||||
|
|
||||||
|
## 1.x
|
||||||
|
|
||||||
|
- [1.1.3](https://github.com/modelcontextprotocol/java-sdk/releases/tag/v1.1.3) /
|
||||||
|
[1.0.2](https://github.com/modelcontextprotocol/java-sdk/releases/tag/v1.0.2) — 2026-05-21:
|
||||||
|
SSE client transport message-endpoint validation (backports)
|
||||||
|
- [1.1.2](https://github.com/modelcontextprotocol/java-sdk/releases/tag/v1.1.2) — 2026-04-25
|
||||||
|
- [1.1.1](https://github.com/modelcontextprotocol/java-sdk/releases/tag/v1.1.1) /
|
||||||
|
[1.0.1](https://github.com/modelcontextprotocol/java-sdk/releases/tag/v1.0.1) — 2026-03-27
|
||||||
|
- [1.1.0](https://github.com/modelcontextprotocol/java-sdk/releases/tag/v1.1.0) — 2026-03-13
|
||||||
|
- [1.0.0](https://github.com/modelcontextprotocol/java-sdk/releases/tag/v1.0.0) — 2026-02-23:
|
||||||
|
first stable release; see the [1.0 migration guide](MIGRATION-1.0.md)
|
||||||
|
|
||||||
|
## 0.x
|
||||||
|
|
||||||
|
- [0.18.3](https://github.com/modelcontextprotocol/java-sdk/releases/tag/v0.18.3) — 2026-06-09:
|
||||||
|
security fix for GHSA-hv2w-8mjj-jw22
|
||||||
|
- [0.18.0](https://github.com/modelcontextprotocol/java-sdk/releases/tag/v0.18.0) –
|
||||||
|
[0.18.2](https://github.com/modelcontextprotocol/java-sdk/releases/tag/v0.18.2) — 2026-02 to 2026-05
|
||||||
|
- [0.8.0](https://github.com/modelcontextprotocol/java-sdk/releases/tag/v0.8.0) –
|
||||||
|
[0.17.2](https://github.com/modelcontextprotocol/java-sdk/releases/tag/v0.17.2) — 2025-03 to 2026-01:
|
||||||
|
see the [full release list](https://github.com/modelcontextprotocol/java-sdk/releases)
|
||||||
39
ROADMAP.md
39
ROADMAP.md
|
|
@ -6,40 +6,35 @@ The SDK tracks implementation of MCP spec components via GitHub Projects, with a
|
||||||
|
|
||||||
## Current Focus Areas
|
## Current Focus Areas
|
||||||
|
|
||||||
### 2025-11-25 Spec Implementation
|
### 2.x — Stable Line (2025-11-25 spec)
|
||||||
|
|
||||||
The Java SDK is actively implementing the [2025-11-25 MCP specification revision](https://github.com/orgs/modelcontextprotocol/projects/26/views/1).
|
The current stable release line is **2.x** ([2.0.0](https://github.com/modelcontextprotocol/java-sdk/releases/tag/v2.0.0), June 2026), implementing the [2025-11-25 MCP specification revision](https://modelcontextprotocol.io/specification/2025-11-25), including:
|
||||||
|
|
||||||
Key features in this revision include:
|
- **Spec-accurate schema**: enforced required fields with lenient wire deserialization, and a JSON compatibility foundation for forward/backward wire compatibility
|
||||||
|
- **Enhanced schemas**: JSON Schema 2020-12 validation of tool inputs and embedded schema documents (SEP-1613)
|
||||||
|
- **Richer elicitation**: client-side schema defaults (SEP-1034), URL mode elicitation (SEP-1036), form-based elicitation schemas
|
||||||
|
- **Icons metadata** (SEP-973): icons for tools, resources, resource templates, and prompts
|
||||||
|
- **Streamable HTTP first**: SSE transports deprecated in favor of Streamable HTTP
|
||||||
|
- **Pluggable JSON serialization**: Jackson 2 and Jackson 3 modules
|
||||||
|
|
||||||
- **Tasks**: Experimental support for tracking durable requests with polling and deferred result retrieval
|
2.x development continues with patch and minor releases for bug fixes, conformance improvements, and non-breaking features. See [CHANGELOG.md](CHANGELOG.md) for the release history.
|
||||||
- **Tool calling in sampling**: Support for `tools` and `toolChoice` parameters
|
|
||||||
- **URL mode elicitation**: Client-side URL elicitation requests
|
|
||||||
- **Icons metadata**: Servers can expose icons for tools, resources, resource templates, and prompts
|
|
||||||
- **Enhanced schemas**: JSON Schema 2020-12 as default, improved enum support, default values for elicitation
|
|
||||||
- **Security improvements**: Updated security best practices, enhanced authorization flows, enabling OAuth integrations
|
|
||||||
|
|
||||||
See the full [changelog](https://modelcontextprotocol.io/specification/2025-11-25/changelog) for details.
|
The earlier **1.x and 0.x release lines receive security patches only** — no feature or bug-fix backports. Users on those lines are encouraged to upgrade via the [v2 migration guide](MIGRATION-2.0.md).
|
||||||
|
|
||||||
### Tier 1 SDK Support
|
### 3.x — 2026-07-28 Spec Support
|
||||||
|
|
||||||
Once we catch up on the most recent MCP specification revision we aim to fully support all the upcoming specification features on the day of its release.
|
The next major version, **3.x**, will implement the [2026-07-28 MCP specification revision](https://modelcontextprotocol.io/specification/2026-07-28), including `server/discover` and the SEP-2575 stateless lifecycle. The first 3.0.0 milestone releases are planned for **September 2026**, tracked via a dedicated spec revision project board.
|
||||||
|
|
||||||
### v1.x Development
|
### SDK Tiering
|
||||||
|
|
||||||
The Java SDK is currently in active development as v1.x, following a recent stable 1.0.0 release. The SDK provides:
|
The Java SDK is an official [Tier 2 SDK](https://modelcontextprotocol.io/community/sdk-tiers) committed to full protocol support: new spec revisions are implemented within the Tier 2 six-month window, with conformance continuously verified against the [MCP conformance suite](https://github.com/modelcontextprotocol/conformance) in CI. Once caught up on the most recent specification revision, we aim for Tier 1: fully supporting new specification features on the day of their release.
|
||||||
|
|
||||||
- MCP protocol implementation
|
### Future Directions
|
||||||
- Synchronous and asynchronous programming models
|
|
||||||
- Multiple transport options (STDIO, HTTP/SSE, Servlet)
|
|
||||||
- Pluggable JSON serialization (Jackson 2 and Jackson 3)
|
|
||||||
|
|
||||||
Development is tracked via [GitHub Issues](https://github.com/modelcontextprotocol/java-sdk/issues) and [GitHub Projects](https://github.com/orgs/modelcontextprotocol/projects).
|
|
||||||
|
|
||||||
### Future Versions
|
|
||||||
|
|
||||||
Major version updates will align with MCP specification changes and breaking API changes as needed. The SDK is designed to evolve with the Java ecosystem, including:
|
Major version updates will align with MCP specification changes and breaking API changes as needed. The SDK is designed to evolve with the Java ecosystem, including:
|
||||||
|
|
||||||
- Virtual Threads and Structured Concurrency support
|
- Virtual Threads and Structured Concurrency support
|
||||||
- Additional transport implementations
|
- Additional transport implementations
|
||||||
- Performance optimizations
|
- Performance optimizations
|
||||||
|
|
||||||
|
Development is tracked via [GitHub Issues](https://github.com/modelcontextprotocol/java-sdk/issues) and [GitHub Projects](https://github.com/orgs/modelcontextprotocol/projects).
|
||||||
|
|
|
||||||
|
|
@ -1,127 +1,110 @@
|
||||||
# MCP Java SDK Conformance Test Validation Results
|
# MCP Java SDK Conformance Test Validation Results
|
||||||
|
|
||||||
|
Last validated: **2026-08-17** against conformance suite
|
||||||
|
**`@modelcontextprotocol/conformance@0.2.0-alpha.11`** (SDK at `main`, 2.0.1-SNAPSHOT), targetting
|
||||||
|
version 2025-11-25 (`--spec-version 2025-11-25`).
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
**Server Tests (active suite):** 44/44 passed (31 scenarios, 100%)
|
**Server Tests (active suite):** 73/73 checks passed (31 scenarios, 100%)
|
||||||
**Server Tests (spec 2025-11-25):** 4/4 passed — SEP-1613 `json-schema-2020-12` scenario ✨
|
**Server Tests (SEP-1613 `json-schema-2020-12`):** 5/5 checks passed (SEP-2106 checks skipped — post-2025-11-25 spec additions)
|
||||||
**Client Tests:** 3/4 scenarios passed (9/10 checks passed)
|
**Client Tests:** 3/4 scenarios passed; `sse-retry` fails (tracked in `conformance-baseline.yml`)
|
||||||
**Auth Tests:** 15/15 scenarios fully passing (195 passed, 0 failed, 0 warnings, 100% scenarios, 100% checks)
|
**Auth Tests:** 14/14 scenarios passing (193 checks, 0 failed, 0 warnings)
|
||||||
|
|
||||||
|
Baseline check passed on every run: all failures are expected per
|
||||||
|
[`conformance-baseline.yml`](conformance-baseline.yml).
|
||||||
|
|
||||||
## Server Test Results
|
## Server Test Results
|
||||||
|
|
||||||
### Active Suite — Passing (31/31 scenarios, 44/44 checks)
|
### Active Suite — Passing (31/31 scenarios, 73/73 checks)
|
||||||
|
|
||||||
- **Lifecycle & Utilities (4/4):** initialize, ping, logging-set-level, completion-complete
|
- **Lifecycle & Utilities:** initialize, ping, logging-set-level, completion-complete
|
||||||
- **Tools (13/13):** All scenarios including progress notifications, sampling, elicitation ✨
|
- **Tools (13/13):** all scenarios including progress notifications, sampling, elicitation
|
||||||
- **Elicitation (10/10):** SEP-1034 defaults (5 checks), SEP-1330 enums (5 checks)
|
- **Elicitation:** SEP-1034 defaults (6 checks), SEP-1330 enums (6 checks)
|
||||||
- **Resources (7/7):** list, read-text, read-binary, templates-read, subscribe, unsubscribe, SEP-2164 resource-not-found
|
- **Resources:** list, read-text, read-binary, templates-read, subscribe, unsubscribe
|
||||||
- **Prompts (5/5):** list, simple, with-args, embedded-resource, with-image
|
- **Prompts:** list, simple, with-args, embedded-resource, with-image
|
||||||
- **SSE Transport (2/2):** Multiple streams
|
- **SSE Transport:** multiple streams
|
||||||
- **Security (2/2):** Localhost validation passes, DNS rebinding protection
|
- **Security:** DNS rebinding protection
|
||||||
|
|
||||||
### Spec 2025-11-25 Scenarios — Passing (1/1 scenario, 4/4 checks)
|
### SEP-1613 — JSON Schema 2020-12 (5/5 checks)
|
||||||
|
|
||||||
- **JSON Schema 2020-12 (SEP-1613) (4/4):** ✨
|
- `json_schema_2020_12_tool` found; `$schema`, `$defs`, and `additionalProperties`
|
||||||
- `json_schema_2020_12_tool` found
|
fields preserved; every JSON-RPC message valid per the spec JSON schema for the
|
||||||
- `inputSchema.$schema` field preserved
|
negotiated spec version (`wire-schema-valid`)
|
||||||
- `inputSchema.$defs` field preserved
|
- SEP-2106 checks (composition/conditional/anchor keywords) reported SKIPPED:
|
||||||
- `inputSchema.additionalProperties` field preserved
|
they postdate the 2025-11-25 spec release and are excluded from scoring
|
||||||
|
|
||||||
## Client Test Results
|
## Client Test Results
|
||||||
|
|
||||||
### Passing (3/4 scenarios, 9/10 checks)
|
### Passing (3/4 scenarios)
|
||||||
|
|
||||||
- **initialize (1/1):** Protocol negotiation, clientInfo, capabilities
|
- **initialize (1/1):** protocol negotiation, clientInfo, capabilities
|
||||||
- **tools_call (1/1):** Tool discovery and invocation
|
- **tools_call (2/2):** tool discovery and invocation
|
||||||
- **elicitation-sep1034-client-defaults (5/5):** Default values for string, integer, number, enum, boolean
|
- **elicitation-sep1034-client-defaults (5/5):** default values for string, integer, number, enum, boolean
|
||||||
|
|
||||||
### Partially Passing (1/4 scenarios, 1/2 checks)
|
### Failing — in baseline (1/4 scenarios)
|
||||||
|
|
||||||
- **sse-retry (1/2 + 1 warning):**
|
- **sse-retry:** client does not parse/respect the `retry:` SSE field timing and
|
||||||
- ✅ Reconnects after stream closure
|
does not send the `Last-Event-ID` header (SHOULD requirement). Expected failure,
|
||||||
- ❌ Does not respect retry timing
|
listed in `conformance-baseline.yml`.
|
||||||
- ⚠️ Does not send Last-Event-ID header (SHOULD requirement)
|
|
||||||
|
|
||||||
**Issue:** Client treats `retry:` SSE field as invalid instead of parsing it for reconnection timing.
|
|
||||||
|
|
||||||
## Auth Test Results (Spring HTTP Client)
|
## Auth Test Results (Spring HTTP Client)
|
||||||
|
|
||||||
**Status: 195 passed, 0 failed, 0 warnings across 15 scenarios**
|
**Status: 193 checks passed, 0 failed, 0 warnings across 14 scenarios**
|
||||||
|
|
||||||
Uses the `client-spring-http-client` module with Spring Security OAuth2 and the [mcp-client-security](https://github.com/springaicommunity/mcp-client-security) library.
|
Uses the `client-spring-http-client` module with Spring Security OAuth2 and the
|
||||||
|
[mcp-client-security](https://github.com/springaicommunity/mcp-client-security) library.
|
||||||
|
|
||||||
### Fully Passing (15/15 scenarios)
|
Fully passing: metadata-default, metadata-var1/2/3, basic-cimd,
|
||||||
|
scope-from-www-authenticate, scope-from-scopes-supported, scope-omitted-when-undefined,
|
||||||
|
scope-step-up, scope-retry-limit, token-endpoint-auth-basic/post/none, pre-registration.
|
||||||
|
|
||||||
- **auth/metadata-default (13/13):** Default metadata discovery
|
Note: `auth/resource-mismatch` (present in earlier suite versions) is no longer part
|
||||||
- **auth/metadata-var1 (13/13):** Metadata discovery variant 1
|
of the 0.2.0-alpha auth suite.
|
||||||
- **auth/metadata-var2 (13/13):** Metadata discovery variant 2
|
|
||||||
- **auth/metadata-var3 (13/13):** Metadata discovery variant 3
|
|
||||||
- **auth/basic-cimd (12/12):** Basic Client-Initiated Metadata Discovery
|
|
||||||
- **auth/scope-from-www-authenticate (14/14):** Scope extraction from WWW-Authenticate header
|
|
||||||
- **auth/scope-from-scopes-supported (14/14):** Scope extraction from scopes_supported
|
|
||||||
- **auth/scope-omitted-when-undefined (14/14):** Scope omitted when not defined
|
|
||||||
- **auth/scope-step-up (16/16):** Scope step-up challenge
|
|
||||||
- **auth/scope-retry-limit (11/11):** Scope retry limit handling
|
|
||||||
- **auth/token-endpoint-auth-basic (18/18):** Token endpoint with HTTP Basic auth
|
|
||||||
- **auth/token-endpoint-auth-post (18/18):** Token endpoint with POST body auth
|
|
||||||
- **auth/token-endpoint-auth-none (18/18):** Token endpoint with no client auth
|
|
||||||
- **auth/resource-mismatch (2/2):** Resource mismatch handling
|
|
||||||
- **auth/pre-registration (6/6):** Pre-registered client credentials flow
|
|
||||||
|
|
||||||
## Known Limitations
|
## Known Limitations
|
||||||
|
|
||||||
1. **Client SSE Retry:** Client doesn't parse or respect the `retry:` field, reconnects immediately, and doesn't send Last-Event-ID header
|
1. **Client SSE Retry:** client doesn't parse or respect the `retry:` field,
|
||||||
|
reconnects immediately, and doesn't send the `Last-Event-ID` header
|
||||||
|
|
||||||
## Running Tests
|
## Running Tests
|
||||||
|
|
||||||
### Server (active suite)
|
### Server (active suite)
|
||||||
```bash
|
```bash
|
||||||
# Start server
|
# Build and start server
|
||||||
./mvnw compile -pl conformance-tests/server-servlet -am exec:java
|
./mvnw clean install -DskipTests
|
||||||
|
mvn exec:java -pl conformance-tests/server-servlet \
|
||||||
|
-Dexec.mainClass="io.modelcontextprotocol.conformance.server.ConformanceServlet"
|
||||||
|
|
||||||
# Run tests (in another terminal)
|
# Run tests (in another terminal, from the repo root)
|
||||||
npx @modelcontextprotocol/conformance server --url http://localhost:8080/mcp --suite active
|
npx @modelcontextprotocol/conformance@0.2.0-alpha.11 server \
|
||||||
|
--url http://localhost:8080/mcp --suite active \
|
||||||
|
--expected-failures ./conformance-tests/conformance-baseline.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
### Server (spec 2025-11-25 scenarios — SEP-1613)
|
### Server (SEP-1613 scenario)
|
||||||
```bash
|
```bash
|
||||||
# Start server (if not already running)
|
npx @modelcontextprotocol/conformance@0.2.0-alpha.11 server \
|
||||||
./mvnw compile -pl conformance-tests/server-servlet -am exec:java
|
--url http://localhost:8080/mcp --scenario json-schema-2020-12
|
||||||
|
|
||||||
# Run json-schema-2020-12 scenario
|
|
||||||
cd ../conformance && node --import tsx/esm src/index.ts server \
|
|
||||||
--url http://localhost:8080/mcp \
|
|
||||||
--scenario json-schema-2020-12
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Client
|
### Client
|
||||||
```bash
|
```bash
|
||||||
# Build
|
|
||||||
cd conformance-tests/client-jdk-http-client
|
|
||||||
../../mvnw clean package -DskipTests
|
|
||||||
|
|
||||||
# Run all scenarios
|
|
||||||
for scenario in initialize tools_call elicitation-sep1034-client-defaults sse-retry; do
|
for scenario in initialize tools_call elicitation-sep1034-client-defaults sse-retry; do
|
||||||
npx @modelcontextprotocol/conformance client \
|
npx @modelcontextprotocol/conformance@0.2.0-alpha.11 client \
|
||||||
--command "java -jar target/client-jdk-http-client-1.1.0-SNAPSHOT.jar" \
|
--command "java -jar conformance-tests/client-jdk-http-client/target/client-jdk-http-client-*.jar" \
|
||||||
--scenario $scenario
|
--scenario $scenario \
|
||||||
|
--expected-failures ./conformance-tests/conformance-baseline.yml
|
||||||
done
|
done
|
||||||
```
|
```
|
||||||
|
|
||||||
### Auth (Spring HTTP Client)
|
### Auth (Spring HTTP Client)
|
||||||
|
|
||||||
Ensure you run with the conformance testing suite `0.1.15` or higher.
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Build
|
npx @modelcontextprotocol/conformance@0.2.0-alpha.11 client \
|
||||||
cd conformance-tests/client-spring-http-client
|
|
||||||
../../mvnw clean package -DskipTests
|
|
||||||
|
|
||||||
# Run auth suite
|
|
||||||
npx @modelcontextprotocol/conformance@0.1.15 client \
|
|
||||||
--spec-version 2025-11-25 \
|
--spec-version 2025-11-25 \
|
||||||
--command "java -jar target/client-spring-http-client-1.1.0-SNAPSHOT.jar" \
|
--command "java -jar conformance-tests/client-spring-http-client/target/client-spring-http-client-*.jar" \
|
||||||
--suite auth
|
--suite auth \
|
||||||
|
--expected-failures ./conformance-tests/conformance-baseline.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
## Recommendations
|
## Recommendations
|
||||||
|
|
|
||||||
|
|
@ -7,3 +7,6 @@ client:
|
||||||
# - Client does not parse or respect retry: field timing
|
# - Client does not parse or respect retry: field timing
|
||||||
# - Client does not send Last-Event-ID header
|
# - Client does not send Last-Event-ID header
|
||||||
- sse-retry
|
- sse-retry
|
||||||
|
# Not supporting versions > 1 year old
|
||||||
|
- auth/2025-03-26-oauth-metadata-backcompat
|
||||||
|
- auth/2025-03-26-oauth-endpoint-fallback
|
||||||
|
|
|
||||||
|
|
@ -195,6 +195,30 @@ McpTransport transport = new StdioClientTransport(params, McpJsonDefaults.getMap
|
||||||
McpTransport transport = new WebFluxSseClientTransport(webClientBuilder);
|
McpTransport transport = new WebFluxSseClientTransport(webClientBuilder);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Protocol Version Negotiation
|
||||||
|
|
||||||
|
During `initialize()`, the client sends the list of protocol versions its transport supports (newest first) and the server picks one it also supports. The negotiated version is returned on the `InitializeResult`:
|
||||||
|
|
||||||
|
```java
|
||||||
|
McpSyncClient client = McpClient.sync(transport).build();
|
||||||
|
|
||||||
|
InitializeResult initResult = client.initialize();
|
||||||
|
String negotiatedVersion = initResult.protocolVersion();
|
||||||
|
```
|
||||||
|
|
||||||
|
Built-in transports (`StdioClientTransport`, `HttpClientStreamableHttpTransport`, `HttpClientSseClientTransport`) advertise every protocol version the SDK understands (`2024-11-05`, `2025-03-26`, `2025-06-18`, `2025-11-25`) via their default `protocolVersions()` implementation, so negotiation normally settles on the newest version both sides support. To restrict a custom transport to a specific subset of versions, override `protocolVersions()` on your `McpClientTransport` implementation:
|
||||||
|
|
||||||
|
```java
|
||||||
|
public class RestrictedTransport extends StdioClientTransport {
|
||||||
|
// ...
|
||||||
|
@Override
|
||||||
|
public List<String> protocolVersions() {
|
||||||
|
return List.of("2025-06-18"); // only negotiate this version
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If the server responds with a version the transport didn't advertise, `initialize()` fails with an `McpError`.
|
||||||
|
|
||||||
## Client Capabilities
|
## Client Capabilities
|
||||||
|
|
||||||
|
|
@ -333,6 +357,29 @@ try {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Elicitation Complete Notification (SEP-1036)
|
||||||
|
|
||||||
|
After a user finishes an out-of-band URL elicitation flow (for example, completing an OAuth authorization in a browser), the server sends a `notifications/elicitation/complete` message so the client knows it can stop waiting and re-check the outcome. Register a consumer to receive it:
|
||||||
|
|
||||||
|
```java
|
||||||
|
var client = McpClient.sync(transport)
|
||||||
|
.capabilities(ClientCapabilities.builder()
|
||||||
|
.elicitation(true, true)
|
||||||
|
.build())
|
||||||
|
.urlElicitation(urlElicitationHandler)
|
||||||
|
.elicitationCompleteConsumer(notification -> {
|
||||||
|
System.out.println("Elicitation " + notification.elicitationId() + " completed, re-checking outcome");
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
```
|
||||||
|
|
||||||
|
On the server side, send the notification once the out-of-band flow resolves (e.g. after the user completes the OAuth redirect), using the ID assigned to the original `ElicitUrlRequest` and the session that issued it:
|
||||||
|
|
||||||
|
```java
|
||||||
|
server.sendElicitationComplete(exchange.sessionId(),
|
||||||
|
new McpSchema.ElicitationCompleteNotification("oauth-123"));
|
||||||
|
```
|
||||||
|
|
||||||
### Logging Support
|
### Logging Support
|
||||||
|
|
||||||
The client can register a logging consumer to receive log messages from the server and set the minimum logging level to filter messages:
|
The client can register a logging consumer to receive log messages from the server and set the minimum logging level to filter messages:
|
||||||
|
|
@ -367,6 +414,33 @@ var mcpClient = McpClient.sync(transport)
|
||||||
.build();
|
.build();
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Pinging the Server
|
||||||
|
|
||||||
|
The client can send a `ping` request to check that the server is alive and responsive:
|
||||||
|
|
||||||
|
```java
|
||||||
|
McpSyncClient client = McpClient.sync(transport).build();
|
||||||
|
client.initialize();
|
||||||
|
|
||||||
|
Object result = client.ping(); // blocks until the server responds, or the request times out
|
||||||
|
```
|
||||||
|
|
||||||
|
The async equivalent, `McpAsyncClient.ping()`, returns a `Mono<Object>` that completes when the server responds.
|
||||||
|
|
||||||
|
### Request Timeouts and Cancellation
|
||||||
|
|
||||||
|
Every request the client sends (`callTool`, `readResource`, `ping`, etc.) is bounded by the `requestTimeout` configured on the client builder (default 20 seconds):
|
||||||
|
|
||||||
|
```java
|
||||||
|
McpSyncClient client = McpClient.sync(transport)
|
||||||
|
.requestTimeout(Duration.ofSeconds(10))
|
||||||
|
.build();
|
||||||
|
```
|
||||||
|
|
||||||
|
If a response doesn't arrive within that window, the pending call fails with a timeout error (an `McpError` on the sync API, or an error signal on the corresponding `Mono` for the async API) instead of blocking indefinitely. The server builder has an equivalent `requestTimeout(Duration)` option (default 10 hours) bounding requests the server sends to the client, such as sampling or elicitation.
|
||||||
|
|
||||||
|
The SDK does not currently send or process the MCP `notifications/cancelled` message, so timing out a request only stops the caller from waiting on it — it does not notify the other side that the in-flight operation should stop executing.
|
||||||
|
|
||||||
## Using MCP Clients
|
## Using MCP Clients
|
||||||
|
|
||||||
### Tool Execution
|
### Tool Execution
|
||||||
|
|
@ -422,6 +496,22 @@ var client = McpClient.sync(transport)
|
||||||
.build();
|
.build();
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Pagination
|
||||||
|
|
||||||
|
`listTools`, `listResources`, `listResourceTemplates`, and `listPrompts` all accept an optional opaque `cursor` string, and their results carry a `nextCursor` that is non-null while more pages remain. Loop until `nextCursor` is `null` to collect every page:
|
||||||
|
|
||||||
|
```java
|
||||||
|
List<McpSchema.Tool> allTools = new ArrayList<>();
|
||||||
|
String cursor = null;
|
||||||
|
do {
|
||||||
|
ListToolsResult page = client.listTools(cursor);
|
||||||
|
allTools.addAll(page.tools());
|
||||||
|
cursor = page.nextCursor();
|
||||||
|
} while (cursor != null);
|
||||||
|
```
|
||||||
|
|
||||||
|
Each paginated method also accepts an optional `_meta` map alongside the cursor, e.g. `client.listTools(cursor, Map.of("key", "value"))`, for passing request metadata through to the server.
|
||||||
|
|
||||||
### Resource Access
|
### Resource Access
|
||||||
|
|
||||||
Resources represent server-side data sources that clients can access using URI templates. The MCP client provides methods to discover available resources and retrieve their contents through a standardized interface.
|
Resources represent server-side data sources that clients can access using URI templates. The MCP client provides methods to discover available resources and retrieve their contents through a standardized interface.
|
||||||
|
|
|
||||||
194
docs/server.md
194
docs/server.md
|
|
@ -407,6 +407,40 @@ var server = McpServer.sync(transportProvider)
|
||||||
|
|
||||||
The embedded JSON Schema documents themselves (`Tool.inputSchema`, `Tool.outputSchema`, and elicitation `requestedSchema`) are validated against the JSON Schema 2020-12 meta-schema (SEP-1613). Malformed schemas are rejected at build time (`McpServer.build()`) and when calling `addTool()`, throwing an `IllegalArgumentException` that names the offending field. A schema that declares a different dialect via `$schema` is accepted without meta-schema validation.
|
The embedded JSON Schema documents themselves (`Tool.inputSchema`, `Tool.outputSchema`, and elicitation `requestedSchema`) are validated against the JSON Schema 2020-12 meta-schema (SEP-1613). Malformed schemas are rejected at build time (`McpServer.build()`) and when calling `addTool()`, throwing an `IllegalArgumentException` that names the offending field. A schema that declares a different dialect via `$schema` is accepted without meta-schema validation.
|
||||||
|
|
||||||
|
#### Tool Result Content Types
|
||||||
|
|
||||||
|
Besides `TextContent`, a `CallToolResult` can return images, audio, and embedded resources — any combination of these can appear in the same result's `content` list:
|
||||||
|
|
||||||
|
```java
|
||||||
|
var syncToolSpecification = SyncToolSpecification.builder()
|
||||||
|
.tool(Tool.builder("generate-report", schema)
|
||||||
|
.description("Generates a report with mixed content")
|
||||||
|
.build())
|
||||||
|
.callHandler((exchange, request) -> {
|
||||||
|
var text = TextContent.builder("Report summary:").build();
|
||||||
|
|
||||||
|
// Image content: base64-encoded data + MIME type
|
||||||
|
var image = ImageContent.builder(base64PngData, "image/png").build();
|
||||||
|
|
||||||
|
// Audio content: base64-encoded data + MIME type
|
||||||
|
var audio = AudioContent.builder(base64WavData, "audio/wav").build();
|
||||||
|
|
||||||
|
// Embedded resource: wraps a TextResourceContents or BlobResourceContents
|
||||||
|
var resourceContents = TextResourceContents.builder("report://details", "Full details...")
|
||||||
|
.mimeType("text/plain")
|
||||||
|
.build();
|
||||||
|
var embeddedResource = EmbeddedResource.builder(resourceContents).build();
|
||||||
|
|
||||||
|
return CallToolResult.builder()
|
||||||
|
.content(List.of(text, image, audio, embeddedResource))
|
||||||
|
.isError(false)
|
||||||
|
.build();
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
```
|
||||||
|
|
||||||
|
`ImageContent.builder(data, mimeType)` and `AudioContent.builder(data, mimeType)` both take base64-encoded binary data. `EmbeddedResource.builder(resourceContents)` wraps either a `TextResourceContents` (for text data) or a `BlobResourceContents` (for base64-encoded binary data) — see [Reading Binary Resources](#reading-binary-resources) for the `BlobResourceContents` shape.
|
||||||
|
|
||||||
### Resource Specification
|
### Resource Specification
|
||||||
|
|
||||||
Specification of a resource with its handler function.
|
Specification of a resource with its handler function.
|
||||||
|
|
@ -444,6 +478,29 @@ Resources provide context to AI models by exposing data such as: File contents,
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Reading Binary Resources
|
||||||
|
|
||||||
|
Binary resources (images, PDFs, audio, etc.) are returned as `BlobResourceContents`, which carries base64-encoded data instead of the plain `text` field used by `TextResourceContents`:
|
||||||
|
|
||||||
|
```java
|
||||||
|
var binaryResourceSpecification = new McpServerFeatures.SyncResourceSpecification(
|
||||||
|
Resource.builder("file:///logo.png", "Logo")
|
||||||
|
.description("Application logo")
|
||||||
|
.mimeType("image/png")
|
||||||
|
.build(),
|
||||||
|
(exchange, request) -> {
|
||||||
|
String base64Data = Base64.getEncoder().encodeToString(readLogoBytes());
|
||||||
|
return ReadResourceResult.builder(List.of(
|
||||||
|
BlobResourceContents.builder(request.uri(), base64Data)
|
||||||
|
.mimeType("image/png")
|
||||||
|
.build()))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
`ReadResourceResult` accepts a list mixing `TextResourceContents` and `BlobResourceContents`, so a single resource read can return multiple representations if needed.
|
||||||
|
|
||||||
### Resource Subscriptions
|
### Resource Subscriptions
|
||||||
|
|
||||||
When the `subscribe` capability is enabled, clients can subscribe to specific resources and receive targeted `notifications/resources/updated` notifications when those resources change. Only sessions that have explicitly subscribed to a given URI receive the notification — not every connected client.
|
When the `subscribe` capability is enabled, clients can subscribe to specific resources and receive targeted `notifications/resources/updated` notifications when those resources change. Only sessions that have explicitly subscribed to a given URI receive the notification — not every connected client.
|
||||||
|
|
@ -549,6 +606,44 @@ The prompt definition includes name (identifier for the prompt), description (pu
|
||||||
The handler function processes requests and returns formatted templates.
|
The handler function processes requests and returns formatted templates.
|
||||||
The first argument is `McpSyncServerExchange`/`McpAsyncServerExchange` for client interaction, and the second argument is a `GetPromptRequest` instance.
|
The first argument is `McpSyncServerExchange`/`McpAsyncServerExchange` for client interaction, and the second argument is a `GetPromptRequest` instance.
|
||||||
|
|
||||||
|
#### Prompts with Embedded Resources and Images
|
||||||
|
|
||||||
|
A prompt's messages can carry `EmbeddedResource` or `ImageContent` instead of plain text by passing them as the `content` argument of `PromptMessage.builder(role, content)`:
|
||||||
|
|
||||||
|
```java
|
||||||
|
// Prompt that embeds a resource's content in one of its messages
|
||||||
|
var promptWithResource = new McpServerFeatures.SyncPromptSpecification(
|
||||||
|
Prompt.builder("review-file")
|
||||||
|
.description("Reviews a file, embedding its content in the prompt")
|
||||||
|
.arguments(List.of(PromptArgument.builder("resourceUri").required(true).build()))
|
||||||
|
.build(),
|
||||||
|
(exchange, request) -> {
|
||||||
|
String resourceUri = (String) request.arguments().get("resourceUri");
|
||||||
|
var resourceContents = TextResourceContents.builder(resourceUri, loadFileContent(resourceUri))
|
||||||
|
.mimeType("text/plain")
|
||||||
|
.build();
|
||||||
|
var embeddedResource = EmbeddedResource.builder(resourceContents).build();
|
||||||
|
|
||||||
|
return GetPromptResult.builder(List.of(
|
||||||
|
PromptMessage.builder(Role.USER, embeddedResource).build(),
|
||||||
|
PromptMessage.builder(Role.USER, TextContent.builder("Please review the file above.").build()).build()))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Prompt that embeds an image in one of its messages
|
||||||
|
var promptWithImage = new McpServerFeatures.SyncPromptSpecification(
|
||||||
|
Prompt.builder("describe-image")
|
||||||
|
.description("Asks the model to describe an embedded image")
|
||||||
|
.arguments(List.of())
|
||||||
|
.build(),
|
||||||
|
(exchange, request) -> GetPromptResult.builder(List.of(
|
||||||
|
PromptMessage.builder(Role.USER, ImageContent.builder(base64PngData, "image/png").build()).build(),
|
||||||
|
PromptMessage.builder(Role.USER, TextContent.builder("Describe the image above.").build()).build()))
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
### Completion Specification
|
### Completion Specification
|
||||||
|
|
||||||
Completions allow servers to provide argument autocompletion suggestions for prompts and resources:
|
Completions allow servers to provide argument autocompletion suggestions for prompts and resources:
|
||||||
|
|
@ -588,7 +683,42 @@ Completions allow servers to provide argument autocompletion suggestions for pro
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
Completions can be registered for both `PromptReference` and `ResourceReference` types.
|
Completions can be registered for both `PromptReference` and `ResourceReference` types. A `ResourceReference` completion suggests values for a resource template's URI parameters instead of a prompt's arguments:
|
||||||
|
|
||||||
|
=== "Sync"
|
||||||
|
|
||||||
|
```java
|
||||||
|
// Sync completion specification for a resource template argument
|
||||||
|
var syncResourceCompletionSpec = new McpServerFeatures.SyncCompletionSpecification(
|
||||||
|
new McpSchema.ResourceReference("file://{path}"), // Reference to a resource template
|
||||||
|
(exchange, request) -> {
|
||||||
|
String argName = request.argument().name();
|
||||||
|
String partial = request.argument().value();
|
||||||
|
// Return matching suggestions, e.g. matching file paths
|
||||||
|
List<String> suggestions = findMatchingPaths(partial);
|
||||||
|
return new McpSchema.CompleteResult(
|
||||||
|
new McpSchema.CompleteResult.CompleteCompletion(suggestions, suggestions.size(), false)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "Async"
|
||||||
|
|
||||||
|
```java
|
||||||
|
// Async completion specification for a resource template argument
|
||||||
|
var asyncResourceCompletionSpec = new McpServerFeatures.AsyncCompletionSpecification(
|
||||||
|
new McpSchema.ResourceReference("file://{path}"),
|
||||||
|
(exchange, request) -> {
|
||||||
|
String argName = request.argument().name();
|
||||||
|
String partial = request.argument().value();
|
||||||
|
List<String> suggestions = findMatchingPaths(partial);
|
||||||
|
return Mono.just(new McpSchema.CompleteResult(
|
||||||
|
new McpSchema.CompleteResult.CompleteCompletion(suggestions, suggestions.size(), false)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
### Using Sampling from a Server
|
### Using Sampling from a Server
|
||||||
|
|
||||||
|
|
@ -779,6 +909,68 @@ var urlTool = SyncToolSpecification.builder()
|
||||||
.build();
|
.build();
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Elicitation with Enum Values (SEP-1330)
|
||||||
|
|
||||||
|
For form elicitation, the SDK provides typed helpers to build `requestedSchema` properties that render as single-select or multi-select choices, with or without human-readable titles:
|
||||||
|
|
||||||
|
```java
|
||||||
|
// Untitled single-select: plain enum values, no separate display titles
|
||||||
|
var untitledSingle = UntitledSingleSelectEnumSchema.builder()
|
||||||
|
.enumValues("small", "medium", "large")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// Titled single-select: value/title pairs via oneOf+const
|
||||||
|
var titledSingle = TitledSingleSelectEnumSchema.builder()
|
||||||
|
.oneOf(new EnumSchemaOption("sm", "Small"),
|
||||||
|
new EnumSchemaOption("md", "Medium"),
|
||||||
|
new EnumSchemaOption("lg", "Large"))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// Untitled multi-select: an array property whose items are an enum
|
||||||
|
var untitledMulti = UntitledMultiSelectEnumSchema
|
||||||
|
.builder(UntitledMultiSelectItems.builder().enumValues("red", "green", "blue").build())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// Titled multi-select: array items with value/title pairs via anyOf+const
|
||||||
|
var titledMulti = TitledMultiSelectEnumSchema
|
||||||
|
.builder(TitledMultiSelectItems.builder()
|
||||||
|
.anyOf(new EnumSchemaOption("red", "Red"), new EnumSchemaOption("green", "Green"))
|
||||||
|
.build())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// Convert the typed schema helpers into the plain Map<String, Object> shape
|
||||||
|
// expected by ElicitRequest.builder(message, requestedSchema)
|
||||||
|
var mapper = McpJsonDefaults.getMapper();
|
||||||
|
TypeRef<Map<String, Object>> mapType = new TypeRef<>() {};
|
||||||
|
Map<String, Object> requestedSchema = Map.of("type", "object", "properties",
|
||||||
|
Map.of("size", mapper.convertValue(titledSingle, mapType),
|
||||||
|
"colors", mapper.convertValue(titledMulti, mapType)),
|
||||||
|
"required", List.of("size", "colors"));
|
||||||
|
|
||||||
|
ElicitRequest elicitRequest = ElicitRequest.builder("Choose your options", requestedSchema).build();
|
||||||
|
ElicitResult result = exchange.createElicitation(elicitRequest);
|
||||||
|
```
|
||||||
|
|
||||||
|
`LegacyTitledEnumSchema` (a value list plus a parallel `enumNames` list, built with `.enumValues(...)` and `.enumNames(...)`) is also available for clients that predate SEP-1330's `oneOf`/`anyOf` convention, but new schemas should prefer `TitledSingleSelectEnumSchema` / `TitledMultiSelectEnumSchema`.
|
||||||
|
|
||||||
|
### Pinging the Client
|
||||||
|
|
||||||
|
A server can check that a connected client is still responsive by sending a `ping` request through the exchange:
|
||||||
|
|
||||||
|
```java
|
||||||
|
var tool = SyncToolSpecification.builder()
|
||||||
|
.tool(Tool.builder("health-check", emptyJsonSchema).description("Verifies the client is reachable").build())
|
||||||
|
.callHandler((exchange, request) -> {
|
||||||
|
exchange.ping(); // blocks until the client responds, or the request times out
|
||||||
|
return CallToolResult.builder()
|
||||||
|
.content(List.of(new McpSchema.TextContent("Client is responsive")))
|
||||||
|
.build();
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
```
|
||||||
|
|
||||||
|
The async equivalent, `McpAsyncServerExchange.ping()`, returns a `Mono<Object>` that completes when the client responds. Ping requests are subject to the same `requestTimeout` configured on the server builder.
|
||||||
|
|
||||||
### Logging Support
|
### Logging Support
|
||||||
|
|
||||||
The server provides structured logging capabilities that allow sending log messages to clients with different severity levels.
|
The server provides structured logging capabilities that allow sending log messages to clients with different severity levels.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue