AgentCore Gateway
Generate an Amazon Bedrock AgentCore Gateway project. An AgentCore Gateway is a managed entry point that aggregates one or more MCP server targets behind a single MCP endpoint, evaluates every tool call against a Cedar policy engine, and signs outbound traffic to MCP servers with IAM SigV4.
Generate an AgentCore Gateway
Section titled “Generate an AgentCore Gateway”- Install the Nx Console VSCode Plugin if you haven't already
- Open the Nx Console in VSCode
- Click
Generate (UI)in the "Common Nx Commands" section - Search for
@aws/nx-plugin - agentcore-gateway - Fill in the required parameters
- Click
Generate
pnpm nx g @aws/nx-plugin:agentcore-gatewayyarn nx g @aws/nx-plugin:agentcore-gatewaynpx nx g @aws/nx-plugin:agentcore-gatewaybunx nx g @aws/nx-plugin:agentcore-gatewayYou can also perform a dry-run to see what files would be changed
pnpm nx g @aws/nx-plugin:agentcore-gateway --dry-runyarn nx g @aws/nx-plugin:agentcore-gateway --dry-runnpx nx g @aws/nx-plugin:agentcore-gateway --dry-runbunx nx g @aws/nx-plugin:agentcore-gateway --dry-runOptions
Section titled “Options”| Parameter | Type | Default | Description |
|---|---|---|---|
| name Required | string | - | The name of your AgentCore Gateway project |
| directory | string | packages | Parent directory where the gateway project is placed. |
| subDirectory | string | - | The sub directory the project is placed in. By default this is the project name. |
| protocol | mcp | mcp | The inbound protocol exposed by your gateway. Only mcp is supported today; additional protocols may be added in future. |
| auth | iam | iam | The method used to authenticate inbound requests to your gateway. Only iam is supported today; cognito and custom-jwt may be added in future. |
| cedarPolicy | boolean | true | Whether to include a Cedar policy engine enforcing fine-grained authorization on the gateway. |
| infra | agentcore | none | agentcore | The type of infrastructure to host your gateway. Select none for no hosting. |
| iac | inherit | cdk | terraform | inherit | The preferred IaC provider. By default this is inherited from your initial selection. |
| preferInstallDependencies | boolean | true | Whether to prefer installing dependencies after the generator runs. Set to false to defer installing when batching multiple generators (an install still runs if needed so subsequent generators can compute the Nx project graph); install once at the end. |
Generator Output
Section titled “Generator Output”The generator creates a new project at packages/<name>/, plus a CDK construct or Terraform module for the infrastructure:
Directorypackages/<name>/
Directorypolicies/ Cedar policy source files (omitted when
cedarPolicy: false)- permit-all.cedar Default Cedar policy that permits authenticated callers from within the same AWS account
- README.md Reference for writing Cedar policies
- local-dev.ts Local gateway aggregating attached MCP servers for local development
- project.json Adds the
serveanddevtargets
Infrastructure
Section titled “Infrastructure”Infrastructure is generated when infra is agentcore (the default). With infra: none no infrastructure is generated — re-run the generator with infra: agentcore later to add it.
Since this generator vends infrastructure as code based on your chosen iacProvider, it will create a project in packages/common which includes the relevant CDK constructs or Terraform modules.
The common infrastructure as code project is structured as follows:
Directorypackages/common/constructs
Directorysrc
Directoryapp/ Constructs for infrastructure specific to a project/generator
- …
Directorycore/ Generic constructs which are reused by constructs in
app- …
- index.ts Entry point exporting constructs from
app
- project.json Project build targets and configuration
Directorypackages/common/terraform
Directorysrc
Directoryapp/ Terraform modules for infrastructure specific to a project/generator
- …
Directorycore/ Generic modules which are reused by modules in
app- …
- project.json Project build targets and configuration
Directorypackages/common/constructs/src
Directorycore
Directoryagentcore-gateway/ Shared gateway construct (readiness probe, Cedar policy loading)
- …
Directoryapp
Directorygateways
Directory<name>/
- <name>.ts CDK construct for deploying the Gateway
Directorypackages/common/terraform/src
Directoryapp
Directorygateways
Directory<name>/
- <name>.tf Terraform module for deploying the Gateway
The generated construct creates the following AWS resources:
- An
AgentCore::Gatewayconfigured for the MCP protocol withGatewayAuthorizer.usingAwsIam()(inbound IAM authentication) - An
AgentCore::PolicyEnginerunning inENFORCEmode, attached to the Gateway (omitted whencedarPolicy: false) - One
AgentCore::Policyper.cedarfile inpolicies/
The Gateway URL is automatically registered in the agentcore.gateways.<ClassName> namespace of Runtime Configuration so agents can discover it at runtime.
Writing Policies
Section titled “Writing Policies”Cedar is the policy language used by AgentCore Gateway to authorize tool calls. Every tools/list and tools/call request flowing through the Gateway is evaluated against the attached policy set, and the caller must have at least one matching permit statement (and no matching forbid) for the request to succeed.
Refer to the AWS documentation on AgentCore Gateway policies for the full reference, including common policy patterns.
Adding a policy
Section titled “Adding a policy”To add a policy, create a new .cedar file alongside permit-all.cedar. Each .cedar file in policies/ must contain exactly one permit or forbid statement and is deployed as a single AWS::BedrockAgentCore::Policy resource. The policy resource’s name is derived from the filename: permit-all.cedar becomes PermitAll (kebab/snake-case is converted to PascalCase). Files containing multiple statements produce unexpected token 'forbid' errors at deploy time — split them into separate files.
For example, to permit only a specific agent role to invoke a particular tool, create:
permit ( principal == AgentCore::IamEntity::"arn:aws:sts::<%= accountId %>:assumed-role/<%= tsAgentRoleName %>", action == AgentCore::Action::"ts-mcp___divide", resource == AgentCore::Gateway::"<%= gatewayArn %>");Pass the role name in as a template variable (see Template variables below) rather than hard-coding it. Re-synth or re-plan to deploy the new policy.
Template variables
Section titled “Template variables”Policies are EJS templates rendered at synth/plan time so they stay portable across accounts and Gateway redeploys:
| Variable | Substituted with |
|---|---|
<%= gatewayArn %> | The deployed Gateway’s ARN |
<%= accountId %> | The AWS account this Gateway is deployed into |
Always reference these variables rather than hard-coding values.
Adding your own variables
Section titled “Adding your own variables”Add new variables where the policies are rendered, for example to pass an agent’s execution role name to the ts-agent-divide.cedar example above:
In packages/common/constructs/src/app/gateways/<name>/<name>.ts, pass cedarPolicyVariables through to the shared construct:
super(scope, id, { cedarPolicyPath: path.join( ... ), cedarPolicyVariables: { tsAgentRoleName: cdk.Token.asString(tsAgent.agentCoreRuntime.role.roleName), },});In packages/common/terraform/src/app/gateways/<name>/<name>.tf, add to the query of the rendered_policies data source:
query = { template = "${local.policies_dir}/${each.value}" gatewayArn = aws_bedrockagentcore_gateway.this.gateway_arn tsAgentRoleName = var.ts_agent_role_name # ...}ENFORCE mode and default-deny
Section titled “ENFORCE mode and default-deny”The PolicyEngine runs in ENFORCE mode, which means default-deny semantics apply: if no permit statement matches the (principal, action, resource) tuple, the request is denied. Tool calls that are denied return:
Tool Execution Denied: Tool call not allowed due to policy enforcement[No policy applies to the request (denied by default).]Additionally, the Gateway filters the response of tools/list so that callers only see tools they have at least one matching permit for: if an agent lacks permission for a given tool, the tool is hidden entirely rather than appearing and failing at call time.
Default permit-all.cedar
Section titled “Default permit-all.cedar”The generator ships a default policy that permits any IAM caller from the AWS account the Gateway is deployed into:
permit ( principal is AgentCore::IamEntity, action, resource == AgentCore::Gateway::"<%= gatewayArn %>") when { principal.id like "arn:aws:*::<%= accountId %>:*"};To lock things down further, add narrower policies alongside it. Always retain at least one matching permit in the policy set, otherwise default-deny will block every call.
Policy scope reference
Section titled “Policy scope reference”For Gateways generated by this plugin, all callers are IAM principals (the Gateway is configured with GatewayAuthorizer.usingAwsIam()):
principal is AgentCore::IamEntityCallers are evaluated as STS assumed-role ARNs with the session name stripped, so a role can be matched exactly — no wildcards needed:
principal == AgentCore::IamEntity::"arn:aws:sts::<%= accountId %>:assumed-role/<%= myAgentRoleName %>"The same value is available as principal.id for when clauses. Reserve like for genuine patterns, such as the account-wide match in the default permit-all.cedar.
Tool invocations reach the policy engine as actions with the form:
AgentCore::Action::"<target-name>___<tool-name>"where <target-name> is the Gateway target name (the MCP server’s mcpServerName by default when using gateway.addMcpServer(...), derived from the MCP project’s class name in kebab-case — e.g. TsMcp → ts-mcp), <tool-name> is the MCP tool’s name, and the separator is ___ (three underscores). Cedar does not support wildcards on actions — match exact actions, or omit action == to match all actions.
The resource is always the Gateway itself:
resource == AgentCore::Gateway::"<%= gatewayArn %>"Validation considerations
Section titled “Validation considerations”The generated infrastructure creates policies with IGNORE_ALL_FINDINGS: AgentCore’s Cedar analyzer (FAIL_ON_ANY_FINDINGS, the service default) rejects many legitimate policies — for example, a forbid disabling a single tool for every caller is rejected as “Overly Restrictive”, even when scoped with a when clause. Enforcement is unaffected; it is configured by the policy engine’s ENFORCE mode.
One ordering constraint still applies: a policy referencing AgentCore::Action::"<target>___<tool>" only validates once the target has registered that tool with the Gateway, which is why the generated infrastructure creates policies after Gateway targets.
If a policy fails to deploy, CloudFormation surfaces the rejection as an opaque Resource stabilization failed error — run aws bedrock-agentcore-control list-policies --policy-engine-id <id> to retrieve the validator’s statusReasons, which contain the actual reason.
Example: forbid one tool while keeping a broader permit
Section titled “Example: forbid one tool while keeping a broader permit”Pair a narrow forbid with a broader permit (Cedar evaluates forbid over permit):
forbid ( principal == AgentCore::IamEntity::"arn:aws:sts::<%= accountId %>:assumed-role/<%= pyAgentRoleName %>", action == AgentCore::Action::"ts-mcp___divide", resource == AgentCore::Gateway::"<%= gatewayArn %>");This denies the Python agent role from calling ts-mcp___divide while leaving the broader permit-all.cedar in place for all other callers.
Local Development
Section titled “Local Development”The generator adds a dev target to the Gateway project, which runs local-dev.ts: a local gateway exposing a single MCP endpoint that aggregates every attached MCP server (connected via the agentcore-gateway#mcp-connection generator), with tools prefixed <target>___<tool> to match the deployed Gateway. Running it starts the local gateway and all attached MCP servers together:
pnpm nx dev <name>yarn nx dev <name>npx nx dev <name>bunx nx dev <name>See the connection guide for the full local development story.
Connections
Section titled “Connections”Use the connection generator to integrate this project with others in your workspace. The following connections involve this project: