Skip to main content
Module 3: Real Architecture 2 / 6
Advanced Session 14 MCP Protocol

MCP Integration

Master the Model Context Protocol as a tool extension system.

March 20, 2026 22 min read

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:

  1. Impossible to maintain — thousands of integrations, each with different APIs
  2. A security nightmare — every integration would need to ship with Claude Code
  3. 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 typestdio for local, sse for 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:

SystemCoreExtension Protocol
Web BrowserRendering engineExtension APIs
VS CodeEditor coreLanguage Server Protocol
UnixKernelstdin/stdout pipes
Claude CodeAgent loopMCP

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 MCPWith MCP
Fixed set of built-in toolsUnlimited extensibility
New integrations require code changesDrop in a server config
Every user gets the same toolsTeams customize their toolset
AI shells out to CLI for custom workAI uses structured, typed tools
No standard for tool extensionsOpen protocol, growing ecosystem
Security is ad-hoc per integrationUnified 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.