| @@ -137,13 +137,15 @@ | ||
| 137 | 137 | $snippet['functionArgs'] = []; |
| 138 | 138 | $snippet['functionArgsDict'] = []; |
| 139 | 139 | $snippet['functionBehavior'] = ''; |
| 140 | 140 | $snippet['functionTarget'] = 'PHP'; |
| 141 | + $snippet['functionMcp'] = false; | |
| 141 | 142 | |
| 142 | 143 | foreach ( $functions_snippet as $function_snippet ) { |
| 143 | 144 | if ( $function_snippet['snippetId'] == $snippetId ) { |
| 144 | 145 | |
| 145 | 146 | $snippet['functionName'] = $function_snippet['name']; |
| 147 | + $snippet['functionMcp'] = !empty( $function_snippet['mcp'] ); | |
| 146 | 148 | if ( !isset( $function_snippet['behavior'] ) || empty( $function_snippet['behavior'] ) ) { |
| 147 | 149 | $snippet['functionBehavior'] = 'dynamic'; |
| 148 | 150 | } |
| 149 | 151 | else { |
| @@ -215,8 +217,9 @@ | ||
| 215 | 217 | 'name' => $params['functionName'], |
| 216 | 218 | 'behavior' => $params['functionBehavior'], |
| 217 | 219 | 'desc' => $params['description'] ?? '', |
| 218 | 220 | 'target' => $params['functionTarget'] ?? 'PHP', |
| 221 | + 'mcp' => !empty( $params['functionMcp'] ), | |
| 219 | 222 | 'args' => [], |
| 220 | 223 | ]; |
| 221 | 224 | |
| 222 | 225 | foreach ( $params['functionArgs'] as $argName ) { |
| @@ -353,8 +356,27 @@ | ||
| 353 | 356 | |
| 354 | 357 | return null; |
| 355 | 358 | } |
| 356 | 359 | |
| 360 | + /** | |
| 361 | + * Return the snippet IDs of every function that opted in to MCP exposure. | |
| 362 | + * Used by the "MCP" list filter and its count (functionMcp lives in the | |
| 363 | + * functions option, not a snippets table column, so it can't be queried in SQL). | |
| 364 | + * | |
| 365 | + * @return int[] | |
| 366 | + */ | |
| 367 | + public function get_mcp_function_ids() | |
| 368 | + { | |
| 369 | + $functions = get_option( $this->option_functions, [] ); | |
| 370 | + $ids = []; | |
| 371 | + foreach ( $functions as $fn ) { | |
| 372 | + if ( !empty( $fn['mcp'] ) && !empty( $fn['snippetId'] ) ) { | |
| 373 | + $ids[] = (int) $fn['snippetId']; | |
| 374 | + } | |
| 375 | + } | |
| 376 | + return $ids; | |
| 377 | + } | |
| 378 | + | |
| 357 | 379 | #endregion |
| 358 | 380 | |
| 359 | 381 | #region Utilities |
| 360 | 382 | |
| @@ -590,8 +612,11 @@ | ||
| 590 | 612 | |
| 591 | 613 | // Combined count for the "Content" filter (PHP + JS). Not added to 'all', already counted above. |
| 592 | 614 | $stats['content'] = (int) $stats['content_php'] + (int) $stats['content_js']; |
| 593 | 615 | |
| 616 | + // Functions exposed via MCP. A subset of 'function', so not added to 'all'. | |
| 617 | + $stats['mcp'] = count( $this->get_mcp_function_ids() ); | |
| 618 | + | |
| 594 | 619 | return $stats; |
| 595 | 620 | } |
| 596 | 621 | |
| 597 | 622 | public function import( ) |
| @@ -708,9 +733,17 @@ | ||
| 708 | 733 | $where[] = $this->wpdb->prepare( "active = %d", $value ); |
| 709 | 734 | } elseif ( $filter['accessor'] === 'endpoint' ) { |
| 710 | 735 | $where[] = boolval( $filter['value'] ) ? "endpoint <> ''" : "endpoint = ''"; |
| 711 | 736 | } elseif ( $filter['accessor'] === 'scope' ) { |
| 712 | - if ( is_array( $filter['value'] ) ) { | |
| 737 | + if ( $filter['value'] === 'mcp' ) { | |
| 738 | + // Not a scope: constrain to the functions opted in to MCP exposure. | |
| 739 | + $ids = $this->get_mcp_function_ids(); | |
| 740 | + if ( empty( $ids ) ) { | |
| 741 | + $where[] = '1 = 0'; | |
| 742 | + } else { | |
| 743 | + $where[] = 'id IN (' . implode( ',', array_map( 'intval', $ids ) ) . ')'; | |
| 744 | + } | |
| 745 | + } else if ( is_array( $filter['value'] ) ) { | |
| 713 | 746 | $scopes = array_map( function( $scope ) { |
| 714 | 747 | return esc_sql( $scope ); |
| 715 | 748 | }, $filter['value'] ); |
| 716 | 749 | $where[] = "scope IN ('" . implode( "', '", $scopes ) . "')"; |