PluginProbe ʕ •ᴥ•ʔ
CommerceBird – AI Command Center, ERP Integrations & B2B for WooCommerce (Zoho, Exact Online). / trunk
CommerceBird – AI Command Center, ERP Integrations & B2B for WooCommerce (Zoho, Exact Online). vtrunk
3.0.3 3.0.2 3.0.1 trunk 2.2.14 2.2.15 2.2.16 2.2.17 2.2.18 2.2.19 2.3.0 2.3.1 2.3.10 2.3.11 2.3.12 2.3.13 2.3.14 2.3.2 2.3.3 2.3.4 2.3.5 2.3.6 2.3.7 2.3.8 2.3.9 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.4.5 2.4.6 2.5.0 2.5.1 2.5.2 2.6.0 2.6.1 2.6.2 2.6.3 2.6.4 2.6.5 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 2.7.8 2.7.9 2.7.91 2.7.92 2.7.93 2.8.0 2.8.1 2.8.2 2.8.3 2.8.4 2.8.5 2.9.0 2.9.1 2.9.2 2.9.3 3.0.0
commercebird / vendor / wordpress / mcp-adapter / includes / Transport / HttpTransport.php
commercebird / vendor / wordpress / mcp-adapter / includes / Transport Last commit date
Contracts 2 months ago Infrastructure 2 months ago HttpTransport.php 2 months ago
HttpTransport.php
152 lines
1 <?php
2 /**
3 * MCP HTTP Transport for WordPress (MCP 2025-11-25 baseline)
4 *
5 * This transport implements the MCP HTTP transport surface used by this plugin.
6 * It can work both with and without the mcp-wordpress-remote proxy.
7 *
8 * Note: SSE (GET streaming) is not yet implemented; GET currently returns 405.
9 *
10 * @package McpAdapter
11 */
12
13 declare( strict_types=1 );
14
15 namespace WP\MCP\Transport;
16
17 use WP\MCP\Transport\Contracts\McpRestTransportInterface;
18 use WP\MCP\Transport\Infrastructure\HttpRequestContext;
19 use WP\MCP\Transport\Infrastructure\HttpRequestHandler;
20 use WP\MCP\Transport\Infrastructure\McpTransportContext;
21 use WP\MCP\Transport\Infrastructure\McpTransportHelperTrait;
22
23 /**
24 * MCP HTTP Transport - Unified transport for both proxy and direct clients
25 *
26 * Implements the MCP 2025-11-25 HTTP transport shape used by this adapter (POST + sessions).
27 *
28 * Note: SSE (GET streaming) is not yet implemented; GET currently returns 405.
29 */
30 class HttpTransport implements McpRestTransportInterface {
31 use McpTransportHelperTrait;
32
33 /**
34 * The HTTP request handler.
35 *
36 * @var \WP\MCP\Transport\Infrastructure\HttpRequestHandler
37 */
38 protected HttpRequestHandler $request_handler;
39
40 /**
41 * Initialize the class and register routes
42 *
43 * @param \WP\MCP\Transport\Infrastructure\McpTransportContext $transport_context The transport context.
44 */
45 public function __construct( McpTransportContext $transport_context ) {
46 $this->request_handler = new HttpRequestHandler( $transport_context );
47 add_action( 'rest_api_init', array( $this, 'register_routes' ), 16 );
48 }
49
50 /**
51 * Register MCP HTTP routes
52 */
53 public function register_routes(): void {
54 // Get server info from request handler's transport context
55 $server = $this->request_handler->get_transport_context()->mcp_server;
56
57 // Single endpoint for MCP communication (POST, GET reserved for SSE, DELETE for session termination).
58 // Do not remove GET: it is part of the MCP HTTP transport shape and will be implemented (SSE) in a future iteration.
59 register_rest_route(
60 $server->get_server_route_namespace(),
61 $server->get_server_route(),
62 array(
63 'methods' => array( 'POST', 'GET', 'DELETE' ),
64 'callback' => array( $this, 'handle_request' ),
65 'permission_callback' => array( $this, 'check_permission' ),
66 )
67 );
68 }
69
70 /**
71 * Check if the user has permission to access the MCP API
72 *
73 * @param \WP_REST_Request<array<string, mixed>> $request The request object.
74 *
75 * @return bool True if the user has permission, false otherwise.
76 */
77 public function check_permission( \WP_REST_Request $request ) {
78 $context = new HttpRequestContext( $request );
79
80 // Check permission using callback or default
81 $transport_context = $this->request_handler->get_transport_context();
82
83 if ( null !== $transport_context->transport_permission_callback ) {
84 try {
85 $result = call_user_func( $transport_context->transport_permission_callback, $context->request );
86
87 // Handle WP_Error returns
88 if ( ! is_wp_error( $result ) ) {
89 // Cast to bool to match return type while preserving truthy/falsy semantics.
90 return (bool) $result;
91 }
92
93 // Log the error and deny access (fail-closed)
94 $this->request_handler->get_transport_context()->error_handler->log(
95 'Permission callback returned WP_Error: ' . $result->get_error_message(),
96 array( 'HttpTransport::check_permission' )
97 );
98
99 return false;
100 } catch ( \Throwable $e ) {
101 // Log the error and deny access (fail-closed)
102 $this->request_handler->get_transport_context()->error_handler->log( 'Error in transport permission callback: ' . $e->getMessage(), array( 'HttpTransport::check_permission' ) );
103
104 return false;
105 }
106 }
107
108 /**
109 * Filters the default user capability required for MCP transport access.
110 *
111 * This filter is only applied when no custom transport permission callback
112 * is provided. The capability is checked using current_user_can().
113 *
114 * @since 0.3.0
115 *
116 * @param string $capability The required capability. Default 'read'.
117 * @param \WP\MCP\Transport\Infrastructure\HttpRequestContext $context The HTTP request context.
118 */
119 $user_capability = apply_filters( 'mcp_adapter_default_transport_permission_user_capability', 'read', $context );
120
121 // Validate that the filtered capability is a non-empty string
122 if ( ! is_string( $user_capability ) || empty( $user_capability ) ) {
123 $user_capability = 'read';
124 }
125
126 $user_has_capability = current_user_can( $user_capability ); // phpcs:ignore WordPress.WP.Capabilities.Undetermined -- Capability is filtered and defaults to 'read'
127
128 if ( ! $user_has_capability ) {
129 $user_id = get_current_user_id();
130 $this->request_handler->get_transport_context()->error_handler->log(
131 sprintf( 'Permission denied for MCP API access. User ID %d does not have capability "%s"', $user_id, $user_capability ),
132 array( 'HttpTransport::check_permission' )
133 );
134 }
135
136 return $user_has_capability;
137 }
138
139 /**
140 * Handle HTTP requests according to MCP 2025-11-25 specification
141 *
142 * @param \WP_REST_Request<array<string, mixed>> $request The request object.
143 *
144 * @return \WP_REST_Response
145 */
146 public function handle_request( \WP_REST_Request $request ): \WP_REST_Response {
147 $context = new HttpRequestContext( $request );
148
149 return $this->request_handler->handle_request( $context );
150 }
151 }
152