v vanemmerik.ai / aws-ai
Tip of the Day 2026 · 06 · 03 ≈ 9 min read Bedrock AgentCore · Policy

AgentCore Policy, in Cedar.

AgentCore Policy is the rule engine yesterday's tip teased — the last component that fires before a tool call ever reaches Lambda, an OpenAPI target, an MCP server, or a Browser session. It sits in front of an AgentCore Gateway, speaks open-source Cedar, and enforces default-deny, forbid-wins authorization on every tools/call.

$ agentcore add policy-engine --attach-mode ENFORCE  — Cedar at the Gateway boundary

01The problem Policy in AgentCore exists to solve

The agent loop is non-deterministic by design. The model decides which tool to call, with what arguments, and when. Most of the time that's exactly what you want — but "most of the time" isn't an audit posture, and "the model decided" isn't a control. From the Policy overview: "AI agents can dynamically adapt to solve complex problems… However, this flexibility introduces new security challenges, as agents may inadvertently misinterpret business rules, or act outside their intended authority."

Three failure modes you've probably seen:

The shift

Policy moves authorization out of the agent's prose and into a declarative engine. The engine speaks Cedar, runs at the Gateway boundary, defaults to deny, and lets forbid always win.

02How Cedar is wired into a Gateway

You need three pieces, in order. From the Core concepts page:

ResourceWhat it isLifecycle
Gateway The MCP endpoint your agent already calls. Day 5's tip. Created first; policy is bolted on.
Policy engine A namespaced collection of Cedar policies plus an auto-generated Cedar schema derived from your gateway's tool definitions. Created via CreatePolicyEngine and attached to one or more gateways.
Policy An individual Cedar statement (≤ 10 KB), validated against the engine's schema at creation time. Attached to an engine.

The two attach modes (set on agentcore add policy-engine --attach-mode):

The minimal CLI to wire it up, from the Getting Started:

$ agentcore add gateway --name PolicyGateway --authorizer-type NONE --runtimes PolicyDemo   $ agentcore add gateway-target --name RefundTarget \     --type lambda-function-arn --lambda-arn <YOUR_LAMBDA_ARN> \     --tool-schema-file refund_tools.json --gateway PolicyGateway   $ agentcore add policy-engine --name RefundPolicyEngine \     --attach-to-gateways PolicyGateway --attach-mode ENFORCE   $ agentcore add policy --name RefundLimit \     --engine RefundPolicyEngine --source refund_policy.cedar

A small bootstrap pitfall worth knowing: policies that reference a specific gateway ARN in the resource field need a two-phase deploy — first create the gateway, grab the ARN from agentcore status, then write the Cedar file. Cedar does not allow wildcards on the resource.

03Anatomy of a Cedar policy

Every Cedar statement has the same three slots — scope (principal, action, resource), effect (permit or forbid), and an optional condition (when or unless). The canonical refund policy from the Understanding Cedar docs:

permit(   principal is AgentCore::OAuthUser,   action == AgentCore::Action::"RefundTool___process_refund",   resource == AgentCore::Gateway::"arn:aws:bedrock-agentcore:us-west-2:111122223333:gateway/refund-gateway" ) when {   principal.hasTag("username") &&   principal.getTag("username") == "John" &&   context.input.amount < 500 };

Read it left to right: when the principal is an OAuth user named "John", calling the RefundTool___process_refund tool on the refund-gateway Gateway, and the refund amount is under $500, the request is permitted. Any of those clauses fails, the rule doesn't match. If no rule matches, the gateway returns DENY (see §05).

The two principal types you can name in the scope, from the Policy scope page:

Actions are exact-match. There is no wildcard on actions — you either name the tool (AgentCore::Action::"ToolName___operation") or you group tools under a Gateway Target and write the rule against that target (action in AgentCore::Action::"ReadToolsTarget").

04The authorization request the gateway builds

For every tools/call, the Gateway constructs a Cedar request from two inputs — the JWT (or IAM identity) and the MCP tool payload — and hands it to the policy engine. From the Authorization flow doc:

{   "principal": "AgentCore::OAuthUser::\"12345678-1234-1234-1234-123456789012\"",   "action":    "AgentCore::Action::\"RefundTool___process_refund\"",   "resource":  "AgentCore::Gateway::\"arn:aws:bedrock-agentcore:us-west-2:111122223333:gateway/refund-gateway\"",   "context": {     "input": { "orderId": "12345", "amount": 450, "reason": "Defective product" }   } }

Two things to notice. The tool's input parameters land in context.input.* exactly as the agent submitted them — that's the only way a Cedar rule can react to "what is being requested," which is why the refund-amount condition above is on context.input.amount. And the JWT claims arrive as entity tags, not as nested object access — you reach them through principal.getTag("username"), not principal.username.

05Default-deny, forbid-wins, and why both matter

Cedar's evaluation algorithm, verbatim from the Understanding Cedar page:

  1. If any forbid policy matches, the decision is DENY.
  2. If no forbid matches and at least one permit matches, the decision is ALLOW.
  3. If nothing matches, the decision is DENY (default deny).

Two non-obvious corollaries:

When a request is denied, the gateway returns an MCP error result with the text AuthorizeActionException - Tool Execution Denied, including the reason — usually "No policy applies to the request (denied by default)."

06Tool listing is a separate, weaker check

The first thing an MCP client does is call tools/list. The policy engine evaluates that call too — but it can't, by definition, know the input parameters yet. From the Use a Gateway with Policy doc: "A principal is only allowed to see tools in the listing that they would be permitted to call by policy. Because the full context of a tool call is not available during listing, this means a principal is allowed to list a tool if there exists any set of circumstances under which a call to that tool would be permitted."

So tools/list returns the superset — every tool the principal might be allowed to call. The real authorization happens on tools/call, where the gateway has the actual input parameters and can evaluate context.input.* conditions. A tool appearing in the list is not a guarantee that calling it will succeed.

07NL2Cedar — natural-language authoring with automated reasoning

You don't have to write Cedar by hand. The CLI's --generate flag calls the policy authoring service, which takes a sentence and returns a Cedar policy validated against the gateway's auto-generated schema. Example from the NL2Cedar docs: "Allow principal with username 'refund-agent' to process refunds when the refund amount is less than $500." — produces exactly the canonical refund policy in §03.

Three quiet features worth knowing:

The CLI shape:

$ agentcore add policy --name RefundLimit --engine RefundPolicyEngine --generate "Only allow refunds under 1000 dollars" --gateway PolicyGateway

The --gateway flag is required for --generate because the service needs the deployed gateway ARN to resolve the schema.

08Limits worth knowing

From the AgentCore Policy Service Quotas table:

Two gotchas not in the quota table:

09Try it in five minutes

Assuming you already have the AgentCore CLI installed and a project scaffolded:

$ # 1. Create a gateway with no inbound auth (tutorial only) $ agentcore add gateway --name PolicyGateway --authorizer-type NONE --runtimes PolicyDemo   $ # 2. Register a Lambda tool target $ agentcore add gateway-target --name RefundTarget --type lambda-function-arn \     --lambda-arn arn:aws:lambda:us-west-2:111122223333:function:refund \     --tool-schema-file refund_tools.json --gateway PolicyGateway   $ # 3. Attach a policy engine in ENFORCE mode $ agentcore add policy-engine --name RefundPolicyEngine \     --attach-to-gateways PolicyGateway --attach-mode ENFORCE   $ # 4. Deploy so the gateway ARN exists $ agentcore deploy   $ # 5. Generate a policy from English (schema-aware, ARN auto-resolved) $ agentcore add policy --name RefundLimit --engine RefundPolicyEngine \     --generate "Only allow refunds under 1000 dollars" --gateway PolicyGateway   $ # 6. Test a denied call $ curl -s -X POST $(agentcore status -o gateway-url) \     -H "Content-Type: application/json" \     -d '{"jsonrpc":"2.0","id":1,"method":"tools/call",         "params":{"name":"RefundTarget___process_refund",                  "arguments":{"orderId":"1","amount":5000}}}'

The response on the over-limit refund is an MCP isError: true with Tool Execution Denied: Tool call not allowed due to policy enforcement. Drop the amount under 1,000 and the same call succeeds.

Tomorrow we'll cover AgentCore Evaluations — three evaluation types (online, on-demand, batch), the built-in LLM-judge evaluators keyed Builtin.*, and the Lambda contract for a custom code-based evaluator that returns {label, value, explanation}.

Verified against the official AWS docs on 2026-06-03.
Sources: Policy in Amazon Bedrock AgentCore, Core concepts, Understanding Cedar policies, Policy scope, Authorization flow, Writing policies in natural language, Getting started with Policy in AgentCore, Use a Gateway with Policy, Service quotas.
If the docs change, this tip is a snapshot of that day — check the sources for current behaviour.
Heads up — this tip is from 2026-06-03. AWS services move fast. Cross-check the AgentCore developer guide before relying on specifics, then come back for today's tip →
C

This page — research, writing, verification, and deployment — was built by Claude Cowork. No human touched the prose, the layout, or the upload pipeline. The tip was generated this morning, cross-checked against the official AWS docs by an independent verification pass, and published to Cloudflare R2 on a schedule.

A daily experiment by Monty van Emmerik · vanemmerik.ai · what is Claude Cowork?