java-sdk/conformance-tests/server-servlet
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Dariusz Jędrzejczyk 22a0efa577 feat!: enforce required MCP spec fields in McpSchema; lenient wire deserialization (#928)
Every wire-serialized record in McpSchema now validates spec-required
fields at construction time. Wire deserialization is intentionally
lenient: a missing required field is replaced with a documented default
and a WARN is logged, instead of failing the parse. A required-first
builder convention is introduced across the schema so it is no longer
possible to obtain a builder that is missing required state.

Required-field guards
---------------------
Every wire record's compact constructor asserts non-null (and non-empty
for String identifiers like name, uri, uriTemplate, version) on its
spec-required components. Passing null now throws
IllegalArgumentException at construction time instead of silently
producing invalid JSON via @JsonInclude(NON_ABSENT). This applies to
the JSON-RPC envelopes, lifecycle types, resource/prompt/tool requests
and results, sampling and elicitation, content records, root, complete,
logging and progress notifications, and the two CompleteReference
implementations. See MIGRATION-2.0.md for the full list.

Lenient wire deserialization
----------------------------
For each of those records (except JSONRPCResponse.JSONRPCError, which
still fails fast) a @JsonCreator static `fromJson` factory substitutes
a documented default for any absent required field — "" for strings,
[] for collections, {} for maps, 0 / 0.0 for numerics, INFO for
LoggingLevel, USER for SamplingMessage.role, ASSISTANT for
CreateMessageResult.role, CANCEL for ElicitResult.action, {values=[]}
for CompleteResult.completion — and logs a WARN naming the field and
the value used. Application code can still observe a malformed message
and react, but the SDK no longer halts the conversation.

Builder convention
------------------
Records that have a builder gain a required-first factory method
`builder(req1, req2, …)`; setters for required fields are removed from
the builder so it cannot be left in an invalid state. Existing no-arg
`builder()` factories and required-field setters are kept where source
compatibility demands it but are marked @Deprecated. New builders are
also added for several records that previously had none (ProgressNotification,
JSONRPCError, CompleteRequest, list/result types, content records, ...).

JSON-RPC envelope ergonomics
----------------------------
Previously every JSON-RPC envelope had to be constructed via the
canonical record constructor and the literal "2.0" string had to be
threaded through every call site, e.g.

    new JSONRPCRequest("2.0", "tools/call", id, params)
    new JSONRPCResponse("2.0", id, result, null)
    new JSONRPCResponse("2.0", id, null, new JSONRPCError(code, message, null))

Now:

    new JSONRPCRequest("tools/call", id, params)              // jsonrpc defaulted
    new JSONRPCNotification("notifications/initialized")       // params optional
    JSONRPCResponse.result(id, result)                        // factory
    JSONRPCResponse.error(id, new JSONRPCError(code, message)) // 2-arg error

JSONRPCResponse's compact constructor additionally enforces the
JSON-RPC invariant that exactly one of `result` / `error` is set —
previously the SDK could build envelopes that violated the protocol.

CompleteReference changes
-------------------------
- PromptReference.equals/hashCode now key on `name` only (previously
  derived from identifier()+type()). Two refs with the same name but
  different titles now compare equal — code using PromptReference as a
  map/set key should be audited.
- PromptReference's compact constructor pins `type` to "ref/prompt"
  and logs a WARN if the caller supplies a different non-null value.
  The legacy two-arg `PromptReference(String type, String name)`
  constructor remains @Deprecated.
- ResourceReference's record components are reduced from (type, uri)
  to (uri) — positional construction breaks. The legacy
  `ResourceReference(String type, String uri)` constructor stays
  @Deprecated and ignores `type`.
- CompleteReference.identifier() is @Deprecated and now returns null
  via a default method on the interface.

Tests / refactor
----------------
Conformance harness, integration tests, sample apps, and internal
callers were migrated to the new builder factories and convenience
constructors. No semantic changes to client/server runtime behaviour
beyond the McpSchema changes above.

Docs
----
- MIGRATION-2.0.md: required-field section now covers the broader
  record set and the lenient-deserialization behaviour; PromptReference
  WARN behaviour and ResourceReference component reduction are
  documented; the JSON-RPC envelope section is rewritten to compare the
  pre-2.0 surface with the new one.
- CONTRIBUTING.md: the "Evolving wire-serialized records" recipe is
  split into two cases — adding a new optional field (existing rules)
  and adding/maintaining a spec-required field (new rules covering
  Assert in compact constructor, @JsonCreator fromJson with defaults
  and WARN, required-first builder factory).

Signed-off-by: Dariusz Jędrzejczyk <2554306+chemicL@users.noreply.github.com>
2026-05-04 21:01:46 +02:00
..
src/main feat!: enforce required MCP spec fields in McpSchema; lenient wire deserialization (#928) 2026-05-04 21:01:46 +02:00
pom.xml Next development version 2026-03-13 13:31:03 +01:00
README.md Support resource subscriptions (#839) 2026-03-02 15:26:03 +01:00

MCP Conformance Tests - Servlet Server

This module contains a comprehensive MCP (Model Context Protocol) server implementation for conformance testing using the servlet stack with an embedded Tomcat server and streamable HTTP transport.

Conformance Test Results

Status: 40 out of 40 tests passing (100%)

The server has been validated against the official MCP conformance test suite. See VALIDATION_RESULTS.md for detailed results.

What's Implemented

✅ Lifecycle & Utilities (4/4)

  • Server initialization, ping, logging, completion

✅ Tools (11/11)

  • Text, image, audio, embedded resources, mixed content
  • Logging, error handling, sampling, elicitation
  • Progress notifications

✅ Elicitation (10/10)

  • SEP-1034: Default values for all primitive types
  • SEP-1330: All enum schema variants

✅ Resources (6/6)

  • List, read text/binary, templates, subscribe, unsubscribe

✅ Prompts (4/4)

  • Simple, parameterized, embedded resources, images

✅ SSE Transport (2/2)

  • Multiple streams support

✅ Security (2/2)

  • ✅ DNS rebinding protection

Features

  • Embedded Tomcat servlet container
  • MCP server using HttpServletStreamableServerTransportProvider
  • Comprehensive test coverage with 15+ tools
  • Streamable HTTP transport with SSE on /mcp endpoint
  • Support for all MCP content types (text, image, audio, resources)
  • Advanced features: sampling, elicitation, progress (partial), completion

Running the Server

To run the conformance server:

cd conformance-tests/server-servlet
../../mvnw compile exec:java -Dexec.mainClass="io.modelcontextprotocol.conformance.server.ConformanceServlet"

Or from the root directory:

./mvnw compile exec:java -pl conformance-tests/server-servlet -Dexec.mainClass="io.modelcontextprotocol.conformance.server.ConformanceServlet"

The server will start on port 8080 with the MCP endpoint at /mcp.

Running Conformance Tests

Once the server is running, you can validate it against the official MCP conformance test suite using npx:

Run Full Active Test Suite

npx @modelcontextprotocol/conformance server --url http://localhost:8080/mcp --suite active

Run Specific Scenarios

# Test tools
npx @modelcontextprotocol/conformance server --url http://localhost:8080/mcp --scenario tools-list --verbose

# Test prompts
npx @modelcontextprotocol/conformance server --url http://localhost:8080/mcp --scenario prompts-list --verbose

# Test resources
npx @modelcontextprotocol/conformance server --url http://localhost:8080/mcp --scenario resources-read-text --verbose

# Test elicitation with defaults
npx @modelcontextprotocol/conformance server --url http://localhost:8080/mcp --scenario elicitation-sep1034-defaults --verbose

Available Test Suites

  • active (default) - All active/stable tests (30 scenarios)
  • all - All tests including pending/experimental
  • pending - Only pending/experimental tests

Common Scenarios

Lifecycle & Utilities:

  • server-initialize - Server initialization
  • ping - Ping utility
  • logging-set-level - Logging configuration
  • completion-complete - Argument completion

Tools:

  • tools-list - List available tools
  • tools-call-simple-text - Simple text response
  • tools-call-image - Image content
  • tools-call-audio - Audio content
  • tools-call-with-logging - Logging during execution
  • tools-call-with-progress - Progress notifications
  • tools-call-sampling - LLM sampling
  • tools-call-elicitation - User input requests

Resources:

  • resources-list - List resources
  • resources-read-text - Read text resource
  • resources-read-binary - Read binary resource
  • resources-templates-read - Resource templates
  • resources-subscribe - Subscribe to resource updates
  • resources-unsubscribe - Unsubscribe from updates

Prompts:

  • prompts-list - List prompts
  • prompts-get-simple - Simple prompt
  • prompts-get-with-args - Parameterized prompt
  • prompts-get-embedded-resource - Prompt with resource
  • prompts-get-with-image - Prompt with image

Elicitation:

  • elicitation-sep1034-defaults - Default values (SEP-1034)
  • elicitation-sep1330-enums - Enum schemas (SEP-1330)

Testing with curl

You can also test the endpoint manually:

# Check endpoint (will show SSE requirement)
curl -X GET http://localhost:8080/mcp

# Initialize session with proper headers
curl -X POST http://localhost:8080/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: text/event-stream" \
  -H "mcp-session-id: test-session-123" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test-client","version":"1.0.0"}}}'

Architecture

  • Transport: HttpServletStreamableServerTransportProvider (streamable HTTP with SSE)
  • Container: Embedded Apache Tomcat
  • Protocol: Streamable HTTP with Server-Sent Events
  • Port: 8080 (default)
  • Endpoint: /mcp
  • Request Timeout: 30 seconds

Implemented Tools

Content Type Tools

  • test_simple_text - Returns simple text content
  • test_image_content - Returns a minimal PNG image (1x1 red pixel)
  • test_audio_content - Returns a minimal WAV audio file
  • test_embedded_resource - Returns embedded resource content
  • test_multiple_content_types - Returns mixed text, image, and resource content

Behavior Tools

  • test_tool_with_logging - Sends log notifications during execution
  • test_error_handling - Intentionally returns an error for testing
  • test_tool_with_progress - Reports progress notifications (⚠️ SDK issue)

Interactive Tools

  • test_sampling - Requests LLM sampling from client
  • test_elicitation - Requests user input from client
  • test_elicitation_sep1034_defaults - Elicitation with default values (SEP-1034)
  • test_elicitation_sep1330_enums - Elicitation with enum schemas (SEP-1330)

Implemented Prompts

  • test_simple_prompt - Simple prompt without arguments
  • test_prompt_with_arguments - Prompt with required arguments (arg1, arg2)
  • test_prompt_with_embedded_resource - Prompt with embedded resource content
  • test_prompt_with_image - Prompt with image content

Implemented Resources

  • test://static-text - Static text resource
  • test://static-binary - Static binary resource (PNG image)
  • test://watched-resource - Resource that can be subscribed to
  • test://template/{id}/data - Resource template with parameter substitution

Known Limitations

See VALIDATION_RESULTS.md for details on remaining client-side limitations.

References