Skip to content

MCP Server Development Guide

This guide explains how to set up a fast, productive development workflow for the StatusPro MCP Server using hot-reload capabilities.

Prerequisites

  1. Install uv (if not already installed):
curl -LsSf https://astral.sh/uv/install.sh | sh

Or see uv installation docs

  1. Python 3.12+ (for hot-reload mode)

  2. Find your uv path: Run which uv (usually ~/.local/bin/uv)

Quick Start

Hot-reload with HTTP transport — ideal for testing with Claude.ai co-work or any HTTP MCP client:

# From the repo root
uv run poe dev

The server starts on http://0.0.0.0:8765/mcp with automatic reload on file changes.

For Claude.ai co-work, you also need an HTTPS tunnel (Claude.ai requires HTTPS + public URL):

# Terminal 1: MCP server with hot-reload
uv run poe dev

# Terminal 2: HTTPS tunnel
ngrok http 8765
# → Paste the https:// URL into Claude.ai > Customize > Connectors

stdio Development Mode

Hot-reload with stdio transport — for Claude Desktop:

uv run poe dev-stdio

Or with mcp-hmr for fine-grained module reloading (preserves connection state):

cd statuspro_mcp_server
uv pip install mcp-hmr
uv run mcp-hmr statuspro_mcp.server:mcp

MCP Inspector (visual debugging)

Launches a web UI for testing tools interactively:

uv run poe dev-inspect

Production Mode (Standard Install)

For release testing and production use:

cd statuspro_mcp_server
uv build
pipx install --force dist/statuspro_mcp_server-*.whl

Development Modes

Command Transport Hot Reload Use Case
poe dev streamable-http Yes Claude.ai co-work, HTTP
poe dev-stdio stdio Yes Claude Desktop, Claude Code
poe dev-inspect Inspector UI Yes Visual tool debugging
mcp-hmr stdio Yes (fast) Fine-grained module reload
statuspro-mcp-server stdio (default) No Production

Claude Desktop Configuration

You can configure Claude Desktop to support both modes simultaneously.

Configuration File Location

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

Example Configuration

{
  "mcpServers": {
    "statuspro-erp-dev": {
      "command": "/absolute/path/to/statuspro-openapi-client/.venv/bin/mcp-hmr",
      "args": ["statuspro_mcp.server:mcp"],
      "cwd": "/absolute/path/to/statuspro-openapi-client/statuspro_mcp_server",
      "env": {
        "STATUSPRO_API_KEY": "your-api-key-here"
      }
    },
    "statuspro-erp": {
      "command": "statuspro-mcp-server",
      "env": {
        "STATUSPRO_API_KEY": "your-api-key-here"
      }
    }
  }
}

Important:

  • Replace /absolute/path/to/ with your actual repository path
  • The .venv/bin/mcp-hmr executable is created when you run uv pip install mcp-hmr
  • On Windows, use .venv\\Scripts\\mcp-hmr.exe instead

Switching Between Modes

  • Development: Use @statuspro-erp-dev in Claude Desktop
  • Production: Use @statuspro-erp in Claude Desktop
  • Both can be enabled at the same time

Tip: Use development mode while coding, then test against production mode before creating a PR.

Development Workflow

Typical Iteration Cycle

# 1. Start development server (once)
cd statuspro_mcp_server
uv run mcp-hmr statuspro_mcp.server:mcp

# 2. Use the MCP server in Claude Desktop
#    - Chat with Claude
#    - Test your tools
#    - Notice a bug or want to add a feature

# 3. Edit code in your editor
#    - Modify src/statuspro_mcp/tools/orders.py
#    - Add logging, fix bugs, improve error handling
#    - Save the file

# 4. Test immediately in Claude Desktop
#    - No restart needed!
#    - Try the tool again
#    - See your changes instantly

# 5. Iterate until satisfied
#    - Keep editing and saving
#    - Changes apply in real-time

What Gets Hot-Reloaded?

Reloads automatically:

  • Tool implementations (src/statuspro_mcp/tools/*.py)
  • Helper functions
  • Request/response models
  • Business logic

Requires restart:

  • server.py changes (FastMCP initialization)
  • Lifespan context changes
  • New dependencies in pyproject.toml

Adding Debug Logging

Hot reload makes debugging much faster:

# Before (in src/statuspro_mcp/tools/orders.py)
async def _update_order_status_impl(request: UpdateOrderStatusRequest, context: Context) -> dict[str, object]:
    server_context = context.request_context.lifespan_context
    client = server_context.client
    product = await client.orders.update_status(request.order_id, request.status_code)
    # ... rest of function

# After (add logging - save file - test immediately!)
import logging
logger = logging.getLogger(__name__)

async def _update_order_status_impl(request: UpdateOrderStatusRequest, context: Context) -> dict[str, object]:
    logger.debug(f"Context structure: {dir(context)}")  # See what's available
    logger.debug(f"Request context: {dir(context.request_context)}")  # Debug paths

    server_context = context.request_context.lifespan_context
    client = server_context.client

    logger.info(f"Checking stock for SKU: {request.sku}")  # Track calls
    product = await client.orders.update_status(request.order_id, request.status_code)
    logger.info(f"Found product: {product.name if product else 'Not found'}")
    # ... rest of function

Save → Test in Claude Desktop → See logs immediately!

How mcp-hmr Works

mcp-hmr (Model Context Protocol Hot Module Replacement) provides:

  1. Fine-grained reloading: Only reloads changed modules, not the entire process
  2. Preserved state: Keeps connections alive, database pools intact
  3. Fast feedback: See changes in seconds, not minutes
  4. Standard Python: Uses importlib machinery, no magic

Technical Details

  • Watches file system for changes to .py files
  • Uses Python's import hooks to reload modified modules
  • Preserves the FastMCP server instance and lifespan context
  • Updates tool registrations without restarting the server

Testing Your Changes

Unit Tests (Always Run First)

# Run tests before committing
uv run poe test

# Pre-commit hooks will also run tests automatically
git commit -m "feat(mcp): add new order tool"

Integration Testing Workflow

  1. Development mode: Iterate rapidly with hot reload
  2. Production mode: Verify the installed package works
  3. Claude Desktop: Test actual tool usage
  4. Automated tests: Ensure everything passes
# After development, test the full cycle
uv run poe test                    # Unit tests pass
uv build                           # Build package
pipx install --force dist/*.whl    # Install for real
# Test in Claude Desktop with production config

Troubleshooting

Hot Reload Not Working

Problem: Changes don't appear after saving

Solutions:

  1. Check that you saved the file
  2. Verify mcp-hmr is installed: uv run --extra dev mcp-hmr --version
  3. Look for error messages in the terminal running the server
  4. Some changes (like server.py) require a full restart

Module Import Errors

Problem: ModuleNotFoundError or ImportError

Solutions:

  1. Ensure you're running from the correct directory: cd statuspro_mcp_server
  2. Verify dependencies are installed: uv sync --extra dev
  3. Check that the file path in the command is correct: src/statuspro_mcp/server.py:mcp

Claude Desktop Not Finding Server

Problem: MCP server doesn't appear in Claude Desktop

Solutions:

  1. Verify claude_desktop_config.json path is correct
  2. Check JSON syntax (use a validator if needed)
  3. Restart Claude Desktop after config changes
  4. Verify cwd path is absolute and correct
  5. Check that STATUSPRO_API_KEY is set in env

Permission Errors

Problem: PermissionError when running server

Solutions:

  1. Ensure the repository directory is writable
  2. Check that .env file has correct permissions
  3. On Unix: chmod 600 .env to secure API key file

Best Practices

1. Use Development Mode for All Coding

# Start dev server once, keep it running all day
uv run mcp-hmr statuspro_mcp.server:mcp

2. Test in Production Mode Before PR

# Before creating a PR, verify production install
uv build
pipx install --force dist/*.whl
# Test in Claude Desktop with production config

3. Keep Tests Passing

# Pre-commit hooks run automatically, but you can run manually:
uv run poe test          # Just tests
uv run poe check         # Full validation (lint + test + format)

4. Use Descriptive Logging

# Good: Helps debug issues
logger.info(f"Updating status for order {request.order_id}")
logger.debug(f"Client config: base_url={client.base_url}, timeout={client.timeout}")
logger.warning(f"SKU not found: {request.sku}, returning empty stock")

# Bad: Not helpful
logger.info("Doing a thing")
logger.debug("Debug message")

5. Structure for Testability

# Good: Separate implementation from FastMCP decorator
async def _update_order_status_impl(request, context) -> dict[str, object]:
    \"\"\"Implementation with full business logic.\"\"\"
    # ... implementation

@mcp.tool()
async def update_order_status(request, context) -> dict[str, object]:
    \"\"\"FastMCP tool wrapper - minimal logic here.\"\"\"
    return await _update_order_status_impl(request, context)

This allows testing _update_order_status_impl directly with mocks.

Additional Resources

Getting Help


Happy coding! 🚀 The hot-reload workflow will dramatically speed up your development iteration time.