Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
174 changes: 7 additions & 167 deletions src/cli/commands/add/__tests__/add-gateway-target.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,43 +9,19 @@ import { afterAll, beforeAll, describe, expect, it } from 'vitest';
describe.skip('add gateway-target command', () => {
let testDir: string;
let projectDir: string;
const agentName = 'TestAgent';
const gatewayName = 'test-gateway'; // Used in skipped behind-gateway tests
const gatewayName = 'test-gateway';

beforeAll(async () => {
testDir = join(tmpdir(), `agentcore-add-gateway-target-${randomUUID()}`);
await mkdir(testDir, { recursive: true });

// Create project with agent
// Create project
const projectName = 'GatewayTargetProj';
let result = await runCLI(['create', '--name', projectName, '--no-agent'], testDir);
const result = await runCLI(['create', '--name', projectName, '--no-agent'], testDir);
if (result.exitCode !== 0) {
throw new Error(`Failed to create project: ${result.stdout} ${result.stderr}`);
}
projectDir = join(testDir, projectName);

// Add agent for mcp-runtime tests
result = await runCLI(
[
'add',
'agent',
'--name',
agentName,
'--language',
'Python',
'--framework',
'Strands',
'--model-provider',
'Bedrock',
'--memory',
'none',
'--json',
],
projectDir
);
if (result.exitCode !== 0) {
throw new Error(`Failed to create agent: ${result.stdout} ${result.stderr}`);
}
});

afterAll(async () => {
Expand All @@ -61,32 +37,9 @@ describe.skip('add gateway-target command', () => {
expect(json.error.includes('--name'), `Error: ${json.error}`).toBeTruthy();
});

it('requires exposure flag', async () => {
const result = await runCLI(
['add', 'gateway-target', '--name', 'test', '--language', 'Python', '--json'],
projectDir
);
expect(result.exitCode).toBe(1);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(false);
expect(json.error.includes('--exposure'), `Error: ${json.error}`).toBeTruthy();
});

it('validates language', async () => {
const result = await runCLI(
[
'add',
'gateway-target',
'--name',
'test',
'--language',
'InvalidLang',
'--exposure',
'mcp-runtime',
'--agents',
agentName,
'--json',
],
['add', 'gateway-target', '--name', 'test', '--language', 'InvalidLang', '--json'],
projectDir
);
expect(result.exitCode).toBe(1);
Expand All @@ -100,19 +53,7 @@ describe.skip('add gateway-target command', () => {

it('accepts Other as valid language option', async () => {
const result = await runCLI(
[
'add',
'gateway-target',
'--name',
'container-tool',
'--language',
'Other',
'--exposure',
'mcp-runtime',
'--agents',
agentName,
'--json',
],
['add', 'gateway-target', '--name', 'container-tool', '--language', 'Other', '--json'],
projectDir
);

Expand All @@ -127,79 +68,6 @@ describe.skip('add gateway-target command', () => {
});
});

describe('mcp-runtime', () => {
it('creates mcp-runtime tool', async () => {
const toolName = `rttool${Date.now()}`;
const result = await runCLI(
[
'add',
'gateway-target',
'--name',
toolName,
'--language',
'Python',
'--exposure',
'mcp-runtime',
'--agents',
agentName,
'--json',
],
projectDir
);

expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(true);
expect(json.toolName).toBe(toolName);

// Verify in mcp.json
const mcpSpec = JSON.parse(await readFile(join(projectDir, 'agentcore/mcp.json'), 'utf-8'));
const tool = mcpSpec.mcpRuntimeTools?.find((t: { name: string }) => t.name === toolName);
expect(tool, 'Tool should be in mcpRuntimeTools').toBeTruthy();

// Verify agent has remote tool reference
const projectSpec = JSON.parse(await readFile(join(projectDir, 'agentcore/agentcore.json'), 'utf-8'));
const agent = projectSpec.agents.find((a: { name: string }) => a.name === agentName);
const hasRef = agent?.remoteTools?.some((rt: { mcpRuntimeName?: string }) => rt.mcpRuntimeName === toolName);
expect(hasRef, 'Agent should have remoteTools reference').toBeTruthy();
});

it('requires agents for mcp-runtime', async () => {
const result = await runCLI(
['add', 'gateway-target', '--name', 'no-agents', '--language', 'Python', '--exposure', 'mcp-runtime', '--json'],
projectDir
);
expect(result.exitCode).toBe(1);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(false);
expect(json.error.includes('--agents'), `Error: ${json.error}`).toBeTruthy();
});

it('returns clear error for Other language with mcp-runtime', async () => {
const result = await runCLI(
[
'add',
'gateway-target',
'--name',
'runtime-container',
'--language',
'Other',
'--exposure',
'mcp-runtime',
'--agents',
agentName,
'--json',
],
projectDir
);

expect(result.exitCode).toBe(1);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(false);
expect(json.error.length > 0, 'Should have error message').toBeTruthy();
});
});

// Gateway disabled - skip behind-gateway tests until gateway feature is enabled
describe.skip('behind-gateway', () => {
it('creates behind-gateway tool', async () => {
Expand All @@ -212,8 +80,6 @@ describe.skip('add gateway-target command', () => {
toolName,
'--language',
'Python',
'--exposure',
'behind-gateway',
'--gateway',
gatewayName,
'--host',
Expand All @@ -237,19 +103,7 @@ describe.skip('add gateway-target command', () => {

it('requires gateway for behind-gateway', async () => {
const result = await runCLI(
[
'add',
'gateway-target',
'--name',
'no-gw',
'--language',
'Python',
'--exposure',
'behind-gateway',
'--host',
'Lambda',
'--json',
],
['add', 'gateway-target', '--name', 'no-gw', '--language', 'Python', '--host', 'Lambda', '--json'],
projectDir
);
expect(result.exitCode).toBe(1);
Expand All @@ -260,19 +114,7 @@ describe.skip('add gateway-target command', () => {

it('requires host for behind-gateway', async () => {
const result = await runCLI(
[
'add',
'gateway-target',
'--name',
'no-host',
'--language',
'Python',
'--exposure',
'behind-gateway',
'--gateway',
gatewayName,
'--json',
],
['add', 'gateway-target', '--name', 'no-host', '--language', 'Python', '--gateway', gatewayName, '--json'],
projectDir
);
expect(result.exitCode).toBe(1);
Expand All @@ -290,8 +132,6 @@ describe.skip('add gateway-target command', () => {
'gateway-container',
'--language',
'Other',
'--exposure',
'behind-gateway',
'--gateway',
gatewayName,
'--host',
Expand Down
46 changes: 7 additions & 39 deletions src/cli/commands/add/__tests__/validate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,9 @@ const validGatewayOptionsJwt: AddGatewayOptions = {
allowedClients: 'client1,client2',
};

const validGatewayTargetOptionsMcpRuntime: AddGatewayTargetOptions = {
const validGatewayTargetOptions: AddGatewayTargetOptions = {
name: 'test-tool',
language: 'Python',
exposure: 'mcp-runtime',
agents: 'Agent1,Agent2',
};

const validGatewayTargetOptionsBehindGateway: AddGatewayTargetOptions = {
name: 'test-tool',
language: 'Python',
exposure: 'behind-gateway',
gateway: 'my-gateway',
host: 'Lambda',
};
Expand Down Expand Up @@ -241,11 +233,10 @@ describe('validate', () => {
const requiredFields: { field: keyof AddGatewayTargetOptions; error: string }[] = [
{ field: 'name', error: '--name is required' },
{ field: 'language', error: '--language is required' },
{ field: 'exposure', error: '--exposure is required' },
];

for (const { field, error } of requiredFields) {
const opts = { ...validGatewayTargetOptionsMcpRuntime, [field]: undefined };
const opts = { ...validGatewayTargetOptions, [field]: undefined };
const result = await validateAddGatewayTargetOptions(opts);
expect(result.valid, `Should fail for missing ${String(field)}`).toBe(false);
expect(result.error).toBe(error);
Expand All @@ -254,42 +245,19 @@ describe('validate', () => {

// AC16: Invalid values rejected
it('returns error for invalid values', async () => {
let result = await validateAddGatewayTargetOptions({
...validGatewayTargetOptionsMcpRuntime,
const result = await validateAddGatewayTargetOptions({
...validGatewayTargetOptions,
language: 'Java' as any,
});
expect(result.valid).toBe(false);
expect(result.error?.includes('Invalid language')).toBeTruthy();

result = await validateAddGatewayTargetOptions({
...validGatewayTargetOptionsMcpRuntime,
exposure: 'invalid' as any,
});
expect(result.valid).toBe(false);
expect(result.error?.includes('Invalid exposure')).toBeTruthy();
});

// AC17: mcp-runtime exposure requires agents
it('returns error for mcp-runtime without agents', async () => {
let result = await validateAddGatewayTargetOptions({ ...validGatewayTargetOptionsMcpRuntime, agents: undefined });
expect(result.valid).toBe(false);
expect(result.error).toBe('--agents is required for mcp-runtime exposure');

result = await validateAddGatewayTargetOptions({ ...validGatewayTargetOptionsMcpRuntime, agents: ',,,' });
expect(result.valid).toBe(false);
expect(result.error).toBe('At least one agent is required');
});

// AC18: behind-gateway exposure is enabled
it('passes for valid behind-gateway options', async () => {
const result = await validateAddGatewayTargetOptions({ ...validGatewayTargetOptionsBehindGateway });
// AC18: Valid options pass
it('passes for valid gateway target options', async () => {
const result = await validateAddGatewayTargetOptions({ ...validGatewayTargetOptions });
expect(result.valid).toBe(true);
});

// AC19: Valid options pass
it('passes for valid mcp-runtime options', async () => {
expect(await validateAddGatewayTargetOptions(validGatewayTargetOptionsMcpRuntime)).toEqual({ valid: true });
});
});

describe('validateAddMemoryOptions', () => {
Expand Down
14 changes: 2 additions & 12 deletions src/cli/commands/add/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,6 @@ export interface ValidatedAddGatewayTargetOptions {
source?: 'existing-endpoint' | 'create-new';
endpoint?: string;
language: 'Python' | 'TypeScript' | 'Other';
exposure: 'mcp-runtime' | 'behind-gateway';
agents?: string;
gateway?: string;
host?: 'Lambda' | 'AgentCoreRuntime';
outboundAuthType?: 'OAUTH' | 'API_KEY' | 'NONE';
Expand Down Expand Up @@ -306,23 +304,15 @@ function buildGatewayTargetConfig(options: ValidatedAddGatewayTargetOptions): Ad
description,
sourcePath,
language: options.language,
exposure: options.exposure,
source: options.source,
endpoint: options.endpoint,
host: options.exposure === 'mcp-runtime' ? 'AgentCoreRuntime' : options.host!,
host: options.host!,
toolDefinition: {
name: options.name,
description,
inputSchema: { type: 'object' },
},
selectedAgents:
options.exposure === 'mcp-runtime'
? options
.agents!.split(',')
.map(s => s.trim())
.filter(Boolean)
: [],
gateway: options.exposure === 'behind-gateway' ? options.gateway : undefined,
gateway: options.gateway,
outboundAuth,
};
}
Expand Down
8 changes: 2 additions & 6 deletions src/cli/commands/add/command.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,6 @@ async function handleAddGatewayTargetCLI(options: AddGatewayTargetOptions): Prom
name: options.name!,
description: options.description,
language: options.language! as 'Python' | 'TypeScript',
exposure: options.exposure!,
agents: options.agents,
gateway: options.gateway,
host: options.host,
});
Expand Down Expand Up @@ -266,10 +264,8 @@ export function registerAdd(program: Command) {
.option('--source <source>', 'Source: existing-endpoint or create-new')
.option('--endpoint <url>', 'MCP server endpoint URL')
.option('--language <lang>', 'Language: Python or TypeScript')
.option('--exposure <mode>', 'Exposure mode: mcp-runtime or behind-gateway')
.option('--agents <names>', 'Comma-separated agent names (for mcp-runtime)')
.option('--gateway <name>', 'Gateway name (for behind-gateway)')
.option('--host <host>', 'Compute host: Lambda or AgentCoreRuntime (for behind-gateway)')
.option('--gateway <name>', 'Gateway name')
.option('--host <host>', 'Compute host: Lambda or AgentCoreRuntime')
.option('--json', 'Output as JSON')
.action(async options => {
requireProject();
Expand Down
2 changes: 0 additions & 2 deletions src/cli/commands/add/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,6 @@ export interface AddGatewayTargetOptions {
source?: string;
endpoint?: string;
language?: 'Python' | 'TypeScript' | 'Other';
exposure?: 'mcp-runtime' | 'behind-gateway';
agents?: string;
gateway?: string;
host?: 'Lambda' | 'AgentCoreRuntime';
outboundAuthType?: 'OAUTH' | 'API_KEY' | 'NONE';
Expand Down
Loading
Loading