Skip to content

Commit 04ba4b2

Browse files
committed
feat: add PolicyEngineConfiguration support for gateways
1 parent 24995ab commit 04ba4b2

File tree

6 files changed

+92
-1
lines changed

6 files changed

+92
-1
lines changed

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: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { findConfigRoot, setEnvVar } from '../../lib';
22
import type { AgentCoreGateway, AgentCoreGatewayTarget, AgentCoreMcpSpec, GatewayAuthorizerType } from '../../schema';
3-
import { AgentCoreGatewaySchema } from '../../schema';
3+
import { AgentCoreGatewaySchema, PolicyEngineModeSchema } from '../../schema';
44
import type { AddGatewayOptions as CLIAddGatewayOptions } from '../commands/add/types';
55
import { validateAddGatewayOptions } from '../commands/add/validate';
66
import { getErrorMessage } from '../errors';
@@ -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: PolicyEngineModeSchema.parse(options.policyEngineMode) }
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: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,24 @@ 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+
console.error(
69+
'Warning: removing a policy engine may grant agents escalated permissions to invoke gateway tools that were previously restricted'
70+
);
71+
}
72+
}
73+
5674
return { success: true };
5775
} catch (err) {
5876
const message = err instanceof Error ? err.message : 'Unknown error';
@@ -84,6 +102,35 @@ export class PolicyEnginePrimitive extends BasePrimitive<AddPolicyEngineOptions,
84102
after: afterSpec,
85103
});
86104

105+
// Show mcp.json changes if any gateways reference this engine
106+
if (this.configIO.configExists('mcp')) {
107+
const mcpSpec = await this.configIO.readMcpSpec();
108+
const affectedGateways = mcpSpec.agentCoreGateways.filter(
109+
gw => gw.policyEngineConfiguration?.policyEngineName === engineName
110+
);
111+
if (affectedGateways.length > 0) {
112+
summary.push(
113+
`Note: ${affectedGateways.length} gateway(s) referencing this engine will have policyEngineConfiguration removed`
114+
);
115+
summary.push(
116+
'Warning: removing a policy engine may grant agents escalated permissions to invoke gateway tools that were previously restricted'
117+
);
118+
const afterMcpSpec = {
119+
...mcpSpec,
120+
agentCoreGateways: mcpSpec.agentCoreGateways.map(gw =>
121+
gw.policyEngineConfiguration?.policyEngineName === engineName
122+
? { ...gw, policyEngineConfiguration: undefined }
123+
: gw
124+
),
125+
};
126+
schemaChanges.push({
127+
file: 'agentcore/mcp.json',
128+
before: mcpSpec,
129+
after: afterMcpSpec,
130+
});
131+
}
132+
}
133+
87134
return { summary, directoriesToDelete: [], schemaChanges };
88135
}
89136

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)