Claude Code is powerful out of the box. But what makes it truly extensible is the Model Context Protocol — MCP. If you have ever wished Claude Code could query your database, browse your staging environment, or interact with your company''s internal APIs, MCP is how you get there.
This post is a deep dive into MCP: what it is, how it works architecturally, and how to build your own MCP server in TypeScript.
What Is MCP?
The Model Context Protocol is an open standard that defines how AI assistants communicate with external tools. Think of it as a USB-C port for AI: a standardized interface that lets any compatible tool plug into any compatible AI assistant.
Before MCP, tool integration was ad-hoc. Every AI product had its own plugin format, its own API conventions, its own way of describing tool capabilities. MCP changes this by defining a universal protocol with three core concepts:
- Tools: Functions the AI can call (e.g., "query the database," "create a Jira ticket")
- Resources: Data the AI can read (e.g., file contents, API responses, database schemas)
- Prompts: Reusable prompt templates the server can offer to the client
The Client-Server Architecture
MCP follows a client-server model:
The client (Claude Code) discovers available tools by calling the server''s tools/list endpoint. When Claude decides to use a tool, it sends a tools/call request with the tool name and arguments. The server executes the tool and returns the result.
Communication happens over stdio (for local servers) or HTTP with Server-Sent Events (for remote servers). The wire format is JSON-RPC 2.0 — simple, well-understood, easy to debug.
How Claude Code Uses MCP Servers
When you configure an MCP server in Claude Code, it becomes available as a tool during your session. Claude can see the tool''s name, description, and parameter schema, and it decides when to call it based on context.
Configuration lives in your project''s .mcp.json or in ~/.claude/settings.json for global servers:
{
"mcpServers": {
"my-database": {
"command": "npx",
"args": ["-y", "@my-org/db-mcp-server"],
"env": {
"DATABASE_URL": "postgresql://localhost:5432/mydb"
}
}
}
}
Once configured, Claude Code starts the server process automatically and communicates with it over stdio. You can verify the connection with the /mcp command inside Claude Code.
Available Community Servers
The MCP ecosystem is growing fast. Here are servers I use regularly:
Filesystem Server (@modelcontextprotocol/server-filesystem)
Read and write files with configurable access controls. Useful when you want to give Claude access to specific directories outside the project root.
PostgreSQL Server (@modelcontextprotocol/server-postgres)
Query your database directly. Claude can inspect schemas, run SELECT queries, and analyze results. I use this for debugging data issues without leaving my editor.
Browser/Playwright Server (@anthropic/mcp-playwright)
Control a browser programmatically. Navigate pages, take screenshots, fill forms. Essential for testing and debugging frontend issues in context.
GitHub Server (@modelcontextprotocol/server-github)
Create issues, read PRs, manage repositories. Integrates your GitHub workflow directly into Claude Code.
Fetch Server (@modelcontextprotocol/server-fetch)
Make HTTP requests to any URL. Useful for interacting with REST APIs, checking webhook responses, or pulling data from external services.
Building a Custom MCP Server
Let us build a practical example: an MCP server that queries your project''s error tracking system. I will use TypeScript and the official MCP SDK.
Step 1: Project Setup
mkdir error-tracker-mcp && cd error-tracker-mcp
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node
Step 2: Define the Server
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: "error-tracker",
version: "1.0.0",
});
server.tool(
"get-recent-errors",
"Fetch recent errors from the error tracking system",
{
project: z.string().describe("Project identifier"),
limit: z.number().optional().default(10).describe("Number of errors to fetch"),
severity: z.enum(["error", "warning", "info"]).optional()
.describe("Filter by severity level"),
},
async ({ project, limit, severity }) => {
const response = await fetch(
`https://errors.internal.company/api/v1/projects/${project}/issues?limit=${limit}${severity ? `&severity=${severity}` : ""}`,
{ headers: { Authorization: `Bearer ${process.env.ERROR_TRACKER_TOKEN}` } }
);
const errors = await response.json();
return {
content: [{ type: "text", text: JSON.stringify(errors, null, 2) }],
};
}
);
const transport = new StdioServerTransport();
await server.connect(transport);
Step 3: Configure in Claude Code
Add to your project''s .mcp.json:
{
"mcpServers": {
"error-tracker": {
"command": "npx",
"args": ["tsx", "./error-tracker-mcp/server.ts"],
"env": {
"ERROR_TRACKER_TOKEN": "your-api-token"
}
}
}
}
Now Claude Code can query your error tracker directly: "Show me the latest critical errors in the frontend project" triggers the get-recent-errors tool automatically.
Practical Use Cases
Here are the MCP servers I have built for my own workflow:
Deployment Status Server: Queries Railway and Vercel APIs to check deployment status, read logs, and trigger redeployments. When debugging a production issue, Claude can check whether a deploy is in progress without me switching context.
Database Migration Server: Wraps Supabase migration commands. Claude can generate migrations, apply them to a local database, and verify the schema — all within the conversation.
Documentation Server: Indexes our internal Confluence pages and makes them searchable. When Claude needs context about a business rule, it can look it up in the actual documentation rather than guessing.
Debugging MCP Servers
When things go wrong, start with these steps:
- Check the MCP connection: Run
/mcpin Claude Code to see which servers are connected and their status. - Test standalone: Run your server directly and send JSON-RPC requests via stdin to verify it responds correctly.
- Check stderr: MCP servers should log errors to stderr, which Claude Code captures and can display.
- Validate schemas: Ensure your Zod schemas match what the server actually accepts. Schema mismatches are the most common source of tool call failures.
Conclusion
MCP transforms Claude Code from a code assistant into an integration hub. Instead of context-switching between your editor, database client, error tracker, and deployment dashboard, you bring all of those tools into a single conversational interface.
The protocol is simple enough to implement in an afternoon. Start with one tool that solves a real friction point in your workflow, and expand from there. The community ecosystem covers the common cases; custom servers handle everything else.


