mongodb-mcp-server 0.1.1 → 0.1.2
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/.dockerignore +11 -0
- package/.github/workflows/check-pr-title.yml +29 -0
- package/.github/workflows/docker.yaml +57 -0
- package/.github/workflows/stale.yml +32 -0
- package/.smithery/Dockerfile +30 -0
- package/.smithery/smithery.yaml +63 -0
- package/CONTRIBUTING.md +1 -1
- package/Dockerfile +10 -0
- package/README.md +135 -14
- package/dist/common/atlas/apiClient.js +10 -1
- package/dist/common/atlas/apiClient.js.map +1 -1
- package/dist/common/atlas/cluster.js +1 -1
- package/dist/common/atlas/cluster.js.map +1 -1
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -1
- package/dist/logger.js +5 -0
- package/dist/logger.js.map +1 -1
- package/dist/server.js +1 -1
- package/dist/server.js.map +1 -1
- package/dist/telemetry/telemetry.js +115 -78
- package/dist/telemetry/telemetry.js.map +1 -1
- package/dist/tools/atlas/create/createProject.js +5 -1
- package/dist/tools/atlas/create/createProject.js.map +1 -1
- package/dist/tools/atlas/read/listAlerts.js +41 -0
- package/dist/tools/atlas/read/listAlerts.js.map +1 -0
- package/dist/tools/atlas/read/listProjects.js +3 -1
- package/dist/tools/atlas/read/listProjects.js.map +1 -1
- package/dist/tools/atlas/tools.js +2 -0
- package/dist/tools/atlas/tools.js.map +1 -1
- package/dist/tools/mongodb/metadata/listDatabases.js.map +1 -1
- package/dist/tools/mongodb/read/count.js +2 -2
- package/dist/tools/mongodb/read/count.js.map +1 -1
- package/dist/tools/tool.js +38 -6
- package/dist/tools/tool.js.map +1 -1
- package/package.json +8 -8
- package/scripts/apply.ts +4 -4
- package/scripts/filter.ts +1 -0
- package/src/common/atlas/apiClient.ts +11 -1
- package/src/common/atlas/cluster.ts +1 -2
- package/src/common/atlas/openapi.d.ts +1242 -28
- package/src/index.ts +20 -0
- package/src/logger.ts +6 -0
- package/src/server.ts +1 -1
- package/src/telemetry/telemetry.ts +150 -98
- package/src/telemetry/types.ts +1 -0
- package/src/tools/atlas/create/createProject.ts +7 -1
- package/src/tools/atlas/read/listAlerts.ts +45 -0
- package/src/tools/atlas/read/listProjects.ts +4 -2
- package/src/tools/atlas/tools.ts +2 -0
- package/src/tools/mongodb/metadata/listDatabases.ts +0 -1
- package/src/tools/mongodb/read/count.ts +3 -2
- package/src/tools/tool.ts +45 -8
- package/tests/integration/helpers.ts +23 -0
- package/tests/integration/tools/atlas/accessLists.test.ts +2 -2
- package/tests/integration/tools/atlas/alerts.test.ts +42 -0
- package/tests/integration/tools/atlas/atlasHelpers.ts +5 -3
- package/tests/integration/tools/atlas/clusters.test.ts +4 -4
- package/tests/integration/tools/atlas/dbUsers.test.ts +7 -7
- package/tests/integration/tools/atlas/orgs.test.ts +2 -2
- package/tests/integration/tools/atlas/projects.test.ts +3 -3
- package/tests/integration/tools/mongodb/create/createCollection.test.ts +2 -2
- package/tests/integration/tools/mongodb/create/createIndex.test.ts +2 -2
- package/tests/integration/tools/mongodb/create/insertMany.test.ts +1 -1
- package/tests/integration/tools/mongodb/delete/dropCollection.test.ts +1 -1
- package/tests/integration/tools/mongodb/metadata/collectionSchema.test.ts +2 -2
- package/tests/integration/tools/mongodb/metadata/dbStats.test.ts +4 -4
- package/tests/integration/tools/mongodb/metadata/explain.test.ts +10 -10
- package/tests/integration/tools/mongodb/metadata/listCollections.test.ts +1 -1
- package/tests/integration/tools/mongodb/metadata/listDatabases.test.ts +9 -5
- package/tests/integration/tools/mongodb/metadata/logs.test.ts +4 -4
- package/tests/integration/tools/mongodb/read/aggregate.test.ts +22 -7
- package/tests/integration/tools/mongodb/read/collectionIndexes.test.ts +5 -5
- package/tests/integration/tools/mongodb/read/count.test.ts +15 -10
- package/tests/integration/tools/mongodb/read/find.test.ts +6 -6
- package/tests/integration/tools/mongodb/update/renameCollection.test.ts +4 -4
- package/tests/unit/EJsonTransport.test.ts +1 -1
- package/tests/unit/session.test.ts +1 -1
- package/tests/unit/telemetry.test.ts +106 -58
- package/tsconfig.build.json +1 -0
- package/tests/integration/telemetry.test.ts +0 -28
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ApiClient } from "../../src/common/atlas/apiClient.js";
|
|
2
2
|
import { Session } from "../../src/session.js";
|
|
3
|
-
import {
|
|
3
|
+
import { Telemetry } from "../../src/telemetry/telemetry.js";
|
|
4
4
|
import { BaseEvent, TelemetryResult } from "../../src/telemetry/types.js";
|
|
5
5
|
import { EventCache } from "../../src/telemetry/eventCache.js";
|
|
6
6
|
import { config } from "../../src/config.js";
|
|
@@ -16,6 +16,8 @@ const MockApiClient = ApiClient as jest.MockedClass<typeof ApiClient>;
|
|
|
16
16
|
jest.mock("../../src/telemetry/eventCache.js");
|
|
17
17
|
const MockEventCache = EventCache as jest.MockedClass<typeof EventCache>;
|
|
18
18
|
|
|
19
|
+
const nextTick = () => new Promise((resolve) => process.nextTick(resolve));
|
|
20
|
+
|
|
19
21
|
describe("Telemetry", () => {
|
|
20
22
|
const machineId = "test-machine-id";
|
|
21
23
|
const hashedMachineId = createHmac("sha256", machineId.toUpperCase()).update("atlascli").digest("hex");
|
|
@@ -24,6 +26,11 @@ describe("Telemetry", () => {
|
|
|
24
26
|
let mockEventCache: jest.Mocked<EventCache>;
|
|
25
27
|
let session: Session;
|
|
26
28
|
let telemetry: Telemetry;
|
|
29
|
+
let telemetryConfig: {
|
|
30
|
+
eventCache: EventCache;
|
|
31
|
+
getRawMachineId: () => Promise<string>;
|
|
32
|
+
getContainerEnv: () => Promise<boolean>;
|
|
33
|
+
};
|
|
27
34
|
|
|
28
35
|
// Helper function to create properly typed test events
|
|
29
36
|
function createTestEvent(options?: {
|
|
@@ -77,19 +84,11 @@ describe("Telemetry", () => {
|
|
|
77
84
|
expect(appendEvents.length).toBe(appendEventsCalls);
|
|
78
85
|
|
|
79
86
|
if (sendEventsCalledWith) {
|
|
80
|
-
expect(sendEvents[0]?.[0]).
|
|
81
|
-
sendEventsCalledWith.map((event) => ({
|
|
82
|
-
...event,
|
|
83
|
-
properties: {
|
|
84
|
-
...telemetry.getCommonProperties(),
|
|
85
|
-
...event.properties,
|
|
86
|
-
},
|
|
87
|
-
}))
|
|
88
|
-
);
|
|
87
|
+
expect(sendEvents[0]?.[0]).toMatchObject(sendEventsCalledWith);
|
|
89
88
|
}
|
|
90
89
|
|
|
91
90
|
if (appendEventsCalledWith) {
|
|
92
|
-
expect(appendEvents[0]?.[0]).
|
|
91
|
+
expect(appendEvents[0]?.[0]).toMatchObject(appendEventsCalledWith);
|
|
93
92
|
}
|
|
94
93
|
}
|
|
95
94
|
|
|
@@ -125,10 +124,13 @@ describe("Telemetry", () => {
|
|
|
125
124
|
setAgentRunner: jest.fn().mockResolvedValue(undefined),
|
|
126
125
|
} as unknown as Session;
|
|
127
126
|
|
|
128
|
-
|
|
127
|
+
telemetryConfig = {
|
|
129
128
|
eventCache: mockEventCache,
|
|
130
129
|
getRawMachineId: () => Promise.resolve(machineId),
|
|
131
|
-
|
|
130
|
+
getContainerEnv: () => Promise.resolve(false),
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
telemetry = Telemetry.create(session, config, telemetryConfig);
|
|
132
134
|
|
|
133
135
|
config.telemetry = "enabled";
|
|
134
136
|
});
|
|
@@ -138,7 +140,8 @@ describe("Telemetry", () => {
|
|
|
138
140
|
it("should send events successfully", async () => {
|
|
139
141
|
const testEvent = createTestEvent();
|
|
140
142
|
|
|
141
|
-
|
|
143
|
+
telemetry.emitEvents([testEvent]);
|
|
144
|
+
await nextTick(); // wait for the event to be sent
|
|
142
145
|
|
|
143
146
|
verifyMockCalls({
|
|
144
147
|
sendEventsCalls: 1,
|
|
@@ -152,7 +155,8 @@ describe("Telemetry", () => {
|
|
|
152
155
|
|
|
153
156
|
const testEvent = createTestEvent();
|
|
154
157
|
|
|
155
|
-
|
|
158
|
+
telemetry.emitEvents([testEvent]);
|
|
159
|
+
await nextTick(); // wait for the event to be sent
|
|
156
160
|
|
|
157
161
|
verifyMockCalls({
|
|
158
162
|
sendEventsCalls: 1,
|
|
@@ -175,7 +179,8 @@ describe("Telemetry", () => {
|
|
|
175
179
|
// Set up mock to return cached events
|
|
176
180
|
mockEventCache.getEvents.mockReturnValueOnce([cachedEvent]);
|
|
177
181
|
|
|
178
|
-
|
|
182
|
+
telemetry.emitEvents([newEvent]);
|
|
183
|
+
await nextTick(); // wait for the event to be sent
|
|
179
184
|
|
|
180
185
|
verifyMockCalls({
|
|
181
186
|
sendEventsCalls: 1,
|
|
@@ -184,9 +189,7 @@ describe("Telemetry", () => {
|
|
|
184
189
|
});
|
|
185
190
|
});
|
|
186
191
|
|
|
187
|
-
it("should correctly add common properties to events", () => {
|
|
188
|
-
const commonProps = telemetry.getCommonProperties();
|
|
189
|
-
|
|
192
|
+
it("should correctly add common properties to events", async () => {
|
|
190
193
|
// Use explicit type assertion
|
|
191
194
|
const expectedProps: Record<string, string> = {
|
|
192
195
|
mcp_client_version: "1.0.0",
|
|
@@ -197,48 +200,86 @@ describe("Telemetry", () => {
|
|
|
197
200
|
device_id: hashedMachineId,
|
|
198
201
|
};
|
|
199
202
|
|
|
200
|
-
|
|
203
|
+
const testEvent = createTestEvent();
|
|
204
|
+
|
|
205
|
+
telemetry.emitEvents([testEvent]);
|
|
206
|
+
await nextTick(); // wait for the event to be sent
|
|
207
|
+
|
|
208
|
+
const checkEvent = {
|
|
209
|
+
...testEvent,
|
|
210
|
+
properties: {
|
|
211
|
+
...testEvent.properties,
|
|
212
|
+
...expectedProps,
|
|
213
|
+
},
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
verifyMockCalls({
|
|
217
|
+
sendEventsCalls: 1,
|
|
218
|
+
clearEventsCalls: 1,
|
|
219
|
+
sendEventsCalledWith: [checkEvent],
|
|
220
|
+
});
|
|
201
221
|
});
|
|
202
222
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
223
|
+
it("should send cache new event while sending another event", async () => {
|
|
224
|
+
const newEvent = createTestEvent({
|
|
225
|
+
command: "new-command",
|
|
226
|
+
component: "new-component",
|
|
207
227
|
});
|
|
208
228
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
229
|
+
const newEvent2 = createTestEvent({
|
|
230
|
+
command: "new-command-2",
|
|
231
|
+
component: "new-component-2",
|
|
212
232
|
});
|
|
213
233
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
getRawMachineId: () => Promise.resolve(machineId),
|
|
217
|
-
});
|
|
234
|
+
telemetry.emitEvents([newEvent]);
|
|
235
|
+
telemetry.emitEvents([newEvent2]);
|
|
218
236
|
|
|
219
|
-
|
|
220
|
-
expect(telemetry.getCommonProperties().device_id).toBe(undefined);
|
|
237
|
+
await nextTick(); // wait for the event to be sent
|
|
221
238
|
|
|
222
|
-
|
|
239
|
+
verifyMockCalls({
|
|
240
|
+
sendEventsCalls: 1,
|
|
241
|
+
clearEventsCalls: 1,
|
|
242
|
+
appendEventsCalls: 1,
|
|
243
|
+
sendEventsCalledWith: [newEvent],
|
|
244
|
+
appendEventsCalledWith: [newEvent2],
|
|
245
|
+
});
|
|
246
|
+
});
|
|
223
247
|
|
|
224
|
-
|
|
225
|
-
|
|
248
|
+
describe("machine ID resolution", () => {
|
|
249
|
+
it("should successfully resolve the machine ID", async () => {
|
|
250
|
+
const testEvent = createTestEvent();
|
|
251
|
+
|
|
252
|
+
telemetry.emitEvents([testEvent]);
|
|
253
|
+
await nextTick(); // wait for the event to be sent
|
|
254
|
+
|
|
255
|
+
const checkEvent = {
|
|
256
|
+
...testEvent,
|
|
257
|
+
properties: {
|
|
258
|
+
...testEvent.properties,
|
|
259
|
+
device_id: hashedMachineId,
|
|
260
|
+
},
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
verifyMockCalls({
|
|
264
|
+
sendEventsCalls: 1,
|
|
265
|
+
clearEventsCalls: 1,
|
|
266
|
+
sendEventsCalledWith: [checkEvent],
|
|
267
|
+
});
|
|
226
268
|
});
|
|
227
269
|
|
|
228
270
|
it("should handle machine ID resolution failure", async () => {
|
|
229
271
|
const loggerSpy = jest.spyOn(logger, "debug");
|
|
230
272
|
|
|
231
273
|
telemetry = Telemetry.create(session, config, {
|
|
274
|
+
...telemetryConfig,
|
|
232
275
|
getRawMachineId: () => Promise.reject(new Error("Failed to get device ID")),
|
|
233
276
|
});
|
|
234
277
|
|
|
235
|
-
|
|
236
|
-
expect(telemetry.getCommonProperties().device_id).toBe(undefined);
|
|
278
|
+
const testEvent = createTestEvent();
|
|
237
279
|
|
|
238
|
-
|
|
280
|
+
telemetry.emitEvents([testEvent]);
|
|
239
281
|
|
|
240
|
-
|
|
241
|
-
expect(telemetry.getCommonProperties().device_id).toBe("unknown");
|
|
282
|
+
await nextTick(); // wait for the event to be sent
|
|
242
283
|
|
|
243
284
|
expect(loggerSpy).toHaveBeenCalledWith(
|
|
244
285
|
LogId.telemetryDeviceIdFailure,
|
|
@@ -247,27 +288,28 @@ describe("Telemetry", () => {
|
|
|
247
288
|
);
|
|
248
289
|
});
|
|
249
290
|
|
|
250
|
-
it("should timeout if machine ID resolution takes too long",
|
|
291
|
+
it("should timeout if machine ID resolution takes too long", () => {
|
|
251
292
|
const loggerSpy = jest.spyOn(logger, "debug");
|
|
252
293
|
|
|
253
|
-
|
|
294
|
+
jest.useFakeTimers();
|
|
254
295
|
|
|
255
|
-
|
|
256
|
-
|
|
296
|
+
telemetry = Telemetry.create(session, config, {
|
|
297
|
+
...telemetryConfig,
|
|
298
|
+
getRawMachineId: () => new Promise(() => {}), // Never resolves
|
|
299
|
+
});
|
|
257
300
|
|
|
258
|
-
|
|
301
|
+
const testEvent = createTestEvent();
|
|
259
302
|
|
|
260
|
-
|
|
261
|
-
expect(telemetry["isBufferingEvents"]).toBe(true);
|
|
262
|
-
expect(telemetry.getCommonProperties().device_id).toBe(undefined);
|
|
303
|
+
telemetry.emitEvents([testEvent]);
|
|
263
304
|
|
|
264
|
-
jest.advanceTimersByTime(
|
|
305
|
+
jest.advanceTimersByTime(5000);
|
|
265
306
|
|
|
266
|
-
|
|
307
|
+
jest.useRealTimers();
|
|
267
308
|
|
|
268
|
-
expect(
|
|
269
|
-
|
|
270
|
-
expect(loggerSpy).
|
|
309
|
+
expect(loggerSpy).toHaveBeenCalledTimes(2);
|
|
310
|
+
|
|
311
|
+
expect(loggerSpy).toHaveBeenNthCalledWith(
|
|
312
|
+
2,
|
|
271
313
|
LogId.telemetryDeviceIdTimeout,
|
|
272
314
|
"telemetry",
|
|
273
315
|
"Device ID retrieval timed out"
|
|
@@ -288,9 +330,12 @@ describe("Telemetry", () => {
|
|
|
288
330
|
it("should not send events", async () => {
|
|
289
331
|
const testEvent = createTestEvent();
|
|
290
332
|
|
|
291
|
-
|
|
333
|
+
telemetry.emitEvents([testEvent]);
|
|
334
|
+
await nextTick(); // wait for the event to be sent
|
|
292
335
|
|
|
293
|
-
verifyMockCalls(
|
|
336
|
+
verifyMockCalls({
|
|
337
|
+
sendEventsCalls: 0,
|
|
338
|
+
});
|
|
294
339
|
});
|
|
295
340
|
});
|
|
296
341
|
|
|
@@ -313,9 +358,12 @@ describe("Telemetry", () => {
|
|
|
313
358
|
it("should not send events", async () => {
|
|
314
359
|
const testEvent = createTestEvent();
|
|
315
360
|
|
|
316
|
-
|
|
361
|
+
telemetry.emitEvents([testEvent]);
|
|
362
|
+
await nextTick(); // wait for the event to be sent
|
|
317
363
|
|
|
318
|
-
verifyMockCalls(
|
|
364
|
+
verifyMockCalls({
|
|
365
|
+
sendEventsCalls: 0,
|
|
366
|
+
});
|
|
319
367
|
});
|
|
320
368
|
});
|
|
321
369
|
});
|
package/tsconfig.build.json
CHANGED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { createHmac } from "crypto";
|
|
2
|
-
import { Telemetry } from "../../src/telemetry/telemetry.js";
|
|
3
|
-
import { Session } from "../../src/session.js";
|
|
4
|
-
import { config } from "../../src/config.js";
|
|
5
|
-
import nodeMachineId from "node-machine-id";
|
|
6
|
-
|
|
7
|
-
describe("Telemetry", () => {
|
|
8
|
-
it("should resolve the actual machine ID", async () => {
|
|
9
|
-
const actualId: string = await nodeMachineId.machineId(true);
|
|
10
|
-
|
|
11
|
-
const actualHashedId = createHmac("sha256", actualId.toUpperCase()).update("atlascli").digest("hex");
|
|
12
|
-
|
|
13
|
-
const telemetry = Telemetry.create(
|
|
14
|
-
new Session({
|
|
15
|
-
apiBaseUrl: "",
|
|
16
|
-
}),
|
|
17
|
-
config
|
|
18
|
-
);
|
|
19
|
-
|
|
20
|
-
expect(telemetry.getCommonProperties().device_id).toBe(undefined);
|
|
21
|
-
expect(telemetry["isBufferingEvents"]).toBe(true);
|
|
22
|
-
|
|
23
|
-
await telemetry.deviceIdPromise;
|
|
24
|
-
|
|
25
|
-
expect(telemetry.getCommonProperties().device_id).toBe(actualHashedId);
|
|
26
|
-
expect(telemetry["isBufferingEvents"]).toBe(false);
|
|
27
|
-
});
|
|
28
|
-
});
|