Building an MCP Server — Python FastMCP

MCP (Model Context Protocol) lets Claude call custom tools you define. FastMCP is the simplest Python way to build one.


Minimal server

from mcp.server.fastmcp import FastMCP
from pydantic import BaseModel, Field
import json
 
mcp = FastMCP("my_mcp")
 
class AddInput(BaseModel):
    a: float = Field(..., description="First number")
    b: float = Field(..., description="Second number")
 
@mcp.tool(
    name="add_numbers",
    annotations={
        "readOnlyHint": True,
        "destructiveHint": False,
        "idempotentHint": True,
        "openWorldHint": False
    }
)
async def add_numbers(params: AddInput) -> str:
    """Add two numbers together."""
    return json.dumps({"result": params.a + params.b})
 
if __name__ == "__main__":
    mcp.run()   # stdio transport by default

Install

pip install "mcp[cli]" httpx pydantic

Test with Inspector

npx @modelcontextprotocol/inspector python my_server.py

Opens browser UI — call tools manually and inspect responses.


Connect to Claude Desktop

~/Library/Application Support/Claude/claude_desktop_config.json (Mac):

{
  "mcpServers": {
    "my_mcp": {
      "command": "python",
      "args": ["/absolute/path/to/my_server.py"]
    }
  }
}

Restart Claude Desktop after editing.


Transport options

TransportWhen to use
mcp.run()Local / Claude Desktop (stdio)
mcp.run(transport="streamable_http", port=8000)Remote / shared server

Tool annotation guide

AnnotationMeaning
readOnlyHint: trueTool doesn’t modify anything
destructiveHint: trueTool deletes/overwrites data
idempotentHint: trueSafe to call multiple times
openWorldHint: trueTool talks to external services

Updates — 2026-05-13

Email MCP (SMTP via Gmail)

Full working example — MCP server that exposes send_email as a Claude-callable tool:

# email_mcp.py
from mcp.server.fastmcp import FastMCP
import smtplib
from email.mime.text import MIMEText
 
mcp = FastMCP("email-server")
 
@mcp.tool()
def send_email(to: str, subject: str, body: str) -> str:
    """Send an email via Gmail SMTP."""
    msg = MIMEText(body)
    msg["Subject"] = subject
    msg["From"] = "you@gmail.com"
    msg["To"] = to
 
    with smtplib.SMTP_SSL("smtp.gmail.com", 465) as smtp:
        smtp.login("you@gmail.com", "APP_PASSWORD_HERE")
        smtp.send_message(msg)
    return f"Email sent to {to}"
 
if __name__ == "__main__":
    mcp.run()

Gmail setup: Use an App Password (not your account password). Google Account → Security → 2-Step Verification → App passwords.

Register in claude_desktop_config.json:

{
  "mcpServers": {
    "email": {
      "command": "python",
      "args": ["/path/to/email_mcp.py"]
    }
  }
}

Key advantage over skills/scripts: Claude can call send_email as a native tool call mid-task (e.g., at the end of the daily PKM run) without needing a separate scripted trigger.