Skip to content

AgentCore Gateway

Filter this guide Pick generator option values to hide sections that don't apply.

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.

  1. Install the Nx Console VSCode Plugin if you haven't already
  2. Open the Nx Console in VSCode
  3. Click Generate (UI) in the "Common Nx Commands" section
  4. Search for @aws/nx-plugin - agentcore-gateway
  5. Fill in the required parameters
    • Click Generate
    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.

    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 serve and dev targets

    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/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

    The generated construct creates the following AWS resources:

    • An AgentCore::Gateway configured for the MCP protocol with GatewayAuthorizer.usingAwsIam() (inbound IAM authentication)
    • An AgentCore::PolicyEngine running in ENFORCE mode, attached to the Gateway (omitted when cedarPolicy: false)
    • One AgentCore::Policy per .cedar file in policies/

    The Gateway URL is automatically registered in the agentcore.gateways.<ClassName> namespace of Runtime Configuration so agents can discover it at runtime.

    cedarPolicy = true

    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.

    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:

    packages/<name>/policies/ts-agent-divide.cedar
    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.

    Policies are EJS templates rendered at synth/plan time so they stay portable across accounts and Gateway redeploys:

    VariableSubstituted 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.

    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),
    },
    });

    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.

    The generator ships a default policy that permits any IAM caller from the AWS account the Gateway is deployed into:

    packages/<name>/policies/permit-all.cedar
    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.

    For Gateways generated by this plugin, all callers are IAM principals (the Gateway is configured with GatewayAuthorizer.usingAwsIam()):

    principal is AgentCore::IamEntity

    Callers 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. TsMcpts-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 %>"

    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):

    packages/<name>/policies/forbid-divide-for-py-agent.cedar
    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.

    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:

    Terminal window
    pnpm nx dev <name>

    See the connection guide for the full local development story.

    Use the connection generator to integrate this project with others in your workspace. The following connections involve this project:

    Amazon Bedrock AgentCore Gateway Model Context Protocol
    AgentCore Gateway to MCP Server Aggregate an MCP server behind an AgentCore Gateway
    Amazon Bedrock AgentCore Gateway Amazon Bedrock AgentCore Gateway
    AgentCore Gateway to AgentCore Gateway Aggregate an AgentCore Gateway behind another AgentCore Gateway
    Strands Agents TypeScript Amazon Bedrock AgentCore Gateway
    TypeScript Agent to AgentCore Gateway Connect a TypeScript Agent to an AgentCore Gateway
    Strands Agents Python Amazon Bedrock AgentCore Gateway
    Python Agent to AgentCore Gateway Connect a Python Agent to an AgentCore Gateway