Skip to content

feat(tools): Gateway MCP targets — self-service registration, agent wiring & health UX (#419)#457

Merged
philmerrell merged 3 commits into
developfrom
fix/gateway-mcpserver-iam-credential
Jun 6, 2026
Merged

feat(tools): Gateway MCP targets — self-service registration, agent wiring & health UX (#419)#457
philmerrell merged 3 commits into
developfrom
fix/gateway-mcpserver-iam-credential

Conversation

@philmerrell
Copy link
Copy Markdown
Contributor

@philmerrell philmerrell commented Jun 5, 2026

Builds on the original mcpServer credential-config fix (None + IAM service) into an end-to-end, admin-self-service path for registering external MCP servers as AgentCore Gateway targets — and surfaces the failures that were previously silent.

The problem

A registered + enabled protocol=mcp tool (gateway_class_search) produced "I don't have that tool" from the agent. Investigation found three stacked blockers, none of them obvious:

  1. Two gateways. The agent resolved a hardcoded /strands-agent-chatbot/dev/… gateway; the admin form registered on the CDK gateway /{PROJECT_PREFIX}/gateway/id. Targets landed on a gateway the agent never connected to.
  2. Gateway role couldn't invoke the target. The target's Lambda Function URL is AuthType=AWS_IAM; the gateway role lacked lambda:InvokeFunctionUrl (a distinct action from InvokeFunction) and the {prefix}-mcp-* wildcard didn't match the mcp-<name>-<env> naming → target stuck FAILED.
  3. Agent-side ID mismatch. The gateway exposes <targetName>___<toolName>, but the catalog tool id gateway_class_search matched none of them → silent drop.

What this PR does

Unify the gateway (Blocker 0) — shared gateway_identity.resolve_gateway_id (one source of truth: AGENTCORE_GATEWAY_ID override → SSM /{PROJECT_PREFIX}/gateway/id); the agent and GatewayTargetService both use it, so they can't diverge.

Expand catalog tools (Blocker 2)base_agent expands a protocol=mcp tool into the gateway's runtime gateway_<target>___<tool> ids so the FilteredMCPClient matches; raw RBAC gateway ids pass through.

Self-service per-target IAM grant (Blocker 1, done right) — instead of a standing wildcard on the gateway role (an infra change per off-convention server), app-api grants the gateway role InvokeFunctionUrl on exactly the registered function at registration (lambda:AddPermission naming the role — a role-specific resource grant authorizes it same-account, no identity grant) and revokes it on delete. So an admin registers any same-account Lambda-URL MCP server through the form with no infra change, and the gateway role can invoke only what was explicitly registered.

  • Cross-account validation: GetFunctionUrlConfig confirms same-account + that the endpoint is under the function URL; otherwise a 400 tells the admin to make it public or use a credential provider.
  • CDK: gateway role loses its Lambda-invoke wildcard (logs only); app-api gains scoped AddPermission/RemovePermission/GetFunctionUrlConfig + GetGateway.
  • New MCPGatewayConfig.lambda_function_name + a conditional "Lambda function name" form field (shown only for Lambda-URL endpoints).

Health surfacingGET /admin/tools/{id}/gateway-status (+ statusReasons) and a Ready/Syncing/Failed/Missing badge on the tools list, so a failed target sync is visible instead of only appearing as a missing tool.

Discovery + form polish — MCP discovery errors unwrap the real upstream status (403 stays 403 with an actionable message; 401→400 to avoid tripping the SPA logout); awsService/awsRegion auto-derive from the endpoint; IAM is recommended when an AWS host is left on None.

Security posture

The gateway's inbound SigV4 (authorizerType=AWS_IAM) is free and unchanged. Outbound IAM is the boundary that keeps an MCP server from being a public bypass of RBAC/approval — kept, but moved from a standing wildcard to a least-privilege per-target grant. Same-account Lambda-URL targets only; cross-account / API-Gateway / custom-domain targets must be public or use a credential provider (the form says so).

Testing

  • Backend: 3497 passed, 3 skipped (full suite); new suites for the resolver, tool-id expansion, the Lambda grant, and discovery errors.
  • Frontend: 30 admin-tools specs (badge mapping, IAM recommend, Lambda-name wiring); full ng build type-checks.
  • Infra: 80 jest (gateway role has no standing invoke; app-api has the scoped grant); tsc clean.
  • Live (dev): target FAILED → READY on the per-target grant alone; agent loads all 9 class-search tools end-to-end; gateway_class_search migrated to the new scheme.

Deploy

  1. platform.yml (CDK role changes — dev gateway is already in the end-state).
  2. backend.yml (new code).

🤖 Generated with Claude Code

philmerrell and others added 3 commits June 5, 2026 16:38
…ervice) (#419)

CreateGatewayTarget rejected our GATEWAY_IAM_ROLE targets with
"IamCredentialProvider is required for mcpServer targets using IAM
authentication": unlike OpenAPI/Lambda targets (which accept a bare
GATEWAY_IAM_ROLE), an mcpServer (HTTP endpoint) target must supply an explicit
iamCredentialProvider naming the AWS service to sign for. We were sending
`[{credentialProviderType: GATEWAY_IAM_ROLE}]` with no nested provider.

- New credential type NONE (public endpoint) — omits credentialProviderConfigurations
  entirely (the API parameter is optional). This is now the default (least-config
  path; the admin opts into IAM/OAuth/API-key).
- GATEWAY_IAM_ROLE now carries aws_service (required) + optional aws_region, sent as
  credentialProvider.iamCredentialProvider{service, region?}. Model validator
  requires aws_service for IAM and the create/update calls omit the credential
  block for NONE.
- Backend: MCPGatewayConfig + request/response models + GatewayTargetService.
- Frontend: 'none' credential option (default), AWS service/region fields shown for
  the IAM case, build/restore wiring, and the discover auth mapping unchanged
  (gateway_iam_role → aws-iam, else none).

Verified: full backend suite 3466 passed; tool-form gateway spec 6/6 (ng test).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…419 catalog tools (#419)

The agent resolved the gateway from a hardcoded `/strands-agent-chatbot/dev/...`
SSM param, while the admin form (#419) registered targets on the CDK gateway
`/{PROJECT_PREFIX}/gateway/id` — two different gateways, so admin-registered
tools never reached the agent.

- New shared `gateway_identity.resolve_gateway_id`/`gateway_url_from_id`: one
  source of truth (explicit id → AGENTCORE_GATEWAY_ID override → SSM
  `/{PROJECT_PREFIX}/gateway/id`). The agent's `get_gateway_url_from_ssm` and
  GatewayTargetService both resolve through it, so they can't diverge.
- Expand a `protocol=mcp` catalog tool (`gateway_class_search`) into the
  gateway's runtime per-tool ids (`gateway_<targetName>___<toolName>`) in
  base_agent, so the FilteredMCPClient matches them; raw RBAC gateway ids
  (already containing `___`) pass through unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rant, health, discovery UX (#419)

Make admins able to register any same-account Lambda-URL MCP server through the
form with no infra change, and surface the failures that were previously silent.

Per-target Lambda grant (replaces the gateway role's standing `mcp-*` wildcard):
- app-api grants the gateway role `lambda:InvokeFunctionUrl` on exactly the
  registered function at registration (`AddPermission` naming the role as
  Principal — a role-specific resource grant authorizes it same-account, no
  identity grant) and revokes it on delete. `gateway_lambda_grant.py` +
  GatewayTargetService lifecycle; new `MCPGatewayConfig.lambda_function_name`
  and a conditional "Lambda function name" form field.
- Cross-account validation: `GetFunctionUrlConfig` confirms same-account + that
  the endpoint is under the function URL; otherwise a 400 tells the admin to go
  public or use a credential provider.
- CDK: gateway role loses its Lambda-invoke wildcard (logs only); app-api gains
  scoped `AddPermission`/`RemovePermission`/`GetFunctionUrlConfig` + `GetGateway`.

Gateway target health: `GET /admin/tools/{id}/gateway-status` (+ statusReasons
on GatewayTargetInfo) and a Ready/Syncing/Failed/Missing badge on the tools
list, so a FAILED target sync is visible instead of only "the agent can't see
the tool".

Discovery + form polish: unwrap the upstream HTTP status from MCP discovery
errors (403 stays 403 with an actionable message; 401→400 to avoid the SPA
logout); auto-derive awsService/awsRegion from the endpoint and recommend IAM
when an AWS host is left on None.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@philmerrell philmerrell changed the title fix(tools): correct Gateway mcpServer credential config — None + IAM service (#419) feat(tools): Gateway MCP targets — self-service registration, agent wiring & health UX (#419) Jun 6, 2026
@philmerrell philmerrell merged commit 470dc5c into develop Jun 6, 2026
1 check passed
@philmerrell philmerrell deleted the fix/gateway-mcpserver-iam-credential branch June 6, 2026 02:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant