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:
Christian Tzolov 2026-08-17 19:57:29 +02:00 • committed by Daniel Garnier-Moiroux
commit 2fd159a79a
7 changed files with 432 additions and 105 deletions

View file

@ -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'
- name: Run conformance tests
uses: modelcontextprotocol/conformance@v0.1.11
uses: modelcontextprotocol/conformance@v0.1.16
with:
mode: server
url: http://localhost:8080/mcp
@ -54,7 +54,7 @@ jobs:
run: mvn clean install -DskipTests
- name: Run conformance test
uses: modelcontextprotocol/conformance@v0.1.11
uses: modelcontextprotocol/conformance@v0.1.16
with:
mode: client
command: 'java -jar conformance-tests/client-jdk-http-client/target/client-jdk-http-client-*-SNAPSHOT.jar'

64
CHANGELOG.md Normal file
View 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)

View file

@ -6,40 +6,35 @@ The SDK tracks implementation of MCP spec components via GitHub Projects, with a
## 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
- **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
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.
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
- 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
### Future Directions
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
- Additional transport implementations
- Performance optimizations
Development is tracked via [GitHub Issues](https://github.com/modelcontextprotocol/java-sdk/issues) and [GitHub Projects](https://github.com/orgs/modelcontextprotocol/projects).

View file

@ -1,127 +1,110 @@
# 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
**Server Tests (active suite):** 44/44 passed (31 scenarios, 100%)
**Server Tests (spec 2025-11-25):** 4/4 passed — SEP-1613 `json-schema-2020-12` scenario ✨
**Client Tests:** 3/4 scenarios passed (9/10 checks passed)
**Auth Tests:** 15/15 scenarios fully passing (195 passed, 0 failed, 0 warnings, 100% scenarios, 100% checks)
**Server Tests (active suite):** 73/73 checks passed (31 scenarios, 100%)
**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; `sse-retry` fails (tracked in `conformance-baseline.yml`)
**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
### 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
- **Tools (13/13):** All scenarios including progress notifications, sampling, elicitation ✨
- **Elicitation (10/10):** SEP-1034 defaults (5 checks), SEP-1330 enums (5 checks)
- **Resources (7/7):** list, read-text, read-binary, templates-read, subscribe, unsubscribe, SEP-2164 resource-not-found
- **Prompts (5/5):** list, simple, with-args, embedded-resource, with-image
- **SSE Transport (2/2):** Multiple streams
- **Security (2/2):** Localhost validation passes, DNS rebinding protection
- **Lifecycle & Utilities:** initialize, ping, logging-set-level, completion-complete
- **Tools (13/13):** all scenarios including progress notifications, sampling, elicitation
- **Elicitation:** SEP-1034 defaults (6 checks), SEP-1330 enums (6 checks)
- **Resources:** list, read-text, read-binary, templates-read, subscribe, unsubscribe
- **Prompts:** list, simple, with-args, embedded-resource, with-image
- **SSE Transport:** multiple streams
- **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
- `inputSchema.$schema` field preserved
- `inputSchema.$defs` field preserved
- `inputSchema.additionalProperties` field preserved
- `json_schema_2020_12_tool` found; `$schema`, `$defs`, and `additionalProperties`
fields preserved; every JSON-RPC message valid per the spec JSON schema for the
negotiated spec version (`wire-schema-valid`)
- SEP-2106 checks (composition/conditional/anchor keywords) reported SKIPPED:
they postdate the 2025-11-25 spec release and are excluded from scoring
## Client Test Results
### Passing (3/4 scenarios, 9/10 checks)
### Passing (3/4 scenarios)
- **initialize (1/1):** Protocol negotiation, clientInfo, capabilities
- **tools_call (1/1):** Tool discovery and invocation
- **elicitation-sep1034-client-defaults (5/5):** Default values for string, integer, number, enum, boolean
- **initialize (1/1):** protocol negotiation, clientInfo, capabilities
- **tools_call (2/2):** tool discovery and invocation
- **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):**
- ✅ Reconnects after stream closure
- ❌ Does not respect retry timing
- ⚠️ Does not send Last-Event-ID header (SHOULD requirement)
**Issue:** Client treats `retry:` SSE field as invalid instead of parsing it for reconnection timing.
- **sse-retry:** client does not parse/respect the `retry:` SSE field timing and
does not send the `Last-Event-ID` header (SHOULD requirement). Expected failure,
listed in `conformance-baseline.yml`.
## 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
- **auth/metadata-var1 (13/13):** Metadata discovery variant 1
- **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
Note: `auth/resource-mismatch` (present in earlier suite versions) is no longer part
of the 0.2.0-alpha auth suite.
## 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
### Server (active suite)
```bash
# Start server
./mvnw compile -pl conformance-tests/server-servlet -am exec:java
# Build and start server
./mvnw clean install -DskipTests
mvn exec:java -pl conformance-tests/server-servlet \
-Dexec.mainClass="io.modelcontextprotocol.conformance.server.ConformanceServlet"
# Run tests (in another terminal)
npx @modelcontextprotocol/conformance server --url http://localhost:8080/mcp --suite active
# Run tests (in another terminal, from the repo root)
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
# Start server (if not already running)
./mvnw compile -pl conformance-tests/server-servlet -am exec:java
# 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
npx @modelcontextprotocol/conformance@0.2.0-alpha.11 server \
--url http://localhost:8080/mcp --scenario json-schema-2020-12
```
### Client
```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
npx @modelcontextprotocol/conformance client \
--command "java -jar target/client-jdk-http-client-1.1.0-SNAPSHOT.jar" \
--scenario $scenario
npx @modelcontextprotocol/conformance@0.2.0-alpha.11 client \
--command "java -jar conformance-tests/client-jdk-http-client/target/client-jdk-http-client-*.jar" \
--scenario $scenario \
--expected-failures ./conformance-tests/conformance-baseline.yml
done
```
### Auth (Spring HTTP Client)
Ensure you run with the conformance testing suite `0.1.15` or higher.
```bash
# Build
cd conformance-tests/client-spring-http-client
../../mvnw clean package -DskipTests
# Run auth suite
npx @modelcontextprotocol/conformance@0.1.15 client \
npx @modelcontextprotocol/conformance@0.2.0-alpha.11 client \
--spec-version 2025-11-25 \
--command "java -jar target/client-spring-http-client-1.1.0-SNAPSHOT.jar" \
--suite auth
--command "java -jar conformance-tests/client-spring-http-client/target/client-spring-http-client-*.jar" \
--suite auth \
--expected-failures ./conformance-tests/conformance-baseline.yml
```
## Recommendations

View file

@ -7,3 +7,6 @@ client:
# - Client does not parse or respect retry: field timing
# - Client does not send Last-Event-ID header
- sse-retry
# Not supporting versions > 1 year old
- auth/2025-03-26-oauth-metadata-backcompat
- auth/2025-03-26-oauth-endpoint-fallback

View file

@ -195,6 +195,30 @@ McpTransport transport = new StdioClientTransport(params, McpJsonDefaults.getMap
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
@ -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
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();
```
### 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
### Tool Execution
@ -422,6 +496,22 @@ var client = McpClient.sync(transport)
.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
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.

View file

@ -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.
#### 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
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
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 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
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
@ -779,6 +909,68 @@ var urlTool = SyncToolSpecification.builder()
.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
The server provides structured logging capabilities that allow sending log messages to clients with different severity levels.