MCP Deep Dive: From Fundamentals to Hands-On TypeScript
Model Context Protocol (MCP) is Anthropic's "USB-C interface for AI applications." This article explains the host/client/server model, resources/tools/prompts, and JSON-RPC, then builds a TypeScript MCP Server for Claude Code from scratch.
Key Points
- Topic Focus: AI Agent、MCP
- Suitable for: indie hackers, full-stack engineers, AI application developers
- Core value: quickly understand background, practical paths, common pitfalls, and engineering advice
If you have built AI applications, you have probably run into this problem: connect the model to GitHub today, a database tomorrow, and an internal enterprise API the day after. Every new tool requires its own adapter, authentication, and error handling, plus some way to fit its schema into the context. Once the tool count grows, the integration code becomes longer than the business logic.
Model Context Protocol (MCP) was created to solve precisely this problem. It standardizes how "models ↔ external capabilities" connect: a capability provider implements an MCP Server, and any MCP Client (Claude Code, Cursor, Continue, Cline...) can invoke it on a plug-and-play basis. In one sentence: MCP is the USB-C interface for AI applications.
1. MCP's Triangular Model
The MCP specification defines three roles:
- Host: The user-facing program that hosts the model, such as Claude Code or Cursor IDE.
- Client: The component inside the Host that communicates with a Server. One Host can connect to multiple Servers.
- Server: An independent process that exposes specific capabilities. It can be local (stdio) or remote (HTTP/SSE).
The key is decoupling: capability providers only need to consider "how to expose my data and operations under the MCP specification," while Hosts only need to consider "how to discover and invoke these capabilities." A uniform protocol connects the two. This follows the same idea as LSP (Language Server Protocol): LSP decouples editors from language analysis, while MCP decouples models from external capabilities.
2. Three Capability Types: Resources / Tools / Prompts
A Server can expose three types of capabilities:
| Type | Semantics | Triggered by | Typical examples |
|---|---|---|---|
| Resources | Read-only data sources (similar to GET) | Host/user explicitly selects | Log files, database table schemas, documentation |
| Tools | Executable operations (similar to POST) | The model decides to invoke them | Send emails, create issues, run SQL queries |
| Prompts | Reusable prompt templates | User selects from a menu | /summarize-pr, /explain-error |
The most commonly used and most agentic capability is Tools. During reasoning, the model decides whether to invoke a tool and which arguments to pass. The Server executes it and feeds the result back to the model. This is an engineering-grade evolution of function calling.
3. Communication: JSON-RPC 2.0
MCP uses JSON-RPC 2.0 as its messaging layer. It currently supports two transports:
- stdio: The Server runs as a local child process, and the Client sends and receives messages through standard input and output. This is suitable for local tools, with zero network overhead and natural isolation.
- HTTP + SSE (or Streamable HTTP): The Server runs as a remote service, making it suitable for sharing and multiple clients.
A typical exchange (initialize → list tools → invoke):
// Client → Server: Initialization handshake
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"demo","version":"0.1"}}}
// Server → Client: Declare capabilities
{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2025-06-18","capabilities":{"tools":{}},"serverInfo":{"name":"time-server","version":"0.1"}}}
// Client → Server: List tools
{"jsonrpc":"2.0","id":2,"method":"tools/list"}
// Client → Server: Call a tool
{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"now","arguments":{"tz":"Asia/Shanghai"}}}
4. Hands-On TypeScript: A Time/Timezone MCP Server
With the official SDK (@modelcontextprotocol/sdk), you can get a Server running in just a few dozen lines. Below is a Server that Claude Code can invoke directly:
// server.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({ name: "time-server", version: "0.1.0" });
// Register a tool that returns the current time for a timezone
server.tool(
"now",
"Return the current time for an IANA timezone, e.g. Asia/Shanghai",
{ tz: z.string().default("UTC").describe("IANA timezone") },
async ({ tz }) => {
try {
const now = new Date().toLocaleString("sv-SE", { timeZone: tz, hour12: false });
return { content: [{ type: "text", text: `${tz}: ${now}` }] };
} catch {
return { content: [{ type: "text", text: `invalid timezone: ${tz}` }], isError: true };
}
},
);
await server.connect(new StdioServerTransport());
Run it:
npx tsx server.ts
Then register it with the client. For Claude Code, use ~/.claude.json or the project's .mcp.json:
{
"mcpServers": {
"time": { "command": "npx", "args": ["tsx", "/abs/path/server.ts"] }
}
}
After restarting the client, ask "What time is it in Tokyo?" The model will automatically invoke now(tz:"Asia/Tokyo") and answer with the result.
5. Engineering Considerations
- A tool's description is part of the prompt. The model relies entirely on
nameanddescriptionto decide whether to invoke it. Descriptions must be precise, include examples, and clearly define boundaries. Vague descriptions are the leading reason Agents go off track. - Constrain arguments with schemas. A Zod schema both validates arguments and automatically generates the model-visible JSON Schema, making the implementation simpler and safer.
- Errors must be recoverable. Return
isError: truewith a readable explanation so the model can retry with different arguments or change its approach. Do not simply throw an exception and terminate the operation. - Remote Servers must use authentication. In Streamable HTTP mode, protect
tools/callwith token / OAuth permissions and apply per-client rate limits. The Server is executing real operations. - Use notifications for progress and logs. Report long-running task progress through
notifications/progress, and write debugging logs to theloggingchannel. Do not contaminate the result channel.
6. What MCP Changes
Before MCP, "adding a capability to an Agent" meant N × M integration hell (N models × M tools). MCP turns this into N + M: tool providers implement a Server once, and every compatible Client can use it. This is why Cursor, Cline, Continue, Zed, Replit, and others adopted it widely within six months.
For developers, MCP also creates a new opportunity: package your product/data as an MCP Server, and it can enter the capability pool of every mainstream AI coding tool directly, without waiting for each tool to integrate with you individually.
At its core, MCP is not a new algorithm but a protocol contract that enables the AI application ecosystem to scale. Once you understand the triangular model, the three capability types, and JSON-RPC, you can build production-ready Servers. In the next article, we will use MCP to build a more complete knowledge-base retrieval Server and bring RAG into Claude Code.
Related
Related Content
Article
From Copilot to Codex: AI Coding Tools Enter the Agent Era
Technical trends, engineering practices, and indie-developer perspectives around AI, AI Agents, and Vibe Coding.
Article
Why Does MCP Matter? Understanding the AI Tool Protocol
Technical trends, engineering practices, and an independent developer perspective on MCP and AI Agents.
Article
Core Architecture of AI Agents: Planning, Memory, Tools, and Execution
Technical trends, engineering practices, and independent developer perspectives around AI Agents and large language models.
Article
How Should RAG and Agents Work Together?
An independent developer's perspective on technical trends and engineering practices involving RAG and AI agents.