execute-spanner-query
Execute SQL queries on Google Cloud Spanner using predefined instance and database IDs. Automates query execution with optional parameters for efficient data retrieval and management within the MCP Server.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| databaseId | No | Spanner database ID (defaults to SPANNER_DATABASE env var) | |
| instanceId | No | Spanner instance ID (defaults to SPANNER_INSTANCE env var) | |
| params | No | Query parameters | |
| sql | Yes | The SQL query to execute |
Implementation Reference
- src/services/spanner/tools.ts:60-126 (handler)The core handler function for the 'execute-spanner-query' tool. It handles input parameters, retrieves the Google Cloud project ID, configures and initializes the Spanner client, executes the SQL query with optional parameters, processes the results, formats them into a markdown table (limiting to 100 rows), and returns the output as structured text content. Includes error handling.async ({ sql, instanceId, databaseId, params }, _extra) => { try { const projectId = await getProjectId(); const config = await getSpannerConfig( Array.isArray(instanceId) ? instanceId[0] : instanceId, Array.isArray(databaseId) ? databaseId[0] : databaseId ); const spanner = await getSpannerClient(); console.log(`Using Spanner client with project ID: ${spanner.projectId} for execute-spanner-query`); const instance = spanner.instance(config.instanceId); const database = instance.database(config.databaseId); // Execute the query const [result] = await database.run({ sql, params: params || {} }); if (!result || result.length === 0) { return { content: [{ type: 'text', text: `# Query Results\n\nProject: ${projectId}\nInstance: ${config.instanceId}\nDatabase: ${config.databaseId}\n\nQuery executed successfully. No results returned.` }] }; } // Convert to markdown table const columns = Object.keys(result[0]); let markdown = `# Query Results\n\nProject: ${projectId}\nInstance: ${config.instanceId}\nDatabase: ${config.databaseId}\n\n`; markdown += `SQL: \`${sql}\`\n\n`; markdown += `Rows: ${result.length}\n\n`; // Table header markdown += '| ' + columns.join(' | ') + ' |\n'; markdown += '| ' + columns.map(() => '---').join(' | ') + ' |\n'; // Table rows (limit to 100 rows for display) const displayRows = result.slice(0, 100); for (const row of displayRows) { const rowValues = columns.map(col => { const value = (row as any)[col]; if (value === null || value === undefined) return 'NULL'; if (typeof value === 'object') return JSON.stringify(value); return String(value); }); markdown += '| ' + rowValues.join(' | ') + ' |\n'; } if (result.length > 100) { markdown += '\n*Results truncated. Showing 100 of ' + result.length + ' rows.*'; } return { content: [{ type: 'text', text: markdown }] }; } catch (error: any) { console.error('Error executing Spanner query:', error); throw error; } }
- src/services/spanner/tools.ts:54-59 (schema)The Zod input schema for the 'execute-spanner-query' tool, defining required 'sql' string and optional 'instanceId', 'databaseId', and 'params' fields with descriptions.{ sql: z.string().describe('The SQL query to execute'), instanceId: z.string().optional().describe('Spanner instance ID (defaults to SPANNER_INSTANCE env var)'), databaseId: z.string().optional().describe('Spanner database ID (defaults to SPANNER_DATABASE env var)'), params: z.record(z.any()).optional().describe('Query parameters') },
- src/services/spanner/tools.ts:52-127 (registration)The registration of the 'execute-spanner-query' tool using server.tool(), including the tool name, input schema, and handler function within the registerSpannerTools export.server.tool( 'execute-spanner-query', { sql: z.string().describe('The SQL query to execute'), instanceId: z.string().optional().describe('Spanner instance ID (defaults to SPANNER_INSTANCE env var)'), databaseId: z.string().optional().describe('Spanner database ID (defaults to SPANNER_DATABASE env var)'), params: z.record(z.any()).optional().describe('Query parameters') }, async ({ sql, instanceId, databaseId, params }, _extra) => { try { const projectId = await getProjectId(); const config = await getSpannerConfig( Array.isArray(instanceId) ? instanceId[0] : instanceId, Array.isArray(databaseId) ? databaseId[0] : databaseId ); const spanner = await getSpannerClient(); console.log(`Using Spanner client with project ID: ${spanner.projectId} for execute-spanner-query`); const instance = spanner.instance(config.instanceId); const database = instance.database(config.databaseId); // Execute the query const [result] = await database.run({ sql, params: params || {} }); if (!result || result.length === 0) { return { content: [{ type: 'text', text: `# Query Results\n\nProject: ${projectId}\nInstance: ${config.instanceId}\nDatabase: ${config.databaseId}\n\nQuery executed successfully. No results returned.` }] }; } // Convert to markdown table const columns = Object.keys(result[0]); let markdown = `# Query Results\n\nProject: ${projectId}\nInstance: ${config.instanceId}\nDatabase: ${config.databaseId}\n\n`; markdown += `SQL: \`${sql}\`\n\n`; markdown += `Rows: ${result.length}\n\n`; // Table header markdown += '| ' + columns.join(' | ') + ' |\n'; markdown += '| ' + columns.map(() => '---').join(' | ') + ' |\n'; // Table rows (limit to 100 rows for display) const displayRows = result.slice(0, 100); for (const row of displayRows) { const rowValues = columns.map(col => { const value = (row as any)[col]; if (value === null || value === undefined) return 'NULL'; if (typeof value === 'object') return JSON.stringify(value); return String(value); }); markdown += '| ' + rowValues.join(' | ') + ' |\n'; } if (result.length > 100) { markdown += '\n*Results truncated. Showing 100 of ' + result.length + ' rows.*'; } return { content: [{ type: 'text', text: markdown }] }; } catch (error: any) { console.error('Error executing Spanner query:', error); throw error; } } );
- src/index.ts:140-140 (registration)Invocation of registerSpannerTools(server) in the main server setup, which registers the execute-spanner-query tool among others.registerSpannerTools(server);