Skip to content

Commit cdfff00

Browse files
committed
feat: wire scaffold MCP server flow with Lambda-only host
Route CLI gateway-target command based on source: existing-endpoint calls createExternalGatewayTarget, create-new calls createToolFromWizard. Add mcpServerScaffold targetType for scaffolded Lambda MCP servers, distinguishing them from external mcpServer endpoints and raw lambda targets. CDK support for mcpServerScaffold comes in Task 15. Restrict scaffold flow to Lambda-only compute host: - TUI: skip host selection step (always Lambda) - CLI: reject --host AgentCoreRuntime for create-new - CLI: reject --host with existing-endpoint Default --source to create-new and --host to Lambda when not specified.
1 parent 6273101 commit cdfff00

File tree

6 files changed

+68
-13
lines changed

6 files changed

+68
-13
lines changed

src/cli/commands/add/actions.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,11 @@ import {
2323
createCredential,
2424
resolveCredentialStrategy,
2525
} from '../../operations/identity/create-identity';
26-
import { createGatewayFromWizard, createToolFromWizard } from '../../operations/mcp/create-mcp';
26+
import {
27+
createExternalGatewayTarget,
28+
createGatewayFromWizard,
29+
createToolFromWizard,
30+
} from '../../operations/mcp/create-mcp';
2731
import { createMemory } from '../../operations/memory/create-memory';
2832
import { createRenderer } from '../../templates';
2933
import type { MemoryOption } from '../../tui/screens/generate/types';
@@ -322,6 +326,10 @@ export async function handleAddGatewayTarget(
322326
): Promise<AddGatewayTargetResult> {
323327
try {
324328
const config = buildGatewayTargetConfig(options);
329+
if (config.source === 'existing-endpoint') {
330+
const result = await createExternalGatewayTarget(config);
331+
return { success: true, toolName: result.toolName };
332+
}
325333
const result = await createToolFromWizard(config);
326334
return { success: true, toolName: result.toolName, sourcePath: result.projectPath };
327335
} catch (err) {

src/cli/commands/add/validate.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,15 +189,23 @@ export async function validateAddGatewayTargetOptions(options: AddGatewayTargetO
189189
return { valid: false, error: '--name is required' };
190190
}
191191

192-
if (options.type && options.type !== 'mcpServer' && options.type !== 'lambda') {
193-
return { valid: false, error: 'Invalid type. Valid options: mcpServer, lambda' };
192+
if (
193+
options.type &&
194+
options.type !== 'mcpServer' &&
195+
options.type !== 'mcpServerScaffold' &&
196+
options.type !== 'lambda'
197+
) {
198+
return { valid: false, error: 'Invalid type. Valid options: mcpServer, mcpServerScaffold, lambda' };
194199
}
195200

196201
if (options.source && options.source !== 'existing-endpoint' && options.source !== 'create-new') {
197202
return { valid: false, error: 'Invalid source. Valid options: existing-endpoint, create-new' };
198203
}
199204

200205
if (options.source === 'existing-endpoint') {
206+
if (options.host) {
207+
return { valid: false, error: '--host is not applicable for existing endpoint targets' };
208+
}
201209
if (!options.endpoint) {
202210
return { valid: false, error: '--endpoint is required when source is existing-endpoint' };
203211
}
@@ -218,6 +226,15 @@ export async function validateAddGatewayTargetOptions(options: AddGatewayTargetO
218226
return { valid: true };
219227
}
220228

229+
// Default source to create-new when not specified (scaffold flow)
230+
options.source ??= 'create-new';
231+
// Default host to Lambda for scaffolded targets
232+
options.host ??= 'Lambda';
233+
234+
if (options.host !== 'Lambda') {
235+
return { valid: false, error: 'Only Lambda is supported as compute host for scaffolded targets' };
236+
}
237+
221238
if (!options.language) {
222239
return { valid: false, error: '--language is required' };
223240
}

src/cli/operations/mcp/create-mcp.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ export async function createToolFromWizard(config: AddGatewayTargetConfig): Prom
310310
// Create a single target with all tool definitions
311311
const target: AgentCoreGatewayTarget = {
312312
name: config.name,
313-
targetType: config.host === 'AgentCoreRuntime' ? 'mcpServer' : 'lambda',
313+
targetType: config.host === 'AgentCoreRuntime' ? 'mcpServer' : 'mcpServerScaffold',
314314
toolDefinitions: toolDefs,
315315
compute:
316316
config.host === 'Lambda'

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

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ function getSteps(source?: 'existing-endpoint' | 'create-new'): AddGatewayTarget
1313
if (source === 'existing-endpoint') {
1414
return ['name', 'source', 'endpoint', 'gateway', 'confirm'];
1515
}
16-
return ['name', 'source', 'language', 'gateway', 'host', 'confirm'];
16+
// Phase 1: Lambda is the only compute host, so skip host selection
17+
return ['name', 'source', 'language', 'gateway', 'confirm'];
1718
}
1819

1920
function deriveToolDefinition(name: string): ToolDefinition {
@@ -91,13 +92,8 @@ export function useAddGatewayTargetWizard(existingGateways: string[] = []) {
9192

9293
const setGateway = useCallback((gateway: string) => {
9394
setConfig(c => {
94-
const isExternal = c.source === 'existing-endpoint';
9595
const isSkipped = gateway === SKIP_FOR_NOW;
96-
if (isExternal || isSkipped) {
97-
setStep('confirm');
98-
} else {
99-
setStep('host');
100-
}
96+
setStep('confirm');
10197
return { ...c, gateway: isSkipped ? undefined : gateway };
10298
});
10399
}, []);

src/schema/schemas/__tests__/mcp.test.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
import { describe, expect, it } from 'vitest';
1515

1616
describe('GatewayTargetTypeSchema', () => {
17-
it.each(['lambda', 'mcpServer', 'openApiSchema', 'smithyModel'])('accepts "%s"', type => {
17+
it.each(['lambda', 'mcpServer', 'mcpServerScaffold', 'openApiSchema', 'smithyModel'])('accepts "%s"', type => {
1818
expect(GatewayTargetTypeSchema.safeParse(type).success).toBe(true);
1919
});
2020

@@ -492,6 +492,27 @@ describe('AgentCoreGatewayTargetSchema with outbound auth', () => {
492492
});
493493
expect(result.success).toBe(false);
494494
});
495+
496+
it('mcpServerScaffold target without compute fails', () => {
497+
const result = AgentCoreGatewayTargetSchema.safeParse({
498+
name: 'myTarget',
499+
targetType: 'mcpServerScaffold',
500+
});
501+
expect(result.success).toBe(false);
502+
});
503+
504+
it('mcpServerScaffold target with compute passes', () => {
505+
const result = AgentCoreGatewayTargetSchema.safeParse({
506+
name: 'myTarget',
507+
targetType: 'mcpServerScaffold',
508+
compute: {
509+
host: 'Lambda',
510+
implementation: { language: 'Python', path: 'app/mcp/myTarget', handler: 'handler.handler' },
511+
pythonVersion: 'PYTHON_3_12',
512+
},
513+
});
514+
expect(result.success).toBe(true);
515+
});
495516
});
496517

497518
describe('AgentCoreMcpSpecSchema', () => {

src/schema/schemas/mcp.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,13 @@ import { z } from 'zod';
88
// MCP-Specific Schemas
99
// ============================================================================
1010

11-
export const GatewayTargetTypeSchema = z.enum(['lambda', 'mcpServer', 'openApiSchema', 'smithyModel']);
11+
export const GatewayTargetTypeSchema = z.enum([
12+
'lambda',
13+
'mcpServer',
14+
'mcpServerScaffold',
15+
'openApiSchema',
16+
'smithyModel',
17+
]);
1218
export type GatewayTargetType = z.infer<typeof GatewayTargetTypeSchema>;
1319

1420
// ============================================================================
@@ -307,6 +313,13 @@ export const AgentCoreGatewayTargetSchema = z
307313
path: ['toolDefinitions'],
308314
});
309315
}
316+
if (data.targetType === 'mcpServerScaffold' && !data.compute) {
317+
ctx.addIssue({
318+
code: z.ZodIssueCode.custom,
319+
message: 'Scaffolded MCP Server targets require compute configuration.',
320+
path: ['compute'],
321+
});
322+
}
310323
if (data.outboundAuth && data.outboundAuth.type !== 'NONE' && !data.outboundAuth.credentialName) {
311324
ctx.addIssue({
312325
code: z.ZodIssueCode.custom,

0 commit comments

Comments
 (0)