Authenticated remote Model Context Protocol server for managing MongoDB,
running on Cloudflare Workers. The Nyuchi-hosted deployment lives at
https://mongodb.nyuchi.dev/mcp. It is an internal, platform-team-only
service: callers sign in with WorkOS OAuth (Authorization Code + PKCE) and
must hold the mongodb:access permission. You can also stand the worker up
under your own Cloudflare account against your own MongoDB cluster; see Set up
your own MCP server further down.
Every request to /mcp rides on a WorkOS-issued OAuth session, so the endpoint
is never public.
MCP client ──OAuth──> Cloudflare Worker ──> WorkOS AuthKit (sign in)
│ /authorize,/token,/callback │
│ ▼
└────────── authorized session ──> MongoMcp (Durable Object) ──> MongoDB
@cloudflare/workers-oauth-providerfronts the worker, serving/authorize,/token, and/registerand gating/mcp.- On sign-in,
AuthkitHandler(src/authkit-handler.ts) redirects the user to WorkOS, then on/callbackexchanges the code (PKCE) and enforces the gate: the access token'sorg_idmust be inWORKOS_ALLOWED_ORG_IDS, and the required permission (mongodb:access) must appear in the grantedscope. The Connect app exposes permissions as OAuth scopes, so the worker requests the permission as a scope and WorkOS grants it only when the user's org role holds it. MongoMcpis aMcpAgentDurable Object. One DO per MCP session caches a singleMongoClientso handshakes amortise across tool calls.- All MongoDB operations are registered as MCP tools in
src/tools.ts, each carrying a human-friendlytitleand behavioural annotations (readOnlyHint/destructiveHint/idempotentHint/openWorldHint) so clients can auto-approve safe reads and warn before destructive operations.
Client note: any MCP client that speaks remote OAuth (Claude's hosted connector, Claude Code, Cursor, VS Code, …) can connect directly — it runs the browser sign-in itself. Clients without native remote support use the
mcp-remoteproxy snippet below, which performs the OAuth dance for them.
Discovery: listDatabases, listCollections, dbStats, collStats, ping,
serverStatus, hostInfo.
Reads: find, findOne, count, aggregate, distinct,
estimatedDocumentCount, explain.
Writes: insertOne, insertMany, updateOne, updateMany, deleteOne,
deleteMany (refuses empty filter without confirm: true), replaceOne,
findOneAndUpdate, findOneAndReplace, findOneAndDelete, bulkWrite.
Admin: createCollection, dropCollection (requires confirm: true),
dropDatabase (requires confirm: true), renameCollection, createView,
collMod, validate, createIndex, listIndexes, dropIndex,
indexStats, runCommand.
Monitoring: currentOp, killOp, getProfilingStatus, setProfilingLevel,
getProfilingData.
Atlas Search: listSearchIndexes, createSearchIndex, updateSearchIndex,
dropSearchIndex.
User management: createUser, updateUser, dropUser (requires
confirm: true), grantRolesToUser, revokeRolesFromUser, listUsers.
Role management: listRoles, createRole, dropRole (requires
confirm: true).
All filter/document/pipeline arguments accept Extended JSON so you can pass
{"_id": {"$oid": "..."}} or {"createdAt": {"$gte": {"$date": "2025-01-01"}}}
directly.
If you just want to talk to the Nyuchi-hosted MCP, drop one of the snippets
below into your client of choice. Replace the URL with your own
https://<your-worker>.workers.dev/mcp if you self-host.
This is an internal service. On first connect your client opens a WorkOS
sign-in page in the browser; authenticate with an account that belongs to
the allowed organization and holds the mongodb:access permission. The client
caches the resulting OAuth session and refreshes it automatically — there is no
token or header to manage by hand.
Claude Desktop: edit ~/Library/Application Support/Claude/claude_desktop_config.json
on macOS or %APPDATA%\Claude\claude_desktop_config.json on Windows.
Claude Code CLI: run claude mcp add mongodb https://mongodb.nyuchi.dev/mcp --transport http
(or add the snippet below to ~/.claude.json). It will prompt you to sign in
through WorkOS on first use.
Add to ~/.cursor/mcp.json (user-wide) or .cursor/mcp.json (project-local):
{
"mcpServers": {
"mongodb": {
"url": "https://mongodb.nyuchi.dev/mcp",
},
},
}Native MCP since VS Code 1.99. Add to .vscode/mcp.json in the workspace or
the equivalent mcp block in user settings:
{
"servers": {
"mongodb": {
"type": "http",
"url": "https://mongodb.nyuchi.dev/mcp",
},
},
}These ship MCP support but do not yet speak remote HTTP — wrap with mcp-remote,
which runs the OAuth sign-in for them:
{
"mcpServers": {
"mongodb": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://mongodb.nyuchi.dev/mcp"],
},
},
}Drop that into:
- Windsurf →
~/.codeium/windsurf/mcp_config.json - Continue →
~/.continue/config.jsonunder the top-levelmcpServerskey - Zed →
~/.config/zed/settings.jsonundercontext_servers
~/.codex/config.toml:
[mcp_servers.mongodb]
command = "npx"
args = ["-y", "mcp-remote", "https://mongodb.nyuchi.dev/mcp"]~/.gemini/settings.json (or the workspace .gemini/settings.json):
{
"mcpServers": {
"mongodb": {
"httpUrl": "https://mongodb.nyuchi.dev/mcp",
},
},
}Any MCP client that can spawn a subprocess works via the proxy snippet shown
under "Windsurf / Continue / Zed". mcp-remote handles the WorkOS OAuth
sign-in and token refresh on the client's behalf.
You only need this section if you want to run your own instance — point it at your own MongoDB cluster, customise org-/permission-gating, or host the worker yourself. Most callers should be able to use the Nyuchi-hosted deployment in the previous section.
npm installIn the WorkOS dashboard:
- Create a Connect (OAuth) application. Note its client id.
- Add your worker's callback as a redirect URI:
https://<your-worker>/callback. - Under Authorization, define a permission (e.g.
mongodb:access) and attach it to the role(s) you want to grant. The Connect app surfaces permissions as OAuth scopes, so the worker can request the permission and WorkOS grants it only to users whose org role holds it. - Note your environment's AuthKit/OAuth domain (e.g.
https://<env>.authkit.app) — it is both issuer and OAuth base.
Set these non-secret vars in wrangler.jsonc:
WORKOS_AUTHKIT_DOMAIN—https://<env>.authkit.app(issuer + OAuth base)WORKOS_CLIENT_ID— the Connect application client idWORKOS_ORGANIZATION_ID— the org the sign-in flow is pinned toWORKOS_ALLOWED_ORG_IDS— comma-separatedorg_idallowlistWORKOS_REQUIRED_PERMISSION— permission requested as a scope and enforced (e.g.mongodb:access)
Then set the secrets:
npx wrangler secret put MONGODB_URI
npx wrangler secret put COOKIE_ENCRYPTION_KEY # any long random stringCOOKIE_ENCRYPTION_KEY encrypts the client-approval cookie; the worker holds
no WorkOS secret (the Connect flow is a public PKCE client).
The user encoded in MONGODB_URI must have the privileges for whichever tools
you intend to call — the MCP can only do what that user is authorised to do.
Grant the smallest role that covers your usage:
| Tools you want to use | Required role (on the target db) |
|---|---|
find, findOne, count, aggregate, distinct, listIndexes, collStats |
read |
Above + insert*, update*, delete*, replaceOne, findOneAnd*, bulkWrite, createIndex, dropIndex, createCollection, dropCollection, renameCollection |
readWrite |
createView, explain, dbStats, profiler-style commands via runCommand |
dbAdmin (combine with readWrite, or use dbOwner) |
createUser, updateUser, dropUser, grantRolesToUser, revokeRolesFromUser |
userAdmin |
Atlas Search tools (listSearchIndexes, createSearchIndex, …) |
Atlas-cluster role with Search privileges (e.g. atlasAdmin) |
| Anything on every database in the cluster | readWriteAnyDatabase / dbAdminAnyDatabase / root |
Tools that hit a permission boundary return the MongoDB error plus a hint
pointing back to this section, so you can iterate without trial-and-error.
Grant or change roles in the Atlas UI (Database Access → edit user) or via
mongosh:
db.getSiblingDB("admin").grantRolesToUser("<mcp-user>", [{ role: "readWrite", db: "<your-db>" }]);Copy .dev.vars.example to .dev.vars, fill it in, then:
npm run devOpen http://localhost:8788/mcp with an MCP client (see How to use above and
substitute the local URL); the client runs the WorkOS sign-in on first connect.
npm run deploynpm test # vitest, runs inside workerd via @cloudflare/vitest-pool-workers
npm run type-checkCoverage:
test/mongo.test.ts— Extended JSON parse/stringify helpers (including truncation of oversized payloads).test/tools.test.ts—permissionHint/failenrichment, the full registered-tool catalogue, and the per-tool title + annotations.
End-to-end smoke testing against a real WorkOS tenant + MongoDB cluster is not
in the test suite; spin up wrangler dev with .dev.vars to exercise the
full path.
The official mongodb Node driver runs on Workers thanks to the
nodejs_compat compatibility flag (which provides node:net, node:tls,
node:dns, and node:timers). The driver opens TCP sockets to your cluster
from inside the Durable Object's request handler — never at module scope —
which is the only place Workers permit TCP connections.
- The MCP endpoint is not public.
/mcpis reachable only through a WorkOS-authorized OAuth session; unauthenticated requests never reach the tools, and the gate fails closed when unconfigured. - Access is double-gated: the session's
org_idmust be in the allowlist and themongodb:accesspermission must be present in the granted OAuth scope (WorkOS grants it only to users whose org role holds it). deleteManywith an empty filter anddropCollectionboth require an explicit confirmation flag from the tool caller.- The worker stores no WorkOS secret (public PKCE client);
COOKIE_ENCRYPTION_KEYencrypts the client-approval cookie andMONGODB_URIis a Wrangler secret. - CI runs
npm audit,actions/dependency-review-action, andgitleakson every PR — see.github/workflows/security.yml. CodeQL static analysis is handled by GitHub's Default Setup (Settings → Code security & analysis).
Tags are the source of truth. Every push to main runs
.github/workflows/auto-tag.yml, which inspects conventional-commit prefixes
since the last tag and pushes a new annotated tag:
| Commit prefix | Bump |
|---|---|
feat: |
minor |
fix: / perf: |
patch |
chore: / docs: / … |
patch |
BREAKING CHANGE: |
major |
The tag push fires release.yml, which delegates to the org-wide
nyuchi/.github/.github/workflows/reusable-release.yml — it validates the
semver shape, generates a CycloneDX SBOM, and publishes the GitHub release
with auto-generated notes.
A RELEASE_BUMP_TOKEN repo secret (fine-grained PAT with
contents: write and actions: read) is required so the auto-tag workflow
can push tags as a user identity rather than GITHUB_TOKEN —
without that, downstream tag-triggered workflows would not fire.
MIT © Nyuchi.
{ "mcpServers": { "mongodb": { "type": "http", "url": "https://mongodb.nyuchi.dev/mcp", }, }, }