Skip to content

Request Logging: no public API to record the reserved mcp_tool and ability log types #906

Description

@azizulhasan

What problem does this address?

The AI Request Logging experiment's read contract already declares three log types, but only one of them can ever exist.

  • AI_Request_Log_Controller::get_collection_params() exposes type with 'enum' => array( '', 'ai_client', 'mcp_tool', 'ability' ) (includes/Logging/REST/AI_Request_Log_Controller.php).
  • The client mirrors that contract — LogEntry.type is typed 'ai_client' | 'mcp_tool' | 'ability' (src/admin/ai-request-logs/types.ts).
  • But the only writer is Log_Data_Extractor::extract_request_data(), which hardcodes 'type' => 'ai_client'. Nothing in the plugin ever produces an mcp_tool or ability row.

There is also no supported way for code outside the experiment to write one. The manager is private at every level:

  • AI_Request_Logging::$manager and ::get_manager() are private
  • Logging_Integration::$log_manager is private static with no accessor

So a consumer's only option is to construct its own new AI_Request_Log_Manager(). That technically works, but it is the wrong shape: it bypasses the experiment-enabled check entirely, and calling init() on it re-runs maybe_upgrade_table() and re-negotiates the daily wp_schedule_event() cleanup cron. Skipping init() instead risks writing into a table that hasn't been upgraded. Either way the caller ends up coupled to internals that carry no backward-compatibility promise.

What is your proposed solution?

Two additive changes:

  1. A public entry point for recording a request, so consumers don't construct their own manager — for example a wpai_log_ai_request( array $data ): string|false function, or a static accessor on Logging_Integration. It would no-op when the experiment is disabled, and otherwise delegate to the already-public AI_Request_Log_Manager::log().
  2. Validate/normalize type against the same set the REST controller advertises, so the write side and the read side can't drift apart.

Optionally, an action fired at write time would let consumers observe rows without polling the REST endpoint.

I'm happy to open a PR for this if the direction looks right.

Why is this important to you?

The MCP Adapter is the obvious first consumer — an MCP tool call is exactly the kind of AI-initiated request a site owner wants in the log, and mcp_tool looks reserved with that in mind. The same applies to direct ability invocations, which today are invisible unless they happen to go out through the AI Client's HTTP transporter.

I maintain a plugin that exposes abilities over MCP, and I'd like its tool calls to appear in the site's existing AI Request Log rather than in a parallel log of my own. Currently that means reaching into private internals. If this isn't solved centrally, each MCP or ability surface will end up shipping its own logging table — which seems to be the outcome the reserved type values were meant to prevent.

Related but distinct: #732 covers requests that bypass the SDK HTTP transporter (a provider transport gap, with #757 in flight). This issue is about log types the schema already anticipates but nothing can produce. The two would compose well rather than overlap.

I have searched existing issues and this is not a duplicate.

  • Yes

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

Status
Done

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions