MCP plugin
The MCP plugin tests Model Context Protocol servers against a declared MCP definition document. It answers two questions:
Surface drift. Do the tools, resources, and prompts the server advertises still match what your definition declares.
Behavioral conformance. When you call a tool, does it behave as documented.
Both checks compare a live server against a contract you authored, which is the difference between Drift and an interactive MCP inspector.
Supported features
Streamable HTTP and stdio transports
Both MCP protocol generations, negotiated automatically
Tool, resource, and prompt invocation
Result validation against a tool's
outputSchemaArgument validation against a tool's
inputSchemaAutomatic safety tagging from tool annotations
Prerequisites
An MCP definition document in the SmartBear Studio format. See MCP support in Swagger Studio for the format reference. Drift does not accept other MCP document formats.
A running MCP server
The
mcpplugin installed in your plugin directoryThe
jsonplugin, if any of your tools declare aninputSchemaoroutputSchema
Name the definition file with a .mcp.yaml, .mcp.yml, or .mcp.json extension. Drift uses the file name to identify the document as an MCP definition.
Write a test
Point sources at your definition document and declare both plugins:
drift-testcase-file: v1
title: "Notes MCP Server"
sources:
- name: notes
path: ../notes.mcp.yaml
plugins:
- name: mcp
- name: json
operations:
drift_all:
target: notes:drift/all
description: "The declared surface still matches the live server"
createNote_Success:
target: notes:tool/create_note
parameters:
arguments:
title: "Shopping"
body: "milk, eggs"
expected:
result:
structuredContent:
title: "Shopping"Run the tests against your server:
drift verify \ --server-url http://localhost:8099/mcp \ --test-files drift/notes.testcases.yaml
Drift checks
Drift checks compare your definition against the live server without calling any tool. They are safe to run against production.
Target | Checks |
|---|---|
| The whole declared surface matches the server |
| Declared tools match |
| Declared resources match |
| Declared templates match |
| Declared prompts match |
| The server serves the capabilities it advertises |
| The server is reachable and a protocol version is negotiated |
drift/all covers the first four checks, so use either drift/all or the individual checks, not both.
A server that offers less than you declared fails the check. A server that offers more produces a warning, because MCP clients read the tool list at the start of every session and adapt to what they find. To fail on undeclared surface as well, see Configuration.
Invocation tests
Drift registers one operation for each declared tool, resource, and prompt:
Target | Sends |
|---|---|
| A |
| A |
| A |
Set tool and prompt arguments under parameters.arguments. Set a resource address under parameters.uri.
Assert on results
MCP has two distinct failure channels you can distinguish:
Channel | What happened | Assert with |
|---|---|---|
Protocol error | The server rejected the request before running the tool |
|
Tool error | The tool ran and reported failure |
|
# The server rejected the request.
createNote_MissingArguments:
target: notes:tool/create_note
parameters:
arguments:
title: "Incomplete"
expected:
error:
code: -32602
# The tool ran and reported failure.
readNote_Unknown:
target: notes:tool/read_note
parameters:
arguments:
id: "does-not-exist"
expected:
result:
isError: true
content:
- contains: "not found"When a tool declares an outputSchema, Drift validates the returned structuredContent against it and fails if the result does not conform. Assert on individual values with expected.result.structuredContent, and on unstructured output with expected.result.content
Transports
Streamable HTTP. Point --server-url at the server endpoint:
--server-url http://localhost:8099/mcp
stdio. Give the command that starts the server. Drift starts the process once per run and reuses it for every operation:
--server-url "stdio:///node?arg=/path/to/server/index.js"
For a long command, name it once in your configuration file and refer to it by alias:
[plugin_config.mcp.servers.myserver] command = "node" args = ["/path/to/server/index.js"]
--server-url "stdio://myserver"
Use three slashes for an inline command and two slashes for a configured alias.
Configuration
Set these values under [plugin_config.mcp] in your drift.config.toml file to modify the MCP plugin behaviour:
Key | Default | Effect |
|---|---|---|
|
| Fail instead of warn when the server advertises a tool, resource, or prompt your definition does not declare |
|
| Fail instead of warn when a tool or prompt description differs from your definition |
|
| Fail when a result contains a field the |
|
| Fail when test case arguments contain a field the |
|
| How long a stdio server has to exit before Drift stops it |
Publishing to PactFlow
When publishing to PactFlow, use mcp as the value for the specification e.g.
pact pactflow publish-provider-contract \ --provider mcp-example \ --provider-app-version 1.0.0 \ ./notes.mcp.yaml \ --specification mcp \ ...
Differences from the OpenAPI plugin
If you have written Drift tests for a REST API, three things change:
Targets name a tool, resource, or prompt, not an HTTP path and verb.
Request data goes under
parameters.argumentsrather than in a body, query, or path.You assert on
expected.resultorexpected.errorrather than on an HTTP status code.
Limitations
Drift cannot authenticate to the MCP server it tests. Test unauthenticated endpoints over HTTP, or use the stdio transport, which passes credentials through the server process environment.
Drift does not expand resource templates. Target a concrete URI with parameters.uri instead.