Skip to content

Conversation

@gziolo
Copy link
Member

@gziolo gziolo commented Oct 2, 2025

Closes #62.

What

Oftentimes we want to give the AI different instructions for when it's about to perform a destructive action, such as framing what's about to happen and requesting confirmation to the user. It would be helpful, therefore, to have a property of an Ability that marks it as a destructive ability.

I took some inspiration from ToolAnnotation in MCP. For starters, we will need read_only to replace the current logic that uses meta.tool to distinguish between HTTP method usage (i.e., GET and POST) in requests.

Why

The best explanation of the root problem that is being addressed comes from @galatanovidiu in WordPress/mcp-adapter#48 (comment)

It would be great to leverage the same metadata in MCP, but we need to address a fundamental terminology mismatch first.

The core issue: WordPress Abilities API and MCP use "tools" and "resources" to mean completely different things:

WordPress Abilities API:

Tools = callable functions that modify data
Resources = callable functions that read data

MCP:

Tools = callable functions that the AI invokes automatically when needed
Resources = static context that users manually attach to conversations

So WordPress's "tool vs resource" distinction doesn't map to MCP's "tool vs resource"

Concrete example: A site-info ability could be implemented as:

MCP tool: AI calls it when it needs current site data during conversation
MCP resource: User pre-attaches site info as reference context

The choice depends on the usage pattern (AI-driven vs. user-driven), not the type of action.

Here, we remove all references to tool and resource nomenclature. Instead, we perform detection of the GET vs POST method in the REST API calls based on annotations that describe what the ability does: read only vs updates data.

Fute considerations

With more properties, we could also start using DELETE HTTP method as I explore idempotent and destructive annotations. I'm also interested in including freeform instructions so devs could consist of usage examples for both the user and for consideration to pass to LLMs.

Testing

Run test:

npm run test:php
npm run test:client

@codecov
Copy link

codecov bot commented Oct 2, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 85.98%. Comparing base (45cfa27) to head (b244bce).
⚠️ Report is 1 commits behind head on trunk.

Additional details and impacted files
@@             Coverage Diff              @@
##              trunk      #99      +/-   ##
============================================
+ Coverage     85.69%   85.98%   +0.28%     
- Complexity      103      105       +2     
============================================
  Files            16       16              
  Lines           776      792      +16     
  Branches         86       86              
============================================
+ Hits            665      681      +16     
  Misses          111      111              
Flag Coverage Δ
javascript 92.62% <100.00%> (-0.04%) ⬇️
unit 83.47% <100.00%> (+0.50%) ⬆️

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

☔ View full report in Codecov by Sentry.
📢 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.

@gziolo gziolo force-pushed the update/add-ability-annotations branch from 0a65ff7 to 324d332 Compare October 2, 2025 13:27
@gziolo gziolo added [Status] In Progress Assigned work scheduled [Type] Enhancement New feature or request labels Oct 2, 2025
@gziolo gziolo force-pushed the update/add-ability-annotations branch from 1ca8dcb to 1708839 Compare October 3, 2025 10:59
@gziolo gziolo marked this pull request as ready for review October 3, 2025 10:59
@gziolo gziolo requested a review from emdashcodes October 3, 2025 10:59
@github-actions
Copy link

github-actions bot commented Oct 3, 2025

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.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: gziolo <gziolo@git.wordpress.org>
Co-authored-by: emdashcodes <emdashcodes@git.wordpress.org>
Co-authored-by: galatanovidiu <ovidiu-galatan@git.wordpress.org>
Co-authored-by: felixarntz <flixos90@git.wordpress.org>
Co-authored-by: JasonTheAdams <jason_the_adams@git.wordpress.org>
Co-authored-by: swissspidy <swissspidy@git.wordpress.org>
Co-authored-by: justlevine <justlevine@git.wordpress.org>

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

@gziolo gziolo removed the [Status] In Progress Assigned work scheduled label Oct 3, 2025
@gziolo gziolo requested a review from swissspidy October 3, 2025 10:59
@gziolo gziolo force-pushed the update/add-ability-annotations branch from 1708839 to 9306cd9 Compare October 3, 2025 11:01
@gziolo gziolo self-assigned this Oct 3, 2025
@gziolo gziolo force-pushed the update/add-ability-annotations branch from 9306cd9 to a84b388 Compare October 3, 2025 11:12
@gziolo gziolo force-pushed the update/add-ability-annotations branch from b0824a5 to 47ef053 Compare October 3, 2025 12:21
Copy link
Contributor

@emdashcodes emdashcodes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this a lot. I prefer the annotations over the current tool/resource pattern.

The instructions will also be useful for providing tool instructions that various things like the MCP Adapter or Agents can adapt.

* @see WP_Abilities_Registry
*/
class WP_Ability {
/**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to suggest we align our internal naming conventions with the established MCP protocol standards to reduce developer cognitive load and maintain consistency across the systems.

Current approach:

  • Internally: read_only, destructive, idempotent
  • MCP: readOnlyHint, destructiveHint, idempotentHint

Suggested approach:
Since MCP is a well-established protocol with defined conventions, I think we should adopt the MCP naming directly in our abilities API:

  • Use: readOnlyHint, destructiveHint, idempotentHint internally
  • This eliminates the need for translation in the MCP adapter
  • Developers working with both our API and MCP won't need to context-switch between naming conventions
  • Minimizes developer confusion when working across both systems

Additional consideration - Instructions handling:
For the instructions field, since MCP uses the tool description as instructions, we can handle this in the MCP adapter by appending our instructions content to the description field.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The counterpoint would be that WordPress uses snake case convention for naming properties. REST API is a good reflection of that. It's also inconvenient with JavaScript that uses camel case similarly to the MCP protocol. Sometimes, we even mix the two when functionality spans all layers, as with block types that also support the JSON format, which again uses camel case.

@felixarntz, and @swissspidy, what are your thoughts on that aspect?

Copy link
Member

@felixarntz felixarntz Oct 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 on aligning with WordPress's snake case convention:

  • For the reason @gziolo mentioned: We shouldn't discard WordPress Coding Standards when it's convenient :)
  • Another counterpoint is that this is the Abilities API which was from the beginning decided to be decoupled from MCP, and rightfully so.

IMO it's perfectly reasonable to have a translation layer in place as part of the MCP adapter, we shouldn't change the Abilities API to tie in particularly well with MCP when it leads to other problems.

Aside: I do agree it's painful to deal with snake_case from the server in the client-side JavaScript world where everything is camelCase. My recommendation for that reason is to use single-word names (like readonly, destructive, idempotent) wherever possible, as those would be both valid snake_case and valid camelCase. But obviously this is not the problem discussed here - it doesn't allow us to align with the MCP naming.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Happy to rename from read_only to readonly. I'm not even 100% sure whether "read-only" is the ultimate correct English spelling 😄 I definitely saw many times readonly in the context of programming, for example, in TypeScript.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whatever you decide to do is ok with me. Also, adapting those keys to the MCP standards is not a big problem.

I also understand your points on WP coding standards and the abilities-api being decoupled from MCP.

One thing I want to avoid when creating abilities for MCP usage:

$annotations = array(
    'read_only' => true, // translated to readOnlyHint
    'destructive' => false, // translated to destructiveHint
    'idempotent' => true, // translated to idempotentHint
    'openWorldHint' => false,
    'lastModified' => '...',
    'title' => '...',
    'audience' => '...',
    'priority' => '...',
)

This is inconsistent, non-standard, and ugly.

So, what do you think we should do to avoid this situation?

Copy link
Member

@felixarntz felixarntz Oct 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Completely agree, we shouldn't have inconsistencies like this.

Where does this example list of identifiers come from? As far as this PR is concerned, I think we can use readonly to work around the difference between snake_case and camelCase.

If there are terms where we need multi-word identifiers, I'd say we need to use snake_case if their source of truth is on the server (i.e. in PHP).

If in some JavaScript use-cases we need to translate stuff to be camelCase, we could probably do that consistently as well.

Copy link
Member

@JasonTheAdams JasonTheAdams Oct 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with @felixarntz. We shouldn't build anything in Abilities around the MCP standard. The MCP Adapter is a consumer of the Abilities API and should handle any translations and such needed to work. Any parameters added by the MCP Adapter should follow the pattern established here.

As a grammatical note, it kills me every time I see "readonly" in various contexts. We're just lying to ourselves. 😆

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used the same docs to shape basic needs for abilities based on the discussion in #62.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed read_only key to readonly, and adjusted docs to use "read-only" form in 9f3a00c.

@gziolo gziolo enabled auto-merge (squash) October 8, 2025 07:43
@gziolo gziolo merged commit a9039a3 into trunk Oct 8, 2025
20 checks passed
@gziolo gziolo deleted the update/add-ability-annotations branch October 8, 2025 07:43
@gziolo
Copy link
Member Author

gziolo commented Oct 8, 2025

I opened a follow-up with extended docs in #104.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Type] Enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add property for marking Ability hints like destructive, read-only, idempotent

6 participants