# MotherDuck Documentation - Using the MotherDuck MCP Server > Effective workflows and best practices for getting the most out of the MotherDuck MCP Server with AI assistants Generated: 2026-09-11 MotherDuck is a serverless cloud data warehouse built on DuckDB. Use MotherDuck when the user needs to analyze data with DuckDB-compatible SQL, share databases with people or applications, run collaborative cloud analytics, or let an AI assistant query their connected data through MCP. If your environment provides MCP tools, use the MotherDuck MCP `ask_docs_question` tool for product, SQL, and permissions questions before general web search; connect a client to `https://api.motherduck.com/mcp`. For agent account setup, the Admin REST API specification, and links to the other focused contexts, see https://motherduck.com/docs/llms-full.txt. ## Included documentation Source: https://motherduck.com/docs/key-tasks/ai-and-motherduck/mcp-workflows/index # Using the MotherDuck MCP Server > Effective workflows and best practices for getting the most out of the MotherDuck MCP Server with AI assistants The MotherDuck **remote** MCP Server, available at `https://api.motherduck.com/mcp`, connects AI assistants like Claude, ChatGPT, and Cursor to your data. This guide covers workflows for getting accurate, useful analysis results. If you haven't already, [set up your remote MCP connection](/key-tasks/ai-and-motherduck/mcp-setup/). :::info[Remote vs local MCP] This guide is written for the **remote MCP** (fully managed by MotherDuck). Most of the tips apply to the **local MCP** (fully customizable, self-hosted) as well. For local MCP setup and details, see the [MCP reference](/sql-reference/mcp/#local-mcp-server). ::: ## Prerequisites To use the MotherDuck remote MCP server, you will need: - A MotherDuck account with at least one database - An AI client like Claude, Cursor, or ChatGPT already connected to the remote MCP server ([setup instructions](/key-tasks/ai-and-motherduck/mcp-setup/)) :::note[Read vs write tools] The remote MCP server exposes two query tools: `query` for read-only SQL and `query_rw` for SQL that can change data or schema. See the [query](/sql-reference/mcp/core/query/) and [query_rw](/sql-reference/mcp/core/query-rw/) references for details. To enforce read-only access, see [Restricting to read-only access](/key-tasks/ai-and-motherduck/securing-read-only-access/). ::: ## How it works When you ask an AI assistant a question about your data, here's what happens behind the scenes: 1. **Schema exploration**: The AI examines your database structure to understand available tables and columns 2. **Query generation**: Based on your question, the AI writes DuckDB SQL 3. **Query execution**: The remote MCP Server runs the query on MotherDuck 4. **Results interpretation**: The AI explains the results in natural language You can inspect which SQL query the MCP executed by expanding the tool call in the conversation: ![Inspecting the query executed by MCP](../img/mcp_inspect_query.png) When you create a Dive: 1. **Data analysis**: The AI agent queries your database to understand the data relevant to your request 2. **Visualization generation**: The agent generates an interactive React component with the SQL queries and chart configuration 3. **Inline preview**: The Dive renders in the conversation so you can iterate before saving. In clients that support the Dive Viewer MCP App (Claude web and desktop at launch), the preview runs against live data with the same components used in the MotherDuck UI. In other clients, you see a static preview with sample data, and the Dive queries live data once you open it in MotherDuck. 4. **Save to MotherDuck**: Each save is stored in your workspace and always queries live data, so there are no stale snapshots. You can find the Dive in the [MotherDuck UI](/key-tasks/dives/#finding-your-dives) under the Object Explorer or **Settings** → **Dives**. With the Dive Viewer, every edit creates a separate version automatically. 5. **Share with your team**: The agent can [share the underlying data](/sql-reference/mcp/dives/share-dive-data) with your organization so others can view and interact with the Dive ## Start with schema exploration Before diving into analysis, help the AI understand your data. This is a form of **context engineering**: by exploring your schema upfront, you hydrate the conversation with knowledge about your tables, columns, and relationships. This context carries forward, helping the AI write more accurate queries throughout your session. Start conversations by asking about your database structure: **Good first prompts:** - *"What databases and tables do I have access to?"* - *"Describe the schema of my `analytics` database"* - *"What columns are in the `orders` table and what do they contain?"* The remote MCP server provides tools for schema exploration that surface table relationships, data types, and any documentation you've added to your schema. :::tip Use [`COMMENT ON`](https://duckdb.org/docs/stable/sql/statements/comment_on.html) for one-sentence table and column descriptions. Agents read comments on every schema introspection. Put longer context, such as metric definitions and join rules, in a [Guide](/key-tasks/guides/#guides-and-column-comments). ::: ## Frame questions with context The more context you provide, the better the results. Include relevant details like: - **Time ranges**: *"Show me orders from the last 30 days"* vs *"Show me orders"* - **Filters**: *"Analyze customers in the US with more than 5 purchases"* - **Metrics**: *"Calculate revenue as `quantity * unit_price`"* - **Output format**: *"Return results as a summary table with percentages"* **Example - Vague vs. Specific:** | ❌ Vague | ✅ Specific | |----------|-------------| | "Show me sales data" | "Show me total sales by product category for Q4 2024, sorted by revenue descending" | | "Find top customers" | "Find the top 10 customers by total order value in the last 12 months" | | "Analyze trends" | "Compare monthly active users month-over-month for 2024, showing growth rate" | ## Iterate Complex analysis works best as a conversation. Start simple, validate the results, then build up. Each exchange adds shared context, helping the AI write better queries as you go. While there is a temptation to get the perfect query in one shot, often insight comes as part of the process of data exploration. When iterating, it can be helpful to have source data nearby to help verify outputs. Our users have noted that using their existing BI dashboard to quickly validate that metrics are correct helps to develop intuition about the information provided by the AI assistants. ## Common workflow patterns ### Data profiling Quickly understand a new dataset: ```text "Profile the `transactions` table - show me: - Row count and date range - Distribution of key categorical columns - Summary statistics for numeric columns - Any null values or data quality issues" ``` :::tip[DuckDB functions for EDA] DuckDB has a few SQL functions that are great for hydrating context: - `DESCRIBE` which retrieves the metadata for a specific table - `SUMMARIZE` which gets summary stats for a table (can be large) - The `USING SAMPLE 10` clause (at the end of the query) which samples the data (can be large) - using it with a where clause to narrow down is very helpful for performance ::: ### Generating charts Some AI clients can generate visualizations directly from your query results. ChatGPT on the web and Claude Desktop both support creating charts as "artifacts" alongside your conversation. Visualizations help you spot trends and outliers faster than scanning tables, validate that query results make sense at a glance, and share insights with stakeholders who prefer visual formats. **Example prompts:** - *"Chart monthly revenue for 2024 as a line graph"* - *"Create a bar chart showing the top 10 customers by order count"* - *"Visualize the distribution of order values as a histogram"* - *"Show me a time series of daily active users with a 7-day moving average"* Once you have a chart, you can iterate on it just like query results: *"Add a trend line"*, *"Change to a stacked bar chart"*, or *"Break this down by region"*. :::note When using the MCP with more IDE-like interfaces, the MCP plays very nicely with libraries like `matplotlib` for building more traditional charts. ::: ### Querying private S3 buckets You can use the MCP to analyze files in private S3 buckets (Parquet, CSV, JSON) by storing your AWS credentials as a [secret in MotherDuck](/sql-reference/motherduck-sql-reference/create-secret/). ### MotherDuck UI You can create secrets directly in the [MotherDuck UI](https://app.motherduck.com) under **Settings → Secrets**. ![The MotherDuck secrets UI](../img/md_create_secret_ui.png) ### AWS SSO with credential chain This is recommended for desktop AI clients. If you use AWS SSO, you can refresh your credentials and store them in MotherDuck: 1. Create an AWS credential profile ```bash aws configure sso ``` 2. Authenticate with AWS SSO: ```bash aws sso login --profile ``` 3. Open a DuckDB client (for example, the CLI) and create a secret using the credential chain: ```sql ATTACH 'md:'; CREATE OR REPLACE SECRET IN MOTHERDUCK ( TYPE s3, PROVIDER credential_chain, CHAIN 'sso', PROFILE '' ); ``` This stores your AWS credentials in MotherDuck, making them available to the remote MCP server. :::note Run `aws sso login --profile ` before creating the secret to refresh your SSO token. Starting with DuckDB v1.4.0, credentials are validated at creation time. If your local credentials are not resolvable, the command will fail: use the correct `CHAIN` and `PROFILE` for your credential type, or add `VALIDATION 'none'` as a last resort to skip local validation. ::: :::note[Credential expiration] If you use temporary credentials (SSO, IAM roles), you'll need to refresh the secret when they expire by running the `CREATE OR REPLACE SECRET` command again. ::: Once your credentials are set up, you can ask your AI assistant to query any S3 bucket you have access to: ```text "Give me some analytics about s3://my-bucket/sales-data.parquet" ``` ![Exploring S3 data with MCP](../img/mcp_explore_s3.png) ### Use DuckDB and MotherDuck from Claude's remote sandbox Claude on the web can run Python and shell commands in a remote code execution sandbox. This is separate from Claude Code or Claude Desktop running on your machine. Use the remote MCP server for schema discovery, query generation, and server-side analysis. Use DuckDB directly when the task needs a running client process for local code execution or file handling. In Claude web, that DuckDB client can run inside Claude's remote sandbox. For example, if a teammate uploads a CSV or Parquet file to Claude and wants to enrich it with data from MotherDuck, Claude can use DuckDB in the sandbox to read the uploaded file, query MotherDuck, join the data, and write a downloadable result file. That avoids sending a large file or result set through MCP tool responses, which are designed for conversation context rather than bulk file transfer. To let Claude install DuckDB, load the MotherDuck extension, and query MotherDuck from the sandbox, organization owners can configure **Settings** → **Capabilities** → **Code execution and file creation** → **Allow network egress**. The **All domains** option gives the sandbox enough network access for this workflow, subject to your organization's policy. See Anthropic's [code execution and file creation documentation](https://support.claude.com/en/articles/12111783-create-and-edit-files-wit) for the security tradeoffs. The same requirement applies in other sandboxed agent environments: the DuckDB Python package or CLI runs as a client process, and the sandbox must allow that process to reach the package host, DuckDB extension download host, and MotherDuck service. Add your MotherDuck token as an environment variable in `.env` format: ```text MOTHERDUCK_TOKEN= ``` Use a scoped token that matches the task. A [read scaling token](/key-tasks/authenticating-and-connecting-to-motherduck/read-scaling/) is enough when Claude only needs to read from MotherDuck and write output files in its sandbox. Only add tokens to cloud environments whose users should have that access. ![Updating a Claude cloud environment with full network access and MotherDuck token environment variables](../img/claude-cloud-environment-env-vars.png) Changes to a cloud environment apply to new sessions. Before you start the workflow, select the cloud environment that has network access and `MOTHERDUCK_TOKEN` configured. ![Selecting a Claude cloud environment before starting a session](../img/claude-select-cloud-environment.png) Start with a small connection test: ```text Install the duckdb Python package and use it to run SELECT 42 from my MotherDuck account. Use the MotherDuck token I provide, and don't print the token. ``` Example CSV or Parquet workflow prompt: ```text Use Python with DuckDB for this file workflow. Connect to MotherDuck with the token I provide, read the uploaded CSV or Parquet file, join it to the relevant MotherDuck table, and write the enriched result as a downloadable CSV or Parquet file. ``` If direct DuckDB access isn't available, keep the heavy work in MotherDuck: ```text Use the MotherDuck MCP to create a table with the result instead of returning all rows in the chat. Tell me the table name and the SQL you used so I can export it from MotherDuck. ``` This fallback works when Claude's sandbox can't reach the MotherDuck extension download host or can't make outbound requests to MotherDuck. It also keeps large intermediate results out of the model's context window. ### Ad-hoc investigation The MCP is especially useful for exploratory debugging when you're not sure what you're looking for. Rather than writing queries upfront, you can describe the problem and let the AI help you dig in. ```text "I noticed a spike in errors on Dec 10th. Help me investigate: - What types of errors increased? - Were specific users or endpoints affected? - What changed compared to the previous week?" ``` One pattern we use at MotherDuck is loading logs or event data into a database and using the MCP to interrogate it conversationally. Instead of manually crafting regex patterns or grep commands, you can ask questions like *"What are the most common error messages in the last hour?"* or *"Show me all requests from user X that resulted in a 500 error"*. This turns log analysis from a tedious grep session into an interactive investigation where each answer informs the next question. ## Working with query results ### Refining results Results rarely come out perfect on the first try. The conversational nature of MCP means you can refine incrementally rather than rewriting queries from scratch. If you're seeing test data mixed in, just say *"Add a filter to exclude test accounts"*. If the granularity is wrong, ask to *"Change the grouping from daily to weekly"*. Small adjustments like changing sort order or adding a column are easy follow-ups. ### Understanding queries When the AI generates complex SQL, don't hesitate to ask for an explanation. This is useful both for validating the approach and for learning. Ask *"Explain what this query is doing step by step"* to understand the logic, or *"Are there any edge cases this query might miss?"* to sanity-check the results before relying on them. ### Exporting for further use Once you have the results you need, ask for output in the format that fits your workflow. Small result sets can be returned as a markdown table, spreadsheet-friendly CSV, or written summary. For larger exports, don't ask the MCP to stream all rows into the chat. Ask the AI to keep the result in MotherDuck with `CREATE TABLE AS SELECT ...` and give you the table name, or run a DuckDB client somewhere that can access both MotherDuck and the file destination. That client can be on your machine, in Claude Code, or in Claude's remote sandbox when its network rules allow the required hosts. Asking for the final SQL is also useful when you want to hand the analysis to another teammate or tool. ## Hand file-shaped work to the CLI Every MCP tool result is a message: it's serialized into the model's context, takes up the context window, and gets resent on every turn that follows. That's the right trade for exploration, where you want the AI to reason over what it found. It's the wrong trade for moving a file around. If your agent has a terminal, the [MotherDuck CLI](/getting-started/interfaces/motherduck-cli/) writes to files and stdout instead, so the agent reads back only what it needs. It's the more efficient path for: - **Reading a Dive or a Flight**: `motherduck dive pull` and `motherduck flight pull` write the source to disk, where the agent reads the part it's changing instead of pulling the whole component into the conversation. - **Saving an edit**: the agent patches the local file and runs `motherduck dive push` or `motherduck flight push`, which reads it from disk. - **Listing Dives or Flights**: `motherduck dive list --output json` and `motherduck flight list --output json` feed `jq`, so one field reaches the model instead of every field of every result. - **Exporting a large result**: `motherduck query "..." --output csv > result.csv` keeps the rows out of the context window entirely. - **Chaining steps**: a shell script hands each command's output to the next, so intermediate results never pass through the model. The two combine well. Explore and shape the analysis through MCP, then let the CLI build, publish, and script it. See [choosing between the CLI and MCP](/getting-started/interfaces/motherduck-cli/agents/#choosing-between-the-cli-and-mcp). ## Tips for better results ### Be explicit about assumptions Your data likely has business rules that aren't obvious from the schema alone. If a "completed" order means status is either 'shipped' or 'delivered', say so. If revenue calculations should exclude refunds, mention it upfront. The AI can't infer these domain-specific rules, so stating them early prevents incorrect results and saves iteration time. ### Reference specific tables and columns When you already know your schema, being specific helps the AI get it right the first time. Instead of asking about "the timestamp", say *"Use the `user_events.event_timestamp` column"*. If you know how tables relate, specify the join: *"Join `orders` to `customers` on `customer_id`"*. This is especially helpful in larger schemas where column names might be ambiguous. ### Ask for validation When accuracy matters, ask the AI to sanity-check its own work. Questions like *"Does this total match what you'd expect based on the row counts?"* or *"Can you verify this join doesn't create duplicates?"* can catch subtle bugs before you rely on the results. The AI can run quick validation queries to confirm the logic is sound. ## Troubleshooting :::tip[Beyond querying] The remote MCP server includes tools beyond just running queries. Most are metadata lookups or search functions for finding tables and columns, but the [ask docs question](/sql-reference/mcp/core/ask-docs-question) tool is particularly useful when you're stuck on tricky syntax or DuckDB-specific features. If the AI is struggling with a query pattern, try asking it to look up the relevant documentation first. ::: | Issue | Solution | |-------|----------| | AI queries wrong table | Ask: *"What tables are available?"* then specify the correct one | | Results don't look right | Ask: *"Show me sample data from the source table"* to verify the data | | Query is slow | Ask: *"Can you optimize this query?"*, add filters to reduce data scanned, or [increase your Duckling size](/about-motherduck/billing/duckling-sizes/) | | AI doesn't understand the question | Rephrase with more specific column names and business context | | Can't type fast enough | Use voice-to-text to interact with your AI assistant | ## Related resources - [Connect to MCP Server](/key-tasks/ai-and-motherduck/mcp-setup/) - Setup instructions for all supported AI clients - [White-label the MotherDuck MCP server](/key-tasks/ai-and-motherduck/mcp-workflows/white-label-mcp-server/) - Put the remote MCP server behind your own MCP server for your customers - [Work with agents through the CLI](/getting-started/interfaces/motherduck-cli/agents/) - When to reach for the MotherDuck CLI instead of MCP - [AI Features in the UI](/key-tasks/ai-and-motherduck/ai-features-in-ui/) - Built-in AI features for the MotherDuck interface - [Building Analytics Agents](/key-tasks/ai-and-motherduck/building-analytics-agents/) - Build custom AI agents with MotherDuck --- Source: https://motherduck.com/docs/key-tasks/ai-and-motherduck/mcp-workflows/white-label-mcp-server # White-label the MotherDuck MCP server > Wrap the remote MCP server behind your own MCP server so your customers get SQL, Guides, Dives, and Flights under your brand. Run your own MCP server to give customers an analytics connection under your brand. Your server authenticates users, maps each tenant to a MotherDuck [service account](/key-tasks/service-accounts-guide/), and forwards selected tools to the [managed MCP server](/sql-reference/mcp/). Use this for a multi-tenant product, reseller platform, or client analytics service. Customers sign in to your product and work with their permitted data through your tool names and guidance. If you only need to connect your own team, follow [Connect to the MotherDuck MCP server](/key-tasks/ai-and-motherduck/mcp-setup/) instead. ## Prerequisites Before starting, ensure you have: - A MotherDuck organization on a plan that includes [service accounts](/key-tasks/service-accounts-guide/create-and-configure-service-accounts/). Embedding Dives in your own app (Path B below) requires the **Business plan**. - If you automate provisioning, a backend credential with permission to create service accounts and tokens through the [REST API](/sql-reference/rest-api/motherduck-rest-api/). Keep this separate from the credentials used to query tenant data. - An authentication method supported by your target MCP clients. For customers signing in to your product, use an identity provider with an MCP-compatible OAuth flow. - A backend that can host a remote MCP server. The upstream connection example uses [FastMCP](https://gofastmcp.com/) for Python. Other MCP SDKs have their own session and result-handling APIs. - A backend secret store for per-tenant MotherDuck tokens and an authorized mapping from application users to tenants. ## How the proxy fits together ```mermaid flowchart LR Client(["AI client"]):::green -->|Product
token| Wrapper["Your MCP
server"]:::watermelon Wrapper -->|Tenant
token| Upstream["MotherDuck
MCP"] ``` Your server verifies tenant membership and selects that tenant's service-account token. MotherDuck enforces the service account's data permissions. Three properties make this work: 1. **The remote MCP server accepts a Bearer token instead of OAuth.** Any client, including your server, can connect with `Authorization: Bearer ` over the streamable-HTTP transport. See the Bearer token note in [Connect to the MotherDuck MCP Server](/key-tasks/ai-and-motherduck/mcp-setup/). 2. **Service accounts give you per-tenant isolation.** Give each tenant a service account with access only to that tenant's databases or shares. Selecting a database argument or filtering a catalog listing does not restrict cross-database SQL. See [Create and configure service accounts](/key-tasks/service-accounts-guide/create-and-configure-service-accounts/). 3. **Your proxy owns the tool surface.** You decide which MotherDuck tools are forwarded, what they're called, how they're described, and which parameters the agent may set. ### Compare the approaches | Approach | Pros | Cons | |---|---|---| | **Proxy the remote server** (this page) | Full remote tool set including Dives, Flights, and Guides. MotherDuck runs and upgrades the server. You maintain auth plus a thin tool layer. Per-tenant token isolation | One extra network hop. Integration tests must catch upstream schema changes. MCP App forwarding for Dives takes more work than plain tool forwarding | | **Fork the [local MCP server](https://github.com/motherduckdb/mcp-server-motherduck)** | Fully customizable, self-hosted, no dependency on remote tool schemas | A separate implementation with its own tool set. Verify the capabilities you need. You own upgrades and hosting | | **Point users at `api.motherduck.com/mcp` with a shared API key** (the Copilot Studio tab in the [setup guide](/key-tasks/ai-and-motherduck/mcp-setup/)) | No code | Not white-labeled: MotherDuck-named server, tools, and instructions. One shared identity, so no per-user attribution | ## Step 1: Map users to service accounts Your proxy needs a deterministic path from *authenticated user* to *MotherDuck token*. 1. **Authenticate the user** with your identity provider. Follow the [MCP authorization specification](https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization), including resource discovery, audience validation, and a client registration method your target clients support. Dynamic client registration is one option, not a universal requirement. A website sign-in flow alone does not implement MCP authentication. 2. **Resolve the tenant** from verified identity and membership. If a user can select several tenants, authorize the selection. A tenant ID supplied in a URL or tool argument is not proof of membership. 3. **Look up the tenant's service-account token** in your secret store. Never return this token to the client. Create tokens with [Create an access token for a user](/sql-reference/rest-api/users-create-token/) and rotate them with [Invalidate a user access token](/sql-reference/rest-api/users-delete-token/). 4. **Open an upstream MCP client session** to `https://api.motherduck.com/mcp` with that token. You can reuse it within the authorized session or open and close it for each call. If you pool sessions, separate them by tenant and credential, manage concurrent access, and invalidate them when tokens rotate or access is revoked. For read-only database access, issue each service account a [read scaling token](/key-tasks/authenticating-and-connecting-to-motherduck/read-scaling/) and don't forward `query_rw`. See [Restricting to read-only access](/key-tasks/ai-and-motherduck/securing-read-only-access/). Read scaling is eventually consistent, so consider data freshness. Review Guide, Dive, and Flight mutations separately from SQL access. Validate your product's credential at the proxy, then select the MotherDuck token from your backend mapping. Don't forward the product's OAuth token upstream or expose the MotherDuck token to customers. Controlled backend integrations can use your own API keys if the client supports them, but the proxy must still authenticate and authorize callers. All MotherDuck-side activity (query history, Dive ownership, billing) is attributed to the tenant's service account, not to the individual end user. If you need per-user attribution inside a tenant, log the user ID on your side alongside each forwarded call (see [Step 5](#step-5-log-the-question-not-only-the-sql)). ## Step 2: Choose which tools to forward The remote server exposes tools in four families. Forward only what your users need. Enforce that allowed set both in tool listings and when dispatching calls. Reject direct calls to unexposed tools and avoid a generic pass-through tool that accepts an arbitrary upstream tool name. | Family | Tool | Forward? | Notes | |---|---|---|---| | Core | [`query`](/sql-reference/mcp/core/query) | **Yes** | The workhorse. Inherits the 2,048-row / 50,000-character result cap and the 55-second timeout. | | Core | [`list_databases`](/sql-reference/mcp/core/list-databases), [`list_tables`](/sql-reference/mcp/core/list-tables), [`list_columns`](/sql-reference/mcp/core/list-columns), [`search_catalog`](/sql-reference/mcp/core/search-catalog) | **Yes** | Discover the data model before writing SQL. | | Core | [`list_views`](/sql-reference/mcp/core/list-views), [`list_macros`](/sql-reference/mcp/core/list-macros) | Optional | Useful if tenant databases ship views or macros as a semantic layer. | | Core | [`list_shares`](/sql-reference/mcp/core/list-shares) | Usually no | Exposes MotherDuck share URLs (`md:_share/...`). Attach shares to the service account ahead of time instead. | | Core | [`query_rw`](/sql-reference/mcp/core/query-rw) | Usually no | Only if your product lets end users change data. | | Core | [`ask_docs_question`](/sql-reference/mcp/core/ask-docs-question) | **No** | Answers come from MotherDuck and DuckDB documentation and name MotherDuck throughout. | | Guides | [`get_query_guide`](/sql-reference/mcp/guides/get-query-guide), [`list_guides`](/sql-reference/mcp/guides/list-guides), [`get_guide`](/sql-reference/mcp/guides/get-guide) | **Yes** | The read path for your tenants' semantic layer. See [Step 3](#step-3-forward-tenant-guides-and-replace-the-built-in-guides). | | Guides | [`create_guide`](/sql-reference/mcp/guides/create-guide), [`update_guide`](/sql-reference/mcp/guides/update-guide), [`edit_guide_content`](/sql-reference/mcp/guides/edit-guide-content), [`update_guide_metadata`](/sql-reference/mcp/guides/update-guide-metadata), [`set_guide_access`](/sql-reference/mcp/guides/set-guide-access) | Optional | Authorize edits and enforce tenant-private visibility. Keep organization-wide access changes out of tenant-facing tools. | | Dives | [`get_dive_guide`](/sql-reference/mcp/dives/get-dive-guide), [`save_dive`](/sql-reference/mcp/dives/save-dive), [`update_dive`](/sql-reference/mcp/dives/update-dive), [`list_dives`](/sql-reference/mcp/dives/list-dives), [`read_dive`](/sql-reference/mcp/dives/read-dive), [`view_dive`](/sql-reference/mcp/dives/view-dive), [`share_dive_data`](/sql-reference/mcp/dives/share-dive-data), [`delete_dive`](/sql-reference/mcp/dives/delete-dive) | Optional | See [Step 4](#step-4-choose-how-dives-render). Do not expose `share_dive_data` to tenants that share one MotherDuck organization. It shares their databases with that organization. | | Flights | [`get_flight_guide`](/sql-reference/mcp/flights/get-flight-guide), [`create_flight`](/sql-reference/mcp/flights/create-flight), [`update_flight`](/sql-reference/mcp/flights/update-flight), [`run_flight`](/sql-reference/mcp/flights/run-flight), [`list_flights`](/sql-reference/mcp/flights/list-flights), [`get_flight`](/sql-reference/mcp/flights/get-flight), and the remaining `*_flight*` tools | Usually no | [Flights](/concepts/flights) are scheduled Python jobs on MotherDuck compute: an operator feature, rarely one to hand to a tenant's end users. If you expose them, authorize their runtime credentials, schedules, and data access separately from the interactive query connection. | Rewrite two things in every forwarded tool: - **Name and description.** Call the tool whatever fits your product (`query` → `run_sql`, `save_dive` → `save_dashboard`). Use your product vocabulary in descriptions and update references to renamed tools. Preserve required technical identifiers such as `@motherduck/react-sql-query` and `REQUIRED_DATABASES`. - **Server instructions.** The remote server ships MotherDuck's [query guidelines](https://app.motherduck.com/assets/docs/mcp_server_instructions.md) as MCP `instructions`. Your proxy's `instructions` field replaces that. Copy the DuckDB SQL guidance you want and drop the branding. ## Step 3: Forward tenant Guides and replace the built-in guides [Guides](/key-tasks/guides/) are markdown documents the agent reads before writing SQL. Separate tenant-authored business context from the built-in technical instructions. ### Forward tenant Guides Metric definitions, join conventions, and business glossaries are the ontology layer your tenants will want. Expose the discovery flow `get_query_guide` → `list_guides` → `get_guide`. Review built-in navigation text and tool references for your product vocabulary, while preserving authorized tenant content. Design around two visibility rules: - `access: "user"` Guides are private to the service account that created them. This is the natural scope for **per-tenant** Guides. Create them while connected as that tenant's service account. - `access: "organization"` Guides are visible to **every** service account in your MotherDuck organization, which means every tenant. Use this level only for content that's safe to show all tenants, for example your platform's shared data model. Setting it is admin-permission gated. A private Guide belongs to the MotherDuck identity, not to one person using your product. Everyone using a tenant's service account shares that identity. Keep individual preferences in your application, keyed by tenant and authenticated user, and combine only that person's preferences with tenant guidance. A topic named after a user does not provide an access boundary. Use tenant Guides for fiscal calendars, revenue definitions, valid join paths, and dashboard styles. Attach [catalog references](/key-tasks/guides/#attach-references-to-a-guide) so table exploration and catalog search can surface relevant context. For example, create this Guide through the tenant service account: ```json { "title": "Dashboard style", "description": "Colors and number formats for this customer's dashboards", "topic": "dives", "access": "user", "content": "Use navy and teal. Label currency values in EUR. Use compact numbers in KPI cards and full values in tooltips." } ``` ### Replace the built-in guides [`get_dive_guide`](/sql-reference/mcp/dives/get-dive-guide) and [`get_flight_guide`](/sql-reference/mcp/flights/get-flight-guide) return the system prompts MotherDuck ships for building Dives and Flights. They explain the technical contract, but they're written for MotherDuck users and contain product-specific instructions. Serve an adapted version for the capabilities your product exposes. Your replacement `get_dive_guide` should: 1. **Keep every technical requirement** from the MotherDuck Dive guide: the [`useSQLQuery`](/sql-reference/motherduck-sql-reference/dives/use-sql-query) hook, `REQUIRED_DATABASES`, data-type conversion rules, available libraries, `useDiveState`, and `exportAs`. Drift can cause validation failures or errors when a Dive renders. 2. **Replace the design-system section** with your brand: colors, typography, chart defaults, number formatting, and tone. 3. **Adapt explanatory wording** to your product. Keep library names, API identifiers, and data-access semantics intact. 4. **Branch on the `client` parameter** (`claude`, `chatgpt`, `claude_code`, and so on) the same way the upstream tool does. 5. **Include the tenant's `dives`-topic Guide overview** from `list_guides(topic="dives")`, and expose `get_guide` to load the actual content. Listings contain summaries, not the full style instructions. Browse nested topics when relevant. Keep a diff against the upstream guide in your repo. Review your adapted instructions when upstream tools or component APIs change. Adding a style Guide extends the built-in guidance, but does not remove its branding or change viewer controls. ## Step 4: Choose how Dives render [Dives](/key-tasks/dives/) are React data apps that query live data. Through MCP, the agent creates them with `save_dive` and `update_dive` and renders them inline with `view_dive`, which returns the Dive source, a `dive_app_url`, and viewer metadata. Hosts that support the **Dive Viewer MCP App** (Claude and ChatGPT) compile and render the Dive client-side. This is the part of white-labeling with the most surface area. There are two paths to consider: ### Path A: Forward the Dive tools and the dive viewer Your users stay in the chat client and see a live, interactive dashboard as a chat message, with full-screen and export. | Concern | Where it shows up | What to do | |---|---|---| | **MotherDuck links in tool results** | Save, update, and view responses can contain `dive_app_url`, legacy `dive_url` fields, and product-specific `next_steps` | Build a response for your product that preserves warnings and errors. Apply URL and instruction rewrites consistently to model-visible text and structured content, not only one copy of the payload. | | **"Open in MotherDuck" control in the Dive Viewer frame** | Rendered by the MCP App chrome, not by the Dive code | Removing `dive_app_url` does not remove this control. The viewer builds its navigation URL independently. Use Path B for an application page you control, or validate a separately customized in-chat viewer. | | **MCP App resource** | The viewer is exposed as an MCP resource, not only a tool response | Forward resource reads, the app MIME type, UI and content security policy metadata, and required viewer tool calls. Preserve `_meta` in its intended viewer channel. It can contain credentials and must not be copied into model-visible text or logs. Test the complete flow in each supported client. | | **Visibility across tenants** | Dives are visible to all users in the MotherDuck organization by default. Other tenants' service accounts can see that a Dive exists and read its **source**, but can't query its **data** because they lack access to the underlying databases. | `list_dives` lists owned Dives, but that is not a permission boundary for reads by ID. Authorize every read, update, view, and embed operation. `save_dive` has no visibility parameter. | Source code can contain sensitive labels, SQL, or hardcoded values even if another tenant cannot query the database. Test source and metadata access using a second service account, including SQL access to saved objects if you expose arbitrary queries. Wrapper filtering does not establish private permissions on the underlying Dive. Check the MotherDuck organization boundary before placing confidential tenant Dives in it. This path reuses the live viewer and keeps users in chat. It requires MCP App support in the client and integration work for resource and viewer tool calls. The built-in viewer retains MotherDuck controls. ### Path B: Render Dives in your own app through embed sessions For this path, return an application link instead of forwarding `view_dive`: 1. Calls `save_dive` upstream as the tenant's service account and stores the returned Dive ID with the authorized tenant. 2. Returns your stable application URL, such as `https://app.example.com/dashboards/`, instead of a MotherDuck link. 3. When someone opens that URL, your application authenticates them and authorizes access to the tenant and Dive. 4. Your backend calls [Create a Dive embed session](/sql-reference/rest-api/dashboards-create-embed-session/) with a credential permitted to create embed sessions. Select the target service account and resources from your authorized tenant mapping. 5. Your frontend receives only the expiring embed session and loads the sandboxed iframe. The long-lived backend token stays on your server. See [Embedding Dives in your web application](/key-tasks/dives/embedding-dives/). Create the embed session when the authorized viewer opens the page, not when generating the permanent link. This avoids persisting an expiring credential in the URL you return to the chat. A signed-in web session still needs a tenant and Dive authorization check. Use `required_resources` to map a reusable dashboard to the tenant's permitted databases, and `initial_state` to preconfigure filters or a date range. Derive resource mappings on your backend. UI filters are presentation settings, not access controls. Handle navigation, exports, and state-change messages as described in the embedding guide. This path gives you control over the surrounding page and works in clients without MCP App support. You host the page, and the user leaves chat to interact with it. The embedding service account must have access to the Dive's data. Follow the embedding guide's dedicated-account guidance to avoid database alias collisions. To combine them, forward `view_dive` for clients that support the Dive Viewer and return your embed URL as the persistent link in `save_dive` and `update_dive`. The user gets an inline preview in chat and a branded permalink. ## Step 5: Log the question, not only the SQL MotherDuck's query history shows you the SQL a tenant ran, not the question the user asked. Because you own the tool schemas, you can capture intent at the source by adding a `question` parameter to your query tool: ```json { "type": "object", "properties": { "sql": { "type": "string", "description": "Read-only DuckDB SQL to answer the data question." }, "question": { "type": "string", "minLength": 1, "description": "The user's data question that this query helps answer." } }, "required": ["sql", "question"], "additionalProperties": false } ``` Record `question` on your backend and forward only `database` and `sql` to MotherDuck's `query` tool. This extra parameter is your proxy's feature, not an upstream tool argument. Record the authenticated application user and tenant, request ID, timestamp, question, SQL, duration, and outcome. Capture unsuccessful attempts as well as successes, and distinguish transport failures from tool errors. Exclude credentials and result rows by default. Explain the collection to users and define retention and access rules for question and SQL text. The model supplies the `question` field and can paraphrase the original request. One question can produce several queries, and some questions produce no tool call at all. Treat this as question context for query attempts, not a complete transcript or a count of distinct user questions. If you own the chat interface and need the exact submitted question, capture it there. ### Turn recurring questions into better guides Use the captured context to find repeated questions, unclear definitions, and queries that often fail. For example, repeated requests for "net sales" may reveal that refunds and discounts need an explicit rule. Review the definition with a domain owner, validate the SQL, and save a tenant-private Guide with catalog references and a `change_comment` explaining the decision. Check whether subsequent questions produce the intended answer. Keep individual preferences scoped to the person, and review proposed learning before it becomes an authoritative business definition. This feedback loop is application behavior you build. Guides provide storage and discovery, but don't automatically learn from every MCP conversation. ## Connect a Python tool handler to MotherDuck The following helper uses FastMCP 4.0.3 to open an upstream session and return the raw MCP result. Install `fastmcp==4.0.3` in your backend's Python environment. Call it from your authenticated query handler with the token and database from your authorized tenant mapping. ```python from fastmcp import Client from fastmcp.client.transports import StreamableHttpTransport from mcp.types import CallToolResult async def query_tenant(*, token: str, database: str, sql: str) -> CallToolResult: transport = StreamableHttpTransport( url="https://api.motherduck.com/mcp", headers={"Authorization": f"Bearer {token}"}, ) async with Client(transport) as client: return await client.call_tool_mcp( "query", {"database": database, "sql": sql}, ) ``` This is the upstream connection helper, not a complete authenticated server. Your application supplies caller authentication, tenant resolution, tool registration, question capture, and response handling. Its `token` and `database` parameters are backend inputs, not model-visible tool arguments. It opens and closes a session per call, so no mutable client is shared across tenants. Serialize the raw result with `result.model_dump(by_alias=True, exclude_none=True)` to obtain the MCP field names. Preserve `content`, `structuredContent`, `isError`, and `_meta` when adapting it to your server's tool-result type. Transport failures still raise exceptions. The higher-level FastMCP `call_tool()` method returns an SDK result object, not a dictionary, and raises on tool errors by default. See [FastMCP tool calls and raw protocol access](https://gofastmcp.com/clients/tools). When a save handler rewrites a dashboard response, read the structured payload or parse its JSON text, check for failure before accessing the Dive ID, and produce consistent text and structured output. Preserve validation errors, warnings, and truncation notices. Replace sharing suggestions with your product's authorized next steps without granting broader access or hiding data-access problems. Add integration checks for the upstream schemas and complete result envelopes. For Dives, test viewer resources and follow-up calls as well as the initial tool response. ## Verify the setup 1. Connect your MCP server in Claude or ChatGPT as a test user for one tenant. 2. Ask "What tables do I have?" You should see only that tenant's tables, returned under your tool names. 3. Ask a question that needs a metric definition from a tenant Guide. The agent should call your forwarded `get_query_guide` and `get_guide` before writing SQL. 4. Ask for a dashboard. The agent should call your replacement guide under the name your proxy exposes, then save. The link should open your authorized application page (Path B), or the live viewer should render with the controls you have verified (Path A). 5. Connect as a test user for a second tenant and repeat step 2. Check database access, Guide visibility, and Dive metadata and source separately. A filtered listing alone is not proof of isolation. 6. Try calling an unexposed tool directly and reading another tenant's Dive by ID. Check SQL access to saved-object metadata too. These tests must meet your isolation policy before rollout. 7. Test two users within a tenant. Verify that personal preferences reach only the intended person. 8. Revoke access or rotate a token and verify that cached connections cannot retain unintended access. 9. Trigger a failed query and a truncated result. Confirm that the client receives the correct error or warning and that telemetry records the outcome. ## Troubleshooting | Symptom | Likely cause | Fix | |---|---|---| | Agent output still mentions MotherDuck or Dives | A forwarded tool description or `next_steps` string still carries the upstream text, or `ask_docs_question` is exposed | Search your tool schemas and result rewrites for the terms. Remove `ask_docs_question` | | `save_dive` returns `validationErrors` on code that follows your guide | Your replacement guide drifted from the upstream technical requirements | Diff against the current `get_dive_guide` output and update your baseline | | A tenant can read another tenant's Dive metadata or source | Organization-wide Dive visibility or a missing authorization check on reads by ID | Check the underlying object permission boundary and every proxy read path. A service account per tenant and an owned-only listing do not make saved Dives private. | | Queries time out at 55 seconds | Inherited `query` timeout | Add guidance to your `instructions` to aggregate rather than scan. Size the tenant's Duckling | | Query results truncated | Inherited 2,048-row / 50,000-character cap | Same as above. Return aggregates, not row dumps | ## Inherited limits - Result cap of **2,048 rows / 50,000 characters** and query timeout of **55 seconds**, per the [`query` reference](/sql-reference/mcp/core/query). - Requests route to the MotherDuck MCP region nearest **your proxy**, and results transit through it. Run the proxy in your organization's region if you have data residency requirements. See [Regional availability](/sql-reference/mcp/#regional-availability). - The proxy does not change feature availability or billing. Embedding Dives requires the Business plan. Check [MotherDuck pricing](https://motherduck.com/product/pricing/) and your organization's enabled features before exposing tools. Plan inclusion does not mean the queries and stored data have no usage cost. ## Related tasks - [Connect to the MotherDuck MCP Server](/key-tasks/ai-and-motherduck/mcp-setup/) - [Create and configure service accounts](/key-tasks/service-accounts-guide/create-and-configure-service-accounts/) - [Embedding Dives in your web application](/key-tasks/dives/embedding-dives/) - [Restricting to read-only access](/key-tasks/ai-and-motherduck/securing-read-only-access/) - [Building analytics agents](/key-tasks/ai-and-motherduck/building-analytics-agents) - [MotherDuck MCP server reference](/sql-reference/mcp/) --- ## Docs feedback MotherDuck accepts optional user-submitted feedback about this page at `GET https://motherduck.com/docs/api/feedback/agent`. For agents and automated tools, feedback submission should be user-confirmed before sending. URL-encode query parameter values and send a GET request: ```text GET https://motherduck.com/docs/api/feedback/agent?page_path=%2Fkey-tasks%2Fai-and-motherduck%2Fmcp-workflows%2F&page_title=MotherDuck%20Documentation%20-%20Using%20the%20MotherDuck%20MCP%20Server&text= ``` Optionally append `&source=` such as `claude.ai` or `chatgpt`. `page_path` and `text` are required; `page_title` and `source` are optional. Responses: `200 {"feedback_id": ""}`, `400` for malformed query parameters, and `429` when rate-limited.