ToolBridge
Provides tools for interacting with the GitHub REST API, enabling searches for repositories, listing issues, and retrieving file contents from a repository.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@ToolBridgesearch GitHub for repositories matching 'mcp server' in Python"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
ToolBridge — Secure MCP Server for Authenticated Tool Execution
Streamable HTTP is the production transport; stdio is used only for local development and rapid iteration.
ToolBridge is a production-grade MCP (Model Context Protocol) server that exposes authenticated, authorized external tools to any MCP-compatible client. It enforces per-user authentication, per-user authorization, secure credential handling, tool-level permissions, audit logging, failure handling, and observability. MCP-compatible AI agents and clients connect to ToolBridge to access its tools — the agent/client itself is not part of this project.
Architecture
MCP-compatible Client or AI Agent (external — not part of this project)
↓
MCP (Streamable HTTP — production | stdio — local dev)
↓
ToolBridge MCP Server
↓
Authentication
↓
Authorization / Policy
↓
Tool Executor
↓
External APIs (GitHub REST API)The server combines two concerns, kept deliberately separate:
Concern | Layer |
MCP protocol, tool registration, tool routing | Official Python MCP SDK server |
HTTP authentication, OAuth callback | Thin FastAPI auth layer |
Related MCP server: Broker
Phase Status
Phase | Description | Status |
Phase 0 | Architecture & Requirements | ✅ COMPLETE |
Phase 1 | Project Foundation | ✅ COMPLETE |
Phase 2 | MCP Server Core | ✅ COMPLETE |
Phase 3 | GitHub Tool Integrations | ✅ COMPLETE |
Phase 4 | MCP Transport & Protocol | ✅ COMPLETE |
Phase 5 | Authentication | ⏳ NEXT |
Phase 6 | Authorization & Permissions | ⏳ |
Phase 7 | Secure Credential Management | ⏳ |
Phase 8 | Policy Engine | ⏳ |
Phase 9 | Reliability & Error Handling | ⏳ |
Phase 10 | Audit Logging & Observability | ⏳ |
Phase 11 | Security Hardening | ⏳ |
Phase 12 | MCP Server Evaluation | ⏳ |
Phase 13 | Production Deployment | ⏳ |
Phase 14 | Documentation & Demo | ⏳ |
Technology Decisions
Current Implementation (Phases 1–4)
Technology | Purpose | Status |
Python 3.12 | Primary language | ✅ Implemented |
uv | Dependency management & virtual environments | ✅ Implemented |
FastAPI | Web framework (MCP HTTP transport, | ✅ Implemented |
pydantic-settings | Typed configuration from environment variables | ✅ Implemented |
structlog | Structured application logging | ✅ Implemented |
SQLAlchemy (async) | Async ORM / database connectivity | ✅ Implemented |
asyncpg | Async PostgreSQL driver | ✅ Implemented |
Alembic | Database schema migrations | ✅ Implemented |
PostgreSQL 16 | Primary data store (via Docker) | ✅ Implemented |
Docker / Docker Compose | Containerisation & local dev environment | ✅ Implemented |
pytest | Test framework | ✅ Implemented |
Ruff | Linting & formatting | ✅ Implemented |
mypy | Static type checking | ✅ Implemented |
GitHub Actions | CI pipeline | ✅ Implemented |
Official Python MCP SDK ( | MCP server primitives, tool registration, in-process client testing | ✅ Implemented (Phase 2) |
httpx | Async HTTP client for GitHub REST API calls | ✅ Implemented (Phase 3) |
respx | httpx mock library for unit-testing GitHub client | ✅ Implemented (Phase 3) |
GitHub REST API | External tool target ( | ✅ Implemented (Phase 3) |
Future Phases (not yet implemented)
Technology | Purpose | Phase |
GitHub OAuth | User identity via GitHub authorization | Phase 5 |
JWT | Short-lived session tokens for MCP requests | Phase 5 |
Phase 4 — MCP Transport & Protocol — ✅ COMPLETE
Integrated the MCP Streamable HTTP transport into the FastAPI application, enabling real MCP clients to connect over the network. The server now fully supports end-to-end tool execution over HTTP, distinct from the in-process testing implemented in Phase 2.
What was delivered
Item | Detail |
HTTP Transport Mount |
|
Session Management | Added |
E2E Transport Tests |
|
Test suite updates | 4 new HTTP transport tests, covering tool discovery and error handling |
Phase 3 — GitHub Tool Integrations — ✅ COMPLETE
Implemented the GitHubClient — a typed, async HTTP client wrapping the GitHub REST API — and wired all three MCP tools to it. Tools now make real API calls and return live data. Phase 3 raises the test count from 4 to 11 passing tests while maintaining 0 Ruff issues and 0 mypy issues.
What was delivered
Item | Detail |
GitHub client |
|
| Issues |
| Issues |
| Issues |
Typed responses |
|
Error hierarchy |
|
MCP tool wiring | All three tools in |
Test suite |
|
Updated MCP tests |
|
Ruff | 0 issues |
mypy | 0 issues |
Pull Request Filtering in list_issues
GitHub's REST API returns pull requests as part of the issues endpoint (a PR is technically a kind of issue in their data model). Each raw item includes a "pull_request" key only if it is actually a PR. GitHubClient.list_issues filters those out so the tool returns genuine issues only, matching what the tool name promises callers.
Base64 Decoding in get_file_contents
GitHub's Contents API returns file content as a Base64-encoded string. GitHubClient.get_file_contents decodes it to a UTF-8 string before returning, so callers receive human-readable text without needing to know about the encoding layer. If the target path is a directory (response is a JSON array rather than an object), the client raises GitHubClientError with a clear "directory" message.
Error Handling
All three client methods map GitHub HTTP status codes to typed exceptions before surfacing them:
HTTP Status | Exception |
401, 403 |
|
404 |
|
429 |
|
Other 4xx/5xx |
|
Phase 2 — MCP Server Core — ✅ COMPLETE
Implemented the core ToolBridge MCP server using the MCP 2.x MCPServer API
(mcp.server.mcpserver.MCPServer). The server exposes three discoverable MCP tools
with explicit Pydantic input/output schemas, backed by in-process tests that cover
both tool invocation and MCP tool discovery. Phase 2 maintains the quality bar
established in Phase 1: 4 passing tests, Ruff passing with 0 issues, and mypy
passing with 0 issues.
What was delivered
Item | Detail |
MCP server instance |
|
Tool: | Input: |
Tool: | Input: |
Tool: | Input: |
Pydantic output schemas |
|
Test: tool invocation × 3 |
|
Test: tool discovery |
|
Test suite |
|
Ruff | 0 issues |
mypy | 0 issues |
MCP SDK Version Drift & Migration
During Phase 2 a real MCP SDK version-drift issue was encountered and deliberately resolved.
Initial implementation used the MCP 1.x API:
from mcp.server.fastmcp import FastMCPThe problem: when the virtual environment was recreated and dependencies were
resolved, uv resolved MCP 2.1.1 (the latest available version satisfying the
broad mcp>=1.9.0 constraint in pyproject.toml). The mcp.server.fastmcp module
does not exist in MCP 2.x, causing:
ModuleNotFoundError: No module named 'mcp.server.fastmcp'Deliberate resolution: rather than silently adapting code until the error
disappeared, the project treated this as a real API compatibility issue and
deliberately migrated from the MCP 1.x FastMCP API to the MCP 2.x MCPServer
API:
MCP 1.x
FastMCP (mcp.server.fastmcp)
↓
API / version drift discovered (mcp.server.fastmcp absent in MCP 2.x)
↓
MCP 2.x
MCPServer (mcp.server.mcpserver)The final import in the implementation is:
from mcp.server.mcpserver import MCPServerThe uv.lock was updated as part of this resolution and now pins the resolved
version to MCP 2.1.1. The pyproject.toml dependency constraint (mcp>=1.9.0)
remains broad enough to have admitted MCP 2.x; constraining it to mcp>=2,<3
would pin the project to MCP 2.x while preventing an uncontrolled future
major-version API change from silently breaking the server. That constraint update
is tracked as a follow-on hygiene item.
MCP Tools
Three read-oriented tools are registered on the MCP server as of Phase 3:
search_repositories(query) → SearchRepositoriesResult
list_issues(repo, state="open") → ListIssuesResponse
get_file_contents(repo, path) → FileContentsResponseThese tools are discoverable (verified by test_tools_are_discoverable_with_schemas),
return schema-driven structured output (verified by the three invocation tests),
and make real GitHub REST API calls via GitHubClient (Phase 3).
Planned Database Schema
users (id, github_username, created_at)
user_tokens (id, user_id, provider, access_token, refresh_token NULL, expires_at NULL, scopes[])
tool_permissions (id, user_id, tool_name, allowed)
audit_log (id, user_id, tool_name, request_payload, response_status, latency_ms, created_at)Note:
refresh_tokenandexpires_atare intentionally nullable. The exact GitHub token lifecycle will be verified during Phase 5 before the schema is finalised.
Getting Started (Phases 1–4)
Prerequisites
Python 3.12+
Docker & Docker Compose
A GitHub personal access token (for tool execution)
Setup
# Clone and enter the project
git clone <repo-url>
cd toolbridge
# Copy environment configuration
cp .env.example .env
# Edit .env — set GITHUB_TOKEN to your GitHub personal access token
# Install dependencies
uv sync
# Start PostgreSQL
docker compose up -d
# Run the application
uv run uvicorn toolbridge.main:app --reload
# Verify health
curl http://localhost:8000/health
# {"status":"ok","db":"ok"}Running Tests
# Unit tests (no external services required)
uv run pytest tests/unit/
# GitHub client tests (uses respx HTTP mocks — no real API calls)
uv run pytest tests/github/
# MCP server tests (uses respx HTTP mocks — no real API calls)
uv run pytest tests/mcp/
# Integration tests (requires PostgreSQL from docker compose up -d)
uv run pytest tests/integration/ -m integration
# All tests
uv run pytestCode Quality
# Linting
uv run ruff check .
# Formatting check
uv run ruff format --check .
# Type checking
uv run mypy .Database Migrations
# Run pending migrations
uv run alembic upgrade head
# Create a new migration
uv run alembic revision --autogenerate -m "description"Project Structure
toolbridge/
│
├── src/
│ └── toolbridge/
│ ├── config/ # Typed settings (pydantic-settings)
│ ├── logging/ # Structured logging (structlog)
│ ├── db/ # Async SQLAlchemy engine + health check
│ ├── server/ # FastAPI application + MCP HTTP transport (Phase 4)
│ ├── github/ # GitHub REST API client + typed responses (Phase 3)
│ └── mcp/ # MCP server instance + tool definitions (Phase 2–3)
│
├── tests/
│ ├── unit/ # Tests with no external service dependencies
│ ├── integration/ # Tests requiring PostgreSQL
│ ├── mcp/ # MCP server and HTTP transport tests (Phase 2–4)
│ └── github/ # GitHub client unit tests with respx mocks (Phase 3)
│
├── migrations/ # Alembic migration scripts
├── docs/ # Architecture documentation
├── scripts/ # Development utility scripts
├── .github/workflows/ # GitHub Actions CI
│
├── pyproject.toml # Project metadata, dependencies, tool config
├── Dockerfile # Multi-stage production image
├── docker-compose.yml # Local development environment (PostgreSQL)
├── .env.example # Environment variable reference (no secrets)
└── README.mdObservability
Logs are emitted in JSON format in production and human-readable format in development.
Every log record includes: timestamp, level, logger, message.
Future phases will add: request_id, user_id, tool_name, latency_ms, status.
Security Notes
Secrets are never committed. The
.envfile is in.gitignore.See
.env.examplefor all required environment variables.OAuth credentials, JWT secrets, and API keys are out of scope until Phase 5/7.
Engineering Decisions & Implementation Notes
MCP SDK Version Drift (Phase 2)
During Phase 2, MCP SDK version drift exposed an API incompatibility between the
original MCP 1.x FastMCP implementation and the MCP 2.x package resolved by uv
(mcp==2.1.1). The issue was resolved deliberately by migrating the server from
FastMCP to MCPServer and updating the code to the MCP 2.x API at
mcp.server.mcpserver.
This keeps the implementation aligned with the resolved MCP major version. Constraining
the pyproject.toml dependency to mcp>=2,<3 is tracked as a follow-on step to
prevent an uncontrolled future major-version upgrade from introducing another
breaking API change.
GitHub Client Design (Phase 3)
GitHubClient is a thin, typed wrapper around httpx.AsyncClient. Each method maps
to a single GitHub REST API endpoint and converts HTTP error codes into a typed
exception hierarchy before they propagate to the MCP tool layer. This keeps error
handling logic out of the MCP tool handlers themselves and makes the client testable
in isolation with respx HTTP mocks — no real network calls are required by any
unit test.
This server cannot be deployed
Maintenance
Related MCP Connectors
Runtime permission, approval, and audit layer for AI agent tool execution.
Security gateway for AI agents: policy, approval, and audited execution, no secrets shared.
OAuth 2.1 short-link tools for AI agents with scoped tokens, approvals, audit logs, and revocation.
- FullmaktOAuthai.fullmakt
Credential broker for AI agents: scoped, revocable API access with policy enforcement and audit.
Related MCP Servers
- AlicenseNot gradedqualityAmaintenanceEnables secure enterprise AI agents to access internal tools like GitHub, Gmail, Calendar, file systems, databases, and knowledge bases through the Model Context Protocol, with built-in security, audit, and observability.MIT
- AlicenseNot gradedqualityDmaintenanceEnables AI agents to securely perform privileged actions like creating GitHub issues by minting short-lived, single-purpose tokens on demand, with policy enforcement and audit logging.MIT
- FlicenseNot gradedqualityCmaintenanceEnables controlled AI-agent access to enterprise-shaped tools with a deny-by-default gated write path, human approval, dry-run execution, and append-only audit logging.1-
- AlicenseAqualityBmaintenanceEnables AI agents to make authenticated API calls and run commands with secrets injected, while keeping credentials completely hidden from the model, with policy enforcement, grants, and audit logging.6MIT