Skip to content

Commit 142762a

Browse files
committed
feat: add PolicyEngineConfiguration support for gateways
1 parent a0f8521 commit 142762a

File tree

9 files changed

+4737
-5
lines changed

9 files changed

+4737
-5
lines changed

package-lock.json

Lines changed: 866 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/assets/cdk/package-lock.json

Lines changed: 3784 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/assets/cdk/package.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,13 @@
1616
"devDependencies": {
1717
"@types/jest": "^29.5.14",
1818
"@types/node": "^24.10.1",
19-
"jest": "^29.7.0",
20-
"ts-jest": "^29.2.5",
2119
"aws-cdk": "2.1100.1",
20+
"jest": "^29.7.0",
2221
"prettier": "^3.4.2",
22+
"ts-jest": "^29.2.5",
2323
"typescript": "~5.9.3"
2424
},
2525
"dependencies": {
26-
"@aws/agentcore-cdk": "^0.1.0-alpha.1",
2726
"aws-cdk-lib": "2.243.0",
2827
"constructs": "^10.0.0"
2928
}

src/cli/commands/add/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ export interface AddGatewayOptions {
3939
agents?: string;
4040
semanticSearch?: boolean;
4141
exceptionLevel?: string;
42+
policyEngine?: string;
43+
policyEngineMode?: string;
4244
json?: boolean;
4345
}
4446

src/cli/commands/add/validate.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,17 @@ export function validateAddGatewayOptions(options: AddGatewayOptions): Validatio
273273
}
274274
}
275275

276+
// Validate policy engine options
277+
if (options.policyEngine && !options.policyEngineMode) {
278+
return { valid: false, error: '--policy-engine-mode is required when --policy-engine is specified' };
279+
}
280+
if (options.policyEngineMode && !options.policyEngine) {
281+
return { valid: false, error: '--policy-engine is required when --policy-engine-mode is specified' };
282+
}
283+
if (options.policyEngineMode && !['LOG_ONLY', 'ENFORCE'].includes(options.policyEngineMode)) {
284+
return { valid: false, error: `Invalid policy engine mode: ${options.policyEngineMode}. Use LOG_ONLY or ENFORCE` };
285+
}
286+
276287
return { valid: true };
277288
}
278289

src/cli/primitives/GatewayPrimitive.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ export interface AddGatewayOptions {
2828
agents?: string;
2929
enableSemanticSearch?: boolean;
3030
exceptionLevel?: string;
31+
policyEngine?: string;
32+
policyEngineMode?: string;
3133
}
3234

3335
/**
@@ -160,6 +162,8 @@ export class GatewayPrimitive extends BasePrimitive<AddGatewayOptions, Removable
160162
.option('--agents <agents>', 'Comma-separated agent names')
161163
.option('--no-semantic-search', 'Disable semantic search for tool discovery')
162164
.option('--exception-level <level>', 'Exception verbosity level', 'NONE')
165+
.option('--policy-engine <name>', 'Policy engine name for Cedar-based authorization')
166+
.option('--policy-engine-mode <mode>', 'Policy engine mode: LOG_ONLY or ENFORCE')
163167
.option('--json', 'Output as JSON')
164168
.action(async (rawOptions: Record<string, string | boolean | undefined>) => {
165169
const cliOptions = rawOptions as unknown as CLIAddGatewayOptions;
@@ -192,6 +196,8 @@ export class GatewayPrimitive extends BasePrimitive<AddGatewayOptions, Removable
192196
agents: cliOptions.agents,
193197
enableSemanticSearch: cliOptions.semanticSearch !== false,
194198
exceptionLevel: cliOptions.exceptionLevel,
199+
policyEngine: cliOptions.policyEngine,
200+
policyEngineMode: cliOptions.policyEngineMode,
195201
});
196202

197203
if (cliOptions.json) {
@@ -290,6 +296,10 @@ export class GatewayPrimitive extends BasePrimitive<AddGatewayOptions, Removable
290296
jwtConfig: undefined,
291297
enableSemanticSearch: options.enableSemanticSearch ?? true,
292298
exceptionLevel: options.exceptionLevel === 'DEBUG' ? 'DEBUG' : 'NONE',
299+
policyEngineConfiguration:
300+
options.policyEngine && options.policyEngineMode
301+
? { policyEngineName: options.policyEngine, mode: options.policyEngineMode as 'LOG_ONLY' | 'ENFORCE' }
302+
: undefined,
293303
};
294304

295305
if (options.authorizerType === 'CUSTOM_JWT' && options.discoveryUrl) {
@@ -358,6 +368,7 @@ export class GatewayPrimitive extends BasePrimitive<AddGatewayOptions, Removable
358368
authorizerConfiguration: this.buildAuthorizerConfiguration(config),
359369
enableSemanticSearch: config.enableSemanticSearch,
360370
exceptionLevel: config.exceptionLevel,
371+
policyEngineConfiguration: config.policyEngineConfiguration,
361372
};
362373

363374
mcpSpec.agentCoreGateways.push(gateway);

src/cli/primitives/PolicyEnginePrimitive.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,21 @@ export class PolicyEnginePrimitive extends BasePrimitive<AddPolicyEngineOptions,
5353
project.policyEngines.splice(index, 1);
5454
await this.writeProjectSpec(project);
5555

56+
// Clean up any gateway references to this engine in mcp.json
57+
if (this.configIO.configExists('mcp')) {
58+
const mcpSpec = await this.configIO.readMcpSpec();
59+
let changed = false;
60+
for (const gw of mcpSpec.agentCoreGateways) {
61+
if (gw.policyEngineConfiguration?.policyEngineName === engineName) {
62+
delete gw.policyEngineConfiguration;
63+
changed = true;
64+
}
65+
}
66+
if (changed) {
67+
await this.configIO.writeMcpSpec(mcpSpec);
68+
}
69+
}
70+
5671
return { success: true };
5772
} catch (err) {
5873
const message = err instanceof Error ? err.message : 'Unknown error';
@@ -84,6 +99,32 @@ export class PolicyEnginePrimitive extends BasePrimitive<AddPolicyEngineOptions,
8499
after: afterSpec,
85100
});
86101

102+
// Show mcp.json changes if any gateways reference this engine
103+
if (this.configIO.configExists('mcp')) {
104+
const mcpSpec = await this.configIO.readMcpSpec();
105+
const affectedGateways = mcpSpec.agentCoreGateways.filter(
106+
gw => gw.policyEngineConfiguration?.policyEngineName === engineName
107+
);
108+
if (affectedGateways.length > 0) {
109+
summary.push(
110+
`Note: ${affectedGateways.length} gateway(s) referencing this engine will have policyEngineConfiguration removed`
111+
);
112+
const afterMcpSpec = {
113+
...mcpSpec,
114+
agentCoreGateways: mcpSpec.agentCoreGateways.map(gw =>
115+
gw.policyEngineConfiguration?.policyEngineName === engineName
116+
? { ...gw, policyEngineConfiguration: undefined }
117+
: gw
118+
),
119+
};
120+
schemaChanges.push({
121+
file: 'agentcore/mcp.json',
122+
before: mcpSpec,
123+
after: afterMcpSpec,
124+
});
125+
}
126+
}
127+
87128
return { summary, directoriesToDelete: [], schemaChanges };
88129
}
89130

src/cli/tui/screens/mcp/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type {
22
ApiGatewayHttpMethod,
33
GatewayAuthorizerType,
44
GatewayExceptionLevel,
5+
GatewayPolicyEngineConfiguration,
56
GatewayTargetType,
67
NodeRuntime,
78
PythonRuntime,
@@ -36,6 +37,8 @@ export interface AddGatewayConfig {
3637
enableSemanticSearch: boolean;
3738
/** Exception verbosity level for the gateway */
3839
exceptionLevel: GatewayExceptionLevel;
40+
/** Policy engine configuration for Cedar-based authorization */
41+
policyEngineConfiguration?: GatewayPolicyEngineConfiguration;
3942
}
4043

4144
/** Item ID for the semantic search toggle in the advanced config pane. */

src/schema/schemas/mcp.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,21 @@ export type AgentCoreGatewayTarget = z.infer<typeof AgentCoreGatewayTargetSchema
593593
export const GatewayExceptionLevelSchema = z.enum(['NONE', 'DEBUG']);
594594
export type GatewayExceptionLevel = z.infer<typeof GatewayExceptionLevelSchema>;
595595

596+
// ============================================================================
597+
// Gateway Policy Engine Configuration
598+
// ============================================================================
599+
600+
export const PolicyEngineModeSchema = z.enum(['LOG_ONLY', 'ENFORCE']);
601+
export type PolicyEngineMode = z.infer<typeof PolicyEngineModeSchema>;
602+
603+
export const GatewayPolicyEngineConfigurationSchema = z
604+
.object({
605+
policyEngineName: z.string().min(1),
606+
mode: PolicyEngineModeSchema,
607+
})
608+
.strict();
609+
export type GatewayPolicyEngineConfiguration = z.infer<typeof GatewayPolicyEngineConfigurationSchema>;
610+
596611
// ============================================================================
597612
// Gateway
598613
// ============================================================================
@@ -614,6 +629,8 @@ export const AgentCoreGatewaySchema = z
614629
enableSemanticSearch: z.boolean().default(true),
615630
/** Exception verbosity level. 'NONE' = generic errors (default), 'DEBUG' = verbose errors. */
616631
exceptionLevel: GatewayExceptionLevelSchema.default('NONE'),
632+
/** Policy engine configuration for Cedar-based authorization of tool calls. */
633+
policyEngineConfiguration: GatewayPolicyEngineConfigurationSchema.optional(),
617634
})
618635
.strict()
619636
.refine(

0 commit comments

Comments
 (0)