ProjectDevOps

Model Context Protocol (MCP) — A Practical End-to-End Guide

Aabhigyan709👁️ 10
Model Context Protocol (MCP) — A Practical End-to-End Guide

MCP (Model Context Protocol) is an open standard that lets AI apps (e.g., Claude Desktop, IDEs, custom agents) safely connect to tools and data through servers that expose resources, tools, and prompts. Think of it like a universal “USB-C port” for AI apps—plug in a server and your AI gains new skills without bespoke integrations. (Model Context Protocol)

In this tutorial you’ll:

  • Understand core MCP concepts (client ↔ server, tools/resources/prompts).

  • Spin up a minimal Python MCP server and a Node.js MCP server.

  • Connect locally from Claude Desktop via JSON config.

  • Run everything in Docker (plus a Compose file for multi-server dev).

  • Try practical tasks (filesystem, HTTP call, Docker control).

  • Lock down permissions and debug common issues.

  • If you’re new to MCP, skim the official overview first to see how the parts fit together. (Model Context Protocol)


    1) Prerequisites

    • Python 3.10+ and/or Node 18+

  • Claude Desktop (optional but convenient client)

  • Docker + Docker Compose (optional, for containerized workflows)

  • A terminal and a code editor

  • Official SDKs and docs: Python SDK, Node SDK, quickstarts, and Claude configuration. (GitHub)


    2) MCP in 3 minutes (Concepts)

    • Server: A process you control that exposes:

    • Tools — callable functions (e.g., “search_web”, “create_repo”).

  • Resources — read-only items (e.g., files, DB rows, API responses).

  • Prompts — reusable prompt templates.

  • Client: The AI app (Claude Desktop, your own chatbot) that connects to servers via transports like stdio or HTTP/SSE. (Model Context Protocol)


  • 3) Quick Start: Python MCP Server

    Create a tiny server with one tool (say_hello) and one resource (/time).

    mkdir mcp-python-hello && cd mcp-python-hello
    python -m venv .venv && source .venv/bin/activate  # Windows: .venv\Scripts\activate
    pip install mcp

    server.py

    import asyncio, json, datetime
    from mcp.server import Server
    from mcp.types import Tool, TextContent
    from mcp.server.stdio import stdio_server
    
    server = Server("python-hello")
    
    # ---- Tools ----
    @server.tool()
    async def say_hello(name: str) -> str:
        """Return a friendly greeting."""
        return f"Hello, {name}! 👋"
    
    # ---- Resources ----
    @server.resource("local://time")
    async def local_time():
        now = datetime.datetime.now().isoformat()
        return TextContent(type="text", text=f"Local time is {now}")
    
    async def main():
        tools = [Tool(name="say_hello", description="Greet by name", inputSchema={"type":"object","properties":{"name":{"type":"string"}},"required":["name"]})]
        async with stdio_server(server, tools=tools):
            await asyncio.Event().wait()
    
    if __name__ == "__main__":
        asyncio.run(main())

    Run it:

    python server.py

    This launches a stdio MCP server named python-hello. You’ll connect a client next. (The Python SDK handles protocol messaging/handshakes for you.) (GitHub)


    4) Quick Start: Node.js MCP Server

    mkdir mcp-node-hello && cd mcp-node-hello
    npm init -y
    npm install @modelcontextprotocol/sdk

    server.mjs

    import { StdioServer, Tool } from "@modelcontextprotocol/sdk/server";
    
    const server = new StdioServer({
      name: "node-hello",
    });
    
    server.tool(
      new Tool({
        name: "echo",
        description: "Echo a message back.",
        inputSchema: {
          type: "object",
          properties: { message: { type: "string" } },
          required: ["message"],
        },
        handler: async ({ message }) => `You said: ${message}`,
      })
    );
    
    // Minimal resource example:
    server.resource({
      uri: "local://info",
      get: async () => ({
        type: "text",
        text: "Node MCP server up and running.",
      }),
    });
    
    await server.start();

    Run it:

    node server.mjs

    This exposes a tool (echo) and a resource (local://info) over stdio using the official Node SDK. (Model Context Protocol)


    5) Connect from Claude Desktop (Local)

    Claude Desktop can auto-start and connect to your local MCP servers via a simple JSON config. Create the config file per the docs, pointing to your server binaries and arguments. (Model Context Protocol)

    macOS

    ~/Library/Application Support/Claude/claude_desktop_config.json

    Windows

    %AppData%\Claude\claude_desktop_config.json

    Linux

    ~/.config/Claude/claude_desktop_config.json

    Example config (both Python and Node servers):

    {
      "mcpServers": {
        "python-hello": {
          "command": "python",
          "args": ["server.py"],
          "cwd": "/absolute/path/to/mcp-python-hello"
        },
        "node-hello": {
          "command": "node",
          "args": ["server.mjs"],
          "cwd": "/absolute/path/to/mcp-node-hello"
        }
      }
    }

    Restart Claude Desktop. In a new chat, try:

    “Use the python-hello server’s say_hello tool with name ‘Abhigyan’.”
    “Read the local://info resource from the node-hello server.”

    (Claude Desktop’s integration with MCP servers and configuration is described in the official docs.) (Claude Docs)


    6) Add Real Tasks

    Here are three practical tools you can add to any server:

    A) Filesystem read (resource)

    Expose a safe directory and allow read-only access (never expose secrets):

    Python

    import pathlib
    base = pathlib.Path.home() / "mcp-safe"
    
    @server.resource("fs://readme")
    async def readme():
        p = base / "README.txt"
        text = p.read_text()
        return TextContent(type="text", text=text)

    B) HTTP fetch (tool)

    import httpx
    @server.tool()
    async def fetch_url(url: str) -> str:
        """Fetch URL (GET) and return first 500 chars."""
        async with httpx.AsyncClient(timeout=10) as client:
            r = await client.get(url, follow_redirects=True)
        return r.text[:500]

    C) Docker control (tool) — via a dedicated Docker MCP server

    Instead of granting shell, point your client to a Docker MCP server which exposes safe operations like listing containers, starting/stopping, composing stacks. Community examples exist and Docker’s blog covers production patterns. (GitHub)


    7) Run MCP Servers in Docker

    Containerizing servers keeps environments consistent and portable. Docker has showcased MCP usage patterns, and you can package your own with a minimal image. (Docker)

    Dockerfile (Python server)

    FROM python:3.12-slim
    
    WORKDIR /app
    COPY server.py /app/server.py
    
    # Optional: httpx for the fetch tool
    RUN pip install --no-cache-dir mcp httpx
    
    # Run as non-root for safety
    RUN useradd -m appuser
    USER appuser
    
    ENTRYPOINT ["python", "server.py"]

    Build & run:

    docker build -t mcp-python-hello:latest .
    docker run --rm -it mcp-python-hello:latest

    Tip: If your server needs files, mount a volume:

    docker run --rm -it -v "$PWD/data:/app/data:ro" mcp-python-hello:latest

    Docker Compose (Python + Node together)

    docker-compose.yml

    services:
      mcp-python-hello:
        build:
          context: ./mcp-python-hello
        tty: true
    
      mcp-node-hello:
        build:
          context: ./mcp-node-hello
        tty: true

    Start both:

    docker compose up --build

    Then point Claude Desktop to run these via docker exec (advanced), or more simply run them on your host and let Claude Desktop manage startup via stdio as shown earlier. For production pipelines and catalogs of MCP servers, see community write-ups on running MCP servers from Docker Desktop. (Medium)


    8) Using an MCP Client (DIY)

    You don’t have to use Claude Desktop—you can write your own client that connects to any MCP server. The official quickstart shows how to build an LLM chatbot client that speaks MCP (Python/Node/Java/Kotlin/C#). (Model Context Protocol)

    • Parse server capabilities.

  • List tools/resources/prompts.

  • Call tools with structured input and stream responses.

  • Route model messages ↔ MCP messages.


  • 9) Security & Permissions

    • Least privilege: expose only the minimal directory, DB, or API scopes your use case needs.

  • User confirmation: MCP clients typically surface tool calls for approval before execution.

  • Network egress: if you allow HTTP, restrict destinations or filter content.

  • Auditability: log tool invocations and inputs/outputs (sans secrets).

  • Desktop Extensions: for Claude, packaging servers as “Desktop Extensions” provides one-click installation and a permissioned UX. (Anthropic)


  • 10) Troubleshooting

    • Server never appears in client
      Double-check your claude_desktop_config.json path and JSON syntax; ensure commands are resolvable (absolute cwd, correct python/node). (Model Context Protocol)

  • Transport errors
    Start with stdio (simplest). If using HTTP/SSE, confirm ports and CORS per SDK docs. (GitHub)

  • Tool schema mismatch
    Ensure your inputSchema matches what the client sends. Use simple types first (string, number) and add objects incrementally. (Model Context Protocol)

  • Docker container exits immediately
    Your entrypoint likely finished. Run servers in the foreground (no daemons), or add tty: true in Compose while developing. (Docker)


  • 11) Where to Go Next

  • SDKs & templates — Python/Node repos and quickstarts. (GitHub)

  • Claude Desktop config — connecting local servers, Desktop Extensions. (Model Context Protocol)

  • Docker + MCP — blog posts and examples for packaging & production. (Docker)

  • Ecosystem news & case studies — launch posts and coverage. (Anthropic)

  • Comments (0)

    No comments yet. Be the first to share your thoughts!