MCP服务器基础库

mcp-common是一个用于构建生产级MCP服务器的Python基础库,提供工具注册、HTTP客户端、CLI生命周期管理、配置管理和安全工具等核心能力,适用于需要快速搭建MCP服务端应用的开发者。

  • by lesleslie
  • Python
  • 本地部署
  • 1 星标
  • 更新于
在 GitHub 查看 lesleslie/mcp-common

使用说明

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.py for 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:

  1. Oneiric-Native - Direct Pydantic, Rich library, and standard patterns
  2. Production-Ready - Extracted from real production systems
  3. Layered Configuration - YAML files + environment variables with clear priority
  4. Rich UI - Professional console output with Rich panels
  5. Type-safe - Full type hints with strict MyPy checking
  6. 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):

  1. settings/local.yaml (gitignored, for development)
  2. settings/{server-name}.yaml (checked into repo)
  3. Environment variables MCP_SERVER_*
  4. 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 - Success
  • 1 - General error
  • 2 - Server not running (status/stop)
  • 3 - Server already running (start)
  • 4 - Health check failed
  • 5 - Configuration error
  • 6 - Permission error
  • 7 - Timeout
  • 8 - 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


Complete Example

See examples/ for a complete production-ready Weather MCP server demonstrating mcp-common patterns.

Key Patterns Demonstrated:

  1. Oneiric Settings - YAML + environment variable configuration with .load()
  2. HTTP Adapter - HTTPClientAdapter with connection pooling
  3. Rich UI - ServerPanels for startup/errors/status
  4. Tool Organization - Modular tool registration with FastMCP
  5. Configuration Layering - Multiple config sources with clear priority
  6. Type Safety - Full Pydantic validation throughout
  7. 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.py to measure on your workload
  • ~10x faster for repeated API key validations (illustrative; based on typical @lru_cache cost amortization)
  • Cache size: 128 most recent entries (@lru_cache(maxsize=128) in mcp_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→IdentityProviderSpec and property-raises pattern in auth-design.md
  • 0.25.1 - register_http_health_route helper, 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:

  1. ✅ Professional Rich UI in all components
  2. ✅ 90%+ test coverage maintained
  3. ✅ Zero production incidents
  4. ✅ Oneiric-native patterns throughout
  5. ✅ Standardized CLI lifecycle management
  6. ✅ Clean dependency tree (no framework lock-in)

License

BSD-3-Clause License - See LICENSE for details


Contributing

Contributions are welcome! Please:

  1. Read examples/README.md for usage patterns
  2. Follow Oneiric patterns (see examples)
  3. Fork and create feature branch
  4. Add tests (coverage ≥90%)
  5. Ensure all quality checks pass (ruff format && ruff check && mypy && pytest)
  6. 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!