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

mcp-harness

v0.1.0

Published

In-memory testing toolkit for MCP servers in TypeScript — supertest for MCP

Readme

mcp-harness

npm version CI License: MIT

In-memory testing toolkit for MCP servers in TypeScript — supertest for MCP.

Why

Testing MCP servers today means running them as subprocesses, connecting over stdio, and manually inspecting JSON. It's slow, flaky, and hard to integrate into CI.

mcp-harness gives you a fast, in-memory test harness that connects directly to your McpServer instance — no child processes, no ports, no IO. Just import your server, create a harness, and assert on results. Think supertest but for the Model Context Protocol.

Quick Start

import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';
import { createHarness } from 'mcp-harness';
import { hasText, getFirstText } from 'mcp-harness/assertions';

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

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

console.log(getFirstText(result)); // "Hello, World!"
console.log(hasText(result, 'Hello')); // true

await harness.close();

Installation

npm install --save-dev mcp-harness

Peer dependency: @modelcontextprotocol/sdk must be installed in your project.

Usage

In-Memory Mode

Pass your McpServer instance directly. Zero IO, instant startup:

import { createHarness } from 'mcp-harness';
import { myServer } from './server.js';

const harness = await createHarness(myServer);

// List tools, resources, prompts
const tools = await harness.listTools();
const resources = await harness.listResources();
const prompts = await harness.listPrompts();

// Call a tool
const result = await harness.callTool('my-tool', { arg: 'value' });

// Read a resource
const resource = await harness.readResource('info://version');

// Get a prompt
const prompt = await harness.getPrompt('my-prompt', { name: 'Gab' });

// Always close when done
await harness.close();

Subprocess Mode

Test compiled servers via stdio transport, just like a real MCP client would:

import { createHarness } from 'mcp-harness';

const harness = await createHarness({
  command: 'node',
  args: ['dist/server.js'],
  env: { API_KEY: 'test-key' },
  cwd: '/path/to/project',
});

const tools = await harness.listTools();
await harness.close();

Assertion Helpers

Framework-agnostic helpers for inspecting MCP results. Use with Vitest, Jest, node:assert, or any test runner:

import {
  hasText,
  getTexts,
  getFirstText,
  hasError,
  hasErrorMatching,
  toolExists,
  findTool,
  resourceExists,
  promptExists,
} from 'mcp-harness/assertions';

// Inspect tool call results
const result = await harness.callTool('echo', { message: 'hi' });
hasText(result, 'hi'); // true
getFirstText(result); // "hi"
getTexts(result); // ["hi"]

// Check for errors
hasError(result); // false
hasErrorMatching(result, /timeout/); // false

// Inspect server capabilities
const tools = await harness.listTools();
toolExists(tools, 'echo'); // true
findTool(tools, 'echo'); // Tool object or undefined

const resources = await harness.listResources();
resourceExists(resources, 'info://version'); // true

const prompts = await harness.listPrompts();
promptExists(prompts, 'greet'); // true

Options

const harness = await createHarness(server, {
  timeout: 5000, // Connection/request timeout in ms
  clientName: 'my-test', // Client name for MCP handshake
  clientVersion: '1.0.0', // Client version
  clientCapabilities: {}, // Additional client capabilities
});

API Reference

createHarness(server, options?)

| Parameter | Type | Description | |-----------|------|-------------| | server | McpServer \| SubprocessConfig | Server instance or subprocess config | | options | HarnessOptions | Optional connection settings | | Returns | Promise<McpHarness> | Connected harness instance |

McpHarness

| Method | Signature | Description | |--------|-----------|-------------| | listTools() | () => Promise<Tool[]> | List all registered tools | | callTool() | (name, args?) => Promise<CallToolResult> | Call a tool by name | | listResources() | () => Promise<Resource[]> | List all registered resources | | readResource() | (uri) => Promise<ReadResourceResult> | Read a resource by URI | | listPrompts() | () => Promise<Prompt[]> | List all registered prompts | | getPrompt() | (name, args?) => Promise<GetPromptResult> | Get a prompt by name | | close() | () => Promise<void> | Close the harness (idempotent) | | client | Client | Raw MCP Client for advanced use |

HarnessOptions

| Field | Type | Default | Description | |-------|------|---------|-------------| | timeout | number | — | Connection/request timeout in ms | | clientName | string | 'mcp-harness' | Client name for handshake | | clientVersion | string | '0.1.0' | Client version | | clientCapabilities | object | — | Additional capabilities |

SubprocessConfig

| Field | Type | Description | |-------|------|-------------| | command | string | Command to execute ('node', 'npx') | | args | string[] | Command arguments | | env | Record<string, string> | Environment variables (merged with process.env) | | cwd | string | Working directory |

Assertion Helpers (mcp-harness/assertions)

| Function | Signature | Description | |----------|-----------|-------------| | hasText() | (result, text) => boolean | Check if any text block contains substring | | getTexts() | (result) => string[] | Extract all text strings from result | | getFirstText() | (result) => string \| undefined | Get first text string or undefined | | hasError() | (result) => boolean | Check if result is an error | | hasErrorMatching() | (result, pattern) => boolean | Check error matches string/RegExp | | toolExists() | (tools, name) => boolean | Check if tool exists by name | | findTool() | (tools, name) => Tool \| undefined | Find tool by name | | resourceExists() | (resources, uri) => boolean | Check if resource exists by URI | | promptExists() | (prompts, name) => boolean | Check if prompt exists by name |

Examples

How It Works

In in-memory mode, mcp-harness uses the MCP SDK's InMemoryTransport to create a linked pair of transports. Your McpServer connects to one side, and a Client connects to the other. Messages flow directly through memory — no serialization, no IO, no child processes.

┌──────────────┐     InMemoryTransport     ┌──────────────┐
│  MCP Client   │◄════════════════════════►│  McpServer    │
│  (harness)    │    linked pair            │  (your code)  │
└──────────────┘                           └──────────────┘

In subprocess mode, the harness spawns your server as a child process and connects via StdioClientTransport, the same way a real MCP host would.

Contributing

git clone https://github.com/gabry-ts/mcp-harness.git
cd mcp-harness
npm install
npm test
npm run build

License

MIT © Gabriele Partiti