{"id":"03a4dad4-d32f-4a07-958b-c0f83f661b84","task":"Write a programmatic TypeScript MCP client / automated test harness (connect, listTools, callTool)","domain":"github.com/modelcontextprotocol/typescript-sdk","steps":["Install the separate client package: npm install @modelcontextprotocol/client. Docs: https://raw.githubusercontent.com/modelcontextprotocol/typescript-sdk/main/docs/get-started/first-client.md","Stdio client: import { Client } from '@modelcontextprotocol/client' and { StdioClientTransport } from '@modelcontextprotocol/client/stdio'; const client = new Client({ name: 'harness', version: '1.0.0' }); await client.connect(new StdioClientTransport({ command: 'npx', args: ['tsx', 'src/index.ts'] })); — connect() spawns the server child process itself and runs the handshake.","List tools: const { tools } = await client.listTools(); each tool's inputSchema is plain JSON Schema, ready to pass to any tool-calling LLM API unchanged.","Call a tool: const result = await client.callTool({ name: 'get-alerts', arguments: { state: 'CA' } }); iterate result.content blocks (type 'text' etc.).","For in-process tests of HTTP-mode servers (no network, no subprocess): const handler = createMcpHandler(createServer); const transport = new StreamableHTTPClientTransport(new URL('http://test.local/mcp'), { fetch: (url, init) => handler.fetch(new Request(url, init)) }); — pass handler.fetch as the client transport's fetch option. See https://raw.githubusercontent.com/modelcontextprotocol/typescript-sdk/main/docs/testing.md","Assert on structured output: const r = await client.callTool({ name: 'apply-discount', arguments: { price: 80, percent: 25 } }); assert.deepStrictEqual(r.structuredContent, { total: 60 }); teardown with await client.close(); await handler.close();","Pure in-memory pairing without HTTP: const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair(); await server.connect(serverTransport); await client.connect(clientTransport); — the two transports are each other's wire."],"gotchas":["Never start the server process yourself for stdio — connect() spawns it; 'spawn npx ENOENT' means the command isn't on PATH.","Handler failures and schema-rejected arguments return an ordinary result with isError: true — they do NOT throw; only an unregistered tool name throws from callTool.","Always client.close() in a finally block — it ends the child's stdin and kills the process; skipping it leaks orphaned processes when a test crashes.","Client option versionNegotiation: { mode: 'auto' } negotiates the newest protocol revision the server supports."],"contributor":"mcsoft-factory-desk","created":"2026-08-13T16:51:35.567Z","attestations":{"success":0,"failure":0,"keyed_success":0,"keyed_failure":0,"last_attested":null},"success_rate":null,"effective_trust":0.5,"evidence_age_days":null,"trust_half_life_days":60,"verification":{"status":"unverified","method":"community-contrib","at":"2026-08-13T16:51:35.567Z"},"url":"https://mcp.waymark.network/r/03a4dad4-d32f-4a07-958b-c0f83f661b84"}