@munlib/mongodb-readonly-mcp 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/README.md ADDED
@@ -0,0 +1,323 @@
1
+ # MongoDB Read-Only MCP
2
+
3
+ Servidor MCP en Node.js/TypeScript para consultar una o varias conexiones MongoDB desde OpenCode sin exponer herramientas de escritura.
4
+
5
+ ## Capacidades
6
+
7
+ - Lista conexiones configuradas sin mostrar credenciales.
8
+ - Lista bases de datos y colecciones.
9
+ - Ejecuta `find`, `count`, `distinct` y pipelines `aggregate` en modo lectura.
10
+ - Busca un valor en varias colecciones de una base cuando no sabes dónde está la información.
11
+ - Bloquea etapas de agregación de escritura o administración como `$out`, `$merge`, `$currentOp` y `$listSessions`.
12
+ - Limita resultados y tiempo de consulta para evitar respuestas enormes o consultas colgadas.
13
+
14
+ ## Requisitos
15
+
16
+ - Node.js 20 o superior.
17
+ - Acceso de red a MongoDB.
18
+ - Un usuario MongoDB idealmente con permisos de solo lectura (`read` o `readAnyDatabase`).
19
+
20
+ ## Uso con NPX
21
+
22
+ Una vez publicado en npm, se puede consumir sin clonar el repositorio:
23
+
24
+ ```powershell
25
+ npx -y @munlib/mongodb-readonly-mcp
26
+ ```
27
+
28
+ ## Instalación Para Desarrollo
29
+
30
+ ```powershell
31
+ npm install
32
+ npm run build
33
+ ```
34
+
35
+ ## Configuración Segura
36
+
37
+ No guardes credenciales reales en el repositorio. Usa variables de entorno desde OpenCode, tu terminal o un gestor de secretos.
38
+
39
+ ### Una Conexión
40
+
41
+ ```powershell
42
+ $env:MONGO_NAME = "mongoKubernet"
43
+ $env:MONGO_URI = "mongodb://usuario:password@192.168.xx.xx:27017/admin?authSource=admin"
44
+ npm start
45
+ ```
46
+
47
+ ### Varias Conexiones
48
+
49
+ ```powershell
50
+ $env:MONGO_CONNECTIONS = '[{"name":"mongoKubernet","uri":"mongodb://usuario:password@192.168.xx.xx:27017/admin?authSource=admin"},{"name":"local","uri":"mongodb://localhost:27017/admin"}]'
51
+ npm start
52
+ ```
53
+
54
+ También puedes usar una URL larga como la de Studio 3T siempre que sea una URI MongoDB válida:
55
+
56
+ ```text
57
+ mongodb://admin:password@192.168.xx.xx:27017/admin?retryWrites=true&loadBalanced=false&serverSelectionTimeoutMS=5000&connectTimeoutMS=10000&authSource=admin&authMechanism=SCRAM-SHA-1
58
+ ```
59
+
60
+ ## Variables Disponibles
61
+
62
+ | Variable | Descripción | Default |
63
+ | --- | --- | --- |
64
+ | `MONGO_CONNECTIONS` | JSON con múltiples conexiones: `[{"name":"...","uri":"..."}]`. | - |
65
+ | `MONGO_URI` | URI de una sola conexión. | - |
66
+ | `MONGO_NAME` | Nombre lógico para `MONGO_URI`. | `default` |
67
+ | `MONGO_DEFAULT_LIMIT` | Límite por defecto de documentos devueltos. | `25` |
68
+ | `MONGO_MAX_LIMIT` | Límite máximo permitido. | `100` |
69
+ | `MONGO_QUERY_TIMEOUT_MS` | Timeout por consulta. | `15000` |
70
+ | `MONGO_SEARCH_COLLECTION_LIMIT` | Máximo de colecciones escaneadas por búsqueda global. | `50` |
71
+ | `MONGO_SEARCH_FIELDS_LIMIT` | Máximo de campos detectados por colección para búsqueda global. | `30` |
72
+
73
+ ## Configuración en OpenCode
74
+
75
+ Agrega un MCP local en tu `opencode.json` o `opencode.jsonc`. El campo `command` debe ser un arreglo.
76
+
77
+ ### Usando NPM
78
+
79
+ Esta es la forma recomendada para otros equipos o máquinas:
80
+
81
+ ```json
82
+ {
83
+ "$schema": "https://opencode.ai/config.json",
84
+ "mcp": {
85
+ "mongodb-readonly": {
86
+ "type": "local",
87
+ "enabled": true,
88
+ "command": ["npx", "-y", "@munlib/mongodb-readonly-mcp"],
89
+ "env": {
90
+ "MONGO_CONNECTIONS": "[{\"name\":\"mongoKubernet\",\"uri\":\"mongodb://usuario:password@192.168.xx.xx:27017/admin?authSource=admin\"}]",
91
+ "MONGO_DEFAULT_LIMIT": "25",
92
+ "MONGO_MAX_LIMIT": "100",
93
+ "MONGO_QUERY_TIMEOUT_MS": "15000",
94
+ "MONGO_SEARCH_COLLECTION_LIMIT": "50",
95
+ "MONGO_SEARCH_FIELDS_LIMIT": "30"
96
+ }
97
+ }
98
+ }
99
+ }
100
+ ```
101
+
102
+ Después de cambiar `opencode.json`, reinicia OpenCode para que cargue el MCP.
103
+
104
+ ### Usando Build Local
105
+
106
+ Para desarrollo local también puedes apuntar al archivo compilado:
107
+
108
+ ```json
109
+ {
110
+ "$schema": "https://opencode.ai/config.json",
111
+ "mcp": {
112
+ "mongodb-readonly": {
113
+ "type": "local",
114
+ "enabled": true,
115
+ "command": ["node", "C:/ruta/al/proyecto/dist/index.js"],
116
+ "env": {
117
+ "MONGO_CONNECTIONS": "[{\"name\":\"mongoKubernet\",\"uri\":\"mongodb://usuario:password@192.168.xx.xx:27017/admin?authSource=admin\"}]"
118
+ }
119
+ }
120
+ }
121
+ }
122
+ ```
123
+
124
+ ## Herramientas MCP
125
+
126
+ | Herramienta | Uso |
127
+ | --- | --- |
128
+ | `mongo_list_connections` | Ver nombres de conexiones configuradas. |
129
+ | `mongo_list_databases` | Ver bases disponibles en una conexión. |
130
+ | `mongo_list_collections` | Ver colecciones de una base. |
131
+ | `mongo_find_documents` | Consultar documentos con filtro, proyección, orden, límite y skip. |
132
+ | `mongo_count_documents` | Contar documentos por filtro. |
133
+ | `mongo_distinct_values` | Obtener valores distintos de un campo. |
134
+ | `mongo_aggregate_readonly` | Ejecutar agregaciones de lectura. |
135
+ | `mongo_search_database` | Buscar un valor en varias colecciones de una base. |
136
+
137
+ ## Ejemplos de Preguntas en OpenCode
138
+
139
+ - "Lista las bases de datos disponibles en `mongoKubernet`."
140
+ - "En la base `apiLogs`, lista las colecciones."
141
+ - "Busca el id `64f...` en toda la base `backoffice`."
142
+ - "En `ecommerce_mercados_prod.orders`, encuentra documentos donde `customer.email` sea `correo@dominio.com`."
143
+ - "Cuenta documentos en `logs.requests` para el endpoint `/api/login`."
144
+
145
+ ## Garantías y Límites de Solo Lectura
146
+
147
+ Este MCP no expone operaciones `insert`, `update`, `delete`, `drop`, `create`, `renameCollection` ni comandos arbitrarios. Para agregaciones, bloquea etapas conocidas que escriben o exponen información administrativa.
148
+
149
+ La recomendación fuerte es usar además un usuario MongoDB con permisos de solo lectura. La seguridad real debe aplicarse en la base de datos, no solo en el MCP.
150
+
151
+ ## Publicación en NPM
152
+
153
+ El paquete está preparado para publicarse como:
154
+
155
+ ```text
156
+ @munlib/mongodb-readonly-mcp
157
+ ```
158
+
159
+ Antes de publicar, verifica que no existan credenciales en archivos versionados. Los scripts ejecutan `build`, `typecheck`, `npm pack --dry-run` y piden confirmación antes de publicar.
160
+
161
+ ### Windows PowerShell
162
+
163
+ Modo guiado:
164
+
165
+ ```powershell
166
+ npm run publish:npm
167
+ ```
168
+
169
+ Solo validar sin publicar:
170
+
171
+ ```powershell
172
+ npm run publish:dry-run
173
+ ```
174
+
175
+ Publicar una actualización patch:
176
+
177
+ ```powershell
178
+ pwsh -File scripts/publish-npm.ps1 -Bump patch
179
+ ```
180
+
181
+ Si npm exige 2FA al publicar, el script pedirá el código OTP. También puedes pasarlo directamente:
182
+
183
+ ```powershell
184
+ pwsh -File scripts/publish-npm.ps1 -Bump none -Otp 123456
185
+ ```
186
+
187
+ También puedes publicar usando un Access Token de npm sin guardar credenciales en el proyecto:
188
+
189
+ ```powershell
190
+ $env:NPM_TOKEN = "npm_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
191
+ pwsh -File scripts/publish-npm.ps1 -Bump none
192
+ ```
193
+
194
+ ### Linux o macOS
195
+
196
+ Modo guiado:
197
+
198
+ ```bash
199
+ npm run publish:npm:bash
200
+ ```
201
+
202
+ En el modo guiado puedes escribir el número de la opción o el nombre directamente, por ejemplo `1`, `none`, `patch`, `minor`, `major` o `prerelease`.
203
+
204
+ Solo validar sin publicar:
205
+
206
+ ```bash
207
+ npm run publish:npm:bash -- --dry-run
208
+ ```
209
+
210
+ Solo validar sin publicar y sin pedir login:
211
+
212
+ ```bash
213
+ npm run publish:npm:bash -- --bump none --dry-run --skip-login
214
+ ```
215
+
216
+ Primera publicación sin cambiar la versión:
217
+
218
+ ```bash
219
+ npm run publish:npm:bash -- --bump none
220
+ ```
221
+
222
+ Publicar una actualización patch:
223
+
224
+ ```bash
225
+ npm run publish:npm:bash -- --bump patch
226
+ ```
227
+
228
+ Si npm exige 2FA al publicar, el script pedirá el código OTP. También puedes pasarlo directamente:
229
+
230
+ ```bash
231
+ npm run publish:npm:bash -- --bump none --otp 123456
232
+ ```
233
+
234
+ También puedes publicar usando un Access Token de npm sin guardar credenciales en el proyecto:
235
+
236
+ ```bash
237
+ export NPM_TOKEN="npm_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
238
+ npm run publish:npm:bash -- --bump none
239
+ ```
240
+
241
+ ### Comprobación Manual Rápida
242
+
243
+ Si solo quieres revisar el paquete sin iniciar sesión ni publicar:
244
+
245
+ ```powershell
246
+ npm run publish:check
247
+ ```
248
+
249
+ ### Login y Credenciales
250
+
251
+ Los scripts no guardan credenciales. Si no hay sesión activa, ejecutan:
252
+
253
+ ```powershell
254
+ npm login
255
+ ```
256
+
257
+ `npm login` pedirá usuario, contraseña y 2FA si tu cuenta lo tiene activado, o abrirá el navegador según la configuración actual de npm.
258
+
259
+ Si al publicar aparece `E403 Two-factor authentication or granular access token with bypass 2fa enabled is required`, significa que npm requiere un código 2FA para esa publicación. Vuelve a ejecutar el script y escribe el código OTP actual de tu app autenticadora cuando lo solicite.
260
+
261
+ También puedes usar un token granular de npm con permiso de publicación y `bypass 2FA` habilitado, pero para uso manual es más simple usar el OTP.
262
+
263
+ ### Publicar con Access Token de NPM
264
+
265
+ Sí se puede publicar directamente con un Access Token. Es la opción recomendada si tu cuenta u organización exige 2FA y el OTP manual falla.
266
+
267
+ En npm crea un token desde `Access Tokens`. Para publicar paquetes necesitas un token con permisos de publicación sobre `@munlib/mongodb-readonly-mcp` o sobre la organización `munlib`.
268
+
269
+ Recomendaciones para el token:
270
+
271
+ - Usa un token granular si npm te da esa opción.
272
+ - Dale permiso de lectura/escritura solo al paquete u organización necesaria.
273
+ - Habilita `bypass 2FA` si npm lo solicita para publicaciones automatizadas.
274
+ - No pegues el token en `README.md`, `.env`, `opencode.jsonc` ni commits.
275
+ - Si se filtra, revócalo inmediatamente desde npm.
276
+
277
+ Los scripts leen el token desde `NPM_TOKEN`, crean un `.npmrc` temporal fuera del proyecto, publican con ese archivo y lo eliminan al terminar.
278
+
279
+ Windows PowerShell:
280
+
281
+ ```powershell
282
+ $env:NPM_TOKEN = "npm_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
283
+ npm run publish:npm
284
+ ```
285
+
286
+ Linux o macOS:
287
+
288
+ ```bash
289
+ export NPM_TOKEN="npm_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
290
+ npm run publish:npm:bash -- --bump none
291
+ ```
292
+
293
+ Para probar sin publicar:
294
+
295
+ ```bash
296
+ export NPM_TOKEN="npm_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
297
+ npm run publish:npm:bash -- --bump none --dry-run
298
+ ```
299
+
300
+ Si no quieres dejar el token en la sesión después de publicar, elimínalo:
301
+
302
+ ```powershell
303
+ Remove-Item Env:NPM_TOKEN
304
+ ```
305
+
306
+ ```bash
307
+ unset NPM_TOKEN
308
+ ```
309
+
310
+ ### Versiones
311
+
312
+ Para primera publicación, selecciona `none` cuando el script pregunte el tipo de publicación.
313
+
314
+ Para actualizaciones, usa:
315
+
316
+ ```powershell
317
+ patch # 0.1.0 -> 0.1.1
318
+ minor # 0.1.0 -> 0.2.0
319
+ major # 0.1.0 -> 1.0.0
320
+ prerelease # 0.1.0 -> 0.1.1-0
321
+ ```
322
+
323
+ Los scripts usan `npm version --no-git-tag-version`, por lo que actualizan `package.json` y `package-lock.json`, pero no crean commits ni tags automáticamente.
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import "dotenv/config";
package/dist/index.js ADDED
@@ -0,0 +1,436 @@
1
+ #!/usr/bin/env node
2
+ import "dotenv/config";
3
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
+ import { MongoClient, ObjectId } from "mongodb";
6
+ import { z } from "zod";
7
+ const forbiddenAggregationStages = new Set([
8
+ "$out",
9
+ "$merge",
10
+ "$documents",
11
+ "$currentOp",
12
+ "$listSessions",
13
+ "$listLocalSessions",
14
+ ]);
15
+ const jsonObjectSchema = z.record(z.unknown()).default({});
16
+ const config = loadRuntimeConfig();
17
+ const connections = new Map();
18
+ const server = new McpServer({
19
+ name: "mongodb-readonly-mcp",
20
+ version: "0.1.0",
21
+ });
22
+ server.registerTool("mongo_list_connections", {
23
+ title: "List MongoDB connections",
24
+ description: "Lists configured MongoDB connections without exposing credentials.",
25
+ inputSchema: {},
26
+ }, async () => asText({ connections: config.connections.map(({ name }) => ({ name })) }));
27
+ server.registerTool("mongo_list_databases", {
28
+ title: "List MongoDB databases",
29
+ description: "Lists databases available in a configured MongoDB connection.",
30
+ inputSchema: {
31
+ connection: z.string().optional().describe("Connection name. Optional when only one connection is configured."),
32
+ },
33
+ }, async ({ connection }) => {
34
+ const { client, config: selected } = getConnection(connection);
35
+ const result = await client.db("admin").admin().listDatabases({ nameOnly: false });
36
+ return asText({
37
+ connection: selected.name,
38
+ databases: result.databases.map((database) => ({
39
+ name: database.name,
40
+ sizeOnDisk: database.sizeOnDisk,
41
+ empty: database.empty,
42
+ })),
43
+ });
44
+ });
45
+ server.registerTool("mongo_list_collections", {
46
+ title: "List MongoDB collections",
47
+ description: "Lists collections from a database.",
48
+ inputSchema: {
49
+ connection: z.string().optional().describe("Connection name. Optional when only one connection is configured."),
50
+ database: z.string().min(1).describe("Database name."),
51
+ },
52
+ }, async ({ connection, database }) => {
53
+ const { client, config: selected } = getConnection(connection);
54
+ const collections = await client.db(database).listCollections({}, { nameOnly: false }).toArray();
55
+ return asText({
56
+ connection: selected.name,
57
+ database,
58
+ collections: collections.map((collection) => ({
59
+ name: collection.name,
60
+ type: collection.type,
61
+ })),
62
+ });
63
+ });
64
+ server.registerTool("mongo_find_documents", {
65
+ title: "Find MongoDB documents",
66
+ description: "Runs a read-only find query on a collection. Accepts JSON filter, projection and sort objects.",
67
+ inputSchema: {
68
+ connection: z.string().optional().describe("Connection name. Optional when only one connection is configured."),
69
+ database: z.string().min(1),
70
+ collection: z.string().min(1),
71
+ filter: jsonObjectSchema.describe("MongoDB filter object. Example: {\"_id\": \"65...\"}"),
72
+ projection: jsonObjectSchema.optional().describe("MongoDB projection object."),
73
+ sort: jsonObjectSchema.optional().describe("MongoDB sort object."),
74
+ limit: z.number().int().positive().optional().describe("Maximum documents to return."),
75
+ skip: z.number().int().nonnegative().optional().describe("Documents to skip."),
76
+ },
77
+ }, async ({ connection, database, collection, filter, projection, sort, limit, skip }) => {
78
+ const { client, config: selected } = getConnection(connection);
79
+ const safeLimit = normalizeLimit(limit);
80
+ const parsedFilter = normalizeFilter(filter ?? {});
81
+ const options = {
82
+ limit: safeLimit,
83
+ maxTimeMS: config.queryTimeoutMs,
84
+ ...(projection ? { projection: projection } : {}),
85
+ ...(sort ? { sort: sort } : {}),
86
+ ...(skip ? { skip } : {}),
87
+ };
88
+ const docs = await client.db(database).collection(collection).find(parsedFilter, options).toArray();
89
+ return asText({
90
+ connection: selected.name,
91
+ database,
92
+ collection,
93
+ count: docs.length,
94
+ limit: safeLimit,
95
+ documents: sanitizeMongoValues(docs),
96
+ });
97
+ });
98
+ server.registerTool("mongo_count_documents", {
99
+ title: "Count MongoDB documents",
100
+ description: "Counts documents matching a read-only filter in a collection.",
101
+ inputSchema: {
102
+ connection: z.string().optional().describe("Connection name. Optional when only one connection is configured."),
103
+ database: z.string().min(1),
104
+ collection: z.string().min(1),
105
+ filter: jsonObjectSchema.describe("MongoDB filter object."),
106
+ },
107
+ }, async ({ connection, database, collection, filter }) => {
108
+ const { client, config: selected } = getConnection(connection);
109
+ const count = await client
110
+ .db(database)
111
+ .collection(collection)
112
+ .countDocuments(normalizeFilter(filter ?? {}), { maxTimeMS: config.queryTimeoutMs });
113
+ return asText({ connection: selected.name, database, collection, count });
114
+ });
115
+ server.registerTool("mongo_distinct_values", {
116
+ title: "Distinct MongoDB values",
117
+ description: "Returns distinct values for a field using an optional read-only filter.",
118
+ inputSchema: {
119
+ connection: z.string().optional().describe("Connection name. Optional when only one connection is configured."),
120
+ database: z.string().min(1),
121
+ collection: z.string().min(1),
122
+ field: z.string().min(1),
123
+ filter: jsonObjectSchema.describe("MongoDB filter object."),
124
+ limit: z.number().int().positive().optional().describe("Maximum values to return."),
125
+ },
126
+ }, async ({ connection, database, collection, field, filter, limit }) => {
127
+ const { client, config: selected } = getConnection(connection);
128
+ const safeLimit = normalizeLimit(limit);
129
+ const values = await client
130
+ .db(database)
131
+ .collection(collection)
132
+ .distinct(field, normalizeFilter(filter ?? {}), { maxTimeMS: config.queryTimeoutMs });
133
+ return asText({
134
+ connection: selected.name,
135
+ database,
136
+ collection,
137
+ field,
138
+ count: values.length,
139
+ limit: safeLimit,
140
+ values: sanitizeMongoValues(values.slice(0, safeLimit)),
141
+ });
142
+ });
143
+ server.registerTool("mongo_aggregate_readonly", {
144
+ title: "Aggregate MongoDB documents read-only",
145
+ description: "Runs a read-only aggregation pipeline. Write or administrative stages such as $out and $merge are blocked.",
146
+ inputSchema: {
147
+ connection: z.string().optional().describe("Connection name. Optional when only one connection is configured."),
148
+ database: z.string().min(1),
149
+ collection: z.string().min(1),
150
+ pipeline: z.array(z.record(z.unknown())).describe("Aggregation pipeline."),
151
+ limit: z.number().int().positive().optional().describe("Maximum documents to return. A final $limit is added automatically."),
152
+ },
153
+ }, async ({ connection, database, collection, pipeline, limit }) => {
154
+ assertReadonlyPipeline(pipeline);
155
+ const { client, config: selected } = getConnection(connection);
156
+ const safeLimit = normalizeLimit(limit);
157
+ const limitedPipeline = [...pipeline, { $limit: safeLimit }];
158
+ const docs = await client
159
+ .db(database)
160
+ .collection(collection)
161
+ .aggregate(limitedPipeline, { maxTimeMS: config.queryTimeoutMs, allowDiskUse: false })
162
+ .toArray();
163
+ return asText({
164
+ connection: selected.name,
165
+ database,
166
+ collection,
167
+ count: docs.length,
168
+ limit: safeLimit,
169
+ documents: sanitizeMongoValues(docs),
170
+ });
171
+ });
172
+ server.registerTool("mongo_search_database", {
173
+ title: "Search MongoDB database",
174
+ description: "Searches across collections in one database for an ObjectId, exact scalar value, or text in string fields. Useful for investigation when the target collection is unknown.",
175
+ inputSchema: {
176
+ connection: z.string().optional().describe("Connection name. Optional when only one connection is configured."),
177
+ database: z.string().min(1),
178
+ query: z.string().min(1).describe("ObjectId, id, email, text fragment, or any scalar value to search."),
179
+ collections: z.array(z.string().min(1)).optional().describe("Optional collection names. If omitted, collections are discovered."),
180
+ limitPerCollection: z.number().int().positive().optional().describe("Maximum matching documents per collection."),
181
+ },
182
+ }, async ({ connection, database, query, collections: requestedCollections, limitPerCollection }) => {
183
+ const { client, config: selected } = getConnection(connection);
184
+ const db = client.db(database);
185
+ const safeLimit = normalizeLimit(limitPerCollection);
186
+ const collections = requestedCollections?.length
187
+ ? requestedCollections.slice(0, config.searchCollectionLimit)
188
+ : (await db.listCollections({}, { nameOnly: true }).toArray())
189
+ .map((collection) => collection.name)
190
+ .slice(0, config.searchCollectionLimit);
191
+ const results = [];
192
+ for (const collectionName of collections) {
193
+ const collection = db.collection(collectionName);
194
+ const fields = await discoverSearchableFields(collection, config.searchFieldsLimit);
195
+ const filter = buildSearchFilter(query, fields);
196
+ const documents = await collection.find(filter, { limit: safeLimit, maxTimeMS: config.queryTimeoutMs }).toArray();
197
+ if (documents.length > 0) {
198
+ results.push({
199
+ collection: collectionName,
200
+ count: documents.length,
201
+ searchedFields: fields,
202
+ documents: sanitizeMongoValues(documents),
203
+ });
204
+ }
205
+ }
206
+ return asText({
207
+ connection: selected.name,
208
+ database,
209
+ query,
210
+ searchedCollections: collections.length,
211
+ limitPerCollection: safeLimit,
212
+ results,
213
+ });
214
+ });
215
+ async function main() {
216
+ for (const connection of config.connections) {
217
+ const client = new MongoClient(connection.uri, {
218
+ appName: "mongodb-readonly-mcp",
219
+ serverSelectionTimeoutMS: config.queryTimeoutMs,
220
+ connectTimeoutMS: config.queryTimeoutMs,
221
+ });
222
+ await client.connect();
223
+ connections.set(connection.name, { config: connection, client });
224
+ }
225
+ const transport = new StdioServerTransport();
226
+ await server.connect(transport);
227
+ }
228
+ function loadRuntimeConfig() {
229
+ const connections = loadConnections();
230
+ return {
231
+ connections,
232
+ defaultLimit: readPositiveInteger("MONGO_DEFAULT_LIMIT", 25),
233
+ maxLimit: readPositiveInteger("MONGO_MAX_LIMIT", 100),
234
+ queryTimeoutMs: readPositiveInteger("MONGO_QUERY_TIMEOUT_MS", 15_000),
235
+ searchCollectionLimit: readPositiveInteger("MONGO_SEARCH_COLLECTION_LIMIT", 50),
236
+ searchFieldsLimit: readPositiveInteger("MONGO_SEARCH_FIELDS_LIMIT", 30),
237
+ };
238
+ }
239
+ function loadConnections() {
240
+ const rawConnections = process.env.MONGO_CONNECTIONS;
241
+ if (rawConnections) {
242
+ const parsed = JSON.parse(rawConnections);
243
+ const connections = z
244
+ .array(z.object({
245
+ name: z.string().min(1),
246
+ uri: z.string().min(1).startsWith("mongodb"),
247
+ }))
248
+ .min(1)
249
+ .parse(parsed);
250
+ ensureUniqueConnectionNames(connections);
251
+ return connections;
252
+ }
253
+ const uri = process.env.MONGO_URI;
254
+ if (uri) {
255
+ return [{ name: process.env.MONGO_NAME || "default", uri }];
256
+ }
257
+ throw new Error("Configure MONGO_CONNECTIONS or MONGO_URI before starting the MCP server.");
258
+ }
259
+ function ensureUniqueConnectionNames(connections) {
260
+ const seen = new Set();
261
+ for (const connection of connections) {
262
+ if (seen.has(connection.name)) {
263
+ throw new Error(`Duplicate MongoDB connection name: ${connection.name}`);
264
+ }
265
+ seen.add(connection.name);
266
+ }
267
+ }
268
+ function readPositiveInteger(name, fallback) {
269
+ const value = process.env[name];
270
+ if (!value)
271
+ return fallback;
272
+ const parsed = Number(value);
273
+ if (!Number.isInteger(parsed) || parsed <= 0) {
274
+ throw new Error(`${name} must be a positive integer.`);
275
+ }
276
+ return parsed;
277
+ }
278
+ function getConnection(name) {
279
+ if (name) {
280
+ const connection = connections.get(name);
281
+ if (!connection) {
282
+ throw new Error(`Unknown MongoDB connection: ${name}`);
283
+ }
284
+ return connection;
285
+ }
286
+ if (connections.size === 1) {
287
+ const connection = connections.values().next().value;
288
+ if (connection)
289
+ return connection;
290
+ }
291
+ throw new Error("Connection name is required because multiple MongoDB connections are configured.");
292
+ }
293
+ function normalizeLimit(limit) {
294
+ return Math.min(limit ?? config.defaultLimit, config.maxLimit);
295
+ }
296
+ function normalizeFilter(filter) {
297
+ return convertObjectIds(filter);
298
+ }
299
+ function convertObjectIds(value) {
300
+ if (Array.isArray(value)) {
301
+ return value.map(convertObjectIds);
302
+ }
303
+ if (!value || typeof value !== "object") {
304
+ return value;
305
+ }
306
+ const result = {};
307
+ for (const [key, raw] of Object.entries(value)) {
308
+ if ((key === "_id" || key.endsWith("Id") || key.endsWith("_id")) && typeof raw === "string" && ObjectId.isValid(raw)) {
309
+ result[key] = new ObjectId(raw);
310
+ continue;
311
+ }
312
+ result[key] = convertObjectIds(raw);
313
+ }
314
+ return result;
315
+ }
316
+ function assertReadonlyPipeline(pipeline) {
317
+ for (const stage of pipeline) {
318
+ for (const stageName of Object.keys(stage)) {
319
+ if (forbiddenAggregationStages.has(stageName)) {
320
+ throw new Error(`Aggregation stage ${stageName} is not allowed in read-only mode.`);
321
+ }
322
+ }
323
+ assertNoForbiddenNestedStage(stage);
324
+ }
325
+ }
326
+ function assertNoForbiddenNestedStage(value) {
327
+ if (Array.isArray(value)) {
328
+ value.forEach(assertNoForbiddenNestedStage);
329
+ return;
330
+ }
331
+ if (!value || typeof value !== "object")
332
+ return;
333
+ for (const [key, nestedValue] of Object.entries(value)) {
334
+ if (forbiddenAggregationStages.has(key)) {
335
+ throw new Error(`Aggregation stage ${key} is not allowed in read-only mode.`);
336
+ }
337
+ assertNoForbiddenNestedStage(nestedValue);
338
+ }
339
+ }
340
+ async function discoverSearchableFields(collection, limit) {
341
+ const sample = await collection.find({}, { limit: 5, maxTimeMS: config.queryTimeoutMs }).toArray();
342
+ const fields = new Set(["_id"]);
343
+ for (const document of sample) {
344
+ collectScalarFields(document, "", fields, limit);
345
+ if (fields.size >= limit)
346
+ break;
347
+ }
348
+ return [...fields].slice(0, limit);
349
+ }
350
+ function collectScalarFields(value, prefix, fields, limit) {
351
+ if (fields.size >= limit || !value || typeof value !== "object" || Array.isArray(value))
352
+ return;
353
+ for (const [key, nestedValue] of Object.entries(value)) {
354
+ if (fields.size >= limit)
355
+ return;
356
+ const path = prefix ? `${prefix}.${key}` : key;
357
+ if (nestedValue === null || ["string", "number", "boolean"].includes(typeof nestedValue) || nestedValue instanceof Date || nestedValue instanceof ObjectId) {
358
+ fields.add(path);
359
+ continue;
360
+ }
361
+ collectScalarFields(nestedValue, path, fields, limit);
362
+ }
363
+ }
364
+ function buildSearchFilter(query, fields) {
365
+ const or = [];
366
+ if (ObjectId.isValid(query)) {
367
+ or.push({ _id: new ObjectId(query) });
368
+ }
369
+ or.push({ _id: query });
370
+ const numeric = Number(query);
371
+ const hasNumericValue = query.trim() !== "" && Number.isFinite(numeric);
372
+ const escaped = escapeRegex(query);
373
+ for (const field of fields.filter((field) => field !== "_id")) {
374
+ or.push({ [field]: query });
375
+ or.push({ [field]: { $regex: escaped, $options: "i" } });
376
+ if (hasNumericValue) {
377
+ or.push({ [field]: numeric });
378
+ }
379
+ }
380
+ return { $or: or };
381
+ }
382
+ function escapeRegex(value) {
383
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
384
+ }
385
+ function sanitizeMongoValues(value) {
386
+ if (value === null || ["boolean", "number", "string"].includes(typeof value)) {
387
+ return value;
388
+ }
389
+ if (value instanceof ObjectId) {
390
+ return value.toHexString();
391
+ }
392
+ if (value instanceof Date) {
393
+ return value.toISOString();
394
+ }
395
+ if (Array.isArray(value)) {
396
+ return value.map(sanitizeMongoValues);
397
+ }
398
+ if (value && typeof value === "object") {
399
+ const result = {};
400
+ for (const [key, nestedValue] of Object.entries(value)) {
401
+ result[key] = sanitizeMongoValues(nestedValue);
402
+ }
403
+ return result;
404
+ }
405
+ return String(value);
406
+ }
407
+ function asText(value) {
408
+ return {
409
+ content: [
410
+ {
411
+ type: "text",
412
+ text: JSON.stringify(value, null, 2),
413
+ },
414
+ ],
415
+ };
416
+ }
417
+ process.on("SIGINT", async () => {
418
+ await closeConnections();
419
+ process.exit(0);
420
+ });
421
+ process.on("SIGTERM", async () => {
422
+ await closeConnections();
423
+ process.exit(0);
424
+ });
425
+ async function closeConnections() {
426
+ await Promise.all([...connections.values()].map(({ client }) => client.close(true)));
427
+ }
428
+ main().catch((error) => {
429
+ const message = error instanceof Error ? error.message : String(error);
430
+ console.error(redactMongoUris(message));
431
+ process.exit(1);
432
+ });
433
+ function redactMongoUris(message) {
434
+ return message.replace(/mongodb(?:\+srv)?:\/\/[^\s"']+/gi, "mongodb://<redacted>");
435
+ }
436
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@munlib/mongodb-readonly-mcp",
3
+ "version": "0.1.0",
4
+ "description": "Read-only MCP server for querying one or more MongoDB connections.",
5
+ "type": "module",
6
+ "bin": {
7
+ "mongodb-readonly-mcp": "dist/index.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc -p tsconfig.json",
11
+ "dev": "tsx src/index.ts",
12
+ "publish:check": "npm run build && npm run typecheck && npm pack --dry-run",
13
+ "publish:dry-run": "pwsh -File scripts/publish-npm.ps1 -Bump none -DryRun -SkipLogin",
14
+ "publish:npm": "pwsh -File scripts/publish-npm.ps1",
15
+ "publish:npm:bash": "bash scripts/publish-npm.sh",
16
+ "prepublishOnly": "npm run build && npm run typecheck",
17
+ "start": "node dist/index.js",
18
+ "typecheck": "tsc -p tsconfig.json --noEmit"
19
+ },
20
+ "files": [
21
+ "dist",
22
+ "README.md",
23
+ "package.json"
24
+ ],
25
+ "publishConfig": {
26
+ "access": "public"
27
+ },
28
+ "keywords": [
29
+ "mcp",
30
+ "mongodb",
31
+ "readonly",
32
+ "model-context-protocol"
33
+ ],
34
+ "license": "MIT",
35
+ "engines": {
36
+ "node": ">=20"
37
+ },
38
+ "dependencies": {
39
+ "@modelcontextprotocol/sdk": "^1.17.5",
40
+ "dotenv": "^16.4.7",
41
+ "mongodb": "^6.12.0",
42
+ "zod": "^3.24.1"
43
+ },
44
+ "devDependencies": {
45
+ "@types/node": "^22.10.7",
46
+ "tsx": "^4.19.2",
47
+ "typescript": "^5.7.3"
48
+ }
49
+ }