Model Context Protocol: The Standard That’s Changing How AI Tools Connect to Everything

Model Context Protocol: The Standard That's Changing How AI Tools Connect to Everything

When AI models need to interact with the real world — read files, query databases, call APIs, control software — there’s been no standard way to do it. Every tool had its own integration pattern. Anthropic released the Model Context Protocol (MCP) in late 2024, and it’s gaining rapid adoption as the emerging standard for AI-tool integration.

By February 2026, MCP support has been added to Claude Desktop, Cursor, Claude Code, Zed, and dozens of third-party integrations. If you’re building tools for AI or building AI that uses tools, MCP is worth understanding.

What MCP Is

MCP is a protocol — a specification for how AI models communicate with external systems. It defines:

  • How a host application (like Claude Desktop or Cursor) connects to MCP servers
  • What capabilities MCP servers can expose: tools (functions the AI can call), resources (data the AI can read), and prompts (pre-built prompt templates)
  • How the AI model requests to use these capabilities
  • How results are returned

The key insight: by standardizing this interface, any MCP server works with any MCP client. Build one MCP server for your database, and Claude, Cursor, and any other MCP client can use it without additional integration work.

MCP Host (Claude Desktop, Cursor, etc.)
    ↓
MCP Client (built into the host)
    ↓
MCP Protocol (JSON-RPC over stdio or SSE)
    ↓
MCP Server (your implementation)
    ↓
External System (database, API, filesystem, etc.)

Building an MCP Server

MCP servers can be built in any language. The official SDKs are for Python and TypeScript, with community SDKs for Go and other languages.

A minimal MCP server in Python:

#!/usr/bin/env python3
"""
MCP server that provides tools for querying a Kubernetes cluster
"""
import asyncio
import subprocess
import json
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent

server = Server("kubernetes-tools")

@server.list_tools()
async def list_tools() -> list[Tool]:
    return [
        Tool(
            name="kubectl_get",
            description="Get Kubernetes resources. Returns resource information as YAML.",
            inputSchema={
                "type": "object",
                "properties": {
                    "resource": {
                        "type": "string",
                        "description": "Resource type (pods, services, deployments, etc.)"
                    },
                    "namespace": {
                        "type": "string",
                        "description": "Namespace (optional, defaults to all namespaces)"
                    },
                    "name": {
                        "type": "string",
                        "description": "Resource name (optional)"
                    },
                    "output": {
                        "type": "string",
                        "enum": ["yaml", "json", "wide"],
                        "description": "Output format"
                    }
                },
                "required": ["resource"]
            }
        ),
        Tool(
            name="kubectl_describe",
            description="Describe a Kubernetes resource in detail",
            inputSchema={
                "type": "object",
                "properties": {
                    "resource": {"type": "string"},
                    "name": {"type": "string"},
                    "namespace": {"type": "string"}
                },
                "required": ["resource", "name"]
            }
        ),
        Tool(
            name="kubectl_logs",
            description="Get logs from a pod",
            inputSchema={
                "type": "object",
                "properties": {
                    "pod": {"type": "string"},
                    "namespace": {"type": "string", "default": "default"},
                    "container": {"type": "string"},
                    "tail": {"type": "integer", "default": 50}
                },
                "required": ["pod"]
            }
        )
    ]

@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
    if name == "kubectl_get":
        cmd = ["kubectl", "get", arguments["resource"]]

        if name_arg := arguments.get("name"):
            cmd.append(name_arg)

        if ns := arguments.get("namespace"):
            cmd.extend(["-n", ns])
        else:
            cmd.append("--all-namespaces")

        output_format = arguments.get("output", "yaml")
        cmd.extend(["-o", output_format])

        result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
        content = result.stdout if result.returncode == 0 else result.stderr
        return [TextContent(type="text", text=content)]

    elif name == "kubectl_describe":
        cmd = ["kubectl", "describe", arguments["resource"], arguments["name"]]
        if ns := arguments.get("namespace"):
            cmd.extend(["-n", ns])

        result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
        content = result.stdout if result.returncode == 0 else result.stderr
        return [TextContent(type="text", text=content)]

    elif name == "kubectl_logs":
        cmd = ["kubectl", "logs", arguments["pod"]]
        cmd.extend(["-n", arguments.get("namespace", "default")])
        if container := arguments.get("container"):
            cmd.extend(["-c", container])
        cmd.extend(["--tail", str(arguments.get("tail", 50))])

        result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
        content = result.stdout if result.returncode == 0 else result.stderr
        return [TextContent(type="text", text=content)]

    return [TextContent(type="text", text=f"Unknown tool: {name}")]

async def main():
    async with stdio_server() as (read_stream, write_stream):
        await server.run(read_stream, write_stream, server.create_initialization_options())

if __name__ == "__main__":
    asyncio.run(main())

Adding Resources

Resources are data sources the AI can read — think of them as files or documents:

from mcp.types import Resource, ResourceContents

@server.list_resources()
async def list_resources() -> list[Resource]:
    return [
        Resource(
            uri="kubernetes://cluster/overview",
            name="Cluster Overview",
            description="High-level overview of cluster health and resource usage",
            mimeType="text/plain"
        ),
        Resource(
            uri="kubernetes://cluster/events",
            name="Recent Cluster Events",
            description="Last 50 cluster events",
            mimeType="text/plain"
        )
    ]

@server.read_resource()
async def read_resource(uri: str) -> ResourceContents:
    if uri == "kubernetes://cluster/overview":
        # Get cluster info
        result = subprocess.run(
            ["kubectl", "cluster-info"],
            capture_output=True, text=True
        )
        nodes_result = subprocess.run(
            ["kubectl", "get", "nodes", "-o", "wide"],
            capture_output=True, text=True
        )
        content = f"Cluster Info:\n{result.stdout}\n\nNodes:\n{nodes_result.stdout}"
        return ResourceContents(uri=uri, mimeType="text/plain", text=content)

    elif uri == "kubernetes://cluster/events":
        result = subprocess.run(
            ["kubectl", "get", "events", "--all-namespaces",
             "--sort-by=.lastTimestamp", "--tail=50"],
            capture_output=True, text=True
        )
        return ResourceContents(uri=uri, mimeType="text/plain", text=result.stdout)

Configuring Claude Desktop to Use MCP

Once you have an MCP server, add it to Claude Desktop’s configuration:

// ~/Library/Application Support/Claude/claude_desktop_config.json (macOS)
// %APPDATA%\Claude\claude_desktop_config.json (Windows)
{
  "mcpServers": {
    "kubernetes": {
      "command": "python3",
      "args": ["/path/to/your/mcp-server.py"],
      "env": {
        "KUBECONFIG": "/home/user/.kube/config"
      }
    },
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/home/user/projects"]
    },
    "github": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "env": {
        "GITHUB_TOKEN": "your-github-token"
      }
    }
  }
}

After restarting Claude Desktop, you can ask Claude: “What pods are failing in my production namespace?” and it will call your kubectl_get tool to find out.

Available MCP Servers

An ecosystem of pre-built MCP servers is growing rapidly:

Server What it does
@modelcontextprotocol/server-filesystem Read/write local files
@modelcontextprotocol/server-github GitHub repos, issues, PRs
@modelcontextprotocol/server-postgres PostgreSQL queries
@modelcontextprotocol/server-slack Slack messages and channels
@modelcontextprotocol/server-brave-search Web search via Brave
mcp-server-kubernetes Kubernetes cluster management
mcp-server-terraform Terraform state and plans

Browse the full list at github.com/modelcontextprotocol/servers.

MCP in Automated Systems

MCP isn’t just for interactive use. You can use MCP programmatically when building AI agents:

# Using MCP with the Anthropic Python SDK
from anthropic import Anthropic
import anthropic.types as types

# The SDK supports MCP tool schemas directly
client = Anthropic()

# Manually pass MCP tool definitions to the API
tools = [
    {
        "name": "kubectl_get",
        "description": "Get Kubernetes resources",
        "input_schema": {
            "type": "object",
            "properties": {
                "resource": {"type": "string"},
                "namespace": {"type": "string"}
            },
            "required": ["resource"]
        }
    }
]

response = client.messages.create(
    model="claude-opus-4-6",
    max_tokens=1024,
    tools=tools,
    messages=[{
        "role": "user",
        "content": "List all failing pods across all namespaces"
    }]
)

Security Considerations for MCP Servers

MCP servers run locally and have the same permissions as the user running them. Key security considerations:

Limit scope: Only expose the operations the AI actually needs. A read-only kubectl wrapper is safer than one with apply/delete capabilities.

Validate inputs: Don’t blindly pass AI-provided arguments to subprocess calls. Validate and sanitize:

import re

def validate_namespace(namespace: str) -> str:
    """Validate Kubernetes namespace name"""
    if not re.match(r'^[a-z0-9][a-z0-9-]*[a-z0-9]$', namespace):
        raise ValueError(f"Invalid namespace: {namespace}")
    return namespace

Audit logging: Log all tool calls with their arguments:

import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("mcp-server")

@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
    logger.info(f"Tool called: {name} with args: {arguments}")
    # ... rest of implementation

Principle of least privilege: Run MCP servers with a service account or kubeconfig that has only the permissions needed.

Conclusion

Model Context Protocol is solving the integration problem for AI tooling. Instead of every team building their own tool integration, MCP provides a standard that works across AI hosts and servers. The ecosystem is growing rapidly — database integrations, cloud provider APIs, development tools, communication platforms.

If you’re building internal tooling for AI use, building an MCP server is the path that makes your work reusable across whatever AI client your team uses. If you’re building AI integrations for your cluster or services, there’s likely already an MCP server that does what you need.

Start with the filesystem and GitHub MCP servers in Claude Desktop to get a feel for the model, then build your own for your specific needs.

Share this post: LinkedIn Reddit WhatsApp Mastodon
Jesse Borden

Jesse Borden

Software Engineer with an interest in hands on learning

I have several years of professional Information Technology (IT) experience leading staff and projects within the Department of War (DOW). I have managed Service Desk, Web Application Development, and System Administration teams. My two greatest passions are learning and conti...