How Auth0 MCP Server Filters Write Operations at the Tool Level in Read-Only Mode
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.
- CLI Parsing: The
--read-onlyflag is captured in theRunOptionsobject. - Server Startup: The
startServerfunction forwards the flag to the tool selection logic. - Tool Filtering: The
getAvailableToolsfunction applies pattern filtering first, then strictly filters by the_meta.readOnlymetadata field.
Step-by-Step Implementation Details
CLI Flag Parsing
The security chain begins in src/commands/run.ts (lines 12-15, 101-107), where the command-line interface parses user input. When an administrator invokes:
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 (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, the pattern filters, and the readOnly boolean.
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 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:
if (readOnly) {
filteredTools = filterToolsByReadOnly(filteredTools);
}
The filterToolsByReadOnly implementation (lines 112-115) performs a strict equality check:
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):
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:
_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:
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:
const response = await mcpClient.request({ type: 'list-tools' });
console.log(response.tools.map(t => t.name));
Result:
[
"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:
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-onlyCLI flag setsRunOptions.readOnly = trueinsrc/commands/run.ts. startServerforwards this flag togetAvailableToolsinsrc/server.ts.getAvailableToolsapplies glob patterns first, then callsfilterToolsByReadOnlyto 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. readOnlyHintprovides UI information but offers no security protection; only_meta.readOnlyenforces 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 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 (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, 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →