code-wheel/mcp-events

Standardized event classes for MCP (Model Context Protocol) tool execution lifecycle

Installs: 256

Dependents: 0

Suggesters: 0

Security: 0

Stars: 1

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/code-wheel/mcp-events

v2.1.1 2026-01-10 04:03 UTC

This package is auto-updated.

Last update: 2026-01-10 06:23:32 UTC


README

CI codecov Latest Stable Version PHP Version License

Standardized event classes for MCP (Model Context Protocol) tool execution lifecycle in PHP.

Zero dependencies - pure PHP 8.1+, framework-agnostic.

Installation

composer require code-wheel/mcp-events

Usage

These events follow a standard structure for MCP tool execution observability. Use them with any PSR-14 compatible event dispatcher.

Dispatching Events

use CodeWheel\McpEvents\ToolExecutionStartedEvent;
use CodeWheel\McpEvents\ToolExecutionSucceededEvent;
use CodeWheel\McpEvents\ToolExecutionFailedEvent;

// When tool execution starts
$startEvent = new ToolExecutionStartedEvent(
    toolName: 'create_user',
    pluginId: 'my_module.create_user',
    arguments: ['username' => 'john', 'email' => '[redacted]'],
    requestId: 'req-123',
    timestamp: microtime(true),
);
$dispatcher->dispatch($startEvent);

// On success
$successEvent = new ToolExecutionSucceededEvent(
    toolName: 'create_user',
    pluginId: 'my_module.create_user',
    arguments: ['username' => 'john', 'email' => '[redacted]'],
    result: $callToolResult,
    durationMs: 45.2,
    requestId: 'req-123',
);
$dispatcher->dispatch($successEvent);

// On failure
$failEvent = new ToolExecutionFailedEvent(
    toolName: 'create_user',
    pluginId: 'my_module.create_user',
    arguments: ['username' => 'john'],
    reason: ToolExecutionFailedEvent::REASON_VALIDATION,
    result: null,
    exception: $validationException,
    durationMs: 12.5,
    requestId: 'req-123',
);
$dispatcher->dispatch($failEvent);

Listening to Events

use CodeWheel\McpEvents\ToolExecutionStartedEvent;
use CodeWheel\McpEvents\ToolExecutionSucceededEvent;
use CodeWheel\McpEvents\ToolExecutionFailedEvent;

class ToolExecutionLogger {

    public function onStart(ToolExecutionStartedEvent $event): void {
        $this->logger->info('Tool started', [
            'tool' => $event->toolName,
            'request_id' => $event->requestId,
        ]);
    }

    public function onSuccess(ToolExecutionSucceededEvent $event): void {
        $this->metrics->histogram('tool_duration_ms', $event->durationMs, [
            'tool' => $event->toolName,
        ]);
    }

    public function onFailure(ToolExecutionFailedEvent $event): void {
        $context = [
            'tool' => $event->toolName,
            'reason' => $event->reason,
            'duration_ms' => $event->durationMs,
        ];

        if ($event->hasException()) {
            $context['exception'] = $event->exception->getMessage();
        }

        if ($event->isPolicyFailure()) {
            $this->logger->warning('Tool blocked by policy', $context);
        } else {
            $this->logger->error('Tool execution failed', $context);
        }
    }
}

Events

ToolExecutionStartedEvent

Dispatched when tool execution begins.

Property Type Description
toolName string MCP tool name
pluginId string Implementation plugin ID
arguments array Sanitized tool arguments
requestId string|int|null MCP request correlation ID
timestamp float Start timestamp (microtime)

ToolExecutionSucceededEvent

Dispatched when tool execution completes successfully.

Property Type Description
toolName string MCP tool name
pluginId string Implementation plugin ID
arguments array Sanitized tool arguments
result object Result object (e.g., CallToolResult from mcp/sdk)
durationMs float Execution duration in ms
requestId string|int|null MCP request correlation ID

ToolExecutionFailedEvent

Dispatched when tool execution fails.

Property Type Description
toolName string MCP tool name
pluginId string Implementation plugin ID
arguments array Sanitized tool arguments
reason string Failure reason constant
result object|null Result object if available
exception Throwable|null Exception if thrown
durationMs float Duration until failure in ms
requestId string|int|null MCP request correlation ID

Failure Reasons

Constant Description
REASON_VALIDATION Input validation failed
REASON_ACCESS_DENIED Permission denied
REASON_INSTANTIATION Failed to create tool instance
REASON_INVALID_TOOL Tool not found or invalid
REASON_RESULT Result processing failed
REASON_EXECUTION General execution failure
REASON_POLICY Blocked by policy
REASON_POLICY_APPROVAL Requires approval
REASON_POLICY_BUDGET Budget exceeded
REASON_POLICY_DRY_RUN Dry run mode
REASON_POLICY_SCOPE Insufficient scope

Helper Methods

// Check if failure was policy-related
if ($event->isPolicyFailure()) {
    // Handle policy block differently
}

// Check if exception was thrown
if ($event->hasException()) {
    $exception = $event->exception;
}

// Get all valid failure reasons
$allReasons = ToolExecutionFailedEvent::allReasons();
// ['REASON_VALIDATION' => 'validation_failed', ...]

// Validate a reason string
if (ToolExecutionFailedEvent::isValidReason($reason)) {
    // Valid reason
}

JSON Serialization

All events implement JsonSerializable for easy logging:

// Direct serialization
$json = json_encode($event);

// Or get the array
$data = $event->jsonSerialize();

// Example output for ToolExecutionFailedEvent:
// {
//   "event": "tool_execution_failed",
//   "tool_name": "create_user",
//   "plugin_id": "my_module.create_user",
//   "reason": "validation_failed",
//   "duration_ms": 12.5,
//   "request_id": "req-123",
//   "is_policy_failure": false,
//   "has_exception": true,
//   "exception_class": "InvalidArgumentException",
//   "exception_message": "Email is required"
// }

Note: The result object is intentionally excluded from serialization as it may contain sensitive data.

Framework Integration

This package has zero dependencies. When using with mcp/sdk, the result object will be a CallToolResult:

// With mcp/sdk (optional)
use Mcp\Schema\Result\CallToolResult;

$event = new ToolExecutionSucceededEvent(
    toolName: 'my_tool',
    pluginId: 'my_module.my_tool',
    arguments: [],
    result: $callToolResult, // CallToolResult from mcp/sdk
    durationMs: 10.5,
    requestId: null,
);

// Access result properties
$structured = $event->result->structuredContent;

License

MIT