|
1 | 1 | """ |
2 | | -Tools for index operations and optimization. |
| 2 | +Tools for index operations. |
3 | 3 |
|
4 | | -This module contains tools for getting index recommendations using the Couchbase Index Advisor. |
| 4 | +This module contains tools for listing and managing indexes in the Couchbase cluster and getting index recommendations using the Couchbase Index Advisor. |
5 | 5 | """ |
6 | 6 |
|
7 | 7 | import logging |
|
10 | 10 | from mcp.server.fastmcp import Context |
11 | 11 |
|
12 | 12 | from tools.query import run_sql_plus_plus_query |
| 13 | +from utils.config import get_settings |
13 | 14 | from utils.constants import MCP_SERVER_NAME |
| 15 | +from utils.index_utils import ( |
| 16 | + fetch_indexes_from_rest_api, |
| 17 | + process_index_data, |
| 18 | + validate_connection_settings, |
| 19 | + validate_filter_params, |
| 20 | +) |
14 | 21 |
|
15 | 22 | logger = logging.getLogger(f"{MCP_SERVER_NAME}.tools.index") |
16 | 23 |
|
@@ -39,7 +46,7 @@ def get_index_advisor_recommendations( |
39 | 46 |
|
40 | 47 | logger.info("Running Index Advisor for the provided query") |
41 | 48 |
|
42 | | - # Execute the ADVISOR function at cluster level using run_cluster_query |
| 49 | + # Execute the ADVISOR function at cluster level using run_sql_plus_plus_query |
43 | 50 | advisor_results = run_sql_plus_plus_query( |
44 | 51 | ctx, bucket_name, scope_name, advisor_query |
45 | 52 | ) |
@@ -86,3 +93,80 @@ def get_index_advisor_recommendations( |
86 | 93 | except Exception as e: |
87 | 94 | logger.error(f"Error running Index Advisor: {e!s}", exc_info=True) |
88 | 95 | raise |
| 96 | + |
| 97 | + |
| 98 | +def list_indexes( |
| 99 | + ctx: Context, |
| 100 | + bucket_name: str | None = None, |
| 101 | + scope_name: str | None = None, |
| 102 | + collection_name: str | None = None, |
| 103 | + index_name: str | None = None, |
| 104 | + include_raw_index_stats: bool = False, |
| 105 | +) -> list[dict[str, Any]]: |
| 106 | + """List all indexes in the cluster with optional filtering by bucket, scope, collection, and index name. |
| 107 | + Returns a list of indexes with their names and CREATE INDEX definitions. |
| 108 | + Uses the Index Service REST API (/getIndexStatus) to retrieve index information directly. |
| 109 | +
|
| 110 | + Args: |
| 111 | + ctx: MCP context for cluster connection |
| 112 | + bucket_name: Optional bucket name to filter indexes |
| 113 | + scope_name: Optional scope name to filter indexes (requires bucket_name) |
| 114 | + collection_name: Optional collection name to filter indexes (requires bucket_name and scope_name) |
| 115 | + index_name: Optional index name to filter indexes (requires bucket_name, scope_name, and collection_name) |
| 116 | + include_raw_index_stats: If True, include raw index stats (as-is from API) in addition |
| 117 | + to cleaned-up version. Default is False. |
| 118 | +
|
| 119 | + Returns: |
| 120 | + List of dictionaries with keys: |
| 121 | + - name (str): Index name |
| 122 | + - definition (str): Cleaned-up CREATE INDEX statement |
| 123 | + - status (str): Current status of the index (e.g., "Ready", "Building", "Deferred") |
| 124 | + - isPrimary (bool): Whether this is a primary index |
| 125 | + - bucket (str): Bucket name where the index exists |
| 126 | + - scope (str): Scope name where the index exists |
| 127 | + - collection (str): Collection name where the index exists |
| 128 | + - raw_index_stats (dict, optional): Complete raw index status object from API including metadata, |
| 129 | + state, keyspace info, etc. (only if include_raw_index_stats=True) |
| 130 | + """ |
| 131 | + try: |
| 132 | + # Validate parameters |
| 133 | + validate_filter_params(bucket_name, scope_name, collection_name, index_name) |
| 134 | + |
| 135 | + # Get and validate connection settings |
| 136 | + settings = get_settings() |
| 137 | + validate_connection_settings(settings) |
| 138 | + |
| 139 | + # Fetch indexes from REST API |
| 140 | + logger.info( |
| 141 | + f"Fetching indexes from REST API for bucket={bucket_name}, " |
| 142 | + f"scope={scope_name}, collection={collection_name}, index={index_name}" |
| 143 | + ) |
| 144 | + |
| 145 | + raw_indexes = fetch_indexes_from_rest_api( |
| 146 | + settings["connection_string"], |
| 147 | + settings["username"], |
| 148 | + settings["password"], |
| 149 | + bucket_name=bucket_name, |
| 150 | + scope_name=scope_name, |
| 151 | + collection_name=collection_name, |
| 152 | + index_name=index_name, |
| 153 | + ca_cert_path=settings.get("ca_cert_path"), |
| 154 | + ) |
| 155 | + |
| 156 | + # Process and format the results |
| 157 | + indexes = [ |
| 158 | + processed |
| 159 | + for idx in raw_indexes |
| 160 | + if (processed := process_index_data(idx, include_raw_index_stats)) |
| 161 | + is not None |
| 162 | + ] |
| 163 | + |
| 164 | + logger.info( |
| 165 | + f"Found {len(indexes)} indexes from REST API " |
| 166 | + f"(include_raw_index_stats={include_raw_index_stats})" |
| 167 | + ) |
| 168 | + return indexes |
| 169 | + |
| 170 | + except Exception as e: |
| 171 | + logger.error(f"Error listing indexes: {e}", exc_info=True) |
| 172 | + raise |
0 commit comments