{"id":32722574,"url":"https://github.com/gnana997/mcp-dev-kit","last_synced_at":"2026-05-16T17:13:51.156Z","repository":{"id":322136586,"uuid":"1088320506","full_name":"gnana997/mcp-dev-kit","owner":"gnana997","description":"Complete testing \u0026 debugging toolkit for MCP (Model Context Protocol) servers","archived":false,"fork":false,"pushed_at":"2025-11-02T19:05:37.000Z","size":90,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-11-02T20:29:19.779Z","etag":null,"topics":["debugging","devtools","json-rpc","mcp","model-context-protocol","stdio","testing","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gnana997.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-11-02T18:35:59.000Z","updated_at":"2025-11-02T19:10:03.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/gnana997/mcp-dev-kit","commit_stats":null,"previous_names":["gnana997/mcp-dev-kit"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/gnana997/mcp-dev-kit","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gnana997%2Fmcp-dev-kit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gnana997%2Fmcp-dev-kit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gnana997%2Fmcp-dev-kit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gnana997%2Fmcp-dev-kit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gnana997","download_url":"https://codeload.github.com/gnana997/mcp-dev-kit/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gnana997%2Fmcp-dev-kit/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":282365370,"owners_count":26657258,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-11-02T02:00:06.609Z","response_time":64,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["debugging","devtools","json-rpc","mcp","model-context-protocol","stdio","testing","typescript"],"created_at":"2025-11-02T22:01:06.144Z","updated_at":"2026-05-16T17:13:51.146Z","avatar_url":"https://github.com/gnana997.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# mcp-dev-kit\n\n[![npm version](https://img.shields.io/npm/v/mcp-dev-kit.svg)](https://www.npmjs.com/package/mcp-dev-kit)\n[![CI](https://github.com/gnana997/mcp-dev-kit/actions/workflows/ci.yml/badge.svg)](https://github.com/gnana997/mcp-dev-kit/actions)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\n**Complete testing and debugging toolkit for Model Context Protocol (MCP) servers**\n\nBuild reliable MCP servers with comprehensive testing utilities, intelligent snapshot testing, and stdio-safe debug logging.\n\n## Why mcp-dev-kit?\n\nDeveloping MCP servers comes with unique challenges:\n\n- ❌ **Testing is hard** - No built-in test utilities for MCP servers\n- ❌ **Snapshots break** - Timestamps and IDs change on every run\n- ❌ **Logging breaks stdio** - `console.log()` corrupts JSON-RPC communication\n- ❌ **Manual assertions** - Repetitive boilerplate for common checks\n\n**mcp-dev-kit solves all of these:**\n\n- ✅ **MCPTestClient** - Full-featured test client for MCP servers\n- ✅ **Smart snapshots** - Auto-exclude dynamic fields (timestamps, IDs)\n- ✅ **Custom matchers** - Readable assertions for tools, resources, prompts\n- ✅ **Safe logging** - Debug without breaking JSON-RPC protocol\n\n## Quick Start\n\n### Installation\n\n```bash\nnpm install --save-dev mcp-dev-kit vitest\n```\n\n### Basic Test Setup\n\n**1. Create `vitest.setup.ts`:**\n\n```typescript\nimport { installMCPMatchers } from 'mcp-dev-kit/matchers';\n\ninstallMCPMatchers();\n```\n\n**2. Configure `vitest.config.ts`:**\n\n```typescript\nimport { defineConfig } from 'vitest/config';\n\nexport default defineConfig({\n  test: {\n    globals: true,\n    environment: 'node',\n    setupFiles: ['./vitest.setup.ts'],\n  },\n});\n```\n\n**3. Write tests (`server.test.ts`):**\n\n```typescript\nimport { describe, it, expect, beforeAll, afterAll } from 'vitest';\nimport { MCPTestClient } from 'mcp-dev-kit/client';\n\ndescribe('My MCP Server', () =\u003e {\n  let client: MCPTestClient;\n\n  beforeAll(async () =\u003e {\n    client = new MCPTestClient({\n      command: 'node',\n      args: ['./my-server.js'],\n    });\n    await client.connect();\n  });\n\n  afterAll(async () =\u003e {\n    await client.disconnect();\n  });\n\n  it('should list available tools', async () =\u003e {\n    const tools = await client.listTools();\n    expect(tools).toHaveLength(2);\n    expect(tools[0]).toHaveToolProperty('name', 'echo');\n  });\n\n  it('should execute tools successfully', async () =\u003e {\n    const result = await client.callTool('echo', { message: 'hello' });\n    await expect(result).toReturnToolResult('hello');\n  });\n\n  it('should have stable response structure', async () =\u003e {\n    const result = await client.callTool('list_files', { path: '/' });\n    expect(result).toMatchToolResponseSnapshot();\n  });\n});\n```\n\n**4. Add debug logging to your server:**\n\n```typescript\n// At the top of your MCP server file\nimport 'mcp-dev-kit/logger';\n\n// Now console.log works without breaking JSON-RPC!\nconsole.log('Server started');\nconsole.error('Connection error', error);\n```\n\n**5. Run tests:**\n\n```bash\nnpx vitest\n```\n\n## Features\n\n### MCP Test Client\n\nComprehensive test client for spawning and testing MCP servers via stdio.\n\n```typescript\nimport { MCPTestClient } from 'mcp-dev-kit/client';\n\nconst client = new MCPTestClient({\n  command: 'node',\n  args: ['./my-server.js'],\n  env: { DEBUG: 'true' },\n  timeout: 30000,\n});\n\nawait client.connect();\n\n// Test server capabilities\nconst serverInfo = client.getServerInfo();\nconst capabilities = client.getServerCapabilities();\n\n// List and call tools\nconst tools = await client.listTools();\nconst result = await client.callTool('my-tool', { param: 'value' });\n\n// List and read resources\nconst resources = await client.listResources();\nconst content = await client.readResource('file://config.json');\n\n// List and get prompts\nconst prompts = await client.listPrompts();\nconst prompt = await client.getPrompt('greeting', { name: 'Alice' });\n\n// Helper methods for common patterns\nconst toolResult = await client.expectToolCallSuccess('my-tool', { input: 'test' });\nconst error = await client.expectToolCallError('bad-tool', {});\n\nawait client.disconnect();\n```\n\n**Key Features:**\n- Automatic process lifecycle management\n- Request/response matching with timeouts\n- Server notification handling\n- Comprehensive error handling\n- TypeScript-first with full type safety\n\n### Custom Vitest Matchers\n\nReadable, expressive assertions for MCP-specific testing.\n\n```typescript\nimport { installMCPMatchers } from 'mcp-dev-kit/matchers';\n\ninstallMCPMatchers();\n```\n\n**Available Matchers:**\n\n```typescript\n// Tool assertions\nawait expect(client).toHaveTool('echo');\nconst tools = await client.listTools();\nexpect(tools[0]).toHaveToolProperty('description', 'Echoes back the message');\nexpect(tools[0]).toMatchToolSchema({\n  type: 'object',\n  required: ['message']\n});\n\n// Resource assertions\nawait expect(client).toHaveResource('config://app.json');\nconst resources = await client.listResources();\nexpect(resources[0]).toHaveProperty('uri', 'config://app.json');\n\n// Prompt assertions\nawait expect(client).toHavePrompt('greeting');\nconst prompts = await client.listPrompts();\nexpect(prompts[0]).toHaveProperty('name', 'greeting');\n\n// Tool result assertions\nconst result = await client.callTool('echo', { message: 'test' });\nawait expect(result).toReturnToolResult('test');\nawait expect(client.callTool('unknown', {})).toThrowToolError();\n```\n\n**Benefits:**\n- Clear, self-documenting test code\n- Better error messages when tests fail\n- Reduces boilerplate in test files\n- Type-safe with TypeScript\n\n### Snapshot Testing\n\nMCP-aware snapshot testing with intelligent field exclusion.\n\n#### Why Snapshot Testing for MCP?\n\nMCP server responses often contain dynamic data that changes on every run:\n- Timestamps (`2024-11-03T10:30:00.000Z`)\n- Request IDs (`abc123`)\n- Execution times (`42.5ms`)\n- Auto-increment IDs, file inodes, git SHAs\n\nRegular snapshot testing would fail on every run. **mcp-dev-kit automatically excludes these fields** while capturing the stable response structure.\n\n#### Quick Example\n\n```typescript\nimport { installMCPMatchers } from 'mcp-dev-kit/matchers';\n\ninstallMCPMatchers();\n\ndescribe('File System Server', () =\u003e {\n  it('should return consistent file listing structure', async () =\u003e {\n    const result = await client.callTool('list_files', { path: '/project' });\n\n    // Timestamps, IDs, and dynamic fields automatically excluded!\n    expect(result).toMatchToolResponseSnapshot();\n  });\n\n  it('should have stable tool definitions', async () =\u003e {\n    const tools = await client.listTools();\n\n    // Captures tool schemas for regression detection\n    expect(tools).toMatchToolListSnapshot();\n  });\n\n  it('should snapshot custom data structures', async () =\u003e {\n    const data = {\n      users: [...],\n      timestamp: new Date().toISOString(), // Auto-excluded\n      requestId: 'abc123',                   // Auto-excluded\n    };\n\n    expect(data).toMatchMCPSnapshot();\n  });\n});\n```\n\n#### Available Snapshot Matchers\n\n**`toMatchMCPSnapshot(options?)`** - Generic snapshot matcher for any MCP data\n```typescript\nexpect(serverResponse).toMatchMCPSnapshot();\nexpect(data).toMatchMCPSnapshot({ exclude: ['user.id', 'files.*.size'] });\n```\n\n**`toMatchToolResponseSnapshot(options?)`** - For tool call results\n```typescript\nconst result = await client.callTool('query_database', { query: 'SELECT * FROM orders' });\nexpect(result).toMatchToolResponseSnapshot();\n```\n\n**`toMatchToolListSnapshot(options?)`** - For tool definitions\n```typescript\nconst tools = await client.listTools();\nexpect(tools).toMatchToolListSnapshot();\n```\n\n**`toMatchResourceListSnapshot(options?)`** - For resource listings\n```typescript\nconst resources = await client.listResources();\nexpect(resources).toMatchResourceListSnapshot();\n```\n\n**`toMatchPromptListSnapshot(options?)`** - For prompt definitions\n```typescript\nconst prompts = await client.listPrompts();\nexpect(prompts).toMatchPromptListSnapshot();\n```\n\n#### Smart Defaults\n\nThese fields are **automatically excluded** from all snapshots:\n\n- `timestamp`\n- `requestId`\n- `executionTime`\n- `cacheKey`\n- `_meta.timestamp`\n- `serverInfo.startedAt`\n- `serverInfo.uptime`\n\n**Example:**\n```typescript\n// Original response\n{\n  \"users\": [...],\n  \"timestamp\": \"2024-11-03T10:30:00.000Z\",  // ❌ Excluded\n  \"requestId\": \"abc123\",                     // ❌ Excluded\n  \"executionTime\": 42.5                      // ❌ Excluded\n}\n\n// Snapshot (only stable data)\n{\n  \"users\": [...]  // ✅ Captured\n}\n```\n\n#### Custom Exclusions\n\nExclude additional fields using glob patterns:\n\n```typescript\n// Exclude file system-specific fields\nexpect(result).toMatchToolResponseSnapshot({\n  exclude: ['files.*.size', 'files.*.inode', 'files.*.modified']\n});\n\n// Exclude all auto-increment IDs\nexpect(data).toMatchMCPSnapshot({\n  exclude: ['*.id', '*.userId', 'rows.*.orderId']\n});\n\n// Exclude nested timestamps with custom names\nexpect(response).toMatchMCPSnapshot({\n  exclude: ['data.users.*.createdAt', 'metadata.generatedAt']\n});\n```\n\n**Pattern Syntax:**\n- `field` - Excludes top-level field\n- `nested.field` - Excludes nested field\n- `array.*.field` - Excludes field from all array items\n- `data.users.*.createdAt` - Excludes `createdAt` from all users in `data.users`\n\n#### Performance\n\nSnapshot testing with property exclusion adds negligible overhead:\n\n| Data Size | Normalization Overhead | Notes |\n|-----------|----------------------|-------|\n| 10-100 items | \u003c 50 microseconds | Typical MCP responses |\n| 1000 items | \u003c 50 microseconds | Large responses |\n| 5000 items | \u003c 50 microseconds | Extra-large responses |\n| Deep nesting (10+ levels) | ~1-2 milliseconds | Rare in practice |\n\n**Performance Characteristics:**\n- **Scales well with data SIZE** - More items ≈ similar overhead\n- **Degrades with NESTING DEPTH** - Deeper structures = slower\n- **Production-ready** - \u003c 1% overhead for typical MCP responses\n\n**Note on Benchmarks:**\nOur benchmarks measure JIT-optimized code after warmup. Real-world \"cold start\" performance may vary slightly. All measurements exclude snapshot file I/O (handled by Vitest).\n\n**Feedback Welcome!**\nI'm actively testing this feature and would love your feedback! If you experience performance issues or have suggestions, please [open an issue on GitHub](https://github.com/gnana997/mcp-dev-kit/issues).\n\n#### Updating Snapshots\n\nWhen you intentionally change your server's response format:\n\n```bash\n# Review what changed\nnpm test\n\n# Update snapshots after verifying changes are correct\nnpm test -- -u\n```\n\n#### Best Practices\n\n**✅ DO:**\n- Snapshot server response structure to catch regressions\n- Use smart defaults for common dynamic fields\n- Snapshot small-to-medium datasets (10-1000 items)\n- Combine snapshots with explicit assertions for critical properties\n- Review snapshot diffs before accepting changes\n- Split large responses into focused, smaller snapshots\n\n**❌ DON'T:**\n- Snapshot without excluding dynamic fields (timestamps, IDs, etc.)\n- Create multi-megabyte snapshots (split into smaller tests instead)\n- Snapshot implementation details that may change frequently\n- Blindly update snapshots with `-u` flag without reviewing\n- Use snapshots as a replacement for explicit assertions\n\n**Example: Combined Approach**\n\n```typescript\nit('should return valid user list', async () =\u003e {\n  const result = await client.callTool('list_users', {});\n  const parsed = JSON.parse(result.content[0]?.text || '{}');\n\n  // Explicit assertions for critical properties\n  expect(parsed.users).toHaveLength(50);\n  expect(parsed.users[0]).toHaveProperty('name');\n  expect(parsed.users[0]).toHaveProperty('email');\n\n  // Snapshot for structure regression detection\n  expect(result).toMatchToolResponseSnapshot();\n});\n```\n\nSee [examples/snapshot-example/](./examples/snapshot-example/) for complete working examples with benchmarks.\n\n### Debug Logging\n\nSafe debug logging that doesn't break JSON-RPC stdio communication.\n\n#### The Problem\n\nMCP servers communicate via JSON-RPC over stdio. Every message must be a single line of JSON on stdout:\n\n```\n{\"jsonrpc\":\"2.0\",\"method\":\"tools/list\",\"params\":{...}}\\n\n```\n\nIf you write anything else to stdout (like `console.log()`), it corrupts the stream:\n\n```\nServer starting...  ← Breaks protocol!\n{\"jsonrpc\":\"2.0\",\"method\":\"tools/list\",\"params\":{...}}\\n\n```\n\nResult: `SyntaxError: Unexpected token 'S'`\n\n#### The Solution\n\n**mcp-dev-kit redirects all console output to stderr**, keeping stdout clean:\n- **stdout** = pure JSON-RPC (protocol)\n- **stderr** = all your logs (debugging)\n\n#### Auto-Patch (Recommended)\n\n```typescript\n// At the top of your MCP server file\nimport 'mcp-dev-kit/logger';\n\n// Now console.log works without breaking JSON-RPC!\nconsole.log('Server started', { port: 3000 });\nconsole.info('Configuration loaded');\nconsole.warn('Deprecated feature used');\nconsole.error('Connection failed', error);\n```\n\n**Opt-out:**\n```bash\nMCP_DEV_KIT_NO_AUTO_PATCH=true node server.js\n```\n\n#### Manual Logger\n\nFor more control, create a custom logger instance:\n\n```typescript\nimport { createLogger } from 'mcp-dev-kit';\n\nconst logger = createLogger({\n  timestamps: true,\n  colors: true,\n  level: 'info', // Only show info and above\n  logFile: './server.log', // Optional file output\n});\n\nlogger.info('Server starting...');\nlogger.warn('Configuration may need updating');\nlogger.error('Connection failed', { reason: 'timeout' });\n\n// Cleanup when done\nawait logger.close();\n```\n\n#### Logger Features\n\n- **Auto-patching** - Just import and console.log works\n- **Colored output** - Color-coded log levels (auto-detects TTY)\n- **Timestamps** - ISO8601 timestamps on all logs\n- **Object formatting** - Pretty-print objects with `util.inspect()`\n- **File logging** - Optional async file output\n- **Cleanup** - Graceful restoration of original console\n- **Zero overhead** - Lightweight, uses picocolors (7 KB)\n\n#### Configuration\n\n**Log Levels:**\n```typescript\nconst logger = createLogger({ level: 'warn' });\n\nlogger.debug('Not shown');\nlogger.info('Not shown');\nlogger.warn('Shown');     // ✓\nlogger.error('Shown');    // ✓\n```\n\n**Colors:**\n```typescript\ncreateLogger({ colors: false }); // Force disable\ncreateLogger({ colors: true });  // Force enable\n// Auto-detected by default based on process.stderr.isTTY\n```\n\n**Timestamps:**\n```typescript\ncreateLogger({ timestamps: false }); // Disable\n// ISO8601 format: 2024-11-03T12:34:56.789Z\n```\n\n**File Logging:**\n```typescript\nconst logger = createLogger({\n  logFile: './server.log',\n});\n\nlogger.info('This goes to both stderr and server.log');\n\n// Flush pending writes\nawait logger.close();\n```\n\n## Testing Guidelines\n\n### Test Structure\n\nOrganize your tests by MCP capabilities:\n\n```typescript\ndescribe('My MCP Server', () =\u003e {\n  let client: MCPTestClient;\n\n  beforeAll(async () =\u003e {\n    client = new MCPTestClient({ command: 'node', args: ['./server.js'] });\n    await client.connect();\n  });\n\n  afterAll(async () =\u003e {\n    await client.disconnect();\n  });\n\n  describe('Server Initialization', () =\u003e {\n    it('should expose correct server info', () =\u003e {\n      const info = client.getServerInfo();\n      expect(info.name).toBe('my-server');\n      expect(info.version).toBe('1.0.0');\n    });\n\n    it('should declare required capabilities', () =\u003e {\n      const caps = client.getServerCapabilities();\n      expect(caps.tools).toBeDefined();\n    });\n  });\n\n  describe('Tools', () =\u003e {\n    it('should list all available tools', async () =\u003e {\n      const tools = await client.listTools();\n      expect(tools).toHaveLength(3);\n      expect(tools.map(t =\u003e t.name)).toEqual(['echo', 'calculate', 'search']);\n    });\n\n    it('should execute tools successfully', async () =\u003e {\n      const result = await client.callTool('echo', { message: 'test' });\n      expect(result.content[0]?.text).toBe('test');\n    });\n\n    it('should handle tool errors gracefully', async () =\u003e {\n      const error = await client.expectToolCallError('calculate', { invalid: 'params' });\n      expect(error.message).toContain('Invalid parameters');\n    });\n\n    it('should have stable tool schemas', async () =\u003e {\n      const tools = await client.listTools();\n      expect(tools).toMatchToolListSnapshot();\n    });\n  });\n\n  describe('Resources', () =\u003e {\n    it('should list available resources', async () =\u003e {\n      await expect(client).toHaveResource('config://app.json');\n    });\n\n    it('should read resource content', async () =\u003e {\n      const content = await client.readResource('config://app.json');\n      expect(content.contents[0]?.text).toContain('version');\n    });\n  });\n\n  describe('Prompts', () =\u003e {\n    it('should provide defined prompts', async () =\u003e {\n      await expect(client).toHavePrompt('greeting');\n    });\n\n    it('should render prompts with arguments', async () =\u003e {\n      const prompt = await client.getPrompt('greeting', { name: 'Alice' });\n      expect(prompt.messages[0]?.content.text).toContain('Alice');\n    });\n  });\n});\n```\n\n### Testing Best Practices\n\n**✅ DO:**\n\n1. **Test all MCP capabilities** - Tools, resources, prompts\n2. **Use descriptive test names** - Clearly state what's being tested\n3. **Combine matchers and snapshots** - Explicit assertions + structure validation\n4. **Test error cases** - Don't just test happy paths\n5. **Clean up resources** - Always disconnect client in `afterAll`\n6. **Use timeouts appropriately** - Set reasonable timeouts for slow operations\n7. **Test server lifecycle** - Test initialization and shutdown\n\n**❌ DON'T:**\n\n1. **Don't share client state** - Each test suite should have its own client\n2. **Don't skip error testing** - Error handling is critical\n3. **Don't test implementation details** - Test public API only\n4. **Don't create flaky tests** - Avoid timing-dependent assertions\n5. **Don't ignore snapshots** - Review snapshot changes carefully\n6. **Don't hardcode system-specific paths** - Use relative paths or env vars\n\n### Error Testing\n\nAlways test error conditions:\n\n```typescript\nit('should validate tool parameters', async () =\u003e {\n  const error = await client.expectToolCallError('calculate', {\n    // Missing required parameter\n  });\n  expect(error.code).toBe(-32602); // Invalid params\n  expect(error.message).toContain('Required parameter');\n});\n\nit('should handle resource not found', async () =\u003e {\n  await expect(\n    client.readResource('nonexistent://resource')\n  ).rejects.toThrow('Resource not found');\n});\n\nit('should reject unknown tools', async () =\u003e {\n  await expect(\n    client.callTool('unknown-tool', {})\n  ).rejects.toThrow();\n});\n```\n\n### Performance Testing\n\nTest response times for critical operations:\n\n```typescript\nit('should respond quickly to tool calls', async () =\u003e {\n  const start = Date.now();\n  await client.callTool('quick-operation', {});\n  const duration = Date.now() - start;\n\n  expect(duration).toBeLessThan(1000); // \u003c 1 second\n});\n```\n\n### Integration Testing\n\nTest real-world workflows:\n\n```typescript\nit('should handle complete user workflow', async () =\u003e {\n  // 1. List available tools\n  const tools = await client.listTools();\n  expect(tools.length).toBeGreaterThan(0);\n\n  // 2. Get resource for context\n  const config = await client.readResource('config://app.json');\n  const settings = JSON.parse(config.contents[0]?.text || '{}');\n\n  // 3. Execute tool with context\n  const result = await client.callTool('process', {\n    mode: settings.defaultMode,\n  });\n  expect(result.content[0]?.text).toBeTruthy();\n\n  // 4. Verify result structure\n  expect(result).toMatchToolResponseSnapshot();\n});\n```\n\n## Examples\n\nSee [examples/](./examples/) directory for complete examples:\n\n- **[snapshot-example/](./examples/snapshot-example/)** - Complete snapshot testing with benchmarks\n- **[logger/basic-usage.ts](./examples/logger/basic-usage.ts)** - Auto-patch console\n- **[logger/manual-setup.ts](./examples/logger/manual-setup.ts)** - Custom logger instance\n- **[logger/file-logging.ts](./examples/logger/file-logging.ts)** - Log to file\n- **[logger/mcp-server-example.ts](./examples/logger/mcp-server-example.ts)** - Real MCP server\n\nRun examples:\n\n```bash\nnpm install -g tsx\ntsx examples/logger/basic-usage.ts\n```\n\n## API Reference\n\n### MCPTestClient\n\n```typescript\nclass MCPTestClient {\n  constructor(options: {\n    command: string;\n    args?: string[];\n    env?: Record\u003cstring, string\u003e;\n    timeout?: number;\n  });\n\n  // Connection management\n  connect(): Promise\u003cvoid\u003e;\n  disconnect(): Promise\u003cvoid\u003e;\n\n  // Server info\n  getServerInfo(): ServerInfo;\n  getServerCapabilities(): ServerCapabilities;\n\n  // Tools\n  listTools(): Promise\u003cTool[]\u003e;\n  callTool(name: string, args: unknown): Promise\u003cCallToolResult\u003e;\n  expectToolCallSuccess(name: string, args: unknown): Promise\u003cstring\u003e;\n  expectToolCallError(name: string, args: unknown): Promise\u003cError\u003e;\n\n  // Resources\n  listResources(): Promise\u003cResource[]\u003e;\n  readResource(uri: string): Promise\u003cReadResourceResult\u003e;\n\n  // Prompts\n  listPrompts(): Promise\u003cPrompt[]\u003e;\n  getPrompt(name: string, args?: unknown): Promise\u003cGetPromptResult\u003e;\n}\n```\n\n### Logger\n\n```typescript\ninterface LoggerOptions {\n  enabled?: boolean;        // Enable/disable logger (default: true)\n  timestamps?: boolean;     // Show timestamps (default: true)\n  colors?: boolean;         // Force colors on/off (default: auto-detect)\n  level?: 'debug'|'info'|'warn'|'error'; // Min level (default: 'debug')\n  stream?: WritableStream;  // Custom output (default: process.stderr)\n  logFile?: string;         // Optional file output\n}\n\nfunction createLogger(options?: LoggerOptions): DebugLogger;\nfunction patchConsole(options?: LoggerOptions): void;\nfunction unpatchConsole(): void;\n```\n\n### Matchers\n\n```typescript\n// Installation\nfunction installMCPMatchers(): void;\n\n// Tool matchers\nexpect(client).toHaveTool(name: string);\nexpect(tool).toHaveToolProperty(property: string, value?: any);\nexpect(tool).toMatchToolSchema(schema: object);\nexpect(result).toReturnToolResult(expected: any);\nexpect(promise).toThrowToolError();\n\n// Resource matchers\nexpect(client).toHaveResource(uri: string);\n\n// Prompt matchers\nexpect(client).toHavePrompt(name: string);\n\n// Snapshot matchers\nexpect(data).toMatchMCPSnapshot(options?: { exclude?: string[] });\nexpect(result).toMatchToolResponseSnapshot(options?: { exclude?: string[] });\nexpect(tools).toMatchToolListSnapshot(options?: { exclude?: string[] });\nexpect(resources).toMatchResourceListSnapshot(options?: { exclude?: string[] });\nexpect(prompts).toMatchPromptListSnapshot(options?: { exclude?: string[] });\n```\n\n## Troubleshooting\n\n### Tests hanging or timing out?\n\nIncrease the timeout:\n\n```typescript\nconst client = new MCPTestClient({\n  command: 'node',\n  args: ['./server.js'],\n  timeout: 60000, // 60 seconds\n});\n```\n\n### Snapshots failing unexpectedly?\n\nCheck if you're excluding enough dynamic fields:\n\n```typescript\nexpect(result).toMatchToolResponseSnapshot({\n  exclude: [\n    'timestamp',\n    'requestId',\n    'files.*.modified',\n    'data.*.generatedAt',\n  ]\n});\n```\n\n### Logs not showing?\n\nCheck your log level:\n\n```typescript\ncreateLogger({ level: 'debug' }); // Show everything\n```\n\n### Colors not working?\n\nColors only work when stderr is a TTY. Force enable/disable:\n\n```typescript\ncreateLogger({ colors: true });  // Always color\ncreateLogger({ colors: false }); // Never color\n```\n\n### Client not connecting?\n\nVerify your server is using stdio transport and responding to initialize:\n\n```typescript\n// Server must respond to initialize request\nserver.setRequestHandler(InitializeRequestSchema, async (request) =\u003e {\n  return {\n    protocolVersion: '2024-11-05',\n    capabilities: { tools: {} },\n    serverInfo: { name: 'my-server', version: '1.0.0' },\n  };\n});\n```\n\n## Requirements\n\n- Node.js \u003e= 18.0.0\n- TypeScript \u003e= 5.0.0 (if using TypeScript)\n- Vitest \u003e= 1.0.0 (for testing features)\n\n## Contributing\n\nSee [CONTRIBUTING.md](./CONTRIBUTING.md) for development setup and guidelines.\n\n## License\n\nMIT © [gnana997](https://github.com/gnana997)\n\n## Related Projects\n\n- [@modelcontextprotocol/sdk](https://modelcontextprotocol.io) - Official MCP SDK\n- [Model Context Protocol](https://modelcontextprotocol.io) - Protocol specification\n- [node-stdio-jsonrpc](https://www.npmjs.com/package/node-stdio-jsonrpc) - JSON-RPC 2.0 over stdio\n\n---\n\n**Built with ❤️ for the MCP community**\n\nFound this useful? [Star it on GitHub](https://github.com/gnana997/mcp-dev-kit) ⭐\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgnana997%2Fmcp-dev-kit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgnana997%2Fmcp-dev-kit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgnana997%2Fmcp-dev-kit/lists"}