Claude Tools

Custom MCP Servers with Python: Supercharge Claude's Tool Ecosystem

Supercharge Claude AI by building custom MCP servers in Python. This step-by-step guide with Flask examples shows how to create tools Claude can call for extended capabilities.

J

Jennifer Yu

Workflow Automation Specialist

December 18, 2025 min read
Share:

Introduction

Claude AI, powered by Anthropic's advanced models like Opus, Sonnet, and Haiku, excels at reasoning, coding, and complex tasks. But to truly unlock its potential in real-world applications, you need to extend it with external tools. Enter Model Context Protocol (MCP) servers—lightweight HTTP servers that expose custom functions as callable tools for Claude.

MCP servers bridge Claude's native abilities with your data sources, APIs, and computations. Whether you're querying databases, scraping websites, or integrating third-party services, custom MCP servers make Claude more powerful and context-aware. In this guide, we'll build production-ready MCP servers using Python and Flask, deploy them, and integrate with Claude's ecosystem.

This is perfect for developers using Claude API, Claude Code CLI, or building AI agents.

What is MCP?

MCP is Anthropic's protocol for tool integration, allowing Claude to discover, call, and receive results from external functions dynamically. Key features:

  • Tool Discovery: Claude queries /mcp/tools to list available tools with schemas.
  • Tool Execution: Claude sends calls to /mcp/call with JSON payloads.
  • Streaming Support: Real-time responses for long-running tasks.
  • Stateful Sessions: Optional context persistence across calls.

Unlike generic APIs, MCP is optimized for LLMs: schemas use JSON Schema, calls are batched, and errors are structured. It's used in Claude Code, agents, and custom integrations.

Why Build Custom MCP Servers?

  • Claude-Specific: Native support in Claude API—no adapters needed.
  • Flexibility: Run anywhere (local, cloud, edge).
  • Scalability: Handle enterprise loads with async Python.
  • Security: Fine-grained auth and validation.

Real-world use cases:

  • Engineering: Database queries, code execution sandboxes.
  • Marketing: CRM lookups, analytics fetches.
  • HR: Employee data retrieval, compliance checks.

Prerequisites

  • Python 3.10+
  • Familiarity with Flask/FastAPI (we'll use Flask for simplicity)
  • Claude API key (from console.anthropic.com)
  • Basic knowledge of JSON Schema

Install dependencies:

pip install flask requests anthropic pydantic

Step 1: Set Up Your MCP Server Skeleton

Create mcp_server.py:

import json
from flask import Flask, request, jsonify
from pydantic import BaseModel

app = Flask(__name__)

class ToolSchema(BaseModel):
    name: str
    description: str
    inputSchema: dict

TOOLS = []  # Populate with your tools

@app.route('/mcp/tools', methods=['GET'])
def list_tools():
    return jsonify([{
        'name': tool.name,
        'description': tool.description,
        'inputSchema': tool.inputSchema
    } for tool in TOOLS])

@app.route('/mcp/call', methods=['POST'])
def call_tool():
    data = request.json
    tool_name = data['name']
    args = data['arguments']
    # Execute tool logic here
    result = {'content': 'Tool result'}
    return jsonify(result)

if __name__ == '__main__():
    app.run(debug=True, port=5000)

Run it: python mcp_server.py. Test with curl http://localhost:5000/mcp/tools.

Step 2: Implement Your First Tool - Calculator

Extend with a calculator tool. Define schema for addition.

Add to TOOLS:

calc_tool = ToolSchema(
    name='calculator',
    description='Add two numbers',
    inputSchema={
        'type': 'object',
        'properties': {
            'a': {'type': 'number'},
            'b': {'type': 'number'}
        },
        'required': ['a', 'b']
    }
)
TOOLS.append(calc_tool)

In /mcp/call:

if tool_name == 'calculator':
    a = args['a']
    b = args['b']
    return jsonify({'content': str(a + b)})

Test:

curl -X POST http://localhost:5000/mcp/call \
  -H "Content-Type: application/json" \
  -d '{"name": "calculator", "arguments": {"a": 5, "b": 3}}'

Output: {"content": "8"}

Step 3: Advanced Tool - Weather Lookup

Integrate OpenWeatherMap (get free API key).

New tool:

import requests

weather_tool = ToolSchema(
    name='get_weather',
    description='Get current weather for a city',
    inputSchema={
        'type': 'object',
        'properties': {
            'city': {'type': 'string'}
        },
        'required': ['city']
    }
)
TOOLS.append(weather_tool)

# In call_tool:
if tool_name == 'get_weather':
    city = args['city']
    api_key = 'YOUR_API_KEY'
    url = f'https://api.openweathermap.org/data/2.5/weather?q={city}&appid={api_key}&units=metric'
    resp = requests.get(url).json()
    temp = resp['main']['temp']
    return jsonify({'content': f"Temperature in {city}: {temp}°C"})

Claude can now fetch real-time data!

Step 4: Integrate with Claude API

Use Anthropic SDK to call your MCP server.

import anthropic

client = anthropic.Anthropic(api_key='your-claude-key')

message = client.messages.create(
    model="claude-3-5-sonnet-20240620",
    max_tokens=1024,
    tools=[{
        "type": "mcp_server",
        "mcp_server_url": "http://localhost:5000"
    }],
    messages=[{"role": "user", "content": "What's 15 + 27? Also, weather in London?"}]
)
print(message.content)

Claude auto-discovers tools, plans calls, and incorporates results.

Step 5: Add Authentication and Error Handling

Secure your server:

from functools import wraps

API_KEYS = {'secret-key': True}

def auth(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        key = request.headers.get('X-API-Key')
        if not API_KEYS.get(key):
            return jsonify({'error': 'Unauthorized'}), 401
        return f(*args, **kwargs)
    return decorated

app.route('/mcp/tools', methods=['GET'])(auth(list_tools))
app.route('/mcp/call', methods=['POST'])(auth(call_tool))

Pass key: -H "X-API-Key: secret-key"

Handle errors:

try:
    # tool logic
except Exception as e:
    return jsonify({'error': str(e)}), 400

Step 6: Streaming and Async Support

For long tasks, stream responses:

from flask import Response

@app.route('/mcp/call', methods=['POST'])
def call_tool():
    # ...
    def generate():
        yield json.dumps({'content': 'Starting...'}) + '\
'
        # Simulate work
        yield json.dumps({'content': 'Done!'}) + '\
'
    return Response(generate(), mimetype='application/json')

Claude supports streaming MCP calls.

Step 7: Deployment

Local/Dev: flask run

Cloud:

  • Render.com: Free tier, deploy via Git.

    # render.yaml
    services:
      - type: web
        name: claude-mcp
        env: python
        buildCommand: pip install -r requirements.txt
        startCommand: gunicorn mcp_server:app
    
  • Vercel: Serverless with vercel.json.

  • AWS Lambda: Use Mangum for ASGI.

Expose public URL, update Claude prompt with it.

For Claude Code CLI: claude-code --mcp http://your-server.com

Best Practices

  • Validation: Use Pydantic for inputs/outputs.
  • Idempotency: Handle retries with unique IDs.
  • Logging: logging.info(f"Tool {tool_name} called with {args}")
  • Rate Limiting: Flask-Limiter.
  • Schemas: Keep simple, descriptive.
  • Testing: Unit tests for each tool.

Example test:

import unittest
from app import app

class TestMCP(unittest.TestCase):
    def setUp(self):
        self.client = app.test_client()
    
    def test_calculator(self):
        rv = self.client.post('/mcp/call', json={'name': 'calculator', 'arguments': {'a': 1, 'b': 2}})
        self.assertEqual(rv.json['content'], '3')

if __name__ == '__main__':
    unittest.main()

Advanced Topics

  • Stateful Tools: Use Redis for session state.
  • Multi-Tool Chaining: Claude orchestrates sequences.
  • n8n/Zapier Integration: Webhook to MCP endpoint.
  • Enterprise: VPC peering, IAM roles.

Compare with others:

FeatureMCPOpenAI ToolsLangChain
Claude NativeAdapter
Schema AutoPartialManual
StreamingPlugin

Conclusion

Custom MCP servers transform Claude from a smart assistant into a full-fledged agent platform. Start with the calculator example, iterate to your domain tools, and deploy to production. Experiment with Claude Opus for complex orchestration.

Resources:

Build yours today—share in comments!

(Word count: ~1450)

The #1 Newsletter in AI

Stay ahead of the AI curve

The most important updates, news, and content — delivered in one weekly newsletter.

No spam. Unsubscribe anytime. Privacy policy

MCP Servers
Claude Tools
Python
Claude API
Custom Tools
ai-agents
J

About Jennifer Yu

Workflow Automation Specialist

Jennifer covers workflow strategy, no-code platforms, and clear implementation guidance for teams adopting automation.

Comments (0)