Execution control plane for AI agents.
Ctrl sits between agent intent and real-world actions.
Agents can decide what to do. Ctrl decides what’s allowed to happen.
It intercepts tool calls, risk-scores them, enforces policy (allow / deny / approve), and executes only what’s authorized — with a full, auditable ledger.
One-liner: a drop-in CtrlMCP wrapper for LangChain that turns agent actions into
governed execution.
Status: early v0; APIs may change quickly — treat demos as the source of truth.
AI agents are moving from reading and drafting to acting: sending emails, issuing refunds, publishing content, changing production systems.
The moment agents take real actions, intelligence stops being the bottleneck — authority, safety, and auditability do.
Today, teams solve this with ad-hoc allowlists, brittle checks, and manual approvals. That doesn’t scale, and it breaks the moment agents run faster than humans.
Ctrl is the missing layer: a runtime that decides whether an agent action should happen, under what constraints, and with what proof.
Think of Ctrl as an action gateway: agents propose actions, Ctrl authorizes and executes them safely.
demo_ctrl.mp4
Run an end-to-end demo where an agent fetches crypto data and attempts to publish a static page via EdgeOne.
The publish action is intercepted, risk-scored, paused for approval, and replayed safely after approval.
git clone https://github.com/MehulG/agent-ctrl
cd ctrl
poetry install
# validate configs + init SQLite ledger
poetry run ctrl validate-config \
--servers demos/e2e_publish_market_report/configs/servers.yaml \
--policy demos/e2e_publish_market_report/configs/policy.yaml \
--db ctrl.db
# run the LangChain agent
GOOGLE_API_KEY=<key> ./demos/e2e_publish_market_report/run.shIf policy returns pending, the agent currently exits after printing a request ID.
This is intentional: nothing runs until approval is recorded.
Start the approvals API (same configs + shared ctrl.db):
poetry run ctrl approvals-serve --host 127.0.0.1 --port 8788
curl -X POST http://127.0.0.1:8788/approve/<id>
curl http://127.0.0.1:8788/status/<id>
# or open the dashboard UI (default http://localhost:3000)Start the dashboard UI locally:
cd dashboard
yarn dev --hostname 0.0.0.0 --port 3000Approvals API: http://localhost:8788
Dashboard UI: http://localhost:3000
Prefer containers? Use the bundled compose in
demos/e2e_publish_market_report/.
- Copy
.env.example→.envand set values - Start long-running services:
docker compose --env-file demos/e2e_publish_market_report/.env \
-f demos/e2e_publish_market_report/docker-compose.yml \
up -d approvals dashboard --buildApprovals API: http://localhost:8788
Dashboard UI: http://localhost:3000
- Run the agent:
docker compose --env-file demos/e2e_publish_market_report/.env \
-f demos/e2e_publish_market_report/docker-compose.yml \
run --rm agent- LangChain-first wrapper (
CtrlMCP) for MCP tool interception - Policy engine:
allow,deny, orpending - Risk scoring pipeline (rules + expressions)
- SQLite-backed intent and decision ledger
- Lightweight approvals API + dashboard
- CLI for config validation, policy linting, explain, and tests
A general-purpose action gateway for AI agents.
The long-term goal is not approvals UI — it’s making autonomous execution safe by default:
- Delegated authority instead of shared credentials
- Budgets, limits, and constraints instead of blanket access
- Auto-approval for low risk, escalation only for edge cases
- Replay-safe execution with durable audit trails
As agents begin handling customer communications and financial actions, Ctrl becomes the place where organizations decide:
Which actions can run automatically — and which must never run unchecked.
LangChain agent
|
v
CtrlMCP wrapper
| log intent → score risk → apply policy
v
SQLite ledger (requests / decisions / events)
|
+--> Approvals API + dashboard (for pending)
|
v
MCP servers (CoinGecko, EdgeOne, ...)
- Python 3.11+
- Poetry
- SQLite (bundled with Python)
git clone https://github.com/MehulG/agent-ctrl
cd ctrl
poetry install
poetry run ctrl versionconfigs/servers.yaml
servers:
- name: context7
transport: http
base_url: "https://mcp.context7.com/mcp"configs/policy.yaml
policies:
- id: context7-approval
match: { server: "context7", tool: "*", env: "*" }
effect: allow
require_approval_if: "risk.mode in ['review','danger']"
- id: allow-default
match: { server: "*", tool: "*", env: "*" }
effect: allowconfigs/risk.yaml
risk:
mode: modes
modes: { safe: {score: 10}, review: {score: 50}, danger: {score: 90} }
rules:
- name: context7-safe
when: { server: "context7", tool: "*" }
set_mode: safepoetry run ctrl validate-config \
--servers configs/servers.yaml \
--policy configs/policy.yaml \
--db ctrl.dbfrom ctrl.langchain.client import CtrlMCP
mcp = CtrlMCP(
servers="configs/servers.yaml",
policy="configs/policy.yaml",
risk="configs/risk.yaml",
db_path="ctrl.db",
)
tools = await mcp.get_tools()Tool calls now flow through: log intent → score risk → decide policy → enforce → execute.
More example for policy/risk/server bundles live in docs/example-policies/.
requests: intent ledger (server, tool, env, args hash)decisions: policy outcomesevents: append-only timeline
Example:
select id, server, tool, status, risk_score
from requests
order by created_at desc
limit 10;- Lint:
poetry run ruff check . - Tests:
poetry run pytest - DB migrations auto-apply via
ensure_db
PRs and issues are welcome. Keep changes small and reliability-focused.