Skip to content

Admin-managed AgentCore Gateway target registration (protocol=mcp) #419

@philmerrell

Description

@philmerrell

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)

  • MCPGatewayConfig model + GatewayListingMode enum (models.py, mirror MCPServerConfig)
  • ToolDefinition.mcp_gateway_config + to_dynamo_item/from_dynamo_item
  • MCPGatewayConfigRequest/Response + ToolCreateRequest.to_model() wiring
  • GatewayTargetService wrapping bedrock-agentcore-control (Create/Update/Delete/Get/ListGatewayTarget); gateway id from SSM /{prefix}/gateway/id. Lives in apis.shared, NOT inference-api.
  • Admin route: protocol=mcp ⟺ mcp_gateway_config validation + create/update/delete lifecycle orchestration

Infrastructure (post-#396)

  • Publish /{prefix}/gateway/id SSM param from the gateway construct
  • app-api IAM: bedrock-agentcore:{Create,Update,Delete,Get,List}GatewayTarget scoped to the Gateway ARN (verify exact action names against the control-plane API ref — silent-no-op precedent exists in this file)
  • app-api ssm:GetParameter for the new gateway-id param

Frontend (admin/tools/)

  • MCPGatewayConfig TS 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 flags
  • Async/pending UX; distinguish 502 (target create failed) from 400 (validation)

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

  1. (separate, ships first) Relabel/gate the empty mcp dropdown until this lands
  2. Backend model + serialization + tests
  3. GatewayTargetService + lifecycle + stubbed-client tests
  4. Infra: SSM id param + IAM grants
  5. Admin route orchestration + validation + tests
  6. Frontend interface + form section + async UX

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions