← All articles
Claude CodeMCP

How to Add MCP Servers to Claude Code

Learn how to configure MCP servers in Claude Code so your AI assistant can access project docs, APIs, and internal tools.

How to Add MCP Servers to Claude Code

Claude Code ships with solid defaults for reading files, running commands, and searching codebases. But out of the box, it can't query your database, hit your internal APIs, or read your custom config formats. That's where MCP servers come in. The Model Context Protocol lets you extend Claude Code with project-specific tools and context sources, so the assistant works the way your team actually works.

This walkthrough covers the full Claude Code MCP server setup process: writing a server, wiring it into your configuration, and confirming it actually responds when called.

What MCP Servers Actually Do

MCP (Model Context Protocol) is an open protocol that standardizes how AI models communicate with external tools and data sources. An MCP server exposes a set of tools and resources over a consistent interface. When you configure one in Claude Code, the CLI discovers those tools at startup and makes them available to Claude during sessions.

The practical upshot: instead of copy-pasting database results or API responses into your prompt, Claude can call your tools directly. It can list database tables, run parameterized queries, fetch ticket details from your tracker, or read proprietary config files — whatever your server exposes.

Three things an MCP server can provide:

  • Tools — Functions Claude can call with arguments. Think "run this query" or "fetch this resource."
  • Resources — Static or dynamic data Claude can read. Think "project README" or "current deployment status."
  • Prompts — Reusable prompt templates (less commonly used in Claude Code workflows).

For most Claude Code setups, tools are the main event.

Setting Up Your Project

You need Node.js installed (v18 or later) and Claude Code configured and working. Create a directory for your MCP server — ideally inside your project or in a dedicated shared package.

mkdir my-mcp-server
cd my-mcp-server
npm init -y

Install the MCP SDK:

npm install @modelcontextprotocol/sdk

Your package.json needs a bin entry so the server can be executed. Update it to include:

{
  "name": "my-mcp-server",
  "version": "1.0.0",
  "type": "module",
  "bin": {
    "my-mcp-server": "./index.js"
  }
}

The "type": "module" line matters — the SDK uses ES module syntax.

Creating Your First MCP Server

Build a minimal server that exposes one tool. This example creates a tool that reads a project's .env file and lists the variable names (not values — we don't want secrets flowing to the model unnecessarily).

Create index.js:

#!/usr/bin/env node

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { readFileSync, existsSync } from "fs";
import { join } from "path";
import { z } from "zod";

const server = new McpServer({
  name: "env-reader",
  version: "1.0.0",
});

server.tool(
  "list-env-keys",
  "List all variable names in the project .env file",
  {
    projectPath: z.string().describe("Absolute path to the project root"),
  },
  async (args) => {
    const envPath = join(args.projectPath, ".env");
    if (!existsSync(envPath)) {
      return {
        content: [{ type: "text", text: "No .env file found at that path" }],
      };
    }

    const content = readFileSync(envPath, "utf-8");
    const keys = content
      .split("\n")
      .filter((line) => line && !line.startsWith("#"))
      .map((line) => line.split("=")[0].trim())
      .filter(Boolean);

    return {
      content: [
        { type: "text", text: `Environment variables: ${keys.join(", ")}` },
      ],
    };
  }
);

const transport = new StdioServerTransport();
await server.connect(transport);

Key parts worth noting:

  • McpServer wraps the protocol details. You define tools with a name, description, parameter schema (using Zod), and an async handler.
  • StdioServerTransport is the transport mechanism Claude Code uses. The CLI spawns your server as a subprocess and communicates over stdin/stdout.
  • The z import comes from Zod, which the SDK uses for parameter validation. It's a dependency of the SDK, so it's already available.
  • Tool responses always use the content array format with { type: "text", text: "..." }.

Make the file executable:

chmod +x index.js

Link it globally so Claude Code can find it:

npm link

Or don't — you can reference the full path instead, which is often simpler during development.

Wiring the Server into Claude Code Configuration

Claude Code reads MCP server configuration from ~/.claude/settings.json (global) or .claude/settings.json in your project root (project-specific). Project-level config is usually better — it keeps the setup portable across your team.

Create or edit .claude/settings.json in your project:

{
  "mcpServers": {
    "env-reader": {
      "command": "node",
      "args": ["/absolute/path/to/my-mcp-server/index.js"]
    }
  }
}

If you used npm link, you can reference the binary name instead:

{
  "mcpServers": {
    "env-reader": {
      "command": "my-mcp-server"
    }
  }
}

Either approach works. The absolute path is more explicit and less likely to break if your global bin setup changes.

You can also pass environment variables to the server process:

{
  "mcpServers": {
    "env-reader": {
      "command": "node",
      "args": ["/absolute/path/to/my-mcp-server/index.js"],
      "env": {
        "DATABASE_URL": "postgresql://localhost:5432/mydb"
      }
    }
  }
}

This is useful for providing connection strings or API keys that the server needs, without hardcoding them into the server code.

Verifying the Server Connection

Start Claude Code in your project directory:

claude

If you're already in a session, restart it — Claude Code discovers MCP tools at startup. Then check that your tools loaded:

/mcp

This displays connected MCP servers and their available tools. You should see env-reader with the list-env-keys tool.

Now test it. Ask Claude something like: "What environment variables are defined in this project?"

Claude should call list-env-keys and return the variable names from your .env file. If it doesn't, there are a few things to check:

  • Server not listed: The config path is wrong, or the JSON is malformed. Verify .claude/settings.json is valid JSON and the command path is absolute.
  • Server listed but no tools: Your server threw an error during startup. Check the Claude Code logs — they're in ~/.claude/logs/.
  • Tool called but returned nothing: The handler has a bug. Add console.error logging to your server code — output to stderr shows up in the logs, while stdout is reserved for the MCP protocol.

Common Tool Patterns That Actually Matter

The env-reader example is simple. Here are patterns worth building for real projects:

Database query tool. Accept a SQL query string, execute it against your development database, return results as formatted text. Never expose production credentials through an MCP server — this should be dev-only.

server.tool(
  "query-dev-db",
  "Run a read-only SQL query against the development database",
  {
    sql: z.string().describe("SELECT query to execute"),
  },
  async (args) => {
    const result = await db.query(args.sql);
    return {
      content: [{ type: "text", text: JSON.stringify(result.rows, null, 2) }],
    };
  }
);

API client tool. Wrap internal APIs that Claude doesn't know about. This is useful for fetching data from services that require authentication or have custom schemas.

Documentation resource. Expose your project's architecture docs, runbooks, or ADRs (Architecture Decision Records) as resources Claude can read on demand:

server.resource(
  "architecture",
  "docs://architecture",
  async () => {
    const content = readFileSync("docs/architecture.md", "utf-8");
    return {
      contents: [
        {
          uri: "docs://architecture",
          text: content,
        },
      ],
    };
  }
);

File format parser. If your project uses custom config formats (YAML with specific schemas, TOML, protobuf definitions), create a tool that parses and describes the structure in plain text.

Debugging Tips That Save Time

MCP servers communicate over stdio, which makes debugging harder than a typical Node process. Keep these approaches in mind:

Log to stderr. Anything written to stderr appears in Claude Code's logs. Use this freely:

process.stderr.write("Server started, registering tools...\n");

Test the server standalone. Before wiring it into Claude Code, verify the server starts without errors:

node /path/to/my-mcp-server/index.js

It should start and wait for input. If it crashes immediately, you have a syntax or import error.

Check the logs. Claude Code logs MCP communication to ~/.claude/logs/. The filenames include timestamps. Look for error messages and the actual JSON-RPC messages being exchanged.

Validate your JSON config. A missing comma or trailing comma in settings.json will silently fail. Run your config through a JSON validator.

Watch for stdout pollution. If your server accidentally writes to stdout (e.g., a console.log that should be console.error), it corrupts the protocol stream. The MCP protocol uses JSON-RPC over stdio — any stray output on stdout breaks parsing. Always use console.error for debug output, never console.log.

Where to Go From Here

The MCP SDK supports more than tools and resources — you can expose prompt templates, handle resource subscriptions, and implement sampling requests where the server asks Claude to generate content. For most Claude Code setups, tools cover 90% of what you need.

Start small. Ship one tool that removes a real friction point in your workflow — querying your dev database, reading your ticket tracker, or parsing your config. Use it for a week. See what Claude actually asks for. Then add more.

The real leverage isn't in building a massive MCP server with every possible tool. It's in exposing exactly the context that makes Claude effective on your specific codebase, with your specific conventions, under your specific constraints. One well-chosen tool that Claude calls reliably beats twenty tools that never get invoked.

For the official MCP specification and more server examples, check the Model Context Protocol repository on GitHub. The Anthropic docs also include a growing catalog of community-built MCP servers you can reference or install directly.