Skip to content

fix(export): export agentcore_gateway tools from harnesses - #2768

Open
tejaskash wants to merge 1 commit into
v1.xfrom
fix/export-harness-gateway-tools
Open

tejaskash wants to merge 1 commit into
v1.xfrom
fix/export-harness-gateway-tools

Conversation

@tejaskash

@tejaskash tejaskash commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

What

agentcore export harness now exports agentcore_gateway tools instead of replacing them with an EXPORT_NOTES entry. Each gateway tool becomes an MCP client in mcp_client/client.py and is added to the agent's tools in main.py. This applies to both --name and --arn exports.

  • The URL is read from GATEWAY_<TOOL>_URL, named after the tool rather than the gateway id.
  • awsIam (also the default when outboundAuth is unset) signs requests with SigV4 for bedrock-agentcore through mcp-proxy-for-aws. none connects without auth. OAuth with CLIENT_CREDENTIALS sends a bearer token from @requires_access_token.
  • allowedTools selectors such as @tools/* filter the gateway's tools the same way they filter remote MCP tools.
  • The exported runtime gets one gateway connection per tool, with id set to the tool name. Deploy then grants access and sets the URL through the existing @aws/agentcore-cdk 1.0.0 connection binding. The URL is also written to agentcore/.env.local for agentcore dev.
  • USER_FEDERATION gateway tools still get the manual follow-up note.

Why

Closes #2766. 0.31.x exported gateway tools, and 1.x dropped them.

How tested

  • Unit tests for the mapper (each auth mode, allowedTools narrowing, USER_FEDERATION note, invalid names, connection id collisions, non-gateway ARNs) and a rendered-tree test.
  • bun test, typecheck, lint, and format all pass.
  • End to end with the built CLI in us-west-2, using the issue's repro steps against two real gateways (awsIam and none). agentcore dev loaded the tools of both gateways and made a SigV4 tool call. The deployed runtime had GATEWAY_TOOLS_URL set by deploy, and invoking it made a successful SigV4 gateway tool call. The stack was torn down afterwards.
  • The OAuth client-credentials path is covered by unit tests only. It was not run against a live OAuth gateway.

Harness export dropped every agentcore_gateway tool with a manual follow-up
note, while 0.31.x generated an MCP client for it. Each gateway tool now
becomes an MCP client in mcp_client/client.py that reads GATEWAY_<TOOL>_URL,
signs requests with SigV4 for awsIam, sends a bearer token for OAuth client
credentials, and honors @tool/* allowedTools selectors. The exported runtime
gets a gateway connection per tool, so deploy grants access and sets the URL,
and agentcore/.env.local carries the URL for agentcore dev.

Closes #2766
@tejaskash
tejaskash requested a review from a team November 5, 2026 18:48
@github-actions github-actions Bot added the size/m PR size: M label Nov 5, 2026
@agentcore-devx-automation agentcore-devx-automation Bot added agentcore-harness-reviewing AgentCore Harness review in progress claude-security-reviewing Claude Code /security-review in progress labels Nov 5, 2026
@github-actions

github-actions Bot commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

Package Tarball

aws-agentcore-1.0.1.tgz

How to install

gh release download pr-2768-tarball --repo aws/agentcore-cli --pattern "*.tgz" --dir /tmp/pr-tarball
npm install -g /tmp/pr-tarball/aws-agentcore-1.0.1.tgz

@agentcore-devx-automation

Copy link
Copy Markdown
Contributor

Claude Security Review: no high-confidence findings. (run)

@agentcore-devx-automation agentcore-devx-automation Bot removed claude-security-reviewing Claude Code /security-review in progress agentcore-harness-reviewing AgentCore Harness review in progress labels Nov 5, 2026

@agentcore-devx-automation agentcore-devx-automation Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AgentCore Harness Review

Verdict: Changes requested

I verified the deploy-side contract against the pinned @aws/agentcore-cdk@1.0.0 tarball and it lines up:
bindConnection → importedDiscoveryPrefix('GATEWAY', connectionTokenFor(connection)) produces exactly
GATEWAY_<TOKEN>_URL with the same connectionEnvToken derivation, gatewayUrlFromArn builds the same
https://<id>.gateway.bedrock-agentcore.<region>.<urlSuffix>/mcp, and ImportedGateway maps
unset/awsIam → AWS_IAM + InvokeGateway, none → NONE, oauth → CUSTOM_JWT + the OAuth token grants —
which is what each rendered client branch needs. mcp_proxy_for_aws.client.aws_iam_streamablehttp_client(endpoint, aws_service, aws_region) exists at the 1.1.0 floor, and its Python lower bound (3.10) matches the exported project's
requires-python, so uv resolution is fine. requires_access_token's sync wrapper handles being called from a
running loop, so fetching the bearer token inside transport() works. Two things to fix.

1. The gateway path can now abort an otherwise-exportable harness (src/core/project/templates/export.ts)

Before this PR, nothing about a gateway tool could fail an export — it degraded to an EXPORT_NOTES.md entry. Now
three paths throw InputValidationError and kill the whole command:

  • L694-699: CONNECTION_ID_PATTERN rejects any tool name that doesn't start with a letter. HarnessToolNameSchema is
    ^[a-zA-Z0-9_-]+$, so 3p-tools or _tools is a perfectly valid harness tool name.
  • L762-772: two tool names that tokenize to the same env token (my-tools + my_tools), or a tool name that collides
    with an existing harness connection id.
  • L786-793 (gatewayUrl): a gatewayArn that isn't an ARN. The harness schema only requires z.string().min(1).

The message tells the user to "Rename the tool in the harness, then export again", but on the --arn path the spec
comes from the service (serviceHarness.ts L75: name: tool.name ?? tool.type), so there is nothing local to rename —
a single oddly-named gateway tool makes agentcore export harness --arn ... unusable with no workaround. Options:

  1. Degrade to GATEWAY_TOOL_NOTE_CATEGORY and skip that one tool, exactly as the USER_FEDERATION branch already does
    (L686-699) — the rest of the harness still exports.
  2. Derive a schema-valid connection id instead of requiring the tool name to be one (e.g. prefix/sanitize, and
    disambiguate on collision), keeping name only for the Python docstrings/log text.
  3. Keep the throw only for the in-project --name path, where the user can actually edit harness.yaml.

Related, non-blocking: the collision check (L762-764) skips spec.connections entries without an id, but the CDK
still tokenizes those via connectionTokenFor → connectionIdForTarget, so an id-less connection can still shadow a
gateway tool's variable silently.

2. pyproject.toml loses its indentation for every export

src/assets/templates/export-harness-python/pyproject.toml L16-17:

    {{#if (includes gatewayAuthTypes "AWS_IAM")}}"mcp-proxy-for-aws >= 1.1.0, < 2.0.0",
    {{/if}}    {{#if bedrockMantle}}"aws-bedrock-token-generator >= 1.1.0, < 2.0.0",

Neither {{#if}} nor {{/if}} is standalone on its line, so the four spaces after {{/if}} are emitted literally and
concatenate with the four before the next block. Every exported pyproject.toml — gateway tools or not — now renders
"strands-agents ... " with eight spaces. Dropping the stray four spaces after {{/if}} restores the existing
pattern. pythonSecurity.test.ts only parses the TOML, so nothing catches this.

Minor

connectionEnvToken and CONNECTION_ID_PATTERN (L778-783) are a third copy of a contract that
src/projectSchemas/v0/schemas/connections.ts explicitly marks as the single source of truth ("do not re-implement the
token derivation") and whose regex also lives in ConnectionSchema in src/projectSchemas/connections.ts. Exporting
the helper (and reusing the schema's regex) from the v1 connections.ts would keep the CLI↔CDK handshake in one place.

@nyousef7

nyousef7 commented Nov 5, 2026

Copy link
Copy Markdown

Thanks for picking this up so quickly. While waiting, we wired a SigV4 gateway client into a 1.0.1 export by hand, and the approach here matches what worked for us. A few things I noticed reading the diff:

  1. OAuth token lifetime. The bearer token is fetched once, when the transport is created (client.py#L52-L56). The MCPClient sits in the module-level tools list and stays connected for the life of the process, so if the token expires before the process does, tool calls start failing with 401. Would an httpx.Auth that calls _get_<tool>_token() per request work here? requires_access_token already caches the token, so it should be cheap.

  2. Tool name prefix. The gateway MCPClient is created without prefix= (client.py#L58-L60), so tools come through as <target>___<tool>. Two gateway tools whose gateways each have a target with the same name would collide. Does the managed harness prefix gateway tools with the harness tool name? If it does, the exported agent sees different tool names than the harness did, which matters for system prompts that refer to tools by name.

  3. Dependency footprint. mcp-proxy-for-aws 1.8.0 requires fastmcp>=3.2,<3.5, which pulls in a full MCP server framework (starlette, authlib, keyring, rich, and more) for what the export uses as one httpx.Auth. Not a blocker, but a small SigV4 httpx.Auth in the template would keep the exported agent's dependency tree much smaller. Was that considered?

Also, the verify failures look like the audit script rather than anything in this diff.

@github-actions github-actions Bot added size/m PR size: M and removed size/m PR size: M labels Nov 5, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m PR size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

export harness: 1.x drops agentcore_gateway tools that 0.31 exported

2 participants