---
sidebar_position: 2
title: Restricting to read-only access
description: Restrict the remote MCP server to read-only queries using client-side blocking, read scaling tokens, or proxy filtering
---

import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

# Restricting to read-only access

The remote MCP server exposes both the read-only `query` tool and the read-write `query_rw` tool. If you want to ensure your AI assistant can only read data, there are three approaches depending on your setup.

| Approach | Enforcement | Setup | Works with OAuth connectors |
|----------|------------|-------|-----------------------------|
| [Block the tool at the client](#block-the-query_rw-tool-at-the-client) | Client-side | Low (UI toggle) | Yes |
| [Use a read scaling token](#use-a-read-scaling-token) | Server-side | Medium (manual config) | No (replaces OAuth) |
| [Proxy filtering](#proxy-filtering) | Application-side | Varies | N/A (custom backend) |

## Block the `query_rw` tool at the client

The simplest approach: keep using the OAuth connector, but configure your MCP client to never call the `query_rw` tool. The server still exposes the tool, but the client will never invoke it.

Most clients support this at the **individual user** level. ChatGPT also lets **organization admins** enforce tool restrictions across all workspace members.

<Tabs groupId="mcp-client">
  <TabItem value="claude" label="Claude" default>

Each user can block tools individually. Go to **Settings → Connectors → MotherDuck**, expand **Write/delete tools**, and select the blocked icon next to `query_rw`:

![Blocking the query_rw tool in Claude's connector settings](./img/query-rw-blocked.png)

:::note
Claude does not support org-level per-tool blocking. Team/Enterprise admins can remove a connector entirely from **Organization settings → Connectors**, but cannot selectively disable individual tools like `query_rw` for all members.
:::

> [Claude connector permissions documentation](https://support.claude.com/en/articles/11175166-get-started-with-custom-connectors-using-remote-mcp)

  </TabItem>
  <TabItem value="chatgpt" label="ChatGPT">

**Enterprise/Edu admins:** Admins can [enable or disable specific app actions after publishing](https://help.openai.com/en/articles/12584461-developer-mode-and-full-mcp-connectors-in-chatgpt-beta). Go to **Workspace Settings → Apps**, click the `...` menu next to MotherDuck, select **Action control**, and deselect `query_rw`. New tools added by the MCP server are disabled by default — admins must explicitly enable them.

**Business plans:** Per-tool Action control is not available for custom MCP apps after publishing. To change which tools are exposed, remove and recreate the app ([developer mode documentation](https://help.openai.com/en/articles/12584461-developer-mode-and-full-mcp-connectors-in-chatgpt-beta)).

  </TabItem>
  <TabItem value="cursor" label="Cursor">

Open **Cursor Settings** → **Tools & MCP**, expand the MotherDuck server entry, and toggle off `query_rw`.

:::note
Tool toggles are stored locally in Cursor's database, not in the `mcp.json` config file. They cannot be shared across a team through config files.
:::

  </TabItem>
  <TabItem value="claude-code" label="Claude Code">

Add a deny rule to your `.claude/settings.json` (project-level) or `~/.claude/settings.json` (user-level):

```json
{
  "permissions": {
    "deny": ["mcp__MotherDuck__query_rw"]
  }
}
```

> [Claude Code permissions documentation](https://code.claude.com/docs/en/permissions)

  </TabItem>
</Tabs>

## Use a read scaling token

For server-side enforcement, authenticate with a [read scaling token](/key-tasks/authenticating-and-connecting-to-motherduck/read-scaling/) instead of a regular access token. Read scaling tokens connect to dedicated read replicas that reject all write operations — even if the client calls `query_rw`, writes will fail. This requires manual configuration instead of the one-click OAuth connectors.

:::note
Read scaling connections are [eventually consistent](/key-tasks/authenticating-and-connecting-to-motherduck/read-scaling/#ensuring-data-freshness). Results may lag a few minutes behind the latest database state.
:::

You can create a read scaling token from the [MotherDuck UI](https://app.motherduck.com) under **Settings → Access Tokens** or through the [REST API](/sql-reference/rest-api/users-create-token/).

Read scaling tokens also unlock concurrent MCP sessions: each MCP instance that connects with a read scaling token is assigned to a read replica (duckling) from a pool. Up to the pool size (default 4, max 16), each connection gets its own duckling; once the pool is full, new connections are assigned to existing ducklings in round-robin. This means you can run many MCP sessions in parallel from the same account—for example, multiple AI agents or team members querying simultaneously. See [Read Scaling](/key-tasks/authenticating-and-connecting-to-motherduck/read-scaling/) for details on pool sizing and how replicas are assigned.

<Tabs groupId="mcp-client">
  <TabItem value="claude" label="Claude" default>

Claude's web connector only supports OAuth, so you need to use the desktop config instead. Open **Settings → Developer → Edit Config** and add:

```json
{
  "mcpServers": {
    "MotherDuck": {
      "command": "npx",
      "args": [
        "mcp-remote",
        "https://api.motherduck.com/mcp",
        "--header",
        "Authorization: Bearer ${MOTHERDUCK_TOKEN}"
      ],
      "env": {
        "MOTHERDUCK_TOKEN": "<your_read_scaling_token>"
      }
    }
  }
}
```

This uses [`mcp-remote`](https://www.npmjs.com/package/mcp-remote) to bridge the remote MCP server into Claude Desktop's local stdio transport.

  </TabItem>
  <TabItem value="chatgpt" label="ChatGPT">

ChatGPT connectors can't set static headers. To use a read scaling token, run a proxy that injects the `Authorization` header and connect ChatGPT to that proxy.

Example proxy (Cloudflare Worker):

```js
export default {
  async fetch(request, env) {
    const upstreamUrl = new URL(request.url);
    upstreamUrl.protocol = "https:";
    upstreamUrl.hostname = "api.motherduck.com";
    upstreamUrl.pathname = "/mcp";

    const upstreamRequest = new Request(upstreamUrl, request);
    upstreamRequest.headers.set(
      "Authorization",
      `Bearer ${env.MOTHERDUCK_READ_SCALING_TOKEN}`
    );
    upstreamRequest.headers.delete("cookie");

    return fetch(upstreamRequest);
  },
};
```

1. Deploy the proxy and store the read scaling token as a secret (for example, `MOTHERDUCK_READ_SCALING_TOKEN`).
2. In [ChatGPT Settings → Connectors](https://chatgpt.com/#settings/Connectors), click **Create App**.
3. Enter:
   - **Name:** `MotherDuck (Read Only)`
   - **MCP Server URL:** `<your_proxy_url>`
   - **Authentication:** `No authentication`
4. Open a chat, select the connector, and run a query (for example: `SELECT * FROM information_schema.tables LIMIT 5`).

`query_rw` may still appear, but writes fail because read scaling tokens are read-only.

  </TabItem>
  <TabItem value="cursor" label="Cursor">

Open **Cursor Settings** → **Tools & MCP** → **+ New MCP Server** and add the following configuration:

```json
{
  "MotherDuck": {
    "url": "https://api.motherduck.com/mcp",
    "type": "http",
    "headers": {
      "Authorization": "Bearer <your_read_scaling_token>"
    }
  }
}
```

  </TabItem>
  <TabItem value="claude-code" label="Claude Code">

```bash
claude mcp add --transport http \
  --header "Authorization: Bearer <your_read_scaling_token>" \
  MotherDuck https://api.motherduck.com/mcp
```

  </TabItem>
  <TabItem value="others" label="Others">

For MCP-compatible clients that support simple authentication, use the following JSON configuration with a read scaling token as the Bearer value:

```json
{
  "mcpServers": {
    "MotherDuck": {
      "url": "https://api.motherduck.com/mcp",
      "type": "http",
      "headers": {
        "Authorization": "Bearer <your_read_scaling_token>"
      }
    }
  }
}
```

For clients that only support local (stdio) servers, use `mcp-remote` to bridge the connection:

```json
{
  "mcpServers": {
    "MotherDuck": {
      "command": "npx",
      "args": [
        "mcp-remote",
        "https://api.motherduck.com/mcp",
        "--header",
        "Authorization: Bearer ${MOTHERDUCK_TOKEN}"
      ],
      "env": {
        "MOTHERDUCK_TOKEN": "<your_read_scaling_token>"
      }
    }
  }
}
```

  </TabItem>
</Tabs>

## Proxy filtering

If you're integrating the remote MCP server into a backend service or custom agent framework, you can restrict access at the application layer. When proxying MCP tool calls, omit or reject calls to the `query_rw` tool and only forward calls to the read-only `query` tool and schema exploration tools.

See [Building Analytics Agents](/key-tasks/ai-and-motherduck/building-analytics-agents) for patterns on building custom agent integrations with read-only access controls.
