Testing

Purpose and Scope

Testing in this repository is organized around proving real MCP behavior rather than exercising isolated helper functions. The public guide shows the preferred pattern: drive a server through an actual client, keep the exchange in process, and avoid replacing the protocol with mocks. That matters because MCP behavior includes transport framing, initialization or discovery, request validation, result shapes, and teardown semantics. A useful test should therefore look like a small host talking to the server exactly as production code would, while still being fast enough to run inside an ordinary unit-test process. Sources: docs/testing.md

The same philosophy appears in the lower-level test assets. Corpus fixtures pin exact wire payload shapes for protocol revisions, while the e2e type definitions name the transports and spec-version axes that broader behavior tests can combine. Together, those files distinguish three kinds of confidence: guide-level examples that teach application authors how to test their own servers, fixture-level artifacts that keep schema behavior stable across revisions, and matrix-level types that make transport and protocol coverage explicit. Sources: packages/core-internal/test/corpus/fixtures/2025-11-25/ReadResourceResult/blob.json, packages/core-internal/test/corpus/fixtures/2026-07-28/BlobResourceContents/image-file-contents.json, test/e2e/types.ts

Relevant Source Files

  • docs/testing.md - The first-party how-to for testing a server in process with a real Client, a StreamableHTTPClientTransport, result assertions, teardown, and an in-memory transport pair.
  • packages/core-internal/test/corpus/fixtures/2025-11-25/ReadResourceResult/blob.json - A 2025-era resource-read fixture that pins blob resource result shape with uri, mimeType, and blob fields.
  • packages/core-internal/test/corpus/fixtures/2026-07-28/BlobResourceContents/image-file-contents.json - A 2026-07-28 blob resource-content fixture that pins the newer revision’s image payload shape.
  • docs/.vitepress/theme/index.ts - The v2 documentation site theme entry that extends the default VitePress theme and injects the shared banner layout.
  • docs/v1/.vitepress/theme/index.ts - The v1 documentation theme entry that mirrors the banner layout and imports shared custom CSS from the v2 docs tree.
  • test/e2e/types.ts - Shared e2e suite types for transport names, spec-version axes, entry transports, known failures, and exclusion reasons.

In-Process Server Tests

The recommended server test starts from the same server factory that the application ships. In the guide, that factory creates an MCP server named pricing, registers an apply-discount tool, validates price and percent input with a schema, and returns structured output for a successful call. Instead of opening a port, the test wraps the factory with the MCP handler creation function and passes the handler’s fetch method into a Streamable HTTP client transport. The URL can be a placeholder because the transport delegates every request to the in-process handler. Sources: docs/testing.md

That arrangement is valuable because it keeps the production handler in the test path. The client still connects over a Streamable HTTP transport, the server still handles real MCP calls, and the test can assert the result seen by a model-facing client. The guide constructs a client named test-harness and opts into automatic version negotiation, so the handshake follows the newest revision the handler serves. A successful apply-discount call with price eighty and percent twenty-five produces structured content containing a total of sixty. Sources: docs/testing.md

The guide also clarifies an important error-handling edge case: tool-level failure is not the same thing as a thrown test exception. A negative price returns an ordinary tool result marked with an error flag, and the content array carries the message that a model would see. Application tests should assert that shape directly instead of wrapping the call in catch logic. This distinction helps keep tests aligned with MCP semantics, where a server can complete the protocol exchange successfully while reporting that the requested tool operation failed. Sources: docs/testing.md

Lifecycle, Isolation, and In-Memory Pairing

Clean teardown is part of the contract, not a cosmetic afterthought. The testing guide tells readers to close the client first and then close the handler in each test cleanup hook. Closing the handler aborts any exchange still in flight, which prevents a hung tool call from leaking into the next test case. That ordering is especially useful in suites that reuse factories or run many server behaviors in parallel, because it makes every test responsible for disposing both ends of the conversation it created. Sources: docs/testing.md

When an HTTP-shaped handler is not the behavior under test, the guide points to an in-memory linked transport pair. The linked pair returns two transport instances that are each other’s wire, so one can be connected to the client and the other to a server instance. This pattern is useful for protocol exercises that need a full duplex client-server connection without involving an HTTP request object, a socket, or a subprocess. It still preserves the discipline of testing with real transport endpoints rather than stubbing client methods. Sources: docs/testing.md

Revision Fixtures and Behavior Pins

The corpus fixtures show how the repository protects wire-level behavior across protocol revisions. One fixture records a 2025-11-25 resource read result containing a file URI, image MIME type, and base64 blob content. Another fixture records a 2026-07-28 blob resource content object with the same essential data categories but under the newer revision-specific fixture path. These files are small, but they are important because they turn expected protocol payloads into reviewable artifacts. When schemas or codecs change, fixture differences make the behavior surface visible. Sources: packages/core-internal/test/corpus/fixtures/2025-11-25/ReadResourceResult/blob.json, packages/core-internal/test/corpus/fixtures/2026-07-28/BlobResourceContents/image-file-contents.json

The shared e2e types make the same idea explicit at a matrix level. The transport axis includes in-memory, stdio, streamable HTTP, stateless Streamable HTTP, SSE, and two createMcpHandler entry arms. The spec-version axis currently includes 2025-11-25 and 2026-07-28. The entry transports are deliberately era-fixed: the stateless entry path serves the 2025-era fallback, while the modern entry path serves the 2026-07-28 per-request envelope route. Tests can therefore state which behavior they verify without blurring transport coverage and protocol revision coverage. Sources: test/e2e/types.ts

Conformance and Matrix Guidance

For conformance-style work, treat the shared e2e types as the vocabulary for coverage. A test body receives a transport and a protocol version, and the matrix can intersect those axes with requirement bounds. The KnownFailure type allows a failure to be scoped by test name, transport, or spec version with a required note, which discourages silent blanket skips. The exclusion reason list is also documentation: it names gaps such as requiring a persistent session, asserting legacy handshake behavior, or depending on methods removed from the modern registry. Sources: test/e2e/types.ts

The most useful test additions are narrow and declarative. If a behavior is valid for all active transports and both active protocol revisions, it should not hard-code a single transport. If a behavior only applies to a persistent session, it should not be forced through an entry arm that creates a fresh instance per request. If a behavior is legacy vocabulary, it should be scoped to the older surface instead of weakening the modern path. This keeps the suite honest about what each protocol era promises and what each transport can actually serve. Sources: test/e2e/types.ts

Documentation Testing Signals

The documentation files are part of the testing surface because examples and guides are published through VitePress. The v2 theme entry extends the default theme, adds a banner to the layout-top slot, and imports custom CSS. The v1 theme performs the same layout extension and intentionally imports the shared custom CSS from the v2 documentation tree rather than duplicating it. That means documentation checks are not only markdown rendering checks; they also validate that both documentation sites can share presentation code without drifting or breaking the versioned docs experience. Sources: docs/.vitepress/theme/index.ts, docs/v1/.vitepress/theme/index.ts

Practical Checklist

Use the shipped server factory when writing application tests, then connect a real client through either an injected Fetch-based Streamable HTTP transport or an in-memory linked pair. Assert successful tool calls through structured content, and assert handler-reported failures through the returned error flag and content rather than exception handling. Close the client before closing the handler in cleanup. For SDK-level changes, ask which protocol revision and which transport arms the behavior belongs to, then encode that scope through the e2e matrix vocabulary and fixture updates where wire shapes are affected. Sources: docs/testing.md, test/e2e/types.ts