settings = $settings; } /** * Hook the interceptor. */ public function register() { add_filter( 'rest_pre_dispatch', array( $this, 'intercept' ), 10, 3 ); } /** * Answer anonymous public-method calls on the MCP route; touch nothing else. * * @param mixed $result Dispatch short-circuit value (null = proceed). * @param \WP_REST_Server $server REST server. * @param \WP_REST_Request $request The request. * @return mixed */ public function intercept( $result, $server, $request ) { if ( null !== $result || ! $this->settings->enabled( 'enable_mcp_server' ) ) { return $result; } if ( '/agentimus/v1/mcp' !== $request->get_route() || 'POST' !== $request->get_method() ) { return $result; } if ( get_current_user_id() ) { return $result; // Authenticated → the adapter's full, sessionful path. } $body = json_decode( (string) $request->get_body(), true ); if ( ! is_array( $body ) || empty( $body['method'] ) || ! is_string( $body['method'] ) ) { return $result; // Batches and malformed bodies keep the old answer. } $method = $body['method']; // The post-initialize notification: acknowledge (202, no body) so a // spec-following anonymous client isn't bounced mid-handshake. if ( 'notifications/initialized' === $method ) { return new \WP_REST_Response( null, 202 ); } if ( ! in_array( $method, self::PUBLIC_METHODS, true ) ) { return $result; // tools/call & friends: 401 + WWW-Authenticate as before. } $mcp = $this->agentimus_server(); if ( null === $mcp ) { return $result; } try { switch ( $method ) { case 'initialize': $params = ( isset( $body['params'] ) && is_array( $body['params'] ) ) ? $body['params'] : array(); $version = ( isset( $params['protocolVersion'] ) && is_string( $params['protocolVersion'] ) ) ? $params['protocolVersion'] : ''; $payload = ( new \WP\MCP\Handlers\Initialize\InitializeHandler( $mcp ) )->handle( $version )->toArray(); break; default: // ping. $payload = new \stdClass(); // An empty JSON object, per the spec. } } catch ( \Throwable $e ) { return $result; // Adapter drift → the old 401, never a broken protocol answer. } $id = isset( $body['id'] ) ? $body['id'] : null; $response = new \WP_REST_Response( array( 'jsonrpc' => '2.0', 'id' => $id, 'result' => $payload, ), 200 ); $response->header( 'Content-Type', 'application/json; charset=UTF-8' ); return $response; } /** * The live Agentimus MCP server object from the adapter, or null. * * @return object|null */ private function agentimus_server() { if ( ! class_exists( '\WP\MCP\Core\McpAdapter' ) || ! did_action( 'mcp_adapter_init' ) ) { return null; } try { $adapter = \WP\MCP\Core\McpAdapter::instance(); if ( ! is_object( $adapter ) || ! method_exists( $adapter, 'get_servers' ) ) { return null; } foreach ( (array) $adapter->get_servers() as $srv ) { if ( is_object( $srv ) && method_exists( $srv, 'get_server_id' ) && 'agentimus' === (string) $srv->get_server_id() ) { return $srv; } } } catch ( \Throwable $e ) { return null; } return null; } }