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 outputSchema

  • Argument validation against a tool's inputSchema

  • Automatic 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 mcp plugin installed in your plugin directory

  • The json plugin, if any of your tools declare an inputSchema or outputSchema

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

<ns>:drift/all

The whole declared surface matches the server

<ns>:drift/tools

Declared tools match tools/list

<ns>:drift/resources

Declared resources match resources/list

<ns>:drift/resourceTemplates

Declared templates match resources/templates/list

<ns>:drift/prompts

Declared prompts match prompts/list

<ns>:drift/capabilities

The server serves the capabilities it advertises

<ns>:drift/discover

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

<ns>:tool/<name>

A tools/call request

<ns>:resource/<uri>

A resources/read request

<ns>:prompt/<name>

A prompts/get request

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

expected.error.code

Tool error

The tool ran and reported failure

expected.result.isError

# 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-on-undeclared

false

Fail instead of warn when the server advertises a tool, resource, or prompt your definition does not declare

fail-on-description-change

false

Fail instead of warn when a tool or prompt description differs from your definition

fail-on-undocumented-output

false

Fail when a result contains a field the outputSchema does not declare

fail-on-undocumented-input

false

Fail when test case arguments contain a field the inputSchema does not declare

stdio-shutdown-timeout-ms

5000

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.arguments rather than in a body, query, or path.

  • You assert on expected.result or expected.error rather 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.

Publication date: