Skip to main content
Glama
isdaniel

PostgreSQL-Performance-Tuner-Mcp

manage_hypothetical_indexes

Test PostgreSQL index performance by creating hypothetical indexes in memory to analyze query plans without building physical indexes, enabling performance optimization through what-if analysis.

Instructions

Manage HypoPG hypothetical indexes for testing.

HypoPG allows you to create "hypothetical" indexes that exist only in memory and can be used to test query plans without the overhead of creating real indexes.

Actions:

  • create: Create a new hypothetical index by specifying table and columns

  • list: List all current hypothetical indexes

  • drop: Drop a specific hypothetical index

  • reset: Drop all hypothetical indexes

  • estimate_size: Estimate the size of a hypothetical index

  • check: Check HypoPG extension status and availability

  • hide: Hide an existing real index from the query planner (useful for testing what-if scenarios)

  • unhide: Unhide a previously hidden index

  • list_hidden: List all currently hidden indexes

  • explain_with_index: Create a hypothetical index and explain a query with before/after comparison

This is useful for:

  • Testing if an index would improve a query

  • Comparing different index strategies

  • Estimating index storage requirements

  • Testing query performance without specific existing indexes (hide)

  • Simulating index removal scenarios

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
actionYesAction to perform
tableNoTable name (required for create, estimate_size, explain_with_index)
columnsNoColumn names for the index (required for create, estimate_size, explain_with_index)
index_typeNoType of index to createbtree
uniqueNoWhether the index should be unique
index_idNoIndex OID (required for drop, hide, unhide)
queryNoSQL query to explain (required for explain_with_index)
schemaNoSchema name for the table (optional, for create and explain_with_index)
whereNoPartial index WHERE condition (optional, for create)
includeNoColumns to include in INCLUDE clause (optional, for create)

Implementation Reference

  • Core handler function that implements the tool logic for managing hypothetical indexes. Dispatches to different actions like create, list, drop, reset, etc., using HypoPGService.
    async def run_tool(self, arguments: dict[str, Any]) -> Sequence[TextContent]:
        try:
            action = arguments.get("action")
    
            if action == "check":
                status = await self.hypopg_service.check_status()
                return self.format_json_result({
                    "hypopg_available": status.is_installed,
                    "hypopg_version": status.version,
                    "message": status.message
                })
    
            if action == "create":
                self.validate_required_args(arguments, ["table", "columns"])
                hypo_index = await self.hypopg_service.create_index(
                    table=arguments["table"],
                    columns=arguments["columns"],
                    using=arguments.get("index_type", "btree"),
                    schema=arguments.get("schema"),
                    where=arguments.get("where"),
                    include=arguments.get("include"),
                )
                return self.format_json_result({
                    "success": True,
                    "index_oid": hypo_index.indexrelid,
                    "index_name": hypo_index.index_name,
                    "table": hypo_index.table_name,
                    "schema": hypo_index.schema_name,
                    "definition": hypo_index.definition,
                    "estimated_size_bytes": hypo_index.estimated_size
                })
    
            elif action == "list":
                indexes = await self.hypopg_service.list_indexes()
                return self.format_json_result({
                    "count": len(indexes),
                    "hypothetical_indexes": [
                        {
                            "index_oid": idx.indexrelid,
                            "index_name": idx.index_name,
                            "schema_name": idx.schema_name,
                            "table_name": idx.table_name,
                            "access_method": idx.am_name,
                            "definition": idx.definition,
                            "estimated_size_bytes": idx.estimated_size
                        }
                        for idx in indexes
                    ]
                })
    
            elif action == "drop":
                self.validate_required_args(arguments, ["index_id"])
                success = await self.hypopg_service.drop_index(
                    arguments["index_id"]
                )
                return self.format_json_result({
                    "success": success,
                    "dropped_index_id": arguments["index_id"]
                })
    
            elif action == "reset":
                success = await self.hypopg_service.reset()
                return self.format_json_result({
                    "success": success,
                    "message": "All hypothetical indexes have been removed" if success else "Failed to reset hypothetical indexes"
                })
    
            elif action == "estimate_size":
                self.validate_required_args(arguments, ["table", "columns"])
                # Create temporarily to get size estimate
                hypo_index = await self.hypopg_service.create_index(
                    table=arguments["table"],
                    columns=arguments["columns"],
                    using=arguments.get("index_type", "btree"),
                )
    
                size = hypo_index.estimated_size
                # Clean up
                await self.hypopg_service.drop_index(hypo_index.indexrelid)
    
                return self.format_json_result({
                    "table": arguments["table"],
                    "columns": arguments["columns"],
                    "index_type": arguments.get("index_type", "btree"),
                    "estimated_size_bytes": size
                })
    
            elif action == "hide":
                self.validate_required_args(arguments, ["index_id"])
                success = await self.hypopg_service.hide_index(
                    arguments["index_id"]
                )
                return self.format_json_result({
                    "success": success,
                    "hidden_index_id": arguments["index_id"],
                    "message": "Index is now hidden from the query planner" if success else "Failed to hide index"
                })
    
            elif action == "unhide":
                self.validate_required_args(arguments, ["index_id"])
                success = await self.hypopg_service.unhide_index(
                    arguments["index_id"]
                )
                return self.format_json_result({
                    "success": success,
                    "unhidden_index_id": arguments["index_id"],
                    "message": "Index is now visible to the query planner" if success else "Failed to unhide index"
                })
    
            elif action == "list_hidden":
                hidden_indexes = await self.hypopg_service.list_hidden_indexes()
                return self.format_json_result({
                    "count": len(hidden_indexes),
                    "hidden_indexes": hidden_indexes
                })
    
            elif action == "explain_with_index":
                self.validate_required_args(arguments, ["query", "table", "columns"])
                result = await self.hypopg_service.explain_with_hypothetical_index(
                    query=arguments["query"],
                    table=arguments["table"],
                    columns=arguments["columns"],
                    using=arguments.get("index_type", "btree"),
                )
                return self.format_json_result(result)
    
            else:
                return self.format_result(f"Unknown action: {action}")
    
        except Exception as e:
            return self.format_error(e)
  • JSON schema defining the input parameters for the tool, including action (required) and parameters for each action like table, columns, index_id, etc.
    inputSchema={
        "type": "object",
        "properties": {
            "action": {
                "type": "string",
                "enum": [
                    "create", "list", "drop", "reset",
                    "estimate_size", "check", "hide", "unhide",
                    "list_hidden", "explain_with_index"
                ],
                "description": "Action to perform"
            },
            "table": {
                "type": "string",
                "description": "Table name (required for create, estimate_size, explain_with_index)"
            },
            "columns": {
                "type": "array",
                "items": {"type": "string"},
                "description": "Column names for the index (required for create, estimate_size, explain_with_index)"
            },
            "index_type": {
                "type": "string",
                "enum": ["btree", "hash", "gin", "gist", "brin"],
                "default": "btree",
                "description": "Type of index to create"
            },
            "unique": {
                "type": "boolean",
                "default": False,
                "description": "Whether the index should be unique"
            },
            "index_id": {
                "type": "integer",
                "description": "Index OID (required for drop, hide, unhide)"
            },
            "query": {
                "type": "string",
                "description": "SQL query to explain (required for explain_with_index)"
            },
            "schema": {
                "type": "string",
                "description": "Schema name for the table (optional, for create and explain_with_index)"
            },
            "where": {
                "type": "string",
                "description": "Partial index WHERE condition (optional, for create)"
            },
            "include": {
                "type": "array",
                "items": {"type": "string"},
                "description": "Columns to include in INCLUDE clause (optional, for create)"
            }
        },
        "required": ["action"]
    },
  • Registers the HypoPGToolHandler instance (for manage_hypothetical_indexes) to the MCP server's tool registry via add_tool_handler.
    add_tool_handler(IndexAdvisorToolHandler(index_advisor))
    add_tool_handler(ExplainQueryToolHandler(driver, hypopg_service))
    add_tool_handler(HypoPGToolHandler(hypopg_service))
    add_tool_handler(UnusedIndexesToolHandler(driver))
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations indicate readOnlyHint=false, destructiveHint=false, etc., covering basic safety. The description adds valuable behavioral context beyond annotations: it explains that indexes are in-memory only (not persisted), lists specific actions like 'reset' that drop all indexes, and mentions testing scenarios without affecting real indexes. No contradiction with annotations exists.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with clear sections (overview, actions list, use cases) and avoids redundancy. However, it's moderately lengthy due to enumerating 10 actions and use cases; some sentences could be more condensed while maintaining clarity, slightly affecting efficiency.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (10 parameters, multiple actions) and lack of output schema, the description provides comprehensive context on actions and use cases. It compensates well for missing output details by explaining what each action does, though it could briefly mention expected return types or error handling for full completeness.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, providing full parameter documentation. The description adds minimal parameter semantics beyond the schema, mainly by listing actions and their purposes without detailing parameter interactions or constraints. This meets the baseline for high schema coverage but doesn't enhance understanding significantly.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool manages HypoPG hypothetical indexes for testing, specifying it creates in-memory indexes to test query plans without creating real ones. It distinguishes from siblings by focusing on hypothetical index management rather than analysis, recommendations, or real index operations found in tools like 'explain_with_indexes' or 'find_unused_indexes'.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly lists 10 specific actions with their purposes and includes a 'This is useful for' section detailing scenarios like testing query improvements, comparing strategies, and simulating index removal. It provides clear when-to-use guidance by contrasting with real index operations and outlining practical testing contexts.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/isdaniel/pgtuner-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server