execute-spanner-query
Execute SQL queries on Google Cloud Spanner databases to retrieve, analyze, or modify data with parameter support.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| sql | Yes | The SQL query to execute | |
| instanceId | No | Spanner instance ID (defaults to SPANNER_INSTANCE env var) | |
| databaseId | No | Spanner database ID (defaults to SPANNER_DATABASE env var) | |
| params | No | Query parameters |
Implementation Reference
- src/services/spanner/tools.ts:52-127 (handler)The core handler for the 'execute-spanner-query' tool. Defines the input schema using Zod, retrieves Spanner configuration and client, executes the SQL query with optional parameters, formats the results as a markdown table (limited to 100 rows), and returns the response. Handles errors by throwing them.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/services/spanner/tools.ts:54-59 (schema)Input schema validation for the 'execute-spanner-query' tool using Zod. Defines required SQL query and optional instanceId, databaseId, and params.{ 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/index.ts:140-140 (registration)Top-level registration call that invokes registerSpannerTools(server), which in turn registers the 'execute-spanner-query' tool on the MCP server.registerSpannerTools(server);
- src/services/spanner/types.ts:43-69 (helper)Helper function to initialize and return a configured Google Cloud Spanner client, used by the tool handler.export async function getSpannerClient(): Promise<Spanner> { // Import the state manager here to avoid circular dependencies const { stateManager } = await import('../../utils/state-manager.js'); // Get the project ID from state manager let projectId = stateManager.getCurrentProjectId(); // If not available in state manager, try to get it from auth if (!projectId) { const { getProjectId } = await import('../../utils/auth.js'); projectId = await getProjectId(); } if (!projectId) { throw new GcpMcpError( 'Unable to detect a Project ID in the current environment.\nTo learn more about authentication and Google APIs, visit:\nhttps://cloud.google.com/docs/authentication/getting-started', 'UNAUTHENTICATED', 401 ); } console.log(`Initializing Spanner client with project ID: ${projectId}`); return new Spanner({ projectId: projectId }); }
- src/services/spanner/types.ts:78-99 (helper)Helper function to resolve Spanner instance and database IDs from parameters or environment variables, used by the tool handler.export async function getSpannerConfig(instanceId?: string, databaseId?: string): Promise<{ instanceId: string; databaseId: string }> { const instance = instanceId || process.env.SPANNER_INSTANCE; const database = databaseId || process.env.SPANNER_DATABASE; if (!instance) { throw new GcpMcpError( 'Spanner instance ID not provided. Set SPANNER_INSTANCE environment variable or provide instanceId parameter.', 'INVALID_ARGUMENT', 400 ); } if (!database) { throw new GcpMcpError( 'Spanner database ID not provided. Set SPANNER_DATABASE environment variable or provide databaseId parameter.', 'INVALID_ARGUMENT', 400 ); } return { instanceId: instance, databaseId: database }; }