Skip to content

Commit 09020ac

Browse files
authored
Modern stateless server for MCP 2026-07-28 (#1170)
A new server in io.modelcontextprotocol.modern, next to the existing one and independent of the legacy McpSchema. Passes all 37 required 2026-07-28 conformance scenarios. - Pick a paradigm, not a server: one McpServer, sync or async repositories, no Reactor in sync code. The immediateExecution flag is gone; the transport decides where blocking code runs, and thread-locals from servlet filters just work. - Primitives are yours: tools, resources, prompts and completions come from repositories you implement. Dynamic, per-tenant, filtered and paginated catalogs, no SDK registry in the way. - Stream when it pays: a handler returns a single result (application/json) or a streaming body (SSE). No more JSON-only stateless vs. SSE-always sessions. - McpFeature as the one extension point: methods, handler, capabilities. - MRTR with requestState HMAC-sealed to principal, expiry, method and tool or resource. - server/discover, caching hints, subscriptions/listen fed by McpChangeFeed. - Tool arguments checked against inputSchema, structuredContent against outputSchema; prompt arguments and completions validated too. - McpException goes out as given; other exception messages stay in the log. - HTTP: POST only, keep-alives, disconnect detection, configurable error statuses, cross-origin refused by default. - Stdio: concurrent requests, cancellation, one writer, line size limit. - Logging dropped, as the revision deprecates it. Also: modern conformance server and CI job, conformance runs moved to 0.2.0-alpha.12, CONTRIBUTING record rules refined. Not yet: SEP-2243 custom headers, tasks, a modern client. Addresses, for users of the modern server - Sessions gone: #274, #499, #577, #738, #952, #1063 - No server-to-client requests: #1061 - Errors: #249, #737, #955, #1081, #1086 - HTTP: #293, #400, #539, #844, #880, #957, #1155 - Stdio: #303, #936 - Repositories: #578, #626, #941, #967 - Context and schema: #492, #766 - 2026-07-28 server side: #1001, #1004, #1009, #1010, #1011 Additionally, reinstall server-everything when its cached copy was pruned: If the install directory exists but lacks the server, e.g. after the OS cleaned up the temporary directory, delete it and install again instead of failing on every run. Signed-off-by: Dariusz Jędrzejczyk <dariusz.jedrzejczyk@broadcom.com>
1 parent 596b1c0 commit 09020ac

67 files changed

Lines changed: 12583 additions & 27 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/workflows/conformance.yml‎

Lines changed: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ on:
66
branches: [main]
77
workflow_dispatch:
88

9+
env:
10+
CONFORMANCE: '@modelcontextprotocol/conformance@0.2.0-alpha.12'
11+
912
jobs:
1013
server:
1114
name: Server Conformance
@@ -26,13 +29,48 @@ jobs:
2629
./mvnw exec:java -pl conformance-tests/server-servlet -Dexec.mainClass="io.modelcontextprotocol.conformance.server.ConformanceServlet" &
2730
timeout 30 bash -c 'until curl -s http://localhost:8080/mcp > /dev/null 2>&1; do sleep 0.5; done'
2831
32+
- name: Set up Node.js
33+
uses: actions/setup-node@v4
34+
with:
35+
node-version: '22'
36+
2937
- name: Run conformance tests
30-
uses: modelcontextprotocol/conformance@v0.1.16
38+
run: |
39+
npx --yes "$CONFORMANCE" server \
40+
--url http://localhost:8080/mcp \
41+
--suite active \
42+
--spec-version 2025-11-25 \
43+
--expected-failures ./conformance-tests/conformance-baseline.yml
44+
45+
modern-server:
46+
name: Modern Server Conformance (2026-07-28)
47+
runs-on: ubuntu-latest
48+
steps:
49+
- uses: actions/checkout@v4
50+
51+
- name: Set up JDK 17
52+
uses: actions/setup-java@v4
53+
with:
54+
java-version: '17'
55+
distribution: 'temurin'
56+
cache: 'maven'
57+
58+
- name: Build and start server
59+
run: |
60+
mvn clean install -DskipTests
61+
mvn exec:java -pl conformance-tests/server-servlet-modern &
62+
timeout 30 bash -c 'until curl -s http://localhost:8081/mcp > /dev/null 2>&1; do sleep 0.5; done'
63+
64+
- name: Set up Node.js
65+
uses: actions/setup-node@v4
3166
with:
32-
mode: server
33-
url: http://localhost:8080/mcp
34-
suite: active
35-
expected-failures: ./conformance-tests/conformance-baseline.yml
67+
node-version: '22'
68+
69+
- name: Run conformance tests
70+
run: |
71+
npx --yes "$CONFORMANCE" server \
72+
--url http://localhost:8081/mcp \
73+
--requirements 2026-07-28
3674
3775
client:
3876
name: Client Conformance
@@ -53,13 +91,18 @@ jobs:
5391
- name: Build client
5492
run: ./mvnw clean install -DskipTests
5593

56-
- name: Run conformance test
57-
uses: modelcontextprotocol/conformance@v0.1.16
94+
- name: Set up Node.js
95+
uses: actions/setup-node@v4
5896
with:
59-
mode: client
60-
command: 'java -jar conformance-tests/client-jdk-http-client/target/client-jdk-http-client-*-SNAPSHOT.jar'
61-
scenario: ${{ matrix.scenario }}
62-
expected-failures: ./conformance-tests/conformance-baseline.yml
97+
node-version: '22'
98+
99+
- name: Run conformance test
100+
run: |
101+
npx --yes "$CONFORMANCE" client \
102+
--command 'java -jar conformance-tests/client-jdk-http-client/target/client-jdk-http-client-*-SNAPSHOT.jar' \
103+
--scenario ${{ matrix.scenario }} \
104+
--spec-version 2025-11-25 \
105+
--expected-failures ./conformance-tests/conformance-baseline.yml
63106
64107
auth:
65108
name: Auth Conformance

‎CONTRIBUTING.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,13 +114,13 @@ Records in `McpSchema` are serialized directly to the MCP JSON wire format. The
114114

115115
When the MCP specification marks a field as required, callers must not be able to construct a structurally invalid record, but the wire parser must still tolerate peers that fail to send it. Follow these rules in addition to the relevant Case A rules (annotation, naming, append-only).
116116

117-
1. **Reject `null` in the compact constructor.** Use `Assert.notNull` for required objects or `Assert.hasText` for required `String` identifiers (`name`, `uri`, `uriTemplate`, `version`). This throws `IllegalArgumentException` at construction time instead of producing a record that fails later in serialization or protocol handling. Overrides Case A Rule 7 for this field.
117+
1. **Reject `null` in the compact constructor** with `Assert.notNull`. This throws `IllegalArgumentException` at construction time instead of producing a record that fails later in serialization or protocol handling. Overrides Case A Rule 7 for this field. The compact constructor is the one path that both Java callers and `fromJson` (Rule 2) go through, so it may only enforce what a wire default can satisfy. Stricter Java-side checks, such as non-empty identifiers (`Assert.hasText` on `name`, `uri`, `uriTemplate`, `version`), belong in the required-first builder factory (Rule 3).
118118
2. **Add a `@JsonCreator` static `fromJson` factory** alongside the canonical constructor. When a required field is absent from the wire, substitute a documented safe default (`""` for strings, `[]` for collections, `{}` for maps, `0` / `0.0` for numerics, `INFO` for `LoggingLevel`, etc.) and log at `WARN` naming the field and the value used. The SDK must not halt the conversation because of a missing field. Place `@JsonCreator` on this `fromJson` factory, never on the canonical constructor (Case A Rule 6 still applies to the canonical constructor itself).
119-
- Exception: `JSONRPCResponse.JSONRPCError` fails fast on missing `code` / `message` because a malformed JSON-RPC error envelope is unrecoverable.
119+
- Exception: do not substitute a default when the missing value would make the message unprocessable anyway. This covers `JSONRPCResponse.JSONRPCError` (`code` / `message`), since a malformed JSON-RPC error envelope is unrecoverable, and the identifiers a request is dispatched on (e.g. `name` of `tools/call` and `prompts/get`, `uri` of `resources/read`, `ref` of `completion/complete`). A missing dispatch identifier must fail fast with an `INVALID_PARAMS` error, not an internal error.
120120
3. **Provide a required-first builder factory** `builder(req1, req2, …)` and remove the corresponding setters from the `Builder`. A no-arg `builder()` factory must not exist on a record that has required fields. If one already exists for source compatibility, mark it `@Deprecated`.
121121
4. **Add tests per required field**:
122122
- Constructing the record with `null` for the field throws `IllegalArgumentException`.
123-
- Deserializing JSON *without* the field succeeds and yields the documented default.
123+
- Deserializing JSON *without* the field succeeds and yields the documented default, or, for a Rule 2 exception, fails.
124124
- Deserializing JSON with an extra *unknown* field still succeeds (Case A Rule 8 also applies).
125125

126126
### Example

‎conformance-tests/VALIDATION_RESULTS.md‎

Lines changed: 68 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
# MCP Java SDK Conformance Test Validation Results
22

3-
Last validated: **2026-08-17** against conformance suite
4-
**`@modelcontextprotocol/conformance@0.2.0-alpha.11`** (SDK at `main`, 2.0.1-SNAPSHOT), targetting
5-
version 2025-11-25 (`--spec-version 2025-11-25`).
3+
Last validated: **2026-10-02** against conformance suite
4+
**`@modelcontextprotocol/conformance@0.2.0-alpha.12`** (2.1.0-SNAPSHOT), targeting version 2025-11-25
5+
(`--spec-version 2025-11-25`) for the legacy server and clients, and the 2026-07-28 requirement set
6+
(`--requirements 2026-07-28`) for the modern server. Auth results below were last validated with
7+
`0.2.0-alpha.11`.
68

79
## Summary
810

11+
**Modern Server Tests (2026-07-28 requirements):** 37/37 required scenarios passed (`server-servlet-modern`)
912
**Server Tests (active suite):** 73/73 checks passed (31 scenarios, 100%)
1013
**Server Tests (SEP-1613 `json-schema-2020-12`):** 5/5 checks passed (SEP-2106 checks skipped — post-2025-11-25 spec additions)
1114
**Client Tests:** 3/4 scenarios passed; `sse-retry` fails (tracked in `conformance-baseline.yml`)
@@ -14,6 +17,34 @@ version 2025-11-25 (`--spec-version 2025-11-25`).
1417
Baseline check passed on every run: all failures are expected per
1518
[`conformance-baseline.yml`](conformance-baseline.yml).
1619

20+
## Modern Server Test Results (2026-07-28)
21+
22+
The `server-servlet-modern` module serves the stateless 2026-07-28 revision with
23+
`io.modelcontextprotocol.modern.server.McpServer` over `HttpServletMcpTransport`.
24+
25+
### Required — Passing (37/37 scenarios)
26+
27+
- **Stateless lifecycle (SEP-2575):** `_meta` validation, `server/discover`, version negotiation,
28+
`MCP-Protocol-Version`/`Mcp-Method` header mismatch, `-32021` capability enforcement, removed
29+
methods answered `404`/`-32601`, `subscriptions/listen` acknowledgement and filtering
30+
- **Tools, Resources, Prompts, Completion:** all content types, progress, resource templates,
31+
SEP-2164 not-found errors
32+
- **Caching (SEP-2549):** `ttlMs`/`cacheScope` on list results and `resources/read`
33+
- **InputRequiredResult / MRTR (SEP-2322):** all 14 scenarios, including multi-round, tampered
34+
`requestState` and capability checks
35+
- **Security:** DNS rebinding protection, SSE streams
36+
37+
### Not scored for 2026-07-28
38+
39+
These run but don't count: the frozen requirement set marks extensions and anything that was pending
40+
in the anchor release (`0.2.0-alpha.10`) as `not_scored`. Pending scenarios are still spec requirements.
41+
42+
- **Passing:** `json-schema-2020-12` (8/8 checks, including the SEP-2106 `allOf`/`anyOf`,
43+
`if`/`then`/`else` and `$anchor` checks), `http-header-validation` (14/14)
44+
- **Failing — `http-custom-header-server-validation` (SEP-2243 custom headers):** not implemented,
45+
see [Known Limitations](#known-limitations)
46+
- **Failing — tasks extension (`tasks-*`, SEP-2663):** not implemented
47+
1748
## Server Test Results
1849

1950
### Active Suite — Passing (31/31 scenarios, 73/73 checks)
@@ -66,9 +97,38 @@ of the 0.2.0-alpha auth suite.
6697

6798
1. **Client SSE Retry:** client doesn't parse or respect the `retry:` field,
6899
reconnects immediately, and doesn't send the `Last-Event-ID` header
100+
2. **Modern server: SEP-2243 custom headers (`Mcp-Param-{Name}`) are not supported.** SEP-2243 is part
101+
of 2026-07-28 and the server-side requirements are MUSTs; only the scenario's pending status keeps
102+
it out of the score. Today the five custom-header checks report "not testable" because no tool
103+
carries `x-mcp-header`. Adding such a tool would turn them into real failures, because nothing
104+
validates the headers yet. Supporting it needs SDK work:
105+
- **Tool definitions:** `Tool.inputSchema` is a free-form map, so `x-mcp-header` can already be
106+
written, but nothing enforces the definition rules: value non-empty, ASCII without space or `:`,
107+
case-insensitively unique per tool, only on `integer`/`string`/`boolean` parameters (not `number`).
108+
- **Request validation on `tools/call`:** for each designated parameter, Base64-decode
109+
`=?base64?…?=` values, check the header matches the body value (integers as decimal strings,
110+
booleans as `true`/`false`), reject headers with invalid characters, don't expect a header when
111+
the value is null or omitted, and reject a missing required parameter. Failures are answered with
112+
`400` and `-32020`.
113+
- **Where it lives:** the check needs the called tool's `inputSchema`, which
114+
`HttpServletMcpTransport` doesn't have. `ToolsFeature` already looks the `Tool` up through
115+
`McpSyncToolRepository#find` / `McpAsyncToolRepository#find` and validates the arguments against
116+
`inputSchema` before calling the tool, so the header check fits there. What's missing is a way to
117+
get the raw `Mcp-Param-*` headers from the transport to the feature (e.g. through the
118+
`McpTransportContext` on `McpRequestContext`).
69119

70120
## Running Tests
71121

122+
### Modern Server (2026-07-28)
123+
```bash
124+
./mvnw clean install -DskipTests
125+
./mvnw exec:java -pl conformance-tests/server-servlet-modern
126+
127+
# In another terminal
128+
npx @modelcontextprotocol/conformance@0.2.0-alpha.12 server \
129+
--url http://localhost:8081/mcp --requirements 2026-07-28
130+
```
131+
72132
### Server (active suite)
73133
```bash
74134
# Build and start server
@@ -77,21 +137,21 @@ mvn exec:java -pl conformance-tests/server-servlet \
77137
-Dexec.mainClass="io.modelcontextprotocol.conformance.server.ConformanceServlet"
78138

79139
# Run tests (in another terminal, from the repo root)
80-
npx @modelcontextprotocol/conformance@0.2.0-alpha.11 server \
81-
--url http://localhost:8080/mcp --suite active \
140+
npx @modelcontextprotocol/conformance@0.2.0-alpha.12 server \
141+
--url http://localhost:8080/mcp --suite active --spec-version 2025-11-25 \
82142
--expected-failures ./conformance-tests/conformance-baseline.yml
83143
```
84144

85145
### Server (SEP-1613 scenario)
86146
```bash
87-
npx @modelcontextprotocol/conformance@0.2.0-alpha.11 server \
88-
--url http://localhost:8080/mcp --scenario json-schema-2020-12
147+
npx @modelcontextprotocol/conformance@0.2.0-alpha.12 server \
148+
--url http://localhost:8080/mcp --scenario json-schema-2020-12 --spec-version 2025-11-25
89149
```
90150

91151
### Client
92152
```bash
93153
for scenario in initialize tools_call elicitation-sep1034-client-defaults sse-retry; do
94-
npx @modelcontextprotocol/conformance@0.2.0-alpha.11 client \
154+
npx @modelcontextprotocol/conformance@0.2.0-alpha.12 client --spec-version 2025-11-25 \
95155
--command "java -jar conformance-tests/client-jdk-http-client/target/client-jdk-http-client-*.jar" \
96156
--scenario $scenario \
97157
--expected-failures ./conformance-tests/conformance-baseline.yml

‎conformance-tests/pom.xml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
<module>client-jdk-http-client</module>
2929
<module>client-spring-http-client</module>
3030
<module>server-servlet</module>
31+
<module>server-servlet-modern</module>
3132
</modules>
3233

3334
</project>
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# MCP Conformance Tests - Modern Servlet Server
2+
3+
Conformance server for the stateless **2026-07-28** MCP revision, built on
4+
`io.modelcontextprotocol.modern.server.McpServer` and `HttpServletMcpTransport` in an embedded Tomcat.
5+
The legacy (2025-11-25) server lives in [`server-servlet`](../server-servlet).
6+
7+
See [VALIDATION_RESULTS.md](../VALIDATION_RESULTS.md) for the latest results.
8+
9+
## Running
10+
11+
```bash
12+
./mvnw clean install -DskipTests
13+
./mvnw exec:java -pl conformance-tests/server-servlet-modern
14+
```
15+
16+
The server listens on `http://localhost:8081/mcp` (override the port with `-Dport=<port>`).
17+
18+
```bash
19+
npx @modelcontextprotocol/conformance@0.2.0-alpha.12 server \
20+
--url http://localhost:8081/mcp --requirements 2026-07-28
21+
```
22+
23+
## Fixtures
24+
25+
Besides the standard `test_*` tools, resources and prompts, the server exposes the fixtures the
26+
2026-07-28 scenarios probe for:
27+
28+
- **SEP-2575 diagnostics:** `test_missing_capability`, `test_streaming_elicitation`, `test_logging_tool`,
29+
`test_trigger_tool_change`, `test_trigger_prompt_change`
30+
- **SEP-2322 MRTR:** `test_input_required_result_*` tools and the `test_input_required_result_prompt` prompt
31+
- **SEP-1613 / SEP-2106:** `json_schema_2020_12_tool`
32+
33+
SEP-2243 custom headers (`x-mcp-header` / `Mcp-Param-{Name}`) are not supported yet; see
34+
[Known Limitations](../VALIDATION_RESULTS.md#known-limitations).
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
<parent>
7+
<groupId>io.modelcontextprotocol.sdk</groupId>
8+
<artifactId>conformance-tests</artifactId>
9+
<version>2.2.0-SNAPSHOT</version>
10+
</parent>
11+
<artifactId>server-servlet-modern</artifactId>
12+
<packaging>jar</packaging>
13+
<name>MCP Conformance Tests - Modern Servlet Server</name>
14+
<description>Servlet Server conformance tests for the modern (2026-07-28) Java MCP SDK server</description>
15+
<url>https://github.com/modelcontextprotocol/java-sdk</url>
16+
17+
<scm>
18+
<url>https://github.com/modelcontextprotocol/java-sdk</url>
19+
<connection>scm:git:git://github.com/modelcontextprotocol/java-sdk.git</connection>
20+
<developerConnection>scm:git:ssh://git@github.com/modelcontextprotocol/java-sdk.git</developerConnection>
21+
</scm>
22+
23+
<properties>
24+
<module.name>io.modelcontextprotocol.sdk.conformance.server.servlet.modern</module.name>
25+
<maven.deploy.skip>true</maven.deploy.skip>
26+
</properties>
27+
28+
<dependencies>
29+
<dependency>
30+
<groupId>io.modelcontextprotocol.sdk</groupId>
31+
<artifactId>mcp</artifactId>
32+
<version>2.2.0-SNAPSHOT</version>
33+
</dependency>
34+
35+
<dependency>
36+
<groupId>org.slf4j</groupId>
37+
<artifactId>slf4j-api</artifactId>
38+
<version>${slf4j-api.version}</version>
39+
</dependency>
40+
41+
<dependency>
42+
<groupId>ch.qos.logback</groupId>
43+
<artifactId>logback-classic</artifactId>
44+
<version>${logback.version}</version>
45+
</dependency>
46+
47+
<dependency>
48+
<groupId>jakarta.servlet</groupId>
49+
<artifactId>jakarta.servlet-api</artifactId>
50+
<version>${jakarta.servlet.version}</version>
51+
<scope>provided</scope>
52+
</dependency>
53+
54+
<dependency>
55+
<groupId>org.apache.tomcat.embed</groupId>
56+
<artifactId>tomcat-embed-core</artifactId>
57+
<version>${tomcat.version}</version>
58+
</dependency>
59+
</dependencies>
60+
61+
<build>
62+
<plugins>
63+
<plugin>
64+
<groupId>org.codehaus.mojo</groupId>
65+
<artifactId>exec-maven-plugin</artifactId>
66+
<version>3.1.0</version>
67+
<configuration>
68+
<mainClass>io.modelcontextprotocol.conformance.server.modern.ModernConformanceServlet</mainClass>
69+
<skip>false</skip>
70+
</configuration>
71+
</plugin>
72+
</plugins>
73+
</build>
74+
75+
</project>

0 commit comments

Comments
 (0)