MCP Integration
Master the Model Context Protocol as a tool extension system.
What You’ll Learn
Claude Code ships with a powerful set of built-in tools — Read, Edit, Bash, Grep, Glob. But what if you need to query a database? Search a company wiki? Interact with a deployment pipeline? You could ask Claude to shell out to curl, but there’s a better way: the Model Context Protocol (MCP).
By the end, you’ll know:
- What MCP is and why it exists
- How MCP servers provide tools, resources, and prompts
- The client-server architecture and transport mechanisms
- How to configure MCP servers in your project
- How MCP tools integrate with Claude Code’s permission system
- How to build a simple MCP server from scratch
The Problem
Every team has its own tools. A frontend team needs Figma integration. A data team needs SQL access. A DevOps team needs Kubernetes controls. Building each integration directly into Claude Code would be:
- Impossible to maintain — thousands of integrations, each with different APIs
- A security nightmare — every integration would need to ship with Claude Code
- Inflexible — organizations can’t add their own internal tools
The pattern is familiar from other domains. Web browsers don’t build in every feature — they support extensions. Code editors don’t bundle every language — they support plugins. Claude Code needs the same thing: a standard protocol for tool extensions.
Without MCP:
┌──────────────────────────────────────────┐
│ Claude Code │
│ │
│ Built-in: Read, Edit, Bash, Grep... │
│ Custom: ??? (fork the codebase?) │
└──────────────────────────────────────────┘
With MCP:
┌──────────────────────────────────────────┐
│ Claude Code │
│ │
│ Built-in: Read, Edit, Bash, Grep... │
│ MCP: Database, Jira, Slack, │
│ Figma, K8s, your-custom-tool │
└──────────────────────────────────────────┘
How It Works
The MCP Architecture
MCP follows a client-server model. Claude Code is the client. Each external integration is a server. They communicate through a standard protocol:
┌─────────────────────────────────────────────────────┐
│ Claude Code (MCP Client) │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Built-in │ │ MCP Conn │ │ MCP Conn │ ... │
│ │ Tools │ │ #1 │ │ #2 │ │
│ └──────────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │
└─────────────────────│──────────────│────────────────┘
│ │
┌───────▼──────┐ ┌────▼─────────┐
│ MCP Server │ │ MCP Server │
│ (Database) │ │ (Jira) │
│ │ │ │
│ Tools: │ │ Tools: │
│ - query │ │ - search │
│ - schema │ │ - create │
│ - explain │ │ - update │
└──────────────┘ └──────────────┘
Each MCP server is a separate process. Claude Code connects to it, discovers what tools it offers, and makes those tools available to the AI alongside the built-in ones.
What MCP Servers Provide
An MCP server can expose three types of capabilities:
1. Tools — Functions the AI can call (most common)
{
"name": "database_query",
"description": "Execute a read-only SQL query against the project database",
"inputSchema": {
"type": "object",
"properties": {
"sql": { "type": "string", "description": "The SQL query to execute" }
},
"required": ["sql"]
}
}
2. Resources — Data the AI can read (files, documents, live data)
{
"uri": "db://schema/users",
"name": "Users table schema",
"mimeType": "application/json"
}
3. Prompts — Reusable prompt templates
{
"name": "code_review",
"description": "Review code for common issues",
"arguments": [
{ "name": "language", "description": "Programming language" }
]
}
In practice, tools are by far the most common. When the AI decides to use an MCP tool, the call flows through the same agent loop as built-in tools — the AI sees no difference.
Transport Mechanisms
MCP supports two transport types:
stdio — For local processes (most common)
Claude Code ──stdin/stdout──► MCP Server Process
The server runs as a child process. Communication happens over stdin and stdout pipes, identical to how the CLI and SDK communicate (Session 13). This is fast, secure, and requires no network configuration.
SSE (Server-Sent Events) — For remote servers
Claude Code ──HTTP/SSE──► Remote MCP Server
(running on a server)
The server runs on a remote machine. Communication happens over HTTP with SSE for streaming. This enables shared MCP servers that a whole team can use.
Configuration
MCP servers are configured in .mcp.json files. Claude Code looks for this file in your project root:
{
"mcpServers": {
"database": {
"command": "npx",
"args": ["-y", "@mcp/postgres", "--connection-string", "postgresql://localhost:5432/mydb"],
"type": "stdio"
},
"memory": {
"command": "npx",
"args": ["-y", "@mcp/memory"],
"type": "stdio"
},
"company-wiki": {
"url": "https://mcp.internal.company.com/wiki",
"type": "sse"
}
}
}
Each entry defines:
- A name — How the tools will be prefixed (e.g.,
mcp__database__query) - A command/url — How to start or connect to the server
- A type —
stdiofor local,ssefor remote
Tool Discovery
At startup, Claude Code connects to each configured MCP server and asks: “What tools do you offer?”
Startup sequence:
─────────────────
1. Claude Code reads .mcp.json
2. For each server:
a. Spawn process (stdio) or connect (sse)
b. Send: { "method": "tools/list" }
c. Receive: list of tool definitions
d. Register tools with naming convention
Result:
┌──────────────────────────────────────────────┐
│ Available Tools │
│ │
│ Built-in: │
│ Read, Edit, Bash, Glob, Grep, Write, │
│ TodoRead, TodoWrite, WebFetch... │
│ │
│ MCP (database): │
│ mcp__database__query │
│ mcp__database__schema │
│ mcp__database__explain │
│ │
│ MCP (memory): │
│ mcp__memory__add_observations │
│ mcp__memory__search_nodes │
│ mcp__memory__read_graph │
└──────────────────────────────────────────────┘
The naming convention mcp__<server>__<tool> prevents collisions. If two servers both have a tool called search, they become mcp__wiki__search and mcp__jira__search.
Permission Integration
MCP tools go through the same permission system as built-in tools. This is critical for security:
AI decides to call mcp__database__query
│
▼
┌─────────────────┐
│ Permission Check │
│ │
│ Is this tool │
│ allowed? │
│ │
│ - Check settings │
│ - Check hooks │
│ - Check scope │
└────┬────────┬────┘
│ │
Allowed Blocked
│ │
▼ ▼
Execute Ask user
tool for permission
You can configure permissions for MCP tools in your settings just like built-in tools. A PreToolUse hook (Session 15) can inspect MCP tool calls and block dangerous ones.
The Communication Flow
Here’s a complete flow when the AI uses an MCP tool:
User: "How many users signed up this week?"
│
▼
┌─────────────────────────────────────────────┐
│ Agent Loop │
│ │
│ AI decides: use mcp__database__query │
│ Input: "SELECT COUNT(*) FROM users │
│ WHERE created_at > NOW() - '7d'" │
│ │
│ Permission check → allowed │
│ │ │
│ ▼ │
│ Claude Code sends to MCP server: │
│ { "method": "tools/call", │
│ "params": { │
│ "name": "query", │
│ "arguments": { "sql": "SELECT..." } │
│ } │
│ } │
│ │ │
│ ▼ │
│ MCP server executes query, returns: │
│ { "result": { "content": [ │
│ { "type": "text", "text": "142" } │
│ ]} │
│ } │
│ │ │
│ ▼ │
│ Result injected as tool_result │
│ AI continues: "142 users signed up..." │
└─────────────────────────────────────────────┘
From the AI’s perspective, calling an MCP tool is identical to calling a built-in tool. It sees the tool definition, calls it with the right parameters, and gets a result back. The protocol handles everything in between.
Key Insight
MCP is what makes Claude Code infinitely extensible without changing its core. Instead of building integrations into the codebase, MCP lets you plug in any tool through a standard protocol.
This mirrors a proven pattern in software:
| System | Core | Extension Protocol |
|---|---|---|
| Web Browser | Rendering engine | Extension APIs |
| VS Code | Editor core | Language Server Protocol |
| Unix | Kernel | stdin/stdout pipes |
| Claude Code | Agent loop | MCP |
The power is in the standard. Because MCP is an open protocol, anyone can build a server. The ecosystem grows independently of Claude Code itself. A database MCP server built by one team works with any MCP client — not just Claude Code.
Hands-On Example
Building a Simple MCP Server
Here’s a minimal MCP server that provides a single tool — a word counter:
// word-counter-server.js
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: "word-counter",
version: "1.0.0",
});
// Register a tool
server.tool(
"count_words",
"Count words in a given text",
{
text: z.string().describe("The text to count words in"),
},
async ({ text }) => {
const wordCount = text.split(/\s+/).filter(Boolean).length;
const charCount = text.length;
return {
content: [
{
type: "text",
text: `Words: ${wordCount}, Characters: ${charCount}`,
},
],
};
}
);
// Start listening on stdio
const transport = new StdioServerTransport();
await server.connect(transport);
Connecting It to Claude Code
Add the server to your project’s .mcp.json:
{
"mcpServers": {
"wordcount": {
"command": "node",
"args": ["word-counter-server.js"],
"type": "stdio"
}
}
}
Now when you start Claude Code in this project, the AI has access to mcp__wordcount__count_words. You can ask “How many words are in this paragraph?” and the AI will use your custom tool.
A More Practical Example: File Metadata Server
// file-metadata-server.js
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import fs from "fs/promises";
import path from "path";
const server = new McpServer({
name: "file-metadata",
version: "1.0.0",
});
server.tool(
"file_stats",
"Get detailed metadata about a file (size, dates, permissions)",
{
file_path: z.string().describe("Path to the file"),
},
async ({ file_path }) => {
const stats = await fs.stat(file_path);
return {
content: [{
type: "text",
text: JSON.stringify({
size_bytes: stats.size,
created: stats.birthtime.toISOString(),
modified: stats.mtime.toISOString(),
is_directory: stats.isDirectory(),
permissions: stats.mode.toString(8),
}, null, 2),
}],
};
}
);
server.tool(
"directory_size",
"Calculate total size of a directory recursively",
{
dir_path: z.string().describe("Path to the directory"),
},
async ({ dir_path }) => {
let totalSize = 0;
const entries = await fs.readdir(dir_path, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dir_path, entry.name);
if (entry.isFile()) {
const stat = await fs.stat(fullPath);
totalSize += stat.size;
}
}
return {
content: [{
type: "text",
text: `Total size: ${(totalSize / 1024).toFixed(1)} KB (${entries.length} entries)`,
}],
};
}
);
const transport = new StdioServerTransport();
await server.connect(transport);
This gives the AI two new capabilities it didn’t have before — detailed file metadata and directory size calculation — without modifying Claude Code at all.
What Changed
| Without MCP | With MCP |
|---|---|
| Fixed set of built-in tools | Unlimited extensibility |
| New integrations require code changes | Drop in a server config |
| Every user gets the same tools | Teams customize their toolset |
| AI shells out to CLI for custom work | AI uses structured, typed tools |
| No standard for tool extensions | Open protocol, growing ecosystem |
| Security is ad-hoc per integration | Unified permission system |
Next Session
MCP lets you add tools. But how do you enforce rules about how tools are used? Session 15 covers the Hooks System — event subscribers that fire before and after tool execution, giving you security gates, audit logs, and organizational policy enforcement.