Gateway Auth & Semantic Tool Search
Ingress, egress, and finding the right tool among thousands
- 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
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.
- 1Two doors: ingress and egress
- 2Inbound auth: gating the gateway's front door
- 3Outbound auth via AgentCore Identity
- 4The target support matrix and the gateway role IAM
- 5Semantic tool search: finding the right tool among thousands
- 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.
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:
authorizerType | What it does | When 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_ONLY | The 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 authorization | No inbound check at all. | Local dev / test only. Never ship this. |
With CUSTOM_JWT, the JWT authorizer is wired in at creation via authorizerConfiguration:
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.comThe AgentCore CLI uses an --authorizer-type flag; NONE is the dev shortcut:
agentcore add gateway --name TestGateway --authorizer-type NONE --runtimes MyGatewayAgentWatch 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
iamCredentialProviderspecifying theservice(e.g.bedrock-agentcore) and an optionalregion; Lambda, API Gateway, and Smithy targets use the basicGATEWAY_IAM_ROLEconfig. 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_ONLYinbound. - 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):
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:
agentcore add gateway-target --name MyOpenAPITarget --type open-api-schema \
--schema path/to/openapi.json --outbound-auth none|api-key|oauth --gateway MyGatewayNote
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:
| Target | No auth | Gateway role | Caller IAM | OAuth 2LO | OAuth 3LO | OAuth OBO | Passthrough | API key |
|---|---|---|---|---|---|---|---|---|
| API Gateway stage | Yes | Yes | No | No | No | No | No | Yes |
| Lambda | No | Yes | No | No | No | No | No | No |
| MCP server | Yes | Yes | No | Yes | Yes | Yes | No | Yes |
| OpenAPI | Yes | Yes | No | Yes | Yes | Yes | No | Yes |
| Smithy | No | Yes | No | Yes | No | No | No | No |
| AgentCore Runtime (HTTP) | No | Yes | Yes | Yes | No | No | Yes | No |
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:
{
"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.
Semantic tool search: finding the right tool among thousands
A gateway can aggregate many targets, and each target can expose many tools. Push thousands of tool definitions into a model's context and two things break: the context window fills with schemas, and the model's tool-selection accuracy collapses. Semantic tool search fixes both by letting the agent search for relevant tools instead of seeing all of them.
You enable it by setting protocolConfiguration.mcp.searchType = "SEMANTIC" at gateway creation:
gw = client.create_gateway(
name="big-tool-gateway",
roleArn="arn:aws:iam::123456789012:role/gw-role",
protocolType="MCP",
authorizerType="CUSTOM_JWT",
authorizerConfiguration={"customJWTAuthorizer": {
"discoveryUrl": "https://.../.well-known/openid-configuration",
"allowedClients": ["clientId"]}},
protocolConfiguration={"mcp": {"searchType": "SEMANTIC"}}) # enables semantic searchThis adds a built-in tool named x_amz_bedrock_agentcore_search to the gateway's tool list. The agent calls it like any other MCP tool, passing a natural-language query, and gets back the matching tools (which it can then call):
{"jsonrpc": "2.0", "id": "search", "method": "tools/call",
"params": {"name": "x_amz_bedrock_agentcore_search",
"arguments": {"query": "find order information"}}}Three hard constraints from the docs:
- Creation-only. "You can't change its configuration to enable semantic search" after the fact — if a live gateway lacks it, you must recreate the gateway. There is no update path.
- MCP targets only. Search uses vector embeddings over MCP (aggregated) targets. HTTP (proxied) targets are not searchable.
- Sync is required. The creator needs the
bedrock-agentcore:SynchronizeGatewayTargetspermission so the search index reflects the current tool inventory; new/changed targets must be synchronized to appear in results.
The embedding model behind the search is not publicly documented, and the search rate limit is volatile (≈25 TPM as of writing) — treat both as implementation details to verify, not constants to design hard limits around.
Watch out
You cannot enable semantic search later
This is the single most common Gateway gotcha. searchType="SEMANTIC" is immutable after creation. If you ship a gateway without it and later need search, you recreate the gateway (and update every client's gatewayUrl). Decide up front whether the gateway will grow past a handful of tools.
Example
Agent loop with search
A well-behaved agent first calls x_amz_bedrock_agentcore_search with the user's intent ("refund a customer's last order"), receives a short list like OrdersTarget___get_order and PaymentsTarget___issue_refund, then calls those tools directly. Only a few schemas ever enter context, regardless of how many hundred tools the gateway aggregates.
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.
| Dimension | As of writing — verify |
|---|---|
| Gateways per account | 1000 |
| Targets per gateway | 100 |
| Tools per target | 1000 |
| Tool name length | 256 chars |
| Invocation timeout | 15 min |
| Inline schema / S3 schema | 1 MB / 10 MB |
| Concurrent connections (tool-call/list) | 1000 (gateway & account) |
| Semantic search rate | 25 TPM |
| Payload size | 6 MB |
| Control-plane TPS | Create/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_ONLYinbound — 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.
- Create a JWT-gated gateway. Using boto3
bedrock-agentcore-control, callcreate_gatewaywithprotocolType="MCP",authorizerType="CUSTOM_JWT", and anauthorizerConfiguration.customJWTAuthorizerpointing at a Cognito (or Okta/Auth0)discoveryUrland yourallowedClients. Also setprotocolConfiguration={"mcp":{"searchType":"SEMANTIC"}}now — you cannot add it later. Print the returnedgatewayUrl. - Build the gateway role IAM. Attach a policy granting
bedrock-agentcore:GetWorkloadAccessToken,bedrock-agentcore:GetResourceOauth2Token, andsecretsmanager:GetSecretValue(scope the resources to your workload identity + secret ARNs). Note where you'd swap inGetResourceApiKeyfor an API-key target. - Add two targets with different egress. (a) A Lambda target using
credentialProviderConfigurations=[{"credentialProviderType":"GATEWAY_IAM_ROLE"}]and an inlinetoolSchema. (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. - Synchronize and search. Ensure your principal has
bedrock-agentcore:SynchronizeGatewayTargets, sync the targets, then issue an MCPtools/calltox_amz_bedrock_agentcore_searchwith{"query":"look up an order"}. Verify the result lists your Lambda tool by its prefixed name (<target>___<tool>). - Strip the prefix. In your Lambda handler, log the incoming tool name and add the line that strips the
${target_name}___prefix before dispatching. - 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
- 1A gateway has two independent auth boundaries: inbound (one `authorizerType` per gateway, gating the MCP endpoint) and outbound (per target, brokered by AgentCore Identity).
- 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).
- 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.
- 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.
- 5The gateway service role needs `bedrock-agentcore:GetWorkloadAccessToken`, `GetResourceOauth2Token` (or `GetResourceApiKey` for API keys), and `secretsmanager:GetSecretValue` for OAuth/API-key egress.
- 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`.
- 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
Top-level Gateway concepts: targets, aggregation vs proxy, the MCP endpoint, ingress/egress overview.
The per-target outbound methods (gateway role, OAuth 2LO/3LO/OBO, passthrough, API key) and which target types support each.
searchType=SEMANTIC at creation, the x_amz_bedrock_agentcore_search tool, MCP-targets-only and SynchronizeGatewayTargets requirements.
The ${target_name}___${tool_name} triple-underscore rule and stripping the prefix in your backend.
The live source for all volatile Gateway numbers: targets/gateway, tools/target, 256-char names, search TPM, TPS.
Narrative deep-dive on the M×N problem, the six capabilities, and the dual ingress/egress auth story.