Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,17 @@ updates:
directory: '/'
schedule:
interval: monthly
labels:
- 'github_actions'
- 'waiting for triage'
- package-ecosystem: 'maven'
directory: '/'
schedule:
interval: monthly
open-pull-requests-limit: 10
labels:
- 'dependencies'
- 'waiting for triage'
ignore:
# Freeze production dependencies of mcp-core
- dependency-name: 'org.slf4j:slf4j-api'
Expand Down
30 changes: 11 additions & 19 deletions conformance-tests/VALIDATION_RESULTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,22 @@

## Summary

**Server Tests:** 37/40 passed (92.5%)
**Server Tests:** 40/40 passed (100%)
**Client Tests:** 3/4 scenarios passed (9/10 checks passed)
**Auth Tests:** 12/14 scenarios fully passing (178 passed, 1 failed, 1 warning, 85.7% scenarios, 98.9% checks)

## Server Test Results

### Passing (37/40)
### Passing (40/40)

- **Lifecycle & Utilities (4/4):** initialize, ping, logging-set-level, completion-complete
- **Tools (11/11):** All scenarios including progress notifications ✨
- **Elicitation (10/10):** SEP-1034 defaults (5 checks), SEP-1330 enums (5 checks)
- **Resources (4/6):** list, read-text, read-binary, templates-read
- **Resources (6/6):** list, read-text, read-binary, templates-read, subscribe, unsubscribe
- **Prompts (4/4):** list, simple, with-args, embedded-resource, with-image
- **SSE Transport (2/2):** Multiple streams
- **Security (2/2):** Localhost validation passes, DNS rebinding protection

### Failing (3/40)

1. **resources-subscribe** - Not implemented in SDK
2. **resources-unsubscribe** - Not implemented in SDK

## Client Test Results

### Passing (3/4 scenarios, 9/10 checks)
Expand Down Expand Up @@ -68,18 +63,16 @@ Uses the `client-spring-http-client` module with Spring Security OAuth2 and the

## Known Limitations

1. **Resource Subscriptions:** SDK doesn't implement `resources/subscribe` and `resources/unsubscribe` handlers
2. **Client SSE Retry:** Client doesn't parse or respect the `retry:` field, reconnects immediately, and doesn't send Last-Event-ID header
3. **Auth Scope Step-Up:** Client does not fully handle scope step-up challenges where the server requests additional scopes after initial authorization
4. **Auth Basic CIMD:** Minor conformance warning in the basic Client-Initiated Metadata Discovery flow
1. **Client SSE Retry:** Client doesn't parse or respect the `retry:` field, reconnects immediately, and doesn't send Last-Event-ID header
2. **Auth Scope Step-Up:** Client does not fully handle scope step-up challenges where the server requests additional scopes after initial authorization
3. **Auth Basic CIMD:** Minor conformance warning in the basic Client-Initiated Metadata Discovery flow

## Running Tests

### Server
```bash
# Start server
cd conformance-tests/server-servlet
../../mvnw compile exec:java -Dexec.mainClass="io.modelcontextprotocol.conformance.server.ConformanceServlet"
./mvnw compile -pl conformance-tests/server-servlet -am exec:java

# Run tests (in another terminal)
npx @modelcontextprotocol/conformance server --url http://localhost:8080/mcp --suite active
Expand All @@ -94,7 +87,7 @@ cd conformance-tests/client-jdk-http-client
# 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.0.0-SNAPSHOT.jar" \
--command "java -jar target/client-jdk-http-client-1.1.0-SNAPSHOT.jar" \
--scenario $scenario
done
```
Expand All @@ -111,14 +104,13 @@ cd conformance-tests/client-spring-http-client
# Run auth suite
npx @modelcontextprotocol/conformance@0.1.15 client \
--spec-version 2025-11-25 \
--command "java -jar target/client-spring-http-client-0.18.0-SNAPSHOT.jar" \
--command "java -jar target/client-spring-http-client-1.1.0-SNAPSHOT.jar" \
--suite auth
```

## Recommendations

### High Priority
1. Fix client SSE retry field handling in `HttpClientStreamableHttpTransport`
2. Implement resource subscription handlers in `McpStatelessAsyncServer`
3. Implement CIMD
4. Implement scope step up
2. Implement CIMD
3. Implement scope step up
14 changes: 7 additions & 7 deletions conformance-tests/client-jdk-http-client/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ cd conformance-tests/client-jdk-http-client

This creates an executable JAR at:
```
target/client-jdk-http-client-1.0.0-SNAPSHOT.jar
target/client-jdk-http-client-1.1.0-SNAPSHOT.jar
```

## Running Tests
Expand All @@ -65,27 +65,27 @@ Run a single scenario:

```bash
npx @modelcontextprotocol/conformance client \
--command "java -jar conformance-tests/client-jdk-http-client/target/client-jdk-http-client-1.0.0-SNAPSHOT.jar" \
--command "java -jar conformance-tests/client-jdk-http-client/target/client-jdk-http-client-1.1.0-SNAPSHOT.jar" \
--scenario initialize

npx @modelcontextprotocol/conformance client \
--command "java -jar conformance-tests/client-jdk-http-client/target/client-jdk-http-client-1.0.0-SNAPSHOT.jar" \
--command "java -jar conformance-tests/client-jdk-http-client/target/client-jdk-http-client-1.1.0-SNAPSHOT.jar" \
--scenario tools_call

npx @modelcontextprotocol/conformance client \
--command "java -jar conformance-tests/client-jdk-http-client/target/client-jdk-http-client-1.0.0-SNAPSHOT.jar" \
--command "java -jar conformance-tests/client-jdk-http-client/target/client-jdk-http-client-1.1.0-SNAPSHOT.jar" \
--scenario elicitation-sep1034-client-defaults

npx @modelcontextprotocol/conformance client \
--command "java -jar conformance-tests/client-jdk-http-client/target/client-jdk-http-client-1.0.0-SNAPSHOT.jar" \
--command "java -jar conformance-tests/client-jdk-http-client/target/client-jdk-http-client-1.1.0-SNAPSHOT.jar" \
--scenario sse-retry
```

Run with verbose output:

```bash
npx @modelcontextprotocol/conformance client \
--command "java -jar conformance-tests/client-jdk-http-client/target/client-jdk-http-client-1.0.0-SNAPSHOT.jar" \
--command "java -jar conformance-tests/client-jdk-http-client/target/client-jdk-http-client-1.1.0-SNAPSHOT.jar" \
--scenario initialize \
--verbose
```
Expand All @@ -96,7 +96,7 @@ You can also run the client manually if you have a test server:

```bash
export MCP_CONFORMANCE_SCENARIO=initialize
java -jar conformance-tests/client-jdk-http-client/target/client-jdk-http-client-1.0.0-SNAPSHOT.jar http://localhost:3000/mcp
java -jar conformance-tests/client-jdk-http-client/target/client-jdk-http-client-1.1.0-SNAPSHOT.jar http://localhost:3000/mcp
```

## Test Results
Expand Down
22 changes: 15 additions & 7 deletions conformance-tests/client-jdk-http-client/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<parent>
<groupId>io.modelcontextprotocol.sdk</groupId>
<artifactId>conformance-tests</artifactId>
<version>1.1.0-SNAPSHOT</version>
<version>2.0.0-SNAPSHOT</version>
</parent>
<artifactId>client-jdk-http-client</artifactId>
<packaging>jar</packaging>
Expand All @@ -16,19 +16,26 @@

<scm>
<url>https://github.com/modelcontextprotocol/java-sdk</url>
<connection>git://github.com/modelcontextprotocol/java-sdk.git</connection>
<developerConnection>git@github.com/modelcontextprotocol/java-sdk.git</developerConnection>
<connection>scm:git:git://github.com/modelcontextprotocol/java-sdk.git</connection>
<developerConnection>scm:git:ssh://git@github.com/modelcontextprotocol/java-sdk.git</developerConnection>
</scm>

<properties>
<maven.deploy.skip>true</maven.deploy.skip>
</properties>

<dependencies>
<dependency>
<groupId>io.modelcontextprotocol.sdk</groupId>
<artifactId>mcp</artifactId>
<version>1.1.0-SNAPSHOT</version>
<version>2.0.0-SNAPSHOT</version>
</dependency>

<!-- mcp-core for EnterpriseAuth / EnterpriseAuthProvider (SEP-990) -->
<dependency>
<groupId>io.modelcontextprotocol.sdk</groupId>
<artifactId>mcp-core</artifactId>
<version>2.0.0-SNAPSHOT</version>
</dependency>

<!-- Logging -->
Expand Down Expand Up @@ -57,7 +64,8 @@
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>io.modelcontextprotocol.conformance.client.ConformanceJdkClientMcpClient</mainClass>
<mainClass>
io.modelcontextprotocol.conformance.client.ConformanceJdkClientMcpClient</mainClass>
</transformer>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
Expand All @@ -79,4 +87,4 @@
</plugins>
</build>

</project>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,15 @@

import java.time.Duration;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.modelcontextprotocol.client.McpClient;
import io.modelcontextprotocol.client.McpSyncClient;
import io.modelcontextprotocol.client.auth.DiscoverAndRequestJwtAuthGrantOptions;
import io.modelcontextprotocol.client.auth.EnterpriseAuth;
import io.modelcontextprotocol.client.auth.EnterpriseAuthProvider;
import io.modelcontextprotocol.client.auth.EnterpriseAuthProviderOptions;
import io.modelcontextprotocol.client.transport.HttpClientStreamableHttpTransport;
import io.modelcontextprotocol.spec.McpSchema;

Expand Down Expand Up @@ -53,13 +60,17 @@ public static void main(String[] args) {
case "sse-retry":
runSSERetryScenario(serverUrl);
break;
case "auth/cross-app-access-complete-flow":
runCrossAppAccessCompleteFlowScenario(serverUrl);
break;
default:
System.err.println("Unknown scenario: " + scenario);
System.err.println("Available scenarios:");
System.err.println(" - initialize");
System.err.println(" - tools_call");
System.err.println(" - elicitation-sep1034-client-defaults");
System.err.println(" - sse-retry");
System.err.println(" - auth/cross-app-access-complete-flow");
System.exit(1);
}
System.exit(0);
Expand Down Expand Up @@ -283,4 +294,82 @@ private static void runSSERetryScenario(String serverUrl) throws Exception {
}
}

/**
* Cross-App Access scenario: Tests SEP-990 Enterprise Managed Authorization flow.
* <p>
* Reads context from {@code MCP_CONFORMANCE_CONTEXT} (JSON) containing:
* {@code client_id}, {@code client_secret}, {@code idp_client_id},
* {@code idp_id_token}, {@code idp_issuer}, {@code idp_token_endpoint}.
* <p>
* Uses {@link EnterpriseAuthProvider} with an assertion callback that performs RFC
* 8693 token exchange at the IdP, then exchanges the ID-JAG for an access token at
* the MCP authorization server via RFC 7523 JWT Bearer grant.
* @param serverUrl the URL of the MCP server
* @throws Exception if any error occurs during execution
*/
private static void runCrossAppAccessCompleteFlowScenario(String serverUrl) throws Exception {
String contextEnv = System.getenv("MCP_CONFORMANCE_CONTEXT");
if (contextEnv == null || contextEnv.isEmpty()) {
System.err.println("Error: MCP_CONFORMANCE_CONTEXT environment variable is not set");
System.exit(1);
}

CrossAppAccessContext ctx = new ObjectMapper().readValue(contextEnv, CrossAppAccessContext.class);

java.net.http.HttpClient httpClient = java.net.http.HttpClient.newHttpClient();

EnterpriseAuthProviderOptions options = EnterpriseAuthProviderOptions.builder()
.clientId(ctx.clientId())
.clientSecret(ctx.clientSecret())
.assertionCallback(assertionCtx -> {
// RFC 8693 token exchange at the IdP: ID Token → ID-JAG
DiscoverAndRequestJwtAuthGrantOptions jagOptions = DiscoverAndRequestJwtAuthGrantOptions
.builder()
.idpUrl(ctx.idpIssuer())
.idpTokenEndpoint(ctx.idpTokenEndpoint())
.idToken(ctx.idpIdToken())
.clientId(ctx.idpClientId())
.audience(assertionCtx.getAuthorizationServerUrl().toString())
.resource(assertionCtx.getResourceUrl().toString())
.build();
return EnterpriseAuth.discoverAndRequestJwtAuthorizationGrant(jagOptions, httpClient);
})
.build();

EnterpriseAuthProvider provider = new EnterpriseAuthProvider(options, httpClient);

HttpClientStreamableHttpTransport transport = HttpClientStreamableHttpTransport.builder(serverUrl)
.httpRequestCustomizer(provider)
.build();

McpSyncClient client = McpClient.sync(transport)
.clientInfo(new McpSchema.Implementation("test-client", "1.0.0"))
.requestTimeout(Duration.ofSeconds(30))
.build();

try {
client.initialize();
System.out.println("Successfully connected to MCP server");

client.listTools();
System.out.println("Successfully listed tools");
}
finally {
client.close();
System.out.println("Connection closed successfully");
}
}

/**
* Context provided by the conformance suite for the cross-app-access-complete-flow
* scenario via the {@code MCP_CONFORMANCE_CONTEXT} environment variable.
*/
@JsonIgnoreProperties(ignoreUnknown = true)
private record CrossAppAccessContext(@JsonProperty("client_id") String clientId,
@JsonProperty("client_secret") String clientSecret,
@JsonProperty("idp_client_id") String idpClientId,
@JsonProperty("idp_id_token") String idpIdToken, @JsonProperty("idp_issuer") String idpIssuer,
@JsonProperty("idp_token_endpoint") String idpTokenEndpoint) {
}

}
10 changes: 5 additions & 5 deletions conformance-tests/client-spring-http-client/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ cd conformance-tests/client-spring-http-client

This creates an executable JAR at:
```
target/client-spring-http-client-0.18.0-SNAPSHOT.jar
target/client-spring-http-client-1.1.0-SNAPSHOT.jar
```

## Running Tests
Expand All @@ -79,7 +79,7 @@ Run the full auth suite:
```bash
npx @modelcontextprotocol/conformance@0.1.15 client \
--spec-version 2025-11-25 \
--command "java -jar conformance-tests/client-spring-http-client/target/client-spring-http-client-0.18.0-SNAPSHOT.jar" \
--command "java -jar conformance-tests/client-spring-http-client/target/client-spring-http-client-1.1.0-SNAPSHOT.jar" \
--suite auth
```

Expand All @@ -88,7 +88,7 @@ Run a single scenario:
```bash
npx @modelcontextprotocol/conformance@0.1.15 client \
--spec-version 2025-11-25 \
--command "java -jar conformance-tests/client-spring-http-client/target/client-spring-http-client-0.18.0-SNAPSHOT.jar" \
--command "java -jar conformance-tests/client-spring-http-client/target/client-spring-http-client-1.1.0-SNAPSHOT.jar" \
--scenario auth/metadata-default
```

Expand All @@ -97,7 +97,7 @@ Run with verbose output:
```bash
npx @modelcontextprotocol/conformance@0.1.15 client \
--spec-version 2025-11-25 \
--command "java -jar conformance-tests/client-spring-http-client/target/client-spring-http-client-0.18.0-SNAPSHOT.jar" \
--command "java -jar conformance-tests/client-spring-http-client/target/client-spring-http-client-1.1.0-SNAPSHOT.jar" \
--scenario auth/metadata-default \
--verbose
```
Expand All @@ -108,7 +108,7 @@ You can also run the client manually if you have a test server:

```bash
export MCP_CONFORMANCE_SCENARIO=auth/metadata-default
java -jar conformance-tests/client-spring-http-client/target/client-spring-http-client-0.18.0-SNAPSHOT.jar http://localhost:3000/mcp
java -jar conformance-tests/client-spring-http-client/target/client-spring-http-client-1.1.0-SNAPSHOT.jar http://localhost:3000/mcp
```

## Known Issues
Expand Down
Loading