Model Context Protocol (MCP) Guide: Build Custom Claude Tools in Python

Model Context Protocol (MCP) is an open-source standard introduced by Anthropic that connects AI models like Claude to external tools, databases, and APIs. Instead of building custom integrations for every tool, MCP acts like a universal USB-C port: an AI application acts as an MCP host, connecting directly to lightweight MCP servers that expose data, executable functions, and pre-built workflows.

Whether you are running Claude for Desktop, using Claude Code in terminal workflows, or building custom agentic pipelines, understanding how to construct and debug custom MCP servers is one of the most practical skills in modern AI engineering.

How Model Context Protocol Works

Traditional AI integration requires writing hardcoded functions or custom function-calling schemas for each LLM provider. Model Context Protocol standardizes this communication using a JSON-RPC 2.0 protocol layer. The architecture consists of three core components:

  • MCP Host: The client application hosting the LLM session (e.g., Claude Desktop, Claude Code, Cursor, or your custom application). The host manages security permissions, initiates connection sessions, and renders available tools to the LLM context.
  • MCP Server: A lightweight background process or microservice that exposes capabilities to the host. Servers can run locally on your system or remotely over HTTP.
  • Capabilities: The three standard primitives offered by a server:
    • Tools: Executable functions that perform actions or fetch real-time data (e.g., executing a database query or running a shell script).
    • Resources: Read-only file-like data objects attached to the conversation context.
    • Prompts: Reusable prompt templates that guide LLM multi-step workflows.

Transport Modes: STDIO vs SSE

MCP supports two main transport channels depending on where your server resides:

Transport ModePrimary Use CaseCommunication Channel
STDIO (Standard I/O)Local development, Claude Desktop, local scriptsStandard input (stdin) & standard output (stdout)
SSE (Server-Sent Events)Remote cloud services, team APIs, microservicesHTTP POST and Server-Sent Events stream

Building a Custom Python MCP Server

The fastest way to build an MCP server in Python is using the official mcp library and its high-level FastMCP wrapper. First, set up your project environment using uv or standard pip:

# Install MCP SDK
pip install mcp

Here is a complete, working Python script for a system status server (server.py) that exposes a tool for inspecting system disk space and running diagnostic checks:

import sys
import logging
import shutil
from mcp.server.fastmcp import FastMCP

# CRITICAL LOGGING CONFIGURATION
# Always route logs to stderr so stdout remains reserved for clean JSON-RPC protocol messages.
logging.basicConfig(
    stream=sys.stderr,
    level=logging.INFO,
    format="%(asctime)s - %(levelname)s - %(message)s"
)

# Initialize FastMCP Server
mcp = FastMCP("System Diagnostics Tool")

@mcp.tool()
def check_disk_space(path: str = "/") -> str:
    """Check total, used, and free disk space for a given path."""
    logging.info(f"Checking disk space for path: {path}")
    total, used, free = shutil.disk_usage(path)
    gb = 1024 ** 3
    return f"Disk Space [{path}]: Total={total/gb:.2f}GB, Used={used/gb:.2f}GB, Free={free/gb:.2f}GB"

@mcp.tool()
def ping_service(host: str) -> str:
    """Simulate a connectivity ping check to a target host."""
    logging.info(f"Ping requested for: {host}")
    return f"Host {host} is reachable and responding within normal latency."

if __name__ == "__main__":
    mcp.run(transport="stdio")

The Critical Developer Pitfall: The Stdout Trap

The single most common bug when developing local STDIO MCP servers is using standard print() statements or letting third-party libraries output unformatted text to standard output (stdout).

Because the MCP host (such as Claude Desktop) communicates with your Python process by reading JSON-RPC 2.0 objects directly from stdout, any stray line written to stdout—such as print("Database connected")—corrupts the JSON stream. This causes immediate connection failures with errors like JSON-RPC parse error or Unexpected token.

To ensure your server runs reliably:

  • Never use `print()`: Route all diagnostic text, warnings, and error logs through Python’s standard logging module configured to write to sys.stderr.
  • Silence Verbose Libraries: If an imported library logs to stdout by default, redirect its logger output to stderr before running `mcp.run()`.

Configuring Claude Desktop to Use Your Server

To connect your Python server to Claude for Desktop, edit your configuration file located at:

  • Windows: %APPDATA%\Claude\claude_desktop_config.json
  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json

Add your server to the mcpServers section:

{
  "mcpServers": {
    "system-diagnostics": {
      "command": "python",
      "args": [
        "C:/path/to/your/server.py"
      ]
    }
  }
}

After saving the file, restart Claude for Desktop. A hammer icon will appear in the input bar, showing your custom tools (check_disk_space and ping_service) ready for Claude to invoke automatically during your chat session.

Connecting MCP to Modern Workflows

Combining custom MCP servers with disciplined prompt patterns makes AI automation significantly more reliable. If you are structuring complex prompts for multi-step agent workflows, check out our guide on ChatGPT prompts that actually work to build structured prompt frameworks. Additionally, if you are evaluating which frontier model best handles function execution and tool call reasoning, read our breakdown of Claude vs ChatGPT vs Gemini.

Frequently Asked Questions (FAQ)

What is Model Context Protocol (MCP)?

Model Context Protocol (MCP) is an open-source standard introduced by Anthropic that connects AI applications to external data sources, security tools, and APIs using a standardized JSON-RPC communication specification.

What is the difference between STDIO and SSE transport in MCP?

STDIO transport communicates over standard input and standard output for local applications running on the same machine, whereas SSE (Server-Sent Events) streams messages over HTTP for remote services and cloud microservices.

Why does print() break an MCP server in Python?

Because STDIO-based MCP servers rely on stdout exclusively for structured JSON-RPC messages, calling print() sends raw text to stdout, corrupting the message stream and breaking host parsing.

Can I use MCP with tools other than Claude?

Yes. Although initiated by Anthropic, MCP is an open-source specification designed for any AI assistant, IDE extension, or LLM host application that implements the protocol.

Posted Under

Leave a Reply