Fix resources/read 500 on ability-backed resources with an object input schema - #299
Fix resources/read 500 on ability-backed resources with an object input schema#299shoemoney wants to merge 2 commits into
Conversation
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the Unlinked AccountsThe following contributors have not linked their GitHub and WordPress.org accounts: @shoemoney. Contributors, please read how to link your accounts to ensure your work is properly credited in WordPress releases. If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
There was a problem hiding this comment.
Pull request overview
Fixes a resources/read 500 when reading ability-backed resources whose abilities declare an object input_schema, by ensuring the resource execution and permission paths normalize an empty argument set through AbilityArgumentNormalizer (consistent with tool/prompt execution).
Changes:
- Normalize empty ability input for ability-backed resources in
McpResource::execute()andMcpResource::check_permission(). - Add a unit test covering an ability-backed resource with an object
input_schemato prevent regressions.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
includes/Domain/Resources/McpResource.php |
Routes empty resource ability input through AbilityArgumentNormalizer and passes it into execute() / check_permissions() to avoid schema validation errors. |
tests/phpunit/Unit/Resources/McpResourceTest.php |
Adds a regression test for ability-backed resources with an object input_schema. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| $ability = wp_get_ability( 'test/resource-object-schema' ); | ||
| $this->assertNotNull( $ability ); | ||
|
|
||
| $mcp_resource = McpResource::fromAbility( $ability ); | ||
| $this->assertNotWPError( $mcp_resource ); | ||
|
|
||
| // resources/read passes protocol-level params (uri); the ability must | ||
| // receive a normalized empty argument set that satisfies its object | ||
| // schema instead of a zero-argument call that validates null against it. | ||
| $permission = $mcp_resource->check_permission( array( 'uri' => 'WordPress://local/resource-object-schema' ) ); | ||
| $this->assertTrue( $permission ); | ||
|
|
||
| $result = $mcp_resource->execute( array( 'uri' => 'WordPress://local/resource-object-schema' ) ); | ||
| $this->assertNotWPError( $result ); | ||
| $this->assertSame( 'object schema content', $result ); | ||
|
|
||
| wp_unregister_ability( 'test/resource-object-schema' ); | ||
| } |
| 'execute_callback' => static function ( $input ) { | ||
| return is_array( $input ) ? 'object schema content' : 'unexpected input'; | ||
| }, | ||
| 'permission_callback' => static function ( $input ) { | ||
| return is_array( $input ); | ||
| }, |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## trunk #299 +/- ##
=========================================
Coverage 88.78% 88.79%
Complexity 1264 1264
=========================================
Files 54 54
Lines 4164 4166 +2
=========================================
+ Hits 3697 3699 +2
Misses 467 467
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
@shoemoney , you mind updating your PR description to use the repository's Pull request template? |
| // Ability-backed resources receive no ability input: the resources/read | ||
| // params (uri) are protocol-level, not ability arguments. The empty | ||
| // argument set is normalized so abilities with an object input schema | ||
| // receive an empty array instead of null, matching McpTool and McpPrompt. |
There was a problem hiding this comment.
This reads like AI slop...
| // Ability-backed resources receive no ability input: the resources/read | ||
| // params (uri) are protocol-level, not ability arguments. The empty | ||
| // argument set is normalized so abilities with an object input schema | ||
| // receive an empty array instead of null, matching McpTool and McpPrompt. |
There was a problem hiding this comment.
This also reads like AI slop
|
Wow sorry about that fixing.
…Sent from my iPhone
On Thu, Aug 27, 2026 at 1:36 AM Dovid Levine ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In includes/Domain/Resources/McpResource.php
<#299 (comment)>
:
> + // Ability-backed resources receive no ability input: the resources/read
+ // params (uri) are protocol-level, not ability arguments. The empty
+ // argument set is normalized so abilities with an object input schema
+ // receive an empty array instead of null, matching McpTool and McpPrompt.
This also reads like AI slop
—
Reply to this email directly, view it on GitHub
<#299?email_source=notifications&email_token=AADMPBRX33KP6GIAFA45YGL5L7JHDA5CNFSNUABKM5UWIORPF5TWS5BNNB2WEL2QOVWGYUTFOF2WK43UKJSXM2LFO4XTKMBTG44DSNZVGEYKM4TFMFZW63VHNVSW45DJN5XKKZLWMVXHJLDGN5XXIZLSL5RWY2LDNM#pullrequestreview-5037897510>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AADMPBQHZA2ELBQE6NAXJ5L5L7JHDAVCNFSNUABGKJSXA33TNF2G64TZHMYTAMJXGIYDSMJUGE5US43TOVSTWNJSGI4TSNJYGA4DHILWAI>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
|
The red The same check has failed on Everything else here is green: PHPUnit across PHP 7.4–8.4 on WP 6.9 / latest / trunk, PHPCS, PHPStan, and codecov. |
|
Updated, thanks for the nudge.
That commit also picks up the two Copilot notes: the callbacks now assert the ability receives exactly |
…schema McpResource::execute() and check_permission() called the ability with zero arguments, so an ability whose input_schema is of type object had null validated against the schema and failed with "input is not of type object". Normalize the empty argument set through AbilityArgumentNormalizer, matching the McpTool and McpPrompt pattern: abilities with an object schema receive an empty array, abilities without a schema still receive null, and a schema with a top-level default still has the default applied. The resources/read protocol params (uri) remain protocol-level and are not forwarded as ability input, preserving the existing contract for abilities registered without an input schema. Fixes WordPress#261
- Replace the four-line duplicated comment blocks with one line each. - Assert the ability receives exactly [], so a forwarded uri fails. - Wrap assertions in try/finally so the ability is always unregistered.
d7ade87 to
6d3ba8b
Compare
What?
Closes #261
resources/readreturns a 500 for any ability-backed resource whose ability declares an objectinput_schema. This routes the empty argument set throughAbilityArgumentNormalizerso the ability receives[]instead of being called with zero arguments.Why?
McpResource::execute()andcheck_permission()called the backing ability with no arguments at all. For an ability with'input_schema' => array( 'type' => 'object' ), core then validatesnullagainst that schema and the read fails:Full credit to @lhero-org for the root-cause analysis in #261, which identified the exact failing call and ruled out the sibling
tools/callpath already fixed in #230.How?
McpToolandMcpPromptalready normalize their arguments throughAbilityArgumentNormalizer;McpResourcewas the one component that did not. This applies the same pattern in bothexecute()andcheck_permission():null, unchanged legacy behavior.defaultstill has that default applied by core.Scope note: the
resources/readprotocol params (uri) are intentionally not forwarded as ability input. They are protocol-level, and the added test asserts the ability receives exactly[], so a regression that forwardeduriwould fail.Testing Instructions
input_schemaisarray( 'type' => 'object' )and expose it as an MCP resource viameta.mcp.type = 'resource'.resources/readfor that resource's URI.Internal error: Ability "..." has invalid input. Reason: input is not of type object.After: the resource reads normally.Automated coverage:
test_ability_backed_resource_with_object_input_schema_executesintests/phpunit/Unit/Resources/McpResourceTest.php.Changelog Entry