Stop giving AI agents API keys. Give them a domain.
Two-command quickstart: real Claude / GPT / Gemini agent talking to a real declarative IDF domain. No mocks. No code-generated boilerplate. You'll watch one agent try to wire $50,000 to a wallet — and another agent, in the same domain, decline to, all the way down to a structured JSON-RPC rejection that the LLM can actually reason about.
→ Landing & full narrative: fold.intent-design.tech → MCP-server source: @intent-driven/mcp-server
→ Watch on Loom → Three real scripts. Real HTTP. Verbatim terminal output.
On April 25 2026 a Cursor agent powered by Claude Opus 4.6, working on a credential mismatch in PocketOS staging, found an unrelated API token, decided to delete a Railway volume to fix things, and wiped the production database and all volume-level backups in 9 seconds. The agent's own post-mortem:
"I guessed that deleting a staging volume via the API would be scoped to staging only. I didn't verify. I didn't check if the volume ID was shared across environments."
30-hour outage. PocketOS rolled back to a 3-month-old backup. (The Register · FastCompany · OECD AI Incident #6153)
That's not an alignment failure. The system never told the agent what was allowed, why it shouldn't, or what would happen if it tried. Existing MCP servers don't either — tool descriptions carry endpoint shape and not much else. The agent learns by colliding with 500s.
This repo runs the proof in three scripts against a live IDF runtime.
Who this is for. You're the engineer at a 5–30-person team putting an AI agent into production this quarter — on top of a real backend, with real customers, real SOC2 review on the horizon. You don't want a guardrail layer that reviews after the fact. You want the system itself to refuse the wrong action — before the call, with a structured reason the agent can read.
How it plugs in. Fold is a sibling service over an HTTP API the runtime exposes from your IDF artifact — not middleware in your existing app, not codegen at runtime. Your current backend stays where it is; the IDF artifact describes the agent-facing surface, and the runtime serves it on its own port. The MCP server is a stdio adapter Claude Desktop / Cursor / Zed connect to.
- Docker Desktop (or
docker composeon Linux) - Node.js 20+ (for the demo scripts only — the runtime itself runs in Docker)
That's it. You do not need to clone the IDF host separately, install
its dependencies, set absolute paths, or remember which terminal is
which. The Docker image bundles the host and bootstraps the invest
demo domain on first start.
git clone https://github.com/intent-driven-software/fold-runtime-quickstart
cd fold-runtime-quickstart
# Terminal 1 — start the runtime (first run takes ~3 min to build the image)
docker compose up
# Terminal 2 — run the demo
npm install
npm run demo:rogue # Act 1: agent tries $50,000 — gets HTTP 403 with structured rejection
npm run demo:grant # Act 2: investor issues $1,000-cap preapproval (one declarative effect)
npm run demo:smart # Act 3: agent reads the cap, scales the order, executes 200 OKWhen you're done: Ctrl-C in terminal 1, then docker compose down.
Agent submits a $50,000 BTC long without preapproval. The runtime intercepts before any effect lands in storage:
HTTP 403
{
"error": "preapproval_denied",
"intentId": "agent_execute_preapproved_order",
"reason": "no_preapproval",
"details": {
"entity": "AgentPreapproval",
"ownerField": "userId",
"viewerId": "user_5f57c252"
}
}
Structured. Not a 500. Not a string. The next move for any sane agent: stop, ask the human for a preapproval, retry.
A human (acting as the investor role) issues:
maxOrderAmount: $1,000dailyLimit: $5,000allowedAssetTypes: crypto, stocksexpiresAt: +7 days
That's one effect in Φ. No new endpoint. No middleware change. The
intent delegate_to_agent is part of the same artifact the agent
reads — and from this moment, the preapproval guard has a row to
evaluate against.
Found preapproval (id=pa_…):
active: true
maxOrderAmount: $1000
dailyLimit: $5000
Desired: 0.5 BTC × $100000 = $50000
Cap: maxOrderAmount = $1000
Decision: scale down to 0.0095 BTC = $950.00
HTTP 200
{ "status": "confirmed", ... }
The agent didn't bump into a wall. It read the wall, walked around
it, and did exactly what was permitted. No special agent code. The
agent reads ontology.roles.agent.preapproval the same way it reads
tool descriptions — through a declarative artifact authored once and
consumed by every reader.
Once docker compose up is running, point Claude Desktop at the
already-bootstrapped invest domain.
Add to ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"invest": {
"command": "npx",
"args": ["-y", "@intent-driven/mcp-server"],
"env": {
"IDF_SERVER": "http://localhost:3001",
"IDF_DOMAIN": "invest",
"IDF_BOOTSTRAP": "0"
}
}
}
}Note IDF_BOOTSTRAP=0 — the Docker container already bootstrapped the
domain, so the MCP server can skip that step (no IDF_ONTOLOGY_PATH
needed). Restart Claude Desktop. In the Tools menu, all 6
agent-callable intents will appear, each tool description carrying:
intent.description— what the action isCreates: <Entity>— what gets added⚠️ Irreversible action— point-of-no-return warning where applicableMay fail on (domain invariants)— relevant integrity rules
Inspect what Claude actually sees:
npm run show-descriptionAnother process or container is bound to 3001. Pick a different host port:
HOST_PORT=3199 docker compose up
IDF_SERVER=http://localhost:3199 npm run demo:rogue
IDF_SERVER=http://localhost:3199 npm run demo:grant
IDF_SERVER=http://localhost:3199 npm run demo:smartTo find the offender: lsof -i :3001 or docker ps --format '{{.Names}} {{.Ports}}'.
First run only — the image clones the IDF host and runs npm install
inside the container (3-5 min on a typical macbook). Subsequent runs
use the cached image and start in ~5 sec.
To pin to a specific upstream commit:
docker compose build --build-arg IDF_REF=<sha-or-tag>The container isn't ready yet, or port forwarding didn't take. Check:
docker compose ps # STATUS should be "Up (healthy)"
docker compose logs --tail=20 # look for "fold-quickstart is ready"
curl http://localhost:3001/api/effects # should return JSONIf docker compose ps says Up (healthy) but curl fails, restart
Docker Desktop — this is a known macOS quirk when many containers run
simultaneously.
Tail the container log for the exact error:
docker compose logs idf-hostMost common cause: the upstream idf repo changed its on-disk layout
and /opt/idf/src/domains/invest/{ontology,intents}.js no longer
exists. Pin to a known-good commit (see above) and report the issue.
- Make sure
docker compose upis running (the runtime needs to be reachable onlocalhost:3001). - Quit Claude Desktop completely (⌘Q, not just close window) and relaunch — config is read at startup only.
- Check Claude Desktop logs:
~/Library/Logs/Claude/mcp-server-invest.log - Verify the MCP server can reach the runtime:
npx -y @intent-driven/mcp-server --domain=invest --no-bootstrap(should connect, fetch schema, exit).
If you'd rather drive the host yourself (e.g. for development against
your own ontologies), see legacy/no-docker.md for the original
multi-step flow. Not recommended unless you're already familiar with
the IDF host.
Dockerfile # Single-image build: clones IDF host + bootstrap
docker-compose.yml # One service, one port, one healthcheck
docker/entrypoint.sh # Boots host, bootstraps domain, prints next-step banner
scripts/
demo-rogue-agent.mjs # Act 1 — $50K rejected with structured 403
demo-grant-preapproval.mjs # Act 2 — investor delegates with $1K cap
demo-smart-agent.mjs # Act 3 — agent reads cap, scales, executes
show-description.mjs # Print verbatim agent-facing tool descriptions
claude_desktop_config.example.json
That's it. ~120 lines of scripts + ~60 lines of Docker plumbing → a working, agent-ready domain runtime with structured rejections, preapproval guards, and irreversibility primitives.
This quickstart runs a pre-built domain (invest). If you want to
declare your own entities / intents / invariants and have Fold serve
them, that's a different project — the IDF SDK.
Three reference points from the public IDF host runtime:
| Domain | Shape | Time |
|---|---|---|
invest |
14 entities · 61 intents · 5 invariants · ~600 lines | a weekend, hand-written |
gravitino |
253 entities (Apache catalog OpenAPI) · 120 intents | imported in <1h, enriched in 2 days |
workflow |
9 entities · 47 intents · timer queue · cascade rules | a day |
The speed comes from importers — you don't write 253 entities by hand.
@intent-driven/cli:idf init— bootstrap an empty domainidf import postgres— reads your live schema, generates entity baselineidf import openapi— reads your API spec, generates intents and references (this is the 253-entity path)idf import prisma— same for ORM-driven backendsidf enrich— LLM pass to fill labels, field roles, suggested preapproval predicates
docs/ontology-authoring-checklist.md— 12-point checklist for first ontology- Why a runtime layer — ~1800-word essay on the agent-safety class question this answers
The quickstart deliberately doesn't cover authoring — start there if you want to ship Fold for your own product.
- Source of truth: github.com/DubovskiyIM/idf — host runtime + 17 reference domains
- MCP server package: github.com/intent-driven-software/idf-mcp —
@intent-driven/mcp-serveron npm - Landing: fold.intent-design.tech
MIT. See LICENSE.