Skip to content

Commit b433faf

Browse files
authored
feat: add --request-header-allowlist CLI flag for agentcore add agent (#825) (#830)
Wire the existing requestHeaderAllowlist feature (already supported in the TUI wizard and schema) into the non-interactive CLI path. Accepts comma-separated header names that are auto-normalized with the X-Amzn-Bedrock-AgentCore-Runtime-Custom- prefix. - Add requestHeaderAllowlist field to CLI AddAgentOptions interface - Register --request-header-allowlist option in AgentPrimitive.registerCommands() - Add validation using existing validateHeaderAllowlist() utility - Pass parsed headers through to both create and BYO agent paths
1 parent 96de3d2 commit b433faf

File tree

3 files changed

+30
-1
lines changed

3 files changed

+30
-1
lines changed

src/cli/commands/add/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export interface AddAgentOptions extends VpcOptions {
3333
customClaims?: string;
3434
clientId?: string;
3535
clientSecret?: string;
36+
requestHeaderAllowlist?: string;
3637
idleTimeout?: number | string;
3738
maxLifetime?: number | string;
3839
json?: boolean;

src/cli/commands/add/validate.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
matchEnumValue,
1717
} from '../../../schema';
1818
import { ARN_VALIDATION_MESSAGE, isValidArn } from '../shared/arn-utils';
19+
import { validateHeaderAllowlist } from '../shared/header-utils';
1920
import { parseAndValidateLifecycleOptions } from '../shared/lifecycle-utils';
2021
import { validateVpcOptions } from '../shared/vpc-utils';
2122
import { validateJwtAuthorizerOptions } from './auth-options';
@@ -252,6 +253,14 @@ export function validateAddAgentOptions(options: AddAgentOptions): ValidationRes
252253
}
253254
}
254255

256+
// Validate request header allowlist
257+
if (options.requestHeaderAllowlist) {
258+
const headerResult = validateHeaderAllowlist(options.requestHeaderAllowlist);
259+
if (!headerResult.success) {
260+
return { valid: false, error: headerResult.error };
261+
}
262+
}
263+
255264
// Parse and validate lifecycle configuration
256265
const lifecycleResult = parseAndValidateLifecycleOptions(options);
257266
if (!lifecycleResult.valid) return lifecycleResult;

src/cli/primitives/AgentPrimitive.tsx

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import type {
1515
import { AgentEnvSpecSchema, CREDENTIAL_PROVIDERS, LIFECYCLE_TIMEOUT_MAX, LIFECYCLE_TIMEOUT_MIN } from '../../schema';
1616
import type { AddAgentOptions as CLIAddAgentOptions } from '../commands/add/types';
1717
import { validateAddAgentOptions } from '../commands/add/validate';
18+
import { parseAndNormalizeHeaders } from '../commands/shared/header-utils';
1819
import type { VpcOptions } from '../commands/shared/vpc-utils';
1920
import { VPC_ENDPOINT_WARNING, parseCommaSeparatedList } from '../commands/shared/vpc-utils';
2021
import { getErrorMessage } from '../errors';
@@ -51,6 +52,7 @@ export interface AddAgentOptions extends VpcOptions {
5152
apiKey?: string;
5253
memory?: MemoryOption;
5354
protocol?: ProtocolMode;
55+
requestHeaderAllowlist?: string[];
5456
codeLocation?: string;
5557
entrypoint?: string;
5658
bedrockAgentId?: string;
@@ -106,7 +108,10 @@ export class AgentPrimitive extends BasePrimitive<AddAgentOptions, RemovableReso
106108
const project = await configIO.readProjectSpec();
107109
const existingAgent = project.runtimes.find(agent => agent.name === options.name);
108110
if (existingAgent) {
109-
return { success: false, error: `Agent "${options.name}" already exists in this project.` };
111+
return {
112+
success: false,
113+
error: `Agent "${options.name}" already exists. To update its configuration, edit agentcore/agentcore.json directly.`,
114+
};
110115
}
111116

112117
if (options.type === 'import') {
@@ -224,6 +229,10 @@ export class AgentPrimitive extends BasePrimitive<AddAgentOptions, RemovableReso
224229
.option('--custom-claims <json>', 'Custom claim validations as JSON array (for CUSTOM_JWT) [non-interactive]')
225230
.option('--client-id <id>', 'OAuth client ID for agent bearer token [non-interactive]')
226231
.option('--client-secret <secret>', 'OAuth client secret [non-interactive]')
232+
.option(
233+
'--request-header-allowlist <headers>',
234+
'Comma-separated list of custom header names to allow (auto-prefixed with X-Amzn-Bedrock-AgentCore-Runtime-Custom-) [non-interactive]'
235+
)
227236
.option(
228237
'--idle-timeout <seconds>',
229238
`Idle session timeout in seconds (${LIFECYCLE_TIMEOUT_MIN}-${LIFECYCLE_TIMEOUT_MAX}) [non-interactive]`
@@ -258,6 +267,11 @@ export class AgentPrimitive extends BasePrimitive<AddAgentOptions, RemovableReso
258267
? (JSON.parse(cliOptions.customClaims) as CustomClaimValidation[])
259268
: undefined;
260269

270+
// Parse request header allowlist if provided
271+
const requestHeaderAllowlist = cliOptions.requestHeaderAllowlist
272+
? parseAndNormalizeHeaders(cliOptions.requestHeaderAllowlist)
273+
: undefined;
274+
261275
const result = await this.add({
262276
name: cliOptions.name!,
263277
type: cliOptions.type ?? 'create',
@@ -271,6 +285,7 @@ export class AgentPrimitive extends BasePrimitive<AddAgentOptions, RemovableReso
271285
networkMode: cliOptions.networkMode,
272286
subnets: cliOptions.subnets,
273287
securityGroups: cliOptions.securityGroups,
288+
requestHeaderAllowlist,
274289
codeLocation: cliOptions.codeLocation,
275290
entrypoint: cliOptions.entrypoint,
276291
bedrockAgentId: cliOptions.agentId,
@@ -378,6 +393,7 @@ export class AgentPrimitive extends BasePrimitive<AddAgentOptions, RemovableReso
378393
customClaims: options.customClaims,
379394
},
380395
}),
396+
requestHeaderAllowlist: options.requestHeaderAllowlist,
381397
idleRuntimeSessionTimeout: options.idleTimeout,
382398
maxLifetime: options.maxLifetime,
383399
};
@@ -520,6 +536,9 @@ export class AgentPrimitive extends BasePrimitive<AddAgentOptions, RemovableReso
520536
}),
521537
// MCP uses mcp.run() which is incompatible with the opentelemetry-instrument wrapper
522538
...(protocol === 'MCP' && { instrumentation: { enableOtel: false } }),
539+
...(options.requestHeaderAllowlist?.length && {
540+
requestHeaderAllowlist: options.requestHeaderAllowlist,
541+
}),
523542
...(authorizerType && { authorizerType }),
524543
...(authorizerConfiguration && { authorizerConfiguration }),
525544
...(lifecycleConfiguration && { lifecycleConfiguration }),

0 commit comments

Comments
 (0)