@mpurdon/mcp-mongodb 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Matthew Purdon
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,148 @@
1
+ # @mpurdon/mcp-mongodb
2
+
3
+ A local **stdio** MCP server that lets Claude query and manage MongoDB across `dev` / `stg` / `prd` environments, with a safety gate on write operations in production.
4
+
5
+ - Three named environments selectable at runtime (`switch_environment`)
6
+ - Read tools (`find`, `aggregate`, `count`, `list_*`) work in every environment
7
+ - Write tools (`insert_*`, `update_*`, `delete_*`) require `confirmed: true` when the active environment is `prd`
8
+ - `drop_collection` requires `confirmed: true` in **every** environment
9
+ - Connection strings are never returned over the wire — only hostnames
10
+
11
+ ---
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ npx -y @mpurdon/mcp-mongodb # downloads & runs the latest published build
17
+ ```
18
+
19
+ No global install needed — the host (Claude Desktop / Cowork / Code) runs it via
20
+ `npx` per session. The fastest way to register it is the monorepo configurator:
21
+
22
+ ```bash
23
+ npx @mpurdon/mcp-servers configure
24
+ ```
25
+
26
+ …or configure it manually with the steps below.
27
+
28
+ ## Setup
29
+
30
+ ### 1. Create the config file
31
+
32
+ The server reads `~/.mongodb-mcp/config.json`. Create it with your real connection strings:
33
+
34
+ ```bash
35
+ mkdir -p ~/.mongodb-mcp
36
+ cat > ~/.mongodb-mcp/config.json <<'EOF'
37
+ {
38
+ "defaultEnvironment": "dev",
39
+ "environments": {
40
+ "dev": { "connectionString": "mongodb://localhost:27017", "name": "Development" },
41
+ "stg": { "connectionString": "mongodb://user:pass@staging-host:27017/?authSource=admin", "name": "Staging" },
42
+ "prd": { "connectionString": "mongodb://user:pass@prod-host:27017/?authSource=admin", "name": "Production" }
43
+ }
44
+ }
45
+ EOF
46
+ chmod 600 ~/.mongodb-mcp/config.json
47
+ ```
48
+
49
+ > The file contains credentials. `chmod 600` ensures only your user can read it.
50
+
51
+ ### 2. Register with your Claude host
52
+
53
+ Add this to your host's MCP config (or let `npx @mpurdon/mcp-servers configure` do it):
54
+
55
+ ```json
56
+ {
57
+ "mcpServers": {
58
+ "mongodb": {
59
+ "command": "npx",
60
+ "args": ["-y", "@mpurdon/mcp-mongodb"],
61
+ "env": {}
62
+ }
63
+ }
64
+ }
65
+ ```
66
+
67
+ Config file location per host:
68
+
69
+ - **Claude Desktop** — `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS)
70
+ - **Claude Code** — `~/.claude.json` (user) or a project `.mcp.json`
71
+ - **Claude Cowork** — the workspace's `.mcp.json`
72
+
73
+ Restart the host.
74
+
75
+ ### 3. Verify
76
+
77
+ Ask: _"What MongoDB environment am I connected to?"_ — Claude should call `current_environment` and return the active env, display name, host, and whether writes are restricted.
78
+
79
+ ---
80
+
81
+ ## Tool reference
82
+
83
+ ### Environment
84
+
85
+ | Tool | Description |
86
+ | ------------------------- | -------------------------------------------------------------- |
87
+ | `switch_environment(env)` | Reconnect to `dev`, `stg`, or `prd`. |
88
+ | `current_environment()` | Return active env, display name, host, write-restriction flag. |
89
+
90
+ ### Read (unrestricted)
91
+
92
+ | Tool | Description |
93
+ | ----------------------------------------------------------------- | ------------------------------------------ |
94
+ | `list_databases()` | List all databases. |
95
+ | `list_collections(database)` | List collections in a db. |
96
+ | `find(database, collection, filter?, projection?, limit?, sort?)` | Query docs. Default `limit=20`, max `500`. |
97
+ | `aggregate(database, collection, pipeline)` | Run a pipeline. Capped at 500 results. |
98
+ | `count(database, collection, filter?)` | Count matching docs. |
99
+
100
+ ### Write (require `confirmed: true` in `prd`)
101
+
102
+ - `insert_one(database, collection, document)`
103
+ - `insert_many(database, collection, documents)`
104
+ - `update_one(database, collection, filter, update, upsert?)`
105
+ - `update_many(database, collection, filter, update)`
106
+ - `delete_one(database, collection, filter)`
107
+ - `delete_many(database, collection, filter)`
108
+
109
+ When called in `prd` without `confirmed: true`, the server returns a structured `confirmationRequired` payload describing the planned execution. Re-invoke the same tool with `confirmed: true` to actually run it.
110
+
111
+ ### Always-confirm
112
+
113
+ - `drop_collection(database, collection)` — requires `confirmed: true` in **every** environment.
114
+
115
+ ---
116
+
117
+ ## Security notes
118
+
119
+ - **No credentials in responses.** Status responses include only the host (`host:port`), never the username/password or full URI.
120
+ - **Config file permissions.** Use `chmod 600 ~/.mongodb-mcp/config.json`.
121
+ - **Stdio only.** No TCP socket is opened; the server only accepts MCP traffic from its stdin (Claude Desktop spawns it per session).
122
+ - **Input validation.** All tool parameters are validated with Zod before any MongoDB call.
123
+ - **Production gate.** Writes in `prd` are rejected unless the caller explicitly opts in with `confirmed: true`. `drop_collection` is gated in every environment.
124
+
125
+ ---
126
+
127
+ ## Development
128
+
129
+ From the monorepo root:
130
+
131
+ ```bash
132
+ pnpm --filter @mpurdon/mcp-mongodb dev # run from source (tsx)
133
+ pnpm --filter @mpurdon/mcp-mongodb typecheck # type-check only
134
+ pnpm --filter @mpurdon/mcp-mongodb build # build to dist/
135
+ ```
136
+
137
+ ### Project layout
138
+
139
+ ```
140
+ src/
141
+ index.ts # server entry — loads config, wires transport
142
+ config.ts # config schema + safe host extraction
143
+ connection.ts # singleton MongoClient manager
144
+ tools/
145
+ environment.ts # switch_environment, current_environment
146
+ read.ts # list_databases, list_collections, find, aggregate, count
147
+ write.ts # insert/update/delete/drop with prod confirmation guard
148
+ ```
package/dist/config.js ADDED
@@ -0,0 +1,83 @@
1
+ import { readFileSync, existsSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { z } from "zod";
5
+ export const CONFIG_PATH = join(homedir(), ".mongodb-mcp", "config.json");
6
+ export const EnvKey = z.enum(["dev", "stg", "prd"]);
7
+ const EnvironmentSchema = z
8
+ .object({
9
+ connectionString: z.string().min(1, "connectionString is required"),
10
+ name: z.string().min(1, "name is required"),
11
+ })
12
+ .strict();
13
+ export const ConfigSchema = z
14
+ .object({
15
+ defaultEnvironment: EnvKey,
16
+ environments: z
17
+ .object({
18
+ dev: EnvironmentSchema,
19
+ stg: EnvironmentSchema,
20
+ prd: EnvironmentSchema,
21
+ })
22
+ .strict(),
23
+ })
24
+ .strict();
25
+ export class ConfigError extends Error {
26
+ constructor(message) {
27
+ super(message);
28
+ this.name = "ConfigError";
29
+ }
30
+ }
31
+ const SAMPLE_CONFIG = `{
32
+ "defaultEnvironment": "dev",
33
+ "environments": {
34
+ "dev": { "connectionString": "mongodb://localhost:27017", "name": "Development" },
35
+ "stg": { "connectionString": "mongodb://user:pass@staging-host:27017/?authSource=admin", "name": "Staging" },
36
+ "prd": { "connectionString": "mongodb://user:pass@prod-host:27017/?authSource=admin", "name": "Production" }
37
+ }
38
+ }`;
39
+ export function loadConfig(path = CONFIG_PATH) {
40
+ if (!existsSync(path)) {
41
+ throw new ConfigError(`MongoDB MCP config file not found at ${path}.\n\n` +
42
+ `Create it with the following contents (fill in real connection strings):\n\n` +
43
+ `mkdir -p "$(dirname '${path}')"\n` +
44
+ `cat > '${path}' <<'EOF'\n${SAMPLE_CONFIG}\nEOF\n`);
45
+ }
46
+ let raw;
47
+ try {
48
+ raw = readFileSync(path, "utf8");
49
+ }
50
+ catch (err) {
51
+ throw new ConfigError(`Failed to read config at ${path}: ${err.message}`);
52
+ }
53
+ let parsed;
54
+ try {
55
+ parsed = JSON.parse(raw);
56
+ }
57
+ catch (err) {
58
+ throw new ConfigError(`Config at ${path} is not valid JSON: ${err.message}`);
59
+ }
60
+ const result = ConfigSchema.safeParse(parsed);
61
+ if (!result.success) {
62
+ throw new ConfigError(`Config at ${path} failed validation:\n${result.error.issues
63
+ .map((i) => ` - ${i.path.join(".") || "(root)"}: ${i.message}`)
64
+ .join("\n")}`);
65
+ }
66
+ return result.data;
67
+ }
68
+ /**
69
+ * Extract a non-sensitive host descriptor from a MongoDB connection string.
70
+ * Never returns credentials or the full URI.
71
+ */
72
+ export function safeHostFromUri(uri) {
73
+ try {
74
+ // mongodb:// and mongodb+srv:// are URL-parseable by WHATWG URL.
75
+ const u = new URL(uri);
76
+ // u.host includes hostname[:port]; strip credentials by construction (URL exposes them in u.username/u.password).
77
+ return u.host || "(unknown host)";
78
+ }
79
+ catch {
80
+ return "(unparseable host)";
81
+ }
82
+ }
83
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,120 @@
1
+ import { MongoClient } from "mongodb";
2
+ import { safeHostFromUri } from "./config.js";
3
+ /**
4
+ * Singleton connection manager. Holds one active MongoClient at a time.
5
+ * Calling `switchTo` closes the existing client and opens a new one.
6
+ */
7
+ export class ConnectionManager {
8
+ client = null;
9
+ _connectingPromise = null;
10
+ activeEnv;
11
+ config;
12
+ constructor(config) {
13
+ this.config = config;
14
+ this.activeEnv = config.defaultEnvironment;
15
+ }
16
+ getActiveEnv() {
17
+ return this.activeEnv;
18
+ }
19
+ getActiveEnvName() {
20
+ return this.config.environments[this.activeEnv].name;
21
+ }
22
+ getActiveHost() {
23
+ return safeHostFromUri(this.config.environments[this.activeEnv].connectionString);
24
+ }
25
+ isProduction() {
26
+ return this.activeEnv === "prd";
27
+ }
28
+ /**
29
+ * Get the connected client for the active environment, connecting lazily.
30
+ *
31
+ * Uses a shared in-flight promise so concurrent callers awaiting an initial
32
+ * connection do not each create a separate MongoClient (TOCTOU race).
33
+ */
34
+ async getClient() {
35
+ if (this.client)
36
+ return this.client;
37
+ if (this._connectingPromise)
38
+ return this._connectingPromise;
39
+ this._connectingPromise = this._connect().finally(() => {
40
+ this._connectingPromise = null;
41
+ });
42
+ return this._connectingPromise;
43
+ }
44
+ async _connect() {
45
+ const uri = this.config.environments[this.activeEnv].connectionString;
46
+ const client = new MongoClient(uri, {
47
+ // Reasonable defaults for an interactive MCP server.
48
+ serverSelectionTimeoutMS: 10_000,
49
+ connectTimeoutMS: 10_000,
50
+ socketTimeoutMS: 30_000,
51
+ maxPoolSize: 5,
52
+ minPoolSize: 1,
53
+ });
54
+ // If the topology is closed (network blip, server restart, idle eviction),
55
+ // drop our cached reference so the next getClient() reconnects cleanly.
56
+ client.on("topologyClosed", () => {
57
+ if (this.client === client)
58
+ this.client = null;
59
+ });
60
+ client.on("close", () => {
61
+ if (this.client === client)
62
+ this.client = null;
63
+ });
64
+ await client.connect();
65
+ this.client = client;
66
+ return client;
67
+ }
68
+ /**
69
+ * Switch to a different environment. Closes the current client and opens a new one
70
+ * to verify the new connection works before returning.
71
+ */
72
+ async switchTo(env) {
73
+ this._connectingPromise = null;
74
+ if (this.client) {
75
+ try {
76
+ await this.client.close();
77
+ }
78
+ catch {
79
+ // ignore close errors during switch
80
+ }
81
+ this.client = null;
82
+ }
83
+ this.activeEnv = env;
84
+ // Eagerly connect so a bad config surfaces immediately.
85
+ await this.getClient();
86
+ }
87
+ /**
88
+ * Re-read a freshly parsed config without restarting the server.
89
+ * Closes the current connection and reconnects to the same active environment
90
+ * using the updated connection string.
91
+ */
92
+ async reloadConfig(newConfig) {
93
+ this._connectingPromise = null;
94
+ if (this.client) {
95
+ try {
96
+ await this.client.close();
97
+ }
98
+ catch {
99
+ // ignore close errors during reload
100
+ }
101
+ this.client = null;
102
+ }
103
+ this.config = newConfig;
104
+ // Keep the current env but reconnect with the (potentially new) URI.
105
+ await this.getClient();
106
+ }
107
+ async close() {
108
+ this._connectingPromise = null;
109
+ if (this.client) {
110
+ try {
111
+ await this.client.close();
112
+ }
113
+ catch {
114
+ // ignore
115
+ }
116
+ this.client = null;
117
+ }
118
+ }
119
+ }
120
+ //# sourceMappingURL=connection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.js","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"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"}
package/dist/index.js ADDED
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { loadConfig, ConfigError } from "./config.js";
5
+ import { ConnectionManager } from "./connection.js";
6
+ import { registerEnvironmentTools } from "./tools/environment.js";
7
+ import { registerReadTools } from "./tools/read.js";
8
+ import { registerWriteTools } from "./tools/write.js";
9
+ async function main() {
10
+ let config;
11
+ try {
12
+ config = loadConfig();
13
+ }
14
+ catch (err) {
15
+ if (err instanceof ConfigError) {
16
+ // Write to stderr so it surfaces in the host's MCP log without
17
+ // corrupting the stdio JSON-RPC stream on stdout.
18
+ process.stderr.write(`[mongodb] ${err.message}\n`);
19
+ process.exit(1);
20
+ }
21
+ throw err;
22
+ }
23
+ const conn = new ConnectionManager(config);
24
+ const server = new McpServer({
25
+ name: "mongodb",
26
+ version: "0.1.0",
27
+ }, {
28
+ instructions: "MongoDB MCP server. Switch environments with switch_environment; check status with current_environment. Read tools are unrestricted. Write tools require confirmed=true when the active environment is 'prd'. drop_collection ALWAYS requires confirmed=true.",
29
+ });
30
+ registerEnvironmentTools(server, conn);
31
+ registerReadTools(server, conn);
32
+ registerWriteTools(server, conn);
33
+ const transport = new StdioServerTransport();
34
+ await server.connect(transport);
35
+ const shutdown = async () => {
36
+ try {
37
+ await conn.close();
38
+ }
39
+ finally {
40
+ process.exit(0);
41
+ }
42
+ };
43
+ process.on("SIGINT", shutdown);
44
+ process.on("SIGTERM", shutdown);
45
+ }
46
+ // Prevent unhandled rejections (e.g. a MongoDB op that times out after the
47
+ // MCP layer already returned) from crashing the process and resetting state.
48
+ process.on("unhandledRejection", (reason) => {
49
+ process.stderr.write(`[mongodb] unhandled rejection (ignored): ${reason}\n`);
50
+ });
51
+ main().catch((err) => {
52
+ process.stderr.write(`[mongodb] fatal: ${err.stack ?? err}\n`);
53
+ process.exit(1);
54
+ });
55
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAEtD,KAAK,UAAU,IAAI;IACjB,IAAI,MAAM,CAAC;IACX,IAAI,CAAC;QACH,MAAM,GAAG,UAAU,EAAE,CAAC;IACxB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,WAAW,EAAE,CAAC;YAC/B,+DAA+D;YAC/D,kDAAkD;YAClD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,GAAG,CAAC,OAAO,IAAI,CAAC,CAAC;YACnD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAE3C,MAAM,MAAM,GAAG,IAAI,SAAS,CAC1B;QACE,IAAI,EAAE,SAAS;QACf,OAAO,EAAE,OAAO;KACjB,EACD;QACE,YAAY,EACV,+PAA+P;KAClQ,CACF,CAAC;IAEF,wBAAwB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACvC,iBAAiB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAChC,kBAAkB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAEjC,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAEhC,MAAM,QAAQ,GAAG,KAAK,IAAI,EAAE;QAC1B,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;QACrB,CAAC;gBAAS,CAAC;YACT,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC;IACF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AAClC,CAAC;AAED,2EAA2E;AAC3E,6EAA6E;AAC7E,OAAO,CAAC,EAAE,CAAC,oBAAoB,EAAE,CAAC,MAAM,EAAE,EAAE;IAC1C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,4CAA4C,MAAM,IAAI,CAAC,CAAC;AAC/E,CAAC,CAAC,CAAC;AAEH,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,oBAAqB,GAAa,CAAC,KAAK,IAAI,GAAG,IAAI,CAAC,CAAC;IAC1E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,85 @@
1
+ import { z } from "zod";
2
+ import { EnvKey, loadConfig, safeHostFromUri, ConfigError } from "../config.js";
3
+ const textResult = (obj) => ({
4
+ content: [{ type: "text", text: JSON.stringify(obj) }],
5
+ });
6
+ const errorResult = (message) => ({
7
+ isError: true,
8
+ content: [{ type: "text", text: message }],
9
+ });
10
+ export function registerEnvironmentTools(server, conn) {
11
+ server.tool("switch_environment", "Switch the active MongoDB environment (dev|stg|prd). Reconnects and verifies the new connection.", { env: EnvKey }, async ({ env }) => {
12
+ try {
13
+ await conn.switchTo(env);
14
+ return textResult({
15
+ active: env,
16
+ name: conn.getActiveEnvName(),
17
+ host: conn.getActiveHost(),
18
+ writesRestricted: conn.isProduction(),
19
+ });
20
+ }
21
+ catch (err) {
22
+ return errorResult(`Failed to switch to '${env}': ${err.message}`);
23
+ }
24
+ });
25
+ server.tool("current_environment", "Return the active environment, its display name, the connection host (no credentials), and whether writes require confirmation.", {}, async () => {
26
+ return textResult({
27
+ active: conn.getActiveEnv(),
28
+ name: conn.getActiveEnvName(),
29
+ host: conn.getActiveHost(),
30
+ writesRestricted: conn.isProduction(),
31
+ });
32
+ });
33
+ server.tool("ping", "Ping the active MongoDB connection. Returns env, host, and round-trip time in ms.", {}, async () => {
34
+ const start = Date.now();
35
+ try {
36
+ const client = await conn.getClient();
37
+ await client.db("admin").command({ ping: 1 });
38
+ return textResult({
39
+ ok: true,
40
+ env: conn.getActiveEnv(),
41
+ host: conn.getActiveHost(),
42
+ roundTripMs: Date.now() - start,
43
+ });
44
+ }
45
+ catch (err) {
46
+ return errorResult(`ping failed (env=${conn.getActiveEnv()}, host=${conn.getActiveHost()}): ${err.message}`);
47
+ }
48
+ });
49
+ server.tool("reload_config", "Re-read ~/.mongodb-mcp/config.json and reconnect without restarting Claude Desktop. Useful after updating connection strings.", {}, async () => {
50
+ let newConfig;
51
+ try {
52
+ newConfig = loadConfig();
53
+ }
54
+ catch (err) {
55
+ return errorResult(err instanceof ConfigError
56
+ ? `Config error: ${err.message}`
57
+ : `Failed to read config: ${err.message}`);
58
+ }
59
+ try {
60
+ await conn.reloadConfig(newConfig);
61
+ }
62
+ catch (err) {
63
+ return errorResult(`Config loaded but reconnect failed: ${err.message}`);
64
+ }
65
+ const envs = Object.entries(newConfig.environments).map(([key, val]) => ({
66
+ env: key,
67
+ name: val.name,
68
+ host: safeHostFromUri(val.connectionString),
69
+ }));
70
+ return textResult({
71
+ reloaded: true,
72
+ active: conn.getActiveEnv(),
73
+ name: conn.getActiveEnvName(),
74
+ host: conn.getActiveHost(),
75
+ writesRestricted: conn.isProduction(),
76
+ environments: envs,
77
+ });
78
+ });
79
+ }
80
+ // Re-exported helpers used by other tool modules.
81
+ export const _internal = { textResult, errorResult };
82
+ export { textResult, errorResult };
83
+ // Validation helpers used by read.ts / write.ts.
84
+ export const NonEmptyString = z.string().min(1);
85
+ //# sourceMappingURL=environment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"environment.js","sourceRoot":"","sources":["../../src/tools/environment.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,145 @@
1
+ import { z } from "zod";
2
+ import { textResult, errorResult, NonEmptyString } from "./environment.js";
3
+ const FilterSchema = z.record(z.unknown()).optional();
4
+ const ProjectionSchema = z
5
+ .record(z.union([z.literal(0), z.literal(1), z.boolean()]))
6
+ .optional();
7
+ const SortSchema = z.record(z.union([z.literal(1), z.literal(-1)])).optional();
8
+ const PipelineSchema = z.array(z.record(z.unknown()));
9
+ const DEFAULT_FIND_LIMIT = 20;
10
+ const MAX_FIND_LIMIT = 500;
11
+ const MAX_AGG_RESULTS = 500;
12
+ // Server-side time budget for every operation. Set below the MCP ~30s timeout
13
+ // so MongoDB aborts cleanly and we can return an errorResult instead of an
14
+ // unhandled rejection after the MCP layer has already given up.
15
+ const OP_MAX_TIME_MS = 25_000;
16
+ // Aggregation stages that write to the database. We block these in every
17
+ // environment because they bypass the write tools' confirmation guard.
18
+ const DISALLOWED_AGG_STAGES = ["$out", "$merge"];
19
+ export function registerReadTools(server, conn) {
20
+ server.tool("list_databases", "List all databases on the active MongoDB connection.", {}, async () => {
21
+ try {
22
+ const client = await conn.getClient();
23
+ const admin = client.db().admin();
24
+ const result = await admin.listDatabases();
25
+ return textResult({
26
+ env: conn.getActiveEnv(),
27
+ databases: result.databases.map((d) => ({
28
+ name: d.name,
29
+ sizeOnDisk: d.sizeOnDisk,
30
+ empty: d.empty,
31
+ })),
32
+ });
33
+ }
34
+ catch (err) {
35
+ return errorResult(`list_databases failed: ${err.message}`);
36
+ }
37
+ });
38
+ server.tool("list_collections", "List all collections in a database.", { database: NonEmptyString }, async ({ database }) => {
39
+ try {
40
+ const client = await conn.getClient();
41
+ const cols = await client.db(database).listCollections().toArray();
42
+ return textResult({
43
+ env: conn.getActiveEnv(),
44
+ database,
45
+ collections: cols.map((c) => ({ name: c.name, type: c.type })),
46
+ });
47
+ }
48
+ catch (err) {
49
+ return errorResult(`list_collections failed: ${err.message}`);
50
+ }
51
+ });
52
+ server.tool("find", "Query documents. Defaults to limit=20, max 500. Returns matched documents.", {
53
+ database: NonEmptyString,
54
+ collection: NonEmptyString,
55
+ filter: FilterSchema,
56
+ projection: ProjectionSchema,
57
+ limit: z.number().int().positive().max(MAX_FIND_LIMIT).optional(),
58
+ sort: SortSchema,
59
+ }, async ({ database, collection, filter, projection, limit, sort }) => {
60
+ try {
61
+ const client = await conn.getClient();
62
+ const col = client.db(database).collection(collection);
63
+ const cursor = col.find((filter ?? {}), {
64
+ projection: projection,
65
+ limit: limit ?? DEFAULT_FIND_LIMIT,
66
+ sort: sort,
67
+ maxTimeMS: OP_MAX_TIME_MS,
68
+ });
69
+ const docs = await cursor.toArray();
70
+ return textResult({
71
+ env: conn.getActiveEnv(),
72
+ database,
73
+ collection,
74
+ count: docs.length,
75
+ limit: limit ?? DEFAULT_FIND_LIMIT,
76
+ documents: docs,
77
+ });
78
+ }
79
+ catch (err) {
80
+ return errorResult(`find failed: ${err.message}`);
81
+ }
82
+ });
83
+ server.tool("aggregate", "Run an aggregation pipeline. Result capped at 500 documents.", {
84
+ database: NonEmptyString,
85
+ collection: NonEmptyString,
86
+ pipeline: PipelineSchema,
87
+ }, async ({ database, collection, pipeline }) => {
88
+ try {
89
+ // Reject write-capable stages ($out, $merge) before sending to the server.
90
+ // These bypass the write tools' confirmation flow entirely.
91
+ for (const stage of pipeline) {
92
+ for (const key of Object.keys(stage)) {
93
+ if (DISALLOWED_AGG_STAGES.includes(key)) {
94
+ return errorResult(`aggregate refused: stage '${key}' performs writes and is blocked in all environments. ` +
95
+ `Use the dedicated write tools (insert/update/delete) with confirmed=true if needed.`);
96
+ }
97
+ }
98
+ }
99
+ const client = await conn.getClient();
100
+ const col = client.db(database).collection(collection);
101
+ const docs = await col
102
+ .aggregate(pipeline, { maxTimeMS: OP_MAX_TIME_MS })
103
+ .limit(MAX_AGG_RESULTS)
104
+ .toArray();
105
+ return textResult({
106
+ env: conn.getActiveEnv(),
107
+ database,
108
+ collection,
109
+ count: docs.length,
110
+ capped: docs.length >= MAX_AGG_RESULTS,
111
+ documents: docs,
112
+ });
113
+ }
114
+ catch (err) {
115
+ return errorResult(`aggregate failed: ${err.message}`);
116
+ }
117
+ });
118
+ server.tool("count", "Count documents. With no filter uses estimatedDocumentCount (fast, metadata-based). With a filter uses countDocuments (exact but slower).", {
119
+ database: NonEmptyString,
120
+ collection: NonEmptyString,
121
+ filter: FilterSchema,
122
+ }, async ({ database, collection, filter }) => {
123
+ try {
124
+ const client = await conn.getClient();
125
+ const col = client.db(database).collection(collection);
126
+ const hasFilter = filter && Object.keys(filter).length > 0;
127
+ const n = hasFilter
128
+ ? await col.countDocuments(filter, {
129
+ maxTimeMS: OP_MAX_TIME_MS,
130
+ })
131
+ : await col.estimatedDocumentCount({ maxTimeMS: OP_MAX_TIME_MS });
132
+ return textResult({
133
+ env: conn.getActiveEnv(),
134
+ database,
135
+ collection,
136
+ count: n,
137
+ estimated: !hasFilter,
138
+ });
139
+ }
140
+ catch (err) {
141
+ return errorResult(`count failed: ${err.message}`);
142
+ }
143
+ });
144
+ }
145
+ //# sourceMappingURL=read.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read.js","sourceRoot":"","sources":["../../src/tools/read.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,188 @@
1
+ import { z } from "zod";
2
+ import { textResult, errorResult, NonEmptyString } from "./environment.js";
3
+ const DocumentSchema = z.record(z.unknown());
4
+ const FilterSchema = z.record(z.unknown());
5
+ const UpdateSchema = z.record(z.unknown());
6
+ /**
7
+ * Wrap a write handler so that, when the active env is `prd` and `confirmed`
8
+ * isn't explicitly true, we return a structured confirmation-required response
9
+ * instead of executing.
10
+ *
11
+ * Pass `alwaysConfirm: true` for extra-dangerous ops (e.g. drop_collection)
12
+ * which require confirmation in every environment.
13
+ */
14
+ function withProdGuard(conn, opName, describe, execute, alwaysConfirm = false) {
15
+ return async (args) => {
16
+ const needsConfirm = alwaysConfirm || conn.isProduction();
17
+ if (needsConfirm && !args.confirmed) {
18
+ return textResult({
19
+ confirmationRequired: true,
20
+ operation: opName,
21
+ environment: conn.getActiveEnv(),
22
+ environmentName: conn.getActiveEnvName(),
23
+ host: conn.getActiveHost(),
24
+ reason: alwaysConfirm
25
+ ? `${opName} is destructive and requires explicit confirmation in every environment.`
26
+ : `Active environment is production. Re-invoke with confirmed=true to execute.`,
27
+ plannedExecution: describe(args),
28
+ });
29
+ }
30
+ try {
31
+ const result = await execute(args);
32
+ return textResult({
33
+ env: conn.getActiveEnv(),
34
+ operation: opName,
35
+ ...result,
36
+ });
37
+ }
38
+ catch (err) {
39
+ return errorResult(`${opName} failed: ${err.message}`);
40
+ }
41
+ };
42
+ }
43
+ export function registerWriteTools(server, conn) {
44
+ // insert_one
45
+ server.tool("insert_one", "Insert one document. In prd, requires confirmed=true.", {
46
+ database: NonEmptyString,
47
+ collection: NonEmptyString,
48
+ document: DocumentSchema,
49
+ confirmed: z.boolean().optional(),
50
+ }, withProdGuard(conn, "insert_one", (a) => ({
51
+ database: a.database,
52
+ collection: a.collection,
53
+ document: a.document,
54
+ }), async (a) => {
55
+ const client = await conn.getClient();
56
+ const r = await client
57
+ .db(a.database)
58
+ .collection(a.collection)
59
+ .insertOne(a.document);
60
+ return { acknowledged: r.acknowledged, insertedId: r.insertedId };
61
+ }));
62
+ // insert_many
63
+ server.tool("insert_many", "Insert multiple documents. In prd, requires confirmed=true.", {
64
+ database: NonEmptyString,
65
+ collection: NonEmptyString,
66
+ documents: z.array(DocumentSchema).min(1),
67
+ confirmed: z.boolean().optional(),
68
+ }, withProdGuard(conn, "insert_many", (a) => ({
69
+ database: a.database,
70
+ collection: a.collection,
71
+ documentCount: a.documents.length,
72
+ firstDocumentPreview: a.documents[0],
73
+ }), async (a) => {
74
+ const client = await conn.getClient();
75
+ const r = await client
76
+ .db(a.database)
77
+ .collection(a.collection)
78
+ .insertMany(a.documents);
79
+ return {
80
+ acknowledged: r.acknowledged,
81
+ insertedCount: r.insertedCount,
82
+ insertedIds: r.insertedIds,
83
+ };
84
+ }));
85
+ // update_one
86
+ server.tool("update_one", "Update one matching document. In prd, requires confirmed=true.", {
87
+ database: NonEmptyString,
88
+ collection: NonEmptyString,
89
+ filter: FilterSchema,
90
+ update: UpdateSchema,
91
+ upsert: z.boolean().optional(),
92
+ confirmed: z.boolean().optional(),
93
+ }, withProdGuard(conn, "update_one", (a) => ({
94
+ database: a.database,
95
+ collection: a.collection,
96
+ filter: a.filter,
97
+ update: a.update,
98
+ upsert: a.upsert ?? false,
99
+ }), async (a) => {
100
+ const client = await conn.getClient();
101
+ const r = await client
102
+ .db(a.database)
103
+ .collection(a.collection)
104
+ .updateOne(a.filter, a.update, { upsert: a.upsert ?? false });
105
+ return {
106
+ acknowledged: r.acknowledged,
107
+ matchedCount: r.matchedCount,
108
+ modifiedCount: r.modifiedCount,
109
+ upsertedCount: r.upsertedCount,
110
+ upsertedId: r.upsertedId,
111
+ };
112
+ }));
113
+ // update_many
114
+ server.tool("update_many", "Update all matching documents. In prd, requires confirmed=true.", {
115
+ database: NonEmptyString,
116
+ collection: NonEmptyString,
117
+ filter: FilterSchema,
118
+ update: UpdateSchema,
119
+ confirmed: z.boolean().optional(),
120
+ }, withProdGuard(conn, "update_many", (a) => ({
121
+ database: a.database,
122
+ collection: a.collection,
123
+ filter: a.filter,
124
+ update: a.update,
125
+ }), async (a) => {
126
+ const client = await conn.getClient();
127
+ const r = await client
128
+ .db(a.database)
129
+ .collection(a.collection)
130
+ .updateMany(a.filter, a.update);
131
+ return {
132
+ acknowledged: r.acknowledged,
133
+ matchedCount: r.matchedCount,
134
+ modifiedCount: r.modifiedCount,
135
+ };
136
+ }));
137
+ // delete_one
138
+ server.tool("delete_one", "Delete one matching document. In prd, requires confirmed=true.", {
139
+ database: NonEmptyString,
140
+ collection: NonEmptyString,
141
+ filter: FilterSchema,
142
+ confirmed: z.boolean().optional(),
143
+ }, withProdGuard(conn, "delete_one", (a) => ({
144
+ database: a.database,
145
+ collection: a.collection,
146
+ filter: a.filter,
147
+ }), async (a) => {
148
+ const client = await conn.getClient();
149
+ const r = await client
150
+ .db(a.database)
151
+ .collection(a.collection)
152
+ .deleteOne(a.filter);
153
+ return { acknowledged: r.acknowledged, deletedCount: r.deletedCount };
154
+ }));
155
+ // delete_many
156
+ server.tool("delete_many", "Delete all matching documents. In prd, requires confirmed=true.", {
157
+ database: NonEmptyString,
158
+ collection: NonEmptyString,
159
+ filter: FilterSchema,
160
+ confirmed: z.boolean().optional(),
161
+ }, withProdGuard(conn, "delete_many", (a) => ({
162
+ database: a.database,
163
+ collection: a.collection,
164
+ filter: a.filter,
165
+ }), async (a) => {
166
+ const client = await conn.getClient();
167
+ const r = await client
168
+ .db(a.database)
169
+ .collection(a.collection)
170
+ .deleteMany(a.filter);
171
+ return { acknowledged: r.acknowledged, deletedCount: r.deletedCount };
172
+ }));
173
+ // drop_collection — ALWAYS requires confirmation
174
+ server.tool("drop_collection", "Drop an entire collection. Requires confirmed=true in EVERY environment (dev/stg/prd).", {
175
+ database: NonEmptyString,
176
+ collection: NonEmptyString,
177
+ confirmed: z.boolean().optional(),
178
+ }, withProdGuard(conn, "drop_collection", (a) => ({ database: a.database, collection: a.collection }), async (a) => {
179
+ const client = await conn.getClient();
180
+ const dropped = await client
181
+ .db(a.database)
182
+ .collection(a.collection)
183
+ .drop();
184
+ return { dropped };
185
+ },
186
+ /* alwaysConfirm */ true));
187
+ }
188
+ //# sourceMappingURL=write.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write.js","sourceRoot":"","sources":["../../src/tools/write.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@mpurdon/mcp-mongodb",
3
+ "version": "0.1.0",
4
+ "description": "Local stdio MCP server for MongoDB with multi-environment support and production write protection.",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "mongodb": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist"
12
+ ],
13
+ "publishConfig": {
14
+ "access": "public"
15
+ },
16
+ "keywords": [
17
+ "mcp",
18
+ "model-context-protocol",
19
+ "mongodb",
20
+ "claude"
21
+ ],
22
+ "license": "MIT",
23
+ "author": "Matthew Purdon",
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "git+https://github.com/mpurdon/mcp-servers.git",
27
+ "directory": "packages/mongodb"
28
+ },
29
+ "dependencies": {
30
+ "@modelcontextprotocol/sdk": "^1.0.4",
31
+ "mongodb": "^6.10.0",
32
+ "zod": "^3.23.8"
33
+ },
34
+ "devDependencies": {
35
+ "@types/node": "^22.10.2",
36
+ "eslint": "^9.17.0",
37
+ "tsx": "^4.19.2",
38
+ "typescript": "^5.7.2",
39
+ "@mpurdon/tsconfig": "0.0.0",
40
+ "@mpurdon/eslint-config": "0.0.0"
41
+ },
42
+ "engines": {
43
+ "node": ">=20"
44
+ },
45
+ "scripts": {
46
+ "build": "tsc",
47
+ "dev": "tsx src/index.ts",
48
+ "start": "node dist/index.js",
49
+ "typecheck": "tsc --noEmit",
50
+ "lint": "eslint ."
51
+ }
52
+ }