Skip to content

Expose configured HTTP routes as Agent Gateway tools - #7572

Merged
Aias00 merged 9 commits into
apache:masterfrom
BobSong-dev:feat/agent-gateway-http-tools
Oct 21, 2026
Merged

Aias00 merged 9 commits into
apache:masterfrom
BobSong-dev:feat/agent-gateway-http-tools

Conversation

@BobSong-dev

Copy link
Copy Markdown
Contributor

Background

Agent Gateway supports Java tool providers and remote MCP servers, but exposing an ordinary HTTP service currently requires a custom provider or a separate MCP server. This adds deployment work for services that already have ShenYu HTTP routes.

This change exposes configured HTTP routes through the existing tools-only MCP entry point. It follows the MCP Server plugin's requestTemplate/argsPosition vocabulary and routing approach without reusing its blocking callback or shared session exchange.

Changes

  • Add opt-in, rule-scoped mcp.httpTools definitions, validated by both Admin and the gateway. Initial support is fixed relative paths, GET query parameters and POST query/flat scalar JSON-body parameters; caller-selected destinations are refused;
  • Dispatch through existing gateway plugins using a forked request-local cursor. Isolate attributes, request headers/body, response, conditional checks, URL transformers and multipart cleanup; do not delegate caller credentials, cookies, web sessions or client TLS identity;
  • Reuse tool authorization, result filtering and logical MCP call observation. Link cancellation/deadlines to the internal HTTP chain, bound response capture and refuse redirects, unsuccessful statuses or unsafe JSON without adapter retries;
  • Capture immutable rule definitions per invocation, allowing synchronized rule changes without per-tool Java Beans or gateway restarts. Retain existing Java Provider extensions and prior behavior when HTTP plans are absent;
  • Add configuration, Admin validation, routing, isolation and namespace/collision regression tests, plus configuration documentation and release notes. No production dependency on MCP Server or Spring AI is added.

Verification

  • Windows selected clean verify for Admin, Agent Gateway starter, Metrics and Web with required reactor dependencies: 688 tests / 40 suites, zero failures/errors/skips; Checkstyle and RAT passed in 46 modules. The tested snapshot is based on master 4835f40f8 / Gson 2.13.2; its Git tree matches this commit exactly;
  • The same feature revision on the earlier merged base 2dff2730f: Windows 688 tests and RISC-V/QEMU verify 626 tests / 37 suites, zero failures/errors/skips. Linux Checkstyle/RAT passed in 15 modules; all 4,442 frozen source files matched;
  • Real Admin/H2 → WebSocket → Divide/Netty/Response → ordinary HTTP fixture: 27 checks passed, including authorization, hot updates, 32 same-ID concurrent calls, result filtering, cancellation and JSON/SSE deadlines;
  • OpenCode 2.0.18 with DeepSeek V4 Flash: JSON, SSE and SSE Code Mode passed, with six actual read-only GET/POST fixture requests correlated to tool outputs and subsequent model messages.

@BobSong-dev
BobSong-dev marked this pull request as draft October 12, 2026 14:16
Aias00
Aias00 previously approved these changes Oct 12, 2026

@Aias00 Aias00 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.

Approve. This opt-in mcp.httpTools feature exposes fixed, configured gateway HTTP routes as MCP tools, and the implementation holds the security bar set by the neighboring Agent Gateway work.

What I verified in the diff:

  • Fixed routes only. AgentGatewayHttpToolConfig.Tool.requestPath builds the target from a validated path matched by /[A-Za-z0-9_/-]+ (no //); the caller's JSON can only populate query/body via argsPosition, and requestPath re-validates and URL-encodes values — the path itself can never be replaced by caller input. method is restricted to GET/POST, and GET cannot map a body.
  • No caller identity delegation. AgentHttpToolExecutor builds a ChildExchange/ToolRequest with empty headers, empty cookies, null SslInfo, and getSession()/getPrincipal() return empty/error — caller credentials, cookies and web sessions are deliberately not forwarded. The internal-only markers HTTP_TOOL_CALL_ATTRIBUTE / MCP_TOOL_CALL are set so the existing MCP Server plugin routes it as an internal call.
  • Reuses the existing chain. chain.fork().execute(child) runs the request through the normal plugin chain, so existing MCP authorization, filtering and terminal metrics still apply.
  • Bounded, validated response. CapturedResponse enforces maxResponseBytes, rejects any response cookies (no session fixation), requires a UTF-8 JSON body, and parses with strict duplicate-key detection and a nesting-depth/string-length cap. Non-2xx completes are rejected.
  • Bounded config parsing. Tool count ≤16, properties ≤16, name/field/value length caps, closed additionalProperties, and strict scalar types — consistent with the rest of the Agent Gateway config hardening.
  • Tests cover valid/invalid plan validation and the executor's isolated dispatch.

Non-blocking note: the rewrite also touches ShenyuPluginChain.fork() and ShenyuWebHandler to support the internal marker; please keep the integration/e2e Agent Gateway CI green so the fork() contract stays verified.

@BobSong-dev
BobSong-dev marked this pull request as ready for review October 13, 2026 11:08
Aias00
Aias00 previously approved these changes Oct 17, 2026

@Aias00 Aias00 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.

Summary

Fresh review of the current head d80bda94e61ad6fbe2cf145b120b77a01525200a (open, not draft, author BobSong-dev). The change exposes configured gateway HTTP routes as Agent Gateway tools via opt-in mcp.httpTools, without a Java Provider per tool.

What the change does

  • AgentGatewayHttpToolConfig (new, shenyu-common/.../dto/AgentGatewayHttpToolConfig.java) parses and freezes bounded plans. parse (AgentGatewayHttpToolConfig.java:107) rejects foreign names (:118), caps tool count at 16 (:112), and Tool's constructor validates the fixed relative path /[A-Za-z0-9_/-]+ with no // (:228), restricts methods to GET/POST, forbids body mapping on GET (:238), and requires the argsPosition key set to equal the schema property set (:232). getInputSchema returns a deep copy (:254).
  • AgentGatewayMcpConfig adds httpTools to the accepted field set and parses it against allowedTools (AgentGatewayMcpConfig.java:64).
  • AgentHttpToolExecutor (new) dispatches through chain.fork() with a fresh request-local cursor (AgentHttpToolExecutor.java:149), replays the configured route with private empty headers/body/cookies and no principal/session (:918–:979), captures a bounded response (CapturedResponse join capped at maxResponseBytes, :1098), and refuses non-2xx, cookies, absent completion and non-UTF-8/non-JSON bodies (:1115–:1142). Request budget is enforced on both body and generated path (:908–:914).
  • AgentHttpToolProvider reuses the existing authorization/result-filter path and maps deadline timeouts to a typed marker (AgentHttpToolProvider.java:60); AgentMcpDispatcher binds the captured providers per request and maps the deadline marker to HTTP 504 / JSON-RPC -32025 (AgentMcpDispatcher.java:83, :99).
  • ShenyuPluginChain.fork() default throws UnsupportedOperationException (source/binary-safe default), implemented in ShenyuWebHandler.DefaultShenyuPluginChain.fork() at ShenyuWebHandler.java:298. AgentGatewayPlugin.skip() (AgentGatewayPlugin.java:130) short-circuits the internal child dispatch via HTTP_TOOL_CALL_ATTRIBUTE, and the child also sets the existing MCP_TOOL_CALL marker (AgentHttpToolExecutor.java:142) consumed by McpServerPlugin (McpServerPlugin.java:206).

Verification

  • Compared against master via the GitHub contents API: ShenyuPluginChain on master has no fork(); the head adds a throwing default, so existing implementers are unaffected. AgentMcpDispatcher/AgentGatewayPlugin pre-change signatures confirmed.
  • Local: mvn -pl shenyu-common,shenyu-plugin/shenyu-plugin-agent-gateway -am test → BUILD SUCCESS. AgentGatewayHttpToolConfigTest 21/21, AgentGatewayMcpConfigTest 28/28, agent-gateway module 512/512 (incl. AgentHttpToolRoutingTest 11, AgentHttpToolExecutorTest 23, AgentHttpToolNamespaceTest 2), shenyu-web 93/93. Zero failures/errors/skips.
  • CI: all relevant checks green — pr_build, Analyze (java), check-license-header, rat_check, scope, sql-matrix, registry_dependencies, agent_gateway_migrations, and every build (shenyu-integrated-test-*) and e2e-case/e2e-storage. No red to attribute.

Notes (non-blocking)

  • fork() is a new SPI on ShenyuPluginChain; custom chains that do not implement it fail closed for internal HTTP invocation only (AgentHttpToolExecutorTest.customChainsMustOptIntoForking), leaving ordinary traffic unchanged. Adding it as a documented, deliberate extension point is reasonable.
  • shenyu-web is added as a test-scope dependency of the agent-gateway module only; no production dependency is introduced.

Conclusion

APPROVE. Correctness, isolation and bounded-input handling are well covered by tests, the change is opt-in with previous behavior preserved when httpTools is absent, and CI plus local module tests pass.

@Aias00 Aias00 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.

Re-approving after the master merge dismissed the previous approval.

I checked the update rather than re-reading the whole PR: the only new commit is a merge of master, and every file this PR introduces is byte-identical to the version I approved (compared the blob shas at 71fe4acf/d80bda94/c831b431/58f16918 against the current head). The files that did change are the ones master touched in parallel — RELEASE-NOTES.md, the other agent-gateway sources, and AbstractShenyuPluginTest's setLogged rename — i.e. the merge brought them in, it did not rewrite this PR's content.

CI is green on the new head (52 pass / 3 skipping, no failures). No further changes requested from me.

@Aias00
Aias00 merged commit 381f75a into apache:master Oct 21, 2026
55 checks passed
@Aias00 Aias00 added this to the 2.7.2 milestone Nov 13, 2026
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.

2 participants