Confidential MCP · Open Quickstart

Set policy on your AI agent. Watch it block a bad action. In about ten minutes.

Install the runtime, write one policy, and watch it stop a real data leak before it happens — then walk away with a signed, verifiable record of exactly what it did. Runs on your laptop. No sales call.

▶ Preview · simulated — a 30-second look at the block
agent → runtime
$ agent calls salesforce.contacts
  catalog tag: compliance_domain: "pii"
< HTTP/1.1 403 Forbidden
< "message": "Request denied by policy"
< "error_code": "POLICY_DENY"
✓ Blocked before it reached Salesforce
Policy overhead target: p50 under 1ms per tool call (documented targets, not a measurement on your machine)

A customer-service agent pulls records from Salesforce to do its job — names, accounts, PII. What stops it leaking sensitive data on a tool call it shouldn't make — and could you prove to a regulator it didn't?

An agent calls a tool. Your policy engine says allow. The call goes through. None of that proves the engine itself wasn't tampered with, or that your audit log reflects what actually happened.


cMCP runs the policy where the agent can't reach it — every tool call your agent routes through the runtime is checked, enforced, and signed into a tamper-evident record. This quickstart lets you feel that in about ten minutes, on your laptop.

Five quick steps from install to a blocked leak

Everything leads to step four — the moment you watch your own policy stop something it shouldn't allow.

01

Install

One pip install — the runtime, that's it.

02

Write your policy

A few lines of Cedar — your rules, not ours.

03

Wire it in

Point one call at the runtime. Nothing else changes.

THE POINT
04

Watch it block

Fire a bad action. Watch the policy return 403.

05

Prove it

Get a signed, verifiable record of what happened.

The quickstart

Copy each block. We've filled in every file for you — nothing to hand-author, nothing to hash. Follow along top to bottom.

Before you startPython 3.11+ · pip · macOS or Linux · two terminal windows · ~10 minutes · no special hardware.
1

Install the runtime

Creates a project folder, an isolated Python environment, and installs the cmcp runtime — the enforcement engine. (The venv is why pip install won't get blocked on recent macOS.) No tool server needed — in Step 4 your policy blocks the call before it forwards, so salesforce.contacts is a placeholder for your real MCP server. Open source (MIT): github.com/agentrust-io/cmcp

Terminal 1
mkdir -p cmcp-quickstart && cd cmcp-quickstart
python3 -m venv venv && source venv/bin/activate
pip install cmcp-runtime
2

Drop in the config & your policy

Paste each block into the same Terminal 1, top to bottom — you're already inside cmcp-quickstart from Step 1. The cat > … EOF wrapper writes each file for you, no editor needed. Your policy (block 2) is what decides what's allowed. Any *.cedar filename works; manifest.json and schema.cedarschema are the two names the runtime requires.

1 · Creates cmcp-config.yaml
mkdir -p policies
cat > cmcp-config.yaml << 'EOF'
attestation:
  provider: auto
  enforcement_mode: enforcing   # enforcing | advisory | silent
policy_bundle_path: ./policies/
catalog_path: ./catalog.json
listen_addr: "127.0.0.1:8443"   # dev mode runs without a bearer token, so stay on loopback
EOF
2 · Creates your policy bundle — your rules
cat > policies/manifest.json << 'EOF'
{ "version": "0.1.0", "authored_at": "2026-06-05T00:00:00Z",
  "author_identity": "[email protected]", "commit_sha": "quickstart-demo" }
EOF

cat > policies/agent.cedar << 'EOF'
// Cedar is default-deny: anything these rules don't permit is denied.

// Rule 1 — let the demo-agent workflow call tools at all
permit ( principal, action == cMCP::Action::"call_tool", resource )
when { context.workflow_id == "demo-agent" };

// Rule 2 — but never let it call a tool the catalog tags as PII.
// forbid always wins over permit in Cedar, so this is the rule
// that produces the 403 in Step 4.
forbid ( principal, action == cMCP::Action::"call_tool", resource )
when { context.compliance_domain == "pii" };
EOF

cat > policies/schema.cedarschema << 'EOF'
{"cMCP":{"entityTypes":{"Principal":{"memberOfTypes":[],"shape":{"type":"Record","attributes":{"session_id":{"type":"String","required":true},"workflow_id":{"type":"String","required":true}}}},"Resource":{"memberOfTypes":[],"shape":{"type":"Record","attributes":{"tool_name":{"type":"String","required":true}}}}},"actions":{"call_tool":{"appliesTo":{"principalTypes":["cMCP::Principal"],"resourceTypes":["cMCP::Resource"],"context":{"type":"Record","attributes":{"compliance_domain":{"type":"String","required":true},"session_max_sensitivity":{"type":"String","required":true},"workflow_id":{"type":"String","required":true}}}}}}}}
EOF
3 · Creates catalog.json — the approved tool, tagged compliance_domain: "pii" (hash pre-computed)
cat > catalog.json << 'EOF'
[
  {
    "tool_name": "salesforce.contacts",
    "server": {
      "display_name": "Salesforce Contacts MCP Server (mock)",
      "url": "http://localhost:9001/mcp",
      "tls_fingerprint": "SHA256:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
      "transport": "http-sse"
    },
    "approved_definition": {
      "description": "Query Salesforce contacts by account name or contact ID.",
      "input_schema": {
        "type": "object",
        "required": ["query"],
        "properties": {
          "query": {"type": "string", "description": "Account name or contact ID"},
          "max_records": {"type": "integer", "default": 50}
        }
      },
      "output_schema": {
        "type": "object",
        "properties": { "contacts": {"type": "array"}, "total_count": {"type": "integer"} }
      }
    },
    "definition_hash": "sha256:b42ecf14612f23456b5b0794864a00288d4038ac444cedb87fc214cefee89e35",
    "compliance_domain": "pii",
    "requires_baa": false,
    "sensitivity_level": "pii",
    "added_at": "2026-06-05T00:00:00Z",
    "approved_by": "[email protected]"
  }
]
EOF
4 · Sanity check — confirm the config parses
cmcp validate-config --config cmcp-config.yaml

Expect ✓ Config valid: cmcp-config.yaml. This checks the YAML only; the policy bundle and catalog are loaded when the runtime starts in Step 3. If you see an error instead, a block didn't paste cleanly — re-run that one.

3

Start the runtime

Run this in the same terminal where you just created the files (you're inside cmcp-quickstart). It keeps running and won't return a prompt — that's normal, and the startup notices are expected, not errors. Leave it open and open a second terminal for the next step.

Terminal 1 — leave running
CMCP_DEV_MODE=1 cmcp start --config cmcp-config.yaml
4

Fire a bad action — watch it get blocked

In a second terminal, run cd cmcp-quickstart && source venv/bin/activate first (so cmcp is available here too), then paste this. It's your support agent trying to pull a customer's Salesforce record. The runtime looks the tool up in your catalog, sees compliance_domain: "pii", and your Rule 2 forbids it. Watch it return a 403 before it ever reaches Salesforce. Not blocked? You're likely in advisory mode — check the config says enforcing.

Terminal 2 — you, playing the agent
curl -i -X POST http://localhost:8443/mcp \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0", "id": 2, "method": "tools/call",
    "params": {
      "name": "salesforce.contacts",
      "arguments": { "query": "Acme Corp" },
      "_cmcp": { "session_id": "demo-session-001", "workflow_id": "demo-agent" }
    }
  }'

workflow_id is the only field the runtime reads out of _cmcp. The session id is a label for your own logs: the runtime mints its own session id, which is why Step 5 looks it up instead of assuming it.

WHAT YOU'LL SEE — 403 FORBIDDEN

Your policy stops a PII record from leaving on a tool call — before it reaches Salesforce, decided by the rule you wrote, enforced where the agent can't tamper with it. That's the barrier most teams can't cross today: shipping an agent you can actually prove is governed.

HTTP/1.1 403 Forbidden · "message": "Request denied by policy" · "error_code": "POLICY_DENY"

5

Walk away with proof

Closing the session mints a signed TRACE claim — a tamper-evident record of which tools ran and which policy decided each call. Verify it yourself; you don't have to trust us. See the full TRACE claim schema →

Terminal 2 — finalize the session, then verify
SID=$(curl -s "http://localhost:8443/audit/export?session_id=demo-session-001" \
  | python3 -c "import sys,json; print(json.load(sys.stdin)['entries'][0]['session_id'])")
curl -s -X POST "http://localhost:8443/sessions/$SID/close" | python3 -m json.tool > claim.json
cmcp verify claim.json

Expected output in dev mode:

schemaPASS ✓
signaturePASS ✓
policy_bundle.hashPASS ✓ (not pinned)
tool_catalog.hashPASS ✓ (not pinned)
attestation_freshnessPASS ✓
audit_chainPASS ✓
hardware_attestationFAIL — software-only mode, not hardware-backed
RESULTFAIL (partially_verified)

Every cryptographic check passes. The one FAIL is hardware attestation, which dev mode can't provide, and that is the honest answer: overall verification only reports verified when the claim is hardware-backed, so the CLI prints FAIL (partially_verified) and exits 1 here. Run the same runtime on a hardware TEE and that last check passes, the result becomes verified, and that is the version you hand a regulator.

GO FURTHER — STATEFUL ESCALATION

The rule you just wrote is stateless: the tool is tagged PII, so it is always forbidden for this workflow. cMCP also tracks sensitivity across a session. Once a response has been inspected and found to contain PII, the session's session_max_sensitivity rises, and a rule like this starts to bite:

The stateful version of Rule 2
forbid ( principal, action == cMCP::Action::"call_tool", resource )
when { context.session_max_sensitivity == "pii" };

That one needs a session with history: sensitivity starts at public and only rises after the runtime inspects a real response, so you need an upstream tool server and at least one allowed call before it fires. The reference quickstart sets that up with a mock upstream: cMCP reference quickstart →

Stuck? Every block above is copy-paste — the file blocks write themselves via cat > … EOF, the rest are commands. Keep your first terminal (the runtime) running while you work in the second. Full walkthrough & troubleshooting: cMCP quickstart · debugging guide.

You did it

Tell the community how it went.

The people building cMCP hang out in Discord. Drop your result, get help, and tell us if it unblocks something you can't ship today.

Join the AgenTrust Discord

Post your result in #general and answer one question — did it unblock something you can't ship today?

Join the Discord →