PluginProbe ʕ •ᴥ•ʔ
AI Engine – The Chatbot, AI Framework & MCP for WordPress / 3.4.9
AI Engine – The Chatbot, AI Framework & MCP for WordPress v3.4.9
3.5.9 3.5.8 3.5.7 3.5.6 3.5.5 3.5.4 3.5.3 3.5.2 3.5.1 3.5.0 3.4.9 3.4.8 3.4.7 0.2.1 1.6.91 0.2.2 1.6.92 0.2.3 1.6.93 0.2.4 1.6.94 0.2.5 1.6.95 0.2.6 1.6.96 0.2.7 1.6.97 0.2.8 1.6.98 0.2.9 1.6.99 0.3.0 1.7.0 0.3.1 1.7.1 0.3.2 1.7.2 0.3.3 1.7.3 0.3.4 1.7.4 0.3.5 1.7.5 0.3.6 1.7.6 0.4.0 1.7.7 0.4.1 1.7.8 0.4.2 1.7.9 0.4.3 1.8.0 0.4.4 1.8.1 0.4.5 1.8.2 0.4.6 1.8.3 0.4.7 1.8.4 0.4.8 1.8.5 0.4.9 1.8.6 0.5.0 1.8.7 0.5.1 1.8.8 0.5.2 1.8.9 0.5.3 1.9.0 0.5.4 1.9.1 0.5.5 1.9.2 0.5.6 1.9.3 0.5.7 1.9.4 0.5.8 1.9.5 0.5.9 1.9.6 0.6.0 1.9.7 0.6.1 1.9.8 0.6.2 1.9.81 0.6.3 1.9.82 0.6.4 1.9.83 0.6.5 1.9.84 0.6.6 1.9.85 0.6.7 1.9.86 0.6.8 1.9.87 0.6.9 1.9.88 0.7.0 1.9.89 0.7.1 1.9.90 0.7.2 1.9.91 0.7.3 1.9.92 0.7.4 1.9.93 0.7.5 1.9.94 0.7.6 1.9.95 0.7.7 1.9.96 0.7.8 1.9.97 0.7.9 1.9.98 0.8.0 1.9.99 0.8.1 2.0.0 0.8.2 2.0.1 0.8.3 2.0.2 0.8.4 2.0.3 0.8.5 2.0.4 0.8.6 2.0.5 0.8.7 2.0.6 0.8.8 2.0.7 0.8.9 2.0.8 0.9.0 2.0.9 0.9.2 2.1.0 0.9.3 2.1.1 0.9.4 2.1.2 0.9.5 2.1.3 0.9.6 2.1.4 0.9.7 2.1.5 0.9.8 2.1.6 0.9.81 2.1.7 0.9.82 2.1.8 0.9.83 2.1.9 0.9.84 2.2.0 0.9.85 2.2.1 0.9.86 2.2.2 0.9.87 2.2.3 0.9.88 2.2.4 0.9.89 2.2.5 0.9.9 2.2.51 0.9.91 2.2.52 0.9.92 2.2.53 0.9.93 2.2.54 0.9.94 2.2.56 0.9.95 2.2.57 0.9.96 2.2.6 0.9.97 2.2.60 0.9.98 2.2.61 0.9.99 2.2.62 1.0.0 2.2.63 1.0.01 2.2.70 1.0.1 2.2.80 1.0.2 2.2.81 1.0.3 2.2.90 1.0.4 2.2.91 1.0.5 2.2.92 1.0.6 2.2.93 1.0.7 2.2.94 1.0.8 2.2.95 1.0.9 2.3.0 1.1.0 2.3.1 1.1.1 2.3.2 1.1.2 2.3.3 1.1.3 2.3.4 1.1.4 2.3.5 1.1.5 2.3.6 1.1.6 2.3.7 1.1.7 2.3.8 1.1.8 2.3.9 1.1.9 2.4.0 1.2.0 2.4.1 1.2.1 2.4.2 1.2.2 2.4.3 1.2.21 2.4.4 1.2.3 2.4.5 1.2.30 2.4.6 1.3.0 2.4.7 1.3.1 2.4.8 1.3.2 2.4.9 1.3.3 2.5.0 1.3.31 2.5.1 1.3.32 2.5.2 1.3.33 2.5.3 1.3.34 2.5.4 1.3.35 2.5.5 1.3.36 2.5.6 1.3.37 2.5.7 1.3.38 2.5.8 1.3.39 2.5.9 1.3.40 2.6.0 1.3.41 2.6.1 1.3.42 2.6.2 1.3.43 2.6.3 1.3.44 2.6.5 1.3.45 2.6.6 1.3.46 2.6.7 1.3.47 2.6.8 1.3.48 2.6.9 1.3.49 2.7.0 1.3.50 2.7.1 1.3.51 2.7.2 1.3.52 2.7.3 1.3.53 2.7.4 1.3.54 2.7.5 1.3.56 2.7.6 1.3.57 2.7.7 1.3.58 2.7.8 1.3.59 2.7.9 1.3.60 2.8.0 1.3.61 2.8.1 1.3.62 2.8.2 1.3.63 2.8.3 1.3.64 2.8.4 1.3.65 2.8.5 1.3.66 2.8.6 1.3.67 2.8.7 1.3.68 2.8.8 1.3.69 2.8.9 1.3.70 2.9.0 1.3.71 2.9.1 1.3.72 2.9.2 1.3.73 2.9.3 1.3.74 2.9.4 1.3.75 2.9.5 1.3.76 2.9.6 1.3.77 2.9.7 1.3.78 2.9.8 1.3.79 2.9.9 1.3.80 3.0.0 1.3.81 3.0.1 1.3.82 3.0.2 1.3.83 3.0.3 1.3.84 3.0.4 1.3.85 3.0.5 1.3.86 3.0.6 1.3.87 3.0.7 1.3.88 3.0.8 1.3.89 3.0.9 1.3.90 3.1.0 1.3.91 3.1.1 1.3.92 3.1.2 1.3.93 3.1.3 1.3.94 3.1.4 1.3.95 3.1.5 1.3.96 3.1.6 1.3.97 3.1.7 1.3.98 3.1.8 1.3.99 3.1.9 1.4.0 3.2.0 1.4.1 3.2.1 1.4.2 3.2.2 1.4.3 3.2.3 1.4.4 3.2.4 1.4.5 3.2.5 1.4.6 3.2.6 1.4.7 3.2.7 1.4.8 3.2.8 1.4.9 3.2.9 1.5.0 3.3.0 1.5.1 3.3.1 1.5.2 3.3.2 1.5.3 3.3.3 1.5.4 3.3.4 1.5.5 3.3.5 1.5.6 3.3.6 1.5.7 3.3.7 1.5.8 3.3.8 1.5.9 3.3.9 1.6.0 3.4.0 1.6.1 3.4.1 1.6.2 3.4.2 1.6.3 3.4.3 1.6.5 3.4.4 1.6.51 3.4.5 1.6.52 3.4.6 1.6.53 1.6.54 1.6.55 1.6.56 1.6.57 1.6.58 1.6.59 1.6.60 1.6.61 1.6.62 1.6.63 1.6.64 1.6.65 1.6.66 1.6.67 1.6.68 trunk 1.6.69 0.0.1 1.6.70 0.0.2 1.6.71 0.0.3 1.6.72 0.0.4 1.6.73 0.0.5 1.6.74 0.0.6 1.6.75 0.0.7 1.6.76 0.0.8 1.6.77 0.0.9 1.6.78 0.1.0 1.6.79 0.1.1 1.6.81 0.1.2 1.6.82 0.1.3 1.6.83 0.1.4 1.6.84 0.1.5 1.6.85 0.1.6 1.6.86 0.1.7 1.6.87 0.1.8 1.6.88 0.1.9 1.6.89 0.2.0 1.6.90
ai-engine / labs / mcp-rest.php
ai-engine / labs Last commit date
mcp-core.php 1 month ago mcp-oauth.php 1 month ago mcp-rest.php 2 months ago mcp.conf 1 year ago mcp.js 8 months ago mcp.md 8 months ago mcp.php 1 month ago wpai-connectors.php 1 month ago wpai-gateway-availability.php 2 months ago wpai-gateway-directory.php 2 months ago wpai-gateway-image-model.php 2 months ago wpai-gateway-model.php 2 months ago wpai-gateway-providers.php 2 months ago wpai-gateway.php 2 months ago
mcp-rest.php
266 lines
1 <?php
2
3 class Meow_MWAI_Labs_MCP_Rest {
4 // Bump the suffix when build_schema_from_args() changes so old cached schemas are ignored.
5 private $cache_key = 'mwai_mcp_tools_cache_v2';
6 private $allowed = [ 'posts', 'pages', 'media' ];
7
8 public function __construct() {
9 add_action( 'rest_api_init', [ $this, 'rest_api_init' ] );
10 }
11
12 public function rest_api_init() {
13 add_filter( 'mwai_mcp_tools', [ $this, 'register_rest_tools' ] );
14 add_filter( 'mwai_mcp_callback', [ $this, 'handle_call' ], 10, 4 );
15 }
16
17 public function register_rest_tools( $prevTools ) {
18 $cached = get_transient( $this->cache_key );
19
20 if ( !$cached ) {
21 $tools = [];
22 $server = rest_get_server();
23 $routes = $server->get_routes();
24
25 foreach ( $this->allowed as $resource ) {
26 $base = "/wp/v2/{$resource}";
27 $item = "{$base}/(?P<id>[\d]+)";
28
29 if ( isset( $routes[ $base ] ) ) {
30 foreach ( $routes[ $base ] as $endpoint ) {
31 if ( !empty( $endpoint['methods']['GET'] ) ) {
32 $tools[ "list_{$resource}" ] = [
33 'name' => "list_{$resource}",
34 'description' => "List {$resource}",
35 'category' => 'Dynamic REST',
36 'inputSchema' => $this->build_schema_from_args( $endpoint['args'] ),
37 'outputSchema' => $this->build_output_schema(),
38 'accessLevel' => 'read',
39 ];
40 break;
41 }
42 }
43 }
44
45 if ( isset( $routes[ $item ] ) ) {
46 foreach ( $routes[ $item ] as $endpoint ) {
47 if ( !empty( $endpoint['methods']['GET'] ) ) {
48 $tools[ "get_{$resource}" ] = [
49 'name' => "get_{$resource}",
50 'description' => "Get single {$resource} by ID",
51 'category' => 'Dynamic REST',
52 'inputSchema' => $this->build_schema_from_args( $endpoint['args'] ),
53 'outputSchema' => $this->build_output_schema(),
54 'accessLevel' => 'read',
55 ];
56 break;
57 }
58 }
59 }
60
61 if ( isset( $routes[ $base ] ) ) {
62 foreach ( $routes[ $base ] as $endpoint ) {
63 if ( !empty( $endpoint['methods']['POST'] ) ) {
64 $tools[ "create_{$resource}" ] = [
65 'name' => "create_{$resource}",
66 'description' => "Create {$resource}",
67 'category' => 'Dynamic REST',
68 'inputSchema' => $this->build_schema_from_args( $endpoint['args'] ),
69 'outputSchema' => $this->build_output_schema(),
70 'accessLevel' => 'write',
71 ];
72 break;
73 }
74 }
75 }
76
77 if ( isset( $routes[ $item ] ) ) {
78 foreach ( $routes[ $item ] as $endpoint ) {
79 $methods = array_keys( $endpoint['methods'] );
80 if ( array_intersect( [ 'POST', 'PUT', 'PATCH' ], $methods ) ) {
81 $tools[ "update_{$resource}" ] = [
82 'name' => "update_{$resource}",
83 'description' => "Update {$resource}",
84 'category' => 'Dynamic REST',
85 'inputSchema' => $this->build_schema_from_args( $endpoint['args'] ),
86 'outputSchema' => $this->build_output_schema(),
87 'accessLevel' => 'write',
88 ];
89 break;
90 }
91 }
92 }
93
94 if ( isset( $routes[ $item ] ) ) {
95 foreach ( $routes[ $item ] as $endpoint ) {
96 if ( !empty( $endpoint['methods']['DELETE'] ) ) {
97 $tools[ "delete_{$resource}" ] = [
98 'name' => "delete_{$resource}",
99 'description' => "Delete {$resource}",
100 'category' => 'Dynamic REST',
101 'inputSchema' => $this->build_schema_from_args( $endpoint['args'] ),
102 'outputSchema' => $this->build_output_schema(),
103 'accessLevel' => 'admin',
104 ];
105 break;
106 }
107 }
108 }
109 }
110
111 set_transient( $this->cache_key, $tools, DAY_IN_SECONDS );
112 $cached = $tools;
113 }
114
115 return array_merge( array_values( $cached ), $prevTools );
116 }
117
118 private function build_schema_from_args( $args ) {
119 $schema = [
120 'type' => 'object',
121 'properties' => [],
122 'required' => [],
123 ];
124
125 // JSON Schema keys worth forwarding from WordPress REST arg definitions.
126 // PHP callbacks (sanitize_callback/validate_callback) and WP-only keys (arg_options,
127 // required) are intentionally excluded - clients would choke on them.
128 $allowed_keys = [
129 'type', 'description', 'enum', 'default', 'format',
130 'items', 'properties', 'additionalProperties',
131 'minimum', 'maximum', 'exclusiveMinimum', 'exclusiveMaximum', 'multipleOf',
132 'minLength', 'maxLength', 'pattern',
133 'minItems', 'maxItems', 'uniqueItems',
134 'oneOf', 'anyOf', 'allOf',
135 ];
136
137 foreach ( $args as $name => $def ) {
138 $property = [];
139 foreach ( $allowed_keys as $key ) {
140 if ( array_key_exists( $key, $def ) ) {
141 $property[ $key ] = $def[ $key ];
142 }
143 }
144 if ( !isset( $property['type'] ) ) {
145 $property['type'] = 'string';
146 }
147 if ( !isset( $property['description'] ) ) {
148 $property['description'] = '';
149 }
150
151 $schema['properties'][ $name ] = $property;
152
153 if ( !empty( $def['required'] ) ) {
154 $schema['required'][] = $name;
155 }
156 }
157
158 return $schema;
159 }
160
161 private function build_output_schema() {
162 return [
163 'type' => 'object',
164 'properties' => [
165 'content' => [
166 'type' => 'array',
167 'items' => [
168 'type' => 'object',
169 'properties' => [
170 'type' => [
171 'type' => 'string',
172 'description' => 'Block type, e.g. text or image',
173 ],
174 'text' => [
175 'type' => 'string',
176 'description' => 'Human-readable content',
177 ],
178 ],
179 'required' => [ 'type', 'text' ],
180 ],
181 ],
182 ],
183 'required' => [ 'content' ],
184 ];
185 }
186
187 public function handle_call( $existing, $tool, $args, $id ) {
188 if ( !empty( $existing ) ) {
189 return $existing;
190 }
191
192 $tools = get_transient( $this->cache_key );
193 if ( !isset( $tools[ $tool ] ) ) {
194 return $existing;
195 }
196
197 // Security check is already done in the MCP auth layer
198 // If we reach here, the user is authorized to use MCP
199
200 list( $action, $resource ) = explode( '_', $tool, 2 );
201 $path = "/wp/v2/{$resource}";
202 $method = 'GET';
203
204 if ( in_array( $action, [ 'get', 'update', 'delete' ], true ) ) {
205 if ( empty( $args['id'] ) ) {
206 return [
207 'jsonrpc' => '2.0',
208 'id' => $id,
209 'error' => [
210 'code' => -32602,
211 'message' => 'Missing parameter: id',
212 ],
213 ];
214 }
215 $path .= '/' . intval( $args['id'] );
216 }
217
218 switch ( $action ) {
219 case 'create':
220 case 'update':
221 $method = 'POST';
222 break;
223 case 'delete':
224 $method = 'DELETE';
225 break;
226 default:
227 $method = 'GET';
228 break;
229 }
230
231 $request = new WP_REST_Request( $method, $path );
232
233 if ( $method === 'GET' ) {
234 foreach ( $args as $key => $value ) {
235 $request->set_param( $key, $value );
236 }
237 }
238 else {
239 $request->set_body_params( $args );
240 }
241
242 $response = rest_do_request( $request );
243
244 if ( is_wp_error( $response ) || $response->get_status() >= 400 ) {
245 $error_obj = is_wp_error( $response ) ? $response : $response->as_error();
246
247 // Return error in old format for backward compatibility
248 // The execute_tool method will detect this and not re-wrap it
249 return [
250 'jsonrpc' => '2.0',
251 'id' => $id,
252 'error' => [
253 'code' => (int) ( $error_obj->get_error_code() ?: $response->get_status() ),
254 'message' => $error_obj->get_error_message(),
255 'data' => $error_obj->get_error_data() ?: null,
256 ],
257 ];
258 }
259
260 $data = $response->get_data();
261
262 // Return just the data - execute_tool will wrap it properly
263 return $data;
264 }
265 }
266