MongoDB MCP Server Read-Only Setup for Claude Code: --readOnly, a read-Role User and a Verified First Query, With Codex and Cursor Notes

Set up the MongoDB MCP server read-only for Claude Code: --readOnly, a read-role user, env-var secrets and five rerunnable checks, with Codex and Cursor notes.

Hero illustration for a MongoDB MCP server read-only setup. Left panel lists eight tools registered with --readOnly, such as find, aggregate and collection-schema. Right dashed panel lists eight tools never registered, such as insert-many, update-many and drop-database. A green strip below reads Lock 2: agent_ro has only role read on db shop_dev, server refuses any write.Hero illustration for a MongoDB MCP server read-only setup. Left panel lists eight tools registered with --readOnly, such as find, aggregate and collection-schema. Right dashed panel lists eight tools never registered, such as insert-many, update-many and drop-database. A green strip below reads Lock 2: agent_ro has only role read on db shop_dev, server refuses any write.
Two locks: --readOnly keeps the write tools out of the agent's tool list, and the read role on shop_dev makes the database refuse anything that slips through. Tool names from the mongodb-mcp-server README, November 2026.

You want a coding agent to answer questions about a MongoDB database. Which collections exist, what does an orders document really look like, and which customers place the most shipped orders? You do not want that same agent able to insert a probe document, rename a collection or drop a database because a prompt went sideways. The MongoDB MCP server can give Claude Code that kind of access, but read-only is not its default. MongoDB’s security best practices for the MCP Server say read-only mode isn’t enabled by default and that the server otherwise allows cluster write operations.

This tutorial builds the safe version end to end on one illustrative self-managed database, shop_dev, reached at localhost:27017, with an orders collection. The complete walkthrough is for Claude Code, whose configuration commands are documented in the selected sources. Codex and Cursor get a shorter section that covers what MongoDB’s own setup tooling documents, and says plainly what it does not. When you finish you will have:

  • A Claude Code server entry that reads its connection string from MDB_MCP_CONNECTION_STRING, never from a command-line argument, and keeps the real string out of the repository.
  • The server started with --readOnly, plus MDB_MCP_READ_ONLY=true as a second switch in the same entry.
  • A dedicated database user, agent_ro, holding only the built-in read role on shop_dev.
  • Five checks you can rerun, showing that reads work, that write tools are absent, and that the database itself refuses a write from the same login.

The dated reason to do this now: mongodb-mcp-server v3.3.0, published on GitHub on November 9, 2026, fixes the setup utility so the MCP configs it generates include --readOnly for all clients (MCP-642, #1669). The same release adds named preconfigured connections through connectionStrings (#1593) and dials the preconfigured connection at startup (#1605). Only Step 4 relies on the setup fix. Everything else uses options the project README documents today.

Why read-only takes two locks on MongoDB

The two layers do different jobs, and it helps to be precise about which one is the hard stop.

The first lock lives in the MCP server. According to the README, readOnly restricts the server to tools with the read, connect and metadata operation types, and tools with create, update or delete types “will not be registered with the server.” The agent never sees insert-many, update-many, delete-many, drop-collection or drop-database. That is a guarantee about the tool list. It says nothing about what the login behind the server can do.

The second lock lives in MongoDB. A user with only the built-in read role on shop_dev can query that database and nothing more. The database enforces it no matter which client connects, so it holds if someone copies the credentials into mongosh, turns read-only mode off while debugging, or finds a write path the flag does not cover. On its own, the read role refuses every write path this guide tests.

So why keep both? Because each one fails differently. If someone later grants agent_ro a write role by mistake, read-only mode still keeps the ordinary write tools out of the agent’s tool list. If someone removes both read-only switches, the --readOnly flag and MDB_MCP_READ_ONLY=true, the role still refuses every write, but the agent now sees write tools and will try them, burning turns on authorization errors and inviting someone to “fix” the permissions. Deleting only one switch changes nothing, because the README documents either one as enough to turn read-only mode on. MongoDB’s guidance asks for the pairing: create dedicated read-only database users for MCP Server connections, use the read role or a custom read-only role, never give the server write credentials, and turn on --readOnly.

One write path shows why the role is the real stop. The README lists aggregate as a read-type tool, so it stays registered in read-only mode. It also says aggregate and aggregate-db always ask for confirmation before running a pipeline with a $out or $merge stage, because those stages write to a collection. MongoDB’s aggregation reference agrees that a pipeline run with db.collection.aggregate() does not modify documents unless it contains a $merge or $out stage. The README does not say that read-only mode blocks such a pipeline. A user holding only read cannot write the output collection, so the database refuses it whatever the server decides.

Matrix chart titled The read role is the stop; --readOnly adds depth. Rows: reads (find, aggregate, count); write tools (insert-many, update-many, drop-*); $out or $merge in aggregate; the same credentials in mongosh. Columns: --readOnly only, read role only, both. Reads are allowed in every column. Under --readOnly only, write tools are not registered, $out is not stated and mongosh is no effect. Under read role only, all three write rows show DB refuses. Under both, all three show blocked.Matrix chart titled The read role is the stop; --readOnly adds depth. Rows: reads (find, aggregate, count); write tools (insert-many, update-many, drop-*); $out or $merge in aggregate; the same credentials in mongosh. Columns: --readOnly only, read role only, both. Reads are allowed in every column. Under --readOnly only, write tools are not registered, $out is not stated and mongosh is no effect. Under read role only, all three write rows show DB refuses. Under both, all three show blocked.
The read role alone refuses every write shown here; --readOnly adds depth by keeping ordinary write tools out of the agent's list, but is not documented to stop $out or a reused login. Sources: mongodb-mcp-server README, MongoDB MCP security best practices and aggregation reference, November 2026.

If you have already set this up for Postgres, the reasoning matches our Postgres MCP server read-only role guide and the SQL Server read-only login walkthrough. What changes here is the mechanics: MongoDB’s built-in read role instead of SQL GRANTs, and MDB_MCP_* environment variables instead of a DSN flag. The general argument for treating an agent’s database login like any other privileged account is in AI agent identity as a privileged user, so this guide skips it.

MongoDB Docs page MongoDB MCP Server Security Best Practices, captured from mongodb.com.
MongoDB's MCP Server Security Best Practices page on mongodb.com/docs. The page recommends read-only mode together with a read-only database user, and environment variables for secrets. · Original source

Before you start: versions, access and the shop_dev example

Check these before you create anything. Each one maps to a failure in the troubleshooting table near the end.

  • [ ] Node.js 22.13.0 or later. The README sets that minimum and warns that Node 20 support is deprecated. Run node -v and expect v22.13.0 or higher.
  • [ ] A self-managed MongoDB deployment where you can create users. MongoDB Community or Enterprise needs access control turned on and a user administrator, per MongoDB’s create-a-user tutorial.
  • [ ] Claude Code installed. Codex and Cursor are optional; Step 4 explains exactly how much of their setup this guide covers.
  • [ ] TLS on the path to the database if you are not on localhost. The createUser command reference warns that the command sends its data, including the password, in cleartext unless transport encryption is on.
  • [ ] A non-production copy of the data, if you can. MongoDB recommends read-only access for production and allows limited write access for development, staging and isolated test clusters. This guide is read-only everywhere, and a copy narrows the blast radius further.

About Atlas. This guide does not walk through an Atlas deployment, and it does not claim the steps below carry over unchanged. The createUser reference states the command is not supported on Atlas clusters, and the dropUser reference says the same, so on Atlas you configure database users through Atlas instead. The README notes that a connection string can point at an Atlas cluster, and it recommends the separate mongodb-atlas plugin, which connects to a MongoDB-hosted server over OAuth, as the way to reach Atlas from an agent. Neither the Atlas user setup, its connection-string authentication options nor its network access steps are verified here. If you adapt the guide to Atlas, treat the five-case table as the acceptance test you must pass again before trusting it.

The worked example uses these illustrative values. Substitute your own as you go.

Item Example value Why it matters
Deployment Self-managed mongod at localhost:27017 The only deployment this walkthrough verifies against its sources
Database shop_dev The only database agent_ro can read
Collection orders Target of the schema and aggregation checks
Agent user agent_ro Created in shop_dev, which makes shop_dev its authentication database
Role read on shop_dev Built-in role; no write actions
Probe value agent-ro-probe-2026-11-11 A unique marker for the refused-write test, so counts and cleanup touch one value only
Shell variable MDB_AGENT_RO_URI Holds the connection string outside every config file
MCP server name mongodb Name you will see in claude mcp list and /mcp

Step 1: Create agent_ro with only the read role

MongoDB’s tutorial says each application and user should map to a distinct user, and that a user should hold only the minimal privileges it needs. An agent is an application, so it gets its own login. Do not reuse your own account or the app’s service user.

  1. Connect as a user administrator, using the tutorial’s form:

    mongosh --port 27017 --authenticationDatabase "admin" -u "myUserAdmin" -p
    
  2. Switch to the target database and create the user with a password prompt rather than a literal string:

    use shop_dev
    db.createUser({
      user: "agent_ro",
      pwd: passwordPrompt(),
      roles: [ { role: "read", db: "shop_dev" } ]
    })
    
  3. Type the password when prompted and store it in your password manager. MongoDB recommends passwordPrompt() so the password never lands on screen or in shell history.

  4. Exit mongosh. You cannot switch users inside one session; the tutorial notes that authenticating as a second user gives the session the privileges of both.

Three choices in that block do real work. Creating the user while use shop_dev is active makes shop_dev the user’s authentication database. That only decides where the login is checked, not what it can touch. The role document { role: "read", db: "shop_dev" } names the database explicitly, so the grant cannot drift to another database. And no readWrite role appears anywhere, which is the whole point.

For a database the agent should reach only from your laptop, the createUser command also accepts authenticationRestrictions with a clientSource list of IP addresses or CIDR ranges. The server then refuses to authenticate agent_ro from anywhere else. Avoid stacking roles with conflicting restrictions: the reference warns that a user who inherits incompatible restrictions becomes unusable.

Step 2: Prove agent_ro cannot write before an agent sees it

Test the database layer by hand first. If this step passes, a later failure points at the agent client’s configuration rather than at permissions. The probe uses one unique value so the count you take before and after cannot be confused with real orders.

  1. Log in as the new user, naming its authentication database:

    mongosh --port 27017 -u "agent_ro" --authenticationDatabase "shop_dev" -p
    
  2. Select the database and read one document through a one-stage pipeline:

    use shop_dev
    db.orders.aggregate([ { $limit: 1 } ])
    

    Expected observation (illustrative): one orders document prints. If it does, the read role is attached to the right database. Keep it on screen; Step 6 compares the agent’s schema against it.

  3. Take the baseline for the probe value:

    db.orders.count({ _probe: "agent-ro-probe-2026-11-11" })
    

    Expected observation (illustrative): 0. If it is not zero, someone already used that marker, so pick a new unique value and repeat this step.

  4. Try the write you would never want the agent to make, still in shop_dev:

    db.orders.insertOne({ _probe: "agent-ro-probe-2026-11-11" })
    

    Expected observation (illustrative): the insert fails with an authorization error saying agent_ro is not authorized to run insert on shop_dev. The exact wording varies by server version.

  5. Run the same count again. Expected observation (illustrative): still 0.

  6. Exit the shell.

If step 4 succeeds or the second count is 1, stop. The user holds a write-capable role somewhere, perhaps inherited from a role granted in another database. Ask an administrator with write access to delete the single document whose _probe equals agent-ro-probe-2026-11-11, and nothing else, then fix the user’s roles and repeat this step. Because the marker is unique, the cleanup cannot touch real orders.

Step 3: Add the server to Claude Code without a secret in argv

The README is blunt about secrets: pass connection strings through environment variables, not command-line arguments, because arguments can be visible in process lists and logged in various system locations. MongoDB’s security page adds that arguments can be tracked and cached. The README also shows a configuration that puts the connection string in args, labelled as not recommended. Do not copy that one.

Claude Code’s MCP documentation gives you a clean way to keep the secret out of both argv and the repository. You can reference an environment variable as ${VAR} in a server’s command, args, env, url or headers value, and a project-scoped entry lives in a .mcp.json file you can commit. The file then holds the variable name, and only your shell holds the value.

  1. Put the connection string in your shell without echoing it or saving it to history:

    read -rs MDB_AGENT_RO_URI   # paste mongodb://agent_ro:<password>@localhost:27017/shop_dev, then Enter
    export MDB_AGENT_RO_URI
    

    The path names shop_dev, the database where agent_ro was created in Step 1. Keeping the user and the path in the same database avoids the most common authentication mismatch.

  2. From the project root, add a project-scoped stdio server:

    claude mcp add-json mongodb '{"type":"stdio","command":"npx","args":["-y","mongodb-mcp-server@latest","--readOnly"],"env":{"MDB_MCP_CONNECTION_STRING":"${MDB_AGENT_RO_URI}","MDB_MCP_READ_ONLY":"true"}}' --scope project
    

    Expected observation: an Added ... line, which Claude Code’s documentation says it prints when the configuration was written.

  3. Open .mcp.json and check that it contains ${MDB_AGENT_RO_URI} literally, not a real mongodb:// string.

  4. Start claude in the same shell, so the variable is in its environment. Approve the project server when asked. Until you do, claude mcp list shows it as ⏸ Pending approval.

  5. Run claude mcp get mongodb and look for a connected status. Inside a session, /mcp shows the server with its tool count.

Why each choice:

Choice What it buys you
--scope project The entry is in .mcp.json, reviewable in a pull request, and shared with teammates who bring their own MDB_AGENT_RO_URI
${MDB_AGENT_RO_URI} in env The secret stays out of the file, the repository and the server’s argv
--readOnly in args The documented flag; the README notes command-line arguments take precedence over environment variables
MDB_MCP_READ_ONLY=true in env A second switch, so read-only survives someone deleting the flag while editing args
"type":"stdio" Claude Code launches the server as a local child process; MongoDB recommends stdio for local deployments

If you prefer a personal entry instead of a shared one, the same JSON works with --scope user or the default local scope, which both store it in ~/.claude.json. Our Claude Code MCP setup and scopes guide covers how those scopes interact.

About v3.3.0’s named connections. The README now marks MDB_MCP_CONNECTION_STRING as deprecated in favour of the preconfigured entry of MDB_MCP_CONNECTION_STRINGS, a JSON object keyed by name that you can set only through an environment variable or a config file. Both expose the connection as preconfigured. This guide keeps the single variable because every README example and MongoDB’s security page use it. If you later need a second deployment, the multi-connection form is {"preconfigured": "mongodb://...", "analytics": "mongodb://..."}, with every login in it scoped exactly like agent_ro.

Step 4: Codex and Cursor: what the setup utility covers and what to confirm

The README notes that configuration file syntax differs across clients and links MongoDB’s per-client Get Started pages for Codex and Cursor. The selected evidence for this guide does not include those pages’ configuration details, so this section does not tell you where Codex or Cursor store their MCP configuration or what syntax they expect. It gives you the two routes the README documents, then checks that work whatever file the client uses. If you need a fully specified path for either client today, follow MongoDB’s Get Started page for it and use the checks below as your acceptance test.

GitHub release page for mongodb-js/mongodb-mcp-server v3.3.0, captured from github.com.
The v3.3.0 release of mongodb-js/mongodb-mcp-server on GitHub, published November 9, 2026. Its release notes include the setup fix that adds --readOnly to generated MCP configs for all clients (MCP-642). · Original source

  1. Setup utility route. Export MDB_AGENT_RO_URI as in Step 3, then run:

    npx -y mongodb-mcp-server@latest setup
    

    The README says it guides you through configuring a connection string or Atlas API credentials. Choose the connection string and supply the agent_ro string. The v3.3.0 release notes say the configs it generates include --readOnly for all clients.

  2. Plugin route. For Codex, the README lists codex plugin marketplace add mongodb/agent-skills, then /plugins to install mongodb; for Cursor it lists a marketplace entry. The README does not say whether those plugins start the server read-only, so do not assume they do.

  3. Check the effective setting, not only argv. With the client running, use the process-list check from Step 5. Expected observation (illustrative): a mongodb-mcp-server command line that contains no mongodb:// text. If --readOnly appears in it, that confirms the flag reached the process. If it does not appear, that alone does not mean the server can write: the README documents MDB_MCP_READ_ONLY=true and a configuration-file readOnly: true as equivalent switches, and neither shows up in argv. Decide from the registered tools instead. Run the Step 5 dry run with the exact args and env the client uses, or, where the client supports MCP resources, read the server’s redacted config://config resource and look for readOnly set to true. If insert-many, update-many or drop-database appear in the tool list, read-only mode is not in effect: add a switch through the client’s own documented configuration or remove the server.

  4. Confirm the tool list. Run case task:mongo-ro-03 from the test table in that client. Expected observation (illustrative): the agent reports it has no insert tool.

  5. Keep the database lock unchanged. Use the same agent_ro login. The read role does not depend on which client connects, which is why it is the lock to trust when client details are uncertain.

For reference, this is the README’s generic connection-string example with agent_ro and the second switch added. It is the README’s shape, not a verified Codex or Cursor file:

{
  "mcpServers": {
    "MongoDB": {
      "command": "npx",
      "args": ["-y", "mongodb-mcp-server@latest", "--readOnly"],
      "env": {
        "MDB_MCP_CONNECTION_STRING": "mongodb://agent_ro:<password>@localhost:27017/shop_dev",
        "MDB_MCP_READ_ONLY": "true"
      }
    }
  }
}

Any file like this holds the real password, so keep it out of repositories you push. The README’s advice for its own JSON config file is owner-only permissions (chmod 600) on Linux and macOS, and the same reasoning applies here.

Step 5: Confirm the write tools never registered

Do not trust the flag because you typed it. Ask the server what it registered, and check what the operating system can see.

Diagram of the path from agent client to shop_dev data. Five stacked nodes: 1 Agent client, 2 Client config with the secret in env and --readOnly in args, 3 mongodb-mcp-server --readOnly registering only read, connect and metadata tools, 4 Login as agent_ro with built-in role read on db shop_dev, 5 Rows from shop_dev. A write call from node 3 goes to a red Stop: tool absent card for insert-many; a write from node 4 goes to a red Stop: not authorized card for a $out stage or reused login.Diagram of the path from agent client to shop_dev data. Five stacked nodes: 1 Agent client, 2 Client config with the secret in env and --readOnly in args, 3 mongodb-mcp-server --readOnly registering only read, connect and metadata tools, 4 Login as agent_ro with built-in role read on db shop_dev, 5 Rows from shop_dev. A write call from node 3 goes to a red Stop: tool absent card for insert-many; a write from node 4 goes to a red Stop: not authorized card for a $out stage or reused login.
Each write meets a stop before it reaches shop_dev: ordinary write tools are never registered, and the read role refuses anything that still arrives, including a $out stage or a reused login. Illustrative path based on the mongodb-mcp-server README and MongoDB's MCP security best practices, November 2026.

  1. Dry run. The README documents a dryRun option that dumps the configuration and enabled tools, then exits without starting the server:

    MDB_MCP_READ_ONLY=true npx -y mongodb-mcp-server@latest --readOnly --dryRun
    

    Expected observation (illustrative): the enabled-tool list includes find, aggregate, count, list-collections and collection-schema, and contains none of insert-many, update-many, delete-many, rename-collection, create-collection, create-index, drop-index, drop-collection or drop-database. The README classifies tools by operation type rather than listing each tool’s type, so treat this output as the place to confirm the classification.

  2. Server log. The README says that in read-only mode the logs show which tools were prevented from registering. With the default disk logger, logs are in ~/.mongodb/mongodb-mcp/.app-logs on macOS and Linux, or %LOCALAPPDATA%\mongodb\mongodb-mcp\.app-logs on Windows.

  3. Process list. With the agent running, check the server’s command line:

    ps -eo args | grep "[m]ongodb-mcp-server"
    

    Expected observation (illustrative): a line ending in mongodb-mcp-server@latest --readOnly, with no mongodb:// or mongodb+srv:// text anywhere. This check proves two things only: the secret is not in argv, and the flag is. It cannot see the MDB_MCP_READ_ONLY switch, so use items 1 and 5 to confirm the effective setting.

  4. Repository. Run git grep -n "mongodb://" and git grep -n "mongodb+srv://" in the project. Expected observation (illustrative): no matches, because .mcp.json holds only the variable name.

  5. Config resource. In Claude Code, mention @mongodb:config://config. The README describes this resource as the server configuration with sensitive parameters redacted. Expect readOnly to be true and the connection string to be redacted.

Step 6: Run the first verified query on orders

With both locks confirmed, give the agent real read work. Paste this prompt into Claude Code:

Using the MongoDB server, list the collections in shop_dev. Then describe the schema of orders, including field types and any fields that look optional. Finally, write an aggregation pipeline that returns the ten customers with the most shipped orders, run it, and explain each stage.

Expected tool calls, in order (illustrative): list-collections on shop_dev, collection-schema on orders, then aggregate with a pipeline shaped like this:

[
  { "$match": { "status": "shipped" } },
  { "$group": { "_id": "$customerId", "orders": { "$sum": 1 } } },
  { "$sort": { "orders": -1 } },
  { "$limit": 10 }
]

The stages follow the pattern in MongoDB’s aggregation reference, which filters with $match, groups and counts with $group and $sum: 1, orders with $sort and trims with $limit. The field names status and customerId are illustrative. A good agent takes them from the collection-schema output rather than guessing, and that is the first thing to check in its answer.

How to judge the run:

  1. Compare the inferred schema with the document you printed in Step 2. A field type that disagrees with real data is a reason to distrust the pipeline.
  2. Check that $match comes first. MongoDB’s reference notes that an early $match reduces the number of documents flowing through the pipeline. If you want the server to reject collection scans outright, the README’s indexCheck option does that.
  3. Ask the agent to call explain on the same pipeline if the collection is large. It is a read tool, so it stays registered.
  4. Remember that find and aggregate return at most 100 documents by default (maxDocumentsPerQuery). An answer that seems to cover “all customers” may be capped.
  5. Treat returned documents as data, not instructions. By default the server wraps tool output in delimiters with a prompt-injection warning. Leave disableUntrustedDataWarning at its default of false.

The five-case test table: task:mongo-ro-01 to task:mongo-ro-05

Run these five cases once after setup and again after any change to the server entry, the client or the user. The expected results are illustrative observations based on the documented behaviour, not output from a recorded run.

Input Action Expected result
task:mongo-ro-01: prompt “List the collections in shop_dev” with the server connected as agent_ro Agent calls list-collections with database shop_dev Collection names including orders are returned with no error
task:mongo-ro-02: the Step 6 prompt asking for the ten customers with the most shipped orders Agent calls collection-schema on orders, then aggregate with $match, $group, $sort, $limit Up to 10 result documents with _id and orders; the explanation names each stage
task:mongo-ro-03: prompt “Insert one document with _probe set to agent-ro-probe-2026-11-11 into shop_dev.orders” Agent looks for an insert tool No insert-many tool exists, so the agent reports it cannot write; db.orders.count({ _probe: "agent-ro-probe-2026-11-11" }) stays 0
task:mongo-ro-04: mongosh --port 27017 -u "agent_ro" --authenticationDatabase "shop_dev" -p, then use shop_dev, then db.orders.insertOne({ _probe: "agent-ro-probe-2026-11-11" }) Direct write with the agent’s own credentials, bypassing the MCP server Authorization error naming insert on shop_dev; the probe count stays 0
task:mongo-ro-05: start claude in a new shell after unset MDB_AGENT_RO_URI Claude Code loads the mongodb entry from .mcp.json claude mcp list warns that MDB_AGENT_RO_URI is missing; queries fail to connect instead of silently using another login

Case 03 tests the first lock and case 04 tests the second. Case 05 tests that the configuration fails closed: Claude Code’s documentation says a missing ${VAR} produces a warning and loads the server with the literal text unexpanded, which is not a usable connection string. A sixth case is worth adding if your agents write pipelines: ask for an aggregation that ends in $out to a new collection. Expect a confirmation prompt from the server, and if you approve it, an authorization failure from the database.

What --readOnly does not cover

The flag removes tools. It does not turn the server into a sealed box. Check each of these against your setup:

  • [ ] $out and $merge stages. aggregate stays registered, and the README describes only a confirmation prompt for write stages. The read role is what stops the write.
  • [ ] Unattended runs. The README warns that clients running non-interactively, such as codex exec or CI, treat an unanswered confirmation as a decline. That is safe here, but do not “fix” it by emptying confirmationRequiredTools on a server that has any write path.
  • [ ] Exports on disk. The export tool writes results to exportsPath (default ~/.mongodb/mongodb-mcp/exports on macOS and Linux). MongoDB’s guidance is to keep that directory, and the log directory, owner-only, because both can hold sensitive data.
  • [ ] Network exposure. Keep the default stdio transport. If you switch to HTTP, the server binds to 127.0.0.1 and refuses a non-loopback host unless MDB_MCP_DANGEROUS_HOST_BINDING=true. The README is explicit that the CLI server has no built-in authentication.
  • [ ] Extra trimming. disabledTools can remove more tools by name, operation type or category. Use it to drop export if the agent never needs files on disk.
  • [ ] Server-side JavaScript. disableServerSideJs defaults to true, which blocks $where, $function and $accumulator. Leave it.
  • [ ] Telemetry. The server collects usage data unless you set MDB_MCP_TELEMETRY=disabled or DO_NOT_TRACK=1. Decide deliberately.

For a fleet, the same entry belongs in the inventory process described in MCP server inventory before allowlist, and the client-side permission defaults belong in restricted-mode fleet policy. The broader injection risk of letting an agent read untrusted documents is covered in securing AI agents.

Troubleshooting auth, Atlas access, Node and missing servers

Most failures fall into one of five patterns. In Claude Code, the server’s debug://mongodb resource (mention @mongodb:debug://mongodb) records the last connectivity attempt and its error, so read it first.

Symptom Likely cause What to do
Authentication failed on every query Wrong password, or the URI points at a different authentication database than the one agent_ro was created in Repeat the Step 2 mongosh login with --authenticationDatabase "shop_dev". If that works, the URI is wrong; if it fails, create a replacement user as in the rollback section
Connection to an Atlas cluster times out (outside this guide’s verified path) Your current IP is not allowed to reach the cluster; the README’s atlas-create-access-list tool exists to allow IP or CIDR ranges Allow the narrowest range that works, per MongoDB’s guidance to restrict access to specific addresses, then rerun all five cases
Server exits at startup or npx reports an engine error Node.js older than 22.13.0 Run node -v, upgrade to Node 22.13 or later, then restart the client
Server missing from claude mcp list or shown as pending Project entry not yet approved, or the folder is untrusted Run claude in the project and approve it. claude mcp get mongodb shows the status and any Issue: detail
Write tools appear in /mcp Read-only not applied: flag missing from args and env not set Re-run the Step 5 dry run with the exact args and env from your config and fix whichever is missing

Two smaller cases come up often. A missing-variable warning in claude mcp list means MDB_AGENT_RO_URI was not exported in the shell that started Claude Code. And an empty result from list-collections usually means the agent asked about a database where agent_ro holds no read role.

Rollback: remove the entry, then drop or rotate agent_ro

Roll back in the reverse order of setup, so no client is left pointing at a login that no longer exists.

  1. Remove the Claude Code entry: claude mcp remove mongodb --scope project. Claude Code’s docs show claude mcp remove <name> --scope <scope>. Commit the .mcp.json change so teammates lose the entry too.

  2. For Codex or Cursor, remove the entry with that client’s own documented tooling, or uninstall the mongodb plugin if you used one.

  3. Clear the secret from your shell: unset MDB_AGENT_RO_URI.

  4. To rotate rather than retire, skip steps 1 to 3 and replace the login instead. The order matters, because a running client keeps whatever environment it started with:

    1. Create a second user, agent_ro_2, with the Step 1 block, then repeat the Step 2 checks logged in as agent_ro_2. Expected observation (illustrative): the read works and the probe insert is refused. Do not continue until both are true.
    2. Exit every running claude session. Changing MDB_AGENT_RO_URI in a shell does not change the environment of a Claude Code process that is already running, or of the MCP server child it launched, so a session left open keeps authenticating as agent_ro.
    3. In a new shell, run read -rs MDB_AGENT_RO_URI, paste the agent_ro_2 connection string, run export MDB_AGENT_RO_URI, then start claude from that same shell.
    4. Rerun the five cases. They pass with either login, so at this point they prove the setup still works, not which user the server uses.
    5. Drop agent_ro as in the next step, exit claude, start it again from the same shell and rerun task:mongo-ro-01. Expected observation (illustrative): the collections list returns. Because agent_ro no longer exists, success shows the server now authenticates as agent_ro_2. If it fails with an authentication error, the entry was still reaching the old login: fix MDB_AGENT_RO_URI in that shell and relaunch. agent_ro_2 is untouched, so nothing else needs undoing.
  5. To drop a user, connect as the user administrator and run the command from MongoDB’s dropUser reference, which must be issued while using the database where the user exists:

    use shop_dev
    db.runCommand({ dropUser: "agent_ro" })
    
  6. Confirm the drop with the Step 2 login. Expected observation (illustrative): authentication fails for agent_ro.

  7. Delete or archive the logs and any exports under ~/.mongodb/mongodb-mcp/ if they hold data you no longer want on the machine.

If you only need to pause the agent, disable the server from the /mcp panel instead. Claude Code keeps the configuration and stops connecting until you turn it back on.

Sources

  1. Release v3.3.0, mongodb-js/mongodb-mcp-server (GitHub, Nov 9, 2026)
  2. mongodb-js/mongodb-mcp-server README (GitHub)
  3. MongoDB MCP Server Security Best Practices (MongoDB Docs)
  4. Create a User on Self-Managed Deployments (MongoDB Docs)
  5. createUser database command (MongoDB Docs)
  6. Aggregation Operations (MongoDB Docs)
  7. dropUser database command (MongoDB Docs)
  8. Connect Claude Code to tools via MCP (Claude Code Docs)
YOU'RE THROUGH THIS ONE.

Keep connecting the dots.

Back to the library

Many assistants.
One place to remember.

Bring your AI sessions, context, and usage together with Automater.

Meet Automater