Skip to content

Add 11 handler filter hooks for extensibility#151

Open
galatanovidiu wants to merge 7 commits intorelease/v0.5.0from
feature/handler-filter-hooks
Open

Add 11 handler filter hooks for extensibility#151
galatanovidiu wants to merge 7 commits intorelease/v0.5.0from
feature/handler-filter-hooks

Conversation

@galatanovidiu
Copy link
Contributor

@galatanovidiu galatanovidiu commented Mar 20, 2026

Summary

11 new apply_filters hooks across the MCP handler lifecycle. Covers execution gates, argument/result transformation, list operations, initialization, and component naming.

Motivation

The plugin had 20 hooks but none in the request/response lifecycle. Once a request entered RequestRouter, there were zero extension points until the JSON-RPC response left. No way to modify arguments, filter results, block execution, or dynamically adjust component lists.

Hooks added

Pre-execution (can short-circuit with WP_Error)

Hook Params Purpose
mcp_adapter_pre_tool_call (array $args, string $tool_name, McpTool $mcp_tool, McpServer $server) => array|WP_Error Modify tool arguments or return WP_Error to block execution
mcp_adapter_pre_resource_read (array $params, string $uri, McpResource $mcp_resource, McpServer $server) => array|WP_Error Modify resource params or return WP_Error to block execution
mcp_adapter_pre_prompt_get (array $arguments, string $prompt_name, McpPrompt $mcp_prompt, McpServer $server) => array|WP_Error Modify prompt arguments or return WP_Error to block execution

Result filters

Hook Params Purpose
mcp_adapter_tool_call_result (mixed $result, array $args, string $tool_name, McpTool $mcp_tool, McpServer $server) => mixed Transform tool result before DTO assembly (PII redaction, audit logging)
mcp_adapter_resource_read_result (mixed $contents, array $params, string $uri, McpResource $mcp_resource, McpServer $server) => mixed Transform resource contents before DTO conversion
mcp_adapter_prompt_get_result (mixed $result, array $arguments, string $prompt_name, McpPrompt $mcp_prompt, McpServer $server) => mixed Transform prompt result before normalization

List filters

Hook Params Purpose
mcp_adapter_tools_list (array $tools, McpServer $server) => array Hide tools per user/role, add dynamic tools, reorder
mcp_adapter_resources_list (array $resources, McpServer $server) => array Filter resources by context, add dynamic resources
mcp_adapter_prompts_list (array $prompts, McpServer $server) => array Filter prompts, add dynamic prompts

Initialize

Hook Params Purpose
mcp_adapter_initialize_response (InitializeResult $result, McpServer $server) => InitializeResult Dynamic capabilities, custom instructions

Naming

Hook Params Purpose
mcp_adapter_prompt_name (string $name, WP_Ability $ability) => string Naming parity with existing mcp_adapter_tool_name and mcp_adapter_resource_name

Why the names differ from #130

The names differ from the ones proposed in the #130 investigation. During implementation I found two problems with the _pre/_post naming from the issue:

  1. _pre was ambiguous. It could mean "modify arguments" or "gate execution" but couldn't do both. I need to block execution (rate limiting, circuit breakers) from the same hook that modifies arguments, not from a separate one.

  2. _post didn't say what you're filtering. _result is explicit.

I went with the WordPress core pre_* short-circuit pattern instead (pre_delete_post, pre_update_option, pre_get_shortlink). The filter returns the modified value to proceed or WP_Error to block. This is a pattern WordPress developers already know.

Test plan

  • All tests pass (npm run test:php)
  • PHPCS clean (npm run lint:php)
  • PHPStan Level 8 zero errors (npm run lint:php:stan)
  • pre_tool_call filter can modify arguments
  • pre_tool_call filter can block execution with WP_Error
  • tool_call_result filter can modify result
  • List filters can remove/add components
  • initialize_response filter can modify instructions
  • prompt_name filter can customize name
  • MCP Inspector smoke test: npx @modelcontextprotocol/inspector --cli --config .mcp.json --server mcp-adapter-default-npx --method tools/list

Ref #130

Add resolve_prompt_name() method following the same pattern as
resolve_tool_name() in RegisterAbilityAsMcpTool. Uses McpNameSanitizer
for normalization and McpValidator for post-filter validation.

Closes the naming parity gap — tools and resources already had naming
filters but prompts did not.

Ref #130
Add mcp_adapter_tools_list, mcp_adapter_resources_list, and
mcp_adapter_prompts_list filters. These fire after retrieving
components from the registry and before assembling the list result
DTO. Enables hiding components per user/role, adding dynamic
components, or reordering.

Ref #130
Filter fires after constructing InitializeResult and before returning.
Enables dynamic capabilities, custom instructions, and server info
modifications.

Ref #130
Pre-filter fires after permission check, before execution. Enables
argument transformation, context injection, and rate limiting.

Post-filter fires after execution, before DTO assembly. Enables
result transformation, PII redaction, and audit logging.

Ref #130
Pre-filter fires after permission check, before resource execution.
Post-filter fires after execution, before DTO conversion. Enables
access control, caching, content transformation, and audit logging.

Ref #130
Pre-filter fires after permission check, before prompt execution.
Post-filter fires after execution, before normalization. Enables
argument normalization, context injection, and message transformation.

Ref #130
Rename pre/post execution hooks to follow WordPress pre_* convention:
- mcp_adapter_tool_call_pre → mcp_adapter_pre_tool_call
- mcp_adapter_tool_call_post → mcp_adapter_tool_call_result
- mcp_adapter_resource_read_pre → mcp_adapter_pre_resource_read
- mcp_adapter_resource_read_post → mcp_adapter_resource_read_result
- mcp_adapter_prompt_get_pre → mcp_adapter_pre_prompt_get
- mcp_adapter_prompt_get_post → mcp_adapter_prompt_get_result

The pre_* filters now support WP_Error returns to short-circuit
execution, following the WordPress pre_delete_post pattern. This
enables rate limiting, circuit breakers, and custom permission
checks from filter callbacks.

Ref #130
@github-actions
Copy link

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: galatanovidiu <ovidiu-galatan@git.wordpress.org>

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

@codecov
Copy link

codecov bot commented Mar 20, 2026

Codecov Report

❌ Patch coverage is 97.50000% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 87.71%. Comparing base (31a4485) to head (85d28a8).

Files with missing lines Patch % Lines
...udes/Domain/Prompts/RegisterAbilityAsMcpPrompt.php 94.44% 1 Missing ⚠️
Additional details and impacted files
@@                 Coverage Diff                  @@
##             release/v0.5.0     #151      +/-   ##
====================================================
+ Coverage             87.62%   87.71%   +0.08%     
- Complexity             1218     1227       +9     
====================================================
  Files                    54       54              
  Lines                  3944     3980      +36     
====================================================
+ Hits                   3456     3491      +35     
- Misses                  488      489       +1     
Flag Coverage Δ
unit 87.71% <97.50%> (+0.08%) ⬆️

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.

@galatanovidiu galatanovidiu requested a review from gziolo March 20, 2026 14:23
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.

1 participant