Skip to main content

Async connection pool for Model Context Protocol (MCP) client sessions — keep sessions warm, reuse across requests, auto-reconnect on failure.

Project description

mcpool

PyPI version Python 3.10+ License: MIT Tests

Async connection pool for Model Context Protocol (MCP) client sessions. mcpool keeps sessions warm, reuses them across requests, collapses concurrent tools/list refreshes, and hardens pool behavior with retries, a circuit breaker, proactive recycling, and optional OpenTelemetry hooks.

Why mcpool

Without pooling, every MCP call pays connection setup cost:

Request -> TCP connect -> TLS handshake -> MCP initialize -> tools/list -> tools/call -> close

With mcpool, warm sessions are reused:

App startup -> pool.start() -> N sessions ready
Request -> pool.session() -> tools/call -> return session
Shutdown -> pool.shutdown() -> drain + close

That removes repeated setup work, reduces tools/list churn, and gives you a safer operational model around endpoint failures.

Installation

pip install mcp-pool

Optional extras:

pip install "mcp-pool[otel]"
pip install "mcp-pool[docs]"

Quickstart

import asyncio
from mcpool import MCPPool, PoolConfig


async def main() -> None:
    config = PoolConfig(
        endpoint="http://gateway.example.com/mcp",
        transport="streamable_http",
        min_sessions=2,
        max_sessions=10,
        tool_cache_ttl_s=300,
        health_check_interval_s=30,
        max_session_lifetime_s=3600,
        recycle_window_s=60,
        borrow_timeout_s=2,
        retry_count=2,
        auth="your-api-key",
        enable_opentelemetry=True,
    )

    async with MCPPool(config=config) as pool:
        async with pool.session(headers={"X-Request-ID": "req-123"}) as session:
            result = await session.call_tool("my_tool", arguments={"key": "value"})
            print(result)

        async with pool.try_session() as session:
            if session is not None:
                await session.call_tool("best_effort_tool")

        tools = await pool.list_tools()
        print(f"Tools: {[tool.name for tool in tools.tools]}")
        print(pool.metrics.snapshot())


asyncio.run(main())

What v0.2.0 Adds

Area Change
Correctness Single-flight list_tools() refresh to prevent cache stampedes
Resilience Session creation retries with exponential backoff + jitter
Safety Circuit breaker for repeated session-creation failures
Scheduling Separate borrow_timeout_s from connect_timeout_s
API pool.try_session() for non-blocking borrow attempts
Lifecycle Background recycling of near-expiry idle sessions
Observability Optional OpenTelemetry spans and metrics
Tooling Benchmark suite, Hypothesis fuzz tests, and mkdocs documentation

Core API

MCPPool

pool = MCPPool(config=PoolConfig(endpoint="http://..."))
# or
pool = MCPPool(endpoint="http://...", min_sessions=2, max_sessions=10)
Method Description
await pool.start() Pre-warm min_sessions and start the health checker
await pool.shutdown() Drain in-flight work and close sessions
async with pool.session(headers=...) as session: Borrow a session, waiting up to borrow_timeout_s
async with pool.try_session(headers=...) as session: Borrow immediately or yield None if no capacity is free
await pool.list_tools() Return cached tools, refreshing with single-flight behavior when stale
pool.invalidate_tools_cache() Force the next list_tools() call to refresh
pool.metrics.snapshot() Return a metrics dictionary

PoolConfig Highlights

Parameter Default Description
min_sessions 2 Sessions to pre-warm on startup
max_sessions 10 Hard cap on concurrent sessions
tool_cache_ttl_s 300.0 TTL for cached tools/list responses
health_check_interval_s 30.0 Seconds between idle-session health sweeps
max_session_lifetime_s 3600.0 Hard lifetime for a session
recycle_window_s 30.0 Recycle idle sessions this long before lifetime expiry
connect_timeout_s 10.0 Timeout for a single session creation attempt
borrow_timeout_s connect_timeout_s Timeout for waiting on pool capacity
retry_count 2 Retries for session creation after the first failure
retry_base_delay_s 0.1 Base delay for exponential retry backoff
retry_max_delay_s 2.0 Maximum retry delay
failure_threshold 5 Consecutive failures before the circuit opens
recovery_timeout_s 30.0 Time before the circuit allows a half-open probe
enable_opentelemetry False Enable OTel spans/metrics if opentelemetry-api is installed

Event Hooks

PoolConfig.event_hooks accepts async or sync callbacks for:

  • on_session_created
  • on_session_destroyed
  • on_borrow
  • on_return
  • on_health_check_failed
  • on_circuit_open
  • on_circuit_close

Metrics

pool.metrics.snapshot() now includes the new resilience and cache fields:

{
    "active": 1,
    "idle": 4,
    "total": 5,
    "borrow_count": 42,
    "avg_borrow_wait_s": 0.003,
    "return_count": 42,
    "cache_hits": 38,
    "cache_misses": 4,
    "cache_refresh_count": 4,
    "cache_waiters": 7,
    "cache_hit_rate": 0.905,
    "reconnect_count": 1,
    "retry_attempts": 2,
    "health_check_count": 120,
    "health_check_failures": 1,
    "recycled_count": 3,
    "sessions_created": 8,
    "sessions_destroyed": 3,
    "errors": 0,
    "circuit_state": "closed",
    "uptime_s": 3600.5,
}

Observability

When OTel is enabled, mcpool emits spans for:

  • pool.borrow
  • pool.return
  • session.create
  • session.close
  • list_tools

And OTel metrics for:

  • pool.active_sessions
  • pool.idle_sessions
  • pool.borrow_wait_seconds
  • pool.errors_total

Documentation

The repository now includes a mkdocs site in docs/ covering:

  • Quickstart
  • Configuration reference
  • Migration guide for 0.1.0 -> 0.2.0
  • Architecture
  • Deployment patterns
  • Performance tuning

Build it locally with:

mkdocs serve

Development

git clone https://github.com/adwantg/mcp-pool.git
cd mcp-pool
pip install -e ".[dev]"
pytest -v
pytest tests/benchmarks tests/fuzz -v --no-cov
ruff check src/ tests/
mypy src/
mkdocs build --strict

License

MIT — see LICENSE.

Citation

If you use mcpool in research, please cite:

@software{adwant_mcpool_2025,
  author = {Goutam Adwant},
  title = {mcpool: Async Connection Pool for MCP Sessions},
  year = {2025},
  url = {https://github.com/adwantg/mcp-pool}
}

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mcp_pool-0.2.0.tar.gz (30.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mcp_pool-0.2.0-py3-none-any.whl (20.8 kB view details)

Uploaded Python 3

File details

Details for the file mcp_pool-0.2.0.tar.gz.

File metadata

  • Download URL: mcp_pool-0.2.0.tar.gz
  • Upload date:
  • Size: 30.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.7

File hashes

Hashes for mcp_pool-0.2.0.tar.gz
Algorithm Hash digest
SHA256 81d79c30d1b9ac883254269e148036c86f5f543f957d037f58efba2ede5d60e7
MD5 c480431f4cc8d2cef31899270ae0e83d
BLAKE2b-256 f596ac621709fcceff6bf4f531ba69fff732ca40f6248e113ff162c3180d0e0a

See more details on using hashes here.

File details

Details for the file mcp_pool-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: mcp_pool-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 20.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.7

File hashes

Hashes for mcp_pool-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 019648404584781e1c52149c9e1ddf3f6c9c4f47e68b8141df3a1475bd21eea6
MD5 2f4dfb4ebfa0bc6ce78574de592e89b2
BLAKE2b-256 f1ee97be302774e12c16d502c27546cd81ccaeb4dbaa1e402cab37b7a7289b47

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page