MCP Adapter 开发文档

可观测性

Estimated reading: 15 minutes 2 views 贡献人员

可观测性

MCP Adapter 使用基于接口的可观测性系统和统一事件记录架构,在整个请求生命周期中跟踪指标和事件。

系统概述

可观测性系统有两个主要组件:

  • 事件跟踪McpObservabilityHandlerInterface 实现跟踪事件和指标
  • 助手工具McpObservabilityHelperTrait 提供标签管理和错误分类
use WPMCPInfrastructureObservabilityContractsMcpObservabilityHandlerInterface;

interface McpObservabilityHandlerInterface {
    public function record_event(string $event, array $tags = [], ?float $duration_ms = null): void;
}

架构:基于元数据的可观测性

可观测性系统遵循中间件模式,其中处理程序返回增强的元数据,这些元数据流向传输层以进行集中事件记录:

  1. 处理程序(业务逻辑层):执行业务逻辑并将 _metadata 附加到响应
  2. RequestRouter(传输层):提取 _metadata,与请求上下文合并,并记录事件
  3. ObservabilityHandler:从单个点接收具有丰富上下文的统一事件

优势:

  • 单一事实来源:所有可观测性通过 RequestRouter 流动
  • 一致计时:在传输层为所有请求跟踪持续时间
  • DRY 原则:处理程序中无重复事件记录
  • 清晰分离:处理程序专注于业务逻辑,而非可观测性

事件发射模式

  • MCP Adapter:处理程序将元数据附加到响应
  • RequestRouter:提取元数据并以一致结构发射事件
  • 处理程序:将事件发送到外部系统(日志、StatsD、Prometheus 等)
  • 外部系统:聚合和分析事件

内置处理程序

NullMcpObservabilityHandler

无操作处理程序,忽略所有事件(可观测性禁用时零开销):

$handler = new NullMcpObservabilityHandler();
$handler->record_event('test.event', []); // 什么都不做
$handler->record_event('test.metric', [], 123.45); // 带计时的事件 - 什么都不做

ErrorLogMcpObservabilityHandler

将事件和指标记录到 PHP 错误日志,采用结构化格式:

$handler = new ErrorLogMcpObservabilityHandler();
$handler->record_event('mcp.request', ['status' => 'success', 'method' => 'tools/call'], 45.23);
// 记录:[MCP Observability] EVENT mcp.request 45.23ms [status=success,method=tools/call,site_id=1,user_id=123,timestamp=1234567890]

跟踪的事件

所有事件使用一致的命名模式和状态标签,以便于过滤和聚合。

请求事件

事件: mcp.request

标签:

  • statussuccess | error
  • method:MCP 方法(例如 tools/callresources/list
  • transport:传输类型(例如 http
  • server_id:MCP 服务器 ID
  • request_id:JSON-RPC 请求 ID
  • session_id:MCP 会话 ID(无会话时为 null)
  • params:已清理的请求参数(仅安全字段)
  • error_code:JSON-RPC 错误代码(仅错误时)
  • error_type:异常类名(仅异常时)
  • error_category:错误类别(validation、execution、logic、system、type、arguments、unknown)

来自处理程序元数据的附加标签:

  • component_typetool | resource | prompt | tools | resources | prompts
  • tool_name:工具名称(工具请求时)
  • ability_name:WordPress 能力名称(适用时)
  • prompt_name:提示词名称(提示词请求时)
  • resource_uri:资源 URI(资源请求时)
  • failurereason:特定失败原因(见下文)- 可用时使用 WPError 代码
  • newsessionid:新创建的会话 ID(仅初始化请求时)

包含持续时间计时: 是(毫秒)

示例:

// 成功的工具执行
[
  'event' => 'mcp.request',
  'tags' => [
    'status' => 'success',
    'method' => 'tools/call',
    'transport' => 'http',
    'server_id' => 'default',
    'request_id' => 82,
    'session_id' => 'a3f2c1d4-5e6f-7890-abcd-ef1234567890',
    'params' => ['name' => 'create-post', 'arguments_count' => 2],
    'component_type' => 'tool',
    'tool_name' => 'create-post',
    'ability_name' => 'create_post',
  ],
  'duration_ms' => 45.23
]

// 失败的请求 - 工具未找到
[
  'event' => 'mcp.request',
  'tags' => [
    'status' => 'error',
    'method' => 'tools/call',
    'transport' => 'http',
    'server_id' => 'default',
    'request_id' => 83,
    'session_id' => 'a3f2c1d4-5e6f-7890-abcd-ef1234567890',
    'params' => ['name' => 'invalid-tool'],
    'component_type' => 'tool',
    'tool_name' => 'invalid-tool',
    'failure_reason' => 'not_found',
    'error_code' => -32002,
  ],
  'duration_ms' => 2.15
]

// 初始化请求(创建新会话)
[
  'event' => 'mcp.request',
  'tags' => [
    'status' => 'success',
    'method' => 'initialize',
    'transport' => 'http',
    'server_id' => 'default',
    'request_id' => 1,
    'session_id' => null, // 还没有会话
    'params' => ['protocolVersion' => '2025-06-18', 'client_name' => 'Bruno'],
    'new_session_id' => 'a3f2c1d4-5e6f-7890-abcd-ef1234567890', // 新创建的
  ],
  'duration_ms' => 12.34
]

// 权限被拒绝,包含详细的 WP_Error
[
  'event' => 'mcp.request',
  'tags' => [
    'status' => 'error',
    'method' => 'tools/call',
    'transport' => 'http',
    'server_id' => 'default',
    'request_id' => 84,
    'session_id' => 'a3f2c1d4-5e6f-7890-abcd-ef1234567890',
    'params' => ['name' => 'user-notifications', 'arguments_count' => 1],
    'component_type' => 'tool',
    'tool_name' => 'user-notifications',
    'ability_name' => 'wpcom-mcp/user-notifications',
    'failure_reason' => 'ability_invalid_input', // 直接使用 WP_Error 代码
    'error_code' => -32004,
  ],
  'duration_ms' => 8.51
]

注意: 当 WordPress 能力从 haspermission() 返回 WPError 对象时,错误代码会自动用作 failurereason,提供 abilityinvalidinputabilitypermissionerror 等具体上下文。这使得跟踪特定权限失败类型变得更加容易。如果返回布尔值 false,则使用通用的 permissiondenied 原因。

失败原因

failurereason 标签提供了错误的具体上下文。当 WordPress 能力返回 WPError 对象时,错误代码会直接用作失败原因。

标准失败原因:

工具相关:

  • not_found:工具不存在
  • permission_denied:权限检查返回 false(通用)
  • permissioncheckfailed:权限回调抛出异常
  • wperror:WordPress 能力在执行期间返回 WPError
  • execution_failed:工具执行抛出异常
  • missing_parameter:缺少必填参数
  • 任何 WPError 代码:例如 abilityinvalidinputabilitypermissionerrorabilityrate_limit

提示词相关:

  • not_found:提示词不存在
  • permission_denied:权限被拒绝(通用)
  • execution_failed:提示词执行抛出异常
  • missing_parameter:缺少必填参数
  • 任何 WP_Error 代码:来自能力权限检查的特定错误代码

资源相关:

  • not_found:资源不存在
  • permission_denied:权限被拒绝(通用)
  • execution_failed:资源读取抛出异常
  • missing_parameter:缺少必填参数
  • 任何 WP_Error 代码:来自能力权限检查的特定错误代码

作为失败原因的 WP_Error 代码示例:

  • abilityinvalidinput:输入验证失败
  • abilitypermissionerror:特定权限问题
  • abilityratelimit:速率限制已超出
  • abilityquotaexceeded:配额已超出
  • 您的能力返回的任何自定义错误代码

组件注册事件

事件: mcp.component.registration

标签:

  • statussuccess | failed
  • componenttypetool | resource | prompt | abilitytool
  • component_name:组件名称
  • server_id:MCP 服务器 ID
  • error_type:异常类名(仅失败时)

包含持续时间计时:

默认行为:组件注册事件默认禁用,以避免在服务器启动期间污染可观测性日志。在需要时使用下面的过滤器启用它们。

示例:

// 成功的工具注册
[
  'event' => 'mcp.component.registration',
  'tags' => [
    'status' => 'success',
    'component_type' => 'tool',
    'component_name' => 'create_post',
    'server_id' => 'default',
  ]
]

// 失败的资源注册
[
  'event' => 'mcp.component.registration',
  'tags' => [
    'status' => 'failed',
    'component_type' => 'resource',
    'component_name' => 'invalid_ability',
    'error_type' => 'InvalidArgumentException',
    'server_id' => 'default',
  ]
]

控制组件注册事件

组件注册事件默认禁用,但可以使用 mcpadapterobservabilityrecordcomponent_registration 过滤器启用:

// 全局启用组件注册事件
add_filter('mcp_adapter_observability_record_component_registration', '__return_true');

// 仅在调试时启用
add_filter('mcp_adapter_observability_record_component_registration', function($should_record) {
    return defined('WP_DEBUG') && WP_DEBUG;
});

// 仅在开发环境中启用
add_filter('mcp_adapter_observability_record_component_registration', function($should_record) {
    return wp_get_environment_type() === 'development';
});

此过滤器对于以下情况特别有用:

  • 在开发过程中调试组件加载问题
  • 在预发布环境中排除注册失败问题
  • 通过禁用启动噪声来保持生产日志清洁

服务器事件

事件: mcp.server.created

标签:

  • statussuccess
  • server_id:服务器 ID
  • transport_count:传输数量
  • tools_count:工具数量
  • resources_count:资源数量
  • prompts_count:提示词数量

包含持续时间计时:

通用标签

所有事件自动包含这些标签:

  • site_id:WordPress 站点 ID
  • user_id:WordPress 用户 ID
  • timestamp:Unix 时间戳

助手特质

McpObservabilityHelperTrait 为处理程序提供了实用方法:

标签管理

  • getdefaulttags():默认标签(siteid、userid、timestamp)
  • sanitize_tags():删除敏感数据并限制标签长度
  • merge_tags():合并用户标签与默认标签
  • formatmetricname():确保一致的指标命名,带有 'mcp.' 前缀

错误处理

  • categorize_error():将异常分类到标准类别
use WPMCPInfrastructureObservabilityMcpObservabilityHelperTrait;

class MyHandler implements McpObservabilityHandlerInterface {
    use McpObservabilityHelperTrait;
    
    public function record_event(string $event, array $tags = [], ?float $duration_ms = null): void {
        $formatted_event = self::format_metric_name($event);
        $merged_tags = self::merge_tags($tags);
        // ... 使用可选的计时发送到您的系统:$duration_ms
    }
}

创建自定义处理程序

实现 McpObservabilityHandlerInterface 以创建自定义处理程序:

基于文件的处理程序

use WPMCPInfrastructureObservabilityContractsMcpObservabilityHandlerInterface;
use WPMCPInfrastructureObservabilityMcpObservabilityHelperTrait;

class FileObservabilityHandler implements McpObservabilityHandlerInterface {
    use McpObservabilityHelperTrait;
    
    public function record_event(string $event, array $tags = [], ?float $duration_ms = null): void {
        $formatted_event = self::format_metric_name($event);
        $merged_tags = self::merge_tags($tags);
        
        // 如果提供了时间信息,则包含计时
        $timing_info = $duration_ms !== null ? sprintf(' %.2fms', $duration_ms) : '';
        $log_entry = sprintf('[MCP Event] %s%s | Tags: %s', 
            $formatted_event,
            $timing_info,
            wp_json_encode($merged_tags)
        );
        
        file_put_contents(WP_CONTENT_DIR . '/mcp-metrics.log', 
            $log_entry . "n", FILE_APPEND | LOCK_EX);
    }
}

外部服务处理程序

class ExternalServiceObservabilityHandler implements McpObservabilityHandlerInterface {
    use McpObservabilityHelperTrait;
    
    public function record_event(string $event, array $tags = [], ?float $duration_ms = null): void {
        $payload = [
            'type' => 'event',
            'name' => self::format_metric_name($event),
            'tags' => self::merge_tags($tags),
            'site' => get_site_url()
        ];
        
        // 如果提供了持续时间,则包含
        if ($duration_ms !== null) {
            $payload['duration_ms'] = $duration_ms;
        }
        
        wp_remote_post('https://metrics.example.com/api/events', [
            'body' => wp_json_encode($payload),
            'headers' => ['Content-Type' => 'application/json'],
            'timeout' => 5
        ]);
    }
}

使用自定义处理程序

一旦创建了自定义可观测性处理程序,您就可以将它们配置用于您的 MCP Adapter 设置。

替换默认服务器的可观测性处理程序

适配器创建的默认 MCP 服务器可以使用 mcpadapterdefaultserverconfig 过滤器替换其可观测性处理程序:

// 替换默认服务器的可观测性处理程序
add_filter('mcp_adapter_default_server_config', function($config) {
    $config['observability_handler'] = FileObservabilityHandler::class;
    return $config;
});

// 或完全禁用可观测性
add_filter('mcp_adapter_default_server_config', function($config) {
    $config['observability_handler'] = NullMcpObservabilityHandler::class;
    return $config;
});

为自定义服务器配置可观测性

创建自定义服务器时,您可以直接指定可观测性处理程序:

// 在您的插件初始化中
add_action('mcp_adapter_init', function($adapter) {
    $adapter->create_server(
        'my-custom-server',
        'my-namespace',
        'my-route',
        '我的自定义服务器',
        '带有基于文件的可观测性的自定义 MCP 服务器',
        '1.0.0',
        [MyCustomTransport::class],
        null, // 使用默认错误处理程序
        FileObservabilityHandler::class, // 自定义可观测性处理程序
        ['my-tool'], // 工具
        [], // 资源
        [], // 提示词
        null // 传输权限回调
    );
});

查询事件

使用统一的事件结构,您可以轻松查询和分析指标:

按方法的成功率

SELECT 
  tags->>'method' as method,
  SUM(CASE WHEN tags->>'status' = 'success' THEN 1 ELSE 0 END) * 100.0 / COUNT(*) as success_rate
FROM mcp_events
WHERE event = 'mcp.request'
GROUP BY tags->>'method'

工具性能

SELECT 
  tags->>'tool_name' as tool_name,
  AVG(duration_ms) as avg_duration,
  COUNT(*) as call_count
FROM mcp_events
WHERE event = 'mcp.request' 
  AND tags->>'component_type' = 'tool'
  AND tags->>'status' = 'success'
GROUP BY tags->>'tool_name'
ORDER BY call_count DESC

失败分析

SELECT 
  tags->>'failure_reason' as reason,
  tags->>'error_category' as category,
  COUNT(*) as count
FROM mcp_events
WHERE event = 'mcp.request' 
  AND tags->>'status' = 'error'
GROUP BY tags->>'failure_reason', tags->>'error_category'
ORDER BY count DESC

最佳实践

  1. 使用状态进行过滤:按 status 标签查询以分离成功和失败
  2. 按事件名称分组:所有请求使用 mcp.request,使聚合变得简单
  3. 利用失败原因:使用 failure_reason 进行详细错误分析
  4. 监控持续时间:使用 duration 字段跟踪性能趋势
  5. 对模式进行警报:为特定的 failure_reason 值设置警报
  6. 上下文丰富的日志记录:处理程序元数据自动提供组件特定的上下文

留下第一个评论