MCP Servers
MCP (Model Context Protocol) is a protocol that lets Claude connect to external tools — databases, APIs, Slack, Jira, GitHub, and more. Claude calls tools via MCP servers you configure in settings.json.
Explain Like I'm 12
Architecture Diagram
This diagram shows how Claude Code acts as the MCP client, connecting to one or more MCP servers, each of which bridges to an external service.
What is MCP?
MCP stands for Model Context Protocol. It is an open protocol created by Anthropic that standardizes how AI models connect to external data sources and tools. Before MCP, every integration was a one-off. Now there is a single protocol that any tool can implement.
Key facts about MCP
- Open standard — anyone can build an MCP server or client
- Client-server architecture — Claude Code is the client, your configured servers provide the tools
- JSON-RPC 2.0 — the wire protocol, transported over stdio or Server-Sent Events (SSE)
- Three primitives — Tools (actions the model can call), Resources (data the model can read), and Prompts (reusable templates)
Think of MCP like an API gateway specifically designed for AI. Instead of hardcoding integrations into the model, you declare which servers are available and the model discovers what each one can do at runtime.
How MCP Works
The flow is straightforward. When Claude Code starts, it reads your MCP server configuration and spawns each server process. The client and server then negotiate capabilities via an initialization handshake.
At runtime, the flow looks like this:
- Discovery — Claude asks each server "what tools do you offer?" and gets back a list with names, descriptions, and input schemas
- Selection — when you ask Claude a question, it decides which tool (if any) to call based on your request and the tool descriptions
- Invocation — Claude sends a JSON-RPC
tools/callrequest to the server with the tool name and arguments - Execution — the MCP server runs the action (queries a database, calls an API, reads a file) and returns the result
- Response — Claude incorporates the result into its answer
stdio vs SSE transport
stdio is the default — Claude spawns the server as a child process and communicates over stdin/stdout. Simple, fast, and works locally. SSE (Server-Sent Events) is for remote servers — the MCP server runs on a separate machine and Claude connects over HTTP. Use SSE when the server needs to run on a different host or be shared across users.
Configuring MCP Servers
MCP servers are configured in your settings.json file (project-level .claude/settings.json or user-level ~/.claude/settings.json). The key is mcpServers, and each entry follows the same pattern: a name, a command to spawn the server, optional arguments, and optional environment variables.
{
"mcpServers": {
"server-name": {
"command": "executable",
"args": ["arg1", "arg2"],
"env": {
"API_KEY": "your-key-here"
}
}
}
}
That is the universal shape. Every MCP server follows it. The only things that change are the command, args, and env values.
Where to put secrets
Never hardcode API keys or tokens directly in settings.json if you commit it to version control. Use environment variable references, a .env file loaded by your shell, or the env field pointing to variables already set in your environment. The .claude/settings.json in your project should be in .gitignore if it contains secrets.
Example: Filesystem MCP Server
The filesystem MCP server lets Claude read and write files outside the current project directory. This is useful when you need to access config files, logs, or data stored elsewhere on your machine.
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"/Users/you/Documents",
"/Users/you/Desktop"
]
}
}
}
The paths in args are the directories the server is allowed to access. Claude can only read and write within those directories — it cannot escape to the rest of your filesystem.
Permission scoping
Each MCP server only exposes the capabilities it is designed for. The filesystem server can only access the directories you explicitly list. This is a core security principle of MCP — servers are sandboxed to their declared scope.
Example: Database MCP Server (PostgreSQL)
Want Claude to query your database directly? The PostgreSQL MCP server exposes tools for running SQL queries, listing tables, and inspecting schemas.
{
"mcpServers": {
"postgres": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-postgres",
"postgresql://localhost:5432/mydb"
]
}
}
}
Once configured, you can ask Claude things like "show me the top 10 orders from last week" and it will write and execute the SQL query through the MCP server, returning the results directly in your conversation.
Use read-only connections for production
When connecting to production databases, use a read-only database user. MCP servers execute whatever the model asks them to — a DROP TABLE is just as easy to run as a SELECT. Scope the database user's permissions to match what you actually want Claude to do.
Example: GitHub MCP Server
The GitHub MCP server gives Claude access to repositories, issues, pull requests, and more through the GitHub API.
{
"mcpServers": {
"github": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-github"
],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_your_token_here"
}
}
}
}
With this configured, Claude can create pull requests, read issue details, search repositories, review code, and manage branches — all through natural language commands.
# Now you can ask Claude things like:
# "Create a PR for the changes on the feature/auth branch"
# "List all open issues labeled 'bug'"
# "What changed in PR #42?"
Token permissions
Create a fine-grained personal access token with only the permissions Claude needs. For read-only workflows, grant repo:read and issues:read. For PR creation, add pull_requests:write. Follow the principle of least privilege.
Building a Custom MCP Server
You can build your own MCP server in Python using the FastMCP framework. This is the quickest path to exposing any API or service to Claude.
Python quickstart with FastMCP
from mcp.server.fastmcp import FastMCP
# Create the server
mcp = FastMCP("my-tools")
# Define a tool using the @mcp.tool() decorator
@mcp.tool()
def get_weather(city: str) -> str:
"""Get the current weather for a city."""
# In real code, call a weather API here
return f"Weather in {city}: 72°F, sunny"
# Define a resource (read-only data Claude can access)
@mcp.resource("config://app")
def get_app_config() -> str:
"""Return the current application configuration."""
return "{ \"debug\": false, \"version\": \"2.1.0\" }"
# Run the server
if __name__ == "__main__":
mcp.run()
The @mcp.tool() decorator turns any function into an MCP tool. The function's docstring becomes the tool description that Claude reads when deciding which tool to call. Type hints define the input schema automatically.
Registering your custom server
{
"mcpServers": {
"my-tools": {
"command": "python",
"args": ["path/to/my_server.py"]
}
}
}
The resource pattern
Resources are read-only data endpoints. Unlike tools (which perform actions), resources expose data that Claude can pull in as context. Use resources for configuration files, documentation, database schemas — anything Claude should be able to read but not modify through the resource itself.
JavaScript/TypeScript alternative
If you prefer Node.js, the official @modelcontextprotocol/sdk package provides the same capabilities:
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: "my-tools",
version: "1.0.0"
});
// Define a tool
server.tool(
"get_weather",
{ city: z.string() },
async ({ city }) => ({
content: [{
type: "text",
text: `Weather in ${city}: 72°F, sunny`
}]
})
);
// Connect via stdio
const transport = new StdioServerTransport();
await server.connect(transport);
Security Considerations
MCP servers run code on your machine with your permissions. Treat them like any other dependency — trust matters.
Security checklist
- Only use trusted servers. An MCP server has access to whatever you grant it — files, databases, APIs. Only install servers from sources you trust.
- Environment variables for secrets. Never put API keys in plain text in committed files. Use
envfields referencing shell variables, or use a secrets manager. - Permission scoping. Give each server the minimum access it needs. Filesystem server? Only list the directories it should see. Database server? Use a read-only user.
- Review server source code. Before installing a third-party MCP server, read the code. Know what it does with your data.
- Network isolation. If a server does not need internet access, run it in an environment without it. MCP servers over stdio run locally by default.
Debugging MCP Servers
When an MCP server is not working, here is how to diagnose the problem.
Check MCP logs
Claude Code logs MCP server activity. Check the logs for connection errors, failed tool calls, or server crashes:
# MCP logs are in the Claude Code log directory
# Look for connection and initialization messages
claude --verbose
Test with mcp-inspector
The mcp-inspector is a standalone tool that connects to your MCP server and lets you test tools interactively — without involving Claude at all.
# Install and run the inspector
npx @modelcontextprotocol/inspector
# It opens a web UI where you can:
# - See all tools the server exposes
# - Call tools with custom arguments
# - View raw JSON-RPC messages
Common errors and fixes
- "Server failed to start" — the command in your config is wrong, or the executable is not in your PATH. Try running the command manually in your terminal first.
- "Tool not found" — the server started but Claude cannot find the tool. Check the tool name matches exactly. Use
mcp-inspectorto verify what tools are exposed. - "Connection closed" — the server crashed mid-conversation. Check the server logs for unhandled exceptions. Add error handling to your custom server code.
- Timeout errors — the server is taking too long to respond. MCP has a default timeout. For slow operations, consider streaming results or increasing the timeout in your server implementation.
Test Yourself
Q: What does MCP stand for, and who created it?
Q: Where do you configure MCP servers in Claude Code?
settings.json, under the "mcpServers" key. Each entry specifies a command, optional args, and optional env.Q: What are the two transport methods MCP supports?
Q: What is the difference between MCP Tools and Resources?
Q: How do you test an MCP server without involving Claude?
npx @modelcontextprotocol/inspector to launch the MCP Inspector. It connects to your server and lets you list tools, call them with custom arguments, and inspect the raw JSON-RPC messages.Interview Questions
Q: How would you design an MCP server that integrates with a third-party API that has rate limits?
Q: What are the security implications of running MCP servers, and how would you mitigate them?
Q: Explain the MCP protocol lifecycle: what happens from server startup to tool invocation?
initialize handshake negotiates protocol version and capabilities. Then the client sends tools/list to discover available tools (names, descriptions, input schemas). When the user makes a request that needs a tool, the client sends tools/call with the tool name and arguments. The server executes the action and returns the result. The client can also request resources/read for context data. The connection stays open for the session duration, and the server is shut down when the session ends.