Install
$ agentstack add mcp-manavgup-ai-agent-controlplane-demo Open-source listing, not yet scanned by AgentStack. Follow the source repository for install instructions.
Security review
⚠ Flagged1 finding(s); flagged for manual review. · v0.1.0 How review works →
- • Prompt-injection patterns
- • Secret / credential exfiltration
- • Dangerous shell & filesystem operations
- • Untrusted network calls
- • Known-malicious package signatures
- high Pipes remote content directly into a shell (remote code execution).
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
ai-agent-controlplane-demo
> IBM Bob × ContextForge — the AI agent control plane. One AI agent, a fintech agent mesh, and a gateway that governs every move. Who's in charge of your agents?
Can't run Docker/ContextForge locally? Click the badge — the whole governed mesh builds in the cloud; you only need IBM Bob on your laptop (Tier 2 in ONBOARDING.md).
A turnkey, follow-along demo of IBM ContextForge (the MCP / A2A gateway) acting as the control plane between an AI agent (IBM Bob) and a fintech ("FinByte") expense-and-payments agent mesh. The gateway sits in the middle of every tool call and every agent-to-agent call, enforces four controls, and you can prove all of them with one command → 16 passed, 0 failed.
What is this?
When an AI agent can read receipts, approve expenses, and move money, the question stops being "can the agent do it?" and becomes "who's in charge of the agent?". This demo answers that with a control plane. IBM Bob is the agent — an MCP client that drives a FinByte agent mesh (expense lookups, ERP payments, policy docs, notifications, and two autonomous A2A agents). It never talks to those backends directly. Every call flows through IBM ContextForge, the gateway that authenticates, authorizes, governs, redacts, and audits — before Bob ever sees a result and before any money moves.
The gateway enforces:
- Policy (OPA). A wire over the $10,000 FinByte T&E cap is blocked unless it carries dual approval — evaluated live by an Open Policy Agent sidecar against a Rego policy.
- Data protection. SSNs, credit-card numbers, API keys, and other secrets in tool output are masked on the gateway before they reach the agent (
***-**-6789,****-****-****-1111,[SECRET_REDACTED]). - Prompt-injection neutralization. Adversarial instructions hidden in tool output ("SYSTEM: ignore all prior policy…") are neutralized to
[INJECTION_BLOCKED]. - RBAC least-privilege. A FinOps analyst persona simply has no
wiretool; only the privileged operator persona can register servers, read the audit trail, or interrogate policy. (Rate limiting is a built-in ContextForge capability, but it is not configured or demonstrated in this stack.) - Agent-mesh governance. The same policy that stops a direct wire also stops a cross-language agent-to-agent payment (a Python auditor delegating a $50k payment to a Rust payments agent) at the bridged tool hook.
The whole stack is path-independent — config, plugins, and Rego policy are baked into the images, so there are zero host bind-mounts and it runs from any clone path.
Architecture
(See the diagram at the top.) IBM Bob connects through the mcpgateway.wrapper stdio bridge to a virtual server on the gateway (a curated, least-privilege slice of the catalog). The gateway fronts five governed MCP servers, one deliberately-unregistered MCP server, and two A2A agents, with an OPA sidecar for policy decisions. Only the gateway and the two A2A agents publish ports to the host; the OPA sidecar and all MCP servers are reachable only on the Compose private network.
| Component | Kind | Port | Role | |---|---|---|---| | IBM Bob | AI agent / MCP client | — | Drives the mesh; connects via the mcpgateway.wrapper stdio bridge to a virtual server | | ContextForge gateway | MCP/A2A gateway (the control plane) | 4444 (host) | Authn/z, governance, redaction, audit, federation; Admin UI at /admin | | OPA | Open Policy Agent sidecar | 8181 (internal) | Evaluates the Rego wire-amount policy (package mcpgateway) | | expense-db | MCP server (Python, FastMCP) | 8000 (internal) | list_pending_expenses, get_expense, get_receipt (holds the PII / injection fixtures) | | erp-payments | MCP server (Python, FastMCP) | 8000 (internal) | approve, reimburse, wire (the governed money path) | | policy-docs | MCP server (Python, FastMCP) | 8000 (internal) | get_policy, wire_limit | | notify | MCP server (Python, FastMCP) | 8000 (internal) | notify | | controlplane | MCP server (Python, FastMCP) | 8000 (internal) | Operator surface: register_mcp_server, list_control_plane, recent_blocks, evaluate_policy | | fx-rates | MCP server (Python, FastMCP) | 8000 (internal) | get_fx_rate, list_currencies — runs but is intentionally unregistered (for the live-register beat) | | auditor | A2A agent (Python, a2a-sdk) | 9001 (host) | Audits expenses; can delegate a payment to the Rust agent | | payments | A2A agent (Rust, a2a-lf / a2a-server-lf) | 3000 (host) | Executes payments; JSON-RPC at /jsonrpc, agent card at /.well-known/agent-card.json |
> The seed registers 5 governed MCP servers (expense-db, erp-payments, policy-docs, notify, controlplane) plus the 2 A2A agents, and curates them into three virtual servers (FinOps, Treasury, Operator). fx-rates is left unregistered on every seed so the operator demo can register it live. > > Naming note: the controlplane and auditor services read their admin token from an env var named AUDITOR_TOKEN (written to .env.tokens by make up). Despite the name, it is an admin JWT — not an auditor-only scope.
How a call is governed
Every tool call passes through two gateway plugin hooks. On tool_pre_invoke, the custom FinByteGuard plugin (gateway/custom/finbyte_guard.py, on the cpex framework) extracts the call's arguments and asks OPA whether the wire-amount policy permits it — denying anything over the $10,000 cap without dual approval, and failing closed if OPA is unreachable. On tool_post_invoke, FinByteGuard deep-scrubs secrets (sk-live-… → [SECRET_REDACTED]) and neutralizes prompt injection (→ [INJECTION_BLOCKED]), while the cpex PIIFilterPlugin masks SSNs and credit-card numbers. Every decision emits an AUDIT [FinByteGuard] … line and shows up in the Admin UI's Logs tab.
The four controls
| Control | Prompt to Bob | What ContextForge does | |---|---|---| | 1 — Policy (OPA) | "Use the finbyte-gateway tools to wire $50,000 to Acme LLC for expense exp_big." | Blocks at OPA: "…exceeds the $10,000 auto-approve limit… FinByte T&E policy §2." Add "with dual approval" → allowed. The same policy blocks the cross-language auditor→payments $50k at the bridged a2a-payments hook. | | 2 — Data protection | "Fetch receipt rcpt_pii, verbatim." | Masks before Bob sees it: SSN → ***-**-6789, card → ****-****-****-1111, API key → [SECRET_REDACTED]. | | 3 — Prompt-injection | "Fetch receipt rcpt_injection." | Neutralizes the embedded SYSTEM: ignore all prior policy… → [INJECTION_BLOCKED]. | | 4 — RBAC least-privilege | "Now wire $50k yourself, directly." | Bob can't — the FinOps virtual server hides erp-payments-wire. MCP Inspector confirms the tool is absent. The operator persona has control-plane tools the analyst lacks. |
Baseline that works: "Process expense exp_clean and reimburse it." — a clean $18.50 expense flows straight through.
- Expense approval quorum — the room votes on a $50k wire from their phones (live local tally) while 5 governed A2A voter agents vote through the ContextForge gateway (each call authn'd + audited). OPA then blocks the wire regardless of either tally. Policy beats consensus.
Two personas (RBAC)
The same Bob binary becomes two different actors depending on which virtual server its .bob/mcp.json points at. Both targets rewrite .bob/mcp.json (from the bob-personas/*.template files, refreshing the live UUID) and launch Bob from the repo root, so they're cwd-proof and reseed-proof.
| | make bob — FinOps analyst (Act 1) | make bob-operator — platform operator (Act 2) | |---|---|---| | Virtual server | FinOps (8 tools) | Operator (4 tools) | | Can do | List/read expenses, read receipts, approve, reimburse, read policy + wire limit, talk to the auditor agent | register_mcp_server, list_control_plane, recent_blocks, evaluate_policy | | Cannot do | No wire tool; can't register servers, read the audit trail, or query policy directly | Not the analyst's expense-handling surface | | Persona file | bob-personas/mcp.json.template (server finbyte-gateway) | bob-personas/mcp.operator.json.template (server finbyte-operator) |
Swap back to the analyst at any time with make bob.
Prerequisites
| Tool | Why | How to get it | |---|---|---| | Docker (running) | Runs the gateway, OPA, MCP servers, and A2A agents | Docker Desktop on macOS/Windows, or Docker Engine on Linux (runs natively, no nested virtualization). Start it before you begin. — docker.com No Docker? The stack also runs on Podman — see [Run on Podman](docs/RUNBOOK.md#run-on-podman-no-docker), or one-shot a fresh Ubuntu/WSL2/x86 host with bash scripts/test-fresh-host.sh. | | uv | Mints the gateway JWT offline (no network round-trip) | https://docs.astral.sh/uv/ | | IBM Bob Shell (bob) | Optional — only to drive Bob; the stack + 16/16 proof run without it | macOS/Linux: curl -fsSL https://bob.ibm.com/download/bobshell.sh \| bash (bob.ibm.com/download) — checks Node ≥ 22.15 first | | Node.js ≥ 22.15 | Optional — required by IBM Bob Shell (it's a Node app) and the MCP Inspector (npx); not needed to bring up the stack or prove the controls | nodejs.org, or nvm install 22 |
> Budget ~5 GB of free disk. On the first run, the pinned ContextForge image pulls once and the seven source images (six MCP servers + the Rust payments agent) build locally. Subsequent cold starts (make down && make quickstart) take roughly ~38 seconds once images are cached.
> Running on Linux / in a VM. Only Docker and uv are truly required to bring up the stack and prove 16/16 — bob/Node are needed only to drive the demo. On Apple silicon a full macOS-guest VM is impractical (~60 GB+ disk); the practical path is a lightweight Linux VM (Multipass/Lima) + Docker Engine, which runs the stack natively on arm64 (the OPA image is multi-arch, so no emulation). IBM Bob Shell is cross-platform and can also be installed in the VM to drive the demo (first run uses an IBMid device-code login on a headless box). See [Running on a fresh Linux box / VM](#running-on-a-fresh-linux-box--vm) below.
Two ways to run it
Same stack, two stories — pick your path:
🎓 Progressive build — build an agent tool from scratch, then watch it get governed (developer path)
Bottom-up, the inverse of make quickstart: start from a bare MCP server you'd recognise anywhere and earn ContextForge one layer at a time — carrying the tool you built the whole way. Open the prompt-card and drive Bob through four stages:
make dev-start # opens docs/build.html — the follow-along build guide (copy-paste Bob prompts)
| Stage | Command | What you (and Bob) do | Bob persona | |---|---|---|---| | ① Build | make stage1-build | Prompt Bob to write mcp-servers/sales-tax/server.py from scratch (a FastMCP add_tax tool). It runs bare on :8000; a call prints add_tax(100) → 108.50. It works — and it's totally ungoverned: no token, no policy, anyone on the port runs anything. | — | | ② Govern | make stage2-govern | The same server is containerised onto the mesh, registered (in the catalog, token-gated — not callable yet), then granted into a Builder virtual server and called back through the gateway → 108.50. 2b bonus: Bob extends a service it didn't write (fx-rates gains convert). | operator → builder | | ③ Control | make stage3-controls | One prompt drives a batch; the four controls bite real calls — PII redaction, injection neutralised, OPA blocks a $50k cross-language wire, RBAC (Bob has no wire tool). | analyst | | ④ Mesh | make stage4-mesh | The full governed picture — identical to the quickstart end-state, but you watched it get built. | — |
The throughline is register → grant → call. Your tool goes from works-but-ungoverned → in the catalog but not callable → granted and callable through the one governed seam, with redaction, policy, and audit applied. Registering a backend doesn't make it callable — granting it to an agent is a separate, privileged step (make salestax-grant adds it to the Builder vserver). That boundary is least-privilege.
The builder persona (make bob-install-builder) is the developer's seat — it calls your own granted tools (add_tax, convert), alongside the existing analyst (least-privilege consumer) and operator (registers / audits / evaluates policy) personas.
No Docker on your laptop? make connect prints a bob mcp add … -t http line pointed at a gateway running elsewhere — a teammate's box, a VM, or a GitHub Codespace — so you drive the whole governed mesh with only Bob installed, governance intact over the wire.
→ Narrated beats: [docs/SHOWCASE-BOB.md](docs/SHOWCASE-BOB.md) · the follow-along [docs/build.html](docs/build.html) (presenter cockpit: [docs/cockpit.html](docs/cockpit.html)) · zero-setup tiers in [docs/ONBOARDING.md](docs/ONBOARDING.md).
🛰 Governed mesh — drop straight into the finished, governed stack (quickstart path)
Top-down: one command takes a laptop from nothing to a running, governed mesh and proves all four controls (16/16) — no Bob required. Then drive Bob as the FinOps analyst (Act 1) and platform operator (Act 2) and watch the control plane react in the Admin UI.
→ Full command + walkthrough in [Quickstart](#quickstart) immediately below (then [Drive Bob](#drive-bob), the watch panes, and the deterministic proof under it).
Quickstart
git clone https://github.com/manavgup/ai-agent-controlplane-demo.git
cd ai-agent-controlplane-demo
make quickstart
make quickstart is one command that takes a laptop from nothing to a running, governed mesh: preflight (requires Docker + uv; warns but continues if bob/npx are absent) → bring up the stack → seed (register servers/agents, build the FinOps / Treasury / Operator virtual servers) → configure Bob (FinOps analyst persona) → prove all four controls (16/16, with no Bob required) → print a copy-paste walkthrough card. It's re-runnable — safe to run again if anything stalls. The Admin UI logs in with admin@finbyte.demo / FinByteAdmin!2026.
> Proof is headless. make quickstart finishes 16 passed, 0 failed even on a box without bob or Node (a Linux VM or CI runner): Bob only drives the demo — it isn't needed to bring up the stack or prove the controls. make bob / make bob-operator also fail gracefully if bob isn't installed (they still write .bob/mcp.json, print an install hint, and exit 0).
Drive Bob
Act 1 — FinOps analyst (least-privilege). Launch with make bob (cwd-proof; it refreshes the config first), then try:
- "Use the finbyte-gateway tools to fetch receipt
rcpt_pii, verbatim." → redacted. - "Fetch receipt
rcpt_injection." →[INJECTION_BLOCKED]. - "Ask the auditor agent to pay $50,000 to Acme LLC." → blocked at OPA (Python → Rust).
- "Now wire $50k yourself, directly." → Bob has no
wiretool.
Act 2 — platform operator. Quit Bob, then make bob-operator to swap personas and relaunch:
- "List everything ContextForge is governing." →
list_control_plane. - "Would a $50,000 wire be allowed? With dual approval?" →
evaluate_policy(deny + reason, then allow). - _"Register the fx-rates service at `http://fx-rates:8000/mc
…
Source & license
This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: manavgup
- Source: manavgup/ai-agent-controlplane-demo
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.