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

@tmcp/transport-in-memory

v0.0.6

Published

In memory transport for `tmcp` servers

Readme

@tmcp/transport-in-memory

In-memory transport for testing MCP servers.

Installation

pnpm add -D @tmcp/transport-in-memory

Overview

@tmcp/transport-in-memory provides an in-memory transport layer for testing MCP (Model Context Protocol) servers without the need for actual network connections or stdio communication. It's designed to make testing MCP servers fast, reliable, and straightforward.

Features

  • In-Memory Communication: No network overhead, perfect for unit and integration tests
  • Session Management: Full support for multiple concurrent sessions
  • Message Capture: Automatically captures all sent and broadcast messages for verification
  • Custom Context Support: Type-safe custom context passing via generics
  • Complete MCP API: All MCP protocol methods supported
  • Subscription Tracking: Monitors resource subscriptions per session

Basic Usage

import { McpServer } from 'tmcp';
import { InMemoryTransport } from '@tmcp/transport-in-memory';

// Create your MCP server
const server = new McpServer({
	name: 'test-server',
	version: '1.0.0',
});

// Add a tool
server.tool(
	{
		name: 'greet',
		description: 'Greet someone',
		inputSchema: {
			type: 'object',
			properties: {
				name: { type: 'string' },
			},
			required: ['name'],
		},
	},
	async ({ name }) => {
		return {
			content: [
				{
					type: 'text',
					text: `Hello, ${name}!`,
				},
			],
		};
	},
);

// Create transport and session
const transport = new InMemoryTransport(server);
const session = transport.session();

// Initialize the connection
await session.initialize(
	'2024-11-05',
	{ elicitation: {} },
	{ name: 'test-client', version: '1.0.0' },
);

// Call the tool
const result = await session.callTool('greet', { name: 'World' });
console.log(result.content[0].text); // "Hello, World!"

// Clean up
session.close();
transport.close();

API Reference

InMemoryTransport

The main transport class that manages server communication and sessions.

Constructor

new InMemoryTransport(server);
  • server: McpServer - The MCP server instance to test

Properties

  • server: McpServer - Get the underlying server instance

Methods

session(session_id?)

Get or create a session.

const session = transport.session(); // Auto-generated ID
const session2 = transport.session('custom-id'); // Custom ID
request(method, params?, sessionId?, ctx?)

Send a low-level JSON-RPC request (prefer using Session methods).

await transport.request('tools/list', {}, session_id);
response(request_id, result?, error?, sessionId?, ctx?)

Send a response to a server-initiated request.

await transport.response(123, { accepted: true });
clear()

Clear all captured messages for all sessions.

transport.clear();
close()

Close all sessions and clean up event listeners.

transport.close();

Session

A session represents a single client connection to the server.

Properties

  • sessionId: string - The session identifier
  • sentMessages: JSONRPCRequest[] - All messages sent by the server (excluding broadcasts)
  • lastRequest: JSONRPCRequest | undefined - The most recent request sent by the server
  • broadcastMessages: JSONRPCRequest[] - All broadcast messages sent by the server
  • sessionInfo: Partial<Context["sessionInfo"]> - Current session information
  • subscriptions: Subscriptions - Current resource subscriptions

MCP Protocol Methods

initialize(protocolVersion, capabilities, clientInfo, ctx?)

Initialize the MCP connection.

await session.initialize(
	'2024-11-05',
	{ tools: {}, prompts: {}, resources: {} },
	{ name: 'my-client', version: '1.0.0' },
);
ping(ctx?)

Ping the server.

await session.ping();
listTools(params?, ctx?)

List all available tools.

const { tools, nextCursor } = await session.listTools();
const morePage = await session.listTools({ cursor: nextCursor });
callTool(name, args?, ctx?)

Call a tool by name.

const result = await session.callTool('my-tool', {
	param1: 'value1',
	param2: 42,
});
listPrompts(params?, ctx?)

List all available prompts.

const { prompts } = await session.listPrompts();
getPrompt(name, args?, ctx?)

Get a prompt with optional arguments.

const result = await session.getPrompt('my-prompt', {
	arg1: 'value1',
});
listResources(params?, ctx?)

List all available resources.

const { resources } = await session.listResources();
listResourceTemplates(params?, ctx?)

List all resource templates.

const { resourceTemplates } = await session.listResourceTemplates();
readResource(uri, ctx?)

Read a resource by URI.

const resource = await session.readResource('file:///path/to/resource');
subscribeResource(uri, ctx?)

Subscribe to resource updates.

await session.subscribeResource('file:///path/to/resource');
unsubscribeResource(uri, ctx?)

Unsubscribe from resource updates.

await session.unsubscribeResource('file:///path/to/resource');
complete(ref, argument, context?, ctx?)

Request completion suggestions.

const result = await session.complete(
	{ type: 'ref/prompt', name: 'my-prompt' },
	{ name: 'param1', value: 'part' },
	{ arguments: { param2: 'value' } },
);
setLogLevel(level, ctx?)

Set the logging level.

await session.setLogLevel('debug');
response(request_id, result?, error?, ctx?)

Respond to a server-initiated request.

// Get the request from sentMessages or lastRequest
const request = session.lastRequest;
await session.response(request.id, { accepted: true });

Session Management Methods

clear()

Clear all captured messages for this session.

session.clear();
close()

Close the session and clean up event listeners.

session.close();

Custom Context

You can pass custom context through all requests using TypeScript generics:

const server = new McpServer({ name: 'test', version: '1.0.0' }).withContext<{
	userId: string;
	requestId: string;
}>();

const transport = new InMemoryTransport(server);
const session = transport.session();

// Pass custom context
await session.callTool(
	'my-tool',
	{ param: 'value' },
	{ userId: '123', requestId: 'abc' },
);

Testing Examples

Testing Tool Execution

import { test } from 'node:test';
import assert from 'node:assert';
import { McpServer } from 'tmcp';
import { InMemoryTransport } from '@tmcp/transport-in-memory';
import * as v from 'valibot';
import { ValibotJsonSchemaAdapter } from '@tmcp/adapter-valibot';

test('tool returns correct result', async () => {
	const server = new McpServer(
		{ name: 'test', version: '1.0.0' },
		{
			adapter: new ValibotJsonSchemaAdapter(),
			capabilities: { tools: {} },
		},
	);

	server.tool(
		{
			name: 'add',
			description: 'Add two numbers',
			schema: v.object({
				a: v.number(),
				b: v.number(),
			}),
		},
		async ({ a, b }) => {
			return {
				content: [{ type: 'text', text: String(a + b) }],
			};
		},
	);

	const transport = new InMemoryTransport(server);
	const session = transport.session();

	await session.initialize(
		'2024-11-05',
		{ tools: {} },
		{ name: 'test', version: '1.0.0' },
	);

	const result = await session.callTool('add', { a: 2, b: 3 });

	assert.strictEqual(result.content[0].text, '5');

	session.close();
	transport.close();
});

Testing Multiple Sessions and Response

const server = new McpServer(
	{ name: 'test', version: '1.0.0' },
	{
		adapter: undefined,
		capabilities: {
			tools: {},
		},
	},
);

server.tool(
	{
		name: 'test',
		description: 'A test tool',
	},
	async () => {
		console.log('Tool executed');
		await server.refreshRoots();
		return {
			content: [{ type: 'text', text: JSON.stringify(server.roots) }],
		};
	},
);
const transport = new InMemoryTransport(server);

const session1 = transport.session('session-1');
const session2 = transport.session('session-2');

await session1.initialize(
	'2024-11-05',
	{ roots: {} },
	{ name: 'client-1', version: '1.0.0' },
);
await session2.initialize(
	'2024-11-05',
	{ roots: {} },
	{ name: 'client-2', version: '1.0.0' },
);

const toolCall = session1.callTool('test');

if (session1.lastRequest?.id) {
	await session1.response(session1.lastRequest.id, {
		roots: ['file:///folder'],
	});
	const result = await toolCall;
	if (result?.content?.[0].type === 'text') {
		assert.equal(result?.content?.[0].text, '["file:///folder"]');
	} else {
		assert.fail('Unexpected tool result');
	}
}

assert.equal(session1.sentMessages.length, 1);
assert.equal(session2.sentMessages.length, 0);

session1.close();
session2.close();
transport.close();

License

MIT