External MCP

External MCP (Model Context Protocol) #

Classification: Internal-Only

BonsAI exposes its platform to AI assistants — Claude, and any other MCP-compatible client — through tofu-external-mcp, a hosted MCP server. Once connected, an assistant can read invoices, bills, bank statements and entity settings, and (for the write tools) create entities, upload documents and trigger extractions — all scoped to the signed-in user’s own organization and permissions, using the same sign-in as the BonsAI app. It lives at apps/tofu-external-mcp and is part of the tofu MCP project (see ENG-6758).

What is MCP? #

MCP is an open protocol that lets an AI assistant call a fixed set of well-defined tools exposed by a server, instead of guessing at a REST API or scraping a UI. Each tool has a name, a description, and a typed input/output schema — the assistant reads the schema, decides which tool answers the user’s question, calls it with structured arguments, and gets back structured data. The user describes what they want in plain language; the assistant picks the right tool(s) and arguments.

What is tofu-external-mcp? #

BonsAI’s MCP server for external, customer-facing use. It forwards every call to bonsapi (BonsAI’s own API) using the caller’s signed-in identity: every tool call carries the user’s Clerk OAuth token and is scoped to exactly what that account can already see and do in the BonsAI app. There is no separate “MCP admin” access and no way to reach another organization’s data through it.

  • Read tools — invoices, bills, direct expenses, bank statements, documents, entities, integrations, knowledge, usage and billing. Nothing is changed.
  • Write tools — create an entity, update entity settings, upload a document, create or re-trigger an extraction, add a knowledge card, manage entity members. Each is gated by the same permission BonsAI’s own UI enforces (for example Entity:Update to change entity settings); there is no more permissive MCP path.

Example #

Once connected, ask in plain language:

“What AP bills are still pending review for my Acme Corp entity?”

Behind the scenes, Claude will typically:

  1. Call list_entities to find the entity id matching “Acme Corp”.
  2. Call list_extractions with {"entity_id": "<id>", "types": ["AP_BILL"], "statuses": ["DRAFT", "NEEDS_REVIEW"]} to get the matching bills, including legacy drafts stored as NEEDS_REVIEW.
  3. Call get_invoice on any of those to show the amounts, vendor and line items.

Where to read what #

You want to… Read
Connect a client: claude.ai to prod, Claude Code locally Connecting
Run it on your machine and log in with Claude Code Local development
Know what runs where (local, dev, prod), env vars, how it deploys Environments
Understand the Clerk OAuth flow and what is verified Auth
See how the server is built: request flow, sessions, tool surface Architecture
Look up what a tool does and its inputs/outputs Tools guide and the generated Tool catalog
Read or search logs and trace a call across services Audit logging
Understand the per-user call limit Rate limiting
See the retired API-key design Service-key policy (retired)

Auth model, in short #

Per-user-scoped, no flat access. Unlike tofu-internal-mcp (engineering-only, org-wide DB access), this server forwards each caller’s own Clerk login token to BonsAPI as-is — BonsAPI enforces that user’s real org/entity permissions, so this server never re-implements access control and never touches /internal/*, the dev admin bypass, or any shared superuser credential.

  • Sign-in, not a static secret. Every call carries the caller’s own Clerk-issued OAuth token, refreshed automatically by the MCP client. There is no long-lived API key to store or leak.
  • Same permissions as the app. If a role can’t do something in the web app, the matching MCP tool denies it too, with a clear error rather than a silent no-op.
  • Org-scoped. A caller only ever sees and changes data in their own organization.