Skip to content

Commit 1d81a94

Browse files
committed
feat: add Lambda interceptors for AgentCore gateways
Adds end-to-end support for Lambda interceptors at gateway REQUEST and RESPONSE points: a new InterceptorPrimitive, three vended templates (pass-through, jwt-scope-authorizer, tools-list-filter) for both Python and Node.js, deploy-side preflight, and `logs interceptor` / `invoke interceptor` verbs. Primitive surface ----------------- - `agentcore add interceptor --name --gateway --interception-points --template --runtime [--timeout] [--no-pass-request-headers] [--additional-policies]` for managed mode - External mode via direct `agentcore.json` edit referencing a customer-provided unqualified Lambda ARN - `agentcore remove interceptor --name` reconciles deploy state on next `agentcore deploy` - `agentcore logs interceptor --name [--follow|--since|--until]` tails CloudWatch for managed interceptors; external interceptors short- circuit to a remediation message pointing at `aws logs tail` - `agentcore invoke interceptor --name [--payload]` invokes the underlying Lambda for managed interceptors with the same external- mode remediation message Schema ------ - `interceptors` array on `AgentCoreProjectSpec` with cross-field superRefine for cardinality (max 1 per point per gateway, max 2 per gateway) and gateway-name reference checks - Lambda ARN regex tightened to reject version/alias qualifiers - `passRequestHeaders`, `entrypoint`, `timeoutSeconds`, `runtime` are optional with consumption-time defaults so user-edited `agentcore.json` stays sparse across CLI round-trips Deploy ------ - `validateInterceptors()` checks managed-mode codeLocation existence and emits a masked cross-account WARN for external mode (with full `aws lambda add-permission` remediation snippet); also wired into `agentcore validate` so the pre-check works without invoking deploy - `validateGatewayTargetLambdas()` does a best-effort `lambda:GetFunction` per `lambda-function-arn` target and WARNs on any failure (NotFound, AccessDenied, throttle) so a typo'd ARN surfaces before CFN ROLLBACK - Account IDs go through `maskAccountId()` for all user-visible warning output (PII masking) Templates --------- - `pass-through`, `jwt-scope-authorizer`, `tools-list-filter` for Python (`pyproject.toml` + hatchling) and Node.js (`index.mjs` + `package.json`) - `PackageName` Handlebars var for NPM-safe lowercase package naming Telemetry --------- - `has_cross_account_warning` boolean on the InterceptorPrimitive command-run telemetry schema
1 parent ae1b932 commit 1d81a94

84 files changed

Lines changed: 4481 additions & 258 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,13 +91,24 @@ agentcore invoke
9191

9292
### Resource Management
9393

94-
| Command | Description |
95-
| -------- | ---------------------------------------------------- |
96-
| `add` | Add agents, memory, credentials, evaluators, targets |
97-
| `remove` | Remove resources from project |
94+
| Command | Description |
95+
| -------- | ------------------------------------------------------------------ |
96+
| `add` | Add agents, memory, credentials, evaluators, targets, interceptors |
97+
| `remove` | Remove resources from project |
9898

9999
> **Note**: Run `agentcore deploy` after `add` or `remove` to update resources in AWS.
100100
101+
#### Interceptors
102+
103+
| Command | Description |
104+
| ------------------------------- | ------------------------------------------------------------------- |
105+
| `add interceptor` | Add a Lambda interceptor (managed scaffold or BYO ARN) to a gateway |
106+
| `remove interceptor` | Remove an interceptor |
107+
| `logs interceptor --name <n>` | Tail or search managed interceptor CloudWatch logs |
108+
| `invoke interceptor --name <n>` | Invoke a managed interceptor with a synthetic payload |
109+
110+
See [docs/interceptors.md](docs/interceptors.md) for templates, schema, and the cross-account behavior.
111+
101112
### Observability
102113

103114
| Command | Description |

docs/interceptors.md

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
# Lambda Interceptors
2+
3+
AgentCore Gateway Interceptors are customer-owned Lambda functions that the gateway invokes on every MCP request to
4+
inspect, transform, or short-circuit traffic. They run at one of two interception points:
5+
6+
- **REQUEST** — before the gateway invokes the target.
7+
- **RESPONSE** — after the target returns, before the gateway replies to the caller.
8+
9+
A gateway can carry up to **2 interceptors** (one REQUEST + one RESPONSE), or a single interceptor wired to both points.
10+
11+
## Modes
12+
13+
The CLI supports two first-class modes, mirroring the existing code-based evaluator pattern:
14+
15+
| Mode | What the CLI owns | When to use |
16+
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------- |
17+
| **Managed** (default) | Scaffolds a templated Lambda project under `app/<name>/`, packages it, deploys it, renders the resulting ARN into the gateway's `InterceptorConfigurations`. | You want the CLI to own the source tree and deploy artifact end-to-end. |
18+
| **External** | You pass an already-deployed Lambda ARN with `--lambda-arn`. The CLI plugs the ARN into the gateway and grants `lambda:InvokeFunction` to the gateway role. | You have a centralized auth Lambda or a third-party-owned function. |
19+
20+
## Quick start — managed
21+
22+
```bash
23+
# Single REQUEST-point interceptor with the JWT scope authorizer template
24+
agentcore add interceptor \
25+
--name auth-check \
26+
--gateway my-gateway \
27+
--interception-points REQUEST \
28+
--template jwt-scope-authorizer \
29+
--runtime python3.12
30+
31+
# Edit app/auth-check/handler.py with your scope rules, then:
32+
agentcore deploy
33+
```
34+
35+
## Quick start — external (BYO ARN)
36+
37+
```bash
38+
agentcore add interceptor \
39+
--name central-auth \
40+
--gateway my-gateway \
41+
--interception-points REQUEST \
42+
--lambda-arn arn:aws:lambda:us-east-1:111111111111:function:central-auth-prod
43+
```
44+
45+
The CLI does not scaffold any code; the only artifact is the JSON entry in `agentcore.json`.
46+
47+
## Dual-point on a single Lambda
48+
49+
A single interceptor can serve both REQUEST and RESPONSE on the same gateway:
50+
51+
```bash
52+
agentcore add interceptor \
53+
--name dual-point \
54+
--gateway my-gateway \
55+
--interception-points REQUEST,RESPONSE \
56+
--template pass-through \
57+
--runtime python3.12
58+
```
59+
60+
This counts as one interceptor against the cardinality cap.
61+
62+
## Templates (managed mode)
63+
64+
| Template | Point(s) | Purpose |
65+
| ---------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------ |
66+
| `pass-through` | REQUEST or RESPONSE | Minimal compliant handler. Demonstrates the input/output envelope and the streaming guard. |
67+
| `jwt-scope-authorizer` | REQUEST | Decodes the inbound `Authorization` JWT and short-circuits with a structured 403 if the required scope is missing. |
68+
| `tools-list-filter` | RESPONSE | Strips unauthorized tools from `tools/list` responses based on a customer-supplied `is_authorized()` predicate. |
69+
70+
Each template ships in both Python 3.12 and Node.js 22.x. Pick with `--runtime python3.12` (default) or
71+
`--runtime nodejs22.x`.
72+
73+
## Operational verbs
74+
75+
```bash
76+
# Tail logs for a managed interceptor
77+
agentcore logs interceptor --name auth-check --follow
78+
79+
# Search logs by time window
80+
agentcore logs interceptor --name auth-check --since 1h --until now
81+
82+
# Invoke synthetically with a payload file
83+
agentcore invoke interceptor --name auth-check --payload-file ./test-event.json
84+
```
85+
86+
For external interceptors, both verbs print a copy-pasteable `aws` CLI remediation and exit non-zero — the CLI doesn't
87+
own those Lambdas.
88+
89+
## Cross-account external interceptors
90+
91+
When `--lambda-arn`'s account ID does not match the deploy target's account, the CLI emits a **warning** at preflight
92+
(with masked account IDs) and **continues** the deploy. The deploy itself succeeds — the gateway role's identity policy
93+
grants `lambda:InvokeFunction` on the foreign ARN. What doesn't work yet is the first invocation: AWS Lambda requires a
94+
matching resource-based policy on the function granting the gateway role permission to invoke it.
95+
96+
Example warning (account IDs masked to last 4 digits):
97+
98+
```
99+
WARNING: Cross-account interceptor detected for "central-auth".
100+
Gateway account(s): ****1947
101+
Lambda: arn:aws:lambda:us-east-1:****1111:function:central-auth-prod
102+
103+
Deploy will succeed, but the first interceptor invocation will fail until
104+
you add a resource-based policy to the Lambda. Run this in the Lambda's
105+
account (once per interceptor) before sending traffic through the gateway:
106+
107+
aws lambda add-permission \
108+
--function-name <your-interceptor-function-name> \
109+
--statement-id GatewayServiceRoleInvoke \
110+
--action lambda:InvokeFunction \
111+
--principal <gateway-role-arn-from-deployed-state>
112+
113+
Continuing with deploy...
114+
```
115+
116+
Run the snippet once in the Lambda's account, before sending traffic through the gateway.
117+
118+
## Schema
119+
120+
```jsonc
121+
{
122+
"interceptors": [
123+
{
124+
"name": "auth-check",
125+
"gatewayName": "my-gateway",
126+
"interceptionPoints": ["REQUEST"],
127+
"passRequestHeaders": true,
128+
"config": {
129+
"managed": {
130+
"codeLocation": "app/auth-check/",
131+
"entrypoint": "handler.lambda_handler",
132+
"timeoutSeconds": 30,
133+
"runtime": "python3.12",
134+
"additionalPolicies": ["execution-role-policy.json"],
135+
},
136+
},
137+
},
138+
{
139+
"name": "central-auth",
140+
"gatewayName": "my-gateway",
141+
"interceptionPoints": ["RESPONSE"],
142+
"passRequestHeaders": true,
143+
"config": {
144+
"external": {
145+
"lambdaArn": "arn:aws:lambda:us-east-1:111111111111:function:central-auth-prod",
146+
},
147+
},
148+
},
149+
],
150+
}
151+
```
152+
153+
`config.managed` and `config.external` are mutually exclusive (exactly one must be set).
154+
155+
## Removal
156+
157+
```bash
158+
agentcore remove interceptor --name auth-check
159+
agentcore deploy
160+
```
161+
162+
Managed-mode removal also deletes the scaffolded `app/<name>/` directory. External-mode removal touches only the JSON
163+
entry. The next `deploy` reconciles the gateway via CloudFormation — no imperative `UpdateGateway` calls.
164+
165+
## Limitations / out of scope (P0)
166+
167+
- PII-redaction template (requires customer-specific patterns).
168+
- Audit-logging template (OpenSearch / S3 wiring).
169+
- Provisioned concurrency.
170+
- Multi-gateway shared interceptor pools.
171+
- Console UX parity.
172+
- Streaming-aware first-invocation guard as active code (shipped commented-in).

0 commit comments

Comments
 (0)