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
Requires
- php: >=8.1
Requires (Dev)
- phpunit/phpunit: ^10.0 || ^11.0
Suggests
- mcp/sdk: For type-safe CallToolResult objects (^0.2)
This package is auto-updated.
Last update: 2026-01-10 06:23:32 UTC
README
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