PluginProbe
ZIP AI – AI Website Builder & AI Agent (Beta) / 0.0.8
ZIP AI – AI Website Builder & AI Agent (Beta) v0.0.8
0.0.10 0.0.9 trunk 0.0.4 0.0.5 0.0.6 0.0.7 0.0.8
zip-ai / lib / mcp-adapter / includes / Transport / HttpTransport.php

HttpTransport.php in ZIP AI – AI Website Builder & AI Agent (Beta) 0.0.8, at lib/mcp-adapter/includes/Transport/HttpTransport.php

152 lines 5.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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