Agentic AI AcademyAgentic AI Academy

Gateway Auth & Semantic Tool Search

Ingress, egress, and finding the right tool among thousands

Intermediate 14 minBuilder
What you'll be able to do
  • Configure inbound auth on a gateway: CUSTOM_JWT (discoveryUrl/allowedClients), IAM SigV4, AUTHENTICATE_ONLY, or none
  • Choose the correct outbound credential method for each target type using the support matrix
  • Attach the exact IAM actions the gateway service role needs for OAuth and API-key egress
  • Explain when AUTHENTICATE_ONLY is mandatory (token passthrough) and how OBO/token-exchange differs from 3LO
  • Enable semantic tool search at creation and call x_amz_bedrock_agentcore_search to discover tools at runtime
  • Identify the volatile quotas (targets/gateway, tools/target, tool-name length, search TPM) and verify them against the live limits page
At a glance

AgentCore Gateway is the only managed MCP service that handles both ingress (who may invoke the gateway) and egress (how the gateway authenticates to each backend) authentication. This lesson maps the inbound options (OAuth/JWT, IAM SigV4, AUTHENTICATE_ONLY, none), the outbound matrix per target type via AgentCore Identity (service role, OAuth 2LO/3LO/OBO, token passthrough, API key), the exact IAM the gateway role needs, and the built-in semantic tool search that keeps thousands of tools from drowning the model's context.

  1. 1Two doors: ingress and egress
  2. 2Inbound auth: gating the gateway's front door
  3. 3Outbound auth via AgentCore Identity
  4. 4The target support matrix and the gateway role IAM
  5. 5Semantic tool search: finding the right tool among thousands
  6. 6Quotas, volatility, and the gotcha checklist

Two doors: ingress and egress

A Gateway sits between agents and backends, and it has two independent authentication boundaries. Get them straight before touching config, because they are configured in different places and answer different questions:

  • Inbound (ingress) auth answers "who is allowed to call this gateway's MCP endpoint?" It is set once per gateway as the gateway's authorizerType. The gateway is an MCP resource server — clients POST JSON-RPC over MCP Streamable HTTP to /mcp, and inbound auth gates that door.
  • Outbound (egress) auth answers "how does the gateway present credentials to the backend behind a given target?" It is configured per target via AgentCore Identity (the credential provider + the gateway's IAM role), because each target may talk to a different system with a different trust model.

AWS describes Gateway as "the only solution that provides both comprehensive ingress and egress authentication in a fully-managed service." The practical consequence: a single tools/call from an agent crosses both boundaries — first the caller is authenticated against the gateway, then the gateway authenticates itself (or forwards the caller's identity) to the backend.

text
  Agent / MCP client                 Gateway (MCP resource server)              Backend
  ──────────────────   inbound auth   ─────────────────────────   outbound auth  ────────
  JWT | SigV4  ─────────────────────▶  authorizerType            ───────────────▶ Lambda /
                                       (one per gateway)           credentialProvider  OpenAPI /
                                                                   (per target)        MCP server / ...

Key insight

Different mechanisms, different planes

Inbound auth is part of the gateway's control-plane configuration (authorizerConfiguration). Outbound auth is brokered at invocation time by AgentCore Identity, using the gateway's IAM service role and, for OAuth/API-key targets, credentials held in the token vault. Mixing them up is the #1 source of "403 from the gateway" vs "403 from the backend" confusion.

Inbound auth: gating the gateway's front door

Inbound auth is the gateway's authorizerType. There are four choices, set at create time:

authorizerTypeWhat it doesWhen to use
CUSTOM_JWT (OAuth/JWT)Validates the bearer token against an OIDC discoveryUrl and checks it was issued for one of allowedClients. Supports inbound 2LO and 3LO; works with Cognito, Okta, Auth0, or a custom OIDC server.Production. The default for real agents that carry a user or machine identity.
IAM (SigV4)Callers sign requests with AWS SigV4; they need the bedrock-agentcore:InvokeGateway IAM permission.Callers are AWS principals (another Runtime agent, a Lambda, an EC2 role).
AUTHENTICATE_ONLYThe gateway validates the inbound token but delegates authorization to the target. This is the only mode that supports token passthrough outbound.You want the backend to make the final allow/deny decision using the original user token.
No authorizationNo inbound check at all.Local dev / test only. Never ship this.

With CUSTOM_JWT, the JWT authorizer is wired in at creation via authorizerConfiguration:

python
import boto3

client = boto3.client('bedrock-agentcore-control')
gw = client.create_gateway(
    name="orders-gateway",
    roleArn="arn:aws:iam::123456789012:role/orders-gateway-service-role",
    protocolType="MCP",
    authorizerType="CUSTOM_JWT",
    authorizerConfiguration={"customJWTAuthorizer": {
        "discoveryUrl": "https://cognito-idp.us-west-2.amazonaws.com/<pool>/.well-known/openid-configuration",
        "allowedClients": ["clientId"]
    }})
print(gw['gatewayUrl'])
# https://{gateway-Id}.gateway.bedrock-agentcore.{Region}.amazonaws.com

The AgentCore CLI uses an --authorizer-type flag; NONE is the dev shortcut:

bash
agentcore add gateway --name TestGateway --authorizer-type NONE --runtimes MyGatewayAgent

Watch out

AUTHENTICATE_ONLY is a prerequisite, not a convenience

Token passthrough (forwarding the inbound user token unchanged to the backend) is only legal when inbound auth is AUTHENTICATE_ONLY. If you plan to passthrough, you must choose this mode at gateway creation — you cannot bolt passthrough onto a CUSTOM_JWT gateway later.

Outbound auth via AgentCore Identity

Outbound auth is brokered by AgentCore Identity and configured per target. The methods, roughly from least to most identity-aware:

  • No auth — the backend is open (some targets only).
  • Gateway service role / IAM SigV4 — the gateway calls the backend as itself using its IAM role. For MCP-server and OpenAPI targets you add an iamCredentialProvider specifying the service (e.g. bedrock-agentcore) and an optional region; Lambda, API Gateway, and Smithy targets use the basic GATEWAY_IAM_ROLE config. SigV4 outbound only works against AWS services that actually verify SigV4.
  • Caller IAM (FAS) — the gateway forwards the caller's IAM identity via Forward Access Sessions. Only AgentCore Runtime (HTTP) targets support this.
  • OAuth 2LO — client-credentials / machine-to-machine; the gateway authenticates as itself.
  • OAuth 3LO — authorization-code; acts on behalf of a user who consents via an auth URL.
  • OAuth OBO (token-exchange) — exchange the inbound user token for a scoped downstream token that carries both the user and the agent identity. Use this when the backend needs to know who the human is, but you don't want to forward the raw inbound token.
  • Token passthrough — forward the inbound token unchanged. Requires AUTHENTICATE_ONLY inbound.
  • API key — a static key injected at a configurable location (header or query) with an optional prefix.

A Lambda target with the gateway role looks like this (boto3 targetConfiguration shape):

python
client.create_gateway_target(
    gatewayIdentifier=gw['gatewayId'],
    name="orders",
    targetConfiguration={"mcp": {"lambda": {
        "lambdaArn": "arn:aws:lambda:us-west-2:123456789012:function:orders",
        "toolSchema": {"inlinePayload": [
            {"name": "get_order", "description": "Look up an order",
             "inputSchema": {"type": "object", "properties": {"id": {"type": "string"}}}}]}}}},
    credentialProviderConfigurations=[{"credentialProviderType": "GATEWAY_IAM_ROLE"}])

An OpenAPI or MCP-server target backed by API key references a credential provider instead — the AgentCore CLI exposes this as --outbound-auth:

bash
agentcore add gateway-target --name MyOpenAPITarget --type open-api-schema \
  --schema path/to/openapi.json --outbound-auth none|api-key|oauth --gateway MyGateway

Note

OBO vs 3LO vs passthrough — the one-line distinction

3LO mints a brand-new user-delegated token via a consent flow. OBO (token-exchange) trades the inbound token for a scoped downstream token carrying user+agent identity — no fresh consent if already granted. Passthrough does no exchange at all: it forwards the original token verbatim, which is why the backend must validate it and why inbound must be AUTHENTICATE_ONLY.

The target support matrix and the gateway role IAM

Not every target supports every outbound method. Memorize the shape of this matrix — picking an unsupported method is a silent misconfiguration that only surfaces at invocation:

TargetNo authGateway roleCaller IAMOAuth 2LOOAuth 3LOOAuth OBOPassthroughAPI key
API Gateway stageYesYesNoNoNoNoNoYes
LambdaNoYesNoNoNoNoNoNo
MCP serverYesYesNoYesYesYesNoYes
OpenAPIYesYesNoYesYesYesNoYes
SmithyNoYesNoYesNoNoNoNo
AgentCore Runtime (HTTP)NoYesYesYesNoNoYesNo

Reading the matrix:

  • Lambda is the simplest: gateway role only — no OAuth, no API key, no passthrough.
  • MCP-server and OpenAPI are the most flexible: they support the full OAuth spread (2LO/3LO/OBO) plus API key.
  • Caller IAM (FAS) and passthrough are exclusive to AgentCore Runtime HTTP targets.
  • Smithy supports the gateway role and 2LO only.

The gateway service role needs explicit IAM for OAuth and API-key egress. AgentCore Identity calls the token vault on the gateway's behalf, so the role must allow:

json
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": [
      "bedrock-agentcore:GetWorkloadAccessToken",
      "bedrock-agentcore:GetResourceOauth2Token",
      "secretsmanager:GetSecretValue"
    ],
    "Resource": "*"
  }]
}

For API-key targets, swap GetResourceOauth2Token for bedrock-agentcore:GetResourceApiKey. (Scope Resource down to your specific workload-identity and secret ARNs in production — * is shown only for clarity.)

Tip

GetWorkloadAccessToken is the common thread

Every OAuth/API-key egress path first obtains a Workload Access Token (GetWorkloadAccessToken), then uses it to fetch the downstream credential from the vault (GetResourceOauth2Token for OAuth, GetResourceApiKey for keys). If egress fails with an access-denied error, check these two actions on the gateway role first, plus secretsmanager:GetSecretValue for the stored client secret.

Quotas, volatility, and the gotcha checklist

Gateway has hard limits, and they move. Teach the dimension, verify the number — every figure below is [VOLATILE] and must be checked against the live AgentCore quotas page before you rely on it.

DimensionAs of writing — verify
Gateways per account1000
Targets per gateway100
Tools per target1000
Tool name length256 chars
Invocation timeout15 min
Inline schema / S3 schema1 MB / 10 MB
Concurrent connections (tool-call/list)1000 (gateway & account)
Semantic search rate25 TPM
Payload size6 MB
Control-plane TPSCreate/Update/Delete 5 TPS, Get/List 10 TPS

Remember the tool-naming rule when you size against the 256-char limit: gateway tools are namespaced by target with a triple underscore${target_name}___${tool_name} (e.g. LambdaUsingSDK___get_order_tool). The prefix counts toward the 256 characters, and your downstream Lambda receives the prefixed name — strip the ${target_name}___ prefix before dispatching.

Gotcha checklist:

  • Trying to enable semantic search after creation — recreate the gateway instead.
  • Token passthrough without AUTHENTICATE_ONLY inbound — it will be rejected.
  • Picking an unsupported outbound method for a target type (see the matrix) — e.g. API key on a Lambda target.
  • IAM/SigV4 outbound against a non-AWS backend — SigV4 outbound only works on AWS services that verify it.
  • Forgetting the gateway-role IAM (GetWorkloadAccessToken, GetResourceOauth2Token/GetResourceApiKey, secretsmanager:GetSecretValue) for OAuth/API-key egress.
  • Not stripping the ${target_name}___ prefix in your backend handler.

Watch out

Numbers here are snapshots, not contracts

AWS adjusts AgentCore quotas and may require a Service Quotas increase request. Never hardcode a design around "100 targets" or "25 TPM" — verify the current value at the live limits page (linked in Resources) and request increases where the workload needs them.

Try it: Wire up dual-auth and prove semantic search

Goal: stand up a gateway with real inbound auth and two differently-authenticated targets, then enable and exercise semantic tool search.

  1. Create a JWT-gated gateway. Using boto3 bedrock-agentcore-control, call create_gateway with protocolType="MCP", authorizerType="CUSTOM_JWT", and an authorizerConfiguration.customJWTAuthorizer pointing at a Cognito (or Okta/Auth0) discoveryUrl and your allowedClients. Also set protocolConfiguration={"mcp":{"searchType":"SEMANTIC"}} now — you cannot add it later. Print the returned gatewayUrl.
  2. Build the gateway role IAM. Attach a policy granting bedrock-agentcore:GetWorkloadAccessToken, bedrock-agentcore:GetResourceOauth2Token, and secretsmanager:GetSecretValue (scope the resources to your workload identity + secret ARNs). Note where you'd swap in GetResourceApiKey for an API-key target.
  3. Add two targets with different egress. (a) A Lambda target using credentialProviderConfigurations=[{"credentialProviderType":"GATEWAY_IAM_ROLE"}] and an inline toolSchema. (b) An OpenAPI target whose backend uses an API key (via the CLI: agentcore add gateway-target --type open-api-schema --outbound-auth api-key ...). Confirm from the matrix why API key works for OpenAPI but would fail for Lambda.
  4. Synchronize and search. Ensure your principal has bedrock-agentcore:SynchronizeGatewayTargets, sync the targets, then issue an MCP tools/call to x_amz_bedrock_agentcore_search with {"query":"look up an order"}. Verify the result lists your Lambda tool by its prefixed name (<target>___<tool>).
  5. Strip the prefix. In your Lambda handler, log the incoming tool name and add the line that strips the ${target_name}___ prefix before dispatching.
  6. Verify the volatile numbers. Open the live AgentCore limits page and write down the current values for targets/gateway, tools/target, tool-name length, and search TPM. Note any that differ from the ~snapshot in this lesson — that's the whole point of treating them as volatile.

Key takeaways

  1. 1A gateway has two independent auth boundaries: inbound (one `authorizerType` per gateway, gating the MCP endpoint) and outbound (per target, brokered by AgentCore Identity).
  2. 2Inbound options: `CUSTOM_JWT` (discoveryUrl + allowedClients), IAM SigV4 (caller needs `bedrock-agentcore:InvokeGateway`), `AUTHENTICATE_ONLY` (delegate authz to the target; required for passthrough), or none (dev only).
  3. 3Outbound options via Identity: no auth, gateway role/SigV4, caller IAM (FAS, Runtime targets only), OAuth 2LO/3LO/OBO (token-exchange), token passthrough (needs AUTHENTICATE_ONLY), and API key.
  4. 4The support matrix matters: Lambda is gateway-role-only; MCP-server and OpenAPI support the full OAuth spread + API key; caller IAM and passthrough are Runtime-HTTP-only.
  5. 5The gateway service role needs `bedrock-agentcore:GetWorkloadAccessToken`, `GetResourceOauth2Token` (or `GetResourceApiKey` for API keys), and `secretsmanager:GetSecretValue` for OAuth/API-key egress.
  6. 6Semantic search is set with `protocolConfiguration.mcp.searchType="SEMANTIC"` AT CREATION ONLY (recreate to add it), adds `x_amz_bedrock_agentcore_search`, works on MCP targets only, and needs `SynchronizeGatewayTargets`.
  7. 7Quotas (targets/gateway, tools/target, 256-char tool names, ~25 TPM search) are volatile — verify against the live AgentCore limits page, and strip the `${target_name}___` prefix in your backend.

Quiz

Lock in what you learned

Check your understanding

0 / 4 answered

1.You want a gateway to forward each end user's original bearer token, unchanged, to the backend so the backend makes the final authorization decision. What inbound auth must the gateway use?

2.Your gateway has an OpenAPI target whose backend is an external SaaS API protected by a static API key. Which outbound configuration works, and what IAM does the gateway role need?

3.A gateway has been live for a month aggregating ~400 tools, and the model keeps picking wrong tools. You decide to turn on semantic tool search. What's the correct move?

4.Which statement about the gateway tool-naming and quota rules is correct?

Go deeper

Hand-picked sources to keep learning