developmentTechArticle

Building a Snowflake MCP Server

Query a Snowflake warehouse from an MCP tool, with a bounded row count and awareness of per-second compute billing.

Quick Answer / TL;DR

A Snowflake MCP server uses the official snowflake-sdk driver to run read-only SQL, with results capped after retrieval since Snowflake bills by warehouse compute time rather than bytes scanned like BigQuery.

Key Takeaways

  • Snowflake bills by warehouse size times time running, not by query - a small warehouse left running idle costs the same as one running a query.
  • The Node driver uses a callback-based execute() API, not promises natively - wrap it once in a helper rather than repeating the callback pattern per tool.
  • Use SHOW and DESCRIBE commands for schema discovery instead of querying INFORMATION_SCHEMA directly - they're the idiomatic Snowflake approach and avoid a full metadata scan.

Why not just copy a reference implementation?

The official MCP servers repository is upfront about its purpose: "The servers in this repository are intended as reference implementations to demonstrate MCP features and SDK usage... not as production-ready solutions." That pattern, applied to Snowflake, means no write-statement rejection and no bound on how many rows come back - both are handled explicitly in the helper below rather than assumed.

A promise-wrapped query helper

snowflake-sdk's connection.execute() takes a completion callback rather than returning a promise, so the first thing worth writing is a thin wrapper that turns it into one the rest of the tool code can await normally.

typescript
import snowflake from "snowflake-sdk";

const connection = snowflake.createConnection({
  account: process.env.SNOWFLAKE_ACCOUNT!,
  username: process.env.SNOWFLAKE_USER!,
  password: process.env.SNOWFLAKE_PASSWORD!,
  warehouse: process.env.SNOWFLAKE_WAREHOUSE ?? "COMPUTE_WH",
  database: process.env.SNOWFLAKE_DATABASE,
});

function execute(sqlText: string, binds: unknown[] = []): Promise<Record<string, unknown>[]> {
  return new Promise((resolve, reject) => {
    connection.execute({
      sqlText,
      binds: binds as snowflake.Binds,
      complete: (err, _stmt, rows) => (err ? reject(err) : resolve(rows ?? [])),
    });
  });
}

async function queryTool(sql: string) {
  if (/\b(INSERT|UPDATE|DELETE|DROP|MERGE)\b/i.test(sql)) {
    throw new Error("Only SELECT queries are permitted through this tool.");
  }
  const rows = await execute(sql);
  return rows.slice(0, 200);
}

Building a Snowflake MCP Server FAQs

Direct answers for developers, operators, and Indian teams evaluating MCP.

M
MCPserver Team

MCP documentation and protocol implementation team

Published: 2026-07-21
Updated: 2026-07-21