# MCP BridgeKit **Turn any MCP stdio server into a production HTTP API your web app can call.** Per-user session pooling · real timeout handling with background-job fallback · fail-closed auth · live dashboard [![CI](https://github.com/mkbhardwas12/mcp-bridgekit/actions/workflows/ci.yml/badge.svg)](https://github.com/mkbhardwas12/mcp-bridgekit/actions/workflows/ci.yml) [![Release](https://img.shields.io/github/v/release/mkbhardwas12/mcp-bridgekit?color=blue)](https://github.com/mkbhardwas12/mcp-bridgekit/releases) [![Python](https://img.shields.io/badge/python-3.11+-blue?logo=python&logoColor=white)](pyproject.toml) [![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE) [![Security policy](https://img.shields.io/badge/security-policy-informational)](SECURITY.md) [![Stars](https://img.shields.io/github/stars/mkbhardwas12/mcp-bridgekit?style=social)](https://github.com/mkbhardwas12/mcp-bridgekit/stargazers) [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fmkbhardwas12%2Fmcp-bridgekit&env=MCP_BRIDGEKIT_REDIS_URL,MCP_BRIDGEKIT_API_KEY&envDescription=Redis%20URL%20for%20sessions%2Fjobs%20and%20an%20API%20key%20for%20X-API-Key%20auth&envLink=https%3A%2F%2Fgithub.com%2Fmkbhardwas12%2Fmcp-bridgekit%23configuration&project-name=mcp-bridgekit&repository-name=mcp-bridgekit)
``` Your Web App ──HTTP──▶ MCP BridgeKit ──stdio──▶ MCP Server (tool) (React, Next.js, pooling · timeouts · (python, node, npx …) mobile, backend) jobs · auth · dashboard ``` ### 60-second demo ```bash git clone https://github.com/mkbhardwas12/mcp-bridgekit.git && cd mcp-bridgekit export MCP_BRIDGEKIT_API_KEY=$(openssl rand -hex 32) docker compose up -d # Redis + API + 3 workers curl -s -H "X-API-Key: $MCP_BRIDGEKIT_API_KEY" localhost:8000/tools/demo | jq .tools[].name # "analyze_data" curl -s -N -H "X-API-Key: $MCP_BRIDGEKIT_API_KEY" -H 'Content-Type: application/json' \ -d '{"user_id":"demo","messages":[],"tool_name":"analyze_data","tool_args":{"query":"Q4 revenue"}}' \ localhost:8000/chat # data: {"content":[{"type":"text","text":"..."}], ...} ← streamed over SSE # …or, if the tool runs >25s: data: {"status":"queued","job_id":"…"} → GET /job/{id} or SSE /mcp/events/{id} ``` Open http://localhost:8000/dashboard for live sessions, jobs, logs and tools. --- ## Table of Contents - [What Is MCP BridgeKit?](#what-is-mcp-bridgekit) - [The Problem It Solves](#the-problem-it-solves) - [Use Cases & Scenarios](#use-cases--scenarios) - [Architecture](#architecture) - [Request Flow](#request-flow) - [Key Features](#key-features) - [Quickstart](#quickstart) - [Docker (Recommended)](#docker-recommended) - [One-Click Deploy (Vercel)](#one-click-deploy-vercel) - [API Reference](#api-reference) - [Concurrency Model](#concurrency-model) - [Configuration](#configuration) - [Security (Auth & Rate Limiting)](#security-auth--rate-limiting) - [Push Notifications (Webhook & SSE)](#push-notifications-webhook--sse) - [Connecting to Any MCP Server](#connecting-to-any-mcp-server) - [Embedding in Your App](#embedding-in-your-app) - [Project Structure](#project-structure) - [TypeScript Version](#typescript-version) - [End-to-End Testing Guide](#end-to-end-testing-guide) - [Horizontal Scaling](#horizontal-scaling) - [Full Architecture Docs](#-full-architecture-docs) - [Contributing & Security](#contributing--security) - [License](#license) --- ## What Is MCP BridgeKit? **MCP** (Model Context Protocol) is an open standard that lets AI applications connect to external tools and data sources. MCP servers communicate over **stdio** (stdin/stdout) — which means they run as local subprocesses and speak JSON-RPC over pipes. **The problem**: Web applications (React, Next.js, Vue, mobile apps) can't spawn local subprocesses. They only speak HTTP. There's a protocol mismatch. **MCP BridgeKit** is the bridge. It sits between your web app and MCP stdio servers, translating HTTP requests into stdio subprocess calls and streaming results back: ``` Your Web App ──HTTP──▶ MCP BridgeKit ──stdio──▶ MCP Server (tool) (this project) ``` Think of it as **"nginx for MCP tools"** — a reverse proxy that makes stdio tools available over HTTP. --- ## The Problem It Solves | Challenge | Without BridgeKit | With BridgeKit | |-----------|-------------------|----------------| | **Web app needs MCP tools** | Can't — browsers can't spawn subprocesses | `POST /chat` with tool name and args | | **Multiple users sharing tools** | Each needs their own server setup | Per-user session pooling (up to 100 concurrent) | | **Tool call takes 60 seconds** | HTTP gateway timeout (Vercel 30s, CloudFlare 30s) | Auto-queues as background job, client polls `GET /job/{id}` | | **Which tools are available?** | Must read docs or hardcode | `GET /tools/{user_id}` — live discovery | | **Monitoring & debugging** | Blind — no visibility | Live dashboard: sessions, jobs, logs, tools | | **Session cleanup** | Zombie processes leak memory | Auto-eviction (TTL + pool limit) + manual `DELETE /session/{id}` | --- ## Use Cases & Scenarios ### 1. AI Chatbot with Tool Calling > **Scenario**: You're building a customer support chatbot in React. The AI can call tools like `search_docs`, `create_ticket`, `check_order_status` — all implemented as MCP servers. ``` React App → POST /chat {tool: "search_docs", args: {query: "refund policy"}} ← SSE stream with search results ``` BridgeKit manages one MCP server process per conversation, so each user has isolated state. ### 2. Multi-Tenant SaaS Platform > **Scenario**: Your SaaS lets customers connect their own MCP tools (data analysis, code generation, API integrations). Each customer uses different tools. ``` Customer A → POST /chat {user_id: "cust-A", mcp_config: {command: "python", args: ["their_tool.py"]}} Customer B → POST /chat {user_id: "cust-B", mcp_config: {command: "node", args: ["their_tool.js"]}} ``` Each customer gets a dedicated session with their own MCP server. Pool manages up to 100 concurrent sessions with automatic eviction. ### 3. Long-Running Data Processing > **Scenario**: An MCP tool runs complex SQL queries or ML inference that takes 45 seconds. Your frontend uses Vercel with a 30-second timeout. ``` Client → POST /chat {tool: "run_analysis", args: {dataset: "sales_2025"}} ← SSE: {status: "queued", job_id: "abc-123"} # 45 seconds later... Client → GET /job/abc-123 ← {status: "completed", result: {revenue: 4200000, growth: "12%"}} ``` BridgeKit's `asyncio.timeout(25s)` catches the slow call, queues it via Redis/RQ, and a background worker completes it. ### 4. Internal Developer Tools > **Scenario**: Your team has MCP tools for database queries, log analysis, and deployment — you want a single HTTP API to access all of them. ```bash # Query production database curl -X POST localhost:8000/chat \ -d '{"user_id": "dev-1", "tool_name": "query_db", "tool_args": {"sql": "SELECT count(*) FROM users"}}' # Check which tools are available curl localhost:8000/tools/dev-1 ``` Run `docker-compose up` and all your tools are accessible from any HTTP client. ### 5. Webhook / Integration Pipelines > **Scenario**: A Slack bot, Zapier workflow, or n8n pipeline needs to call MCP tools based on triggers. ``` Slack Event → Zapier → POST /chat {tool: "summarize", args: {text: "..."}} ← {result: "Here's the summary..."} ``` BridgeKit is a standard HTTP API — any integration platform can call it. ### 6. Mobile Applications > **Scenario**: An iOS/Android app needs to call MCP tools but can't run subprocesses on the device. ``` Mobile App → POST https://your-server.com/chat ← SSE stream or job_id for polling ``` Deploy BridgeKit on your server, and mobile clients communicate over HTTPS. ### When NOT to Use BridgeKit | Scenario | Better Alternative | |----------|-------------------| | CLI tool calling MCP servers locally | Use MCP SDK directly — no HTTP needed | | MCP server already speaks HTTP (Streamable HTTP transport) | Connect directly — no bridge needed | | Single-user desktop app | MCP SDK + stdio directly | --- ## Architecture ```mermaid graph LR subgraph Clients A[🌐 Web Chatbot] B[⌨️ CLI / Script] C[🔗 Third-party API] end subgraph BridgeKit D[🚀 FastAPI Server\napp.py] E[⚡ BridgeKit Core\ncore.py] F[📊 Dashboard\ndashboard.py] end subgraph Backend G[🧠 MCP Server\nstdio process] H[(💾 Redis)] I[👷 RQ Worker\nworker.py] end A -- POST /chat --> D B -- POST /chat --> D C -- POST /chat --> D A -. GET /dashboard .-> F D --> E E -- stdio --> G G -- stdout --> E E -. timeout? .-> H H --> I I -- stdio --> G I -- store result --> H E -. poll result .-> H style E fill:#10b981,stroke:#059669,color:#fff style H fill:#f59e0b,stroke:#d97706,color:#fff style D fill:#3b82f6,stroke:#2563eb,color:#fff style G fill:#8b5cf6,stroke:#7c3aed,color:#fff style I fill:#a855f7,stroke:#9333ea,color:#fff ``` --- ## Request Flow ```mermaid sequenceDiagram participant Client participant FastAPI participant BridgeKit as BridgeKit Core participant MCP as MCP Server participant Redis participant Worker as RQ Worker Client->>FastAPI: POST /chat {user_id, tool_name, tool_args} FastAPI->>BridgeKit: bridge.call(req) BridgeKit->>BridgeKit: Get/create session (per-user lock) BridgeKit->>MCP: call_tool(name, args) with asyncio.timeout(25s) alt ✅ Completes within timeout MCP-->>BridgeKit: Tool result BridgeKit-->>FastAPI: SSE: data: {result} FastAPI-->>Client: Stream response else ⏱ Timeout exceeded BridgeKit->>Redis: Enqueue job + set status=queued BridgeKit-->>FastAPI: SSE: {status: queued, job_id} FastAPI-->>Client: Return job_id Worker->>Redis: Pick up job Worker->>MCP: Spawn session + call_tool MCP-->>Worker: Result Worker->>Redis: Store result + set status=completed Client->>FastAPI: GET /job/{job_id} FastAPI->>Redis: Get status + result Redis-->>FastAPI: {status: completed, result: ...} FastAPI-->>Client: Return result end ``` --- ## Key Features - **Per-user sessions**: Each `user_id` gets its own MCP stdio process - **Real timeout handling**: `asyncio.timeout()` wraps every tool call — if it exceeds the threshold, the call is automatically queued as a background job via Redis/RQ - **Background job polling**: `GET /job/{job_id}` to check status/results - **Tool discovery**: `GET /tools/{user_id}` lists available tools from the MCP server - **Session management**: Auto-eviction when pool is full, TTL-based expiry, manual `DELETE /session/{user_id}` - **Live dashboard**: HTMX + Tailwind — sessions, jobs, logs, tools (no build step) - **Structured logging**: via structlog - **API key auth** _(v0.8)_: `X-API-Key` header protection — **required by default**; opt out only with `ALLOW_NO_AUTH=true` - **MCP command allowlist**: clients can only spawn `DEFAULT_MCP_COMMAND` plus anything in `ALLOWED_MCP_COMMANDS` - **Rate limiting** _(v0.8)_: Per-user fixed-window via Redis (default 60 req/min), returns HTTP 429 - **Retry with backoff** _(v0.8)_: Transient failures retried up to 2× with exponential backoff (1s, 2s) - **Prometheus metrics** _(v0.8)_: `GET /metrics` in Prometheus exposition format — scrape with any monitoring stack - **Structured error codes** _(v0.8)_: All errors include `error_code` (e.g. `TOOL_CALL_FAILED`, `RATE_LIMITED`) - **Webhook push** _(v0.9)_: Worker POSTs job result to your URL when a background job completes - **SSE push** _(v0.9)_: `GET /mcp/events/{job_id}` — browser/client listens in real-time, stream closes automatically on completion ## Quickstart ```bash # Clone & install git clone https://github.com/mkbhardwas12/mcp-bridgekit.git cd mcp-bridgekit # Copy environment config cp .env.example .env # REQUIRED: set an API key (protected endpoints return 401 until you do) echo "MCP_BRIDGEKIT_API_KEY=$(openssl rand -hex 32)" >> .env # Install (pick one) uv sync --dev # recommended — fastest pip install -e ".[dev]" # alternative # Start Redis (required for job queue) docker run -d -p 6379:6379 redis:7-alpine # Terminal 1 — Run the server uvicorn mcp_bridgekit.app:app --reload # Terminal 2 — Start the background worker mcp-bridgekit-worker ``` Open: - http://localhost:8000 — Landing page - http://localhost:8000/dashboard — Live dashboard - http://localhost:8000/docs — Interactive API docs ## Docker (Recommended) ```mermaid graph TB subgraph Docker Compose R[(Redis\nredis:7-alpine\nport 6379)] S[BridgeKit Server\npython:3.12-slim\nport 8000] W[RQ Worker\npython:3.12-slim] end Internet((Internet)) -->|:8000| S S --> R W --> R style R fill:#ef4444,stroke:#dc2626,color:#fff style S fill:#3b82f6,stroke:#2563eb,color:#fff style W fill:#8b5cf6,stroke:#7c3aed,color:#fff ``` ```bash export MCP_BRIDGEKIT_API_KEY=$(openssl rand -hex 32) # or put it in .env docker-compose up ``` This starts Redis, the BridgeKit server (port 8000), and 3 RQ worker replicas. Compose refuses to start without `MCP_BRIDGEKIT_API_KEY`. By default the API is bound to **127.0.0.1** only. To expose it (behind TLS / a reverse proxy): ```bash MCP_BRIDGEKIT_BIND=0.0.0.0 docker-compose up ``` ## One-Click Deploy (Vercel) [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fmkbhardwas12%2Fmcp-bridgekit&env=MCP_BRIDGEKIT_REDIS_URL&envDescription=Redis%20connection%20URL%20for%20session%20and%20job%20storage&envLink=https%3A%2F%2Fgithub.com%2Fmkbhardwas12%2Fmcp-bridgekit%23configuration&project-name=mcp-bridgekit&repository-name=mcp-bridgekit) Click the button above to deploy your own instance. You'll need: 1. A **Redis instance** (e.g., [Upstash](https://upstash.com), [Railway](https://railway.app), or [Redis Cloud](https://redis.com/cloud/)) 2. Set `MCP_BRIDGEKIT_REDIS_URL` and `MCP_BRIDGEKIT_API_KEY` (e.g. `openssl rand -hex 32`) during setup > **Note**: Vercel's 30s function timeout is exactly why BridgeKit exists — any MCP tool call exceeding 25s is automatically queued as a background job. Your users get a `job_id` instantly and poll for results. ## API Reference ### `POST /chat` Call an MCP tool. Returns SSE stream. Auto-queues on timeout. ```json { "user_id": "user-123", "messages": [{"role": "user", "content": "analyze sales data"}], "tool_name": "analyze_data", "tool_args": {"query": "Q4 revenue trends"}, "mcp_config": {"command": "python", "args": ["examples/mcp_server.py"]} } ``` `mcp_config` is optional (defaults to `DEFAULT_MCP_COMMAND`/`DEFAULT_MCP_ARGS`). `command` must be in the allowlist — anything else returns HTTP 400 `COMMAND_NOT_ALLOWED`. Only `command` and `args` are accepted. ### `GET /job/{job_id}` Poll background job status. Returns `queued`, `running`, `completed` (with result), or `failed`. ### `GET /tools/{user_id}` List available tools from the server's default MCP server (`DEFAULT_MCP_COMMAND` / `DEFAULT_MCP_ARGS`). ### `DELETE /session/{user_id}` Close a user's MCP session. ### `GET /health` Health check with stats: active sessions, total requests, errors, queued jobs, known tools. ### `GET /metrics` Prometheus-compatible metrics in text exposition format. Safe to scrape without auth. ``` bridgekit_active_sessions 3 bridgekit_requests_total 472 bridgekit_errors_total 2 bridgekit_queued_jobs 1 ... ``` Configure Prometheus: ```yaml scrape_configs: - job_name: mcp-bridgekit static_configs: - targets: [bridgekit:8000] metrics_path: /metrics ``` ### `GET /mcp/events/{job_id}` _(v0.9)_ Server-Sent Events stream. Connect after submitting a long job; fires **one event** when the background job completes, then closes automatically. No auth required. ```javascript const es = new EventSource(`/mcp/events/${jobId}`); es.onmessage = (e) => { const data = JSON.parse(e.data); if (data.status === 'completed') { console.log('Job done!', data.result); es.close(); } }; ``` Event payload: ```json { "job_id": "abc-123", "status": "completed", "result": { ... }, "user_id": "user-456", "completed_at": "2026-02-23T12:00:00+00:00" } ``` ### Error Response Format (v0.8.0+) All error payloads include a structured `error_code` field: ```json { "status": "error", "error_code": "TOOL_CALL_FAILED", "message": "...", "attempts": 3 } { "status": "queued", "error_code": "TOOL_TIMED_OUT", "job_id": "..." } ``` | Error Code | Cause | |---|---| | `SESSION_CREATE_FAILED` | Could not spawn MCP server subprocess | | `TOOL_CALL_FAILED` | Tool raised an exception (after all retries) | | `TOOL_TIMED_OUT` | Tool exceeded timeout — job queued | | `RATE_LIMITED` | User exceeded requests-per-minute limit | | `COMMAND_NOT_ALLOWED` | `mcp_config.command` is not in the allowlist | | `AUTH_NOT_CONFIGURED` | No `API_KEY` set and `ALLOW_NO_AUTH` is false | | `MISSING_API_KEY` | Auth enabled but header absent | | `INVALID_API_KEY` | Wrong API key provided | ## Concurrency Model ```mermaid graph TD R1[Request user-abc] --> GL R2[Request user-abc] --> GL R3[Request user-xyz] --> GL GL[🔒 Global Lock\nheld for nanoseconds] GL -->|get/create| UL1[🔑 Lock: user-abc] GL -->|get/create| UL2[🔑 Lock: user-xyz] UL1 -->|serialize| S1[Session: user-abc\nMCP stdio process] UL2 -->|serialize| S2[Session: user-xyz\nMCP stdio process] S1 --> POOL[(Session Pool\nTTL: 3600s\nEviction: oldest)] S2 --> POOL style GL fill:#ef4444,stroke:#dc2626,color:#fff style UL1 fill:#f59e0b,stroke:#d97706,color:#fff style UL2 fill:#f59e0b,stroke:#d97706,color:#fff style POOL fill:#10b981,stroke:#059669,color:#fff ``` ## Configuration Set via environment variables or `.env` file (prefix: `MCP_BRIDGEKIT_`): | Variable | Default | Description | |----------|---------|-------------| | `REDIS_URL` | `redis://localhost:6379` | Redis connection | | `MAX_SESSIONS` | `100` | Max concurrent MCP sessions | | `SESSION_TTL_SECONDS` | `3600` | Session expiry (1 hour) | | `TIMEOUT_THRESHOLD_SECONDS` | `25.0` | Seconds before queuing as background job | | `JOB_RESULT_TTL_SECONDS` | `600` | How long job results stay in Redis | | `DEFAULT_MCP_COMMAND` | `python` | Default MCP server command | | `DEFAULT_MCP_ARGS` | `["examples/mcp_server.py"]` | Default MCP server args | | `ALLOWED_MCP_COMMANDS` | `[]` | Extra commands clients may request via `mcp_config.command`. The default command is always allowed. | | `API_KEY` | _(empty)_ | **Required.** API key for `X-API-Key` header auth. Protected endpoints return 401 until set. | | `ALLOW_NO_AUTH` | `false` | Explicitly run without an API key. Only for trusted/private networks. | | `RATE_LIMIT_PER_MINUTE` | `60` | **v0.8** Max requests per `user_id` per minute. `0` = disabled. | | `MAX_TOOL_RETRIES` | `2` | **v0.8** Retry transient tool failures N times with exponential backoff. | | `WEBHOOK_URL` | _(empty)_ | **v0.9** URL to POST job completion payload to. Leave empty to disable. | | `ENABLE_SSE` | `true` | **v0.9** Publish completion events to `GET /mcp/events/{job_id}`. | ## Security (Auth & Rate Limiting) Auth is **fail-closed**: with no `API_KEY` configured, protected endpoints return HTTP 401 `AUTH_NOT_CONFIGURED`. Set `MCP_BRIDGEKIT_ALLOW_NO_AUTH=true` only on trusted private networks. ### API Key Authentication Protects `/chat`, `/tools/`, `/job/`, and `/session/` endpoints. `/health`, `/metrics`, and `/dashboard` stay public for monitoring tools. ```bash # Generate and set a key export MCP_BRIDGEKIT_API_KEY=$(openssl rand -hex 32) # All protected calls need the header curl -X POST http://bridgekit:8000/chat \ -H "X-API-Key: your-secret-key" \ -H "Content-Type: application/json" \ -d '{...}' ``` Without the key (or with wrong key): HTTP 401 with `error_code: MISSING_API_KEY` or `INVALID_API_KEY`. ### MCP Command Allowlist `mcp_config.command` is validated against an allowlist before any subprocess is spawned. By default only `DEFAULT_MCP_COMMAND` is allowed; extend it explicitly: ```bash export MCP_BRIDGEKIT_ALLOWED_MCP_COMMANDS='["node", "npx"]' ``` Requests naming any other command get HTTP 400 `COMMAND_NOT_ALLOWED`. `mcp_config` accepts only `command` and `args` — extra fields (`env`, `cwd`, …) are rejected with 422. ### Rate Limiting Per-user fixed-window rate limiting backed by Redis. Rejects excess requests with HTTP 429. ```bash # Default: 60 requests per user_id per minute export MCP_BRIDGEKIT_RATE_LIMIT_PER_MINUTE=60 # Disable entirely export MCP_BRIDGEKIT_RATE_LIMIT_PER_MINUTE=0 ``` ### Retry with Backoff Transient tool-call failures (e.g. subprocess crash) are automatically retried. ```bash # Default: 2 retries — waits 1s, then 2s between attempts export MCP_BRIDGEKIT_MAX_TOOL_RETRIES=2 # Disable retries export MCP_BRIDGEKIT_MAX_TOOL_RETRIES=0 ``` Note: `TimeoutError` is **never retried** — it immediately queues as a background job. --- ## Push Notifications (Webhook & SSE) Added in v0.9.0. Instead of polling `GET /job/{job_id}`, get **instant push** when a background job finishes. ### Option 1: Webhook (AWS API / backend) BridgeKit POSTs the result to your configured URL when any background job completes. ```bash export MCP_BRIDGEKIT_WEBHOOK_URL=https://your-api.example.com/webhook/mcp ``` Payload your endpoint receives: ```json { "job_id": "abc-123", "status": "completed", "result": { ... }, "user_id": "user-456", "completed_at": "2026-02-23T12:00:00+00:00" } ``` Webhook failures are caught and logged — they never crash the worker. ### Option 2: SSE (browser / real-time frontend) Your frontend holds open a Server-Sent Events connection. The stream closes automatically after the completion event. ```javascript // After POST /chat returns {status: "queued", job_id: "abc-123"} const es = new EventSource(`/mcp/events/abc-123`); es.onmessage = (e) => { const data = JSON.parse(e.data); if (data.status === 'completed') { renderResult(data.result); es.close(); } }; ``` SSE uses Redis Pub/Sub internally, so it works correctly when the API server and RQ worker are in separate processes/containers. ### Using Both Together (AWS example) ```python # Your AWS Lambda / FastAPI endpoint @app.post("/api/v1/analyze") async def analyze(req: BridgeRequest): result = await bridge.call(req) if result.status == "queued": return { "status": "queued", "job_id": result.job_id, "sse_url": f"https://bridgekit.yourdomain.com/mcp/events/{result.job_id}" } return result ``` Disable either channel independently: ```bash MCP_BRIDGEKIT_ENABLE_SSE=false # turn off SSE, keep webhook MCP_BRIDGEKIT_WEBHOOK_URL= # leave empty to disable webhook ``` --- ## Connecting to Any MCP Server BridgeKit works with **any MCP server** — you just pass different `mcp_config` values. The `mcp_config` tells BridgeKit which command to spawn: ### Supported MCP Servers | MCP Server | `command` | `args` | Example Tools | |---|---|---|---| | **AWS MCP** | `npx` | `["-y", "@aws/aws-mcp"]` | `describe_instances`, `list_s3_buckets` | | **AWS CDK** | `npx` | `["-y", "@aws/aws-cdk-mcp-server"]` | `GenerateCDK`, `ExplainCDK` | | **AWS Docs** | `npx` | `["-y", "@aws/aws-documentation-mcp-server"]` | `search_documentation` | | **GitHub** | `npx` | `["-y", "@modelcontextprotocol/server-github"]` | `search_repositories`, `create_issue` | | **Filesystem** | `npx` | `["-y", "@modelcontextprotocol/server-filesystem", "/data"]` | `read_file`, `write_file` | | **PostgreSQL** | `npx` | `["-y", "@modelcontextprotocol/server-postgres"]` | `query` | | **Custom Python** | `python` | `["/path/to/server.py"]` | Whatever you define | ### Quick Example: AWS MCP from Your API ```python import httpx BRIDGEKIT_URL = "http://your-bridgekit-host:8000" # Step 1: Discover available tools resp = await httpx.AsyncClient().get( f"{BRIDGEKIT_URL}/tools/discovery", params={"command": "npx", "args": "-y,@aws/aws-mcp"}, ) print(resp.json()) # Shows all tools with schemas # Step 2: Call a tool async with httpx.AsyncClient(timeout=60.0) as client: resp = await client.post(f"{BRIDGEKIT_URL}/chat", json={ "user_id": "user-123", "messages": [{"role": "user", "content": "list instances"}], "mcp_config": { "command": "npx", "args": ["-y", "@aws/aws-mcp"], }, "tool_name": "describe_instances", "tool_args": {"region": "us-east-1"}, }) ``` Prerequisites: Install the MCP server package on the BridgeKit host (`npm install -g @aws/aws-mcp`) and configure AWS credentials. > **Full guide**: See [docs/INTEGRATION_GUIDE.md](docs/INTEGRATION_GUIDE.md) for deployment on AWS, credential setup, polling for long-running jobs, and complete working examples. > **Code examples**: See [examples/aws_integration.py](examples/aws_integration.py) for 8 ready-to-use endpoint patterns. ## Embedding in Your App ```python from fastapi import FastAPI from mcp_bridgekit import BridgeKit, BridgeRequest app = FastAPI() bridge = BridgeKit() @app.post("/chat") async def chat(req: BridgeRequest): return await bridge.call(req) ``` ## Project Structure ```mermaid graph LR subgraph src/mcp_bridgekit APP[app.py\nFastAPI routes] CORE[core.py\nBridgeKit engine ⚡] CFG[config.py\npydantic-settings] MDL[models.py\nPydantic models] WRK[worker.py\nRQ worker] DASH[dashboard.py\n/dashboard] LAND[landing.py\n/ landing] end APP --> CORE APP --> DASH APP --> LAND CORE --> CFG CORE --> MDL WRK --> CFG style CORE fill:#10b981,stroke:#059669,color:#fff style APP fill:#3b82f6,stroke:#2563eb,color:#fff style WRK fill:#8b5cf6,stroke:#7c3aed,color:#fff ``` ## TypeScript Version A TypeScript implementation is available in `ts/`. Same architecture — session pooling, timeout handling, Redis queueing. ```bash cd ts && npm install && npm run build && npm start ``` ## End-to-End Testing Guide Verify everything works with these steps: ### 1. Start Services ```bash # Option A — Docker (easiest) docker-compose up --build # Option B — Manual (3 terminals) # T1: docker run -d -p 6379:6379 redis:7-alpine # T2: mcp-bridgekit-worker # T3: uvicorn mcp_bridgekit.app:app --reload --port 8000 ``` ### 2. Check Dashboard Open http://localhost:8000/dashboard — should show 0 sessions, 0 jobs. ### 3. Call a Tool (instant response) ```bash curl -N -X POST http://localhost:8000/chat \ -H "Content-Type: application/json" \ -d '{ "user_id": "test-001", "messages": [{"role": "user", "content": "hello"}], "tool_name": "analyze_data", "tool_args": {"query": "test query"} }' ``` → SSE stream with result appears immediately. Dashboard shows 1 active session. ### 4. Test Timeout Survival (background job) Edit `examples/mcp_server.py` — change `asyncio.sleep(2)` to `asyncio.sleep(35)`, then re-run the curl above. → Immediate response: `{"status": "queued", "job_id": "..."}` → Dashboard updates live (Queued Jobs increases) → Worker terminal shows processing ### 5. Poll Job Result ```bash curl http://localhost:8000/job/{job_id_from_step_4} ``` → Returns `{"status": "completed", "result": {...}}` once the worker finishes. ### 6. List Available Tools ```bash curl "http://localhost:8000/tools/test-001" ``` ### 7. Cleanup Session ```bash curl -X DELETE http://localhost:8000/session/test-001 ``` ### 8. Health Check ```bash curl http://localhost:8000/health ``` → Returns active sessions, request counts, error counts, queue depth. **Expected result**: Tools respond instantly → slow tools get queued → dashboard shows live updates → sessions scale cleanly. --- ## Horizontal Scaling BridgeKit is designed to scale horizontally: | Component | How to Scale | Notes | |-----------|-------------|-------| | **Web server** | `gunicorn --workers N` | Dockerfile defaults to 4 workers. Each worker has its own event loop. | | **RQ workers** | Run multiple `mcp-bridgekit-worker` processes | docker-compose defaults to 3 replicas. All point to the same Redis. | | **Redis** | Use managed Redis (Upstash, ElastiCache, Redis Cloud) | Single Redis handles thousands of connections. | | **Multi-machine** | Run workers on different machines pointing to same Redis | `MCP_BRIDGEKIT_REDIS_URL=redis://your-redis-host:6379` | ```bash # Scale workers to 5 replicas docker-compose up --scale worker=5 # Or run standalone workers on any machine MCP_BRIDGEKIT_REDIS_URL=redis://shared-redis:6379 mcp-bridgekit-worker ``` --- ## 📐 Full Architecture Docs See [ARCHITECTURE.md](ARCHITECTURE.md) for detailed diagrams and component docs. When running the server, visit `/architecture` for an interactive HTML version. ## Contributing & Security Issues and pull requests are welcome — see [CONTRIBUTING.md](CONTRIBUTING.md). Every fix or hardening that lands is credited in [CHANGELOG.md](CHANGELOG.md). To report a vulnerability privately, use [GitHub's security advisories](https://github.com/mkbhardwas12/mcp-bridgekit/security/advisories/new) — details in [SECURITY.md](SECURITY.md). ### Acknowledgements - [@NakedoShadow](https://github.com/NakedoShadow) — timing-safe API key comparison, SSE connection timeout, `.dockerignore` / `.gitignore` hardening ([#1](https://github.com/mkbhardwas12/mcp-bridgekit/pull/1)) - [@shunfeng8421](https://github.com/shunfeng8421) (blueQ) — reported the unauthenticated RCE via attacker-controlled MCP stdio command that led to fail-closed auth and the command allowlist ([#3](https://github.com/mkbhardwas12/mcp-bridgekit/issues/3)) ## License MIT