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¶
- Install uv (if not already installed):
Or see uv installation docs
-
Python 3.12+ (for hot-reload mode)
-
Find your uv path: Run
which uv(usually~/.local/bin/uv)
Quick Start¶
HTTP Development Mode (recommended)¶
Hot-reload with HTTP transport — ideal for testing with Claude.ai co-work or any HTTP MCP client:
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:
Or with mcp-hmr for fine-grained module reloading (preserves connection state):
MCP Inspector (visual debugging)¶
Launches a web UI for testing tools interactively:
Production Mode (Standard Install)¶
For release testing and production use:
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-hmrexecutable is created when you runuv pip install mcp-hmr - On Windows, use
.venv\\Scripts\\mcp-hmr.exeinstead
Switching Between Modes¶
- Development: Use
@statuspro-erp-devin Claude Desktop - Production: Use
@statuspro-erpin 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.pychanges (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:
- Fine-grained reloading: Only reloads changed modules, not the entire process
- Preserved state: Keeps connections alive, database pools intact
- Fast feedback: See changes in seconds, not minutes
- Standard Python: Uses importlib machinery, no magic
Technical Details¶
- Watches file system for changes to
.pyfiles - 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¶
- Development mode: Iterate rapidly with hot reload
- Production mode: Verify the installed package works
- Claude Desktop: Test actual tool usage
- 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:
- Check that you saved the file
- Verify
mcp-hmris installed:uv run --extra dev mcp-hmr --version - Look for error messages in the terminal running the server
- Some changes (like
server.py) require a full restart
Module Import Errors¶
Problem: ModuleNotFoundError or ImportError
Solutions:
- Ensure you're running from the correct directory:
cd statuspro_mcp_server - Verify dependencies are installed:
uv sync --extra dev - 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:
- Verify
claude_desktop_config.jsonpath is correct - Check JSON syntax (use a validator if needed)
- Restart Claude Desktop after config changes
- Verify
cwdpath is absolute and correct - Check that
STATUSPRO_API_KEYis set in env
Permission Errors¶
Problem: PermissionError when running server
Solutions:
- Ensure the repository directory is writable
- Check that
.envfile has correct permissions - On Unix:
chmod 600 .envto secure API key file
Best Practices¶
1. Use Development Mode for All Coding¶
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¶
- FastMCP Docs: https://github.com/jlowin/fastmcp
- mcp-hmr GitHub: https://github.com/mizchi/mcp-hmr
- MCP Specification: https://modelcontextprotocol.io
- Project README: ../statuspro_mcp_server/README.md
- Testing Guide: Client Testing Guide
Getting Help¶
- Issues: https://github.com/dougborg/statuspro-openapi-client/issues
- Discussions: https://github.com/dougborg/statuspro-openapi-client/discussions
- MCP Community: https://discord.gg/modelcontextprotocol
Happy coding! 🚀 The hot-reload workflow will dramatically speed up your development iteration time.