Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
This module contains all the MCP tools for Couchbase operations.
"""

# Index tools
from .index import get_index_advisor_recommendations

# Key-Value tools
from .kv import (
delete_document_by_id,
Expand Down Expand Up @@ -40,6 +43,7 @@
delete_document_by_id,
get_schema_for_collection,
run_sql_plus_plus_query,
get_index_advisor_recommendations,
]

__all__ = [
Expand All @@ -55,6 +59,7 @@
"delete_document_by_id",
"get_schema_for_collection",
"run_sql_plus_plus_query",
"get_index_advisor_recommendations",
# Convenience
"ALL_TOOLS",
]
84 changes: 84 additions & 0 deletions src/tools/index.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
"""
Tools for index operations and optimization.

This module contains tools for getting index recommendations using the Couchbase Index Advisor.
"""

import logging
from typing import Any

from mcp.server.fastmcp import Context

from tools.query import run_cluster_query
from utils.constants import MCP_SERVER_NAME

logger = logging.getLogger(f"{MCP_SERVER_NAME}.tools.index")


def get_index_advisor_recommendations(ctx: Context, query: str) -> dict[str, Any]:
"""Get index recommendations from Couchbase Index Advisor for a given SQL++ query.

The Index Advisor analyzes the query and provides recommendations for optimal indexes.
This tool works with SELECT, UPDATE, DELETE, or MERGE queries.
The query should contain fully qualified keyspace (e.g., bucket.scope.collection).

Returns a dictionary with:
- current_used_indexes: Array of currently used indexes (if any)
- recommended_indexes: Array of recommended secondary indexes (if any)
- recommended_covering_indexes: Array of recommended covering indexes (if any)

Each index object contains:
- index: The CREATE INDEX SQL++ command
- statements: Array of statement objects with the query and run count
"""
try:
# Build the ADVISOR query
advisor_query = f"SELECT ADVISOR('{query}') AS advisor_result"

logger.info("Running Index Advisor for the provided query")

# Execute the ADVISOR function at cluster level using run_cluster_query
advisor_results = run_cluster_query(ctx, advisor_query)

if not advisor_results:
return {
"message": "No recommendations available",
"current_used_indexes": [],
"recommended_indexes": [],
"recommended_covering_indexes": [],
}

# The result is wrapped in advisor_result key
advisor_data = advisor_results[0].get("advisor_result", {})

# Extract the relevant fields with defaults
response = {
"current_used_indexes": advisor_data.get("current_used_indexes", []),
"recommended_indexes": advisor_data.get("recommended_indexes", []),
"recommended_covering_indexes": advisor_data.get(
"recommended_covering_indexes", []
),
}

# Add summary information for better user experience
response["summary"] = {
"current_indexes_count": len(response["current_used_indexes"]),
"recommended_indexes_count": len(response["recommended_indexes"]),
"recommended_covering_indexes_count": len(
response["recommended_covering_indexes"]
),
"has_recommendations": bool(
response["recommended_indexes"]
or response["recommended_covering_indexes"]
),
}

logger.info(
f"Index Advisor completed. Found {response['summary']['recommended_indexes_count']} recommended indexes"
)

return response

except Exception as e:
logger.error(f"Error running Index Advisor: {e!s}", exc_info=True)
raise