AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
MCP verified MIT Self-run

Mcp Harness

mcp-gabry-ts-mcp-harness · by gabry-ts

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

No reviews yet
0 installs
17 views
0.0% view→install

Install

$ agentstack add mcp-gabry-ts-mcp-harness

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution Used
  • Environment & secrets Used
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/mcp-gabry-ts-mcp-harness)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
1mo ago

Declared compatibility

Claude CodeClaude DesktopCursorWindsurf

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Mcp Harness? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

mcp-harness

[](https://www.npmjs.com/package/mcp-harness) [](https://github.com/gabry-ts/mcp-harness/actions/workflows/ci.yml) [](https://opensource.org/licenses/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 templates = await harness.listResourceTemplates();
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' });

// Server info
const capabilities = harness.getServerCapabilities();
const version = harness.getServerVersion();

// Health check
await harness.ping();

// 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();

// Access subprocess stderr for debugging
console.log(harness.stderr);

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 | Connected harness instance |

McpHarness

| Method | Signature | Description | |--------|-----------|-------------| | listTools() | () => Promise | List all registered tools | | callTool() | (name, args?) => Promise | Call a tool by name | | listResources() | () => Promise | List all registered resources | | readResource() | (uri) => Promise | Read a resource by URI | | listResourceTemplates() | () => Promise | List resource templates | | listPrompts() | () => Promise | List all registered prompts | | getPrompt() | (name, args?) => Promise | Get a prompt by name | | ping() | () => Promise | Ping the server | | getServerCapabilities() | () => ServerCapabilities \| undefined | Get server capabilities | | getServerVersion() | () => { name, version } \| undefined | Get server name and version | | close() | () => Promise | Close the harness (idempotent) | | client | Client | Raw MCP Client for advanced use | | stderr | string \| undefined | Subprocess stderr output (subprocess mode only) |

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 | 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

  • [Vitest example](./examples/vitest-example/) — Full test suite with Vitest
  • [Jest example](./examples/jest-example/) — Same tests with Jest + ESM
  • [Standalone example](./examples/standalone-example/) — No test framework, just node:assert

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.

flowchart LR
  client["MCP Client(harness)"]
  server["McpServer(your code)"]

  client  |InMemoryTransportlinked pair| server

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](./LICENSE) © Gabriele Partiti

Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.