MCP server for querying Snowflake databases and exporting results to Google Sheets with email campaign capabilities
Server has 21 tools with mostly complete schemas and descriptions, but significant gaps in naming clarity, parameter validation, and error handling guidance. Many tools lack actionable error messages and recovery hints. Some parameter descriptions are generic or incomplete. Tool names are generally verb-based but lack consistency in distinguishing similar operations (e.g., search_snowflake vs _query_data_internal). Output schemas are not formally documented. Error handling returns raw exceptions rather than categorized, actionable guidance.
[INTERNAL] Discover available Snowflake databases. Use this internally to find data sources. DO NOT show database names to users — use this information to construct queries only. Returns ------- str Markdown table of databases with name, owner, and creation date.
[INTERNAL] Discover available schemas within a Snowflake database. Use this internally to explore database structure. DO NOT show schema/database names to users — use this to find where data lives and then construct targeted queries. Parameters ---------- database_name: Optional. Limit discovery to schemas inside this database. Defaults to the current database context. Returns ------- str Markdown table of schemas with database, schema name, and creation date.
[INTERNAL] Discover available tables within a Snowflake schema. Use this internally to find what data exists before constructing a query. DO NOT show table names to users — use this to explore structure, then present results in business terms. Parameters ---------- schema_name: Optional. Fully-qualified schema (e.g. ``"FINANCIALS.PUBLIC"``). Defaults to the current schema context. Returns ------- str Markdown table of tables with schema, table name, type, and row count.
[INTERNAL] Execute a Snowflake SQL query and return results. Business users should not call this directly. The agent uses this internally to answer exploratory, ad-hoc questions that don't have a dedicated hardcoded tool. **Why [INTERNAL]?** The ``[INTERNAL]`` prefix in the name signals to the LLM (via the system prompt) that this tool is infrastructure — it should never say "I'm using _query_data_internal" in a response. The user should only ever see the *results*, expressed in business language. **Safety guardrails:** - The Snowflake role used has SELECT-only privileges (no writes). - The system prompt explicitly forbids generating DML (INSERT/UPDATE/DELETE). - Parameterised values aren't used here because Snowflake DDL names can't be parameterised; instead, the system prompt enforces READ-ONLY. Parameters ---------- query: Valid Snowflake SQL SELECT statement. Constructed by the LLM from the user's natural language question. Returns ------- str Markdown table of results, or a user-friendly error message.
Internal tools use underscore prefix naming (_list_databases_internal, _query_data_internal) which is non-standard. LLM instruction to avoid these relies on system prompt fragility rather than tool design. Consider separate tool registry or explicit 'internal' parameter.
Error handling returns raw exception messages and toolkit.error_handler.format_error_response() calls, but actual error categorization (retryable vs user-fixable vs fatal) is not visible in source. Error responses lack recovery guidance like 'Try search_users() with a partial name.'
Output schemas not formally documented. Tools return formatted tables or JSON but LLM has no schema definition of what fields to expect. E.g., list_tables returns SCHEMA, TABLE, TYPE, ROWS but this is not declared in tool metadata.
Inferred effective spec: 2026-07-28+.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | D | 56 | 2026-07-28+ | v2 |
Create a chart in a Google Sheet for visual insights. Use this after exporting data to visualise trends, comparisons, or proportions. Parameters ---------- spreadsheet_id: Alias or raw spreadsheet ID. sheet_name: Worksheet tab name where the source data lives. chart_type: One of: ``line``, ``bar``, ``column``, ``pie``, ``scatter``, ``area``. data_range: A1 notation range covering headers + data (e.g. ``"A1:B10"``). chart_title: Title shown above the chart. Returns ------- str Confirmation with the sheet URL.
Create a new Google Sheet and share it with the configured user email. After creation, the sheet is saved with an auto-generated alias (e.g. ``"q4_report"`` for a sheet titled ``"Q4 Report"``) so subsequent tools can reference it by name instead of ID. Parameters ---------- title: Display name for the new spreadsheet. Returns ------- str JSON with ``status``, ``spreadsheet_id``, ``alias``, and ``url``.
Fetch detailed information for a specific account from Snowflake. This is a helper tool that queries the FINANCIAL_SUMMARY table.
Look up information about a specific customer or account. Use this when the user asks about a specific account, customer, or client by name. Returns key details and metrics for that account. **Why hardcoded SQL?** This is the most frequent query in the BI workflow and the one where errors would be most visible to clients. By hardcoding the SQL pattern, we guarantee consistent results regardless of how the LLM interprets the user's question. Parameters ---------- account_name: The name (or partial name) of the account to look up. Returns ------- str Markdown table of account details, or a not-found message.
Get column information for a table. Args: table_name: Table name (can include schema)
Get statistics about a table (row count, size, etc.). Args: table_name: Table name (can include schema)
List all databases accessible to the current user. Returns: Clean formatted list of databases
List all saved resource aliases. Returns ------- str JSON mapping of alias → resource ID.
List all schemas in the current database or a specified database. Args: database_name: Optional database name. If empty, uses current database from config. Returns: Clean formatted list of schemas
List all tables in the current schema or a specified schema. Args: schema_name: Optional schema name. If empty, uses current schema from config. Returns: Clean formatted list of tables
Preview the first N rows of a table. Args: table_name: Table name (can include schema, e.g., SCHEMA.TABLE or just TABLE) limit: Number of rows to preview (default: 10)
Get statistical profile of query results (count, nulls, distinct values). This tool analyzes each column in the result set. Args: query: SQL query to profile
Free up Drive storage by deleting old Google Sheets/files. **Safe by default** — running without arguments lists files that *would* be deleted without actually deleting them. Parameters ---------- max_files: Maximum number of files to delete (guards against mass deletion). older_than_days: Only target files created more than N days ago. dry_run: ``True`` → list only. ``False`` → actually delete. Returns ------- str List of files (dry run) or deletion report (live run).
Rename an existing Google Sheet and update the alias store. Parameters ---------- sheet_alias_or_id: Current alias or raw spreadsheet ID. new_name: New display name for the sheet. Returns ------- str Confirmation message with the sheet URL and new alias.
Save a user-friendly alias for a resource ID. Parameters ---------- alias: Short, memorable name. resource_id: The actual resource identifier (typically a Google Spreadsheet ID). Returns ------- str Confirmation message.
Execute a SQL query against Snowflake. Use this tool to look up data, find accounts, or retrieve metrics. Ensure strict SQL syntax.
Send personalised campaign emails from a Google Sheet. Reads customer data from a Google Sheet and sends an email to each row that has a valid email address. Subject and body templates use Python ``str.format()`` syntax with column names as keys. **ALWAYS start with dry_run=True** to preview the emails before sending! Example Templates ----------------- - subject: ``"Exclusive offer for {customer_name}!"`` - body: ``"Hi {customer_name}, we'd love to offer you {recommended_product}."` Parameters ---------- sheet_id_or_alias: Google Sheet ID or saved alias (see ``save_resource_alias``). Also accepts a full Sheet URL. subject_template: Email subject line with ``{column_name}`` placeholders. body_template: Email body with ``{column_name}`` placeholders (plain text). sheet_name: Worksheet tab name containing the campaign data. test_mode: If ``True``, only sends to the first 3 recipients (after dry run). dry_run: If ``True`` (default), shows a preview only — no emails are sent. Set to ``False`` to actually send. Returns ------- str Preview summary (dry run) or delivery report (live run).
Destructive tool prune_drive_files lacks explicit confirmation/dry-run enforcement in tool definition. While description mentions dry_run parameter, no schema validation or confirmation pattern enforces safe-by-default behavior at the tool level.
Parameter descriptions for schema_name and database_name in list_tables/list_schemas are vague ('Optional schema name. If empty, uses current schema from config.'). No guidance on format, valid characters, or how to discover valid names.