# How Auth0 MCP Server Filters Write Operations at the Tool Level in Read-Only Mode

> Learn how Auth0 MCP server filters write operations at the tool level in read-only mode. Discover how it prevents AI assistants from performing non-read operations by checking _meta.readOnly === true.

- Repository: [Auth0/auth0-mcp-server](https://github.com/auth0/auth0-mcp-server)
- Tags: internals
- Published: 2026-02-25

---

**The Auth0 MCP server removes write-capable tools from the available set by filtering for `_meta.readOnly === true` when the `--read-only` flag is enabled, preventing AI assistants from invoking any non-read operations.**

The `auth0/auth0-mcp-server` implements a strict security boundary that prevents accidental or malicious modifications to Auth0 tenants when operating in restricted environments. When administrators enable read-only mode, the server does not rely on runtime permission checks or API-level blocks; instead, it surgically removes write-capable tools from the Model Context Protocol (MCP) tool registry before the AI assistant can even discover them.

## How Read-Only Filtering Works

The filtering mechanism operates through three coordinated stages that transform the full tool registry into a sanitized, read-only subset. According to the source code in `auth0/auth0-mcp-server`, this happens before the server responds to any `ListTools` MCP requests.

1. **CLI Parsing**: The `--read-only` flag is captured in the `RunOptions` object.
2. **Server Startup**: The `startServer` function forwards the flag to the tool selection logic.
3. **Tool Filtering**: The `getAvailableTools` function applies pattern filtering first, then strictly filters by the `_meta.readOnly` metadata field.

## Step-by-Step Implementation Details

### CLI Flag Parsing

The security chain begins in [`src/commands/run.ts`](https://github.com/auth0/auth0-mcp-server/blob/main/src/commands/run.ts) (lines 12-15, 101-107), where the command-line interface parses user input. When an administrator invokes:

```bash
npx @auth0/auth0-mcp-server run --read-only --tools '*'

```

The parser instantiates a `RunOptions` object with `readOnly: true` and the specified tool patterns. This configuration object travels through the application boundary to the server initialization code.

### Server Initialization

In [`src/server.ts`](https://github.com/auth0/auth0-mcp-server/blob/main/src/server.ts) (line 65), the `startServer` function receives the `RunOptions` and immediately calls `getAvailableTools` with three arguments: the complete `TOOLS` array imported from [`src/tools/index.ts`](https://github.com/auth0/auth0-mcp-server/blob/main/src/tools/index.ts), the pattern filters, and the `readOnly` boolean.

```typescript
const availableTools = getAvailableTools(TOOLS, options?.tools, options?.readOnly);

```

This single call determines which tools the AI assistant will be allowed to see for the entire session lifecycle.

### Tool Filtering Logic

The core enforcement happens in [`src/utils/tools.ts`](https://github.com/auth0/auth0-mcp-server/blob/main/src/utils/tools.ts) within the `getAvailableTools` function (lines 57-60). The logic applies two sequential filters:

**Pattern Filtering**: If specific tool patterns are provided (e.g., `--tools 'list-*'`), the function first calls `filterToolsByPatterns` to match against Glob objects.

**Read-Only Filtering**: When `readOnly` is `true`, the function invokes `filterToolsByReadOnly`:

```typescript
if (readOnly) {
  filteredTools = filterToolsByReadOnly(filteredTools);
}

```

The `filterToolsByReadOnly` implementation (lines 112-115) performs a strict equality check:

```typescript
const readOnlyTools = tools.filter((tool) => tool._meta?.readOnly === true);

```

This **excludes every tool capable of write operations** because such tools declare `readOnly: false` or omit the flag entirely. The resulting array contains only safe tools like `list-clients`, `get-resource-servers`, and `list-logs`, while removing `create-client`, `delete-resource-server`, and `patch-application`.

Before returning the filtered set via the MCP protocol, the server sanitizes the response by stripping the internal `_meta` fields (lines 77-82 in [`src/server.ts`](https://github.com/auth0/auth0-mcp-server/blob/main/src/server.ts)):

```typescript
const sanitizedTools = availableTools.map(({ _meta, ...rest }) => rest);
return { tools: sanitizedTools };

```

## The Critical Role of _meta.readOnly vs readOnlyHint

Some tool definitions include `readOnlyHint: true`, but this property **only affects UI hints** in command-line output and documentation. It provides zero security enforcement. The actual protection relies entirely on the `_meta.readOnly` boolean field, which is the only value checked by `filterToolsByReadOnly`.

Tools that perform read operations explicitly declare:

```typescript
_meta: {
  readOnly: true
}

```

Write-capable tools either set this to `false` or omit the property, causing the filter to exclude them from the sanitized tool list.

## Practical Examples

### Running in Read-Only Mode

Start the server with write protection enabled:

```bash
npx @auth0/auth0-mcp-server run --read-only --tools '*'

```

**Console output:**

```

Starting server in read-only mode
Auth0 MCP Server version 1.2.3 running on stdio with 7/23 tools available

```

The count shows only 7 tools because 16 write-capable tools were filtered out.

### Inspecting Available Tools

When an AI client requests the tool list via the MCP protocol:

```typescript
const response = await mcpClient.request({ type: 'list-tools' });
console.log(response.tools.map(t => t.name));

```

**Result:**

```json
[
  "list-clients",
  "get-resource-servers",
  "list-logs",
  "list-forms",
  "list-applications",
  "list-action-triggers",
  "list-actions"
]

```

Write operations like `create-client` or `delete-resource-server` are **absent** from the response.

### Attempting a Write Operation

If the AI attempts to invoke a filtered tool:

```typescript
const result = await mcpClient.callTool('create-client', { name: 'new-app' });

```

The server responds with:

```

Error: Unknown tool: create-client

```

This occurs because `create-client` was removed from the available tools during initialization, making it invisible to the MCP protocol.

## Summary

- The `--read-only` CLI flag sets `RunOptions.readOnly = true` in [`src/commands/run.ts`](https://github.com/auth0/auth0-mcp-server/blob/main/src/commands/run.ts).
- `startServer` forwards this flag to `getAvailableTools` in [`src/server.ts`](https://github.com/auth0/auth0-mcp-server/blob/main/src/server.ts).
- `getAvailableTools` applies glob patterns first, then calls `filterToolsByReadOnly` to keep only tools where `_meta.readOnly === true`.
- The filtered tool list is sanitized (stripped of `_meta`) and exposed via the MCP protocol; write-capable tools return "Unknown tool" errors if invoked.
- `readOnlyHint` provides UI information but offers no security protection; only `_meta.readOnly` enforces restrictions.

## Frequently Asked Questions

### What happens if I try to call a write operation while in read-only mode?

The server returns an "Unknown tool" error because write-capable tools are removed from the MCP tool registry during startup. The tool never appears in the `ListTools` response, so the AI assistant cannot construct valid requests for it.

### How is read-only mode different from using readOnlyHint in tool definitions?

`readOnlyHint` is a cosmetic property that only affects CLI help text and logging output. It does not prevent tool execution. The `_meta.readOnly` boolean is the enforcement mechanism used by `filterToolsByReadOnly` in [`src/utils/tools.ts`](https://github.com/auth0/auth0-mcp-server/blob/main/src/utils/tools.ts) to physically exclude tools from the available set.

### Can I combine read-only mode with specific tool patterns?

Yes. The server applies pattern filtering first via `filterToolsByPatterns`, then applies read-only filtering. In [`src/utils/tools.ts`](https://github.com/auth0/auth0-mcp-server/blob/main/src/utils/tools.ts) (lines 57-60), the code checks `if (readOnly)` after pattern matching, ensuring that even if a pattern matches a write-capable tool, it will be removed if the read-only flag is set.

### Where are the read-only flags defined for each tool?

Tool definitions across the codebase (such as in [`src/tools/resource-servers.ts`](https://github.com/auth0/auth0-mcp-server/blob/main/src/tools/resource-servers.ts), [`src/tools/logs.ts`](https://github.com/auth0/auth0-mcp-server/blob/main/src/tools/logs.ts), and other tool files) declare the `_meta.readOnly` property. Read-only tools set this to `true`, while management tools that create, update, or delete resources omit the flag or set it to `false`.