Architectural Invariant Design: Model Context Protocol (MCP) TypeScript SDK Standard
The Model Context Protocol (MCP) establishes an open JSON-RPC 2.0 communication standard enabling AI agents to query external tools, resources, and prompts across IDE hosts like Cursor, Claude Code, and Windsurf. In stdio transport architectures, stdout is strictly reserved for JSON-RPC frame serialization. Emitting application telemetry or console.log statements directly to stdout contaminates the transport pipe, triggering unrecoverable client framing desynchronization.
Hazard Analysis: What Breaks Without This Rule
The following failure modes have been cataloged from hundreds of hours of production code reviews where AI assistants produced non-compliant implementations:
| Failure Mode | AI Symptom (The Anti-Pattern) | Severity | Production Impact & Remedy |
|---|---|---|---|
| Stdout JSON-RPC Frame Contamination | AI places console.log('Starting server...') or console.log(data) in tool implementations | Critical | Immediately corrupts JSON-RPC packet framing, crashing Claude Desktop or Cursor MCP background daemons. Strictly redirect all diagnostic logging to process.stderr.write or console.error; keep stdout 100% reserved for protocol frames. |
| Untyped Tool Arguments & Uncaught Rejections | AI declares tool handlers with raw any types and throws standard Error without catching | High | Causes unhandled promise rejections that kill the server process instead of returning structured recoverable errors. Validate all arguments using strict Zod schemas and catch exceptions to return { content: [{ type: 'text', text: ... }], isError: true }. |
Step-by-Step Production Directives
Each rule constraint below is injected into the AI agent's system prompt to establish deterministic boundaries:
NEVER emit debug output to stdout in stdio mode
NEVER emit debug output to stdout in stdio mode; route all diagnostic telemetry to process.stderr.write or console.error to avoid JSON-RPC framing corruption.
Define all MCP tool parameters with explicit Zod schemas using z.object({...}) and pass them to zodToJsonSchema or SDK schema converters.
Define all MCP tool parameters with explicit Zod schemas using z.object({...}) and pass them to zodToJsonSchema or SDK schema converters.
Isolate tool execution inside try/catch blocks and return structured error objects with isError
Isolate tool execution inside try/catch blocks and return structured error objects with isError: true instead of throwing unhandled exceptions.
Gracefully handle SIGINT and SIGTERM OS signals to close active transports and release network and database connection pools.
Gracefully handle SIGINT and SIGTERM OS signals to close active transports and release network and database connection pools.
Frequently Asked Technical Questions
Common questions developers face when configuring AI coding assistants for this stack:
Q:Why does my MCP server disconnect immediately in Claude Desktop or Cursor?
The most frequent cause is debug logging to stdout (e.g. console.log). Because stdio transport uses standard input/output for JSON-RPC messages, any extraneous text breaks packet framing. Route all logs to process.stderr.write instead.
Q:How do I return errors in MCP tools without crashing the daemon?
Never let exceptions bubble out of the callTool request handler. Catch errors and return an MCP response envelope containing { content: [{ type: 'text', text: err.message }], isError: true }. This allows the AI agent to read the error message and self-correct.