Working with Claude Code: MCP Servers
The Model Context Protocol connects Claude Code to external tools and data. From concept to a working MCP server.
You’re working in Claude Code on a feature that needs data from your PostgreSQL database. You write a SQL query, copy it to a terminal tab, run it against the dev database, copy the output back to Claude Code as context, and ask Claude to finish the feature based on that data. Two steps later the data is outdated and the context no longer holds.
This is the problem MCP solves.
What MCP is
MCP stands for Model Context Protocol. It’s an open standard — originally introduced by Anthropic — that defines how an AI model communicates with external tools and data sources. In the context of Claude Code: MCP makes it possible to add tools that go beyond the built-in Read, Write, Edit, Bash, and Grep.
The model uses the term “server” for an MCP integration. That’s confusing if you think of web servers. An MCP server is a locally running process that offers tools to Claude Code. It can be a database connection, an API wrapper, a filesystem interface, or a custom tool you build yourself. Claude Code communicates with the MCP server via a standardized protocol, and the tools the server offers appear as extra options alongside the built-in tools.
The architecture:
Claude Code (client) ←→ MCP Protocol ←→ MCP Server (tool provider)
Claude Code is the client. It sends requests to the server. The server executes the action and returns the result. Communication runs via JSON-RPC over stdio or HTTP. You don’t need to implement the protocol yourself — there are SDKs for TypeScript and Python.
Claude Code as MCP client
Claude Code can run multiple MCP servers simultaneously. You configure them in .claude/settings.json:
{
"mcpServers": {
"postgres": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres"],
"env": {
"DATABASE_URL": "postgresql://user:pass@localhost:5432/mydb"
}
}
}
}
After this configuration, Claude Code starts the postgres MCP server automatically with every session. The server offers tools — in this case tools to execute SQL queries against your database. Claude Code sees those tools and can use them when relevant.
In practice:
> Which orders were created today with an amount over €100?
Claude Code recognizes this as a database question, formulates a SQL query, sends it to PostgreSQL via the MCP server, gets the results back, and presents them in your session. No copy-paste. No switching terminal tabs. The data is current at the moment of the question.
Available MCP servers
The MCP ecosystem is growing. There are ready-made servers for the most common use cases.
Database servers
{
"mcpServers": {
"postgres": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres"],
"env": { "DATABASE_URL": "postgresql://..." }
},
"sqlite": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-sqlite", "--db-path", "./data/app.db"]
}
}
}
With a database server, Claude Code can inspect schemas, run queries, and retrieve data as context for code decisions. This is particularly useful when writing migrations, debugging data-related bugs, and generating seed data.
Filesystem servers
Claude Code’s built-in Read tool already works with your local filesystem. But an MCP filesystem server can provide access to files outside your project directory, or to files on a remote system.
API wrapper servers
An MCP server can wrap any HTTP API. Suppose you have an internal API for user management:
{
"mcpServers": {
"user-api": {
"command": "node",
"args": ["./mcp-servers/user-api/index.js"],
"env": { "API_BASE_URL": "http://localhost:3000/api" }
}
}
}
The server offers tools like getUser, listUsers, updateUser. Claude Code can use those tools to retrieve context or perform actions as part of a broader task.
Memory and knowledge servers
There are MCP servers that provide persistent memory — a key-value store that Claude Code can use to remember information between sessions. This goes beyond CLAUDE.md: the model can decide itself what it stores and retrieves.
Building your own MCP server
The power of MCP lies in its extensibility. Every tool you can write as a function can become an MCP server. The TypeScript SDK makes it straightforward.
The structure
An MCP server has three components:
- Server initialization — define the server and its metadata.
- Tool definitions — describe which tools the server offers, with parameters and return types.
- Tool handlers — implement what each tool does.
A minimal example
Suppose you want a server that summarizes the current git status:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { execSync } from "child_process";
import { z } from "zod";
const server = new McpServer({
name: "git-summary",
version: "1.0.0",
});
server.tool(
"git_status_summary",
"Returns a summary of the current git status",
{},
async () => {
const status = execSync("git status --porcelain").toString();
const branch = execSync("git branch --show-current").toString().trim();
const lastCommit = execSync("git log -1 --oneline").toString().trim();
const lines = status.split("\n").filter(Boolean);
const modified = lines.filter((l) => l.startsWith(" M")).length;
const added = lines.filter((l) => l.startsWith("A")).length;
const untracked = lines.filter((l) => l.startsWith("??")).length;
return {
content: [
{
type: "text",
text: `Branch: ${branch}\nLast commit: ${lastCommit}\nModified: ${modified}, Added: ${added}, Untracked: ${untracked}`,
},
],
};
}
);
const transport = new StdioServerTransport();
await server.connect(transport);
Register the server in .claude/settings.json:
{
"mcpServers": {
"git-summary": {
"command": "npx",
"args": ["tsx", "./mcp-servers/git-summary/index.ts"]
}
}
}
From now on, Claude Code can use the git_status_summary tool. When you ask “what’s the current status of the project?”, Claude can call this tool alongside the built-in tools.
Tools with parameters
Tools can accept parameters with a JSON Schema definition via Zod:
server.tool(
"git_diff_file",
"Shows the diff of a specific file",
{
filePath: z.string().describe("Path to the file"),
staged: z.boolean().optional().describe("Show staged changes"),
},
async ({ filePath, staged }) => {
const flag = staged ? "--staged" : "";
const diff = execSync(`git diff ${flag} -- ${filePath}`).toString();
return {
content: [{ type: "text", text: diff || "No changes." }],
};
}
);
Claude Code sees the parameter descriptions and fills them in automatically based on the conversation context. If you ask “what have I changed in src/api/auth.ts?”, Claude calls git_diff_file with filePath: "src/api/auth.ts".
MCP in practice
Three patterns that work well:
Pattern 1: Database as first-class context
With a PostgreSQL MCP server, Claude Code can answer questions that combine code and data:
> The function calculateRevenue in src/services/analytics.ts returns
> an incorrect total. Run the same query the function uses directly
> against the database and compare the result with the expected output.
Claude reads the function, extracts the query logic, executes a similar query via the MCP server, and identifies the difference. This kind of debugging is a five-step manual process without MCP.
Pattern 2: API integration as a development tool
When you’re working on a frontend that fetches data from your own API, an MCP server can make that API directly available:
> Fetch the current user via the user-api and generate a
> TypeScript type that describes the response.
Claude calls the API, inspects the response, and generates a type definition that exactly matches the actual data. No manually looking up API documentation.
Pattern 3: Custom tools for project-specific workflows
Every project has repetitive tasks that don’t fit the standard toolset. An MCP server can wrap those tasks:
- A tool that queries your Stripe test account for recent transactions.
- A tool that retrieves your feature flags from LaunchDarkly.
- A tool that searches your error tracking (Sentry, Bugsnag).
The investment is a few hours per server. The return is that Claude Code can retrieve that information whenever it’s relevant, without you having to provide the context manually.
Safety and boundaries
MCP servers run locally and have access to everything you give them. A database MCP server with a production connection string can read and write production data. An API wrapper with admin credentials can execute admin actions.
Three rules:
-
Use dev credentials. MCP servers in Claude Code should run against dev or staging environments. Production credentials in a local
.claude/settings.jsonare a risk. -
Limit write access. If an MCP server only needs read access, configure a read-only database user or API key.
-
Review tool calls. In the default permission mode, Claude Code shows MCP tool calls before they’re executed. Evaluate them just as critically as Bash commands.
The bigger picture
MCP transforms Claude Code from a code editor assistant into a platform that communicates with your entire development environment. Without MCP, Claude Code is limited to what it can find in your files and terminal. With MCP, it has access to databases, APIs, monitoring tools, and anything you can wrap as a server.
That’s a fundamental shift in what “context” means for an AI coding tool. Context is no longer just the files in your project. Context is your entire development environment — data, services, infrastructure — available via a standardized protocol.
In part 5 we’ll look at how to deploy Claude Code on complex projects: multi-file refactoring, sub-agents for parallel tasks, and when Claude Code is the right tool and when it’s not.
This is part 4 of the series “Working with Claude Code”. Read parts 1-3 for the basics.
Vic Boomer is an essay-led AI studio that turns ideas about AI, agents and software into clear analysis, working systems and practical tools.