Skip to main content
Glama
bintocher

Qlik Sense MCP Server

engine_create_hypercube

Create multi-dimensional data cubes for analysis with customizable sorting, including top-N results via expression-based dimension sorting.

Instructions

Create hypercube for data analysis with custom sorting options. IMPORTANT: To get top-N records, use qSortByExpression: 1 in dimension sorting with qExpression containing the measure formula (e.g., 'Count(field)' for ascending, '-Count(field)' for descending). Measure sorting is ignored by Qlik Engine.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
app_idYesApplication ID
dimensionsNoList of dimension definitions with optional sorting
measuresNoList of measure definitions with optional sorting
max_rowsNoMaximum rows to return

Implementation Reference

  • Core handler function implementing hypercube creation via Qlik Engine API CreateSessionObject and GetLayout calls. Handles dimension/measure conversion, sorting, and returns hypercube data.
    def create_hypercube(
        self,
        app_handle: int,
        dimensions: List[Dict[str, Any]] = None,
        measures: List[Dict[str, Any]] = None,
        max_rows: int = 1000,
    ) -> Dict[str, Any]:
        """Create hypercube for data extraction with proper structure."""
        try:
            # Handle empty dimensions/measures
            if dimensions is None:
                dimensions = []
            if measures is None:
                measures = []
    
            # Convert old format (list of strings) to new format (list of dicts) for backward compatibility
            converted_dimensions = []
            for dim in dimensions:
                if isinstance(dim, str):
                    # Old format - just field name
                    converted_dimensions.append({
                        "field": dim,
                        "sort_by": {
                            "qSortByNumeric": 0,
                            "qSortByAscii": 1,  # Default: ASCII ascending
                            "qSortByExpression": 0,
                            "qExpression": ""
                        }
                    })
                else:
                    # New format - dict with field and sort options
                    # Set defaults if not specified
                    if "sort_by" not in dim:
                        dim["sort_by"] = {
                            "qSortByNumeric": 0,
                            "qSortByAscii": 1,  # Default: ASCII ascending
                            "qSortByExpression": 0,
                            "qExpression": ""
                        }
                    converted_dimensions.append(dim)
    
            converted_measures = []
            for measure in measures:
                if isinstance(measure, str):
                    # Old format - just expression
                    converted_measures.append({
                        "expression": measure,
                        "sort_by": {
                            "qSortByNumeric": -1  # Default: numeric descending
                        }
                    })
                else:
                    # New format - dict with expression and sort options
                    # Set defaults if not specified
                    if "sort_by" not in measure:
                        measure["sort_by"] = {
                            "qSortByNumeric": -1  # Default: numeric descending
                        }
                    converted_measures.append(measure)
    
            # Create correct hypercube structure
            hypercube_def = {
                "qDimensions": [
                    {
                        "qDef": {
                            "qFieldDefs": [dim["field"]],
                            "qSortCriterias": [
                                {
                                    "qSortByState": 0,
                                    "qSortByFrequency": 0,
                                    "qSortByNumeric": dim["sort_by"].get("qSortByNumeric", 0),
                                    "qSortByAscii": dim["sort_by"].get("qSortByAscii", 1),
                                    "qSortByLoadOrder": 0,
                                    "qSortByExpression": dim["sort_by"].get("qSortByExpression", 0),
                                    "qExpression": {"qv": dim["sort_by"].get("qExpression", "")},
                                }
                            ],
                        },
                        "qNullSuppression": False,
                        "qIncludeElemValue": True,
                    }
                    for dim in converted_dimensions
                ],
                "qMeasures": [
                    {
                        "qDef": {"qDef": measure["expression"], "qLabel": measure.get("label", f"Measure_{i}")},
                        "qSortBy": measure["sort_by"],
                    }
                    for i, measure in enumerate(converted_measures)
                ],
                "qInitialDataFetch": [
                    {
                        "qTop": 0,
                        "qLeft": 0,
                        "qHeight": max_rows,
                        "qWidth": len(converted_dimensions) + len(converted_measures),
                    }
                ],
                "qSuppressZero": False,
                "qSuppressMissing": False,
                "qMode": "S",
                "qInterColumnSortOrder": list(range(len(converted_dimensions) + len(converted_measures))),
            }
    
            obj_def = {
                "qInfo": {
                    "qId": f"hypercube-{len(converted_dimensions)}d-{len(converted_measures)}m",
                    "qType": "HyperCube",
                },
                "qHyperCubeDef": hypercube_def,
            }
    
            result = self.send_request(
                "CreateSessionObject", [obj_def], handle=app_handle
            )
    
            if "qReturn" not in result or "qHandle" not in result["qReturn"]:
                return {"error": "Failed to create hypercube", "response": result}
    
            cube_handle = result["qReturn"]["qHandle"]
    
            # Получаем layout с данными
            layout = self.send_request("GetLayout", [], handle=cube_handle)
    
            if "qLayout" not in layout or "qHyperCube" not in layout["qLayout"]:
                return {"error": "No hypercube in layout", "layout": layout}
    
            hypercube = layout["qLayout"]["qHyperCube"]
    
            return {
                "hypercube_handle": cube_handle,
                "hypercube_data": hypercube,
                "dimensions": converted_dimensions,
                "measures": converted_measures,
                "total_rows": hypercube.get("qSize", {}).get("qcy", 0),
                "total_columns": hypercube.get("qSize", {}).get("qcx", 0),
            }
    
        except Exception as e:
            return {"error": str(e), "details": "Error in create_hypercube method"}
  • MCP server handler for the tool that connects to engine, opens the app, calls engine_api.create_hypercube, and returns JSON response.
    elif name == "engine_create_hypercube":
        app_id = arguments["app_id"]
        dimensions = arguments.get("dimensions", [])
        measures = arguments.get("measures", [])
        max_rows = arguments.get("max_rows", 1000)
    
        def _create_hypercube():
            try:
                self.engine_api.connect()
                app_result = self.engine_api.open_doc(app_id, no_data=False)
                app_handle = app_result.get("qReturn", {}).get("qHandle", -1)
                if app_handle != -1:
                    return self.engine_api.create_hypercube(app_handle, dimensions, measures, max_rows)
                else:
                    raise Exception("Failed to open app")
            except Exception as e:
                return {"error": str(e)}
            finally:
                self.engine_api.disconnect()
    
        result = await asyncio.to_thread(_create_hypercube)
        return [
            TextContent(
                type="text",
                text=json.dumps(result, indent=2, ensure_ascii=False)
            )
        ]
  • MCP tool registration including name, description, and detailed input schema for dimensions, measures with sorting options.
    Tool(name="engine_create_hypercube", description="Create hypercube for data analysis with custom sorting options. IMPORTANT: To get top-N records, use qSortByExpression: 1 in dimension sorting with qExpression containing the measure formula (e.g., 'Count(field)' for ascending, '-Count(field)' for descending). Measure sorting is ignored by Qlik Engine.", inputSchema={
        "type": "object",
        "properties": {
            "app_id": {"type": "string", "description": "Application ID"},
            "dimensions": {
                "type": "array",
                "items": {
                    "type": "object",
                    "properties": {
                        "field": {"type": "string", "description": "Field name for dimension"},
                        "label": {"type": "string", "description": "Optional label for dimension"},
                        "sort_by": {
                            "type": "object",
                            "properties": {
                                "qSortByNumeric": {"type": "integer", "description": "Sort by numeric value (-1 desc, 0 none, 1 asc)", "default": 0},
                                "qSortByAscii": {"type": "integer", "description": "Sort by ASCII value (-1 desc, 0 none, 1 asc)", "default": 1},
                                "qSortByExpression": {"type": "integer", "description": "Use expression for sorting (0/1). For top-N results, set to 1 and use qExpression with measure formula", "default": 0},
                                "qExpression": {"type": "string", "description": "Expression for custom sorting. For top-N: 'Count(field)' for ascending, '-Count(field)' for descending", "default": ""}
                            },
                            "additionalProperties": False
                        }
                    },
                    "additionalProperties": False
                },
                "description": "List of dimension definitions with optional sorting"
            },
            "measures": {
                "type": "array",
                "items": {
                    "type": "object",
                    "properties": {
                        "expression": {"type": "string", "description": "Measure expression"},
                        "label": {"type": "string", "description": "Optional label for measure"},
                        "sort_by": {
                            "type": "object",
                            "properties": {
                                "qSortByNumeric": {"type": "integer", "description": "Sort by numeric value (-1 desc, 0 none, 1 asc). NOTE: Measure sorting is ignored by Qlik Engine - use dimension sorting with qSortByExpression for top-N results", "default": -1}
                            },
                            "additionalProperties": False
                        }
                    },
                    "additionalProperties": False
                },
                "description": "List of measure definitions with optional sorting"
            },
            "max_rows": {"type": "integer", "description": "Maximum rows to return", "default": 1000}
        },
        "required": ["app_id"]
    })
  • Detailed input schema defining parameters for app_id, dimensions (with field, label, sort_by options), measures (with expression, label, sort_by), and max_rows.
        "type": "object",
        "properties": {
            "app_id": {"type": "string", "description": "Application ID"},
            "dimensions": {
                "type": "array",
                "items": {
                    "type": "object",
                    "properties": {
                        "field": {"type": "string", "description": "Field name for dimension"},
                        "label": {"type": "string", "description": "Optional label for dimension"},
                        "sort_by": {
                            "type": "object",
                            "properties": {
                                "qSortByNumeric": {"type": "integer", "description": "Sort by numeric value (-1 desc, 0 none, 1 asc)", "default": 0},
                                "qSortByAscii": {"type": "integer", "description": "Sort by ASCII value (-1 desc, 0 none, 1 asc)", "default": 1},
                                "qSortByExpression": {"type": "integer", "description": "Use expression for sorting (0/1). For top-N results, set to 1 and use qExpression with measure formula", "default": 0},
                                "qExpression": {"type": "string", "description": "Expression for custom sorting. For top-N: 'Count(field)' for ascending, '-Count(field)' for descending", "default": ""}
                            },
                            "additionalProperties": False
                        }
                    },
                    "additionalProperties": False
                },
                "description": "List of dimension definitions with optional sorting"
            },
            "measures": {
                "type": "array",
                "items": {
                    "type": "object",
                    "properties": {
                        "expression": {"type": "string", "description": "Measure expression"},
                        "label": {"type": "string", "description": "Optional label for measure"},
                        "sort_by": {
                            "type": "object",
                            "properties": {
                                "qSortByNumeric": {"type": "integer", "description": "Sort by numeric value (-1 desc, 0 none, 1 asc). NOTE: Measure sorting is ignored by Qlik Engine - use dimension sorting with qSortByExpression for top-N results", "default": -1}
                            },
                            "additionalProperties": False
                        }
                    },
                    "additionalProperties": False
                },
                "description": "List of measure definitions with optional sorting"
            },
            "max_rows": {"type": "integer", "description": "Maximum rows to return", "default": 1000}
        },
        "required": ["app_id"]
    })
Behavior4/5

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

With no annotations provided, the description carries the full burden of behavioral disclosure. It adds valuable context beyond the input schema by explaining that 'Measure sorting is ignored by Qlik Engine' and providing specific guidance on how to achieve top-N results using qSortByExpression. This clarifies important behavioral traits not evident from the schema alone.

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 appropriately sized with two sentences that are front-loaded with the main purpose. The second sentence provides crucial implementation details without unnecessary elaboration. Every sentence earns its place, though it could be slightly more structured for clarity.

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

Completeness3/5

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

Given the complexity of the tool (4 parameters with nested objects, no output schema, and no annotations), the description is somewhat complete but has gaps. It explains key behavioral aspects like the engine's handling of measure sorting, but doesn't cover other potential behaviors such as error conditions, performance implications, or what the created hypercube output entails. This is adequate but with clear room for improvement.

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?

The schema description coverage is 100%, so the schema already documents all parameters thoroughly. The description adds some semantic value by explaining the purpose of qSortByExpression for top-N results and clarifying that measure sorting is ignored, but it doesn't provide significant additional meaning beyond what's in the schema descriptions. This meets the baseline of 3 for high schema coverage.

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

Purpose4/5

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

The description clearly states the tool creates a hypercube for data analysis with custom sorting options, providing a specific verb ('create') and resource ('hypercube'). However, it doesn't explicitly differentiate this tool from its siblings (which are all 'get' operations), though the distinction is implied by the 'create' action versus their 'get' actions.

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

Usage Guidelines3/5

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

The description provides implicit usage guidance by mentioning 'To get top-N records, use qSortByExpression: 1 in dimension sorting,' which suggests when to use certain parameters. However, it doesn't explicitly state when to use this tool versus alternatives (e.g., compared to sibling 'get' tools) or provide clear exclusions, leaving some context to inference.

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/bintocher/qlik-sense-mcp'

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