mcp-common
Version: 0.25.2 (Oneiric-Native) Status: Production Ready
Quick Links
Overview
mcp-common is a Oneiric-native foundation library for building production-grade MCP (Model Context Protocol) servers. It provides battle-tested patterns extracted from production servers including Crackerjack, Session Buddy, and FastBlocks.
Quality & CI
Crackerjack is the standard quality-control and CI/CD gate for changes to this library and the downstream MCP servers that build on it.
🎯 What This Library Provides:
- Tool Profile System - Gated tool registration to reduce MCP context overhead (~400+ tools across the 5 Bodai ecosystem components at FULL profile; per-server counts vary)
- Description Trimming - Utility to trim tool docstrings to 200 chars for token efficiency
- Oneiric CLI Factory - Standardized server lifecycle with start/stop/restart/status/health commands
- HTTP Client Adapter - Connection pooling with httpx (reduces handshake overhead on warm connections; ratio depends on payload, concurrency, and TLS resumption — see
tests/performance/test_http_pooling.pyfor measurement harness) - Prompting/Notification Adapter - Unified cross-platform user interaction with automatic backend detection
- Security Utilities - API key validation (with 90% faster caching) and input sanitization (2x faster)
- Rich Console UI - Beautiful panels and notifications for server operations
- Settings Management - YAML + environment variable configuration (Pydantic-based)
- Health Check System - Production-ready health monitoring
- Type-Safe - Full Pydantic validation and type hints
- Comprehensive Testing - ~1,687 tests with property-based, concurrency, and integration coverage
Design Principles:
- Oneiric-Native - Direct Pydantic, Rich library, and standard patterns
- Production-Ready - Extracted from real production systems
- Layered Configuration - YAML files + environment variables with clear priority
- Rich UI - Professional console output with Rich panels
- Type-safe - Full type hints with strict MyPy checking
- Well-Tested - 90% coverage minimum
📚 Examples
See examples/ for complete production-ready examples:
1. CLI Server (Oneiric-Native)
Demonstrates the CLI factory for standardized server lifecycle management:
- 5 lifecycle commands (start, stop, restart, status, health)
- PID file management with security validation
- Runtime health snapshots
- Graceful shutdown with signal handling
- Custom lifecycle handlers
cd examples
python cli_server.py start
python cli_server.py status
python cli_server.py health
python cli_server.py stop
2. Weather MCP Server (Oneiric-Native)
Demonstrates HTTP adapters and FastMCP integration:
- HTTPClientAdapter with connection pooling (warm-connection speedup depends on workload)
- MCPBaseSettings with YAML + environment configuration
- ServerPanels for beautiful terminal UI
- Oneiric configuration patterns (direct instantiation)
- FastMCP tool integration (optional; install separately)
cd examples
python weather_server.py
Full documentation: examples/README.md
Quick Start
Installation
pip install mcp-common>=0.25.2
This automatically installs Pydantic, Rich, FastMCP (≥3.4), and all required dependencies. FastMCP is a hard dependency as of v0.17 — no separate install needed.
Minimal Example
# my_server/settings.py
from mcp_common.config import MCPBaseSettings
from pydantic import Field
class MyServerSettings(MCPBaseSettings):
"""Server configuration following Oneiric pattern.
Loads from (priority order):
1. settings/local.yaml (gitignored)
2. settings/my-server.yaml
3. Environment variables MY_SERVER_*
4. Defaults below
"""
api_key: str = Field(description="API key for service")
timeout: int = Field(default=30, description="Request timeout")
# my_server/main.py
from fastmcp import FastMCP # Hard dependency since v0.17
from mcp_common import ServerPanels, HTTPClientAdapter, HTTPClientSettings
from my_server.settings import MyServerSettings
# Initialize
mcp = FastMCP("MyServer")
settings = MyServerSettings.load("my-server")
# Initialize HTTP adapter
http_settings = HTTPClientSettings(timeout=settings.timeout)
http_adapter = HTTPClientAdapter(settings=http_settings)
# Define tools
@mcp.tool()
async def call_api():
# Use the global adapter instance
response = await http_adapter.get("https://api.example.com")
return response.json()
# Run server
if __name__ == "__main__":
# Display startup panel
ServerPanels.startup_success(
server_name="My MCP Server",
version="1.0.0",
features=["HTTP Client", "YAML Configuration"],
)
mcp.run()
Core Features
🔌 HTTP Client Adapter
Connection Pooling with httpx:
- Avoids per-request TLS handshake by reusing the connection pool across requests (exact speedup is workload-dependent; live measurement against a public endpoint averaged ~2x — see
tests/performance/test_http_pooling.py) - Automatic initialization and cleanup
- Configurable timeouts, retries, connection limits
from mcp_common import HTTPClientAdapter, HTTPClientSettings
# Configure HTTP adapter
http_settings = HTTPClientSettings(
timeout=30,
max_connections=50,
retry_attempts=3,
)
# Create adapter
http_adapter = HTTPClientAdapter(settings=http_settings)
# Make requests
response = await http_adapter.get("https://api.example.com")
Architecture Overview:
graph TB
subgraph "mcp-common Components"
A[HTTP Client Adapter<br/>with Connection Pooling]
B[Settings Management<br/>YAML + Env Vars]
C[CLI Factory<br/>Lifecycle Management]
D[Rich UI Panels<br/>Console Output]
E[Security Utilities<br/>Validation & Sanitization]
end
subgraph "Integration"
F[FastMCP<br/>Optional]
G[MCP Server<br/>Application]
end
A --> G
B --> G
C --> G
D --> G
E --> G
F --> G
style A fill:#e1f5fe
style B fill:#f3e5f5
style C fill:#e8f5e8
style D fill:#fff3e0
style E fill:#fce4ec
Note: Rate limiting is not provided by this library. If you use FastMCP, its built-in RateLimitingMiddleware can be enabled; otherwise, use project-specific configuration.
🎯 Oneiric CLI Factory
Production-Ready Server Lifecycle Management:
The MCPServerCLIFactory provides standardized CLI commands for managing MCP server lifecycles, inspired by Oneiric's operational patterns. It handles process management, health monitoring, and graceful shutdown out of the box.
Features:
- 5 Standard Commands -
start,stop,restart,status,health - Security-First - Secure PID files (0o600), cache directories (0o700), ownership validation
- Process Validation - Detects stale PIDs, prevents race conditions, validates process identity
- Health Monitoring - Runtime health snapshots with configurable TTL
- Signal Handling - Graceful shutdown on SIGTERM/SIGINT
- Custom Handlers - Extensible lifecycle hooks for server-specific logic
- Dual Output - Human-readable and JSON output modes
- Standard Exit Codes - Shell-scriptable with semantic exit codes
CLI Factory Architecture:
graph LR
subgraph "User Application"
A[Server Implementation]
B[Custom Handlers]
end
subgraph "mcp-common CLI Factory"
C[MCPServerCLIFactory]
D[MCPServerSettings]
E[PID File Management]
F[Health Snapshots]
G[Signal Handlers]
end
subgraph "Typer CLI"
H[start command]
I[stop command]
J[restart command]
K[status command]
L[health command]
end
A --> C
B --> C
D --> C
C --> E
C --> F
C --> G
C --> H
C --> I
C --> J
C --> K
C --> L
style A fill:#e8f5e8
style B fill:#fff3e0
style C fill:#e3f2fd
style D fill:#f3e5f5
style H fill:#e0f2f1
style I fill:#e0f2f1
style J fill:#e0f2f1
style K fill:#e0f2f1
style L fill:#e0f2f1
Quick Example:
from mcp_common.cli import MCPServerCLIFactory, MCPServerSettings
# 1. Load settings (YAML + env vars)
settings = MCPServerSettings.load("my-server")
# 2. Define lifecycle handlers
def start_server():
print("Server initialized!")
# Your server startup logic here
def stop_server(pid: int):
print(f"Stopping PID {pid}")
# Your cleanup logic here
def check_health():
# Return current health snapshot
return RuntimeHealthSnapshot(
orchestrator_pid=os.getpid(),
watchers_running=True,
)
# 3. Create CLI factory
factory = MCPServerCLIFactory(
server_name="my-server",
settings=settings,
start_handler=start_server,
stop_handler=stop_server,
health_probe_handler=check_health,
)
# 4. Create and run Typer app
app = factory.create_app()
if __name__ == "__main__":
app()
Command Usage:
# Start server (creates PID file and health snapshot)
python my_server.py start
# Check status (lightweight process check)
python my_server.py status
# Output: Server running (PID 12345, snapshot age: 2.3s, fresh: True)
# View health (detailed health information)
python my_server.py health
# Live health probe
python my_server.py health --probe
# Stop server (graceful shutdown with SIGTERM)
python my_server.py stop
# Force stop with timeout
python my_server.py stop --timeout 5 --force
# Restart (stop + start)
python my_server.py restart
# JSON output for automation
python my_server.py status --json
Configuration:
Settings are loaded from multiple sources (priority order):
settings/local.yaml(gitignored, for development)settings/{server-name}.yaml(checked into repo)- Environment variables
MCP_SERVER_* - Defaults in
MCPServerSettings
Example settings/my-server.yaml:
server_name: "My MCP Server"
cache_root: .oneiric_cache
health_ttl_seconds: 60.0
log_level: INFO
Exit Codes:
0- Success1- General error2- Server not running (status/stop)3- Server already running (start)4- Health check failed5- Configuration error6- Permission error7- Timeout8- Stale PID file (use--force)
Full Example:
See examples/cli_server.py for a complete working example with custom commands and health probes.
⚙️ Settings with YAML Support (Oneiric Pattern)
- Pure Pydantic BaseModel
- Layered configuration: YAML files + environment variables
- Type validation with Pydantic
- Path expansion (
~→ home directory)
from mcp_common.config import MCPBaseSettings
class ServerSettings(MCPBaseSettings):
api_key: str # Required
timeout: int = 30 # Optional with default
# Load with layered configuration
settings = ServerSettings.load("my-server")
# Loads from:
# 1. settings/my-server.yaml
# 2. settings/local.yaml
# 3. Environment variables MY_SERVER_*
# 4. Defaults
📝 Standard Python Logging
mcp-common uses standard Python logging. Configure as needed for your server:
import logging
# Configure logging
logging.basicConfig(
level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger(__name__)
logger.info("Server started")
🎨 Rich Console UI
- Beautiful startup panels
- Error displays with context
- Statistics tables
- Progress bars
from mcp_common.ui import ServerPanels
ServerPanels.startup_success(
server_name="Mailgun MCP",
http_endpoint="http://localhost:8000",
features=["Rate Limiting", "Security Filters"],
)
🧪 Testing Utilities
- Mock MCP clients
- HTTP response mocking
- Shared fixtures
- DI-friendly testing
from mcp_common.testing import MockMCPClient, mock_http_response
async def test_tool():
with mock_http_response(status=200, json={"ok": True}):
result = await my_tool()
assert result["success"]
🔧 Tool Profile System
Reduce MCP context overhead by gating which tools are registered at startup. Each server reads a {SERVER_NAME}_TOOL_PROFILE environment variable (minimal, standard, or full) and only registers the corresponding tool groups.
Why: A server with 170 tools sends ~70k tokens of tool definitions to Claude on every request. Profile gating reduces this to ~10-20k tokens for daily development.
MANDATORY_TOOLS: Tools added to mcp_common.tools.MANDATORY_TOOLS are always registered regardless of profile. The default set is empty; add infrastructure-critical tool names (health probes, liveness, readiness) per-server so they survive profile gating.
ToolProfile enum:
from mcp_common.tools import ToolProfile, trim_description, MANDATORY_TOOLS
# Resolve from environment (defaults to FULL for backward compatibility)
profile = ToolProfile.from_env("MY_SERVER_TOOL_PROFILE")
# Ordering comparisons work
assert ToolProfile.MINIMAL < ToolProfile.STANDARD < ToolProfile.FULL
# Safe fallback for invalid values
assert ToolProfile.from_string("unknown") == ToolProfile.FULL
Description trimming:
# Keep first paragraph, trim at word boundary up to max_length chars (default 200)
trimmed = trim_description("""Check health of a service.
Args:
service_name: Name of the service
port: Port number
Returns:
Health status dictionary""")
# Result: "Check health of a service."
Per-server profiles.py pattern:
# my_server/mcp/tools/profiles.py
from mcp_common.tools import ToolProfile
MINIMAL_REGISTRATIONS = ["register_health_tools"]
STANDARD_REGISTRATIONS = MINIMAL_REGISTRATIONS + ["register_core_tools"]
FULL_REGISTRATIONS = STANDARD_REGISTRATIONS + ["register_advanced_tools"]
PROFILE_REGISTRATIONS = {
ToolProfile.MINIMAL: MINIMAL_REGISTRATIONS,
ToolProfile.STANDARD: STANDARD_REGISTRATIONS,
ToolProfile.FULL: FULL_REGISTRATIONS,
}
def get_active_profile(env_var="MY_SERVER_TOOL_PROFILE"):
return ToolProfile.from_env(env_var)
Profile tiers across the ecosystem:
| Server | MINIMAL | STANDARD | FULL |
|---|---|---|---|
| session-buddy | 4 groups (~12 tools) | 13 groups (~35 tools) | 32 groups (~151 tools) |
| mahavishnu | 1 group (health) | 7 groups | 14 groups (~174 tools) |
| crackerjack | 2 groups | 7 groups | 12 groups (~60 tools) |
| akosha | 1 group (health) | 2 groups | 4 groups (~5 tools) |
discover_tools meta-tool: Each server registers a discover_tools(query) tool that is always available, letting Claude find unloaded tools and suggest profile changes.
Documentation
- examples/README.md - START HERE - Example servers and usage patterns
- ONEIRIC_CLI_FACTORY_IMPLEMENTATION.md - CLI factory implementation guide
- ONEIRIC_CLI_FACTORY_PLAN.md - CLI factory design plan
- ONEIRIC_CLI_FACTORY_SPEC_REVIEW.md - CLI factory spec review
- ONEIRIC_CLI_AUDIT_RESPONSE.md - Audit response with cross-references
Complete Example
See examples/ for a complete production-ready Weather MCP server demonstrating mcp-common patterns.
Key Patterns Demonstrated:
- Oneiric Settings - YAML + environment variable configuration with
.load() - HTTP Adapter - HTTPClientAdapter with connection pooling
- Rich UI - ServerPanels for startup/errors/status
- Tool Organization - Modular tool registration with FastMCP
- Configuration Layering - Multiple config sources with clear priority
- Type Safety - Full Pydantic validation throughout
- Error Handling - Graceful error display with ServerPanels
Performance Benchmarks
✨ Phase 4 Optimizations
Sanitization Early-Exit Optimization:
| Scenario | Before | After | Speedup |
|---|---|---|---|
| Clean text (no sensitive data) | 22μs | 10μs | ~2x faster (illustrative) |
| Text with sensitive data | 22μs | 22μs | No change |
API Key Validation Caching:
| Call Type | Time | Speedup |
|---|---|---|
| First call (uncached) | 100μs | baseline |
| Subsequent calls (cached) | 10μs | ~10x faster (illustrative) |
Impact:
- ~2x faster for clean text sanitization (most common case); ratio is illustrative, run
tests/performance/test_sanitization_benchmarks.pyto measure on your workload - ~10x faster for repeated API key validations (illustrative; based on typical
@lru_cachecost amortization) - Cache size: 128 most recent entries (
@lru_cache(maxsize=128)inmcp_common/security/api_keys.py) - Zero breaking changes
HTTP Client Adapter (vs new client per request)
Before: 100 requests, ~500MB peak memory with new client per request
After: 100 requests, ~50MB peak memory with a shared pooled client
Result: avoids per-request client allocation; exact throughput and memory
savings depend on request count, payload size, and TLS resumption.
Run `pytest tests/performance/test_http_pooling.py --benchmark-only` to measure
on your workload.
Rate Limiter Overhead
Without: 1000 requests in 1.2 seconds
With: 1000 requests in 1.25 seconds
Result: +4% overhead (negligible vs network I/O)
📊 Testing Performance
Test Suite Growth:
| Version | Tests | Coverage |
|---|---|---|
| v0.5.2 | 564 | 94% |
| v0.6.0 | 615 | 99%+ |
| v0.15.0 | ~1,400 | 95% |
| v0.17.9 | ~1,687 | 96% |
| v0.25.2 (current) | 2,116 | 90.0% (CI hard gate) |
Testing Capabilities:
- ✅ Property-based tests (Hypothesis, 6 modules)
- ✅ Concurrency + async safety tests
- ✅ Performance benchmarks (
tests/performance/) - ✅ Backward compatibility across the 0.13→0.17 series
Usage Patterns
Pattern 1: Configure Settings with YAML
from mcp_common.config import MCPBaseSettings
from pydantic import Field
class MySettings(MCPBaseSettings):
api_key: str = Field(description="API key")
timeout: int = Field(default=30, description="Timeout")
# Load from settings/my-server.yaml + env vars
settings = MySettings.load("my-server")
# Access configuration
print(f"Using API key: {settings.get_masked_key()}")
Pattern 2: Use HTTP Client Adapter
from mcp_common import HTTPClientAdapter, HTTPClientSettings
# Configure HTTP client
http_settings = HTTPClientSettings(
timeout=30,
max_connections=50,
retry_attempts=3,
)
# Create adapter
http = HTTPClientAdapter(settings=http_settings)
# Make requests
@mcp.tool()
async def call_api():
response = await http.get("https://api.example.com/data")
return response.json()
# Cleanup when done
await http._cleanup_resources()
Pattern 3: Display Rich UI Panels
from mcp_common import ServerPanels
# Startup panel
ServerPanels.startup_success(
server_name="My Server",
version="1.0.0",
features=["Feature 1", "Feature 2"],
)
# Error panel
ServerPanels.error(
title="API Error",
message="Failed to connect",
suggestion="Check your API key",
)
# Status table
ServerPanels.status_table(
title="Health Check",
rows=[
("API", "✅ Healthy", "200 OK"),
("Database", "⚠️ Degraded", "Slow queries"),
],
)
Development
Setup
git clone https://github.com/lesleslie/mcp-common.git
cd mcp-common
uv sync --group dev
Running Tests
# Run all tests with coverage
pytest --cov=mcp_common --cov-report=html
# Run specific test
pytest tests/test_http_adapter.py -v
# Run integration tests
pytest tests/integration/ -v
Code Quality
# Format code
ruff format
# Lint code
ruff check
# Type checking
mypy mcp_common tests
# Run all quality checks
crackerjack run --all
Versioning
Recent Versions:
- 0.25.2 (current) - docs: correct
IdentityProviderConfig→IdentityProviderSpecandproperty-raisespattern inauth-design.md - 0.25.1 -
register_http_health_routehelper, dependency-groups migration - 0.17.0 - Plan 7 Phase 1: FastMCP 3.4 foundation
- 0.16.0 - AppleScript bridge + iTerm2 protocol spec, async multi-line escaping
- 0.15.0 - LLM layer: per-tier retry loop, error sanitization, llama_server support, Multimodal TaskType
- 0.14.0 - HailuoAdapter (MiniMax video generation), task_routing model resolution
- 0.13.0 - Property-based tests expansion, Hypothesis edge cases
- 0.6.0 - Tool Profile System, description trimming, MANDATORY_TOOLS
- 0.3.3 - Oneiric CLI Factory
Compatibility:
- Requires Python 3.14+
- Requires FastMCP ≥3.4 (hard dependency, declared in
pyproject.toml) - Uses Pydantic 2.13+, Rich 15+
Success Metrics
Current Status:
- ✅ Professional Rich UI in all components
- ✅ 90%+ test coverage maintained
- ✅ Zero production incidents
- ✅ Oneiric-native patterns throughout
- ✅ Standardized CLI lifecycle management
- ✅ Clean dependency tree (no framework lock-in)
License
BSD-3-Clause License - See LICENSE for details
Contributing
Contributions are welcome! Please:
- Read
examples/README.mdfor usage patterns - Follow Oneiric patterns (see examples)
- Fork and create feature branch
- Add tests (coverage ≥90%)
- Ensure all quality checks pass (
ruff format && ruff check && mypy && pytest) - Submit pull request
Acknowledgments
Built with patterns extracted from 8 production MCP servers:
Primary Pattern Sources:
- crackerjack - MCP server structure, Rich UI panels, CLI patterns
- session-buddy - Configuration patterns, health checks
- fastblocks - Adapter organization, settings management
Additional Contributors:
- raindropio-mcp (HTTP client patterns)
- excalidraw-mcp (testing patterns)
- opera-cloud-mcp
- mailgun-mcp
- unifi-mcp
Support
For support, please check the documentation in the docs/ directory or create an issue in the repository.
Ready to get started? Check out examples/ for working examples demonstrating all features!
Metadata
Release files for mcp-common 0.30.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| mcp_common-0.30.0.tar.gz | 1.2 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mcp_common-0.30.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.6 MB
Release files / mcp_common-0.30.0.tar.gz
| Download URL | mcp_common-0.30.0.tar.gz |
|---|---|
| Size | 1.2 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
119c05b3c30b277854b5f07ffdca603f44f92eb8441ea0079eed35c7bf44f386
|
|
BLAKE2b-256 checksum How to use checksums |
b2eee0139b0201352ce005fc518a6bca0c40b75d515d5ed9487e5002d747c7c0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.16 {"installer":{"name":"uv","version":"0.12.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / mcp_common-0.30.0-py3-none-any.whl
| Download URL | mcp_common-0.30.0-py3-none-any.whl |
|---|---|
| Size | 467.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
fc34aff11a1a6542690b99c596a4b4c502a0c4932ee399e5ef716ef2a93ab183
|
|
BLAKE2b-256 checksum How to use checksums |
e9717e10ea6e5b69a51a71a89ce872e0c28a91982bb10e49d213ad3c208eef02
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.16 {"installer":{"name":"uv","version":"0.12.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|