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
30 changes: 19 additions & 11 deletions integ-tests/add-remove-gateway.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createTestProject, runCLI } from '../src/test-utils/index.js';
import type { TestProject } from '../src/test-utils/index.js';
import { readFile, writeFile } from 'node:fs/promises';
import { mkdir, readFile, writeFile } from 'node:fs/promises';
import { join } from 'node:path';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';

Expand Down Expand Up @@ -124,14 +124,17 @@ describe('integration: add and remove gateway with OpenAPI schema target', () =>
});

it('adds an OpenAPI schema target with a local file', async () => {
// Write a minimal OpenAPI schema file inside agentcore/ directory
// Write a minimal OpenAPI schema file at project root (alongside agentcore/)
const specsDir = join(project.projectPath, 'specs');
await mkdir(specsDir, { recursive: true });
const schemaContent = JSON.stringify({
openapi: '3.0.0',
info: { title: 'Petstore', version: '1.0.0' },
paths: { '/pets': { get: { summary: 'List pets', operationId: 'listPets' } } },
});
await writeFile(join(project.projectPath, 'agentcore', schemaFileName), schemaContent, 'utf-8');
await writeFile(join(specsDir, schemaFileName), schemaContent, 'utf-8');

const schemaPath = `specs/${schemaFileName}`;
const result = await runCLI(
[
'add',
Expand All @@ -141,7 +144,7 @@ describe('integration: add and remove gateway with OpenAPI schema target', () =>
'--type',
'open-api-schema',
'--schema',
schemaFileName,
schemaPath,
'--gateway',
gatewayName,
'--outbound-auth',
Expand All @@ -163,10 +166,11 @@ describe('integration: add and remove gateway with OpenAPI schema target', () =>
const target = gateway?.targets?.find((t: { name: string }) => t.name === targetName);
expect(target, `Target "${targetName}" should be in gateway targets`).toBeTruthy();
expect(target.targetType).toBe('openApiSchema');
expect(target.schemaSource?.inline?.path).toBe(schemaFileName);
expect(target.schemaSource?.inline?.path).toBe(schemaPath);
});

it('rejects duplicate target name', async () => {
const schemaPath = `specs/${schemaFileName}`;
const result = await runCLI(
[
'add',
Expand All @@ -176,7 +180,7 @@ describe('integration: add and remove gateway with OpenAPI schema target', () =>
'--type',
'open-api-schema',
'--schema',
schemaFileName,
schemaPath,
'--gateway',
gatewayName,
'--outbound-auth',
Expand Down Expand Up @@ -359,12 +363,16 @@ describe('integration: add gateway with Smithy model target', () => {
});

it('adds a Smithy model target with a local file', async () => {
// Write a minimal Smithy model file at project root (alongside agentcore/)
const specsDir = join(project.projectPath, 'specs');
await mkdir(specsDir, { recursive: true });
const schemaContent = JSON.stringify({
smithy: '2.0',
shapes: { 'example#MyService': { type: 'service', version: '2024-01-01' } },
});
await writeFile(join(project.projectPath, 'agentcore', schemaFileName), schemaContent, 'utf-8');
await writeFile(join(specsDir, schemaFileName), schemaContent, 'utf-8');

const schemaPath = `specs/${schemaFileName}`;
const result = await runCLI(
[
'add',
Expand All @@ -374,7 +382,7 @@ describe('integration: add gateway with Smithy model target', () => {
'--type',
'smithy-model',
'--schema',
schemaFileName,
schemaPath,
'--gateway',
gatewayName,
'--json',
Expand All @@ -392,7 +400,7 @@ describe('integration: add gateway with Smithy model target', () => {
const target = gateway?.targets?.find((t: { name: string }) => t.name === targetName);
expect(target, `Target "${targetName}" should be in gateway targets`).toBeTruthy();
expect(target.targetType).toBe('smithyModel');
expect(target.schemaSource?.inline?.path).toBe(schemaFileName);
expect(target.schemaSource?.inline?.path).toBe(schemaPath);
});

it('removes the Smithy model target', async () => {
Expand Down Expand Up @@ -455,7 +463,7 @@ describe('integration: schema-based target validation errors', () => {
});

it('rejects open-api-schema with non-JSON file', async () => {
await writeFile(join(project.projectPath, 'agentcore', 'spec.yaml'), 'openapi: 3.0.0', 'utf-8');
await writeFile(join(project.projectPath, 'spec.yaml'), 'openapi: 3.0.0', 'utf-8');

const result = await runCLI(
[
Expand All @@ -478,7 +486,7 @@ describe('integration: schema-based target validation errors', () => {
});

it('rejects --schema-s3-account with local file', async () => {
await writeFile(join(project.projectPath, 'agentcore', 'local.json'), '{}', 'utf-8');
await writeFile(join(project.projectPath, 'local.json'), '{}', 'utf-8');

const result = await runCLI(
[
Expand Down
11 changes: 6 additions & 5 deletions src/cli/commands/add/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import type {
AddMemoryOptions,
} from './types';
import { existsSync } from 'fs';
import { extname, join, resolve } from 'path';
import { dirname, extname, join, resolve } from 'path';

export interface ValidationResult {
valid: boolean;
Expand Down Expand Up @@ -393,14 +393,15 @@ export async function validateAddGatewayTargetOptions(options: AddGatewayTargetO
return { valid: false, error: 'Invalid S3 URI format. Expected: s3://bucket-name/key' };
}
} else {
// Local file validation — resolve relative to agentcore/ directory
// Local file validation — resolve relative to project root (parent of agentcore/)
const configRoot = findConfigRoot();
const resolvedPath = configRoot ? join(configRoot, options.schema) : resolve(options.schema);
const projectRoot = configRoot ? dirname(configRoot) : undefined;
const resolvedPath = projectRoot ? join(projectRoot, options.schema) : resolve(options.schema);
if (!existsSync(resolvedPath)) {
return {
valid: false,
error: configRoot
? `Schema file not found: ${options.schema} (resolved to ${resolvedPath}). Path should be relative to the agentcore/ directory.`
error: projectRoot
? `Schema file not found: ${options.schema} (resolved to ${resolvedPath}). Path should be relative to the project root.`
: `Schema file not found: ${options.schema}`,
};
}
Expand Down
2 changes: 1 addition & 1 deletion src/cli/primitives/GatewayTargetPrimitive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ export class GatewayTargetPrimitive extends BasePrimitive<AddGatewayTargetOption
.option('--tool-filter-methods <methods>', 'Comma-separated HTTP methods, e.g. GET,POST')
.option(
'--schema <path>',
'Path to schema file (relative to agentcore/) or S3 URI (for open-api-schema / smithy-model)'
'Path to schema file (relative to project root) or S3 URI (for open-api-schema / smithy-model)'
)
.option('--schema-s3-account <id>', 'S3 bucket owner account ID (for cross-account access)')
.option('--json', 'Output as JSON')
Expand Down
6 changes: 3 additions & 3 deletions src/cli/tui/screens/mcp/AddGatewayTargetScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -377,8 +377,8 @@ export function AddGatewayTargetScreen({
<TextInput
prompt={
wizard.config.targetType === 'smithyModel'
? 'Smithy model JSON file (relative to agentcore/) or S3 URI'
: 'OpenAPI schema JSON file (relative to agentcore/) or S3 URI'
? 'Smithy model JSON file (relative to project root) or S3 URI'
: 'OpenAPI schema JSON file (relative to project root) or S3 URI'
}
placeholder="specs/schema.json or s3://bucket/spec.json"
onSubmit={(value: string) => {
Expand Down Expand Up @@ -473,7 +473,7 @@ export function AddGatewayTargetScreen({
label: 'Schema Source',
value:
'inline' in wizard.config.schemaSource
? `agentcore/${wizard.config.schemaSource.inline.path}`
? wizard.config.schemaSource.inline.path
: wizard.config.schemaSource.s3.uri,
},
]
Expand Down
Loading