Skip to main content
Glama
LiusCraft

Superset MCP Server

by LiusCraft

query-superset

Execute natural language queries on Apache Superset databases to retrieve data, specify database, schema, or table if needed.

Instructions

执行 Superset 数据查询

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
databaseIdNo可选的数据库ID,如果不提供将自动选择匹配的数据库
queryYes用户的自然语言查询,例如'查询最近10条日志'
schemaNo可选的schema名称
tableNameNo可选的表名

Implementation Reference

  • The main handler function for the 'query-superset' tool. It processes the natural language query, selects appropriate database and table (or uses provided), fetches fields, generates SQL, executes the query using SupersetApiService, and formats the results.
    async ({ query, databaseId, schema, tableName }) => {
      try {
        // 如果缓存为空,初始化缓存
        if (databasesCache.length === 0) {
          initializeCache();
          return {
            content: [
              {
                type: "text",
                text: "初始化缓存中...",
              },
            ],
          };
        }
    
        
        let selectedDb: Database | undefined;
        let selectedTable: Table | undefined;
        
        // 如果提供了明确的数据库ID和表名
        if (databaseId !== undefined && schema && tableName) {
          selectedDb = databasesCache.find(db => db.id === databaseId);
          
          if (selectedDb) {
            const tables = tablesCache.get(selectedDb.id) || [];
            selectedTable = tables.find(t => t.schema === schema && t.name === tableName);
          }
        } else {
          // 否则,根据查询自动选择匹配的表
          const match = findMatchingTable(query);
          if (match) {
            selectedDb = match.database;
            selectedTable = match.table;
          }
        }
        
        if (!selectedDb || !selectedTable) {
          return {
            content: [
              {
                type: "text",
                text: "无法找到匹配的数据库或表。请提供更具体的查询或明确指定数据库ID和表名。",
              },
            ],
          };
        }
        
        // 获取表的字段信息
        const fields = await getTableFields(selectedDb.id, selectedTable.schema, selectedTable.name);
        
        if (fields.length === 0) {
          return {
            content: [
              {
                type: "text",
                text: `无法获取表 ${selectedTable.schema}.${selectedTable.name} 的字段信息。`,
              },
            ],
          };
        }
        
        // 生成 SQL 查询
        const sql = generateSqlQuery(query, selectedTable, fields);
        
        // 执行查询
        const queryRequest: QueryRequest = {
          database_id: selectedDb.id,
          sql,
          schema: selectedTable.schema,
          client_id: `mcp_client_${Date.now()}`,
          sql_editor_id: `mcp_editor_${Date.now()}`,
          runAsync: false,
          json: true,
        };
        
        console.log(`执行查询: ${sql}`);
        const queryResult = await supersetApi.executeQuery(queryRequest);
        
        // 处理查询结果
        if (queryResult.status === "success") {
          // 格式化查询结果
          const resultText = `查询结果 (${queryResult.data?.length || 0} 行):\n\n` +
            JSON.stringify(queryResult.data, null, 2);
          
          return {
            content: [
              {
                type: "text",
                text: `数据库: ${selectedDb.database_name}\n表: ${selectedTable.schema}.${selectedTable.name}\n\nSQL: ${sql}\n\n${resultText}`,
              },
            ],
          };
        } else if (queryResult.status === "running" && queryResult.query_id) {
          // 异步查询,获取结果
          const results = await supersetApi.getQueryResults(queryResult.query_id);
          
          const resultText = `查询结果 (${results.data?.length || 0} 行):\n\n` +
            JSON.stringify(results.data, null, 2);
          
          return {
            content: [
              {
                type: "text",
                text: `数据库: ${selectedDb.database_name}\n表: ${selectedTable.schema}.${selectedTable.name}\n\nSQL: ${sql}\n\n${resultText}`,
              },
            ],
          };
        } else {
          return {
            content: [
              {
                type: "text",
                text: `查询失败: ${queryResult.error?.message || "未知错误"}\n\nSQL: ${sql}`,
              },
            ],
          };
        }
      } catch (error) {
        console.error("执行查询时发生错误:", error);
        return {
          content: [
            {
              type: "text",
              text: `执行查询时发生错误: ${(error as Error).message}`,
            },
          ],
        };
      }
    }
  • Input schema definition using Zod for validating tool parameters: query (required string), optional databaseId, schema, tableName.
      query: z.string().describe("用户的自然语言查询,例如'查询最近10条日志'"),
      databaseId: z.number().optional().describe("可选的数据库ID,如果不提供将自动选择匹配的数据库"),
      schema: z.string().optional().describe("可选的schema名称"),
      tableName: z.string().optional().describe("可选的表名"),
    },
  • src/index.ts:158-296 (registration)
    Registration of the 'query-superset' tool on the MCP server using server.tool(), including name, description, input schema, and handler function.
    server.tool(
      "query-superset",
      "执行 Superset 数据查询",
      {
        query: z.string().describe("用户的自然语言查询,例如'查询最近10条日志'"),
        databaseId: z.number().optional().describe("可选的数据库ID,如果不提供将自动选择匹配的数据库"),
        schema: z.string().optional().describe("可选的schema名称"),
        tableName: z.string().optional().describe("可选的表名"),
      },
      async ({ query, databaseId, schema, tableName }) => {
        try {
          // 如果缓存为空,初始化缓存
          if (databasesCache.length === 0) {
            initializeCache();
            return {
              content: [
                {
                  type: "text",
                  text: "初始化缓存中...",
                },
              ],
            };
          }
    
          
          let selectedDb: Database | undefined;
          let selectedTable: Table | undefined;
          
          // 如果提供了明确的数据库ID和表名
          if (databaseId !== undefined && schema && tableName) {
            selectedDb = databasesCache.find(db => db.id === databaseId);
            
            if (selectedDb) {
              const tables = tablesCache.get(selectedDb.id) || [];
              selectedTable = tables.find(t => t.schema === schema && t.name === tableName);
            }
          } else {
            // 否则,根据查询自动选择匹配的表
            const match = findMatchingTable(query);
            if (match) {
              selectedDb = match.database;
              selectedTable = match.table;
            }
          }
          
          if (!selectedDb || !selectedTable) {
            return {
              content: [
                {
                  type: "text",
                  text: "无法找到匹配的数据库或表。请提供更具体的查询或明确指定数据库ID和表名。",
                },
              ],
            };
          }
          
          // 获取表的字段信息
          const fields = await getTableFields(selectedDb.id, selectedTable.schema, selectedTable.name);
          
          if (fields.length === 0) {
            return {
              content: [
                {
                  type: "text",
                  text: `无法获取表 ${selectedTable.schema}.${selectedTable.name} 的字段信息。`,
                },
              ],
            };
          }
          
          // 生成 SQL 查询
          const sql = generateSqlQuery(query, selectedTable, fields);
          
          // 执行查询
          const queryRequest: QueryRequest = {
            database_id: selectedDb.id,
            sql,
            schema: selectedTable.schema,
            client_id: `mcp_client_${Date.now()}`,
            sql_editor_id: `mcp_editor_${Date.now()}`,
            runAsync: false,
            json: true,
          };
          
          console.log(`执行查询: ${sql}`);
          const queryResult = await supersetApi.executeQuery(queryRequest);
          
          // 处理查询结果
          if (queryResult.status === "success") {
            // 格式化查询结果
            const resultText = `查询结果 (${queryResult.data?.length || 0} 行):\n\n` +
              JSON.stringify(queryResult.data, null, 2);
            
            return {
              content: [
                {
                  type: "text",
                  text: `数据库: ${selectedDb.database_name}\n表: ${selectedTable.schema}.${selectedTable.name}\n\nSQL: ${sql}\n\n${resultText}`,
                },
              ],
            };
          } else if (queryResult.status === "running" && queryResult.query_id) {
            // 异步查询,获取结果
            const results = await supersetApi.getQueryResults(queryResult.query_id);
            
            const resultText = `查询结果 (${results.data?.length || 0} 行):\n\n` +
              JSON.stringify(results.data, null, 2);
            
            return {
              content: [
                {
                  type: "text",
                  text: `数据库: ${selectedDb.database_name}\n表: ${selectedTable.schema}.${selectedTable.name}\n\nSQL: ${sql}\n\n${resultText}`,
                },
              ],
            };
          } else {
            return {
              content: [
                {
                  type: "text",
                  text: `查询失败: ${queryResult.error?.message || "未知错误"}\n\nSQL: ${sql}`,
                },
              ],
            };
          }
        } catch (error) {
          console.error("执行查询时发生错误:", error);
          return {
            content: [
              {
                type: "text",
                text: `执行查询时发生错误: ${(error as Error).message}`,
              },
            ],
          };
        }
      }
    );
  • Helper function to generate SQL query from natural language input, table info, and fields, including basic WHERE and LIMIT extraction.
    function generateSqlQuery(query: string, table: Table, fields: Field[]): string {
      // 基本的 SQL 生成,可以根据需要改进
      const fieldNames = fields.map(f => `"${f.name}"`).join(", ");
      
      // 提取可能的过滤条件
      let whereClause = "";
      const keywords = ["where", "filter", "条件", "筛选"];
      
      for (const keyword of keywords) {
        const keywordIndex = query.toLowerCase().indexOf(keyword);
        if (keywordIndex !== -1) {
          const condition = query.substring(keywordIndex + keyword.length).trim();
          if (condition) {
            // 尝试从条件中提取字段名和值
            for (const field of fields) {
              if (condition.toLowerCase().includes(field.name.toLowerCase())) {
                // 简单的条件提取,实际应用中可能需要更复杂的解析
                whereClause = `WHERE "${field.name}" LIKE '%${condition.replace(/['"]/g, "")}%'`;
                break;
              }
            }
          }
        }
      }
      
      // 提取可能的限制行数
      let limitClause = "LIMIT 10"; // 默认限制
      const limitKeywords = ["limit", "top", "限制", "前"];
      
      for (const keyword of limitKeywords) {
        const keywordIndex = query.toLowerCase().indexOf(keyword);
        if (keywordIndex !== -1) {
          const limitText = query.substring(keywordIndex + keyword.length).trim().split(/\s+/)[0];
          const limit = parseInt(limitText);
          if (!isNaN(limit) && limit > 0) {
            limitClause = `LIMIT ${limit}`;
          }
        }
      }
      
      return `SELECT ${fieldNames} FROM "${table.schema}"."${table.name}" ${whereClause} ${limitClause}`;
    }
  • Key helper method in SupersetApiService for executing SQL queries against Superset API, used directly in the tool handler.
    public async executeQuery(request: QueryRequest): Promise<QueryResult> {
      try {
        // 确保必要的参数
        if (!request.database_id || !request.sql) {
          throw new Error('执行查询需要提供 database_id 和 sql 参数');
        }
        
        // 设置默认值
        const queryRequest: QueryRequest = {
          ...request,
          client_id: request.client_id || `client_${Date.now()}`,
          sql_editor_id: request.sql_editor_id || `editor_${Date.now()}`,
          runAsync: request.runAsync !== undefined ? request.runAsync : false,
          json: true
        };
        
        const response = await this.client.post<QueryResult>('/api/v1/sqllab/execute/', queryRequest);
        console.log(response)
        if (!response.success) {
          throw new Error(response.error?.message || '执行查询失败');
        }
        
        return response.data as QueryResult;
      } catch (error) {
        console.error('执行查询失败:', error);
        throw error;
      }
    }
Behavior2/5

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

No annotations are provided, so the description carries the full burden of behavioral disclosure. It states the tool executes a query but doesn't describe what happens (e.g., returns data, modifies data, requires authentication, has rate limits, or error handling). For a query tool with zero annotation coverage, this leaves critical behavioral traits unspecified.

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

Conciseness5/5

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

The description is a single, efficient sentence with zero waste. It's appropriately sized and front-loaded, directly stating the tool's purpose without unnecessary elaboration. Every word earns its place, making it highly concise.

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

Completeness2/5

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

Given the complexity of a data query tool with 4 parameters, no annotations, and no output schema, the description is incomplete. It doesn't explain what the tool returns, how results are formatted, or any behavioral context. The agent lacks sufficient information to understand the tool's full functionality and outcomes.

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%, so the schema already documents all four parameters (databaseId, query, schema, tableName) with descriptions. The description adds no additional meaning beyond what the schema provides, such as explaining relationships between parameters or usage examples. Baseline 3 is appropriate when the schema does the heavy lifting.

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

Purpose3/5

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

The description '执行 Superset 数据查询' (Execute Superset data query) states the basic action but is vague. It specifies the verb '执行' (execute) and resource 'Superset 数据查询' (Superset data query), but doesn't clarify what type of query this is (natural language, SQL, etc.) or how it differs from sibling tools like list-databases, list-fields, or list-tables. It's functional but lacks specificity.

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

Usage Guidelines2/5

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

The description provides no guidance on when to use this tool versus alternatives. It doesn't mention any prerequisites, context for usage, or comparisons with sibling tools. The agent must infer usage from the input schema alone, which is insufficient for effective tool selection.

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

Related 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/LiusCraft/superset-mcp-server'

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