Skip to content

Fix resources/read 500 on ability-backed resources with an object input schema - #299

Open
shoemoney wants to merge 2 commits into
WordPress:trunkfrom
shoemoney:fix/resource-argument-normalizer
Open

Fix resources/read 500 on ability-backed resources with an object input schema#299
shoemoney wants to merge 2 commits into
WordPress:trunkfrom
shoemoney:fix/resource-argument-normalizer

Conversation

@shoemoney

@shoemoney shoemoney commented Aug 24, 2026

Copy link
Copy Markdown

What?

Closes #261

resources/read returns a 500 for any ability-backed resource whose ability declares an object input_schema. This routes the empty argument set through AbilityArgumentNormalizer so the ability receives [] instead of being called with zero arguments.

Why?

McpResource::execute() and check_permission() called the backing ability with no arguments at all. For an ability with 'input_schema' => array( 'type' => 'object' ), core then validates null against that schema and the read fails:

Internal error: Ability "..." has invalid input. Reason: input is not of type object.

Full credit to @lhero-org for the root-cause analysis in #261, which identified the exact failing call and ruled out the sibling tools/call path already fixed in #230.

How?

McpTool and McpPrompt already normalize their arguments through AbilityArgumentNormalizer; McpResource was the one component that did not. This applies the same pattern in both execute() and check_permission():

  • Abilities with an object schema now receive an empty array.
  • Abilities with no schema still receive null, unchanged legacy behavior.
  • A schema with a top-level default still has that default applied by core.

Scope note: the resources/read protocol 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 forwarded uri would fail.

Testing Instructions

  1. Register an ability whose input_schema is array( 'type' => 'object' ) and expose it as an MCP resource via meta.mcp.type = 'resource'.
  2. Call resources/read for that resource's URI.
  3. Before this change: 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_executes in tests/phpunit/Unit/Resources/McpResourceTest.php.

composer test -- --filter McpResourceTest

Changelog Entry

Fixed - resources/read returning a 500 for ability-backed resources whose ability declares an object input schema.

Open WordPress Playground Preview

Copilot AI lite review requested due to automatic review settings August 24, 2026 00:39
@github-actions

github-actions Bot commented Aug 24, 2026

Copy link
Copy Markdown

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 props-bot label.

Unlinked Accounts

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

Unlinked contributors: shoemoney.

Co-authored-by: justlevine <justlevine@git.wordpress.org>
Co-authored-by: lhero-org <lheroorg@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

Copilot AI 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.

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() and McpResource::check_permission().
  • Add a unit test covering an ability-backed resource with an object input_schema to 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.

Comment on lines +74 to +91
$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' );
}
Comment on lines +58 to +63
'execute_callback' => static function ( $input ) {
return is_array( $input ) ? 'object schema content' : 'unexpected input';
},
'permission_callback' => static function ( $input ) {
return is_array( $input );
},
@codecov

codecov Bot commented Aug 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 88.79%. Comparing base (7d8fbbf) to head (6d3ba8b).

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           
Flag Coverage Δ
unit 88.79% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@justlevine

Copy link
Copy Markdown
Contributor

@shoemoney , you mind updating your PR description to use the repository's Pull request template?

Comment on lines +273 to +276
// 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.

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.

This reads like AI slop...

Comment on lines +314 to +317
// 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.

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.

This also reads like AI slop

@shoemoney

shoemoney commented Aug 27, 2026 via email

Copy link
Copy Markdown
Author

@shoemoney

Copy link
Copy Markdown
Author

The red Run Plugin Check here is not from this diff. It reports two findings in readme.txt, which this PR does not touch:

ERROR   outdated_tested_upto_header — "Tested up to: 7.0" < 7.1
WARNING readme_reserved_contributors — "wordpressdotorg" is reserved

The same check has failed on trunk on every run since 21 Aug, so any open PR inherits it. #307 bumps the header to 7.1, which should clear the error for all of them.

Everything else here is green: PHPUnit across PHP 7.4–8.4 on WP 6.9 / latest / trunk, PHPCS, PHPStan, and codecov.

@shoemoney

Copy link
Copy Markdown
Author

Updated, thanks for the nudge.

  • Description now follows the repository template, including the AI-tools disclosure.
  • Both comment blocks you flagged are down to a single line each (d7ade87). They were restating the diff rather than explaining it.

That commit also picks up the two Copilot notes: the callbacks now assert the ability receives exactly [] — so a regression that forwarded uri would fail instead of passing — and the wp_unregister_ability() moved into a finally so a failed assertion can't leak state into the rest of the suite.

…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.
@shoemoney
shoemoney force-pushed the fix/resource-argument-normalizer branch from d7ade87 to 6d3ba8b Compare September 1, 2026 19:25
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.

resources/read on ability-backed resources 500s: McpResource::execute() calls ability->execute() with zero args

3 participants