Consumer-driven contract testing for Model Context Protocol (MCP) servers and clients, built as a Pact plugin.
Test that an AI agent (MCP client) and the MCP server it depends on agree on the shape of tools/call, tools/list, resources/read|list, prompts/get|list, and their results — without spinning up the whole stack. You author expectations against your real @modelcontextprotocol/sdk Client, and verify them against your real MCP server. No stubbing, no service decomposition.
- MCP has no contract-testing story. Agents break when a server changes a tool's arguments or result shape; servers break their consumers without knowing.
- Core Pact can't test MCP: it has no stdio transport (and most MCP servers are stdio-first), no Streamable HTTP/SSE handling, and no knowledge of the MCP handshake or tool semantics. This plugin adds all of that.
- One matching engine, every language. Matching lives once in a Rust engine; language adapters are thin DX layers over it, so pacts are portable (author in TS, verify anywhere).
| stdio | Streamable HTTP | |
|---|---|---|
| Provider verification | ✅ | ✅ (+ bearer / API key / custom headers) |
| OAuth2 (client-credentials) | — | ✅ |
| Consumer mock | ✅ | ✅ (loopback) |
tools/call / tools/list, resources/read / resources/list, prompts/get / prompts/list |
✅ | ✅ |
Auto initialize handshake + capability negotiation |
✅ | ✅ |
| Core plugin (Rust / all languages) | ✅ | ✅ |
| TypeScript API (for improved DX) | ✅ | ✅ |
Install the package — its postinstall also downloads the engine binary (the
Pact mcp plugin) for your platform into ~/.pact/plugins/:
npm install @pactflow/pact-mcp-pluginNot using npm (e.g. verifying from the JVM/.NET/Go verifier)? Install the engine binary directly:
curl -fsSL https://raw.githubusercontent.com/mefellows/pact-mcp-plugin/main/scripts/install-plugin.sh | bash
# …or from source: ./scripts/install-local.shConsumer — drive your real MCP client against a Pact-synthesized mock:
import { McpPact, like } from "@pactflow/pact-mcp-plugin";
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
await new McpPact({ consumer: "weather-agent", provider: "weather-mcp" })
.whenClientCallsTool("get_weather", { city: "Melbourne" })
.willRespondWith({ content: [{ type: "text", text: like("Sunny, 22C") }], isError: false })
.executeTest(async ({ transport }) => {
const client = new Client({ name: "weather-agent", version: "1.0.0" }); // your REAL client
await client.connect(transport);
const res = await client.callTool({ name: "get_weather", arguments: { city: "Melbourne" } });
expect(res.content[0].text).toContain("22C");
});
// -> writes ./pacts/weather-agent-weather-mcp.jsonProvider — verify that pact against your real server, over stdio or HTTP:
import { McpProviderVerifier } from "@pactflow/pact-mcp-plugin";
await new McpProviderVerifier({ provider: "weather-mcp", pactUrls: ["./pacts/weather-agent-weather-mcp.json"] })
.withServerTransport({ type: "stdio", command: "node", args: ["dist/server.js"] })
// or: { type: "http", url: "https://mcp.example.com/mcp", auth: { type: "bearer", token: "${MCP_TOKEN}" } }
.verify();Pacts also verify through the stock pact-js Verifier (broker fetch, result publishing, can-i-deploy — see docs/usage.md and docs/bdct-walkthrough.md). Provider states, tools/list / resources/* / prompts/* expectations, and multi-interaction tests are covered in adapters/ts/pact-mcp/README.md.
MIT © 2026 Matt Fellows