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
24 changes: 5 additions & 19 deletions src/cli/commands/add/__tests__/validate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -469,40 +469,26 @@ describe('validate', () => {
expect(result.valid).toBe(true);
});

// Passthrough is gated behind ENABLE_GATED_FEATURES
describe('passthrough feature flag', () => {
describe('passthrough target type', () => {
const passthroughOpts: AddGatewayTargetOptions = {
name: 'pt-target',
type: 'passthrough',
gateway: 'my-gateway',
passthroughEndpoint: 'https://api.example.com',
} as AddGatewayTargetOptions;

afterEach(() => {
delete process.env.ENABLE_GATED_FEATURES;
});

it('rejects passthrough when the flag is off', async () => {
delete process.env.ENABLE_GATED_FEATURES;
it('accepts a valid passthrough target', async () => {
const result = await validateAddGatewayTargetOptions({ ...passthroughOpts });
expect(result.valid).toBe(false);
expect(result.error).toBe('Passthrough targets are not yet available.');
expect(result.valid).toBe(true);
});

it('omits passthrough from the invalid-type error when the flag is off', async () => {
delete process.env.ENABLE_GATED_FEATURES;
it('lists passthrough in the invalid-type error', async () => {
const result = await validateAddGatewayTargetOptions({
...validGatewayTargetOptions,
type: 'bogus-type',
} as AddGatewayTargetOptions);
expect(result.valid).toBe(false);
expect(result.error).not.toContain('passthrough');
});

it('allows passthrough when the flag is on', async () => {
process.env.ENABLE_GATED_FEATURES = '1';
const result = await validateAddGatewayTargetOptions({ ...passthroughOpts });
expect(result.valid).toBe(true);
expect(result.error).toContain('passthrough');
});
});
// AC20: type validation
Expand Down
6 changes: 1 addition & 5 deletions src/cli/commands/add/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,6 @@
return { valid: false, error: '--name is required' };
}

// passthrough is gated; omit it from advertised type lists when the flag is off.
const validTypeList = [
'mcp-server',
'api-gateway',
Expand All @@ -408,7 +407,7 @@
'lambda-function-arn',
'http-runtime',
'connector',
...(isGatedFeaturesEnabled() ? ['passthrough'] : []),
'passthrough',
'web-search',
].join(', ');

Expand Down Expand Up @@ -727,14 +726,11 @@

// Passthrough targets: validate early and return
if (mappedType === 'passthrough') {
if (!isGatedFeaturesEnabled()) {
return { valid: false, error: 'Passthrough targets are not yet available.' };
}
const passthroughEndpoint = (options as Record<string, string | undefined>).passthroughEndpoint;
if (!passthroughEndpoint) {
return { valid: false, error: '--passthrough-endpoint is required for passthrough type' };
}
if (!/^https:\/\/[a-zA-Z0-9\-.]+(:[0-9]{1,5})?(\/.*)?$/.test(passthroughEndpoint)) {

Check warning on line 733 in src/cli/commands/add/validate.ts

View workflow job for this annotation

GitHub Actions / lint

Unsafe Regular Expression
return { valid: false, error: '--passthrough-endpoint must be a valid HTTPS URL' };
}
if (options.language && options.language !== 'Other') {
Expand Down
66 changes: 20 additions & 46 deletions src/cli/primitives/GatewayTargetPrimitive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import {
import type { AddGatewayTargetOptions as CLIAddGatewayTargetOptions } from '../commands/add/types';
import { validateAddGatewayTargetOptions } from '../commands/add/validate';
import { getErrorMessage } from '../errors';
import { isGatedFeaturesEnabled } from '../feature-flags';
import { upsertAgenticRetrieveTarget } from '../operations/knowledge-base/agentic-retrieve-upsert';
import type { RemovableGatewayTarget } from '../operations/remove/remove-gateway-target';
import type { RemovalPreview, SchemaChange } from '../operations/remove/types';
Expand Down Expand Up @@ -63,14 +62,6 @@ import { dirname, join } from 'path';

const MCP_DEFS_FILE = 'mcp-defs.json';

/**
* Hide a passthrough-only CLI option from --help unless gated features are enabled.
* The option is still parsed if passed; the runtime guard in validate.ts rejects it.
*/
function gatePassthroughOption(option: Option): Option {
return isGatedFeaturesEnabled() ? option : option.hideHelp();
}

/**
* Options for adding a gateway target (CLI-level).
*/
Expand Down Expand Up @@ -283,9 +274,8 @@ export class GatewayTargetPrimitive extends BasePrimitive<AddGatewayTargetOption
}

registerCommands(addCmd: Command, removeCmd: Command): void {
const typeDescription = isGatedFeaturesEnabled()
? 'Target type (required): mcp-server, api-gateway, open-api-schema, smithy-model, lambda-function-arn, http-runtime, connector, passthrough, web-search [non-interactive]'
: 'Target type (required): mcp-server, api-gateway, open-api-schema, smithy-model, lambda-function-arn, http-runtime, connector, web-search [non-interactive]';
const typeDescription =
'Target type (required): mcp-server, api-gateway, open-api-schema, smithy-model, lambda-function-arn, http-runtime, connector, passthrough, web-search [non-interactive]';

// Reject repeated use of --exclude-domains. Domains must be passed as a
// single comma-separated value.
Expand Down Expand Up @@ -357,47 +347,35 @@ export class GatewayTargetPrimitive extends BasePrimitive<AddGatewayTargetOption
)
.option('--runtime <name>', 'Runtime from your project (for http-runtime type) [non-interactive]')
.option('--runtime-endpoint <name>', 'Runtime endpoint / version alias (for http-runtime type) [non-interactive]')
// Passthrough-only flags are gated behind ENABLE_GATED_FEATURES — hidden from help when off.
// Passthrough-only flags.
.addOption(
gatePassthroughOption(
new Option('--passthrough-endpoint <url>', 'HTTPS endpoint URL for passthrough targets [non-interactive]')
)
new Option('--passthrough-endpoint <url>', 'HTTPS endpoint URL for passthrough targets [non-interactive]')
)
.addOption(
gatePassthroughOption(
new Option(
'--passthrough-protocol <type>',
'Passthrough protocol: MCP | A2A | INFERENCE | CUSTOM (default: CUSTOM) [non-interactive]'
)
new Option(
'--passthrough-protocol <type>',
'Passthrough protocol: MCP | A2A | INFERENCE | CUSTOM (default: CUSTOM) [non-interactive]'
)
)
.addOption(
gatePassthroughOption(
new Option(
'--stickiness-identifier <expr>',
'Session routing expression for passthrough targets [non-interactive]'
)
new Option(
'--stickiness-identifier <expr>',
'Session routing expression for passthrough targets [non-interactive]'
)
)
.addOption(
gatePassthroughOption(
new Option('--stickiness-timeout <seconds>', 'Sticky session timeout in seconds (1-86400) [non-interactive]')
)
new Option('--stickiness-timeout <seconds>', 'Sticky session timeout in seconds (1-86400) [non-interactive]')
)
.addOption(
gatePassthroughOption(
new Option(
'--signing-service <name>',
'SigV4 signing service name for passthrough GATEWAY_IAM_ROLE auth [non-interactive]'
)
new Option(
'--signing-service <name>',
'SigV4 signing service name for passthrough GATEWAY_IAM_ROLE auth [non-interactive]'
)
)
.addOption(
gatePassthroughOption(
new Option(
'--signing-region <region>',
'SigV4 signing region for passthrough (defaults to project region) [non-interactive]'
)
new Option(
'--signing-region <region>',
'SigV4 signing region for passthrough (defaults to project region) [non-interactive]'
)
)
.option('--json', 'Output as JSON [non-interactive]')
Expand Down Expand Up @@ -430,17 +408,13 @@ Target types and their options:
connector — Wire a managed AWS connector (Bedrock KB, agentic-retrieve)
--connector <id> bedrock-knowledge-bases or bedrock-agentic-retrieve
--knowledge-base-id <id> Project KB name or 10-char external KB id (repeatable for agentic-retrieve)
${
isGatedFeaturesEnabled()
? `

passthrough — Route to an external HTTPS endpoint
--passthrough-endpoint <url> HTTPS endpoint URL
--stickiness-identifier <expr> Session routing expression (optional)
--stickiness-timeout <seconds> Sticky session timeout in seconds (optional)
`
: ''
}
Auth (mcp-server, open-api-schema, smithy-model, lambda-function-arn${isGatedFeaturesEnabled() ? ', passthrough' : ''}):

Auth (mcp-server, open-api-schema, smithy-model, lambda-function-arn, passthrough):
--outbound-auth <type> oauth, api-key, or none
--credential-name <name> Existing credential name
`
Expand Down
15 changes: 5 additions & 10 deletions src/cli/tui/screens/mcp/AddGatewayTargetScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { ApiGatewayHttpMethod, GatewayTargetType, PassthroughProtocolType } from '../../../../schema';
import { REAL_KB_ID_PATTERN, ToolNameSchema } from '../../../../schema';
import { isGatedFeaturesEnabled } from '../../../feature-flags';
import { ConfirmReview, Panel, Screen, StepIndicator, TextInput, WizardSelect } from '../../components';
import type { SelectableItem } from '../../components';
import { HELP_TEXT } from '../../constants';
Expand Down Expand Up @@ -154,15 +153,11 @@ export function AddGatewayTargetScreen({
);
const targetTypeItems: SelectableItem[] = useMemo(
() =>
TARGET_TYPE_OPTIONS.map(o => {
const gated = o.id === 'passthrough' && !isGatedFeaturesEnabled();
return {
id: o.id,
title: o.title,
description: gated ? 'Coming soon' : o.description,
disabled: gated,
};
}),
TARGET_TYPE_OPTIONS.map(o => ({
id: o.id,
title: o.title,
description: o.description,
})),
[]
);
const outboundAuthItems: SelectableItem[] = useMemo(
Expand Down
Loading