Skip to main content
Glama
samusimonfoti-arch

business-ai-bridge

business-ai-bridge

Connect an AI assistant to a business's existing systems — its database, its CRM, and the desktop software that has no API — without letting it guess, overreach, or write anything nobody approved.

The demo session: questions answered with sources, a draft order stopped at the gate, the assistant's figures re-checked

Most "chat with your data" setups let a model write SQL and hope. This toolkit takes the opposite approach, built from real integration work:

  • Ask: the assistant gets narrow, typed MCP tools, one per business question (customer_overview, stock_level, overdue_invoices, ...). Every answer says which system each figure came from and how old it is, and flags stale data.

  • Change: writes go through a gate. propose_order_draft returns a priced preview with stock and credit checks and writes nothing. Only confirm(token) writes, once, after re-checking that nothing in the preview has changed. Then it reads the result back and compares it with what was approved.

  • Check: bridge eval recomputes the figures the assistant stated from the source systems, with separate code and no model, and reports mismatches and stale sources.

It connects to SQL databases (SQLite, PostgreSQL), REST APIs, and folders of CSV/Excel exports. The exports pattern covers desktop software that can only export reports on a schedule: each export is recorded in a manifest (finish time, row count, SHA-256), so a half-written file is refused and the data's real age is known.

Run the demo (one command)

docker compose run --rm demo

This starts a fictional bicycle-parts wholesaler, Acme Cycle Parts, with three systems and synthetic data: an orders database (PostgreSQL), a CRM (REST API) and a warehouse program that only exports files. Then it plays the assistant's side of a session through a real MCP client.

Without Docker (Python 3.10+):

pip install -e '.[demo]'
bridge demo            # SQLite + a local fake CRM; asks before approving the draft

A real run (bridge demo --yes):

Acme Cycle Parts (fictional) · business-ai-bridge demo
Connected: orders_db (sql), crm (rest), warehouse (files)

[1] "How many 11-speed chains do we have?"
    stock_level(sku="CH-11S")
    -> 142 on hand, 30 reserved, 112 available
       source: warehouse/stock.csv exported 2.0 h ago (fresh)

[2] "How is Bike Shop Alpha doing?"
    customer_overview(customer_id="C-0001")
    -> revenue last 90 days 3,000.80 EUR, 7 open orders, 1 overdue invoice(s) (438.40 EUR, 12 days late)
       credit limit 23,409.75 EUR, used 2,774.80 EUR, left 20,634.95 EUR
       sources: crm/customers (live), orders_db/orders (live), orders_db/invoices (live)

[3] "Order 20 chains for Bike Shop Alpha."
    propose_order_draft(customer_id="C-0001", lines=[{"sku": "CH-11S", "qty": 20}])
    -> PREVIEW, NOT APPLIED: 20 x CH-11S at 18.40 = 368.00 EUR
       checks: stock ok (112 available), credit left 20,634.95 EUR -> 20,266.95 EUR
       warning: warehouse/prices.xlsx, exported 31.0 h ago (STALE, limit 24 h): confirm figures before approving
    Approve this draft? [y/N] y
    confirm(token="y6r1Xz...")
    -> applied: draft order SO-1601 written
       read back from orders_db: SO-1601, status draft, 368.00 EUR (matches the preview)
    confirm(token="y6r1Xz...")
    -> refused: unknown, used or expired token; make a new proposal

[4] "Also 5 disc brake pads." (the warehouse export lands between preview and approval)
    propose_order_draft(customer_id="C-0001", lines=[{"sku": "BP-DISC", "qty": 5}])
    confirm(token="Txpsqy...")
    -> refused: the data changed since the preview (lines.0.available, sources.warehouse/stock.csv.as_of); nothing written, make a new proposal

[5] Checking the assistant's answers against the sources (separate code, no model)
    PASS      Chains 11-speed on hand       claimed        142  source        142
    PASS      Bike Shop Alpha revenue 90 d  claimed   3,000.80  source   3,000.80
    MISMATCH  Bike Shop Alpha open orders   claimed          6  source          7  off by -1
    PASS      Bike Shop Alpha credit limit  claimed  23,409.75  source  23,409.75
    PASS      Chain 11-speed list price     claimed      18.40  source      18.40
    STALE     warehouse/prices.xlsx is 31.0 h old (limit 24 h): answers from it may be out of date

The open-orders figure in step 5 is misstated on purpose, to show a mismatch being caught. The run also writes an HTML version of the check (eval-report.html).

Related MCP server: retrieval_mcp_server

Connect your own systems

One YAML file describes the systems. Secrets come only from environment variables.

connectors:
  orders_db: {type: sql, url: "${ORDERS_DB_URL}"}            # opened read-only
  crm:
    type: rest
    base_url: https://crm.internal.example
    api_key_env: CRM_API_KEY                                 # read at request time, never logged
    tables:
      customers:
        path: /customers
        columns: [customer_id, name, city, credit_limit, account_manager, email]
  warehouse: {type: files, folder: /srv/exports, max_age_hours: 24, require_manifest: true}
pack: {type: wholesale, orders: orders_db, crm: crm, warehouse: warehouse}
writes:
  create_order_draft: {url: "${ORDERS_DB_WRITE_URL}"}        # separate writable connection
gate: {store: /var/lib/bridge/gate.db}
bridge serve --config bridge.yaml                    # MCP over stdio (Claude Desktop, IDEs)
bridge serve --config bridge.yaml --transport http   # MCP over streamable HTTP
bridge eval claims.json --config bridge.yaml --html report.html
bridge audit --config bridge.yaml                    # every proposal, confirmation and outcome

A full working config is in demo/bridge.compose.yaml.

How it is built

The design decisions and their trade-offs are in ARCHITECTURE.md. In short:

Part

What it guarantees

Connector contract

Every connector passes the same 15-test conformance suite (CSV, Excel with manifest, SQLite, REST, PostgreSQL in CI). Unknown tables and columns are refused with a clear error, before any query is sent; filters are parameters, never string-formatted.

Write gate

Token works once (atomic, across processes), expires, and is refused if anything in the preview changed, including a source going stale; audit is written before the business system; the outcome is decided by read-back (every line and the total), so a timeout after a successful write is not reported as a failure.

Evaluation

No model in the loop; exact Decimal arithmetic; malformed input is an error, never a pass; a stale source fails the run.

Money

Tools compute in Decimal (from database NUMERIC, JSON numbers, Excel cells or CSV text) and MCP responses carry amounts as strings, never floats.

Tests run on Python 3.10 and 3.12 in CI, against PostgreSQL, and CI runs the Docker demo end to end. Before release, an adversarial review found 12 defects (and some overclaims in these docs). Each defect has a test in tests/test_review_regressions.py that failed before its fix.

pip install -e '.[test,postgres]'
pytest -q

Limits

  • The bridge acts as one service account; per-user permissions are not built.

  • The gate can make sure nothing is written without a separate, approved confirmation. It cannot prove a human made that call; that relies on the MCP client asking the user before running a tool marked destructive. MCP elicitation is the next step.

  • Range filters and joins run in tool code after fetching; for large tables, expose a database view.

  • The demo data is synthetic and the company is fictional.

About

Built by Samuel Simon-Fóti, automation developer in The Hague. The patterns come from connecting AI assistants to real business software, including desktop programs with no API. This repository is a clean, generic version with a fictional company.

MIT licensed.

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    C
    maintenance
    Enables 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
    -
  • F
    license
    Not graded
    quality
    B
    maintenance
    Enables LLMs to access cited company documents and perform permission-gated operational actions (HR, IT access, vendor extraction, renewal) with explicit scope, evidence checks, and durable human approval gates.
    -
  • A
    license
    B
    quality
    C
    maintenance
    Enables AI agents to act on live business objects under enforceable per-call identity, per-tool grants, and mandatory human approval for irreversible actions, with connectors isolated from core logic.
    8
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables AI agents to call backend tools under per-agent policy, deciding which tools each agent can see, what arguments it may pass, how often it may call, and which writes wait for human approval. It also redacts secrets and PII from tool results, fences untrusted output against injection, and records every call in a hash-chained audit log.
    25 npm
    MIT