Skip to content

Implement non-blocking provider setup with gated tool stubs#28

Merged
BillJr99 merged 1 commit into
mainfrom
claude/confident-cori-p56gd4
Jun 19, 2026
Merged

Implement non-blocking provider setup with gated tool stubs#28
BillJr99 merged 1 commit into
mainfrom
claude/confident-cori-p56gd4

Conversation

@BillJr99

Copy link
Copy Markdown
Owner

Summary

This PR implements non-blocking provider setup for mcpproxy. Instead of blocking the MCP server startup while installing dependencies for all providers, the server now registers tools immediately as gated stubs and runs setup in the background. Tools whose providers are still initializing return a structured retry directive rather than failing.

Key Changes

  • New provider_status.py module: Tracks per-provider readiness state (PENDING/READY/FAILED) and holds real tool handlers once setup completes. Shared between server.py's gated handlers and the frontend UI.

  • Refactored register_provider() in server.py: Extracted handler-building logic into a new build_tool_handlers() function that is setup-independent. This allows handlers to be built either synchronously (old behavior) or after background setup completes (new behavior).

  • New gating mechanism:

    • register_gated_provider(): Registers tools as stubs built from YAML alone, without running setup or executing code.
    • _make_gate_handler(): Creates a handler that dispatches based on provider state — returns retry directive while PENDING, delegates to real handler when READY, or surfaces error when FAILED.
    • _resolve_provider(): Runs setup and builds real handlers on a background thread, flipping provider state from PENDING to READY/FAILED.
  • Configuration knobs:

    • MCPPROXY_BACKGROUND_SETUP (default: 1): Set to 0 to use the old synchronous behavior.
    • MCPPROXY_INIT_RETRY_SECONDS (default: 15): Seconds advertised in retry directives.
  • Startup flow: When background setup is enabled, all providers are registered as gated stubs immediately, then a background thread runs _background_bootstrap() to sequentially resolve each provider's setup and handlers.

  • Updated documentation: README now explains non-blocking startup, the retry directive format, and the configuration options.

Implementation Details

  • The gated stub approach allows the MCP server and UI to come up immediately while setup runs in the background.
  • Setup failures are non-fatal for handler construction — package/REST handlers don't require setup, and code providers may still import already-present modules.
  • The gate handler is async-safe and properly forwards all kwargs to the real handler.
  • Backward compatibility is preserved: setting MCPPROXY_BACKGROUND_SETUP=0 restores the original synchronous behavior.

https://claude.ai/code/session_01RX32pG1y9CvXd2VXHPzM8q

Provider setup (git clone + build, per-requirement pip install, and
setup_commands such as `npx playwright install chrome`) previously ran
synchronously and sequentially at import, before mcp.run() — so the MCP
endpoint accepted no connections until the slowest provider finished
installing.

Now every provider's tools are registered up front as gated stubs built
from the YAML alone, and the slow setup runs on a background daemon thread
started in __main__. The MCP server (and UI) come up immediately; a tool
whose provider is still installing returns a structured retry directive
(`status: "initializing"`, `retry_after_seconds`) instead of failing or
hanging, and the same registered tool transparently delegates to the real
handler once setup completes. A provider whose handlers fail to build
surfaces `status: "failed"` at call time.

- New provider_status.py holds per-provider readiness state shared in-process
  (added to the Dockerfile COPY line; guarded by test_dockerfile.py).
- Extracted build_tool_handlers() from register_provider() so the real
  handlers can be resolved on the background thread; register_provider and
  bootstrap_provider keep their existing behavior (synchronous opt-out path).
- Knobs: MCPPROXY_BACKGROUND_SETUP (default 1) and MCPPROXY_INIT_RETRY_SECONDS
  (default 15).
- Pin PIP_CACHE_DIR / UV_CACHE_DIR to the persisted /root/.cache volume.
- Document the behavior in the README and add tests for the gated flow.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RX32pG1y9CvXd2VXHPzM8q
@BillJr99 BillJr99 merged commit bf8ea88 into main Jun 19, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants