Bridge
Turned a company's OpenAPI spec into an access-controlled MCP server, so non-engineers could run exports, dashboards and database actions through chat.
The problem
In most companies the data people need sits behind an API that only engineers can drive, so “can I get an export of X” becomes a ticket. Bridge took a company’s OpenAPI spec and turned it into an access-controlled MCP server, so non-engineers could run custom exports, dashboards and database actions through chat.
What I built
- The agent loop, on Claude Sonnet: server-authoritative tool calling with a round-trip cap, composed abort signals and SSE streaming. The loop is decoupled from the client connection, so closing the tab still persists the turn.
- OAuth 2.1 for the MCP layer, working around the protocol’s thin auth story. Each session is scoped to an identity and a permission set, tenants are isolated, and upstream credentials are envelope-encrypted so customer secrets never left our infrastructure.
- An open-source Rust CLI that serves MCP over both stdio JSON-RPC and Streamable HTTP, cross-compiled to four targets. It drew unsolicited outside contributions.
Decisions worth keeping
Three tools instead of two hundred. A real OpenAPI spec produces 200+ tools, and listing them all every turn buries the model in context. The agent gets three meta-tools instead (search, describe, invoke). Search ranks tools by a hybrid of embedding similarity and lexical signals and runs at the edge, with no vector database.
Measure discovery, then gate on it. An eval harness of 24 benchmark cases across four difficulty tiers scores top-1 accuracy, recall and MRR against a committed baseline, and fails CI when discovery quality regresses.
Let code do the math. Generated dashboards compute their aggregates in code and leave the model only the presentation, so a chart can’t contain a hallucinated number. The same guardrail layer kept every LLM-driven action scoped and auditable against production systems.
How it ended
Two of us shipped the whole product in two months. Design partners were interested, but the interest didn’t turn into revenue, so we wound the company down instead of extending the burn.