Skip to content

OSPP2: Add local and federated MCP resources - #7669

Merged
Aias00 merged 8 commits into
apache:masterfrom
BobSong-dev:feat/agent-gateway-resources
Oct 26, 2026
Merged

Aias00 merged 8 commits into
apache:masterfrom
BobSong-dev:feat/agent-gateway-resources

Conversation

@BobSong-dev

@BobSong-dev BobSong-dev commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Background

The Agent Gateway currently aggregates MCP tools, but cannot expose resources from local providers or remote MCP servers through the same request-scoped entry. This draft adds the resources part of the OSPP scope without expanding the merged tools-only PR or depending on other unmerged PRs.

Changes

  • Add resources/list, resources/templates/list and resources/read for explicit local providers and managed remote servers;
  • Intersect trusted resourceGrants with rule-level allowedResources, independently of tool permissions. Admin validates resource grants before saving;
  • Prefix remote permission names with server.name and expose reversible mcp-resource://server/... identifiers. Resource URIs are identifiers, not caller-selected HTTP or file destinations;
  • Reuse the existing request-owned transport, fixed service credentials and generation leases. Hot updates preserve in-flight reads; cancellation releases only the owning request;
  • Keep remote resource discovery disabled by default. Operators must explicitly enable shenyu.plugins.agent.gateway.aggregation.resources-enabled;
  • Add regression tests and document configuration, limits and compatibility.

Verification

  • Windows/JDK 17: targeted Maven Wrapper clean verify across Admin, Agent Gateway, Starter and dependencies: 654 tests / 35 suites, 0 failures, errors or skipped tests; all 38 reactor modules passed Checkstyle and RAT;
  • Linux RISC-V64 under QEMU/JDK 17: targeted Maven Wrapper clean verify across Agent Gateway, Starter and dependencies: 620 tests / 33 suites, 0 failures, errors or skipped tests; all 15 reactor modules passed Checkstyle and RAT. Admin is not included in this run;
  • Owned Admin/H2 → WebSocket → gateway → two loopback MCP servers: 20 checks passed for JSON/SSE discovery and reads, authorization, rule updates, same-ID request isolation, invalid configuration, timeout/disconnect cancellation, generation pinning and cleanup;
  • Frozen source and test-report checks passed; git diff --check passed. A subsequent README clarification changed documentation only.

@BobSong-dev
BobSong-dev marked this pull request as ready for review October 17, 2026 03:00
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.

Review: OSPP2: Add local and federated MCP resources

Verdict: APPROVE

This is a large but well-engineered feature addition that exposes MCP resources/list, resources/templates/list and resources/read through the existing Agent Gateway entry point, with an independent, deny-by-default authorization model. I read the core new classes, traced the full request path, ran the new test classes locally, and reviewed CI.

Security model (the strongest part of this PR)

The design consistently treats the resource URI as an identifier, never a destination, and backs it with multiple independent guards:

  • No caller-selected endpoints. RemoteResourceDirectory.read resolves the requested URI to a registered Entry, then calls that entry's fixed Target.reads callback (bound to the configured server). The URI is never used to open a connection or read a file. The remote server endpoint itself is constrained by ManagedRemoteMcpCatalog.allowedEndpoints + RemoteTransportPolicy.endpoint, so even the remote hop is allowlisted.
  • Canonical, fixed-scheme identifiers. AgentResourceNamespace.expose builds mcp-resource://<server>/<encoded> where server is validated [A-Za-z0-9_-]{1,48} and original is percent-encoded via UriUtils.encode; original() requires a round-trip-equal canonical form, rejecting encoding tricks. AgentResourceContent further requires the URI to be URI.create(...).isAbsolute() with no userinfo and no whitespace/control chars (no user:pass@host, no file: smuggling) — and explicitly "is never dereferenced".
  • Independent, intersected authorization. AgentMcpExecutionContext computes allowedResources = ruleResources ∩ resourceGrants and allowedTools = ruleTools ∩ securityGrants. AgentMcpResourceDispatcher and RemoteResourceDirectory both re-check allowedResources.contains(name) and definition.matches(uri); ambiguous matches (size != 1) are rejected with 403. Admin rejects wildcard * and non-unique/blank grant values (AgentGatewayMcpConfig.strings), and the resources capability is only advertised when ruleResources is non-empty.
  • Bounded everywhere. Local registry ≤ 16 providers; remote directory ≤ 16 entries; pagination capped at 4 pages with cycle detection; contents arrays capped at 16; URI/description ≤ 4096; config ≤ 16384 bytes with queue backpressure. AgentResourceContent.rewrite also verifies each returned content's URI equals the requested original (confused-deputy protection) and transforms it to the exposed identifier.
  • Strict request parsing. AgentMcpRequestParser enforces JSON-RPC 2.0 shape, strict duplicate detection, max nesting depth 64, trailing-token rejection, strict UTF-8, body byte limit, and verifies mirrored Mcp-Name/Mcp-Method/MCP-Protocol-Version headers match the body (anti-header-injection). resources/read URI is validated as an absolute identifier before dispatch.

Correctness & lifecycle

  • Hot updates preserve in-flight reads: ManagedRemoteMcpCatalog uses a single owned control-plane subscription, revision fencing, and a request-owned Snapshot; closeAsync drains before exit. AgentMcpDispatcher.dispatch is request-local, no retry, and maps SecurityException/CatalogUnavailableException to structured 403/503 errors.
  • Remote resource discovery is disabled by default (resourcesEnabled flows into RemoteCatalogLifecycle), exactly as documented.

Verification

  • Local: mvn -pl shenyu-plugin/shenyu-plugin-agent-gateway -am test for the resource-related suites → 170 tests, 0 failures, 0 errors (AgentMcpResourceIntegrationTest, RemoteResourceDirectoryTest, RemoteResourceLifecycleTest, RemoteResourceNetworkTest, AgentResourceContentTest, AgentResourceRegistryTest, AgentMcpRequestParserTest, AgentMcpHttpHandlerTest, plus existing suites). BUILD SUCCESS.
  • CI: pr_build, Analyze (java), CodeQL, list-contract, native-sql (all 5 matrix entries), sql-matrix, scope, check-license-header, rat_check, and the storage e2e matrix (h2/mysql/opengauss/postgres) all pass. The only reds are the e2e aggregate and several e2e-case *sync-compose / logging-rocketmq jobs, which are orthogonal to this PR (Agent Gateway MCP resources; remote discovery off by default) and are known-infra-flaky — the e2e-http-sync-compose job log I inspected actually shows BUILD SUCCESS. None of the failing cases exercise agent-gateway resource code.

Non-blocking suggestions

  1. Minor dead call: AgentResourceRegistry.read (line ~104) calls AgentResourceContent.text(uri, null, "") and discards the result — it serves only as a URI-validation side effect. Harmless, but a named validateUri helper would read more clearly.
  2. Large feature surface: at +2346/−28 this is a substantial addition; the per-class encapsulation (registry / directory / namespace / content / identity) is clean and test-covered, so I see no structural concern, only a note that ongoing hardening (e.g. further e2e coverage for the federated read path) can follow as separate PRs.

No changes requested. The implementation is careful, security-conscious, and well tested.

Aias00
Aias00 previously approved these changes Oct 21, 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.

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 commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

This branch now has merge conflicts with master — merging the sibling agent-gateway PR (#7572) touched the same AgentMcpDispatcher / AgentMcpExecutionContext / AgentMcpHttpHandler files, so mergePullRequest is rejected.

Could you rebase onto the latest master (or merge it in) and re-run the checks? My approval from the previous review stands on the merits; I only need the branch resolved and green again before merging.

…resources

# Conflicts:
#	RELEASE-NOTES.md
#	shenyu-admin/src/test/java/org/apache/shenyu/admin/validation/validator/AgentGatewayRuleHandleValidatorTest.java
#	shenyu-common/src/main/java/org/apache/shenyu/common/dto/AgentGatewayMcpConfig.java
#	shenyu-plugin/shenyu-plugin-agent-gateway/README.md
#	shenyu-plugin/shenyu-plugin-agent-gateway/src/main/java/org/apache/shenyu/plugin/agent/gateway/protocol/AgentMcpDispatcher.java
#	shenyu-plugin/shenyu-plugin-agent-gateway/src/main/java/org/apache/shenyu/plugin/agent/gateway/protocol/AgentMcpExecutionContext.java
#	shenyu-plugin/shenyu-plugin-agent-gateway/src/main/java/org/apache/shenyu/plugin/agent/gateway/protocol/AgentMcpHttpHandler.java
#	shenyu-spring-boot-starter/shenyu-spring-boot-starter-plugin/shenyu-spring-boot-starter-plugin-agent-gateway/src/main/java/org/apache/shenyu/springboot/starter/plugin/agent/gateway/AgentGatewayPluginConfiguration.java

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

Thanks for this contribution. The MCP resources support is cleanly layered: a new resource package (AgentResourceRegistry, AgentResourceDefinition, AgentResourceInvocation, AgentResourceContent, AgentResourceNamespace, AgentResourceProvider), an AgentMcpResourceDispatcher that maps resources/* MCP methods, capability advertisement gated on ruleResources being non-empty, and the remote-catalog path also validates resources before dispatch. The dispatcher routing (validate -> resources/ branch before route) reads correctly and the SecurityException -> 403 mapping is appropriate.

Notes (non-blocking):

  • This PR and #8095 both modify AgentMcpDispatcher.java (resource handling / admission). Please coordinate the merge order and rebase so the two do not collide on the dispatcher.
  • Consider adding a unit test that exercises AgentMcpResourceDispatcher.dispatch with an allowed vs denied resource to lock in the 403 path.

Approving. CI is green.

@Aias00
Aias00 merged commit e0578b9 into apache:master Oct 26, 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