Testing MCP Tools: Unit Tests, Integration Tests, and Mocking
MCP tools power AI agents. Bugs can cascade into bad agent decisions. This guide covers testing at every level.
Why Test MCP Tools?
- Agents depend on correct tool behavior — wrong output = wrong agent actions
- Schema changes break clients — test your
inputSchema
- Error handling matters — agents need clear error messages
- Regressions are invisible — without tests, you won't know
Level 1: Unit Testing Tool Handlers
Extract tool logic into testable functions:
<span class="hljs-comment">
<span class="hljs-keyword">export</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">addTodo</span>(<span class="hljs-params">todos, task, priority = <span class="hljs-string">"medium"</span></span>) {
<span class="hljs-keyword">const</span> todo = {
<span class="hljs-attr">id</span>: todos.<span class="hljs-property">length</span> + <span class="hljs-number">1</span>,
task,
priority,
<span class="hljs-attr">created</span>: <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>().<span class="hljs-title function_">toISOString</span>()
};
<span class="hljs-keyword">return</span> {
<span class="hljs-attr">todos</span>: [...todos, todo],
<span class="hljs-attr">message</span>: <span class="hljs-string">`✅ Added: "<span class="hljs-subst">${task}</span>"`</span>
};
}
<span class="hljs-keyword">export</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">listTodos</span>(<span class="hljs-params">todos, filter = <span class="hljs-string">"all"</span></span>) {
<span class="hljs-keyword">const</span> filtered = filter === <span class="hljs-string">"all"</span>
? todos
: todos.<span class="hljs-title function_">filter</span>(<span class="hljs-function"><span class="hljs-params">t</span> =></span> t.<span class="hljs-property">priority</span> === filter);
<span class="hljs-keyword">return</span> filtered.<span class="hljs-title function_">map</span>(<span class="hljs-function"><span class="hljs-params">t</span> =></span> <span class="hljs-string">`[<span class="hljs-subst">${t.priority.toUpperCase()}</span>] <span class="hljs-subst">${t.task}</span>`</span>);
}
Test with your preferred framework:
<span class="hljs-comment">
<span class="hljs-keyword">import</span> { describe, it, expect } <span class="hljs-keyword">from</span> <span class="hljs-string">"vitest"</span>;
<span class="hljs-keyword">import</span> { addTodo, listTodos } <span class="hljs-keyword">from</span> <span class="hljs-string">"./tools.js"</span>;
<span class="hljs-title function_">describe</span>(<span class="hljs-string">"addTodo"</span>, <span class="hljs-function">() =></span> {
<span class="hljs-title function_">it</span>(<span class="hljs-string">"adds a todo with default priority"</span>, <span class="hljs-function">() =></span> {
<span class="hljs-keyword">const</span> { todos, message } = <span class="hljs-title function_">addTodo</span>([], <span class="hljs-string">"Buy milk"</span>);
<span class="hljs-title function_">expect</span>(todos).<span class="hljs-title function_">toHaveLength</span>(<span class="hljs-number">1</span>);
<span class="hljs-title function_">expect</span>(todos[<span class="hljs-number">0</span>].<span class="hljs-property">task</span>).<span class="hljs-title function_">toBe</span>(<span class="hljs-string">"Buy milk"</span>);
<span class="hljs-title function_">expect</span>(todos[<span class="hljs-number">0</span>].<span class="hljs-property">priority</span>).<span class="hljs-title function_">toBe</span>(<span class="hljs-string">"medium"</span>);
<span class="hljs-title function_">expect</span>(message).<span class="hljs-title function_">toContain</span>(<span class="hljs-string">"Buy milk"</span>);
});
<span class="hljs-title function_">it</span>(<span class="hljs-string">"adds a todo with custom priority"</span>, <span class="hljs-function">() =></span> {
<span class="hljs-keyword">const</span> { todos } = <span class="hljs-title function_">addTodo</span>([], <span class="hljs-string">"Fix bug"</span>, <span class="hljs-string">"high"</span>);
<span class="hljs-title function_">expect</span>(todos[<span class="hljs-number">0</span>].<span class="hljs-property">priority</span>).<span class="hljs-title function_">toBe</span>(<span class="hljs-string">"high"</span>);
});
});
<span class="hljs-title function_">describe</span>(<span class="hljs-string">"listTodos"</span>, <span class="hljs-function">() =></span> {
<span class="hljs-title function_">it</span>(<span class="hljs-string">"filters by priority"</span>, <span class="hljs-function">() =></span> {
<span class="hljs-keyword">const</span> todos = [
{ <span class="hljs-attr">task</span>: <span class="hljs-string">"A"</span>, <span class="hljs-attr">priority</span>: <span class="hljs-string">"high"</span> },
{ <span class="hljs-attr">task</span>: <span class="hljs-string">"B"</span>, <span class="hljs-attr">priority</span>: <span class="hljs-string">"low"</span> }
];
<span class="hljs-keyword">const</span> result = <span class="hljs-title function_">listTodos</span>(todos, <span class="hljs-string">"high"</span>);
<span class="hljs-title function_">expect</span>(result).<span class="hljs-title function_">toHaveLength</span>(<span class="hljs-number">1</span>);
<span class="hljs-title function_">expect</span>(result[<span class="hljs-number">0</span>]).<span class="hljs-title function_">toContain</span>(<span class="hljs-string">"A"</span>);
});
<span class="hljs-title function_">it</span>(<span class="hljs-string">"returns empty for no matches"</span>, <span class="hljs-function">() =></span> {
<span class="hljs-title function_">expect</span>(<span class="hljs-title function_">listTodos</span>([], <span class="hljs-string">"high"</span>)).<span class="hljs-title function_">toEqual</span>([]);
});
});
Level 2: Testing the MCP Protocol
Test the request/response cycle:
<span class="hljs-comment">
<span class="hljs-keyword">import</span> { describe, it, expect } <span class="hljs-keyword">from</span> <span class="hljs-string">"vitest"</span>;
<span class="hljs-comment">// Simulate the tools/list handler</span>
<span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">simulateListTools</span>(<span class="hljs-params">server</span>) {
<span class="hljs-comment">// You'd need to expose the handler for testing</span>
<span class="hljs-comment">// or use the MCP SDK's test utilities</span>
}
<span class="hljs-title function_">describe</span>(<span class="hljs-string">"tools/list"</span>, <span class="hljs-function">() =></span> {
<span class="hljs-title function_">it</span>(<span class="hljs-string">"returns correct tool schema"</span>, <span class="hljs-title function_">async</span> () => {
<span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> server.<span class="hljs-title function_">handleRequest</span>({
<span class="hljs-attr">method</span>: <span class="hljs-string">"tools/list"</span>,
<span class="hljs-attr">params</span>: {}
});
<span class="hljs-title function_">expect</span>(response.<span class="hljs-property">tools</span>).<span class="hljs-title function_">toHaveLength</span>(<span class="hljs-number">2</span>);
<span class="hljs-title function_">expect</span>(response.<span class="hljs-property">tools</span>[<span class="hljs-number">0</span>].<span class="hljs-property">name</span>).<span class="hljs-title function_">toBe</span>(<span class="hljs-string">"add_todo"</span>);
<span class="hljs-title function_">expect</span>(response.<span class="hljs-property">tools</span>[<span class="hljs-number">0</span>].<span class="hljs-property">inputSchema</span>.<span class="hljs-property">required</span>).<span class="hljs-title function_">toContain</span>(<span class="hljs-string">"task"</span>);
});
});
<span class="hljs-title function_">describe</span>(<span class="hljs-string">"tools/call"</span>, <span class="hljs-function">() =></span> {
<span class="hljs-title function_">it</span>(<span class="hljs-string">"add_todo creates a todo"</span>, <span class="hljs-title function_">async</span> () => {
<span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> server.<span class="hljs-title function_">handleRequest</span>({
<span class="hljs-attr">method</span>: <span class="hljs-string">"tools/call"</span>,
<span class="hljs-attr">params</span>: {
<span class="hljs-attr">name</span>: <span class="hljs-string">"add_todo"</span>,
<span class="hljs-attr">arguments</span>: { <span class="hljs-attr">task</span>: <span class="hljs-string">"Test task"</span>, <span class="hljs-attr">priority</span>: <span class="hljs-string">"high"</span> }
}
});
<span class="hljs-title function_">expect</span>(response.<span class="hljs-property">content</span>[<span class="hljs-number">0</span>].<span class="hljs-property">text</span>).<span class="hljs-title function_">toContain</span>(<span class="hljs-string">"Test task"</span>);
});
<span class="hljs-title function_">it</span>(<span class="hljs-string">"unknown tool throws error"</span>, <span class="hljs-title function_">async</span> () => {
<span class="hljs-keyword">await</span> <span class="hljs-title function_">expect</span>(
server.<span class="hljs-title function_">handleRequest</span>({
<span class="hljs-attr">method</span>: <span class="hljs-string">"tools/call"</span>,
<span class="hljs-attr">params</span>: { <span class="hljs-attr">name</span>: <span class="hljs-string">"nonexistent"</span>, <span class="hljs-attr">arguments</span>: {} }
})
).<span class="hljs-property">rejects</span>.<span class="hljs-title function_">toThrow</span>(<span class="hljs-string">"Unknown tool"</span>);
});
});
Level 3: Integration Testing with a Real Client
Use the MCP SDK's Client class:
<span class="hljs-keyword">import</span> { <span class="hljs-title class_">Client</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">"@modelcontextprotocol/sdk/client/index.js"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title class_">StdioClientTransport</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">"@modelcontextprotocol/sdk/client/stdio.js"</span>;
<span class="hljs-keyword">const</span> transport = <span class="hljs-keyword">new</span> <span class="hljs-title class_">StdioClientTransport</span>({
<span class="hljs-attr">command</span>: <span class="hljs-string">"node"</span>,
<span class="hljs-attr">args</span>: [<span class="hljs-string">"./index.js"</span>],
<span class="hljs-attr">env</span>: { <span class="hljs-attr">OPENWEATHER_API_KEY</span>: <span class="hljs-string">"test_key"</span> }
});
<span class="hljs-keyword">const</span> client = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Client</span>({ <span class="hljs-attr">name</span>: <span class="hljs-string">"test"</span>, <span class="hljs-attr">version</span>: <span class="hljs-string">"1.0.0"</span> }, {
<span class="hljs-attr">capabilities</span>: {}
});
<span class="hljs-keyword">await</span> client.<span class="hljs-title function_">connect</span>(transport);
<span class="hljs-comment">// List tools</span>
<span class="hljs-keyword">const</span> { tools } = <span class="hljs-keyword">await</span> client.<span class="hljs-title function_">listTools</span>();
<span class="hljs-title function_">expect</span>(tools.<span class="hljs-title function_">map</span>(<span class="hljs-function"><span class="hljs-params">t</span> =></span> t.<span class="hljs-property">name</span>)).<span class="hljs-title function_">toContain</span>(<span class="hljs-string">"get_current_weather"</span>);
<span class="hljs-comment">// Call a tool</span>
<span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> client.<span class="hljs-title function_">callTool</span>({
<span class="hljs-attr">name</span>: <span class="hljs-string">"get_current_weather"</span>,
<span class="hljs-attr">arguments</span>: { <span class="hljs-attr">city</span>: <span class="hljs-string">"London"</span> }
});
<span class="hljs-title function_">expect</span>(result.<span class="hljs-property">content</span>[<span class="hljs-number">0</span>].<span class="hljs-property">text</span>).<span class="hljs-title function_">toContain</span>(<span class="hljs-string">"London"</span>);
Level 4: Schema Validation
Test that your inputSchema is valid JSON Schema:
<span class="hljs-keyword">import</span> <span class="hljs-title class_">Ajv</span> <span class="hljs-keyword">from</span> <span class="hljs-string">"ajv"</span>;
<span class="hljs-keyword">const</span> ajv = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Ajv</span>();
<span class="hljs-title function_">it</span>(<span class="hljs-string">"inputSchema is valid JSON Schema"</span>, <span class="hljs-function">() =></span> {
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> tool <span class="hljs-keyword">of</span> <span class="hljs-variable constant_">TOOLS</span>) {
<span class="hljs-keyword">const</span> valid = ajv.<span class="hljs-title function_">validateSchema</span>(tool.<span class="hljs-property">inputSchema</span>);
<span class="hljs-title function_">expect</span>(valid).<span class="hljs-title function_">toBe</span>(<span class="hljs-literal">true</span>);
}
});
Testing Best Practices
- Extract pure logic — tool functions should be testable without MCP
- Mock external APIs — use
nock or msw for HTTP calls
- Test error paths — invalid input, API failures, timeouts
- Validate schemas — JSON Schema validation catches regressions
- Run in CI — GitHub Actions, every push
Sample CI Config
<span class="hljs-comment"># .github/workflows/test.yml</span>
<span class="hljs-attr">name:</span> <span class="hljs-string">Test</span>
<span class="hljs-attr">on:</span> [<span class="hljs-string">push</span>, <span class="hljs-string">pull_request</span>]
<span class="hljs-attr">jobs:</span>
<span class="hljs-attr">test:</span>
<span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
<span class="hljs-attr">steps:</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v4</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/setup-node@v4</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">ci</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">test</span>
Tested tools are trusted tools. Your users (and their AI agents) will thank you.
#MCP #Tutorial #Testing #Quality #mcpm
Testing MCP Tools: Unit Tests, Integration Tests, and Mocking
MCP tools power AI agents. Bugs can cascade into bad agent decisions. This guide covers testing at every level.
Why Test MCP Tools?
inputSchemaLevel 1: Unit Testing Tool Handlers
Extract tool logic into testable functions:
<span class="hljs-comment">// tools.js — pure functions, easy to test</span> <span class="hljs-keyword">export</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">addTodo</span>(<span class="hljs-params">todos, task, priority = <span class="hljs-string">"medium"</span></span>) { <span class="hljs-keyword">const</span> todo = { <span class="hljs-attr">id</span>: todos.<span class="hljs-property">length</span> + <span class="hljs-number">1</span>, task, priority, <span class="hljs-attr">created</span>: <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>().<span class="hljs-title function_">toISOString</span>() }; <span class="hljs-keyword">return</span> { <span class="hljs-attr">todos</span>: [...todos, todo], <span class="hljs-attr">message</span>: <span class="hljs-string">`✅ Added: "<span class="hljs-subst">${task}</span>"`</span> }; } <span class="hljs-keyword">export</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">listTodos</span>(<span class="hljs-params">todos, filter = <span class="hljs-string">"all"</span></span>) { <span class="hljs-keyword">const</span> filtered = filter === <span class="hljs-string">"all"</span> ? todos : todos.<span class="hljs-title function_">filter</span>(<span class="hljs-function"><span class="hljs-params">t</span> =></span> t.<span class="hljs-property">priority</span> === filter); <span class="hljs-keyword">return</span> filtered.<span class="hljs-title function_">map</span>(<span class="hljs-function"><span class="hljs-params">t</span> =></span> <span class="hljs-string">`[<span class="hljs-subst">${t.priority.toUpperCase()}</span>] <span class="hljs-subst">${t.task}</span>`</span>); }Test with your preferred framework:
<span class="hljs-comment">// tools.test.js</span> <span class="hljs-keyword">import</span> { describe, it, expect } <span class="hljs-keyword">from</span> <span class="hljs-string">"vitest"</span>; <span class="hljs-keyword">import</span> { addTodo, listTodos } <span class="hljs-keyword">from</span> <span class="hljs-string">"./tools.js"</span>; <span class="hljs-title function_">describe</span>(<span class="hljs-string">"addTodo"</span>, <span class="hljs-function">() =></span> { <span class="hljs-title function_">it</span>(<span class="hljs-string">"adds a todo with default priority"</span>, <span class="hljs-function">() =></span> { <span class="hljs-keyword">const</span> { todos, message } = <span class="hljs-title function_">addTodo</span>([], <span class="hljs-string">"Buy milk"</span>); <span class="hljs-title function_">expect</span>(todos).<span class="hljs-title function_">toHaveLength</span>(<span class="hljs-number">1</span>); <span class="hljs-title function_">expect</span>(todos[<span class="hljs-number">0</span>].<span class="hljs-property">task</span>).<span class="hljs-title function_">toBe</span>(<span class="hljs-string">"Buy milk"</span>); <span class="hljs-title function_">expect</span>(todos[<span class="hljs-number">0</span>].<span class="hljs-property">priority</span>).<span class="hljs-title function_">toBe</span>(<span class="hljs-string">"medium"</span>); <span class="hljs-title function_">expect</span>(message).<span class="hljs-title function_">toContain</span>(<span class="hljs-string">"Buy milk"</span>); }); <span class="hljs-title function_">it</span>(<span class="hljs-string">"adds a todo with custom priority"</span>, <span class="hljs-function">() =></span> { <span class="hljs-keyword">const</span> { todos } = <span class="hljs-title function_">addTodo</span>([], <span class="hljs-string">"Fix bug"</span>, <span class="hljs-string">"high"</span>); <span class="hljs-title function_">expect</span>(todos[<span class="hljs-number">0</span>].<span class="hljs-property">priority</span>).<span class="hljs-title function_">toBe</span>(<span class="hljs-string">"high"</span>); }); }); <span class="hljs-title function_">describe</span>(<span class="hljs-string">"listTodos"</span>, <span class="hljs-function">() =></span> { <span class="hljs-title function_">it</span>(<span class="hljs-string">"filters by priority"</span>, <span class="hljs-function">() =></span> { <span class="hljs-keyword">const</span> todos = [ { <span class="hljs-attr">task</span>: <span class="hljs-string">"A"</span>, <span class="hljs-attr">priority</span>: <span class="hljs-string">"high"</span> }, { <span class="hljs-attr">task</span>: <span class="hljs-string">"B"</span>, <span class="hljs-attr">priority</span>: <span class="hljs-string">"low"</span> } ]; <span class="hljs-keyword">const</span> result = <span class="hljs-title function_">listTodos</span>(todos, <span class="hljs-string">"high"</span>); <span class="hljs-title function_">expect</span>(result).<span class="hljs-title function_">toHaveLength</span>(<span class="hljs-number">1</span>); <span class="hljs-title function_">expect</span>(result[<span class="hljs-number">0</span>]).<span class="hljs-title function_">toContain</span>(<span class="hljs-string">"A"</span>); }); <span class="hljs-title function_">it</span>(<span class="hljs-string">"returns empty for no matches"</span>, <span class="hljs-function">() =></span> { <span class="hljs-title function_">expect</span>(<span class="hljs-title function_">listTodos</span>([], <span class="hljs-string">"high"</span>)).<span class="hljs-title function_">toEqual</span>([]); }); });Level 2: Testing the MCP Protocol
Test the request/response cycle:
<span class="hljs-comment">// server.test.js</span> <span class="hljs-keyword">import</span> { describe, it, expect } <span class="hljs-keyword">from</span> <span class="hljs-string">"vitest"</span>; <span class="hljs-comment">// Simulate the tools/list handler</span> <span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">simulateListTools</span>(<span class="hljs-params">server</span>) { <span class="hljs-comment">// You'd need to expose the handler for testing</span> <span class="hljs-comment">// or use the MCP SDK's test utilities</span> } <span class="hljs-title function_">describe</span>(<span class="hljs-string">"tools/list"</span>, <span class="hljs-function">() =></span> { <span class="hljs-title function_">it</span>(<span class="hljs-string">"returns correct tool schema"</span>, <span class="hljs-title function_">async</span> () => { <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> server.<span class="hljs-title function_">handleRequest</span>({ <span class="hljs-attr">method</span>: <span class="hljs-string">"tools/list"</span>, <span class="hljs-attr">params</span>: {} }); <span class="hljs-title function_">expect</span>(response.<span class="hljs-property">tools</span>).<span class="hljs-title function_">toHaveLength</span>(<span class="hljs-number">2</span>); <span class="hljs-title function_">expect</span>(response.<span class="hljs-property">tools</span>[<span class="hljs-number">0</span>].<span class="hljs-property">name</span>).<span class="hljs-title function_">toBe</span>(<span class="hljs-string">"add_todo"</span>); <span class="hljs-title function_">expect</span>(response.<span class="hljs-property">tools</span>[<span class="hljs-number">0</span>].<span class="hljs-property">inputSchema</span>.<span class="hljs-property">required</span>).<span class="hljs-title function_">toContain</span>(<span class="hljs-string">"task"</span>); }); }); <span class="hljs-title function_">describe</span>(<span class="hljs-string">"tools/call"</span>, <span class="hljs-function">() =></span> { <span class="hljs-title function_">it</span>(<span class="hljs-string">"add_todo creates a todo"</span>, <span class="hljs-title function_">async</span> () => { <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> server.<span class="hljs-title function_">handleRequest</span>({ <span class="hljs-attr">method</span>: <span class="hljs-string">"tools/call"</span>, <span class="hljs-attr">params</span>: { <span class="hljs-attr">name</span>: <span class="hljs-string">"add_todo"</span>, <span class="hljs-attr">arguments</span>: { <span class="hljs-attr">task</span>: <span class="hljs-string">"Test task"</span>, <span class="hljs-attr">priority</span>: <span class="hljs-string">"high"</span> } } }); <span class="hljs-title function_">expect</span>(response.<span class="hljs-property">content</span>[<span class="hljs-number">0</span>].<span class="hljs-property">text</span>).<span class="hljs-title function_">toContain</span>(<span class="hljs-string">"Test task"</span>); }); <span class="hljs-title function_">it</span>(<span class="hljs-string">"unknown tool throws error"</span>, <span class="hljs-title function_">async</span> () => { <span class="hljs-keyword">await</span> <span class="hljs-title function_">expect</span>( server.<span class="hljs-title function_">handleRequest</span>({ <span class="hljs-attr">method</span>: <span class="hljs-string">"tools/call"</span>, <span class="hljs-attr">params</span>: { <span class="hljs-attr">name</span>: <span class="hljs-string">"nonexistent"</span>, <span class="hljs-attr">arguments</span>: {} } }) ).<span class="hljs-property">rejects</span>.<span class="hljs-title function_">toThrow</span>(<span class="hljs-string">"Unknown tool"</span>); }); });Level 3: Integration Testing with a Real Client
Use the MCP SDK's
Clientclass:<span class="hljs-keyword">import</span> { <span class="hljs-title class_">Client</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">"@modelcontextprotocol/sdk/client/index.js"</span>; <span class="hljs-keyword">import</span> { <span class="hljs-title class_">StdioClientTransport</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">"@modelcontextprotocol/sdk/client/stdio.js"</span>; <span class="hljs-keyword">const</span> transport = <span class="hljs-keyword">new</span> <span class="hljs-title class_">StdioClientTransport</span>({ <span class="hljs-attr">command</span>: <span class="hljs-string">"node"</span>, <span class="hljs-attr">args</span>: [<span class="hljs-string">"./index.js"</span>], <span class="hljs-attr">env</span>: { <span class="hljs-attr">OPENWEATHER_API_KEY</span>: <span class="hljs-string">"test_key"</span> } }); <span class="hljs-keyword">const</span> client = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Client</span>({ <span class="hljs-attr">name</span>: <span class="hljs-string">"test"</span>, <span class="hljs-attr">version</span>: <span class="hljs-string">"1.0.0"</span> }, { <span class="hljs-attr">capabilities</span>: {} }); <span class="hljs-keyword">await</span> client.<span class="hljs-title function_">connect</span>(transport); <span class="hljs-comment">// List tools</span> <span class="hljs-keyword">const</span> { tools } = <span class="hljs-keyword">await</span> client.<span class="hljs-title function_">listTools</span>(); <span class="hljs-title function_">expect</span>(tools.<span class="hljs-title function_">map</span>(<span class="hljs-function"><span class="hljs-params">t</span> =></span> t.<span class="hljs-property">name</span>)).<span class="hljs-title function_">toContain</span>(<span class="hljs-string">"get_current_weather"</span>); <span class="hljs-comment">// Call a tool</span> <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> client.<span class="hljs-title function_">callTool</span>({ <span class="hljs-attr">name</span>: <span class="hljs-string">"get_current_weather"</span>, <span class="hljs-attr">arguments</span>: { <span class="hljs-attr">city</span>: <span class="hljs-string">"London"</span> } }); <span class="hljs-title function_">expect</span>(result.<span class="hljs-property">content</span>[<span class="hljs-number">0</span>].<span class="hljs-property">text</span>).<span class="hljs-title function_">toContain</span>(<span class="hljs-string">"London"</span>);Level 4: Schema Validation
Test that your
inputSchemais valid JSON Schema:<span class="hljs-keyword">import</span> <span class="hljs-title class_">Ajv</span> <span class="hljs-keyword">from</span> <span class="hljs-string">"ajv"</span>; <span class="hljs-keyword">const</span> ajv = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Ajv</span>(); <span class="hljs-title function_">it</span>(<span class="hljs-string">"inputSchema is valid JSON Schema"</span>, <span class="hljs-function">() =></span> { <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> tool <span class="hljs-keyword">of</span> <span class="hljs-variable constant_">TOOLS</span>) { <span class="hljs-keyword">const</span> valid = ajv.<span class="hljs-title function_">validateSchema</span>(tool.<span class="hljs-property">inputSchema</span>); <span class="hljs-title function_">expect</span>(valid).<span class="hljs-title function_">toBe</span>(<span class="hljs-literal">true</span>); } });Testing Best Practices
nockormswfor HTTP callsSample CI Config
<span class="hljs-comment"># .github/workflows/test.yml</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Test</span> <span class="hljs-attr">on:</span> [<span class="hljs-string">push</span>, <span class="hljs-string">pull_request</span>] <span class="hljs-attr">jobs:</span> <span class="hljs-attr">test:</span> <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span> <span class="hljs-attr">steps:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v4</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/setup-node@v4</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">ci</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">test</span>Tested tools are trusted tools. Your users (and their AI agents) will thank you.
#MCP #Tutorial #Testing #Quality #mcpm