fix(resources): skip post-fetch resolution for direct_proxy reads - #5463
fix(resources): skip post-fetch resolution for direct_proxy reads#5463Ewertonslv wants to merge 3 commits into
Conversation
In direct_proxy gateway mode, read_resource builds the final TextResourceContents/BlobResourceContents from the live upstream fetch, but execution then fell into the post-fetch "RESOLVE CONTENT" block, which calls getattr(content, "id") unconditionally. Those MCP-compliant models have no `id` field, so every direct_proxy text or blob read raised AttributeError, was swallowed at the transport layer, and returned empty content to the client. Track whether the content was produced by the direct_proxy branch and skip the metadata->content resolution for it, keeping the gateway access and resource access checks and the post-fetch hooks intact. Adds regression tests that exercise the real (id-less) production models instead of the id-bearing test subclasses that previously masked the bug. Closes IBM#5451 Signed-off-by: Ewerton Silva <ewertoncom297@gmail.com>
|
Correction to my checklist above: I ticked "the linked issue is not labeled For coordination: this and #5467 were opened the same day and both touch |
Resolve conflict in resource_service.py: keep the direct_proxy short-circuit (issue IBM#5451) alongside upstream's _set_gateway_content template-placeholder guard.
|
Hi @Ewertonslv — sincere apologies for leaving this PR open for so long without a proper review or merge. That's on us, and we're sorry for the lack of responsiveness. We're closing this PR now as part of a housekeeping pass on long-open contributions. The linked issue (#5451) remains open, so the need is still tracked. If you're still interested in contributing this change, please feel free to:
We genuinely appreciate your contribution and hope to give it the attention it deserves. Thank you for taking the time to contribute to ContextForge! 🙏 |
|
Thanks @jonpspri — no problem at all, and thanks for the transparency about the housekeeping pass. Rebased onto current Same change, only rebased. One conflict, resolved by keeping both sides — The bug is still present on today's |
🔗 Related Issue
Closes #5451
📝 Summary
In
direct_proxygateway mode,ResourceService.read_resourcefetches theresource live from the upstream and builds the final
TextResourceContents/BlobResourceContents. Execution then fell through into the shared post-fetchRESOLVE CONTENT block, whose first branch runs
getattr(content, "id")unconditionally. Those MCP-compliant models (
mcpgateway/common/models.py) haveno
idfield, so every direct_proxy text or blob read raisedAttributeError: 'TextResourceContents' object has no attribute 'id'. Thetransport layer catches the exception and returns empty content, so the client
silently receives
text=""instead of the fetched payload.Root cause
The direct_proxy branch never marked its content as already-resolved — the
# Skip the rest of the DB lookup logiccomment did not actually skip anything.Fix
Track whether content came from the direct_proxy branch (
direct_proxy_read) andskip the metadata→content resolution for it. The gateway access check, the
resource access check, and the post-fetch plugin hooks all still run — only the
id-based
invoke_resourcere-resolution is bypassed, since the payload is final.Tests (before → after)
The existing happy-path direct_proxy tests masked the bug by monkey-patching
id-bearing subclasses over the real models, so production classes never took the
failing path. This PR adds two regression tests exercising the real
TextResourceContents/BlobResourceContents:AttributeErrorat thegetattr(content, "id")call.image/pngblob read returns itsblob, and
invoke_resourceis asserted not re-awaited.pytest tests/unit/mcpgateway/services/test_resource_service.pypasses locally(full file green, including module doctests via
--doctest-modules).📏 Reviewability
triage🏷️ Type of Change