document variant. * * @var array */ const PATHS = array( '.well-known/mcp.json' => 'card', '.well-known/mcp/server-card.json' => 'card', '.well-known/mcp-server' => 'card', '.well-known/mcp' => 'manifest', ); /** * Well-known path => Existing_Content artifact key. * * @var array */ const ARTIFACT_KEYS = array( '.well-known/mcp.json' => 'mcp_json', '.well-known/mcp/server-card.json' => 'mcp_server_card', '.well-known/mcp-server' => 'mcp_server_wk', '.well-known/mcp' => 'mcp_manifest', ); /** * Hook everything up. */ public function register_hooks() { add_action( 'init', array( __CLASS__, 'add_rewrite_rules' ) ); add_filter( 'query_vars', array( $this, 'register_query_vars' ) ); add_action( 'parse_request', array( $this, 'maybe_serve' ), 0 ); } /** * Rewrite rules. Static for activation-time flushing. Skipped per path * when the site already provides the document from another source, so a * competing handler's rule wins the pattern. */ public static function add_rewrite_rules() { foreach ( self::PATHS as $path => $variant ) { if ( Existing_Content::is_external( self::ARTIFACT_KEYS[ $path ] ) ) { continue; } $pattern = '^' . str_replace( '.', '\.', $path ) . '$'; add_rewrite_rule( $pattern, 'index.php?' . self::QUERY_VAR . '=' . $variant, 'top' ); } } /** * Register the query var. * * @param string[] $vars Public query vars. * @return string[] */ public function register_query_vars( $vars ) { $vars[] = self::QUERY_VAR; return $vars; } /** * Serve the discovery document when requested. * * @param \WP $wp The WP environment instance. */ public function maybe_serve( $wp ) { // Detection probes must see what the site would serve WITHOUT us. if ( Existing_Content::is_probe_request() ) { return; } $variant = isset( $wp->query_vars[ self::QUERY_VAR ] ) ? (string) $wp->query_vars[ self::QUERY_VAR ] : ''; // Resolve the concrete well-known path from the request URI - needed // both as the rewrite fallback and to gate per path on existing // content (the query var alone cannot distinguish the card paths). $uri = isset( $_SERVER['REQUEST_URI'] ) ? wp_unslash( $_SERVER['REQUEST_URI'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- compared against fixed strings only. $path = (string) wp_parse_url( $uri, PHP_URL_PATH ); $home_path = rtrim( (string) wp_parse_url( home_url( '/' ), PHP_URL_PATH ), '/' ); $matched_path = ''; foreach ( self::PATHS as $known => $known_variant ) { if ( '/' . $known === $path || $home_path . '/' . $known === $path ) { $matched_path = $known; if ( '' === $variant ) { $variant = $known_variant; } break; } } if ( '' === $variant ) { return; } // The site already provides this document from another source // (physical file or another plugin) - never override it. if ( '' !== $matched_path && Existing_Content::is_external( self::ARTIFACT_KEYS[ $matched_path ] ) ) { return; } $card = self::server_card(); $payload = 'manifest' === $variant ? array( 'version' => '1', 'servers' => array( $card ), ) : $card; status_header( 200 ); header( 'Content-Type: application/json; charset=utf-8' ); header( 'X-42A-AEL: ' . AEL42A_VERSION ); echo wp_json_encode( $payload, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ); // phpcs:ignore WordPress.Security.EscapeOutput -- JSON body. exit; } /** * The server card describing this site's MCP endpoint. * * `url` duplicates `endpoint` because SEP-1649 uses `url` while earlier * conventions (and 42A's vitals probe) read `endpoint`. * * @return array */ private static function server_card() { return array( 'name' => get_bloginfo( 'name' ), 'description' => get_bloginfo( 'description' ), 'endpoint' => rest_url( '42a/v1/mcp' ), 'url' => rest_url( '42a/v1/mcp' ), 'transport' => 'streamable-http', 'protocol_version' => Mcp_Server::PROTOCOL_VERSION, 'capabilities' => array( 'tools' ), 'authentication' => 'none', 'generator' => 'ael-42a/' . AEL42A_VERSION, ); } }