PATH:
home
/
thebhoeo
/
.trash
/
backwpup
/
vendor
/
wordpress
/
mcp-adapter
/
includes
/
Domain
/
Tools
<?php /** * RegisterAbilityAsMcpTool class for converting WordPress abilities to MCP tools. * * @package McpAdapter */ declare( strict_types=1 ); namespace WP\MCP\Domain\Tools; use WP\MCP\Domain\Utils\McpAnnotationMapper; use WP\MCP\Domain\Utils\McpNameSanitizer; use WP\MCP\Domain\Utils\McpValidator; use WP\MCP\Domain\Utils\SchemaTransformer; use WP\McpSchema\Server\Tools\DTO\Tool as ToolDto; use WP_Error; /** * RegisterAbilityAsMcpTool class. * * This class registers a WordPress ability as an MCP tool. * * @internal * * @package McpAdapter */ class RegisterAbilityAsMcpTool { /** * The WordPress ability instance. * * @var \WP_Ability */ private \WP_Ability $ability; /** * Constructor. * * @param \WP_Ability $ability The ability. */ private function __construct( \WP_Ability $ability ) { $this->ability = $ability; } /** * Build a clean Tool DTO and adapter metadata for internal wiring. * * This method returns a protocol-only Tool DTO and provides the adapter metadata * separately. This keeps the DTO stable across MCP spec changes and avoids coupling internal execution * wiring to protocol surfaces. * * @param \WP_Ability $ability The ability. * * @return array{tool: \WP\McpSchema\Server\Tools\DTO\Tool, adapter_meta: array<string, mixed>}|\WP_Error * @since 0.5.0 * */ public static function build( \WP_Ability $ability ) { $tool = new self( $ability ); $data = $tool->build_tool_data(); if ( is_wp_error( $data ) ) { return $data; } try { $tool_dto = ToolDto::fromArray( $data['tool_data'] ); } catch ( \Throwable $e ) { return new WP_Error( 'mcp_tool_dto_creation_failed', sprintf( /* translators: %s: error message */ __( 'Failed to create Tool DTO for ability %1$s: %2$s', 'mcp-adapter' ), $ability->get_name(), $e->getMessage() ), array( 'exception' => $e ) ); } // Optional deep validation if enabled. $mcp_validation_enabled = apply_filters( 'mcp_adapter_validation_enabled', false ); if ( $mcp_validation_enabled ) { $validation_result = McpToolValidator::validate_tool_dto( $tool_dto ); if ( is_wp_error( $validation_result ) ) { return $validation_result; } } return array( 'tool' => $tool_dto, 'adapter_meta' => $data['adapter_meta'], ); } /** * Build Tool DTO data and adapter metadata. * * @return array{tool_data: array<string, mixed>, adapter_meta: array<string, mixed>}|\WP_Error * @since 0.5.0 * */ private function build_tool_data() { // Resolve tool name first (can fail). $tool_name = $this->resolve_tool_name(); if ( is_wp_error( $tool_name ) ) { return $tool_name; } // Transform input schema to MCP-compatible object format. $input_transform = SchemaTransformer::transform_to_object_schema( $this->ability->get_input_schema() ); $tool_data = array( 'name' => $tool_name, 'description' => trim( $this->ability->get_description() ), 'inputSchema' => $input_transform['schema'], ); // Add optional title from ability label. $label = $this->ability->get_label(); $label = trim( $label ); if ( ! empty( $label ) ) { $tool_data['title'] = $label; } // Add optional output schema, transformed to object format if needed. $output_schema = $this->ability->get_output_schema(); $output_transform = null; if ( ! empty( $output_schema ) ) { $output_transform = SchemaTransformer::transform_to_object_schema( $output_schema, 'result' ); $tool_data['outputSchema'] = $output_transform['schema']; } // Map annotations from ability meta to MCP format using unified mapper. $ability_meta = $this->ability->get_meta(); if ( ! empty( $ability_meta['annotations'] ) && is_array( $ability_meta['annotations'] ) ) { $mcp_annotations = McpAnnotationMapper::map( $ability_meta['annotations'], 'tool' ); if ( ! empty( $mcp_annotations ) ) { $tool_data['annotations'] = $mcp_annotations; } } // Set annotations.title from label if annotations exist but don't have a title. if ( ! empty( $label ) && isset( $tool_data['annotations'] ) && ! isset( $tool_data['annotations']['title'] ) ) { $tool_data['annotations']['title'] = $label; } // Store transformation metadata as internal metadata (stripped before responding to clients). // Only record keys when semantically meaningful to keep metadata minimal and accurate. $adapter_meta = array( 'ability' => $this->ability->get_name(), ); // Only record input transformation metadata when a wrapper was actually applied. if ( ! empty( $input_transform['was_transformed'] ) ) { $adapter_meta['input_schema_transformed'] = true; $adapter_meta['input_schema_wrapper'] = $input_transform['wrapper_property']; } // Only record output transformation metadata when outputSchema exists. // Record wrapper only when transformation actually occurred. if ( null !== $output_transform && ! empty( $output_transform['was_transformed'] ) ) { $adapter_meta['output_schema_transformed'] = true; $adapter_meta['output_schema_wrapper'] = $output_transform['wrapper_property']; } // Map icons from ability.meta.mcp.icons if present. $mcp_meta = $ability_meta['mcp'] ?? array(); if ( ! empty( $mcp_meta['icons'] ) && is_array( $mcp_meta['icons'] ) ) { $icons_result = McpValidator::validate_icons_array( $mcp_meta['icons'] ); if ( ! empty( $icons_result['valid'] ) ) { $tool_data['icons'] = $icons_result['valid']; } } // Build Tool `_meta`: // - Preserve user-provided `_meta` from ability.meta.mcp._meta. // - Adapter metadata is NEVER included in protocol DTO meta; it is returned separately in adapter_meta. $tool_meta = array(); if ( ! empty( $mcp_meta['_meta'] ) && is_array( $mcp_meta['_meta'] ) ) { $tool_meta = $mcp_meta['_meta']; } if ( ! empty( $tool_meta ) ) { $tool_data['_meta'] = $tool_meta; } return array( 'tool_data' => $tool_data, 'adapter_meta' => $adapter_meta, ); } /** * Resolve the MCP tool name from ability. * * Sanitizes the ability name to MCP-valid format, applies filter, and validates result. * * @return string|\WP_Error Valid tool name or error. * @since 0.5.0 * */ private function resolve_tool_name() { // Sanitize ability name to MCP-valid format. $sanitized_name = McpNameSanitizer::sanitize_name( $this->ability->get_name() ); if ( is_wp_error( $sanitized_name ) ) { return $sanitized_name; } /** * Filters the MCP tool name derived from an ability. * * @since 0.5.0 * * @param string $name The sanitized tool name. * @param \WP_Ability $ability The source ability instance. */ $filtered_name = apply_filters( 'mcp_adapter_tool_name', $sanitized_name, $this->ability ); // Validate post-filter (in case filter broke it). if ( ! is_string( $filtered_name ) || ! McpValidator::validate_name( $filtered_name ) ) { return new WP_Error( 'mcp_tool_name_filter_invalid', sprintf( /* translators: %s: invalid tool name returned by filter */ __( 'Filter returned invalid MCP tool name: %s', 'mcp-adapter' ), is_string( $filtered_name ) ? $filtered_name : gettype( $filtered_name ) ) ); } return $filtered_name; } }
[-] RegisterAbilityAsMcpTool.php
[edit]
[-] McpToolValidator.php
[edit]
[-] McpTool.php
[edit]
[+]
..