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

vitest-mcp

v0.1.0

Published

Vitest harness and matchers for testing MCP (Model Context Protocol) servers — connect over an in-memory transport and assert on tools, resources, prompts, and results.

Readme

vitest-mcp

npm license

Vitest harness and matchers for MCP servers.

Test your Model Context Protocol server over the SDK's in-memory transport — no subprocess, no stdio plumbing, no network. Tests run in milliseconds and exercise the real protocol path: initialization handshake, JSON-RPC framing, schema validation. A passing test means your server actually speaks MCP, not just that your handlers return the right shapes.

import { describe, expect, it } from "vitest";
import "vitest-mcp/matchers";
import { connectMCP } from "vitest-mcp";
import { createServer } from "../src/server.js"; // your MCP server factory

describe("my mcp server", () => {
  it("exposes and executes tools", async () => {
    const mcp = await connectMCP(createServer());

    await expect(mcp).toHaveTool("search");

    const result = await mcp.callTool("search", { query: "hello" });
    expect(result).toHaveTextContent(/hello/);
    expect(result).not.toBeToolError();

    await mcp.close();
  });
});

Install

npm i -D vitest-mcp

Works with any server built on @modelcontextprotocol/sdk — both the high-level McpServer and the low-level Server (anything with a connect(transport) method).

Why not just call your handlers?

Unit-testing handler functions skips everything MCP adds: capability negotiation, request/response envelopes, input-schema validation, error mapping. Spawning your server as a subprocess gets you the real thing but costs seconds per test and flakes in CI. The in-memory transport is the middle path: the genuine Client from the SDK, wired directly to your server in the same process.

Matchers

Import once per test file (or in a Vitest setupFiles):

import "vitest-mcp/matchers";

| Matcher | Asserts | |---|---| | await expect(mcp).toHaveTool(name) | server advertises the tool | | await expect(mcp).toHaveResource(uri) | server advertises the resource | | await expect(mcp).toHavePrompt(name) | server advertises the prompt | | expect(result).toBeToolError() | tool result has isError: true | | expect(result).toHaveTextContent(str \| regex) | concatenated text blocks contain/match |

Failure messages enumerate what the server actually exposes — a typo'd tool name tells you the fix:

expected server to expose tool "serach" — available tools: search, fetch, summarize

Harness API

const mcp = await connectMCP(server, {
  clientName: "my-test",       // optional handshake identity
  capabilities: {},            // optional client capabilities
});

await mcp.tools();                       // Tool[]
await mcp.callTool("name", { arg: 1 });  // CallToolResult
await mcp.resources();                   // Resource[]
await mcp.resourceTemplates();           // ResourceTemplate[]
await mcp.readResource("uri://…");       // ReadResourceResult
await mcp.prompts();                     // Prompt[]
await mcp.getPrompt("name", { k: "v" }); // GetPromptResult
await mcp.ping();
mcp.client;                              // underlying SDK Client for anything else
await mcp.close();

getTextContent(result) is also exported — the concatenated text of every text content block.

Gotchas this library documents for you

  • Unknown tools don't reject. The SDK returns them as isError results, exactly like handler failures. Assert with toBeToolError(), not .rejects.
  • One server instance, one connection. The in-memory pair links a server to a single client — use a factory (createServer()) and connect a fresh instance per suite.

License

MIT © Binaya Dhakal