npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@buildwithabid/mcp-testkit

v1.0.0

Published

Testing framework for MCP (Model Context Protocol) servers. Custom matchers, schema validation, fuzz testing, and snapshot support for vitest and jest.

Readme

mcp-testkit

The Testing Framework for MCP Servers

Test your Model Context Protocol tools, resources, and prompts with expressive matchers, schema validation, fuzz testing, and snapshot support.

npm version CI License: MIT Node.js TypeScript

Getting Started · Features · API Reference · Contributing


Why mcp-testkit?

Building MCP servers is straightforward. Knowing they work correctly is the hard part.

| Problem | How mcp-testkit solves it | |:--------|:-------------------------| | No standard testing patterns | One-line harness setup for any MCP server | | Boilerplate transport wiring | createHarness() handles InMemoryTransport or stdio for you | | Manual result.content[0].text checks | Purpose-built matchers like toBeSuccessful() and toHaveTextContent() | | No schema validation | validateToolSchema() catches issues before production | | Edge cases found by users, not tests | Built-in fuzz testing with reproducible seeds |

Write tests for MCP servers the same way you write tests for REST APIs — fast, expressive, and reliable.

Table of Contents


Getting Started

Installation

Not published to a registry yet. Install from source with the steps below; the commands in this README assume you have done so.

git clone https://github.com/BuildWithAbid/mcp-testkit.git
cd mcp-testkit
npm install && npm run build
npm link            # then `npm link mcp-testkit` in your project

Peer dependencies (you likely already have these):

npm install @modelcontextprotocol/sdk zod

Your First Test

import { describe, it, expect } from "vitest";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { createHarness } from "mcp-testkit";
import { mcpMatchers } from "mcp-testkit/matchers";

expect.extend(mcpMatchers);

const server = new McpServer({ name: "my-server", version: "1.0.0" });
server.tool(
  "greet",
  "Greet a user",
  { name: z.string() },
  async ({ name }) => ({
    content: [{ type: "text", text: `Hello, ${name}!` }],
  })
);

describe("My MCP Server", () => {
  it("greets users", async () => {
    const harness = await createHarness(server);

    const tools = await harness.listTools();
    expect(tools).toHaveToolNamed("greet");

    const result = await harness.callTool("greet", { name: "World" });
    expect(result).toBeSuccessful();
    expect(result).toHaveTextContent("Hello, World!");

    await harness.close();
  });
});

Auto-Setup (Optional)

Skip the expect.extend() boilerplate by registering matchers automatically:

// vitest.config.ts
export default {
  test: {
    setupFiles: ["mcp-testkit/setup/vitest"],
  },
};

Features

Test Harness

Create a fully wired MCP client-server pair in one line. No transport setup, no boilerplate.

import { createHarness } from "mcp-testkit";

// In-memory harness (unit tests) — pass an McpServer instance
const harness = await createHarness(server);

// Stdio harness (integration tests) — pass a command config
const harness = await createHarness({
  command: "node",
  args: ["./dist/server.js"],
  env: { API_KEY: "test" },
});

// Same API for both modes
const tools = await harness.listTools();
const result = await harness.callTool("my-tool", { input: "test" });
const resources = await harness.listResources();
const prompts = await harness.listPrompts();

await harness.close();

| | In-memory | Stdio | |---|---|---| | Speed | Instant | Spawns a child process | | Use case | Unit tests, CI | Integration tests, E2E | | Setup | Pass McpServer instance | Pass { command, args } config | | Isolation | Shares process memory | Full process isolation |

Use in-memory for fast iteration during development. Use stdio to test the actual binary your users will run.

Custom Matchers

Expressive assertions purpose-built for MCP testing. Works with Vitest and Jest.

Tool Discovery

expect(tools).toHaveToolNamed("search");
expect(tools).toHaveToolCount(5);
expect(tools).toHaveToolDescription("search", /find items/i);
expect(tools).toHaveInputSchema("search", { query: {}, limit: {} });

Tool Results

expect(result).toBeSuccessful();
expect(result).toBeToolError();
expect(result).toHaveTextContent("expected output");
expect(result).toHaveTextContent(/pattern/);
expect(result).toHaveContentCount(1);
expect(result).toHaveContentType("text");

Schema Validation

Validate that your tool schemas follow MCP best practices before they reach production.

import { validateToolSchema, validateAllToolSchemas } from "mcp-testkit";

const tools = await harness.listTools();

// Validate a single tool
const issues = validateToolSchema(tools[0]);
// [{ path: "properties.query", message: "Missing description", severity: "warning" }]

// Validate all tools at once
const allIssues = validateAllToolSchemas(tools);
// Map<string, SchemaIssue[]>

Input Generation

Automatically generate valid and edge-case inputs from any tool's JSON Schema.

import { generateValidInput, generateEdgeCaseInputs } from "mcp-testkit";

const schema = tools[0].inputSchema;

// Generate a minimal valid input
const input = generateValidInput(schema);
// { query: "test" }

// Generate edge cases for thorough testing
const edgeCases = generateEdgeCaseInputs(schema);
// [
//   { label: "empty object",                      input: {} },
//   { label: "missing required: query",            input: {} },
//   { label: "wrong type for: query",              input: { query: 42 } },
//   { label: "boundary: empty string for query",   input: { query: "" } },
//   { label: "boundary: very long string for query", input: { query: "a".repeat(10000) } },
// ]

Fuzz Testing

Throw random and adversarial inputs at your tools to surface crashes before your users do.

import { fuzzTool, fuzzAllTools } from "mcp-testkit";

// Fuzz a single tool — with a seed for reproducibility
const result = await fuzzTool(harness, "search", {
  runs: 100,
  seed: 42,
  assert: (toolResult) => {
    expect(toolResult.content.length).toBeGreaterThan(0);
  },
});

console.log(`${result.passed}/${result.runs} passed`);
console.log("Failures:", result.errors);

// Fuzz every tool on the server
const results = await fuzzAllTools(harness, { runs: 50, seed: 42 });

Snapshot Testing

Custom serializer that strips non-deterministic fields for stable, readable snapshots.

import { toolResultSerializer } from "mcp-testkit";

expect.addSnapshotSerializer(toolResultSerializer);

const result = await harness.callTool("greet", { name: "World" });
expect(result).toMatchSnapshot();

Text Helpers

Utility functions for extracting and checking text content in tool results.

import { getTexts, getFirstText, hasText } from "mcp-testkit";

const result = await harness.callTool("greet", { name: "World" });

getTexts(result);          // ["Hello, World!"]
getFirstText(result);      // "Hello, World!"
hasText(result, /hello/i); // true

API Reference

Harness

| Function | Description | |:---------|:------------| | createHarness(server) | Create an in-memory test harness from an McpServer instance | | createHarness(config) | Create a stdio test harness from { command, args?, env?, cwd? } |

| Method | Returns | Description | |:-------|:--------|:------------| | harness.listTools() | ToolInfo[] | List all registered tools | | harness.callTool(name, args?) | ToolResult | Call a tool by name | | harness.listResources() | ResourceInfo[] | List all resources | | harness.readResource(uri) | ResourceResult | Read a resource by URI | | harness.listPrompts() | PromptInfo[] | List all prompts | | harness.getPrompt(name, args?) | PromptResult | Get a prompt by name | | harness.getServerCapabilities() | Record<string, unknown> | Get server capabilities | | harness.close() | void | Close connection and clean up (idempotent) |

Matchers

| Matcher | Applies to | Description | |:--------|:-----------|:------------| | toHaveToolNamed(name) | ToolInfo[] | Assert a tool with the given name exists | | toHaveToolCount(n) | ToolInfo[] | Assert the exact number of tools | | toHaveToolDescription(name, expected) | ToolInfo[] | Assert a tool's description matches a string or regex | | toHaveInputSchema(name, props) | ToolInfo[] | Assert a tool's schema contains expected properties | | toBeSuccessful() | ToolResult | Assert the result has no error flag | | toBeToolError() | ToolResult | Assert the result has the error flag | | toHaveTextContent(expected) | ToolResult | Assert text content matches a string or regex | | toHaveContentCount(n) | ToolResult | Assert the exact number of content items | | toHaveContentType(type) | ToolResult | Assert at least one content item has the given type |

Schema & Validation

| Function | Description | |:---------|:------------| | validateToolSchema(tool) | Validate a single tool's JSON Schema, returns SchemaIssue[] | | validateAllToolSchemas(tools) | Validate all tools, returns Map<string, SchemaIssue[]> | | validateOutput(output, schema) | Validate output data against a JSON Schema | | generateValidInput(schema) | Generate a minimal valid input object from a schema | | generateEdgeCaseInputs(schema) | Generate an array of edge-case inputs for boundary testing |

Fuzz

| Function | Description | |:---------|:------------| | fuzzTool(harness, name, options?) | Fuzz test a single tool with random inputs | | fuzzAllTools(harness, options?) | Fuzz test every tool on the server |

FuzzOptions

| Option | Type | Default | Description | |:-------|:-----|:--------|:------------| | runs | number | 50 | Number of random inputs to generate | | seed | number | Date.now() | Seed for reproducible runs | | assert | (result, input) => void | — | Custom assertion applied to every result |

Snapshot

| Export | Description | |:-------|:------------| | toolResultSerializer | Pre-configured snapshot serializer (strips _meta, _timestamp, etc.) | | createToolResultSerializer(options?) | Create a custom serializer with your own stripFields |

Helpers

| Function | Description | |:---------|:------------| | getTexts(result) | Extract all text strings from a ToolResult | | getFirstText(result) | Extract the first text string, or undefined | | hasText(result, pattern) | Check if any text content matches a string or regex |


Compatibility

| Dependency | Supported Versions | |:-----------|:-------------------| | Node.js | 18, 20, 22+ | | Test Runners | Vitest, Jest | | MCP SDK | @modelcontextprotocol/sdk v1.12+ | | Zod | Zod v3.23+ or v4 | | TypeScript | 5.x |


Contributing

Contributions are welcome! Please open an issue first to discuss what you'd like to change.

git clone https://github.com/BuildWithAbid/mcp-testkit.git
cd mcp-testkit
npm install
npm test
npm run build

License

MIT © BuildWithAbid