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
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,18 @@ import { join } from 'node:path';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';

// MCP Tool feature is disabled (coming soon) - skip all tests
describe.skip('add mcp-tool command', () => {
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

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

// Create project with agent
const projectName = 'McpToolProj';
const projectName = 'GatewayTargetProj';
let result = await runCLI(['create', '--name', projectName, '--no-agent'], testDir);
if (result.exitCode !== 0) {
throw new Error(`Failed to create project: ${result.stdout} ${result.stderr}`);
Expand Down Expand Up @@ -54,15 +54,18 @@ describe.skip('add mcp-tool command', () => {

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

it('requires exposure flag', async () => {
const result = await runCLI(['add', 'mcp-tool', '--name', 'test', '--language', 'Python', '--json'], projectDir);
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);
Expand All @@ -73,7 +76,7 @@ describe.skip('add mcp-tool command', () => {
const result = await runCLI(
[
'add',
'mcp-tool',
'gateway-target',
'--name',
'test',
'--language',
Expand All @@ -99,7 +102,7 @@ describe.skip('add mcp-tool command', () => {
const result = await runCLI(
[
'add',
'mcp-tool',
'gateway-target',
'--name',
'container-tool',
'--language',
Expand Down Expand Up @@ -130,7 +133,7 @@ describe.skip('add mcp-tool command', () => {
const result = await runCLI(
[
'add',
'mcp-tool',
'gateway-target',
'--name',
toolName,
'--language',
Expand Down Expand Up @@ -163,7 +166,7 @@ describe.skip('add mcp-tool command', () => {

it('requires agents for mcp-runtime', async () => {
const result = await runCLI(
['add', 'mcp-tool', '--name', 'no-agents', '--language', 'Python', '--exposure', 'mcp-runtime', '--json'],
['add', 'gateway-target', '--name', 'no-agents', '--language', 'Python', '--exposure', 'mcp-runtime', '--json'],
projectDir
);
expect(result.exitCode).toBe(1);
Expand All @@ -176,7 +179,7 @@ describe.skip('add mcp-tool command', () => {
const result = await runCLI(
[
'add',
'mcp-tool',
'gateway-target',
'--name',
'runtime-container',
'--language',
Expand Down Expand Up @@ -204,7 +207,7 @@ describe.skip('add mcp-tool command', () => {
const result = await runCLI(
[
'add',
'mcp-tool',
'gateway-target',
'--name',
toolName,
'--language',
Expand Down Expand Up @@ -236,7 +239,7 @@ describe.skip('add mcp-tool command', () => {
const result = await runCLI(
[
'add',
'mcp-tool',
'gateway-target',
'--name',
'no-gw',
'--language',
Expand All @@ -259,7 +262,7 @@ describe.skip('add mcp-tool command', () => {
const result = await runCLI(
[
'add',
'mcp-tool',
'gateway-target',
'--name',
'no-host',
'--language',
Expand All @@ -282,7 +285,7 @@ describe.skip('add mcp-tool command', () => {
const result = await runCLI(
[
'add',
'mcp-tool',
'gateway-target',
'--name',
'gateway-container',
'--language',
Expand Down
28 changes: 14 additions & 14 deletions src/cli/commands/add/__tests__/validate.test.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import type {
AddAgentOptions,
AddGatewayOptions,
AddGatewayTargetOptions,
AddIdentityOptions,
AddMcpToolOptions,
AddMemoryOptions,
} from '../types.js';
import {
validateAddAgentOptions,
validateAddGatewayOptions,
validateAddGatewayTargetOptions,
validateAddIdentityOptions,
validateAddMcpToolOptions,
validateAddMemoryOptions,
} from '../validate.js';
import { describe, expect, it } from 'vitest';
Expand Down Expand Up @@ -46,14 +46,14 @@ const validGatewayOptionsJwt: AddGatewayOptions = {
allowedClients: 'client1,client2',
};

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

const validMcpToolOptionsBehindGateway: AddMcpToolOptions = {
const validGatewayTargetOptionsBehindGateway: AddGatewayTargetOptions = {
name: 'test-tool',
language: 'Python',
exposure: 'behind-gateway',
Expand Down Expand Up @@ -235,55 +235,55 @@ describe('validate', () => {
});
});

describe('validateAddMcpToolOptions', () => {
describe('validateAddGatewayTargetOptions', () => {
// AC15: Required fields validated
it('returns error for missing required fields', () => {
const requiredFields: { field: keyof AddMcpToolOptions; error: string }[] = [
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 = { ...validMcpToolOptionsMcpRuntime, [field]: undefined };
const result = validateAddMcpToolOptions(opts);
const opts = { ...validGatewayTargetOptionsMcpRuntime, [field]: undefined };
const result = validateAddGatewayTargetOptions(opts);
expect(result.valid, `Should fail for missing ${String(field)}`).toBe(false);
expect(result.error).toBe(error);
}
});

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

result = validateAddMcpToolOptions({ ...validMcpToolOptionsMcpRuntime, exposure: 'invalid' as any });
result = 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', () => {
let result = validateAddMcpToolOptions({ ...validMcpToolOptionsMcpRuntime, agents: undefined });
let result = validateAddGatewayTargetOptions({ ...validGatewayTargetOptionsMcpRuntime, agents: undefined });
expect(result.valid).toBe(false);
expect(result.error).toBe('--agents is required for mcp-runtime exposure');

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

// AC18: behind-gateway exposure is disabled (coming soon)
it('returns coming soon error for behind-gateway exposure', () => {
const result = validateAddMcpToolOptions({ ...validMcpToolOptionsBehindGateway });
const result = validateAddGatewayTargetOptions({ ...validGatewayTargetOptionsBehindGateway });
expect(result.valid).toBe(false);
expect(result.error).toContain('coming soon');
});

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

Expand Down
20 changes: 14 additions & 6 deletions src/cli/commands/add/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,15 @@ import { createGatewayFromWizard, createToolFromWizard } from '../../operations/
import { createMemory } from '../../operations/memory/create-memory';
import { createRenderer } from '../../templates';
import type { MemoryOption } from '../../tui/screens/generate/types';
import type { AddGatewayConfig, AddMcpToolConfig } from '../../tui/screens/mcp/types';
import type { AddGatewayConfig, AddGatewayTargetConfig } from '../../tui/screens/mcp/types';
import { DEFAULT_EVENT_EXPIRY } from '../../tui/screens/memory/types';
import type { AddAgentResult, AddGatewayResult, AddIdentityResult, AddMcpToolResult, AddMemoryResult } from './types';
import type {
AddAgentResult,
AddGatewayResult,
AddGatewayTargetResult,
AddIdentityResult,
AddMemoryResult,
} from './types';
import { mkdirSync } from 'fs';
import { dirname, join } from 'path';

Expand Down Expand Up @@ -57,7 +63,7 @@ export interface ValidatedAddGatewayOptions {
agents?: string;
}

export interface ValidatedAddMcpToolOptions {
export interface ValidatedAddGatewayTargetOptions {
name: string;
description?: string;
language: 'Python' | 'TypeScript' | 'Other';
Expand Down Expand Up @@ -276,7 +282,7 @@ export async function handleAddGateway(options: ValidatedAddGatewayOptions): Pro
}

// MCP Tool handler
function buildMcpToolConfig(options: ValidatedAddMcpToolOptions): AddMcpToolConfig {
function buildGatewayTargetConfig(options: ValidatedAddGatewayTargetOptions): AddGatewayTargetConfig {
const sourcePath = `${APP_DIR}/${MCP_APP_SUBDIR}/${options.name}`;

const description = options.description ?? `Tool for ${options.name}`;
Expand All @@ -303,9 +309,11 @@ function buildMcpToolConfig(options: ValidatedAddMcpToolOptions): AddMcpToolConf
};
}

export async function handleAddMcpTool(options: ValidatedAddMcpToolOptions): Promise<AddMcpToolResult> {
export async function handleAddGatewayTarget(
options: ValidatedAddGatewayTargetOptions
): Promise<AddGatewayTargetResult> {
try {
const config = buildMcpToolConfig(options);
const config = buildGatewayTargetConfig(options);
const result = await createToolFromWizard(config);
return { success: true, toolName: result.toolName, sourcePath: result.projectPath };
} catch (err) {
Expand Down
24 changes: 15 additions & 9 deletions src/cli/commands/add/command.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
import { COMMAND_DESCRIPTIONS } from '../../tui/copy';
import { requireProject } from '../../tui/guards';
import { AddFlow } from '../../tui/screens/add/AddFlow';
import { handleAddAgent, handleAddGateway, handleAddIdentity, handleAddMcpTool, handleAddMemory } from './actions';
import {
handleAddAgent,
handleAddGateway,
handleAddGatewayTarget,
handleAddIdentity,
handleAddMemory,
} from './actions';
import type {
AddAgentOptions,
AddGatewayOptions,
AddGatewayTargetOptions,
AddIdentityOptions,
AddMcpToolOptions,
AddMemoryOptions,
} from './types';
import {
validateAddAgentOptions,
validateAddGatewayOptions,
validateAddGatewayTargetOptions,
validateAddIdentityOptions,
validateAddMcpToolOptions,
validateAddMemoryOptions,
} from './validate';
import type { Command } from '@commander-js/extra-typings';
Expand Down Expand Up @@ -92,8 +98,8 @@ async function _handleAddGatewayCLI(options: AddGatewayOptions): Promise<void> {
}

// MCP Tool disabled - prefix with underscore until feature is re-enabled
async function _handleAddMcpToolCLI(options: AddMcpToolOptions): Promise<void> {
const validation = validateAddMcpToolOptions(options);
async function _handleAddGatewayTargetCLI(options: AddGatewayTargetOptions): Promise<void> {
const validation = validateAddGatewayTargetOptions(options);
if (!validation.valid) {
if (options.json) {
console.log(JSON.stringify({ success: false, error: validation.error }));
Expand All @@ -103,7 +109,7 @@ async function _handleAddMcpToolCLI(options: AddMcpToolOptions): Promise<void> {
process.exit(1);
}

const result = await handleAddMcpTool({
const result = await handleAddGatewayTarget({
name: options.name!,
description: options.description,
language: options.language! as 'Python' | 'TypeScript',
Expand Down Expand Up @@ -252,10 +258,10 @@ export function registerAdd(program: Command) {
process.exit(1);
});

// Subcommand: add mcp-tool (disabled - coming soon)
// Subcommand: add gateway-target (disabled - coming soon)
addCmd
.command('mcp-tool', { hidden: true })
.description('Add an MCP tool to the project')
.command('gateway-target', { hidden: true })
.description('Add a gateway target to the project')
.option('--name <name>', 'Tool name')
.option('--description <desc>', 'Tool description')
.option('--language <lang>', 'Language: Python or TypeScript')
Expand Down
4 changes: 2 additions & 2 deletions src/cli/commands/add/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export interface AddGatewayResult {
}

// MCP Tool types
export interface AddMcpToolOptions {
export interface AddGatewayTargetOptions {
name?: string;
description?: string;
language?: 'Python' | 'TypeScript' | 'Other';
Expand All @@ -53,7 +53,7 @@ export interface AddMcpToolOptions {
json?: boolean;
}

export interface AddMcpToolResult {
export interface AddGatewayTargetResult {
success: boolean;
toolName?: string;
sourcePath?: string;
Expand Down
4 changes: 2 additions & 2 deletions src/cli/commands/add/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import {
import type {
AddAgentOptions,
AddGatewayOptions,
AddGatewayTargetOptions,
AddIdentityOptions,
AddMcpToolOptions,
AddMemoryOptions,
} from './types';

Expand Down Expand Up @@ -154,7 +154,7 @@ export function validateAddGatewayOptions(options: AddGatewayOptions): Validatio
}

// MCP Tool validation
export function validateAddMcpToolOptions(options: AddMcpToolOptions): ValidationResult {
export function validateAddGatewayTargetOptions(options: AddGatewayTargetOptions): ValidationResult {
if (!options.name) {
return { valid: false, error: '--name is required' };
}
Expand Down
Loading
Loading