Skip to main content
Glama

CedarDraft MCP

An MCP server that turns plain-language access rules for agent tool calls (English or Spanish) into Cedar policies, refuses any draft it cannot prove, and then gates each proposed tool call with ALLOW or DENY plus the ids of the policies that decided it.

CI License

The problem

Teams know an agent's tool calls should sit behind a policy: name the tools and arguments a task may use, and deny everything else. Writing Cedar by hand is slow. A policy nobody has checked can be worse than no policy, because it looks like protection. CedarDraft writes the draft, proves it against its own stated intent with the official Cedar tools, and enforces only proven drafts.

Related MCP server: ERDL Guard

Three tools

Tool

What it does

draft_policy

Rules, one sentence per line, EN or ES -> one Cedar policy per rule (@id("rule-N")) plus a Cedar schema fragment generated from the tool catalog. Keyless: a deterministic template drafter for the documented patterns. With GEMINI_API_KEY: Gemini structured output returns the rule intent and its own Cedar text.

prove_policy

Accepts a draft only if all 8 stages pass (below). Writes proof.json and tests.json into the bundle.

gate_call

ALLOW/DENY for one call {agent, roles, server, tool, arguments} with the cedar-policy 4.14.0 crate, against a bundle whose proof is accepted and whose files still hash to the proof. Returns the determining policy ids.

How a call maps to Cedar

MCP

Cedar

agent id, roles

CedarDraft::Agent::"<id>", member of CedarDraft::Role::"<role>"

tool refund on server billing

CedarDraft::Action::"billing___refund", member of group action CedarDraft::Action::"billing"

server

CedarDraft::Server::"billing" (resource)

tool arguments

context.input, typed from the tool's JSON Schema (integer -> Long, string -> String, boolean -> Bool, arrays -> Set, objects -> records)

The <server>___<tool> action name copies the <TargetName>___<ToolName> shape that the AgentCore Gateway docs give for MCP targets. That shape is the only AgentCore-related piece here.

Shared base schema with Cedar 4.14.0 external

schemas/base.cedarschema defines Role, Agent and Server once. Each draft's schema.fragment.cedarschema declares external entity Agent; and external entity Server; and only defines the actions for its catalog. The engine links them with Schema::schema_link_complete (cedar-policy 4.14.0). cedar validate refuses the fragment alone because it is a partial schema. Many drafts can share one base.

prove_policy stages

  1. tools: cedar --version is exactly cedar-policy-cli 4.14.0, and the engine was built on cedar-policy 4.14.0.

  2. intent: every rule names catalog servers, tools and arguments of the right type.

  3. link: the fragment uses external, and linking it with the base leaves nothing external.

  4. validate: strict validation in the crate against the linked schema, with zero errors and zero warnings.

  5. cedar-cli: cedar validate --validation-mode strict --deny-warnings with the official CLI. The CLI has no schema-link command, so it gets a JSON merge of cedar translate-schema output.

  6. ids: policy @ids equal the rule ids. Unexplained extra policies are rejected.

  7. table: an authorization table built from the rule intent and the catalog, never from the policy text. Every catalog tool is called by an agent of each named role and of an outsider role, with neutral arguments and boundary values for each condition (N-1, N, N+1; each listed value plus one unlisted value). There must be at least one default-deny case, and every case must pass cedar run-tests.

  8. crate-table: the same table through the crate, which is the gate_call path. Decisions and determining-policy sets must match exactly.

Quickstart

Requires Bun 1.4.3, Node 24 (.nvmrc) and Rust 1.99.0 (rust-toolchain.toml). No API keys.

bun install --frozen-lockfile
bun run setup        # cargo install cedar-policy-cli 4.14.0 --locked into .tools/, build engine/
bun run check        # full local gate

bun run cedardraft -- draft --file examples/rules.en-es.txt --out .cedardraft/example
bun run cedardraft -- prove .cedardraft/example                  # exit 0 accepted, 1 rejected
bun run cedardraft -- gate .cedardraft/example --agent a1 --role soporte \
  --server billing --tool refund --args '{"invoice_id":"i1","amount":50001,"currency":"USD"}'
# DENY - default deny: no permit policy matched                  (exit 2)

MCP client config (stdio):

{ "mcpServers": { "cedardraft": { "command": "bun", "args": ["run", "/path/to/cedardraft-mcp/src/mcp.ts"] } } }

The skill at .claude/skills/cedardraft-mcp/SKILL.md tells an agent how to use the three tools.

Rule patterns

The template drafter understands only these patterns. Any other sentence is an error that names the sentence. The drafter does not guess. Tools are written server.tool, or as any tool on server S / cualquier herramienta del servidor S. Roles are written agents with role R (or R2) / los agentes con rol R (o R2), or any agent / cualquier agente.

Pattern

English

Spanish

allow

Allow agents with role support to call billing.get_invoice and billing.list_invoices

Permitir que los agentes con rol soporte llamen a billing.get_invoice y billing.list_invoices

allow-cap

Allow agents with role support to call billing.refund only when amount is at most 50000

Permitir que los agentes con rol soporte llamen a billing.refund solo cuando amount sea como máximo 50000

allow-values

Allow agents with role dev or ops to call github.create_issue only when repo is one of "acme/web", "acme/api"

Permitir que los agentes con rol dev o ops llamen a github.create_issue solo cuando repo sea uno de "acme/web", "acme/api"

forbid

Never allow any agent to call billing.delete_customer

Nunca permitir que ningún agente llame a billing.delete_customer

forbid-when

Never allow any agent to call crm.export_contacts when include_pii is true

Nunca permitir que ningún agente llame a crm.export_contacts cuando include_pii sea verdadero

scope

In this task only crm.search_contacts and crm.get_contact are in scope

En esta tarea solo crm.search_contacts y crm.get_contact están en alcance

Numeric conditions: at most / no more than / up to (<=), under / less than / below (<), at least (>=), over / more than / greater than / above (>). Spanish: como máximo / a lo sumo / hasta, menor que / menos de, al menos / como mínimo, mayor que / más de. An optional argument is guarded with has, so a missing argument never satisfies a condition.

Measured

Produced by bun run report (reports/cedardraft.md, reports/cedardraft.json) on the committed fixtures on this machine. The CLI was cedar-policy-cli 4.14.0 and the engine was built on cedar-policy 4.14.0.

Measure

Result

Documented patterns drafted keyless and accepted by prove_policy

12/12 (6 patterns x EN, ES)

Example rule set (examples/rules.en-es.txt, 6 rules, mixed EN/ES)

accepted

Its generated table

85 cases: 19 allow, 66 deny (10 forbid, 56 default deny)

cedar run-tests / crate agreement on that table

85/85 passed / 85/85 matched

Deliberately wrong drafts rejected

8/8 (widened limit, off-by-one: table; type error, unknown argument, missing @id: validate; extra policy: ids; permit-all: table; fragment without external: link)

gate_call vs expected decision + determining policies

85/85 agree

gate_call latency (one engine process per call)

median 14.2 ms, p95 18.2 ms

Live Gemini drafting

not run: gemini-3.8-flash returned HTTP 402 for this key

Verified facts (and where)

Claim

Status

Source

cedar-policy and cedar-policy-cli 4.14.0 are current

Verified: crates.io API, both max_stable_version 4.14.0, published 2026-10-17. Tags v4.14.0 and cedar-policy-cli-v4.14.0 exist on GitHub.

crates.io, github.com/cedar-policy/cedar releases

4.14.0 adds the external keyword for partial/shared schemas

Verified in the cedar-policy changelog [4.14.0] - 2026-10-17 (external entity type and action declarations, Schema::schema_link, schema_link_complete, is_complete) and in PR #2615. Checked in code: the fragment links, and cedar validate refuses the fragment alone because "schema is incomplete".

cedar-policy/CHANGELOG.md on main, PR cedar-policy/cedar#2615

CLI flags used

Read from cedar --help at 4.14.0: validate --schema --schema-format --policies --validation-mode strict --deny-warnings --error-format, run-tests --policies --tests --schema --schema-format, translate-schema --direction cedar-to-json. The CLI uses each policy's @id annotation as its id.

.tools/bin/cedar 4.14.0, its source

Amazon Bedrock AgentCore Policy uses Cedar

Verified in the AgentCore developer guide: policies are "constructed using Cedar language". The same page says Dogwood is also supported. The Cedar page documents AgentCore::Action::"<TargetName>___<ToolName>" for MCP targets. No AWS call was made.

docs.aws.amazon.com/bedrock-agentcore/latest/devguide/policy.html and policy-understanding-cedar.html

Limits

  • Templates are narrow on purpose. There are six patterns, one condition per sentence, and one server per sentence. Anything else must be rephrased or drafted with Gemini.

  • Gemini drafting is untested live. The key on this machine returns HTTP 402 (no credits), so no Gemini-written draft has been produced or measured. The Gemini path is covered by unit tests with synthetic, mocked responses. tests/live skips on a missing key, 402 or 429.

  • The proof checks the policy text against the rule intent, not against what the user meant. If the intent itself is wrong (a wrong role or a too-high limit), the draft can prove. Review draft.json rules and their source sentences.

  • The table is generated, not exhaustive. It probes boundaries and every catalog tool × role. It is not a symbolic proof. cedar symcc exists in the experimental CLI build and is not used here.

  • The CLI does not link schemas. The cedar-cli stage validates a JSON merge that this repo computes. The link itself is checked by the crate (schema_link_complete). Both must pass.

  • Cedar types: JSON number arguments (non-integer) cannot be typed in Cedar. They are left out of the context and listed as warnings, and no rule can reference them. Undeclared arguments make gate_call DENY.

  • No AgentCore export. The idea's optional "AgentCore Policy export" is not built. The policies use the CedarDraft namespace, not AgentCore.

  • Unverified idea context: the CERT-In guidance the idea quotes (define in-scope systems and actions per task) was not checked here. The scope pattern is only modeled on it. The Cedar docs site's schema grammar page does not yet mention external; the changelog and PR are the sources.

  • Speed: gate_call starts one engine process per call (median 14.2 ms here). A long-lived engine would be faster but is not built.

Not affiliated

Not affiliated with, endorsed by, or sponsored by Amazon Web Services, the Cedar project, Google (Gemini), Anthropic or the Model Context Protocol project. Cedar and AgentCore are referenced only to describe compatibility facts checked above.


Built by SkilledDragon LLC. Questions: hello@skilleddragon.com. Educational minilab, not regularly maintained. See SECURITY.md.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    A
    maintenance
    Deterministic policy enforcement for AI agent tool calls. It evaluates every tool call against user-defined rules before execution, with no LLM in the authorization path.
    4
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Enforces deterministic policy decisions on AI agent tool calls, supporting allow, deny, correct, escalate, and human review actions with verifiable audit receipts.
    11 npm
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables governed tool-calling agents with policy decisions, optional human approval, hash-chained audit logging, and deterministic evaluation.
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enforces configurable policies on AI agent tool calls, allowing, requesting review, or denying actions before they are executed.
    MIT