Enterprise MCP Governance Gateway

Govern

Cedar policies

The 7 policy files under policies/, rendered from the repository. 5 are deployed by the manifest and 2 are kept disabled.

Validation mode

policies/manifest.json creates every policy with validationMode IGNORE_ALL_FINDINGS. The manifest comment describes FAIL_ON_ANY_FINDINGS and warns against falling back without understanding the finding.

Source: manifest.json (opens in new tab)

Two files are not deployed

atlassian-read-all-write-admin and github-read-only-by-default are listed under disabledPolicies. Their MCP-server targets are not registered on the live gateway, and the policy engine derives its Cedar schema from registered tools, so policies for unregistered tools fail validation with unrecognized action.

Source: manifest.json (opens in new tab)

admin-write-only.cedar

Only users with role admin can create, update or delete docs pages.

Deployed: listed under policies in manifest.json.

Source: admin-write-only.cedar (opens in new tab)

// Policy 2: Only users with role "admin" can create/update/delete docs.
// Resource ARN is substituted at deploy time (token __GATEWAY_ARN__).
// NOTE: One single-action permit per tool. The policy engine does not match
// Cedar action set-membership (`action in [...]`) for tool authorization.
//
// IMPORTANT: these permits depend on a `role` principal tag. With this Cognito
// setup `custom:role` is present only in the ID token, NOT the access token the
// gateway validates — so for the demo users these permits do not fire and writes
// stay DENIED (the safe default). To demonstrate role-based writes, surface the
// role in the ACCESS token (Cognito pre-token-generation V2 Lambda, or a custom
// OAuth scope per role) and these policies enforce as written.
permit(
  principal is AgentCore::OAuthUser,
  action == AgentCore::Action::"DocsAPI___create_page",
  resource == AgentCore::Gateway::"__GATEWAY_ARN__"
)
when {
  principal.hasTag("role") &&
  principal.getTag("role") == "admin"
};

permit(
  principal is AgentCore::OAuthUser,
  action == AgentCore::Action::"DocsAPI___update_page",
  resource == AgentCore::Gateway::"__GATEWAY_ARN__"
)
when {
  principal.hasTag("role") &&
  principal.getTag("role") == "admin"
};

permit(
  principal is AgentCore::OAuthUser,
  action == AgentCore::Action::"DocsAPI___delete_page",
  resource == AgentCore::Gateway::"__GATEWAY_ARN__"
)
when {
  principal.hasTag("role") &&
  principal.getTag("role") == "admin"
};

View admin-write-only.cedar on GitHub (opens in new tab)

allow-docs-read.cedar

Allows every authenticated OAuth user to call the read-only Docs tools.

Deployed: listed under policies in manifest.json.

Source: allow-docs-read.cedar (opens in new tab)

// Policy 1: Allow all authenticated OAuth users to call read-only Docs tools.
// Resource ARN is substituted at deploy time (token __GATEWAY_ARN__).
// NOTE: The AgentCore policy engine does NOT match Cedar action set-membership
// (`action in [...]`) for tool authorization. Each tool MUST be its own
// single-action `action == AgentCore::Action::"..."` permit statement.
permit(
  principal is AgentCore::OAuthUser,
  action == AgentCore::Action::"DocsAPI___get_page",
  resource == AgentCore::Gateway::"__GATEWAY_ARN__"
);

permit(
  principal is AgentCore::OAuthUser,
  action == AgentCore::Action::"DocsAPI___search_pages",
  resource == AgentCore::Gateway::"__GATEWAY_ARN__"
);

permit(
  principal is AgentCore::OAuthUser,
  action == AgentCore::Action::"DocsAPI___list_spaces",
  resource == AgentCore::Gateway::"__GATEWAY_ARN__"
);

View allow-docs-read.cedar on GitHub (opens in new tab)

atlassian-read-all-write-admin.cedar

Every authenticated user may read and search Jira and Confluence; writes are permitted only for users carrying role atlassian-writer.

Not deployed: listed under disabledPolicies in manifest.json.

Source: atlassian-read-all-write-admin.cedar (opens in new tab)

// Real target: Atlassian (Jira + Confluence) MCP server, fronted by the gateway
// with per-user Atlassian OAuth (3LO). Tools surface as Atlassian___<tool>.
//
// Governance model:
//   * READ/SEARCH  -> permitted for every authenticated gateway user.
//   * WRITE        -> permitted ONLY for users carrying role = "atlassian-writer".
//     Demo users have no `role` tag in the Cognito ACCESS token, so the write
//     permits never fire and writes stay DENIED by default (the safe MVP posture).
//     To enable writes in production, surface `custom:role` into the access token
//     (Cognito pre-token-generation Lambda) — then these permits enforce as written.
//
// NOTE: one single-action statement per tool — the policy engine does not match
// Cedar action set-membership (`action in [...]`). Resource ARN is substituted at
// deploy time (token __GATEWAY_ARN__). Beneath this, Atlassian still enforces each
// user's own product permissions (the 3LO token is theirs) — defence in depth.

// ---- Jira read (all authenticated users) ----
permit(
  principal is AgentCore::OAuthUser,
  action == AgentCore::Action::"Atlassian___getJiraIssue",
  resource == AgentCore::Gateway::"__GATEWAY_ARN__"
);

permit(
  principal is AgentCore::OAuthUser,
  action == AgentCore::Action::"Atlassian___searchJiraIssuesUsingJql",
  resource == AgentCore::Gateway::"__GATEWAY_ARN__"
);

permit(
  principal is AgentCore::OAuthUser,
  action == AgentCore::Action::"Atlassian___getVisibleJiraProjects",
  resource == AgentCore::Gateway::"__GATEWAY_ARN__"
);

// ---- Confluence read (all authenticated users) ----
permit(
  principal is AgentCore::OAuthUser,
  action == AgentCore::Action::"Atlassian___getConfluencePage",
  resource == AgentCore::Gateway::"__GATEWAY_ARN__"
);

permit(
  principal is AgentCore::OAuthUser,
  action == AgentCore::Action::"Atlassian___searchConfluenceUsingCql",
  resource == AgentCore::Gateway::"__GATEWAY_ARN__"
);

permit(
  principal is AgentCore::OAuthUser,
  action == AgentCore::Action::"Atlassian___getConfluenceSpaces",
  resource == AgentCore::Gateway::"__GATEWAY_ARN__"
);

// ---- Jira write (role = "atlassian-writer" only) ----
permit(
  principal is AgentCore::OAuthUser,
  action == AgentCore::Action::"Atlassian___createJiraIssue",
  resource == AgentCore::Gateway::"__GATEWAY_ARN__"
)
when { principal.hasTag("role") && principal.getTag("role") == "atlassian-writer" };

permit(
  principal is AgentCore::OAuthUser,
  action == AgentCore::Action::"Atlassian___editJiraIssue",
  resource == AgentCore::Gateway::"__GATEWAY_ARN__"
)
when { principal.hasTag("role") && principal.getTag("role") == "atlassian-writer" };

permit(
  principal is AgentCore::OAuthUser,
  action == AgentCore::Action::"Atlassian___addCommentToJiraIssue",
  resource == AgentCore::Gateway::"__GATEWAY_ARN__"
)
when { principal.hasTag("role") && principal.getTag("role") == "atlassian-writer" };

permit(
  principal is AgentCore::OAuthUser,
  action == AgentCore::Action::"Atlassian___transitionJiraIssue",
  resource == AgentCore::Gateway::"__GATEWAY_ARN__"
)
when { principal.hasTag("role") && principal.getTag("role") == "atlassian-writer" };

// ---- Confluence write (role = "atlassian-writer" only) ----
permit(
  principal is AgentCore::OAuthUser,
  action == AgentCore::Action::"Atlassian___createConfluencePage",
  resource == AgentCore::Gateway::"__GATEWAY_ARN__"
)
when { principal.hasTag("role") && principal.getTag("role") == "atlassian-writer" };

permit(
  principal is AgentCore::OAuthUser,
  action == AgentCore::Action::"Atlassian___updateConfluencePage",
  resource == AgentCore::Gateway::"__GATEWAY_ARN__"
)
when { principal.hasTag("role") && principal.getTag("role") == "atlassian-writer" };

View atlassian-read-all-write-admin.cedar on GitHub (opens in new tab)

block-large-queries.cedar

Forbids database queries requesting more than 1000 rows and forbids bulk export unless the user has the data-engineer role.

Deployed: listed under policies in manifest.json.

Source: block-large-queries.cedar (opens in new tab)

// Policy 4: Block queries exceeding the row-limit threshold, and gate bulk export
// behind the "data-engineer" role.
// Resource ARN is substituted at deploy time (token __GATEWAY_ARN__).

// Forbid database queries requesting more than 1000 rows.
forbid(
  principal is AgentCore::OAuthUser,
  action == AgentCore::Action::"DatabaseAPI___execute_query",
  resource == AgentCore::Gateway::"__GATEWAY_ARN__"
)
when {
  // maxRows is JSON type "number", which the policy-engine Cedar schema maps to
  // the `decimal` extension type. Comparing a decimal to a Long literal (`> 1000`)
  // is a type error; use the decimal extension's .greaterThan(decimal("...")).
  context.input has maxRows &&
  context.input.maxRows.greaterThan(decimal("1000.0"))
};

// Forbid bulk export tool unless user has "data-engineer" role.
forbid(
  principal is AgentCore::OAuthUser,
  action == AgentCore::Action::"DatabaseAPI___bulk_export",
  resource == AgentCore::Gateway::"__GATEWAY_ARN__"
)
unless {
  principal.hasTag("role") &&
  principal.getTag("role") == "data-engineer"
};

View block-large-queries.cedar on GitHub (opens in new tab)

forbid-destructive-db.cedar

Forbids destructive database operations for everyone and constrains execute_query to read-only SELECT statements.

Deployed: listed under policies in manifest.json.

Source: forbid-destructive-db.cedar (opens in new tab)

// Policy 3: Forbid destructive database operations for everyone, and constrain
// execute_query to read-only SELECT statements.
// Resource ARN is substituted at deploy time (token __GATEWAY_ARN__).

// Block ALL users from calling destructive database tools regardless of role.
// One single-action forbid per tool: the policy engine does not match Cedar
// action set-membership (`action in [...]`) for tool authorization.
forbid(
  principal is AgentCore::OAuthUser,
  action == AgentCore::Action::"DatabaseAPI___drop_table",
  resource == AgentCore::Gateway::"__GATEWAY_ARN__"
);

forbid(
  principal is AgentCore::OAuthUser,
  action == AgentCore::Action::"DatabaseAPI___truncate_table",
  resource == AgentCore::Gateway::"__GATEWAY_ARN__"
);

forbid(
  principal is AgentCore::OAuthUser,
  action == AgentCore::Action::"DatabaseAPI___delete_records",
  resource == AgentCore::Gateway::"__GATEWAY_ARN__"
);

// Allow read-only database queries for all authenticated users.
permit(
  principal is AgentCore::OAuthUser,
  action == AgentCore::Action::"DatabaseAPI___execute_query",
  resource == AgentCore::Gateway::"__GATEWAY_ARN__"
)
when {
  context.input has query &&
  context.input.query like "SELECT *"
};

// Forbid execute_query if it contains dangerous patterns.
forbid(
  principal is AgentCore::OAuthUser,
  action == AgentCore::Action::"DatabaseAPI___execute_query",
  resource == AgentCore::Gateway::"__GATEWAY_ARN__"
)
when {
  context.input has query &&
  (context.input.query like "*DROP*" ||
   context.input.query like "*DELETE*" ||
   context.input.query like "*TRUNCATE*" ||
   context.input.query like "*INSERT*" ||
   context.input.query like "*UPDATE*")
};

View forbid-destructive-db.cedar on GitHub (opens in new tab)

github-read-only-by-default.cedar

Everyone may read and search GitHub; writes are forbidden unless the user carries the github-writer role tag.

Not deployed: listed under disabledPolicies in manifest.json.

Source: github-read-only-by-default.cedar (opens in new tab)

// Real target: GitHub remote MCP server.
// Everyone may read/search; writes forbidden unless the user carries the
// "github-writer" role tag.
// Resource ARN is substituted at deploy time (token __GATEWAY_ARN__).

// One single-action statement per tool: the policy engine does not match Cedar
// action set-membership (`action in [...]`) for tool authorization.

// Everyone may read/search GitHub.
permit(
  principal is AgentCore::OAuthUser,
  action == AgentCore::Action::"GitHub___search_repositories",
  resource == AgentCore::Gateway::"__GATEWAY_ARN__"
);

permit(
  principal is AgentCore::OAuthUser,
  action == AgentCore::Action::"GitHub___search_code",
  resource == AgentCore::Gateway::"__GATEWAY_ARN__"
);

permit(
  principal is AgentCore::OAuthUser,
  action == AgentCore::Action::"GitHub___search_issues",
  resource == AgentCore::Gateway::"__GATEWAY_ARN__"
);

permit(
  principal is AgentCore::OAuthUser,
  action == AgentCore::Action::"GitHub___get_file_contents",
  resource == AgentCore::Gateway::"__GATEWAY_ARN__"
);

permit(
  principal is AgentCore::OAuthUser,
  action == AgentCore::Action::"GitHub___list_pull_requests",
  resource == AgentCore::Gateway::"__GATEWAY_ARN__"
);

// Forbid GitHub writes unless the user carries the "github-writer" role tag.
forbid(
  principal is AgentCore::OAuthUser,
  action == AgentCore::Action::"GitHub___create_issue",
  resource == AgentCore::Gateway::"__GATEWAY_ARN__"
)
unless {
  principal.hasTag("role") && principal.getTag("role") == "github-writer"
};

forbid(
  principal is AgentCore::OAuthUser,
  action == AgentCore::Action::"GitHub___update_issue",
  resource == AgentCore::Gateway::"__GATEWAY_ARN__"
)
unless {
  principal.hasTag("role") && principal.getTag("role") == "github-writer"
};

forbid(
  principal is AgentCore::OAuthUser,
  action == AgentCore::Action::"GitHub___create_pull_request",
  resource == AgentCore::Gateway::"__GATEWAY_ARN__"
)
unless {
  principal.hasTag("role") && principal.getTag("role") == "github-writer"
};

View github-read-only-by-default.cedar on GitHub (opens in new tab)

sensitive-tool-restrict.cedar

Restricts sensitive tools to specific users or scopes: query_audit_logs to one username and export_pii_report to an OAuth scope.

Deployed: listed under policies in manifest.json.

Source: sensitive-tool-restrict.cedar (opens in new tab)

// Policy 5: Restrict sensitive tools to specific users / scopes.
// Resource ARN is substituted at deploy time (token __GATEWAY_ARN__).

// Restrict the audit_logs tool to a specific user (PRODUCTION PATTERN / illustration).
// NOTE: with this Cognito setup the access-token `username` claim is the user's
// sub-UUID, not the email — so this exact match won't fire for the demo users
// (audit_logs stays denied, which is the safe default). In production, drive this
// from a stable identity claim present in your access token (e.g. a custom scope
// or a verified principal id) rather than an email.
permit(
  principal is AgentCore::OAuthUser,
  action == AgentCore::Action::"DatabaseAPI___query_audit_logs",
  resource == AgentCore::Gateway::"__GATEWAY_ARN__"
)
when {
  principal.hasTag("username") &&
  principal.getTag("username") == "security-admin@example.com"
};

// PII export is restricted by OAuth scope.
//
// IMPORTANT (verified against this Cognito setup): the GATEWAY validates the
// Cognito ACCESS token, whose claims become Cedar principal tags. The access
// token reliably carries `sub`, `username`, and `scope` — but NOT `email` or
// `custom:role` (those live only in the ID token, which the gateway rejects).
// So identity policies MUST key on a claim present in the ACCESS token.
//
//   (a) PRODUCTION pattern: a dedicated OAuth scope (e.g. "mcp-gateway/pii:read")
//       granted only to privileged app clients/users via a Cognito resource
//       server. Replace the match below with that scope.
//   (b) DEMO: the demo users share Cognito's default `aws.cognito.signin.user.admin`
//       scope, so the match below lets an authenticated demo user exercise the
//       RESPONSE interceptor's PII redaction from a CLEAN deploy. This is NOT
//       fine-grained authz — it exists to make the redaction control demonstrable.
permit(
  principal is AgentCore::OAuthUser,
  action == AgentCore::Action::"DatabaseAPI___export_pii_report",
  resource == AgentCore::Gateway::"__GATEWAY_ARN__"
)
when {
  principal.hasTag("scope") &&
  principal.getTag("scope") like "*aws.cognito.signin.user.admin*"
};

View sensitive-tool-restrict.cedar on GitHub (opens in new tab)