Post

Model Context Protocol (MCP)

Model Context Protocol (MCP) is an open standard for connecting AI agents to external data sources, tools, and APIs via a vendor-neutral interface -- enabling any AI client (Claude, Gemini, open-source models) to securely integrate with custom business systems without rebuilding integrations for each AI platform.

Model Context Protocol (MCP)

Model Context Protocol (MCP) is an open standard for connecting AI agents to external data sources, tools, and APIs via a vendor-neutral interface – enabling any AI client (Claude, Gemini, open-source models) to securely integrate with custom business systems without rebuilding integrations for each AI platform.


What is MCP?

MCP is the “OpenAPI for AI integrations.” It standardizes how AI agents discover and use tools, access data, and interact with external systems.

The Problem MCP Solves

Before MCP: Every AI + Tool integration required custom code:

  • Claude + GitHub = custom OAuth flow + API wrappers
  • Claude + Slack = custom webhook handlers
  • Claude + Database = custom SQL builders
  • Gemini + GitHub = different OAuth flow + API wrappers
  • (Repeat for every AI platform)

After MCP: Build once, use everywhere:

  • Build one MCP server connecting to your GitHub repo
  • Claude can talk to it (Claude.ai, Claude Code, Claude API)
  • Gemini CLI can talk to it (via Vertex AI)
  • Open-source models can talk to it (llama.cpp, Ollama)
  • Any future AI platform can talk to it

Architecture

MCP operates as a client-server protocol with explicit user approval:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
┌─────────────────────────────────────────────────────────────────┐
│ AI Client (Claude Desktop, Claude Code, Custom App)             │
│  - Discovers available tools                                     │
│  - Sends tool requests to user's chosen MCP servers             │
│  - Returns results to Claude                                    │
└────────────────────┬────────────────────────────────────────────┘
                     │ (User approves each action)
    ┌────────────────┴─────────────────────┐
    │ MCP Protocol (JSON-RPC over stdio)    │
    └────────────────┬─────────────────────┘
                     │
    ┌────────────────┴──────────────────────────────────────────────┐
    │ MCP Servers (Local or Remote)                                  │
    │                                                                 │
    │ ├─ Filesystem Server: read/write files, search, grep          │
    │ ├─ GitHub Server: fetch repos, list issues, create PRs        │
    │ ├─ Slack Server: post messages, fetch history, threads        │
    │ ├─ Database Server: execute queries, generate migrations      │
    │ ├─ Custom Internal Server: KMS, auth, compliance checks       │
    │ └─ (100+ community servers)                                   │
    └─────────────────────────────────────────────────────────────┘

Three Core Primitives

Primitive Purpose Example
Tools Actions Claude can perform “create_github_pr”, “send_slack_message”, “execute_sql_query”
Resources Data Claude can access/read “github_repo_contents”, “slack_channel_history”, “database_schema”
Prompts Templated instructions “code_review_template”, “incident_response_playbook”

Security Model (User Approval First)

Critical principle: Every action requires explicit user approval. MCP is never autonomous in sensitive contexts.

Default Behavior

  1. Tool discovery: Claude discovers available tools lazily (only when relevant to user’s request)
  2. Tool call: Claude decides to use a tool (e.g., “I need to check GitHub for this”)
  3. User approval: Dialog shows: “Claude wants to execute GitHub action ‘create_pr’. Approve? [Yes/No]”
  4. Execution: Only if user approves
  5. Transparency: User sees what was called, with what parameters, and what happened

Example Approval Flow

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
User: "Create a PR fixing the auth bug"

Claude: "I'll help. Let me fetch the current code and create a PR.
[ACTION PENDING] Fetch src/auth.ts from GitHub.
[Waiting for user approval...]"

[User sees in UI:
  Tool: github.readFile
  Repo: myorg/myapp
  File: src/auth.ts
  [APPROVE] [DENY]]

User: Approves

[Claude proceeds]

Why This Matters

  • Prevents prompt injection: Malicious content can’t trick Claude into running unauthorized actions
  • Audit trail: Every action is logged with user approval
  • User agency: You control what Claude does with your data/systems
  • Compliance: Meets regulatory requirements (especially financial, healthcare, legal)

Official MCP Servers (Production-Ready)

Filesystem Server

Read/write/search local files. Always available; no setup needed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Claude can automatically:
# - Read files: "Show me src/auth.ts"
# - Search: "Find all TODO comments in src/"
# - Write: "Create a new test file"
# - Execute shell commands: "Run npm test"

# Usage (Agent SDK):
from anthropic import Agent
from anthropic.tools import read_file, write_file, bash, grep

agent = Agent(
    model="claude-sonnet-4-20250514",
    tools=[read_file, write_file, bash, grep],
)

result = agent.run("Refactor src/utils.ts to use TypeScript strict mode")

GitHub Server

Clone repos, query issues, create PRs, manage workflow.

1
2
3
4
5
6
7
8
9
10
11
12
# Claude Desktop config: ~/.config/claude/claude_desktop_config.json
{
  "mcpServers": {
    "github": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "env": {
        "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}"
      }
    }
  }
}

Capabilities:

1
2
3
4
5
6
# Claude can now:
# - Read repo contents: "Show me the entire fastapi_project repo"
# - Query issues: "What's the status of issue #123?"
# - Create PRs: "Create a PR to fix the auth module"
# - Check workflows: "Is the CI passing?"
# - Merge PRs: "Merge PR #456 after tests pass"

Slack Server

Post messages, fetch channel history, manage threads.

1
2
3
4
5
6
7
8
9
10
11
12
{
  "mcpServers": {
    "slack": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-slack"],
      "env": {
        "SLACK_BOT_TOKEN": "${SLACK_TOKEN}",
        "SLACK_CHANNEL": "engineering"
      }
    }
  }
}

Capabilities:

1
2
3
4
5
# Claude can:
# - Post: "Send a summary of this week's bugs to #engineering"
# - Fetch: "What did the team say about deployment yesterday?"
# - Thread: "Start a thread about the new deployment process"
# - React: "Add a emoji to the latest deploy message"

Database Servers (PostgreSQL, MySQL)

Execute queries, generate migrations, run transactions.

1
2
3
4
5
6
7
8
9
10
11
{
  "mcpServers": {
    "postgres": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-postgres"],
      "env": {
        "DATABASE_URL": "postgresql://user:pass@localhost:5432/db"
      }
    }
  }
}

Capabilities:

1
2
3
4
5
# Claude can:
# - Query: "Get top 10 slowest queries by execution time"
# - Analyze: "Which tables are missing indexes?"
# - Migrate: "Generate migration to add user_role column to users table"
# - Optimize: "Recommend indexing strategy for our query patterns"

Other Official Servers

Server Use Case Status
Brave Search Web search without context-switching Production
Playwright Browser automation (screenshot, interact) Production
Postgres/MySQL Database queries and migrations Production
Jira Issue tracking, sprint management Production
GitLab Similar to GitHub Production
Linear Issue tracking alternative Production

Full list: modelcontextprotocol.io/servers


Building Custom MCP Servers

Build domain-specific servers connecting Claude to your internal systems.

Example 1: Internal KMS (Key Management System) Server

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
"""
MCP server exposing internal KMS to Claude.
Claude can request key rotations, encrypt data, generate new keys.
"""

import json
from typing import Any
from mcp.server import Server
from mcp.types import Tool, Resource, TextContent, ToolResult

app = Server("internal-kms")

# Define tools
@app.call_tool()
async def rotate_key(key_id: str, force: bool = False) -> ToolResult:
    """Rotate an encryption key."""
    # Call internal KMS API
    response = requests.post(
        "https://kms.internal/rotate",
        json={"key_id": key_id, "force": force},
        headers={"Authorization": f"Bearer {os.getenv('KMS_API_KEY')}"}
    )

    if response.status_code == 200:
        return ToolResult(
            content=[TextContent(
                type="text",
                text=f"Key {key_id} rotated successfully. New version: {response.json()['version']}"
            )],
            isError=False
        )
    else:
        return ToolResult(
            content=[TextContent(type="text", text=f"Failed to rotate: {response.text}")],
            isError=True
        )

@app.call_tool()
async def encrypt_data(data: str, key_id: str) -> ToolResult:
    """Encrypt data using KMS."""
    response = requests.post(
        "https://kms.internal/encrypt",
        json={"data": data, "key_id": key_id},
        headers={"Authorization": f"Bearer {os.getenv('KMS_API_KEY')}"}
    )

    if response.status_code == 200:
        return ToolResult(
            content=[TextContent(type="text", text=f"Encrypted: {response.json()['ciphertext']}")],
            isError=False
        )
    else:
        return ToolResult(
            content=[TextContent(type="text", text=f"Encryption failed: {response.text}")],
            isError=True
        )

# Register tools with descriptions
TOOLS = [
    Tool(
        name="rotate_key",
        description="Rotate an encryption key (new version generated)",
        inputSchema={
            "type": "object",
            "properties": {
                "key_id": {"type": "string", "description": "Key identifier"},
                "force": {"type": "boolean", "description": "Force rotation even if recently rotated"}
            },
            "required": ["key_id"]
        }
    ),
    Tool(
        name="encrypt_data",
        description="Encrypt data using KMS",
        inputSchema={
            "type": "object",
            "properties": {
                "data": {"type": "string", "description": "Data to encrypt"},
                "key_id": {"type": "string", "description": "Key identifier"}
            },
            "required": ["data", "key_id"]
        }
    ),
]

@app.list_tools()
async def list_tools() -> list[Tool]:
    return TOOLS

if __name__ == "__main__":
    app.run()

Configure in Claude Desktop:

1
2
3
4
5
6
7
8
9
10
11
12
{
  "mcpServers": {
    "internal-kms": {
      "command": "python",
      "args": ["/path/to/mcp_kms_server.py"],
      "env": {
        "KMS_API_KEY": "${KMS_API_KEY}",
        "KMS_ENDPOINT": "https://kms.internal"
      }
    }
  }
}

Usage:

1
2
3
4
5
6
7
User: "Rotate the customer database encryption key (key_id: db_v3)"

Claude: [ACTION PENDING] Call tool: rotate_key(key_id="db_v3")
[User approves]

Claude: Key db_v3 rotated successfully. New version: v4.
Next steps: Update database config to use key_v4 and verify no service interruptions.

Example 2: Compliance Policy Checker

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
"""
MCP server exposing compliance policies to Claude.
Claude can check if proposed changes violate policies.
"""

import json
from mcp.server import Server
from mcp.types import Tool, TextContent, ToolResult

app = Server("compliance-checker")

POLICIES = {
    "pii_handling": {
        "description": "PII data handling rules",
        "rules": [
            "Never log PII (SSN, credit card numbers, health data)",
            "PII must be encrypted at rest and in transit",
            "PII data must be deleted within 90 days of retention period",
        ]
    },
    "api_security": {
        "description": "API security requirements",
        "rules": [
            "All APIs must use HTTPS (no HTTP)",
            "APIs must implement rate limiting (100 req/min default)",
            "APIs must validate all input (no SQL injection, XSS)",
            "APIs must use API keys for authentication (no basic auth)",
        ]
    },
    "data_residency": {
        "description": "Data residency rules",
        "rules": [
            "US customer data must reside in US data centers",
            "EU customer data must comply with GDPR",
            "Cross-border data transfers require legal review",
        ]
    },
}

@app.call_tool()
async def check_compliance(code: str, policy: str) -> ToolResult:
    """Check if code snippet violates compliance policies."""

    if policy not in POLICIES:
        return ToolResult(
            content=[TextContent(type="text", text=f"Policy '{policy}' not found")],
            isError=True
        )

    policy_rules = POLICIES[policy]["rules"]
    violations = []

    # Simple keyword matching (real: use AST parsing or static analysis)
    for rule in policy_rules:
        if "PII" in rule.upper() and "print(" in code:
            violations.append(f"Violation: {rule} (found print statement)")
        elif "HTTP" in rule and "http://" in code:
            violations.append(f"Violation: {rule} (found unencrypted HTTP)")
        elif "HTTPS" in rule and "https://" not in code:
            violations.append(f"Violation: {rule} (missing HTTPS)")

    if violations:
        return ToolResult(
            content=[TextContent(type="text", text=f"Violations found:\n" + "\n".join(violations))],
            isError=False
        )
    else:
        return ToolResult(
            content=[TextContent(type="text", text=f"No violations found for {policy}")],
            isError=False
        )

@app.list_tools()
async def list_tools():
    return [
        Tool(
            name="check_compliance",
            description="Check code snippet against compliance policies",
            inputSchema={
                "type": "object",
                "properties": {
                    "code": {"type": "string", "description": "Code snippet to check"},
                    "policy": {"type": "string", "enum": list(POLICIES.keys())}
                },
                "required": ["code", "policy"]
            }
        )
    ]

if __name__ == "__main__":
    app.run()

Usage:

1
2
3
4
5
6
7
8
9
User: "Review this API endpoint for compliance before merging"

Claude: Let me check the code against our security and PII policies.
[Analyzes code]
[ACTION PENDING] Call check_compliance(code=endpoint_code, policy="api_security")
[User approves]

Claude: API security compliance check passed. No violations found.
However, I notice you're logging the request body -- recommend adding PII masking.

MCP in Production Systems

Pattern 1: Enterprise Data Access

1
2
Claude <-> MCP Server <-> Internal Databases/APIs
          (with OAuth, API keys, VPN tunneling)

Example: Financial services company

1
2
3
4
5
6
7
8
9
10
11
12
{
  "mcpServers": {
    "trading_platform": {
      "command": "python",
      "args": ["/internal/trading_mcp_server.py"],
      "env": {
        "API_KEY": "${TRADING_PLATFORM_API_KEY}",
        "COMPLIANCE_CHECK": "true"  # Automatic policy enforcement
      }
    }
  }
}

Claude can now:

1
"List all open positions in portfolio XYZ. Flag any that violate our position limit policy."

Pattern 2: Autonomous Infrastructure Management

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
  "mcpServers": {
    "aws": {
      "command": "npx",
      "args": ["-y", "@anthropic-mcp/server-aws"],
      "env": {
        "AWS_PROFILE": "default"
      }
    },
    "kubernetes": {
      "command": "npx",
      "args": ["-y", "@anthropic-mcp/server-kubernetes"],
      "env": {
        "KUBECONFIG": "${HOME}/.kube/config"
      }
    }
  }
}

Claude can:

1
2
3
4
"Scale the payment-processing deployment to 10 replicas.
Monitor CloudWatch metrics for next 5 minutes.
Report if error rate exceeds 2%.
If yes, rollback to previous version."

(With user approval for each action.)

Pattern 3: Team Collaboration

1
2
3
4
5
6
7
8
{
  "mcpServers": {
    "jira": {...},
    "github": {...},
    "slack": {...},
    "confluence": {...}
  }
}

Claude can orchestrate:

1
2
3
4
5
"Summarize this week's progress:
1. Fetch completed Jira tickets
2. Get merged PRs from GitHub
3. Compile into Confluence page
4. Post summary to #weekly-standup Slack channel"

Comparison: MCP vs. Traditional Integrations

Aspect MCP Custom Integration Traditional RPA
Setup time Hours (write MCP server) Weeks (custom code per platform) Weeks (BPM configuration)
Standards compliance Open standard (vendor-neutral) Proprietary Proprietary
Portability Move to new AI platform (Gemini, open source) Rebuild from scratch Rebuild from scratch
User approval Built-in (explicit) Custom implementation Sometimes missing
Cost Development cost once, reusable Per-integration cost Licensing + implementation
Maintainability Centralized (one MCP server) Fragmented (multiple integrations) Vendor lock-in

Key Properties

Property Value Notes
Official servers 30+ GitHub, Slack, Postgres, Jira, Playwright, Brave, etc.
Community servers 100+ Growing ecosystem at github.com/modelcontextprotocol
Build a custom server 2-4 hours Depends on complexity of external system
MCP latency <100ms (local), 100-500ms (remote) Over JSON-RPC
User approval overhead 5-10 seconds Dialog shows tool call, user approves
Backward compatibility Guaranteed Spec promises stability (v1.0+)
Open source Yes MIT license; github.com/modelcontextprotocol

Roadmap & Future

Current (Feb 2025):

  • Tools and Resources primitives
  • Prompts (templated instructions)
  • Official servers (30+)
  • Claude Desktop, Claude Code integration

Planned (2025-2026):

  • Web browsing server (Playwright integration)
  • More official servers (Salesforce, HubSpot, NetSuite)
  • Server discoverability/marketplace (install MCP servers from central registry)
  • Gemini CLI support (already partial support via Vertex AI)
  • Open-source model support (llama.cpp, Ollama plugins)

Vision: MCP becomes the universal standard for AI-tool integrations (like OpenAPI for REST APIs).


Real-World Example: Venture Due Diligence

Scenario: Engineering Leader (investor/advisor) evaluating Series A startup

1
2
3
4
5
6
7
8
9
10
# Custom MCP servers for due diligence
{
  "mcpServers": {
    "github": {...},
    "linear": {...},
    "sentry": {...},  # Error tracking
    "datadog": {...}, # Monitoring
    "stripe": {...}   # Revenue/payments
  }
}

Evaluation workflow:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Analyst: "Complete technical due diligence for CompanyXYZ"

Claude: [with MCP servers connected]

1. Code Quality:
   "Fetch CompanyXYZ/backend repo.
    Analyze: test coverage, architectural patterns, security issues.
    Estimate refactor effort if patterns are poor."

2. Engineering Velocity:
   "Get Linear issues (completed, in progress).
    Analyze: velocity trend, cycle time, deployment frequency."

3. Reliability:
   "Check Sentry for error rates.
    Get Datadog metrics: uptime, latency, resource usage.
    Are we confident in production stability?"

4. Business Metrics:
   "Query Stripe for: MRR, churn rate, customer acquisition cost.
    Analyze growth trajectory."

5. Report:
   "Compile findings into due diligence report:
    - Risk assessment (technical debt, reliability, growth)
    - Valuation recommendation
    - Integration risks post-acquisition"

Result: Complete technical assessment in 30-60 minutes vs. 1-2 weeks of manual review.


When to Build an MCP Server

Build if:

  • You have an internal system Claude needs to access (KMS, compliance engine, internal APIs)
  • You want Claude + Gemini to work with the same integration
  • You’re building long-term AI tooling (avoid lock-in to single AI platform)
  • Your team needs multiple integrations to a single system (GitHub, Jira, databases)

Skip if:

  • The official MCP server already exists and is sufficient
  • The system is simple enough to use via direct API in Agent SDK
  • You need custom business logic (use Agent SDK instead)

References

Official Documentation:

Guides & Tutorials:

  • “Building Your First MCP Server” (step-by-step)
  • “MCP Security Best Practices” (auth, data access, user approval)
  • “Deploying MCP Servers in Production” (Docker, managed services)

Video Resources:

  • Anthropic: “Model Context Protocol Deep Dive” (60 min technical)
  • “Building a Custom MCP Server” (hands-on tutorial, 30 min)

Community & Code:


Last Updated: Feb 2025

This post is licensed under CC BY 4.0 by the author.