
Your agent needs to query the warehouse. It has to find the right table, generate SQL a data engineer would not be embarrassed by, run it under the right role, and come back with numbers someone will put in a board deck. Snowflake ships a managed MCP server and a full REST API surface, and unlike most tools in this comparison, both live at the same /api/v2 endpoint prefix on your account URL. That symmetry makes the choice less obvious than it looks. Here is how to pick.
Most MCP-versus-API comparisons pit a vendor-defined tool catalog against a raw REST surface. Snowflake does not fit that shape. Both objects below are Snowflake REST resources, addressed on the same host, authenticated the same way. What differs is who authors the contract and what the contract can carry.
The Snowflake-managed MCP server is Generally Available and supports MCP revision 2025-11-25. It is not available in government regions. Snowflake hosts and scales it; you define it as a first-class object inside a database and schema, and it is served at https://<account_url>/api/v2/databases/{database}/schemas/{schema}/mcp-servers/{name}.
You author the tool surface in a YAML specification:
USAGE on the MCP server lets a client connect and discover tools. Invoking a tool requires a separate grant on the underlying object: USAGE on the Cortex Search Service, SELECT on the semantic view, USAGE on the Cortex Agent or the UDF. Server access does not imply tool access.
The Snowflake REST API covers control-plane resources including Databases, Schemas, Tables, Warehouses, Tasks, Dynamic Tables, Grants, Roles, Stages, and Users, plus the Cortex family. The SQL API at /api/v2/statements executes standard queries and most DDL and DML, synchronously or asynchronously, returning results in partitions Snowflake sizes for you.
Alongside it sit the purpose-built AI endpoints: Cortex Analyst for text-to-SQL, Cortex Search at cortex-search-services/{name}:query, Cortex Inference at /api/v2/cortex/v1/chat/completions, and the Cortex Agents Run API at agent:run, which streams by default and supports background runs up to six hours.
Authentication accepts KEYPAIR_JWT, OAUTH, and PROGRAMMATIC_ACCESS_TOKEN, selected with the optional X-Snowflake-Authorization-Token-Type header.
Four dimensions decide this for a production agent: what the surface can do, how you authenticate, what you own operationally, and which shape of workload each one fits. Capability first, because for Snowflake the gap is narrower and stranger than you would expect.
The MCP server covers the analytical read path well. Everything structural, streaming, or large lives on the REST side.
The limits are quantitative, not categorical. Fifty tools per server, counting Search, Analyst, Agents, SQL execution, and generic tools together. Responses from SYSTEM_EXECUTE_SQL and GENERIC tools truncate at 250 KB, which a single wide SELECT will blow through. Non-streaming only, so a two-minute Cortex Analyst response arrives as silence and then a wall of JSON.
The MCP server also drops most of the protocol: no resources, prompts, roots, notifications, version negotiation, or sampling. Tools only.
This inverts the usual tradeoff and it is the most useful thing to internalize about Snowflake MCP. With Slack or Salesforce, the vendor decides what the MCP tools are and you accept the gap. With Snowflake, a poorly performing agent is frequently your specification, not Snowflake's ceiling.
Snowflake says it directly in the limitations: higher tool counts degrade tool-selection accuracy. The read_only flag on SYSTEM_EXECUTE_SQL is the single highest-leverage line in most specifications, and one Cortex Analyst tool per semantic domain beats one tool over everything. To understand more about how MCP and APIs differ structurally, the tradeoffs go deeper than just tool count.
For most tools in this series, MCP forces an interactive OAuth flow and the API offers a headless escape hatch. Snowflake does not work that way, which changes what you should actually worry about.
Snowflake recommends OAuth 2.0 for the MCP server and documents CREATE SECURITY INTEGRATION with OAUTH_CLIENT = CUSTOM as the setup path. Dynamic Client Registration is not supported, so the client ID and secret are provisioned once and shared across all users in the account. Each user still authenticates individually to obtain their own access token.
Programmatic Access Tokens work as a bearer credential against the MCP endpoint too. Snowflake's own getting-started guide posts tools/list with Authorization: Bearer <PAT>, and Snowflake's guidance is to scope any PAT to the least-privileged role that can reach the server, because the token is a secret carrying that role.
This is the number that decides your architecture. Snowflake OAuth access tokens return expires_in: 600. Refresh token validity is configurable through OAUTH_REFRESH_TOKEN_VALIDITY and can extend to 90 days, but the access token is short by design.
An analytical agent that discovers schema, generates SQL, waits on a warehouse, and fetches three result partitions will cross a ten-minute boundary regularly. Waiting for a 401 is not a strategy at that cadence; it is a guaranteed mid-run failure on any query that takes real time. The operational challenge of token refresh for AI agents is well-documented and Snowflake's 600-second window makes it acute.
Secondary roles are not supported in Snowflake OAuth sessions. The session runs as the connecting user's DEFAULT_ROLE, and Snowflake's own recommendation for differentiated access is separate agents with dedicated roles rather than role switching.
Two operational consequences follow. Every user needs DEFAULT_ROLE and DEFAULT_WAREHOUSE set, because a null default warehouse fails session initialization outright. And ACCOUNTADMIN, ORGADMIN, GLOBALORGADMIN, and SECURITYADMIN are blocked from OAuth authentication by default, which is correct and worth leaving alone.
Snowflake manages more of the MCP path than most vendors do, because the server is an object in your account rather than a service in theirs. The ownership split still matters, and it is not where you would guess.
Snowflake owns hosting, scaling, and protocol compliance. You own the specification, which means you own tool-selection accuracy. You own per-user token storage and proactive refresh against the 600-second window. You own the grant matrix, since server access and tool access are separate privileges.
You also own disaster recovery for the server definition. MCP server objects are not replicated in failover groups, so a secondary account needs them recreated. OAuth security integrations do replicate.
You own the full surface: request construction, partition pagination on large result sets, 429 handling with jittered backoff, statement handles for async execution, and adapter code for each endpoint family you touch.
You gain explicit control in return. Async execution with async=true returns a statement handle you poll on your own schedule. Background Cortex Agent runs survive six hours. Partitioned fetch means a 40 GB scan is a pagination problem rather than a truncated response.
This one surprises teams in the first week. When a remote MCP client such as Claude, ChatGPT, or Cursor connects to your MCP server, the request originates from the client provider's infrastructure, not from the end user's browser. If your account has network policies enabled, those outbound IP ranges must be allowlisted with an ingress network rule.
Underscores in hostnames also break MCP connections. Use hyphens in the account URL.
Both lists below assume a production agent with real users, not a prototype. The split follows workload shape rather than capability envy.
Snowflake enforces identity correctly. Every MCP tool call and every SQL API statement runs under the authorizing user's role, with row access policies and masking applied. What Snowflake does not do is manage the credential lifecycle for your application.
Take an analytics agent serving 60 analysts across 5 Snowflake accounts. That is 60 OAuth grants to store encrypted, 60 access tokens to refresh before each 600-second expiry, and 60 grants to revoke at offboarding. A ten-minute token refreshed reactively across 60 users generates a steady stream of mid-query 401s. This is the core challenge that secure token management for AI agents at scale must solve, regardless of which Snowflake path you choose.
The token type differs between the two paths. The infrastructure required does not.
Snowflake supports single-use refresh tokens, where a successful refresh invalidates all previous refresh tokens and access tokens for that grant. Security-wise this is the right default. Operationally it converts naive concurrent refresh into a correctness bug.
Two agent threads refresh the same user's token simultaneously. One wins. The other now holds a token Snowflake has already invalidated, and every subsequent call for that user fails until someone reauthorizes. Distributed locking around refresh is not optional at this token lifetime. The question of who holds the token across agent tool-calling patterns is precisely why credential infrastructure cannot be an afterthought.
Scalekit's Snowflake connector resolves the per-user credential server-side on every tool call, refreshes proactively against the 600-second window, and serializes refresh so concurrent agent threads do not invalidate each other. Credentials never touch the agent runtime or the LLM context.
A second connector, Snowflake Key Pair Auth, covers the key-pair JWT path for service-identity workloads. Both expose the same fourteen tools built on the SQL API, so the auth model changes without the agent code changing.
The connection is configured once per environment and reused for every user. What follows is the discovery, scope, and execution sequence with the Snowflake connector.
In the Scalekit dashboard, go to AgentKit, Connections, Create Connection, pick Snowflake, and copy the redirect URI. Then create the matching security integration in Snowsight:
Paste OAUTH_CLIENT_ID and one of the returned client secrets back into the Scalekit connection. The connection_name you set here must match every connection_name string in your code exactly. This mismatch is the single most common integration error.
get_or_create_connected_account returns the account if it exists and creates it if it does not. Surface the authorization link only when the status is not ACTIVE.
Before any tool runs, the agent loads the tools this user's connected account is authorized to call. This is not a connector catalog and it is not exploration. It is a scoped, deterministic surface derived from what the user granted.
The Snowflake connector exposes fourteen tools, including snowflake_execute_query, snowflake_show_databases_schemas, snowflake_get_tables, snowflake_get_columns, snowflake_get_query_status, and snowflake_get_query_partition. A schema-exploration agent needs four of them.
actions.langchain.get_tools() returns native StructuredTool objects. Filtering by tool_names is what keeps the decision space small enough for reliable selection. This pattern mirrors what the LangChain tool calling integration enables more broadly across connectors.
listScopedTools returns schemas in input_schema form, which is exactly what Anthropic's tool use API expects. No conversion layer.
Cortex Analyst, Cortex Search, and agent:run are not in the prebuilt tool list. actions.request proxies any Snowflake REST path with the user's credential injected server-side, and resolves the account domain from the connected account so you never hardcode it.
A warehouse agent is rarely only a warehouse agent. It queries Snowflake, then posts to Slack, then files a ticket. Scalekit's Virtual MCP servers give that agent one endpoint across all of it, with per-user credential isolation and no MCP server to deploy or maintain.
A Snowflake connection surfaces fourteen tools. A revenue summarizer needs four. Handing it all fourteen means snowflake_execute_query with write-capable SQL sits inside the blast radius of a read-only reporting job, and every unused schema burns context on every run.
The tool-bloat math is unforgiving. Forty tools at roughly 200 tokens each is 8,000 tokens before the agent does any work. Scoping to five or ten cuts that by around 80 percent and materially improves selection accuracy. The fix is not better prompting. It is surface reduction. For perspective on why MCP is significantly more expensive than CLI in token terms, the math applies directly to tool count discipline.
Define the server once, not once per user. The response carries a static mcp_server_url you reuse for every user and every session. Add a McpConfigConnectionToolMapping entry per connector to span multiple tools in one endpoint.
Runtime is a token mint. Confirm the user's connections are still active, then issue a short-lived token bound to that specific user's connected accounts. Never reuse a token across sessions.
Set expiry longer than the expected run. The endpoint is static; the identity is per-user.
Snowflake's own history views answer what ran. They cannot answer which agent run triggered it, under which authorization grant, or whether that grant was still valid at execution time. That gap is where audits stall.
The question a security reviewer asks is never "did a query run." It is: which human authorized this agent, when did they consent, what scope did they grant, and was the credential valid at the moment of the call. Standard application logs capture a user ID and a timestamp, which is not enough when four principals are involved in a single action. Understanding audit trails for agent auth in B2B SaaS clarifies exactly what events must be captured and why Snowflake's query history alone is insufficient.
Scalekit's auth logs record authorization events, token lifecycle events, and every downstream tool call tied back to the connected account that authorized it, with 90 days of queryable history.
QUERY_HISTORY shows the user and the role. With per-user connected accounts that attribution is at least correct, which is more than a shared service account gives you. What it still cannot show is the agent run that produced the query, the tool the model selected, or the consent event behind the grant.
Correlate on the connected account and both halves line up: Snowflake tells you what executed against the data, Scalekit tells you which authorization made it permissible. For teams building production observability, agent tool observability requires correlating the LLM decision, the tool call, and the credential event in one place.
The decision is workload shape, not capability envy, and for Snowflake it is unusually clean.
Build against the Snowflake-managed MCP server. Curate a semantic view, expose one Cortex Analyst tool per domain, set read_only: true on SQL execution, and let Snowflake's RBAC and masking policies do the constraining. Keep the specification small on purpose.
Build against the REST API directly. Object management, streaming, background runs, and partitioned result fetch are not coming to the MCP tool types, and truncation at 250 KB is a hard wall you will hit on the first real result set.
Sixty analysts is sixty credentials, refreshed against a 600-second window, serialized so concurrent runs do not invalidate each other, and revoked the day someone leaves. That is the part that needs production-grade infrastructure, and it is identical on both paths.
If you are wiring an agent into a warehouse and want to compare notes on semantic view design, tool scoping, or refresh strategy, join the Scalekit Slack community.
For a working session on a specific architecture, talk to an engineer.
Browse the Scalekit Snowflake connector: scalekit.com/connectors/snowflake