{"_id":"@buildwithabid/mcp-testkit","name":"@buildwithabid/mcp-testkit","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@buildwithabid/mcp-testkit","version":"1.0.0","description":"Testing framework for MCP (Model Context Protocol) servers. Custom matchers, schema validation, fuzz testing, and snapshot support for vitest and jest.","type":"module","main":"dist/index.cjs","module":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./matchers":{"import":{"types":"./dist/matchers/index.d.ts","default":"./dist/matchers/index.js"},"require":{"types":"./dist/matchers/index.d.cts","default":"./dist/matchers/index.cjs"}},"./setup/vitest":{"import":{"types":"./dist/setup/vitest.d.ts","default":"./dist/setup/vitest.js"}}},"scripts":{"build":"tsup","dev":"tsup --watch","test":"vitest run","test:watch":"vitest","lint":"tsc --noEmit","prepublishOnly":"npm run build"},"keywords":["mcp","mcp-server","mcp-testing","mcp-tools","mcp-test-framework","model-context-protocol","testing-framework","vitest","jest","custom-matchers","schema-validation","fuzz-testing","snapshot-testing","ai-tools","ai-testing","llm","llm-tools","claude","test-harness","integration-testing","mcp-sdk","tool-testing"],"author":{"name":"BuildWithAbid"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/BuildWithAbid/mcp-testkit.git"},"homepage":"https://github.com/BuildWithAbid/mcp-testkit#readme","bugs":{"url":"https://github.com/BuildWithAbid/mcp-testkit/issues"},"peerDependencies":{"@modelcontextprotocol/sdk":"^1.12.0","zod":"^3.23.0 || ^4.0.0"},"dependencies":{"ajv":"^8.17.1","ajv-formats":"^3.0.1"},"devDependencies":{"@modelcontextprotocol/sdk":"^1.12.1","@types/node":"^22.15.3","tsup":"^8.4.0","typescript":"^5.8.3","vitest":"^3.1.2","zod":"^3.24.4"},"engines":{"node":"18 || 20 || >=22"},"publishConfig":{"access":"public"},"gitHead":"3be02e2617459eab88562bc5f14b77e413b5a2ba","_id":"@buildwithabid/mcp-testkit@1.0.0","_nodeVersion":"24.13.1","_npmVersion":"11.9.0","dist":{"integrity":"sha512-UoxCkq/KFnFmc600BWc45h7gYHk1Y2NYHVUq9+J+QpUh9K2tuEaqrxKhiIIP1wsX7BYibPVjvQZ9O1Lu3+sOOA==","shasum":"7d340d184d9bfd8d6448a68a568b87a547db48fd","tarball":"https://registry.npmjs.org/@buildwithabid/mcp-testkit/-/mcp-testkit-1.0.0.tgz","fileCount":23,"unpackedSize":211368,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDC9xd9AF4/QWOGlRM+icMKSOZw9baDyeVuqKTwAIlzHQIhAMxWr+V4p0ElWORrv/Xz0OKoQ/caXi+a59WyKshQXNYA"}]},"_npmUser":{"name":"buildwithabid","email":"abidtech2017@gmail.com"},"directories":{},"maintainers":[{"name":"buildwithabid","email":"abidtech2017@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/mcp-testkit_1.0.0_1785669042543_0.29564352647537606"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-02T11:10:42.325Z","1.0.0":"2026-08-02T11:10:42.694Z","modified":"2026-08-02T11:10:42.946Z"},"maintainers":[{"name":"buildwithabid","email":"abidtech2017@gmail.com"}],"description":"Testing framework for MCP (Model Context Protocol) servers. Custom matchers, schema validation, fuzz testing, and snapshot support for vitest and jest.","homepage":"https://github.com/BuildWithAbid/mcp-testkit#readme","keywords":["mcp","mcp-server","mcp-testing","mcp-tools","mcp-test-framework","model-context-protocol","testing-framework","vitest","jest","custom-matchers","schema-validation","fuzz-testing","snapshot-testing","ai-tools","ai-testing","llm","llm-tools","claude","test-harness","integration-testing","mcp-sdk","tool-testing"],"repository":{"type":"git","url":"git+https://github.com/BuildWithAbid/mcp-testkit.git"},"author":{"name":"BuildWithAbid"},"bugs":{"url":"https://github.com/BuildWithAbid/mcp-testkit/issues"},"license":"MIT","readme":"<div align=\"center\">\n\n# mcp-testkit\n\n### The Testing Framework for MCP Servers\n\nTest your [Model Context Protocol](https://modelcontextprotocol.io) tools, resources, and prompts with\nexpressive matchers, schema validation, fuzz testing, and snapshot support.\n\n[![npm version](https://img.shields.io/npm/v/mcp-testkit.svg?style=flat-square&color=cb3837)](https://www.npmjs.com/package/mcp-testkit)\n[![CI](https://github.com/BuildWithAbid/mcp-testkit/actions/workflows/ci.yml/badge.svg)](https://github.com/BuildWithAbid/mcp-testkit/actions/workflows/ci.yml)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square)](https://opensource.org/licenses/MIT)\n[![Node.js](https://img.shields.io/badge/node-18%20%7C%2020%20%7C%20%E2%89%A522-43853d.svg?style=flat-square&logo=node.js&logoColor=white)](https://nodejs.org)\n[![TypeScript](https://img.shields.io/badge/TypeScript-5.x-3178c6.svg?style=flat-square&logo=typescript&logoColor=white)](https://www.typescriptlang.org)\n\n<br />\n\n[Getting Started](#-getting-started) ·\n[Features](#-features) ·\n[API Reference](#-api-reference) ·\n[Contributing](#-contributing)\n\n<br />\n\n</div>\n\n---\n\n<br />\n\n## Why mcp-testkit?\n\nBuilding MCP servers is straightforward. **Knowing they work correctly is the hard part.**\n\n| Problem | How mcp-testkit solves it |\n|:--------|:-------------------------|\n| No standard testing patterns | One-line harness setup for any MCP server |\n| Boilerplate transport wiring | `createHarness()` handles `InMemoryTransport` or stdio for you |\n| Manual `result.content[0].text` checks | Purpose-built matchers like `toBeSuccessful()` and `toHaveTextContent()` |\n| No schema validation | `validateToolSchema()` catches issues before production |\n| Edge cases found by users, not tests | Built-in fuzz testing with reproducible seeds |\n\n> **Write tests for MCP servers the same way you write tests for REST APIs &mdash; fast, expressive, and reliable.**\n\n<br />\n\n## Table of Contents\n\n- [Getting Started](#-getting-started)\n  - [Installation](#installation)\n  - [Your First Test](#your-first-test)\n  - [Auto-Setup (Optional)](#auto-setup-optional)\n- [Features](#-features)\n  - [Test Harness](#-test-harness)\n  - [Custom Matchers](#-custom-matchers)\n  - [Schema Validation](#-schema-validation)\n  - [Input Generation](#-input-generation)\n  - [Fuzz Testing](#-fuzz-testing)\n  - [Snapshot Testing](#-snapshot-testing)\n  - [Text Helpers](#-text-helpers)\n- [API Reference](#-api-reference)\n  - [Harness](#harness)\n  - [Matchers](#matchers)\n  - [Schema & Validation](#schema--validation)\n  - [Fuzz](#fuzz)\n  - [Snapshot](#snapshot)\n  - [Helpers](#helpers)\n- [Compatibility](#-compatibility)\n- [Contributing](#-contributing)\n- [License](#-license)\n\n<br />\n\n---\n\n<br />\n\n## Getting Started\n\n### Installation\n\n> **Not published to a registry yet.** Install from source with the steps below; the commands in this README assume you have done so.\n\n```bash\ngit clone https://github.com/BuildWithAbid/mcp-testkit.git\ncd mcp-testkit\nnpm install && npm run build\nnpm link            # then `npm link mcp-testkit` in your project\n```\n\n**Peer dependencies** (you likely already have these):\n\n```bash\nnpm install @modelcontextprotocol/sdk zod\n```\n\n### Your First Test\n\n```typescript\nimport { describe, it, expect } from \"vitest\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { createHarness } from \"mcp-testkit\";\nimport { mcpMatchers } from \"mcp-testkit/matchers\";\n\nexpect.extend(mcpMatchers);\n\nconst server = new McpServer({ name: \"my-server\", version: \"1.0.0\" });\nserver.tool(\n  \"greet\",\n  \"Greet a user\",\n  { name: z.string() },\n  async ({ name }) => ({\n    content: [{ type: \"text\", text: `Hello, ${name}!` }],\n  })\n);\n\ndescribe(\"My MCP Server\", () => {\n  it(\"greets users\", async () => {\n    const harness = await createHarness(server);\n\n    const tools = await harness.listTools();\n    expect(tools).toHaveToolNamed(\"greet\");\n\n    const result = await harness.callTool(\"greet\", { name: \"World\" });\n    expect(result).toBeSuccessful();\n    expect(result).toHaveTextContent(\"Hello, World!\");\n\n    await harness.close();\n  });\n});\n```\n\n### Auto-Setup (Optional)\n\nSkip the `expect.extend()` boilerplate by registering matchers automatically:\n\n```typescript\n// vitest.config.ts\nexport default {\n  test: {\n    setupFiles: [\"mcp-testkit/setup/vitest\"],\n  },\n};\n```\n\n<br />\n\n---\n\n<br />\n\n## Features\n\n### Test Harness\n\nCreate a fully wired MCP client-server pair in **one line**. No transport setup, no boilerplate.\n\n```typescript\nimport { createHarness } from \"mcp-testkit\";\n\n// In-memory harness (unit tests) — pass an McpServer instance\nconst harness = await createHarness(server);\n\n// Stdio harness (integration tests) — pass a command config\nconst harness = await createHarness({\n  command: \"node\",\n  args: [\"./dist/server.js\"],\n  env: { API_KEY: \"test\" },\n});\n\n// Same API for both modes\nconst tools = await harness.listTools();\nconst result = await harness.callTool(\"my-tool\", { input: \"test\" });\nconst resources = await harness.listResources();\nconst prompts = await harness.listPrompts();\n\nawait harness.close();\n```\n\n<details>\n<summary><strong>In-memory vs stdio — when to use which?</strong></summary>\n\n<br />\n\n| | In-memory | Stdio |\n|---|---|---|\n| **Speed** | Instant | Spawns a child process |\n| **Use case** | Unit tests, CI | Integration tests, E2E |\n| **Setup** | Pass `McpServer` instance | Pass `{ command, args }` config |\n| **Isolation** | Shares process memory | Full process isolation |\n\nUse **in-memory** for fast iteration during development. Use **stdio** to test the actual binary your users will run.\n\n</details>\n\n<br />\n\n### Custom Matchers\n\nExpressive assertions purpose-built for MCP testing. Works with **Vitest** and **Jest**.\n\n#### Tool Discovery\n\n```typescript\nexpect(tools).toHaveToolNamed(\"search\");\nexpect(tools).toHaveToolCount(5);\nexpect(tools).toHaveToolDescription(\"search\", /find items/i);\nexpect(tools).toHaveInputSchema(\"search\", { query: {}, limit: {} });\n```\n\n#### Tool Results\n\n```typescript\nexpect(result).toBeSuccessful();\nexpect(result).toBeToolError();\nexpect(result).toHaveTextContent(\"expected output\");\nexpect(result).toHaveTextContent(/pattern/);\nexpect(result).toHaveContentCount(1);\nexpect(result).toHaveContentType(\"text\");\n```\n\n<br />\n\n### Schema Validation\n\nValidate that your tool schemas follow MCP best practices before they reach production.\n\n```typescript\nimport { validateToolSchema, validateAllToolSchemas } from \"mcp-testkit\";\n\nconst tools = await harness.listTools();\n\n// Validate a single tool\nconst issues = validateToolSchema(tools[0]);\n// [{ path: \"properties.query\", message: \"Missing description\", severity: \"warning\" }]\n\n// Validate all tools at once\nconst allIssues = validateAllToolSchemas(tools);\n// Map<string, SchemaIssue[]>\n```\n\n<br />\n\n### Input Generation\n\nAutomatically generate valid and edge-case inputs from any tool's JSON Schema.\n\n```typescript\nimport { generateValidInput, generateEdgeCaseInputs } from \"mcp-testkit\";\n\nconst schema = tools[0].inputSchema;\n\n// Generate a minimal valid input\nconst input = generateValidInput(schema);\n// { query: \"test\" }\n\n// Generate edge cases for thorough testing\nconst edgeCases = generateEdgeCaseInputs(schema);\n// [\n//   { label: \"empty object\",                      input: {} },\n//   { label: \"missing required: query\",            input: {} },\n//   { label: \"wrong type for: query\",              input: { query: 42 } },\n//   { label: \"boundary: empty string for query\",   input: { query: \"\" } },\n//   { label: \"boundary: very long string for query\", input: { query: \"a\".repeat(10000) } },\n// ]\n```\n\n<br />\n\n### Fuzz Testing\n\nThrow random and adversarial inputs at your tools to surface crashes before your users do.\n\n```typescript\nimport { fuzzTool, fuzzAllTools } from \"mcp-testkit\";\n\n// Fuzz a single tool — with a seed for reproducibility\nconst result = await fuzzTool(harness, \"search\", {\n  runs: 100,\n  seed: 42,\n  assert: (toolResult) => {\n    expect(toolResult.content.length).toBeGreaterThan(0);\n  },\n});\n\nconsole.log(`${result.passed}/${result.runs} passed`);\nconsole.log(\"Failures:\", result.errors);\n\n// Fuzz every tool on the server\nconst results = await fuzzAllTools(harness, { runs: 50, seed: 42 });\n```\n\n<br />\n\n### Snapshot Testing\n\nCustom serializer that strips non-deterministic fields for stable, readable snapshots.\n\n```typescript\nimport { toolResultSerializer } from \"mcp-testkit\";\n\nexpect.addSnapshotSerializer(toolResultSerializer);\n\nconst result = await harness.callTool(\"greet\", { name: \"World\" });\nexpect(result).toMatchSnapshot();\n```\n\n<br />\n\n### Text Helpers\n\nUtility functions for extracting and checking text content in tool results.\n\n```typescript\nimport { getTexts, getFirstText, hasText } from \"mcp-testkit\";\n\nconst result = await harness.callTool(\"greet\", { name: \"World\" });\n\ngetTexts(result);          // [\"Hello, World!\"]\ngetFirstText(result);      // \"Hello, World!\"\nhasText(result, /hello/i); // true\n```\n\n<br />\n\n---\n\n<br />\n\n## API Reference\n\n### Harness\n\n| Function | Description |\n|:---------|:------------|\n| `createHarness(server)` | Create an in-memory test harness from an `McpServer` instance |\n| `createHarness(config)` | Create a stdio test harness from `{ command, args?, env?, cwd? }` |\n\n| Method | Returns | Description |\n|:-------|:--------|:------------|\n| `harness.listTools()` | `ToolInfo[]` | List all registered tools |\n| `harness.callTool(name, args?)` | `ToolResult` | Call a tool by name |\n| `harness.listResources()` | `ResourceInfo[]` | List all resources |\n| `harness.readResource(uri)` | `ResourceResult` | Read a resource by URI |\n| `harness.listPrompts()` | `PromptInfo[]` | List all prompts |\n| `harness.getPrompt(name, args?)` | `PromptResult` | Get a prompt by name |\n| `harness.getServerCapabilities()` | `Record<string, unknown>` | Get server capabilities |\n| `harness.close()` | `void` | Close connection and clean up (idempotent) |\n\n### Matchers\n\n| Matcher | Applies to | Description |\n|:--------|:-----------|:------------|\n| `toHaveToolNamed(name)` | `ToolInfo[]` | Assert a tool with the given name exists |\n| `toHaveToolCount(n)` | `ToolInfo[]` | Assert the exact number of tools |\n| `toHaveToolDescription(name, expected)` | `ToolInfo[]` | Assert a tool's description matches a string or regex |\n| `toHaveInputSchema(name, props)` | `ToolInfo[]` | Assert a tool's schema contains expected properties |\n| `toBeSuccessful()` | `ToolResult` | Assert the result has no error flag |\n| `toBeToolError()` | `ToolResult` | Assert the result has the error flag |\n| `toHaveTextContent(expected)` | `ToolResult` | Assert text content matches a string or regex |\n| `toHaveContentCount(n)` | `ToolResult` | Assert the exact number of content items |\n| `toHaveContentType(type)` | `ToolResult` | Assert at least one content item has the given type |\n\n### Schema & Validation\n\n| Function | Description |\n|:---------|:------------|\n| `validateToolSchema(tool)` | Validate a single tool's JSON Schema, returns `SchemaIssue[]` |\n| `validateAllToolSchemas(tools)` | Validate all tools, returns `Map<string, SchemaIssue[]>` |\n| `validateOutput(output, schema)` | Validate output data against a JSON Schema |\n| `generateValidInput(schema)` | Generate a minimal valid input object from a schema |\n| `generateEdgeCaseInputs(schema)` | Generate an array of edge-case inputs for boundary testing |\n\n### Fuzz\n\n| Function | Description |\n|:---------|:------------|\n| `fuzzTool(harness, name, options?)` | Fuzz test a single tool with random inputs |\n| `fuzzAllTools(harness, options?)` | Fuzz test every tool on the server |\n\n**`FuzzOptions`**\n\n| Option | Type | Default | Description |\n|:-------|:-----|:--------|:------------|\n| `runs` | `number` | `50` | Number of random inputs to generate |\n| `seed` | `number` | `Date.now()` | Seed for reproducible runs |\n| `assert` | `(result, input) => void` | &mdash; | Custom assertion applied to every result |\n\n### Snapshot\n\n| Export | Description |\n|:-------|:------------|\n| `toolResultSerializer` | Pre-configured snapshot serializer (strips `_meta`, `_timestamp`, etc.) |\n| `createToolResultSerializer(options?)` | Create a custom serializer with your own `stripFields` |\n\n### Helpers\n\n| Function | Description |\n|:---------|:------------|\n| `getTexts(result)` | Extract all text strings from a `ToolResult` |\n| `getFirstText(result)` | Extract the first text string, or `undefined` |\n| `hasText(result, pattern)` | Check if any text content matches a string or regex |\n\n<br />\n\n---\n\n<br />\n\n## Compatibility\n\n| Dependency | Supported Versions |\n|:-----------|:-------------------|\n| **Node.js** | 18, 20, 22+ |\n| **Test Runners** | [Vitest](https://vitest.dev), [Jest](https://jestjs.io) |\n| **MCP SDK** | [@modelcontextprotocol/sdk](https://www.npmjs.com/package/@modelcontextprotocol/sdk) v1.12+ |\n| **Zod** | [Zod](https://zod.dev) v3.23+ or v4 |\n| **TypeScript** | 5.x |\n\n<br />\n\n---\n\n<br />\n\n## Contributing\n\nContributions are welcome! Please open an issue first to discuss what you'd like to change.\n\n```bash\ngit clone https://github.com/BuildWithAbid/mcp-testkit.git\ncd mcp-testkit\nnpm install\nnpm test\nnpm run build\n```\n\n<br />\n\n---\n\n<br />\n\n## License\n\n[MIT](LICENSE) &copy; [BuildWithAbid](https://github.com/BuildWithAbid)\n","readmeFilename":"README.md","_rev":"1-20e810d936ac6a464c21aa63c35c3e93"}