MCP Servers

模型上下文协议服务器、框架、SDK 和模板的综合目录。

Standardized event classes for MCP tool execution lifecycle in PHP

创建于 1/9/2026
更新于 about 21 hours ago
Repository documentation and setup instructions

MCP Events

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

快速设置
此服务器的安装指南

安装命令 (包未发布)

git clone https://github.com/code-wheel/mcp-events
手动安装: 请查看 README 获取详细的设置说明和所需的其他依赖项。

Cursor 配置 (mcp.json)

{ "mcpServers": { "code-wheel-mcp-events": { "command": "git", "args": [ "clone", "https://github.com/code-wheel/mcp-events" ] } } }