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
8 changes: 4 additions & 4 deletions docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,14 +198,14 @@ agentcore add
# External MCP server endpoint
agentcore add gateway-target \
--name WeatherTools \
--source existing-endpoint \
--type mcp-server \
--endpoint https://mcp.example.com/mcp \
--gateway MyGateway

# External endpoint with OAuth outbound auth
agentcore add gateway-target \
--name SecureTools \
--source existing-endpoint \
--type mcp-server \
--endpoint https://api.example.com/mcp \
--gateway MyGateway \
--outbound-auth oauth \
Expand All @@ -218,7 +218,7 @@ agentcore add gateway-target \
| -------------------------------- | ----------------------------------------------- |
| `--name <name>` | Target name |
| `--description <desc>` | Target description |
| `--source <source>` | `existing-endpoint` |
| `--type <type>` | Target type (required): `mcp-server` |
| `--endpoint <url>` | MCP server endpoint URL |
| `--gateway <name>` | Gateway to attach target to |
| `--outbound-auth <type>` | `oauth`, `api-key`, or `none` |
Expand Down Expand Up @@ -382,7 +382,7 @@ agentcore deploy -y
agentcore add gateway --name MyGateway
agentcore add gateway-target \
--name WeatherTools \
--source existing-endpoint \
--type mcp-server \
--endpoint https://mcp.example.com/mcp \
--gateway MyGateway
agentcore deploy -y
Expand Down
10 changes: 5 additions & 5 deletions docs/gateway.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ agentcore add gateway --name my-gateway

# 3. Add a target (external MCP server)
agentcore add gateway-target \
--source existing-endpoint \
--type mcp-server \
--name weather-tools \
--endpoint https://mcp.example.com/mcp \
--gateway my-gateway
Expand All @@ -39,7 +39,7 @@ requests to.

```bash
agentcore add gateway-target \
--source existing-endpoint \
--type mcp-server \
--name my-tools \
--endpoint https://mcp.example.com/mcp \
--gateway my-gateway
Expand Down Expand Up @@ -87,7 +87,7 @@ Controls how the gateway authenticates with upstream MCP servers. Configured per

```bash
agentcore add gateway-target \
--source existing-endpoint \
--type mcp-server \
--name secure-tools \
--endpoint https://api.example.com/mcp \
--gateway my-gateway \
Expand All @@ -108,7 +108,7 @@ agentcore add identity \
--client-secret my-secret

agentcore add gateway-target \
--source existing-endpoint \
--type mcp-server \
--name secure-tools \
--endpoint https://api.example.com/mcp \
--gateway my-gateway \
Expand All @@ -129,7 +129,7 @@ include gateway client code with the correct authentication for your framework.
# 1. Add gateway and targets
agentcore add gateway --name my-gateway
agentcore add gateway-target \
--source existing-endpoint \
--type mcp-server \
--name my-tools \
--endpoint https://mcp.example.com/mcp \
--gateway my-gateway
Expand Down
2 changes: 1 addition & 1 deletion docs/local-development.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ populated by `agentcore deploy`. If you haven't deployed yet, no gateway env var
```bash
# 1. Add a gateway and target
agentcore add gateway --name my-gateway
agentcore add gateway-target --name my-tools --source existing-endpoint \
agentcore add gateway-target --name my-tools --type mcp-server \
--endpoint https://mcp.example.com/mcp --gateway my-gateway

# 2. Deploy to create the gateway
Expand Down
2 changes: 2 additions & 0 deletions integ-tests/add-remove-gateway.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ describe('integration: add and remove gateway with external MCP server', () => {
'gateway-target',
'--name',
targetName,
'--type',
'mcp-server',
'--endpoint',
'https://mcp.exa.ai/mcp',
'--gateway',
Expand Down
4 changes: 3 additions & 1 deletion src/cli/commands/add/__tests__/add-gateway-target.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ describe('add gateway-target command', () => {

it('requires endpoint', async () => {
const result = await runCLI(
['add', 'gateway-target', '--name', 'noendpoint', '--gateway', gatewayName, '--json'],
['add', 'gateway-target', '--name', 'noendpoint', '--type', 'mcp-server', '--gateway', gatewayName, '--json'],
projectDir
);
expect(result.exitCode).toBe(1);
Expand All @@ -63,6 +63,8 @@ describe('add gateway-target command', () => {
'gateway-target',
'--name',
targetName,
'--type',
'mcp-server',
'--endpoint',
'https://mcp.exa.ai/mcp',
'--gateway',
Expand Down
66 changes: 45 additions & 21 deletions src/cli/commands/add/__tests__/validate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ const validGatewayOptionsJwt: AddGatewayOptions = {

const validGatewayTargetOptions: AddGatewayTargetOptions = {
name: 'test-tool',
source: 'existing-endpoint',
type: 'mcp-server',
endpoint: 'https://example.com/mcp',
gateway: 'my-gateway',
};
Expand Down Expand Up @@ -326,15 +326,15 @@ describe('validate', () => {

it('returns error when no gateways exist', async () => {
mockReadMcpSpec.mockResolvedValue({ agentCoreGateways: [] });
const result = await validateAddGatewayTargetOptions(validGatewayTargetOptions);
const result = await validateAddGatewayTargetOptions({ ...validGatewayTargetOptions });
expect(result.valid).toBe(false);
expect(result.error).toContain('No gateways found');
expect(result.error).toContain('agentcore add gateway');
});

it('returns error when specified gateway does not exist', async () => {
mockReadMcpSpec.mockResolvedValue({ agentCoreGateways: [{ name: 'other-gateway' }] });
const result = await validateAddGatewayTargetOptions(validGatewayTargetOptions);
const result = await validateAddGatewayTargetOptions({ ...validGatewayTargetOptions });
expect(result.valid).toBe(false);
expect(result.error).toContain('Gateway "my-gateway" not found');
expect(result.error).toContain('other-gateway');
Expand All @@ -345,22 +345,21 @@ describe('validate', () => {
const result = await validateAddGatewayTargetOptions({ ...validGatewayTargetOptions });
expect(result.valid).toBe(true);
});
// AC20: existing-endpoint source validation
it('rejects create-new source', async () => {
// AC20: type validation
it('returns error when --type is missing', async () => {
const options: AddGatewayTargetOptions = {
name: 'test-tool',
source: 'create-new' as any,
gateway: 'my-gateway',
};
const result = await validateAddGatewayTargetOptions(options);
expect(result.valid).toBe(false);
expect(result.error).toBe("Only 'existing-endpoint' source is currently supported");
expect(result.error).toContain('--type is required');
});

it('passes for valid existing-endpoint with https', async () => {
it('accepts --type mcp-server', async () => {
const options: AddGatewayTargetOptions = {
name: 'test-tool',
source: 'existing-endpoint',
type: 'mcp-server',
endpoint: 'https://example.com/mcp',
gateway: 'my-gateway',
};
Expand All @@ -369,32 +368,54 @@ describe('validate', () => {
expect(options.language).toBe('Other');
});

it('passes for valid existing-endpoint with http', async () => {
it('returns error for invalid --type', async () => {
const options: AddGatewayTargetOptions = {
name: 'test-tool',
type: 'invalid',
gateway: 'my-gateway',
};
const result = await validateAddGatewayTargetOptions(options);
expect(result.valid).toBe(false);
expect(result.error).toContain('Invalid type');
});

it('passes for mcp-server with https endpoint', async () => {
const options: AddGatewayTargetOptions = {
name: 'test-tool',
type: 'mcp-server',
endpoint: 'https://example.com/mcp',
gateway: 'my-gateway',
};
const result = await validateAddGatewayTargetOptions(options);
expect(result.valid).toBe(true);
});

it('passes for mcp-server with http endpoint', async () => {
const options: AddGatewayTargetOptions = {
name: 'test-tool',
source: 'existing-endpoint',
type: 'mcp-server',
endpoint: 'http://localhost:3000/mcp',
gateway: 'my-gateway',
};
const result = await validateAddGatewayTargetOptions(options);
expect(result.valid).toBe(true);
});

it('returns error for existing-endpoint without endpoint', async () => {
it('returns error for mcp-server without endpoint', async () => {
const options: AddGatewayTargetOptions = {
name: 'test-tool',
source: 'existing-endpoint',
type: 'mcp-server',
gateway: 'my-gateway',
};
const result = await validateAddGatewayTargetOptions(options);
expect(result.valid).toBe(false);
expect(result.error).toBe('--endpoint is required when source is existing-endpoint');
expect(result.error).toContain('--endpoint is required');
});

it('returns error for existing-endpoint with non-http(s) URL', async () => {
it('returns error for mcp-server with non-http(s) URL', async () => {
const options: AddGatewayTargetOptions = {
name: 'test-tool',
source: 'existing-endpoint',
type: 'mcp-server',
endpoint: 'ftp://example.com/mcp',
gateway: 'my-gateway',
};
Expand All @@ -403,10 +424,10 @@ describe('validate', () => {
expect(result.error).toBe('Endpoint must use http:// or https:// protocol');
});

it('returns error for existing-endpoint with invalid URL', async () => {
it('returns error for mcp-server with invalid URL', async () => {
const options: AddGatewayTargetOptions = {
name: 'test-tool',
source: 'existing-endpoint',
type: 'mcp-server',
endpoint: 'not-a-url',
gateway: 'my-gateway',
};
Expand All @@ -423,6 +444,7 @@ describe('validate', () => {

const options: AddGatewayTargetOptions = {
name: 'test-tool',
type: 'mcp-server',
endpoint: 'https://example.com/mcp',
gateway: 'my-gateway',
outboundAuthType: 'API_KEY',
Expand All @@ -440,6 +462,7 @@ describe('validate', () => {

const options: AddGatewayTargetOptions = {
name: 'test-tool',
type: 'mcp-server',
endpoint: 'https://example.com/mcp',
gateway: 'my-gateway',
outboundAuthType: 'API_KEY',
Expand All @@ -457,6 +480,7 @@ describe('validate', () => {

const options: AddGatewayTargetOptions = {
name: 'test-tool',
type: 'mcp-server',
endpoint: 'https://example.com/mcp',
gateway: 'my-gateway',
outboundAuthType: 'API_KEY',
Expand Down Expand Up @@ -531,17 +555,17 @@ describe('validate', () => {
expect(result.error).toBe('--oauth-discovery-url must be a valid URL');
});

it('rejects --host with existing-endpoint', async () => {
it('rejects --host with mcp-server type', async () => {
const options: AddGatewayTargetOptions = {
name: 'test-tool',
source: 'existing-endpoint',
type: 'mcp-server',
endpoint: 'https://example.com/mcp',
host: 'Lambda',
gateway: 'my-gateway',
};
const result = await validateAddGatewayTargetOptions(options);
expect(result.valid).toBe(false);
expect(result.error).toBe('--host is not applicable for existing endpoint targets');
expect(result.error).toBe('--host is not applicable for MCP server targets');
});
});

Expand Down
1 change: 0 additions & 1 deletion src/cli/commands/add/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ export interface AddGatewayTargetOptions {
name?: string;
description?: string;
type?: string;
source?: string;
endpoint?: string;
language?: 'Python' | 'TypeScript' | 'Other';
gateway?: string;
Expand Down
22 changes: 11 additions & 11 deletions src/cli/commands/add/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,13 +219,16 @@ export async function validateAddGatewayTargetOptions(options: AddGatewayTargetO
return { valid: false, error: '--name is required' };
}

if (options.type && options.type !== 'mcpServer' && options.type !== 'lambda') {
return { valid: false, error: 'Invalid type. Valid options: mcpServer, lambda' };
if (!options.type) {
return { valid: false, error: '--type is required. Valid options: mcp-server' };
}

if (options.source && options.source !== 'existing-endpoint') {
return { valid: false, error: "Only 'existing-endpoint' source is currently supported" };
const typeMap: Record<string, string> = { 'mcp-server': 'mcpServer' };
const mappedType = typeMap[options.type];
if (!mappedType) {
return { valid: false, error: `Invalid type: ${options.type}. Valid options: mcp-server` };
}
options.type = mappedType;

// Gateway is required — a gateway target must be attached to a gateway
if (!options.gateway) {
Expand Down Expand Up @@ -260,10 +263,7 @@ export async function validateAddGatewayTargetOptions(options: AddGatewayTargetO
};
}

// Default to existing-endpoint (only supported source for now)
options.source ??= 'existing-endpoint';

// Validate outbound auth configuration (applies to all source types)
// Validate outbound auth configuration
if (options.outboundAuthType && options.outboundAuthType !== 'NONE') {
const hasInlineOAuth = !!(options.oauthClientId ?? options.oauthClientSecret ?? options.oauthDiscoveryUrl);

Expand Down Expand Up @@ -309,12 +309,12 @@ export async function validateAddGatewayTargetOptions(options: AddGatewayTargetO
}
}

if (options.source === 'existing-endpoint') {
if (mappedType === 'mcpServer') {
if (options.host) {
return { valid: false, error: '--host is not applicable for existing endpoint targets' };
return { valid: false, error: '--host is not applicable for MCP server targets' };
}
if (!options.endpoint) {
return { valid: false, error: '--endpoint is required when source is existing-endpoint' };
return { valid: false, error: '--endpoint is required for mcp-server type' };
}

try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ describe('remove gateway-target command', () => {
'https://example.com/mcp',
'--gateway',
tempGateway,
'--type',
'mcp-server',
'--json',
],
projectDir
Expand Down
2 changes: 2 additions & 0 deletions src/cli/commands/remove/__tests__/remove-gateway.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ describe('remove gateway command', () => {
'https://example.com/mcp',
'--gateway',
gatewayName,
'--type',
'mcp-server',
'--json',
],
projectDir
Expand Down
Loading
Loading