Every AI assistant in your organization — Claude in the IDE, the support copilot, the analyst chatbot — will eventually want to touch the data platform. The wrong answer is a different bespoke integration per assistant, each with its own credentials and its own idea of "safe".
Tier: Advanced · Workflow area: Text-to-SQL & AI-ready serving
The Model Context Protocol (MCP) gives you one governed surface instead: a server exposing a small set of typed tools — run_query with limits, search_catalog, get_lineage — that any MCP-capable assistant can use, with authorization and audit logging in one place you control.
Don't reach for it to serve one internal app you fully control — a plain API is simpler. MCP pays off at the many assistants, one platform shape.
Design the tool surface as verbs you're willing to govern. Start minimal and read-only:
search_catalog(query) — find tables/columns by name and description; returns metadata only.get_schema(table) — columns, types, descriptions for one table.get_lineage(table) — upstream/downstream dependencies.run_query(sql, max_rows) — execute a single SELECT under hard limits.Resist a generic execute(anything) tool; each verb having its own schema is what lets you gate, log, and rate-limit them differently.
Implement with an MCP SDK, one tool at a time. The Python SDK makes each tool a typed function; descriptions matter because the model reads them to decide when to call:
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("data-platform")
@mcp.tool()
def run_query(sql: str, max_rows: int = 100) -> str:
"""Run a single read-only SELECT against the analytics warehouse.
Rejects DDL/DML. Results capped at max_rows (hard limit 1000)."""
validate_select_only(sql) # parse; reject anything else
return execute_readonly(sql, min(max_rows, 1000))
Enforce guardrails below the tool layer. Same doctrine as text-to-SQL: the server's warehouse credential is a read-only role over allowlisted schemas, with statement timeouts and a dedicated small warehouse/reservation so runaway cost is bounded. The validate_select_only parser is defense in depth on top of the grant, not instead of it. search_catalog and get_lineage read from the catalog/metadata store, never from data.
Authenticate callers and scope what they see. Every connection is identified (per-assistant tokens or the org's SSO-backed gateway). Map identities to scopes: the finance copilot sees finance marts; the eng assistant sees observability tables. Deny by default. MCP is the transport — authz is your code, and it's the part that makes this "governed" rather than "exposed".
Log every call as an audit event. One structured record per tool call: caller identity, tool, arguments (the SQL text), rows returned, duration, and outcome — into the warehouse itself so it's queryable:
SELECT caller, tool, COUNT(*), SUM(rows_returned)
FROM platform_audit.mcp_calls
WHERE called_at >= CURRENT_DATE - 7
GROUP BY 1, 2 ORDER BY 3 DESC;
Expected outcome: you can answer "which assistant queried what, when, and how much" in one query — this is the requirement security will actually hold you to.
Test with a real assistant against a staging warehouse. Connect an MCP-capable client, then run a scripted conversation: a catalog search, a schema lookup, a valid query, and the hostile set — DELETE attempt, blocked-schema read, a query designed to blow the timeout, and a prompt-injected "ignore your limits". Expected output: correct answers on the valid path, clean refusals with logged denials on the hostile one.
Version and publish the surface. Tool schemas are a contract with every assistant in the org — version them, announce changes, and keep a changelog. Removing a tool argument breaks someone's copilot in a way you'll hear about at a distance.
An MCP server executes whatever a model decides to send it, on behalf of users who may be prompt-injected by content they're reading. Design every tool assuming hostile input on every call — allowlist grants, parse-and-reject, hard caps — because "the assistant would never do that" is not a security property.
run_anything(sql) as the only tool discards the per-verb gating, logging, and rate limits that justify MCP over a bare connection.Standing up an MCP server inside a client environment concentrates risk — it's a single door to their warehouse — so treat it as a security deliverable, not a utility script. Deploy it in the client's infrastructure under their identity provider, with credentials from their secrets manager; your team should never hold the warehouse role. Get the tool surface, scope matrix, and hostile-test results reviewed by their security team before any assistant connects, and agree which assistants (and which LLM vendors behind them) are in scope — the server hands query results to whatever model the assistant runs on, which is where their data-egress policy actually bites. Hand off the audit dashboard, the versioning process, and an incident runbook (how to revoke one assistant's access in minutes) as part of closing the engagement.