Your compact reference for all 10 foundational MCP concepts. Keep this open while you build.
Tools (actions) · Resources (data) · Prompts (templates)JSON-RPC 2.0 over any transport{ jsonrpc, id, method, params }{ jsonrpc, id, result } or { …, error }id field (fire-and-forget)initialize → initialized → [use] → closenew McpServer(info, { capabilities })server.tool(name, desc, schema, handler)StdioServerTransport → server.connect(transport)z.object({ x: z.string().describe('…') })verb_noun — e.g. search_repos, get_file_contenttry/catch → return isError: true on failurereadOnlyHint · destructiveHint · idempotentHintnextCursor in responseserver.resource(name, uri, {mimeType}, handler)new ResourceTemplate('github://repo/{owner}/{repo}', …){+path} allows slashes in variable (RFC 6570 level 2)text field for UTF-8 · blob field for base64 binaryserver.prompt(name, desc, argSchema, handler){ messages: PromptMessage[] }server.createMessage() → calls LLM via hostEmbeddedResource: { type:'resource', resource:{uri,…} }McpServer instances for WS/SSEconsole.error ONLY for logging (stdout = JSON-RPC wire)capabilities.sampling = {} required for createMessage()stdio: 1:1, no network, Claude Desktop, console.error onlyGET /sse (stream) + POST /message (client→server)proxy_buffering off + proxy_read_timeout 86400snew Client(info, {capabilities}) → client.connect(transport)StdioClientTransport: spawns server as child processSSEClientTransport: connects to remote HTTP+SSE serverresult.isError = domain failure · McpError = protocol failureisError:true · Protocol errors → throw McpErrorInternalError ✓ · InvalidParams ✗ · RequestTimeout ✓AbortController + clearTimeout in finally block| Code | Name | Notes | Retry? |
|---|---|---|---|
| -32700 | ParseError | Malformed JSON | No |
| -32600 | InvalidRequest | Bad JSON-RPC structure | No |
| -32601 | MethodNotFound | Unknown method | No |
| -32602 | InvalidParams | Wrong params — won't fix itself | Never |
| -32603 | InternalError | Server-side crash | ✓ Yes |
| -32001 | RequestTimeout | Took too long | ✓ Yes |
| -32002 | ResourceNotFound | URI not found | No |
// Server import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js' import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js' import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js' import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js' // Client import { Client } from '@modelcontextprotocol/sdk/client/index.js' import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js' import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js' // Types import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js' // Validation import { z } from 'zod'
console.log in stdio servers — it corrupts the JSON-RPC stream. Use console.error.isError:true. Protocol errors → throw McpError. Never mix the two..describe() — Claude reads these descriptions to understand how to call your tools.{+path} not {path} for URI templates that need to contain forward slashes.client.close() in a finally block — never leave connections dangling.getServerCapabilities() first before assuming a feature is available.sampling:{} capability in your server options to enable server-to-AI sampling calls.McpServer instances = isolated, safe state. Never share a single instance across WS/SSE connections.You've nailed the foundations. Level 2 takes you into advanced territory — real APIs, auth flows, and production-grade patterns.