Token导航 LogoToken导航TokenDH.com
MCP Dev Kit logo
开发工具stdio官方级别未说明来源级核验

MCP Dev Kit

MCP Server

vitest

为Model Context Protocol (MCP)服务器提供全面的测试、调试和日志记录工具,包括智能快照测试、自定义匹配器和安全的调试日志记录。

工具数

0

提示词数

0

GitHub Stars

1

资源数

0
开发工具TypeScriptJavaScript

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

作者 / 组织

gnana997

提供方

gnana997

最后核验

2026/5/17 20:20

运行时

Node.js

快速接入

先看主来源和安装命令,再打开仓库或文档;下面只保留这个条目的关键接入事实。

命令预览

npx vitest

详细介绍

mcp开发工具包

](https://www.npmjs.com/package/mcp-dev-kit) ![CI](https://github.com/gnana997/mcp-dev-kit/actions) ![License: MIT](https://opensource.org/licenses/MIT)

模型上下文协议(MCP)服务器的完整测试和调试工具包

使用全面的测试工具、智能快照测试和stdio安全调试日志构建可靠的MCP服务器。

为什么选择mcp开发工具包?

开发MCP服务器面临着独特的挑战:

  • 测试很难 -MCP服务器没有内置测试实用程序
  • 快照中断 -每次运行时,时间戳和ID都会发生变化
  • 日志记录中断stdio - console.log() 破坏JSON-RPC通信
  • 手动断言 -用于常见检查的重复样板

mcp-dev-kit解决了所有这些问题:

  • MCPTestClient -MCP服务器的全功能测试客户端
  • 智能快照 -自动排除动态字段(时间戳、ID)
  • 定制火柴 -工具、资源、提示的可读断言
  • 安全记录 -调试时不破坏JSON-RPC协议

快速开始

安装

npm install --save-dev mcp-dev-kit vitest

基本测试设置

1.创建 vitest.setup.ts:

import { installMCPMatchers } from 'mcp-dev-kit/matchers';

installMCPMatchers();

2.配置 vitest.config.ts:

import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    globals: true,
    environment: 'node',
    setupFiles: ['./vitest.setup.ts'],
  },
});

3.编写测试(server.test.ts):

import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import { MCPTestClient } from 'mcp-dev-kit/client';

describe('My MCP Server', () => {
  let client: MCPTestClient;

  beforeAll(async () => {
    client = new MCPTestClient({
      command: 'node',
      args: ['./my-server.js'],
    });
    await client.connect();
  });

  afterAll(async () => {
    await client.disconnect();
  });

  it('should list available tools', async () => {
    const tools = await client.listTools();
    expect(tools).toHaveLength(2);
    expect(tools[0]).toHaveToolProperty('name', 'echo');
  });

  it('should execute tools successfully', async () => {
    const result = await client.callTool('echo', { message: 'hello' });
    await expect(result).toReturnToolResult('hello');
  });

  it('should have stable response structure', async () => {
    const result = await client.callTool('list_files', { path: '/' });
    expect(result).toMatchToolResponseSnapshot();
  });
});

4.将调试日志添加到服务器:

// At the top of your MCP server file
import 'mcp-dev-kit/logger';

// Now console.log works without breaking JSON-RPC!
console.log('Server started');
console.error('Connection error', error);

5.运行测试:

npx vitest

特性

MCP测试客户端

通过stdio生成和测试MCP服务器的综合测试客户端。

import { MCPTestClient } from 'mcp-dev-kit/client';

const client = new MCPTestClient({
  command: 'node',
  args: ['./my-server.js'],
  env: { DEBUG: 'true' },
  timeout: 30000,
});

await client.connect();

// Test server capabilities
const serverInfo = client.getServerInfo();
const capabilities = client.getServerCapabilities();

// List and call tools
const tools = await client.listTools();
const result = await client.callTool('my-tool', { param: 'value' });

// List and read resources
const resources = await client.listResources();
const content = await client.readResource('file://config.json');

// List and get prompts
const prompts = await client.listPrompts();
const prompt = await client.getPrompt('greeting', { name: 'Alice' });

// Helper methods for common patterns
const toolResult = await client.expectToolCallSuccess('my-tool', { input: 'test' });
const error = await client.expectToolCallError('bad-tool', {});

await client.disconnect();

主要特点:

  • 自动流程生命周期管理
  • 请求/响应与超时匹配
  • 服务器通知处理
  • 全面的错误处理
  • TypeScript优先,具有完全类型安全性

定制Vitest配对器

用于MCP特定测试的可读、富有表现力的断言。

import { installMCPMatchers } from 'mcp-dev-kit/matchers';

installMCPMatchers();

可用匹配器:

// Tool assertions
await expect(client).toHaveTool('echo');
const tools = await client.listTools();
expect(tools[0]).toHaveToolProperty('description', 'Echoes back the message');
expect(tools[0]).toMatchToolSchema({
  type: 'object',
  required: ['message']
});

// Resource assertions
await expect(client).toHaveResource('config://app.json');
const resources = await client.listResources();
expect(resources[0]).toHaveProperty('uri', 'config://app.json');

// Prompt assertions
await expect(client).toHavePrompt('greeting');
const prompts = await client.listPrompts();
expect(prompts[0]).toHaveProperty('name', 'greeting');

// Tool result assertions
const result = await client.callTool('echo', { message: 'test' });
await expect(result).toReturnToolResult('test');
await expect(client.callTool('unknown', {})).toThrowToolError();

优点:

  • 清晰、自文档化的测试代码
  • 测试失败时更好的错误消息
  • 减少测试文件中的样板
  • 使用TypeScript进行类型安全

快照测试

MCP感知快照测试,具有智能现场排除功能。

为什么要对MCP进行快照测试?

MCP服务器响应通常包含每次运行时都会发生变化的动态数据:

  • 时间戳(2024-11-03T10:30:00.000Z)
  • 请求ID(abc123)
  • 执行时间(42.5ms)
  • 自动递增ID、文件索引节点、git SHA

每次运行时,常规快照测试都会失败。 mcp-devkit会自动排除这些字段 同时捕获稳定的响应结构。

快速示例

import { installMCPMatchers } from 'mcp-dev-kit/matchers';

installMCPMatchers();

describe('File System Server', () => {
  it('should return consistent file listing structure', async () => {
    const result = await client.callTool('list_files', { path: '/project' });

    // Timestamps, IDs, and dynamic fields automatically excluded!
    expect(result).toMatchToolResponseSnapshot();
  });

  it('should have stable tool definitions', async () => {
    const tools = await client.listTools();

    // Captures tool schemas for regression detection
    expect(tools).toMatchToolListSnapshot();
  });

  it('should snapshot custom data structures', async () => {
    const data = {
      users: [...],
      timestamp: new Date().toISOString(), // Auto-excluded
      requestId: 'abc123',                   // Auto-excluded
    };

    expect(data).toMatchMCPSnapshot();
  });
});

可用的快照匹配器

toMatchMCPSnapshot(options?) -任何MCP数据的通用快照匹配器

expect(serverResponse).toMatchMCPSnapshot();
expect(data).toMatchMCPSnapshot({ exclude: ['user.id', 'files.*.size'] });

toMatchToolResponseSnapshot(options?) -工具调用结果

const result = await client.callTool('query_database', { query: 'SELECT * FROM orders' });
expect(result).toMatchToolResponseSnapshot();

toMatchToolListSnapshot(options?) -关于工具定义

const tools = await client.listTools();
expect(tools).toMatchToolListSnapshot();

toMatchResourceListSnapshot(options?) -关于资源列表

const resources = await client.listResources();
expect(resources).toMatchResourceListSnapshot();

toMatchPromptListSnapshot(options?) -用于快速定义

const prompts = await client.listPrompts();
expect(prompts).toMatchPromptListSnapshot();

智能默认值

这些字段是 自动排除 从所有快照中:

  • timestamp
  • requestId
  • executionTime
  • cacheKey
  • _meta.timestamp
  • serverInfo.startedAt
  • serverInfo.uptime

例子:

// Original response
{
  "users": [...],
  "timestamp": "2024-11-03T10:30:00.000Z",  // ❌ Excluded
  "requestId": "abc123",                     // ❌ Excluded
  "executionTime": 42.5                      // ❌ Excluded
}

// Snapshot (only stable data)
{
  "users": [...]  // ✅ Captured
}

自定义排除

使用glob模式排除其他字段:

// Exclude file system-specific fields
expect(result).toMatchToolResponseSnapshot({
  exclude: ['files.*.size', 'files.*.inode', 'files.*.modified']
});

// Exclude all auto-increment IDs
expect(data).toMatchMCPSnapshot({
  exclude: ['*.id', '*.userId', 'rows.*.orderId']
});

// Exclude nested timestamps with custom names
expect(response).toMatchMCPSnapshot({
  exclude: ['data.users.*.createdAt', 'metadata.generatedAt']
});

模式语法:

  • field -不包括顶级字段
  • nested.field -排除嵌套字段
  • array.*.field -从所有数组项中排除字段
  • data.users.*.createdAt -不包括 createdAt 来自所有用户 data.users

演出

带有属性排除的快照测试增加的开销可以忽略不计:

数据大小标准化开销备注
10-100个项目\ {

const result = await client.callTool('list_users', {}); const parsed = JSON.parse(result.content[0]?.text || '{}');

// Explicit assertions for critical properties expect(parsed.users).toHaveLength(50); expect(parsed.users[0]).toHaveProperty('name'); expect(parsed.users[0]).toHaveProperty('email');

// Snapshot for structure regression detection expect(result).toMatchToolResponseSnapshot(); });


看 [示例/快照示例/](./examples/snapshot-example/) 查看带有基准的完整工作示例。

### 调试日志记录

不会中断JSON-RPC stdio通信的安全调试日志记录。

#### 问题

MCP服务器通过stdio上的JSON-RPC进行通信。每条消息必须是stdout上的一行JSON:

{"jsonrpc":"2.0","method":"tools/list","params":{...}}\n


如果你在stdout中写任何其他东西(比如 `console.log()`),它腐蚀了溪流:

Server starting... ← Breaks protocol! {"jsonrpc":"2.0","method":"tools/list","params":{...}}\n


结果: `SyntaxError: Unexpected token 'S'`

#### 解决方案

**mcp-devkit将所有控制台输出重定向到stderr**,保持stdout干净:

- **标准输出** =纯JSON-RPC(协议)
- **标准错误** =所有日志(调试)

#### 自动修补程序(推荐)

// At the top of your MCP server file import 'mcp-dev-kit/logger';

// Now console.log works without breaking JSON-RPC! console.log('Server started', { port: 3000 }); console.info('Configuration loaded'); console.warn('Deprecated feature used'); console.error('Connection failed', error);


**选择退出:**

MCP_DEV_KIT_NO_AUTO_PATCH=true node server.js


#### 手动记录仪

为了获得更多控制,请创建一个自定义记录器实例:

import { createLogger } from 'mcp-dev-kit';

const logger = createLogger({ timestamps: true, colors: true, level: 'info', // Only show info and above logFile: './server.log', // Optional file output });

logger.info('Server starting...'); logger.warn('Configuration may need updating'); logger.error('Connection failed', { reason: 'timeout' });

// Cleanup when done await logger.close();


#### 记录器功能

- **自动修补** -只需导入并整合.log即可
- **彩色输出** -颜色编码日志级别(自动检测TTY)
- **时间戳** -所有日志上的ISO8601时间戳
- **对象格式化** -漂亮的打印对象 `util.inspect()`
- **文件日志记录** -可选异步文件输出
- **清理** -对原始控制台的优雅修复
- **零开销** -轻量级,使用微微色(7 KB)

#### 配置

**日志级别:**

const logger = createLogger({ level: 'warn' });

logger.debug('Not shown'); logger.info('Not shown'); logger.warn('Shown'); // ✓ logger.error('Shown'); // ✓


**颜色:**

createLogger({ colors: false }); // Force disable createLogger({ colors: true }); // Force enable // Auto-detected by default based on process.stderr.isTTY


**时间戳:**

createLogger({ timestamps: false }); // Disable // ISO8601 format: 2024-11-03T12:34:56.789Z


**文件日志记录:**

const logger = createLogger({ logFile: './server.log', });

logger.info('This goes to both stderr and server.log');

// Flush pending writes await logger.close();


## 测试指南

### 测试结构

按MCP功能组织测试:

describe('My MCP Server', () => { let client: MCPTestClient;

beforeAll(async () => { client = new MCPTestClient({ command: 'node', args: ['./server.js'] }); await client.connect(); });

afterAll(async () => { await client.disconnect(); });

describe('Server Initialization', () => { it('should expose correct server info', () => { const info = client.getServerInfo(); expect(info.name).toBe('my-server'); expect(info.version).toBe('1.0.0'); });

it('should declare required capabilities', () => { const caps = client.getServerCapabilities(); expect(caps.tools).toBeDefined(); }); });

describe('Tools', () => { it('should list all available tools', async () => { const tools = await client.listTools(); expect(tools).toHaveLength(3); expect(tools.map(t => t.name)).toEqual(['echo', 'calculate', 'search']); });

it('should execute tools successfully', async () => { const result = await client.callTool('echo', { message: 'test' }); expect(result.content[0]?.text).toBe('test'); });

it('should handle tool errors gracefully', async () => { const error = await client.expectToolCallError('calculate', { invalid: 'params' }); expect(error.message).toContain('Invalid parameters'); });

it('should have stable tool schemas', async () => { const tools = await client.listTools(); expect(tools).toMatchToolListSnapshot(); }); });

describe('Resources', () => { it('should list available resources', async () => { await expect(client).toHaveResource('config://app.json'); });

it('should read resource content', async () => { const content = await client.readResource('config://app.json'); expect(content.contents[0]?.text).toContain('version'); }); });

describe('Prompts', () => { it('should provide defined prompts', async () => { await expect(client).toHavePrompt('greeting'); });

it('should render prompts with arguments', async () => { const prompt = await client.getPrompt('greeting', { name: 'Alice' }); expect(prompt.messages[0]?.content.text).toContain('Alice'); }); }); });


### 测试最佳实践

**✅ 做:**

1. **测试所有MCP功能** -工具、资源、提示
1. **使用描述性测试名称** -明确说明正在测试的内容
1. **结合匹配和快照** -显式断言+结构验证
1. **测试错误案例** -不要只测试快乐的道路
1. **清理资源** -始终断开客户端连接 `afterAll`
1. **适当使用超时** -为慢速操作设置合理的超时
1. **测试服务器生命周期** -测试初始化和关闭

**❌ 不要:**

1. **不共享客户端状态** -每个测试套件都应该有自己的客户端
1. **不要跳过错误测试** -错误处理至关重要
1. **不测试实现细节** -仅测试公共API
1. **不要创建不稳定的测试** -避免依赖时间的断言
1. **不要忽略快照** -仔细查看快照更改
1. **不要硬编码系统特定的路径** -使用相对路径或环境变量

### 错误测试

始终测试错误条件:

it('should validate tool parameters', async () => { const error = await client.expectToolCallError('calculate', { // Missing required parameter }); expect(error.code).toBe(-32602); // Invalid params expect(error.message).toContain('Required parameter'); });

it('should handle resource not found', async () => { await expect( client.readResource('nonexistent://resource') ).rejects.toThrow('Resource not found'); });

it('should reject unknown tools', async () => { await expect( client.callTool('unknown-tool', {}) ).rejects.toThrow(); });


### 性能测试

关键操作的测试响应时间:

it('should respond quickly to tool calls', async () => { const start = Date.now(); await client.callTool('quick-operation', {}); const duration = Date.now() - start;

expect(duration).toBeLessThan(1000); // { // 1. List available tools const tools = await client.listTools(); expect(tools.length).toBeGreaterThan(0);

// 2. Get resource for context const config = await client.readResource('config://app.json'); const settings = JSON.parse(config.contents[0]?.text || '{}');

// 3. Execute tool with context const result = await client.callTool('process', { mode: settings.defaultMode, }); expect(result.content[0]?.text).toBeTruthy();

// 4. Verify result structure expect(result).toMatchToolResponseSnapshot(); });


## 例子

看 [示例/](./examples/) 完整示例目录:

- **[快照示例/](./examples/snapshot-example/)** -使用基准完成快照测试
- **[记录器/基本用法.ts](./examples/logger/basic-usage.ts)** -自动补丁控制台
- **[记录器/手动设置.ts](./examples/logger/manual-setup.ts)** -自定义记录器实例
- **[logger/file-loging.ts](./examples/logger/file-logging.ts)** -记录到文件
- **[logger/mcp-server-example.ts](./examples/logger/mcp-server-example.ts)** -真正的MCP服务器

运行示例:

npm install -g tsx tsx examples/logger/basic-usage.ts


## API 参考

### MCPTestClient

class MCPTestClient { constructor(options: { command: string; args?: string[]; env?: Record; timeout?: number; });

// Connection management connect(): Promise; disconnect(): Promise;

// Server info getServerInfo(): ServerInfo; getServerCapabilities(): ServerCapabilities;

// Tools listTools(): Promise; callTool(name: string, args: unknown): Promise; expectToolCallSuccess(name: string, args: unknown): Promise; expectToolCallError(name: string, args: unknown): Promise;

// Resources listResources(): Promise; readResource(uri: string): Promise;

// Prompts listPrompts(): Promise ; getPrompt(name: string, args?: unknown): Promise; }


### 日志记录器

interface LoggerOptions { enabled?: boolean; // Enable/disable logger (default: true) timestamps?: boolean; // Show timestamps (default: true) colors?: boolean; // Force colors on/off (default: auto-detect) level?: 'debug'|'info'|'warn'|'error'; // Min level (default: 'debug') stream?: WritableStream; // Custom output (default: process.stderr) logFile?: string; // Optional file output }

function createLogger(options?: LoggerOptions): DebugLogger; function patchConsole(options?: LoggerOptions): void; function unpatchConsole(): void;


### 匹配器

// Installation function installMCPMatchers(): void;

// Tool matchers expect(client).toHaveTool(name: string); expect(tool).toHaveToolProperty(property: string, value?: any); expect(tool).toMatchToolSchema(schema: object); expect(result).toReturnToolResult(expected: any); expect(promise).toThrowToolError();

// Resource matchers expect(client).toHaveResource(uri: string);

// Prompt matchers expect(client).toHavePrompt(name: string);

// Snapshot matchers expect(data).toMatchMCPSnapshot(options?: { exclude?: string[] }); expect(result).toMatchToolResponseSnapshot(options?: { exclude?: string[] }); expect(tools).toMatchToolListSnapshot(options?: { exclude?: string[] }); expect(resources).toMatchResourceListSnapshot(options?: { exclude?: string[] }); expect(prompts).toMatchPromptListSnapshot(options?: { exclude?: string[] });


## 故障排除

### 测试挂起还是超时?

增加超时时间:

const client = new MCPTestClient({ command: 'node', args: ['./server.js'], timeout: 60000, // 60 seconds });


### 快照意外失败?

检查是否排除了足够的动态字段:

expect(result).toMatchToolResponseSnapshot({ exclude: [ 'timestamp', 'requestId', 'files.*.modified', 'data.*.generatedAt', ] });


### 日志未显示?

检查您的日志级别:

createLogger({ level: 'debug' }); // Show everything


### 颜色不起作用?

颜色仅在stderr为TTY时有效。强制启用/禁用:

createLogger({ colors: true }); // Always color createLogger({ colors: false }); // Never color


### 客户端未连接?

验证您的服务器是否正在使用stdio传输并响应初始化:

// Server must respond to initialize request server.setRequestHandler(InitializeRequestSchema, async (request) => { return { protocolVersion: '2024-11-05', capabilities: { tools: {} }, serverInfo: { name: 'my-server', version: '1.0.0' }, }; });


## 需求

- Node.js>=18.0.0
- TypeScript>=5.0.0(如果使用TypeScript)
- Vitest>=1.0.0(用于测试功能)

## 贡献

看 [贡献.md](./CONTRIBUTING.md) 用于开发设置和指南。

## 许可证

MIT© [格纳997](https://github.com/gnana997)

## 相关项目

- [@模型上下文协议/sdk](https://modelcontextprotocol.io) -官方MCP SDK
- [模型上下文协议](https://modelcontextprotocol.io) -协议规范
- [Nochen jsonrpc](https://www.npmjs.com/package/node-stdio-jsonrpc) -基于stdio的JSON-RPC 2.0

______________________________________________________________________

**内置❤️ 对于MCP社区**

觉得这个有用吗?  ⭐

目录标签

目录标签

开发工具TypeScriptJavaScriptMCP测试本地部署服务器调试快照测试JSON-RPC

接入字段

传输方式(transport,传输协议)

stdio

鉴权方式(authType,认证方式)

token

运行时(runtime,运行环境)

Node.js

来源包(packageName,安装包名)

vitest

工具数量(toolCount,工具数)

0

资源数量(resourceCount,资源数)

0

提示词数量(promptCount,提示词数)

0

权限和风险

stdiotoken部署方式未说明

接入前请确认传输方式、认证方式和部署位置,并根据实际工具能力限制访问范围。

安装前确认

不要直接授予不必要的文件、网络或账号权限;先核对安装命令和配置内容。

来源信息

继续浏览同类 MCP