Summary
Selecting "MCP Gateway (AgentCore)" (protocol=mcp) on the admin tools form (/admin/tools/new) exposes no configuration fields. The backend accepts protocol="mcp" but stores no config and runs no validation — the option is a non-functional placeholder.
This issue tracks Option B: let an admin register an externally deployed MCP server as a target on the centralized AgentCore Gateway from the admin UI. The tool then flows to agents through the existing Gateway discovery path (gateway_mcp_client.FilteredMCPClient.list_tools_sync()) with zero new agent-side runtime code, while gaining Gateway semantic tool search, centralized SigV4 auth, and Gateway-side observability.
Not to be confused with mcp_external ("MCP External Server"), which already ships and connects the agent directly to an external MCP server (no Gateway). This issue is specifically the Gateway-fronted topology.
Full design: docs/specs/MCP_GATEWAY_TARGET_REGISTRATION_SPEC.md
⛔ Dependency
Blocked on PR #396 (feature/stack-architecture-simplification) merging to develop. #396 relocates the Gateway into infrastructure/lib/constructs/gateway/agentcore-gateway-construct.ts and app-api IAM into infrastructure/lib/constructs/app-api/app-api-iam-grants.ts — the exact files this work edits. Building pre-#396 = guaranteed conflicts. Do not start until #396 lands.
The hard part
Creating a catalog row now has a side effect in AWS (a live Gateway target). Catalog ↔ AWS state must not diverge:
- Create the AWS target first; persist the DynamoDB row only on success.
- Update/delete must reconcile the target; delete AWS target first (404 = already gone), then the row.
- Partial-failure window: v1 keeps repair manual but must log the orphaned identifier loudly.
This lifecycle coupling — not the form — is the real work.
Scope checklist
Backend (apis/shared, app_api)
Infrastructure (post-#396)
Frontend (admin/tools/)
Gotchas
- Listing mode co-gating: 3LO/OAuth + semantic search require
DEFAULT mode; DYNAMIC disables both. Encode in model + form.
- SSM path consistency: runtime client reads
/{project}/{env}/mcp/gateway-url; construct publishes /{prefix}/gateway/url. Confirm the prefix convention for the new /gateway/id param.
- pytest is the only correctness gate — run the full backend suite locally; CI doesn't.
- Credential-provider provisioning from the form is out of scope (reference-by-ARN only in v1).
Suggested PR sequence
- (separate, ships first) Relabel/gate the empty
mcp dropdown until this lands
- Backend model + serialization + tests
GatewayTargetService + lifecycle + stubbed-client tests
- Infra: SSM id param + IAM grants
- Admin route orchestration + validation + tests
- Frontend interface + form section + async UX
Summary
Selecting "MCP Gateway (AgentCore)" (
protocol=mcp) on the admin tools form (/admin/tools/new) exposes no configuration fields. The backend acceptsprotocol="mcp"but stores no config and runs no validation — the option is a non-functional placeholder.This issue tracks Option B: let an admin register an externally deployed MCP server as a target on the centralized AgentCore Gateway from the admin UI. The tool then flows to agents through the existing Gateway discovery path (
gateway_mcp_client.FilteredMCPClient.list_tools_sync()) with zero new agent-side runtime code, while gaining Gateway semantic tool search, centralized SigV4 auth, and Gateway-side observability.Full design:
docs/specs/MCP_GATEWAY_TARGET_REGISTRATION_SPEC.md⛔ Dependency
Blocked on PR #396 (
feature/stack-architecture-simplification) merging todevelop. #396 relocates the Gateway intoinfrastructure/lib/constructs/gateway/agentcore-gateway-construct.tsand app-api IAM intoinfrastructure/lib/constructs/app-api/app-api-iam-grants.ts— the exact files this work edits. Building pre-#396 = guaranteed conflicts. Do not start until #396 lands.The hard part
Creating a catalog row now has a side effect in AWS (a live Gateway target). Catalog ↔ AWS state must not diverge:
This lifecycle coupling — not the form — is the real work.
Scope checklist
Backend (
apis/shared,app_api)MCPGatewayConfigmodel +GatewayListingModeenum (models.py, mirrorMCPServerConfig)ToolDefinition.mcp_gateway_config+to_dynamo_item/from_dynamo_itemMCPGatewayConfigRequest/Response+ToolCreateRequest.to_model()wiringGatewayTargetServicewrappingbedrock-agentcore-control(Create/Update/Delete/Get/ListGatewayTarget); gateway id from SSM/{prefix}/gateway/id. Lives inapis.shared, NOT inference-api.protocol=mcp ⟺ mcp_gateway_configvalidation + create/update/delete lifecycle orchestrationInfrastructure (post-#396)
/{prefix}/gateway/idSSM param from the gateway constructbedrock-agentcore:{Create,Update,Delete,Get,List}GatewayTargetscoped to the Gateway ARN (verify exact action names against the control-plane API ref — silent-no-op precedent exists in this file)ssm:GetParameterfor the new gateway-id paramFrontend (
admin/tools/)MCPGatewayConfigTS interface (matches Python model — same-PR contract)@if (selectedProtocol() === 'mcp')form section: target name, endpoint URL, credential-provider ARN, listing-mode selector, per-tool approval flagsGotchas
DEFAULTmode;DYNAMICdisables both. Encode in model + form./{project}/{env}/mcp/gateway-url; construct publishes/{prefix}/gateway/url. Confirm the prefix convention for the new/gateway/idparam.Suggested PR sequence
mcpdropdown until this landsGatewayTargetService+ lifecycle + stubbed-client tests