Skip to content

Commit a61ebdd

Browse files
authored
feat: add VPC network mode support (#545)
* feat: add VPC network mode support Replace the PRIVATE network mode placeholder with VPC across the full CLI stack: schema validation, TUI wizards, CLI flags, template rendering, and CDK config persistence. Schema: - NetworkModeSchema enum: PUBLIC | PRIVATE → PUBLIC | VPC - Add NetworkConfigSchema with subnet/security group ID validation - Cross-field superRefine: VPC requires networkConfig, non-VPC forbids it CLI flags: - Add --network-mode, --subnets, --security-groups to create and add agent - Shared vpc-utils.ts with parseCommaSeparatedList, validateVpcOptions, validateSubnetIds, validateSecurityGroupIds, and VPC_ENDPOINT_WARNING TUI: - VPC prompts in both create (GenerateWizard) and BYO (AddAgentScreen) paths - Inline validation for subnet/SG ID format in TextInput fields - VPC endpoint warning on completion screens Template rendering: - Add isVpc flag to AgentRenderConfig - Skip Exa AI MCP example endpoint in VPC mode (unreachable without NAT) - VPC stubs return None/empty so main.py null-checks work unchanged Data persistence: - AgentPrimitive passes VPC config through both handleCreatePath and handleByoPath to agentcore.json - useAddAgent mappers (mapByoConfigToAgent, mapAddAgentConfigToGenerateConfig) thread VPC fields through TUI path Warnings: - dev command warns about VPC behavior differences in local mode - invoke command warns about VPC endpoint requirements * fix: restore existing tests deleted by VPC commit, append VPC tests The original VPC commit accidentally replaced three test files instead of appending to them. This restores all existing tests from main and adds VPC-specific tests at the end of each file. * fix: add format validation to non-interactive VPC path, consolidate VpcOptions type - validateVpcOptions now calls validateSubnetIds/validateSecurityGroupIds so the non-interactive CLI path gives friendly format errors instead of falling through to Zod regex failures - CreateOptions, AddAgentOptions, and AgentPrimitive.AddAgentOptions now extend VpcOptions instead of redeclaring networkMode/subnets/securityGroups * fix: add VPC warnings to TUI dev and invoke screens The non-interactive CLI paths had VPC warnings but the TUI screens did not. - DevScreen: warn that local dev runs outside VPC - InvokeScreen: warn about VPC endpoint requirements - Thread networkMode through useInvokeFlow config * fix: pass VPC config through create TUI flow The GenerateWizard collected networkMode/subnets/securityGroups but useCreateFlow did not include them when building the GenerateConfig, so agents created via TUI always defaulted to PUBLIC.
1 parent 30e6a74 commit a61ebdd

42 files changed

Lines changed: 1112 additions & 57 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.

src/assets/__tests__/__snapshots__/assets.snapshot.test.ts.snap

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,6 +1012,15 @@ exports[`Assets Directory Snapshots > Python framework assets > python/python/au
10121012
10131013
exports[`Assets Directory Snapshots > Python framework assets > python/python/autogen/base/mcp_client/client.py should match snapshot 1`] = `
10141014
"from typing import List
1015+
{{#if isVpc}}
1016+
# VPC mode: external MCP endpoints are not reachable without a NAT gateway.
1017+
# Add an AgentCore Gateway with \`agentcore add gateway\`, or configure your own endpoint below.
1018+
1019+
1020+
async def get_streamable_http_mcp_tools() -> List:
1021+
"""No MCP server configured. Add a gateway with \`agentcore add gateway\`."""
1022+
return []
1023+
{{else}}
10151024
from autogen_ext.tools.mcp import (
10161025
StreamableHttpMcpToolAdapter,
10171026
StreamableHttpServerParams,
@@ -1029,6 +1038,7 @@ async def get_streamable_http_mcp_tools() -> List[StreamableHttpMcpToolAdapter]:
10291038
# to use an MCP server that supports bearer authentication, add headers={"Authorization": f"Bearer {access_token}"}
10301039
server_params = StreamableHttpServerParams(url=EXAMPLE_MCP_ENDPOINT)
10311040
return await mcp_server_tools(server_params)
1041+
{{/if}}
10321042
"
10331043
`;
10341044
@@ -1792,6 +1802,14 @@ def get_all_gateway_mcp_toolsets() -> list[MCPToolset]:
17921802
{{/each}}
17931803
return toolsets
17941804
{{else}}
1805+
{{#if isVpc}}
1806+
# VPC mode: external MCP endpoints are not reachable without a NAT gateway.
1807+
# Add an AgentCore Gateway with \`agentcore add gateway\`, or configure your own endpoint below.
1808+
1809+
def get_streamable_http_mcp_client() -> MCPToolset | None:
1810+
"""No MCP server configured. Add a gateway with \`agentcore add gateway\`."""
1811+
return None
1812+
{{else}}
17951813
# ExaAI provides information about code through web searches, crawling and code context searches through their platform. Requires no authentication
17961814
EXAMPLE_MCP_ENDPOINT = "https://mcp.exa.ai/mcp"
17971815
@@ -1803,6 +1821,7 @@ def get_streamable_http_mcp_client() -> MCPToolset:
18031821
connection_params=StreamableHTTPConnectionParams(url=EXAMPLE_MCP_ENDPOINT)
18041822
)
18051823
{{/if}}
1824+
{{/if}}
18061825
"
18071826
`;
18081827
@@ -2098,6 +2117,14 @@ def get_all_gateway_mcp_client() -> MultiServerMCPClient | None:
20982117
return None
20992118
return MultiServerMCPClient(servers)
21002119
{{else}}
2120+
{{#if isVpc}}
2121+
# VPC mode: external MCP endpoints are not reachable without a NAT gateway.
2122+
# Add an AgentCore Gateway with \`agentcore add gateway\`, or configure your own endpoint below.
2123+
2124+
def get_streamable_http_mcp_client() -> MultiServerMCPClient | None:
2125+
"""No MCP server configured. Add a gateway with \`agentcore add gateway\`."""
2126+
return None
2127+
{{else}}
21012128
# ExaAI provides information about code through web searches, crawling and code context searches through their platform. Requires no authentication
21022129
EXAMPLE_MCP_ENDPOINT = "https://mcp.exa.ai/mcp"
21032130
@@ -2114,6 +2141,7 @@ def get_streamable_http_mcp_client() -> MultiServerMCPClient:
21142141
}
21152142
)
21162143
{{/if}}
2144+
{{/if}}
21172145
"
21182146
`;
21192147
@@ -2545,6 +2573,14 @@ def get_all_gateway_mcp_servers() -> list[MCPServerStreamableHttp]:
25452573
{{/each}}
25462574
return servers
25472575
{{else}}
2576+
{{#if isVpc}}
2577+
# VPC mode: external MCP endpoints are not reachable without a NAT gateway.
2578+
# Add an AgentCore Gateway with \`agentcore add gateway\`, or configure your own endpoint below.
2579+
2580+
def get_streamable_http_mcp_client() -> MCPServerStreamableHttp | None:
2581+
"""No MCP server configured. Add a gateway with \`agentcore add gateway\`."""
2582+
return None
2583+
{{else}}
25482584
# ExaAI provides information about code through web searches, crawling and code context searches through their platform. Requires no authentication
25492585
EXAMPLE_MCP_ENDPOINT = "https://mcp.exa.ai/mcp"
25502586
@@ -2556,6 +2592,7 @@ def get_streamable_http_mcp_client() -> MCPServerStreamableHttp:
25562592
name="AgentCore Gateway MCP", params={"url": EXAMPLE_MCP_ENDPOINT}
25572593
)
25582594
{{/if}}
2595+
{{/if}}
25592596
"
25602597
`;
25612598
@@ -2879,6 +2916,14 @@ def get_all_gateway_mcp_clients() -> list[MCPClient]:
28792916
{{/each}}
28802917
return clients
28812918
{{else}}
2919+
{{#if isVpc}}
2920+
# VPC mode: external MCP endpoints are not reachable without a NAT gateway.
2921+
# Add an AgentCore Gateway with \`agentcore add gateway\`, or configure your own endpoint below.
2922+
2923+
def get_streamable_http_mcp_client() -> MCPClient | None:
2924+
"""No MCP server configured. Add a gateway with \`agentcore add gateway\`."""
2925+
return None
2926+
{{else}}
28822927
# ExaAI provides information about code through web searches, crawling and code context searches through their platform. Requires no authentication
28832928
EXAMPLE_MCP_ENDPOINT = "https://mcp.exa.ai/mcp"
28842929
@@ -2887,6 +2932,7 @@ def get_streamable_http_mcp_client() -> MCPClient:
28872932
# to use an MCP server that supports bearer authentication, add headers={"Authorization": f"Bearer {access_token}"}
28882933
return MCPClient(lambda: streamablehttp_client(EXAMPLE_MCP_ENDPOINT))
28892934
{{/if}}
2935+
{{/if}}
28902936
"
28912937
`;
28922938

src/assets/python/autogen/base/mcp_client/client.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
11
from typing import List
2+
{{#if isVpc}}
3+
# VPC mode: external MCP endpoints are not reachable without a NAT gateway.
4+
# Add an AgentCore Gateway with `agentcore add gateway`, or configure your own endpoint below.
5+
6+
7+
async def get_streamable_http_mcp_tools() -> List:
8+
"""No MCP server configured. Add a gateway with `agentcore add gateway`."""
9+
return []
10+
{{else}}
211
from autogen_ext.tools.mcp import (
312
StreamableHttpMcpToolAdapter,
413
StreamableHttpServerParams,
@@ -16,3 +25,4 @@ async def get_streamable_http_mcp_tools() -> List[StreamableHttpMcpToolAdapter]:
1625
# to use an MCP server that supports bearer authentication, add headers={"Authorization": f"Bearer {access_token}"}
1726
server_params = StreamableHttpServerParams(url=EXAMPLE_MCP_ENDPOINT)
1827
return await mcp_server_tools(server_params)
28+
{{/if}}

src/assets/python/googleadk/base/mcp_client/client.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,14 @@ def get_all_gateway_mcp_toolsets() -> list[MCPToolset]:
5353
{{/each}}
5454
return toolsets
5555
{{else}}
56+
{{#if isVpc}}
57+
# VPC mode: external MCP endpoints are not reachable without a NAT gateway.
58+
# Add an AgentCore Gateway with `agentcore add gateway`, or configure your own endpoint below.
59+
60+
def get_streamable_http_mcp_client() -> MCPToolset | None:
61+
"""No MCP server configured. Add a gateway with `agentcore add gateway`."""
62+
return None
63+
{{else}}
5664
# ExaAI provides information about code through web searches, crawling and code context searches through their platform. Requires no authentication
5765
EXAMPLE_MCP_ENDPOINT = "https://mcp.exa.ai/mcp"
5866

@@ -64,3 +72,4 @@ def get_streamable_http_mcp_client() -> MCPToolset:
6472
connection_params=StreamableHTTPConnectionParams(url=EXAMPLE_MCP_ENDPOINT)
6573
)
6674
{{/if}}
75+
{{/if}}

src/assets/python/langchain_langgraph/base/mcp_client/client.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,14 @@ def get_all_gateway_mcp_client() -> MultiServerMCPClient | None:
5050
return None
5151
return MultiServerMCPClient(servers)
5252
{{else}}
53+
{{#if isVpc}}
54+
# VPC mode: external MCP endpoints are not reachable without a NAT gateway.
55+
# Add an AgentCore Gateway with `agentcore add gateway`, or configure your own endpoint below.
56+
57+
def get_streamable_http_mcp_client() -> MultiServerMCPClient | None:
58+
"""No MCP server configured. Add a gateway with `agentcore add gateway`."""
59+
return None
60+
{{else}}
5361
# ExaAI provides information about code through web searches, crawling and code context searches through their platform. Requires no authentication
5462
EXAMPLE_MCP_ENDPOINT = "https://mcp.exa.ai/mcp"
5563

@@ -66,3 +74,4 @@ def get_streamable_http_mcp_client() -> MultiServerMCPClient:
6674
}
6775
)
6876
{{/if}}
77+
{{/if}}

src/assets/python/openaiagents/base/mcp_client/client.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,14 @@ def get_all_gateway_mcp_servers() -> list[MCPServerStreamableHttp]:
5252
{{/each}}
5353
return servers
5454
{{else}}
55+
{{#if isVpc}}
56+
# VPC mode: external MCP endpoints are not reachable without a NAT gateway.
57+
# Add an AgentCore Gateway with `agentcore add gateway`, or configure your own endpoint below.
58+
59+
def get_streamable_http_mcp_client() -> MCPServerStreamableHttp | None:
60+
"""No MCP server configured. Add a gateway with `agentcore add gateway`."""
61+
return None
62+
{{else}}
5563
# ExaAI provides information about code through web searches, crawling and code context searches through their platform. Requires no authentication
5664
EXAMPLE_MCP_ENDPOINT = "https://mcp.exa.ai/mcp"
5765

@@ -63,3 +71,4 @@ def get_streamable_http_mcp_client() -> MCPServerStreamableHttp:
6371
name="AgentCore Gateway MCP", params={"url": EXAMPLE_MCP_ENDPOINT}
6472
)
6573
{{/if}}
74+
{{/if}}

src/assets/python/strands/base/mcp_client/client.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ def get_all_gateway_mcp_clients() -> list[MCPClient]:
5454
{{/each}}
5555
return clients
5656
{{else}}
57+
{{#if isVpc}}
58+
# VPC mode: external MCP endpoints are not reachable without a NAT gateway.
59+
# Add an AgentCore Gateway with `agentcore add gateway`, or configure your own endpoint below.
60+
61+
def get_streamable_http_mcp_client() -> MCPClient | None:
62+
"""No MCP server configured. Add a gateway with `agentcore add gateway`."""
63+
return None
64+
{{else}}
5765
# ExaAI provides information about code through web searches, crawling and code context searches through their platform. Requires no authentication
5866
EXAMPLE_MCP_ENDPOINT = "https://mcp.exa.ai/mcp"
5967

@@ -62,3 +70,4 @@ def get_streamable_http_mcp_client() -> MCPClient:
6270
# to use an MCP server that supports bearer authentication, add headers={"Authorization": f"Bearer {access_token}"}
6371
return MCPClient(lambda: streamablehttp_client(EXAMPLE_MCP_ENDPOINT))
6472
{{/if}}
73+
{{/if}}

src/cli/commands/add/__tests__/validate.test.ts

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -989,3 +989,80 @@ describe('validate', () => {
989989
});
990990
});
991991
});
992+
993+
describe('validateAddAgentOptions - VPC validation', () => {
994+
const baseOptions: AddAgentOptions = {
995+
name: 'TestAgent',
996+
type: 'byo',
997+
language: 'Python',
998+
framework: 'Strands',
999+
modelProvider: 'Bedrock',
1000+
build: 'CodeZip',
1001+
codeLocation: './app/test/',
1002+
};
1003+
1004+
it('accepts valid VPC options', () => {
1005+
const result = validateAddAgentOptions({
1006+
...baseOptions,
1007+
networkMode: 'VPC',
1008+
subnets: 'subnet-12345678',
1009+
securityGroups: 'sg-12345678',
1010+
});
1011+
expect(result.valid).toBe(true);
1012+
});
1013+
1014+
it('accepts PUBLIC network mode without VPC options', () => {
1015+
const result = validateAddAgentOptions({
1016+
...baseOptions,
1017+
networkMode: 'PUBLIC',
1018+
});
1019+
expect(result.valid).toBe(true);
1020+
});
1021+
1022+
it('rejects invalid network mode', () => {
1023+
const result = validateAddAgentOptions({
1024+
...baseOptions,
1025+
networkMode: 'INVALID',
1026+
});
1027+
expect(result.valid).toBe(false);
1028+
expect(result.error).toContain('Invalid network mode');
1029+
});
1030+
1031+
it('rejects VPC mode without subnets', () => {
1032+
const result = validateAddAgentOptions({
1033+
...baseOptions,
1034+
networkMode: 'VPC',
1035+
securityGroups: 'sg-12345678',
1036+
});
1037+
expect(result.valid).toBe(false);
1038+
expect(result.error).toContain('--subnets is required');
1039+
});
1040+
1041+
it('rejects VPC mode without security groups', () => {
1042+
const result = validateAddAgentOptions({
1043+
...baseOptions,
1044+
networkMode: 'VPC',
1045+
subnets: 'subnet-12345678',
1046+
});
1047+
expect(result.valid).toBe(false);
1048+
expect(result.error).toContain('--security-groups is required');
1049+
});
1050+
1051+
it('rejects subnets without VPC mode', () => {
1052+
const result = validateAddAgentOptions({
1053+
...baseOptions,
1054+
subnets: 'subnet-12345678',
1055+
});
1056+
expect(result.valid).toBe(false);
1057+
expect(result.error).toContain('only valid with --network-mode VPC');
1058+
});
1059+
1060+
it('rejects security groups without VPC mode', () => {
1061+
const result = validateAddAgentOptions({
1062+
...baseOptions,
1063+
securityGroups: 'sg-12345678',
1064+
});
1065+
expect(result.valid).toBe(false);
1066+
expect(result.error).toContain('only valid with --network-mode VPC');
1067+
});
1068+
});

src/cli/commands/add/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import type { GatewayAuthorizerType, ModelProvider, SDKFramework, TargetLanguage } from '../../../schema';
22
import type { MemoryOption } from '../../tui/screens/generate/types';
3+
import type { VpcOptions } from '../shared/vpc-utils';
34

45
// Agent types
5-
export interface AddAgentOptions {
6+
export interface AddAgentOptions extends VpcOptions {
67
name?: string;
78
type?: 'create' | 'byo';
89
build?: string;

src/cli/commands/add/validate.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
getSupportedModelProviders,
1212
matchEnumValue,
1313
} from '../../../schema';
14+
import { validateVpcOptions } from '../shared/vpc-utils';
1415
import type {
1516
AddAgentOptions,
1617
AddGatewayOptions,
@@ -150,6 +151,12 @@ export function validateAddAgentOptions(options: AddAgentOptions): ValidationRes
150151
}
151152
}
152153

154+
// Validate VPC options
155+
const vpcResult = validateVpcOptions(options);
156+
if (!vpcResult.valid) {
157+
return { valid: false, error: vpcResult.error };
158+
}
159+
153160
return { valid: true };
154161
}
155162

0 commit comments

Comments
 (0)