mongodb-mcp-server 0.0.4 → 0.0.5
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/.github/workflows/code_health.yaml +53 -4
- package/.github/workflows/prepare_release.yaml +4 -4
- package/README.md +2 -0
- package/dist/common/atlas/apiClient.js +30 -2
- package/dist/common/atlas/apiClient.js.map +1 -1
- package/dist/config.js +2 -7
- package/dist/config.js.map +1 -1
- package/dist/index.js +10 -4
- package/dist/index.js.map +1 -1
- package/dist/logger.js +52 -27
- package/dist/logger.js.map +1 -1
- package/dist/packageInfo.js +6 -0
- package/dist/packageInfo.js.map +1 -0
- package/dist/server.js +71 -8
- package/dist/server.js.map +1 -1
- package/dist/session.js +17 -12
- package/dist/session.js.map +1 -1
- package/dist/telemetry/constants.js +15 -0
- package/dist/telemetry/constants.js.map +1 -0
- package/dist/telemetry/eventCache.js +53 -0
- package/dist/telemetry/eventCache.js.map +1 -0
- package/dist/telemetry/telemetry.js +96 -0
- package/dist/telemetry/telemetry.js.map +1 -0
- package/dist/telemetry/types.js +2 -0
- package/dist/telemetry/types.js.map +1 -0
- package/dist/tools/atlas/atlasTool.js +8 -3
- package/dist/tools/atlas/atlasTool.js.map +1 -1
- package/dist/tools/atlas/createAccessList.js +0 -1
- package/dist/tools/atlas/createAccessList.js.map +1 -1
- package/dist/tools/atlas/createDBUser.js +0 -1
- package/dist/tools/atlas/createDBUser.js.map +1 -1
- package/dist/tools/atlas/createFreeCluster.js +0 -1
- package/dist/tools/atlas/createFreeCluster.js.map +1 -1
- package/dist/tools/atlas/createProject.js +0 -1
- package/dist/tools/atlas/createProject.js.map +1 -1
- package/dist/tools/atlas/inspectAccessList.js +0 -1
- package/dist/tools/atlas/inspectAccessList.js.map +1 -1
- package/dist/tools/atlas/inspectCluster.js +0 -1
- package/dist/tools/atlas/inspectCluster.js.map +1 -1
- package/dist/tools/atlas/listClusters.js +0 -1
- package/dist/tools/atlas/listClusters.js.map +1 -1
- package/dist/tools/atlas/listDBUsers.js +0 -1
- package/dist/tools/atlas/listDBUsers.js.map +1 -1
- package/dist/tools/atlas/listOrgs.js +0 -1
- package/dist/tools/atlas/listOrgs.js.map +1 -1
- package/dist/tools/atlas/listProjects.js +10 -4
- package/dist/tools/atlas/listProjects.js.map +1 -1
- package/dist/tools/mongodb/metadata/collectionSchema.js +15 -13
- package/dist/tools/mongodb/metadata/collectionSchema.js.map +1 -1
- package/dist/tools/mongodb/metadata/collectionStorageSize.js +32 -3
- package/dist/tools/mongodb/metadata/collectionStorageSize.js.map +1 -1
- package/dist/tools/mongodb/metadata/connect.js +5 -6
- package/dist/tools/mongodb/metadata/connect.js.map +1 -1
- package/dist/tools/mongodb/metadata/dbStats.js +6 -1
- package/dist/tools/mongodb/metadata/dbStats.js.map +1 -1
- package/dist/tools/mongodb/metadata/explain.js +14 -7
- package/dist/tools/mongodb/metadata/explain.js.map +1 -1
- package/dist/tools/mongodb/metadata/logs.js +45 -0
- package/dist/tools/mongodb/metadata/logs.js.map +1 -0
- package/dist/tools/mongodb/mongodbTool.js +10 -11
- package/dist/tools/mongodb/mongodbTool.js.map +1 -1
- package/dist/tools/mongodb/read/aggregate.js +3 -3
- package/dist/tools/mongodb/read/aggregate.js.map +1 -1
- package/dist/tools/mongodb/read/collectionIndexes.js +24 -5
- package/dist/tools/mongodb/read/collectionIndexes.js.map +1 -1
- package/dist/tools/mongodb/read/find.js +3 -2
- package/dist/tools/mongodb/read/find.js.map +1 -1
- package/dist/tools/mongodb/tools.js +2 -0
- package/dist/tools/mongodb/tools.js.map +1 -1
- package/dist/tools/mongodb/update/renameCollection.js +28 -4
- package/dist/tools/mongodb/update/renameCollection.js.map +1 -1
- package/dist/tools/mongodb/update/updateMany.js +5 -7
- package/dist/tools/mongodb/update/updateMany.js.map +1 -1
- package/dist/tools/tool.js +39 -10
- package/dist/tools/tool.js.map +1 -1
- package/eslint.config.js +29 -10
- package/global.d.ts +1 -0
- package/package.json +6 -2
- package/scripts/apply.ts +9 -7
- package/scripts/filter.ts +3 -2
- package/src/common/atlas/apiClient.ts +48 -7
- package/src/config.ts +4 -10
- package/src/index.ts +10 -6
- package/src/logger.ts +66 -28
- package/src/packageInfo.ts +6 -0
- package/src/server.ts +103 -9
- package/src/session.ts +34 -17
- package/src/telemetry/constants.ts +15 -0
- package/src/telemetry/eventCache.ts +62 -0
- package/src/telemetry/telemetry.ts +137 -0
- package/src/telemetry/types.ts +60 -0
- package/src/tools/atlas/atlasTool.ts +7 -5
- package/src/tools/atlas/createAccessList.ts +0 -2
- package/src/tools/atlas/createDBUser.ts +0 -2
- package/src/tools/atlas/createFreeCluster.ts +0 -2
- package/src/tools/atlas/createProject.ts +0 -1
- package/src/tools/atlas/inspectAccessList.ts +0 -2
- package/src/tools/atlas/inspectCluster.ts +0 -2
- package/src/tools/atlas/listClusters.ts +0 -2
- package/src/tools/atlas/listDBUsers.ts +0 -2
- package/src/tools/atlas/listOrgs.ts +0 -2
- package/src/tools/atlas/listProjects.ts +12 -4
- package/src/tools/mongodb/metadata/collectionSchema.ts +16 -14
- package/src/tools/mongodb/metadata/collectionStorageSize.ts +41 -3
- package/src/tools/mongodb/metadata/connect.ts +5 -6
- package/src/tools/mongodb/metadata/dbStats.ts +6 -1
- package/src/tools/mongodb/metadata/explain.ts +20 -7
- package/src/tools/mongodb/metadata/logs.ts +55 -0
- package/src/tools/mongodb/mongodbTool.ts +12 -15
- package/src/tools/mongodb/read/aggregate.ts +3 -3
- package/src/tools/mongodb/read/collectionIndexes.ts +29 -5
- package/src/tools/mongodb/read/find.ts +3 -2
- package/src/tools/mongodb/tools.ts +2 -0
- package/src/tools/mongodb/update/renameCollection.ts +33 -4
- package/src/tools/mongodb/update/updateMany.ts +5 -7
- package/src/tools/tool.ts +51 -15
- package/tests/integration/helpers.ts +84 -107
- package/tests/integration/inMemoryTransport.ts +3 -2
- package/tests/integration/server.test.ts +47 -21
- package/tests/integration/tools/atlas/accessLists.test.ts +13 -15
- package/tests/integration/tools/atlas/atlasHelpers.ts +3 -8
- package/tests/integration/tools/atlas/clusters.test.ts +12 -13
- package/tests/integration/tools/atlas/dbUsers.test.ts +9 -9
- package/tests/integration/tools/atlas/orgs.test.ts +4 -4
- package/tests/integration/tools/atlas/projects.test.ts +10 -12
- package/tests/integration/tools/mongodb/create/createCollection.test.ts +19 -62
- package/tests/integration/tools/mongodb/create/createIndex.test.ts +41 -87
- package/tests/integration/tools/mongodb/create/insertMany.test.ts +35 -78
- package/tests/integration/tools/mongodb/delete/deleteMany.test.ts +25 -62
- package/tests/integration/tools/mongodb/delete/dropCollection.test.ts +22 -71
- package/tests/integration/tools/mongodb/delete/dropDatabase.test.ts +29 -63
- package/tests/integration/tools/mongodb/metadata/collectionSchema.test.ts +154 -0
- package/tests/integration/tools/mongodb/metadata/collectionStorageSize.test.ts +86 -0
- package/tests/integration/tools/mongodb/metadata/connect.test.ts +33 -23
- package/tests/integration/tools/mongodb/metadata/dbStats.test.ts +104 -0
- package/tests/integration/tools/mongodb/metadata/explain.test.ts +171 -0
- package/tests/integration/tools/mongodb/metadata/listCollections.test.ts +28 -56
- package/tests/integration/tools/mongodb/metadata/listDatabases.test.ts +32 -26
- package/tests/integration/tools/mongodb/metadata/logs.test.ts +83 -0
- package/tests/integration/tools/mongodb/mongodbHelpers.ts +165 -0
- package/tests/integration/tools/mongodb/read/aggregate.test.ts +99 -0
- package/tests/integration/tools/mongodb/read/collectionIndexes.test.ts +99 -0
- package/tests/integration/tools/mongodb/read/count.test.ts +31 -79
- package/tests/integration/tools/mongodb/read/find.test.ts +182 -0
- package/tests/integration/tools/mongodb/update/renameCollection.test.ts +194 -0
- package/tests/integration/tools/mongodb/update/updateMany.test.ts +238 -0
- package/tsconfig.jest.json +2 -1
- package/tsconfig.json +1 -1
- package/tsconfig.lint.json +8 -0
package/src/session.ts
CHANGED
|
@@ -1,26 +1,43 @@
|
|
|
1
1
|
import { NodeDriverServiceProvider } from "@mongosh/service-provider-node-driver";
|
|
2
|
-
import { ApiClient } from "./common/atlas/apiClient.js";
|
|
3
|
-
import
|
|
2
|
+
import { ApiClient, ApiClientCredentials } from "./common/atlas/apiClient.js";
|
|
3
|
+
import { Implementation } from "@modelcontextprotocol/sdk/types.js";
|
|
4
|
+
|
|
5
|
+
export interface SessionOptions {
|
|
6
|
+
apiBaseUrl?: string;
|
|
7
|
+
apiClientId?: string;
|
|
8
|
+
apiClientSecret?: string;
|
|
9
|
+
}
|
|
4
10
|
|
|
5
11
|
export class Session {
|
|
12
|
+
sessionId?: string;
|
|
6
13
|
serviceProvider?: NodeDriverServiceProvider;
|
|
7
|
-
apiClient
|
|
14
|
+
apiClient: ApiClient;
|
|
15
|
+
agentRunner?: {
|
|
16
|
+
name: string;
|
|
17
|
+
version: string;
|
|
18
|
+
};
|
|
8
19
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
20
|
+
constructor({ apiBaseUrl, apiClientId, apiClientSecret }: SessionOptions = {}) {
|
|
21
|
+
const credentials: ApiClientCredentials | undefined =
|
|
22
|
+
apiClientId && apiClientSecret
|
|
23
|
+
? {
|
|
24
|
+
clientId: apiClientId,
|
|
25
|
+
clientSecret: apiClientSecret,
|
|
26
|
+
}
|
|
27
|
+
: undefined;
|
|
28
|
+
|
|
29
|
+
this.apiClient = new ApiClient({
|
|
30
|
+
baseUrl: apiBaseUrl,
|
|
31
|
+
credentials,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
16
34
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
});
|
|
35
|
+
setAgentRunner(agentRunner: Implementation | undefined) {
|
|
36
|
+
if (agentRunner?.name && agentRunner?.version) {
|
|
37
|
+
this.agentRunner = {
|
|
38
|
+
name: agentRunner.name,
|
|
39
|
+
version: agentRunner.version,
|
|
40
|
+
};
|
|
24
41
|
}
|
|
25
42
|
}
|
|
26
43
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { getMachineIdSync } from "native-machine-id";
|
|
2
|
+
import { packageInfo } from "../packageInfo.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Machine-specific metadata formatted for telemetry
|
|
6
|
+
*/
|
|
7
|
+
export const MACHINE_METADATA = {
|
|
8
|
+
device_id: getMachineIdSync(),
|
|
9
|
+
mcp_server_version: packageInfo.version,
|
|
10
|
+
mcp_server_name: packageInfo.mcpServerName,
|
|
11
|
+
platform: process.platform,
|
|
12
|
+
arch: process.arch,
|
|
13
|
+
os_type: process.platform,
|
|
14
|
+
os_version: process.version,
|
|
15
|
+
} as const;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { BaseEvent } from "./types.js";
|
|
2
|
+
import { LRUCache } from "lru-cache";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Singleton class for in-memory telemetry event caching
|
|
6
|
+
* Provides a central storage for telemetry events that couldn't be sent
|
|
7
|
+
* Uses LRU cache to automatically drop oldest events when limit is exceeded
|
|
8
|
+
*/
|
|
9
|
+
export class EventCache {
|
|
10
|
+
private static instance: EventCache;
|
|
11
|
+
private static readonly MAX_EVENTS = 1000;
|
|
12
|
+
|
|
13
|
+
private cache: LRUCache<number, BaseEvent>;
|
|
14
|
+
private nextId = 0;
|
|
15
|
+
|
|
16
|
+
private constructor() {
|
|
17
|
+
this.cache = new LRUCache({
|
|
18
|
+
max: EventCache.MAX_EVENTS,
|
|
19
|
+
// Using FIFO eviction strategy for events
|
|
20
|
+
allowStale: false,
|
|
21
|
+
updateAgeOnGet: false,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Gets the singleton instance of EventCache
|
|
27
|
+
* @returns The EventCache instance
|
|
28
|
+
*/
|
|
29
|
+
public static getInstance(): EventCache {
|
|
30
|
+
if (!EventCache.instance) {
|
|
31
|
+
EventCache.instance = new EventCache();
|
|
32
|
+
}
|
|
33
|
+
return EventCache.instance;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Gets a copy of the currently cached events
|
|
38
|
+
* @returns Array of cached BaseEvent objects
|
|
39
|
+
*/
|
|
40
|
+
public getEvents(): BaseEvent[] {
|
|
41
|
+
return Array.from(this.cache.values());
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Appends new events to the cached events
|
|
46
|
+
* LRU cache automatically handles dropping oldest events when limit is exceeded
|
|
47
|
+
* @param events - The events to append
|
|
48
|
+
*/
|
|
49
|
+
public appendEvents(events: BaseEvent[]): void {
|
|
50
|
+
for (const event of events) {
|
|
51
|
+
this.cache.set(this.nextId++, event);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Clears all cached events
|
|
57
|
+
*/
|
|
58
|
+
public clearEvents(): void {
|
|
59
|
+
this.cache.clear();
|
|
60
|
+
this.nextId = 0;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { Session } from "../session.js";
|
|
2
|
+
import { BaseEvent } from "./types.js";
|
|
3
|
+
import { config } from "../config.js";
|
|
4
|
+
import logger from "../logger.js";
|
|
5
|
+
import { mongoLogId } from "mongodb-log-writer";
|
|
6
|
+
import { ApiClient } from "../common/atlas/apiClient.js";
|
|
7
|
+
import { MACHINE_METADATA } from "./constants.js";
|
|
8
|
+
import { EventCache } from "./eventCache.js";
|
|
9
|
+
|
|
10
|
+
type EventResult = {
|
|
11
|
+
success: boolean;
|
|
12
|
+
error?: Error;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
type CommonProperties = {
|
|
16
|
+
device_id?: string;
|
|
17
|
+
mcp_server_version: string;
|
|
18
|
+
mcp_server_name: string;
|
|
19
|
+
mcp_client_version?: string;
|
|
20
|
+
mcp_client_name?: string;
|
|
21
|
+
platform: string;
|
|
22
|
+
arch: string;
|
|
23
|
+
os_type: string;
|
|
24
|
+
os_version?: string;
|
|
25
|
+
session_id?: string;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export class Telemetry {
|
|
29
|
+
private readonly commonProperties: CommonProperties;
|
|
30
|
+
|
|
31
|
+
constructor(
|
|
32
|
+
private readonly session: Session,
|
|
33
|
+
private readonly eventCache: EventCache = EventCache.getInstance()
|
|
34
|
+
) {
|
|
35
|
+
this.commonProperties = {
|
|
36
|
+
...MACHINE_METADATA,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Checks if telemetry is currently enabled
|
|
42
|
+
* This is a method rather than a constant to capture runtime config changes
|
|
43
|
+
*
|
|
44
|
+
* Follows the Console Do Not Track standard (https://consoledonottrack.com/)
|
|
45
|
+
* by respecting the DO_NOT_TRACK environment variable
|
|
46
|
+
*/
|
|
47
|
+
private static isTelemetryEnabled(): boolean {
|
|
48
|
+
// Check if telemetry is explicitly disabled in config
|
|
49
|
+
if (config.telemetry === "disabled") {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const doNotTrack = process.env.DO_NOT_TRACK;
|
|
54
|
+
if (doNotTrack) {
|
|
55
|
+
const value = doNotTrack.toLowerCase();
|
|
56
|
+
// Telemetry should be disabled if DO_NOT_TRACK is "1", "true", or "yes"
|
|
57
|
+
if (value === "1" || value === "true" || value === "yes") {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Emits events through the telemetry pipeline
|
|
67
|
+
* @param events - The events to emit
|
|
68
|
+
*/
|
|
69
|
+
public async emitEvents(events: BaseEvent[]): Promise<void> {
|
|
70
|
+
try {
|
|
71
|
+
if (!Telemetry.isTelemetryEnabled()) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
await this.emit(events);
|
|
76
|
+
} catch {
|
|
77
|
+
logger.debug(mongoLogId(1_000_002), "telemetry", `Error emitting telemetry events.`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Gets the common properties for events
|
|
83
|
+
* @returns Object containing common properties for all events
|
|
84
|
+
*/
|
|
85
|
+
public getCommonProperties(): CommonProperties {
|
|
86
|
+
return {
|
|
87
|
+
...this.commonProperties,
|
|
88
|
+
mcp_client_version: this.session.agentRunner?.version,
|
|
89
|
+
mcp_client_name: this.session.agentRunner?.name,
|
|
90
|
+
session_id: this.session.sessionId,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Attempts to emit events through authenticated and unauthenticated clients
|
|
96
|
+
* Falls back to caching if both attempts fail
|
|
97
|
+
*/
|
|
98
|
+
private async emit(events: BaseEvent[]): Promise<void> {
|
|
99
|
+
const cachedEvents = this.eventCache.getEvents();
|
|
100
|
+
const allEvents = [...cachedEvents, ...events];
|
|
101
|
+
|
|
102
|
+
logger.debug(
|
|
103
|
+
mongoLogId(1_000_003),
|
|
104
|
+
"telemetry",
|
|
105
|
+
`Attempting to send ${allEvents.length} events (${cachedEvents.length} cached)`
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
const result = await this.sendEvents(this.session.apiClient, allEvents);
|
|
109
|
+
if (result.success) {
|
|
110
|
+
this.eventCache.clearEvents();
|
|
111
|
+
logger.debug(mongoLogId(1_000_004), "telemetry", `Sent ${allEvents.length} events successfully`);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
logger.warning(
|
|
116
|
+
mongoLogId(1_000_005),
|
|
117
|
+
"telemetry",
|
|
118
|
+
`Error sending event to client: ${result.error instanceof Error ? result.error.message : String(result.error)}`
|
|
119
|
+
);
|
|
120
|
+
this.eventCache.appendEvents(events);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Attempts to send events through the provided API client
|
|
125
|
+
*/
|
|
126
|
+
private async sendEvents(client: ApiClient, events: BaseEvent[]): Promise<EventResult> {
|
|
127
|
+
try {
|
|
128
|
+
await client.sendEvents(events);
|
|
129
|
+
return { success: true };
|
|
130
|
+
} catch (error) {
|
|
131
|
+
return {
|
|
132
|
+
success: false,
|
|
133
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Result type constants for telemetry events
|
|
3
|
+
*/
|
|
4
|
+
export type TelemetryResult = "success" | "failure";
|
|
5
|
+
export type ServerCommand = "start" | "stop";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Base interface for all events
|
|
9
|
+
*/
|
|
10
|
+
export interface Event {
|
|
11
|
+
timestamp: string;
|
|
12
|
+
source: "mdbmcp";
|
|
13
|
+
properties: Record<string, unknown>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface BaseEvent extends Event {
|
|
17
|
+
properties: {
|
|
18
|
+
device_id?: string;
|
|
19
|
+
mcp_server_version: string;
|
|
20
|
+
mcp_server_name: string;
|
|
21
|
+
mcp_client_version?: string;
|
|
22
|
+
mcp_client_name?: string;
|
|
23
|
+
platform: string;
|
|
24
|
+
arch: string;
|
|
25
|
+
os_type: string;
|
|
26
|
+
component: string;
|
|
27
|
+
duration_ms: number;
|
|
28
|
+
result: TelemetryResult;
|
|
29
|
+
category: string;
|
|
30
|
+
os_version?: string;
|
|
31
|
+
session_id?: string;
|
|
32
|
+
} & Event["properties"];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Interface for tool events
|
|
37
|
+
*/
|
|
38
|
+
export interface ToolEvent extends BaseEvent {
|
|
39
|
+
properties: {
|
|
40
|
+
command: string;
|
|
41
|
+
error_code?: string;
|
|
42
|
+
error_type?: string;
|
|
43
|
+
project_id?: string;
|
|
44
|
+
org_id?: string;
|
|
45
|
+
cluster_name?: string;
|
|
46
|
+
is_atlas?: boolean;
|
|
47
|
+
} & BaseEvent["properties"];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Interface for server events
|
|
52
|
+
*/
|
|
53
|
+
export interface ServerEvent extends BaseEvent {
|
|
54
|
+
properties: {
|
|
55
|
+
command: ServerCommand;
|
|
56
|
+
reason?: string;
|
|
57
|
+
startup_time_ms?: number;
|
|
58
|
+
runtime_duration_ms?: number;
|
|
59
|
+
} & BaseEvent["properties"];
|
|
60
|
+
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { ToolBase, ToolCategory } from "../tool.js";
|
|
2
|
-
import { Session } from "../../session.js";
|
|
3
2
|
|
|
4
3
|
export abstract class AtlasToolBase extends ToolBase {
|
|
5
|
-
constructor(protected readonly session: Session) {
|
|
6
|
-
super(session);
|
|
7
|
-
}
|
|
8
|
-
|
|
9
4
|
protected category: ToolCategory = "atlas";
|
|
5
|
+
|
|
6
|
+
protected verifyAllowed(): boolean {
|
|
7
|
+
if (!this.config.apiClientId || !this.config.apiClientSecret) {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
return super.verifyAllowed();
|
|
11
|
+
}
|
|
10
12
|
}
|
|
@@ -27,8 +27,6 @@ export class CreateAccessListTool extends AtlasToolBase {
|
|
|
27
27
|
comment,
|
|
28
28
|
currentIpAddress,
|
|
29
29
|
}: ToolArgs<typeof this.argsShape>): Promise<CallToolResult> {
|
|
30
|
-
this.session.ensureAuthenticated();
|
|
31
|
-
|
|
32
30
|
if (!ipAddresses?.length && !cidrBlocks?.length && !currentIpAddress) {
|
|
33
31
|
throw new Error("One of ipAddresses, cidrBlocks, currentIpAddress must be provided.");
|
|
34
32
|
}
|
|
@@ -15,8 +15,6 @@ export class CreateFreeClusterTool extends AtlasToolBase {
|
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
protected async execute({ projectId, name, region }: ToolArgs<typeof this.argsShape>): Promise<CallToolResult> {
|
|
18
|
-
this.session.ensureAuthenticated();
|
|
19
|
-
|
|
20
18
|
const input = {
|
|
21
19
|
groupId: projectId,
|
|
22
20
|
name,
|
|
@@ -14,7 +14,6 @@ export class CreateProjectTool extends AtlasToolBase {
|
|
|
14
14
|
};
|
|
15
15
|
|
|
16
16
|
protected async execute({ projectName, organizationId }: ToolArgs<typeof this.argsShape>): Promise<CallToolResult> {
|
|
17
|
-
this.session.ensureAuthenticated();
|
|
18
17
|
let assumedOrg = false;
|
|
19
18
|
|
|
20
19
|
if (!projectName) {
|
|
@@ -12,8 +12,6 @@ export class InspectAccessListTool extends AtlasToolBase {
|
|
|
12
12
|
};
|
|
13
13
|
|
|
14
14
|
protected async execute({ projectId }: ToolArgs<typeof this.argsShape>): Promise<CallToolResult> {
|
|
15
|
-
this.session.ensureAuthenticated();
|
|
16
|
-
|
|
17
15
|
const accessList = await this.session.apiClient.listProjectIpAccessLists({
|
|
18
16
|
params: {
|
|
19
17
|
path: {
|
|
@@ -14,8 +14,6 @@ export class InspectClusterTool extends AtlasToolBase {
|
|
|
14
14
|
};
|
|
15
15
|
|
|
16
16
|
protected async execute({ projectId, clusterName }: ToolArgs<typeof this.argsShape>): Promise<CallToolResult> {
|
|
17
|
-
this.session.ensureAuthenticated();
|
|
18
|
-
|
|
19
17
|
const cluster = await this.session.apiClient.getCluster({
|
|
20
18
|
params: {
|
|
21
19
|
path: {
|
|
@@ -13,8 +13,6 @@ export class ListClustersTool extends AtlasToolBase {
|
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
protected async execute({ projectId }: ToolArgs<typeof this.argsShape>): Promise<CallToolResult> {
|
|
16
|
-
this.session.ensureAuthenticated();
|
|
17
|
-
|
|
18
16
|
if (!projectId) {
|
|
19
17
|
const data = await this.session.apiClient.listClustersForAllProjects();
|
|
20
18
|
|
|
@@ -13,8 +13,6 @@ export class ListDBUsersTool extends AtlasToolBase {
|
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
protected async execute({ projectId }: ToolArgs<typeof this.argsShape>): Promise<CallToolResult> {
|
|
16
|
-
this.session.ensureAuthenticated();
|
|
17
|
-
|
|
18
16
|
const data = await this.session.apiClient.listDatabaseUsers({
|
|
19
17
|
params: {
|
|
20
18
|
path: {
|
|
@@ -9,8 +9,6 @@ export class ListOrganizationsTool extends AtlasToolBase {
|
|
|
9
9
|
protected argsShape = {};
|
|
10
10
|
|
|
11
11
|
protected async execute(): Promise<CallToolResult> {
|
|
12
|
-
this.session.ensureAuthenticated();
|
|
13
|
-
|
|
14
12
|
const data = await this.session.apiClient.listOrganizations();
|
|
15
13
|
|
|
16
14
|
if (!data?.results?.length) {
|
|
@@ -13,7 +13,15 @@ export class ListProjectsTool extends AtlasToolBase {
|
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
protected async execute({ orgId }: ToolArgs<typeof this.argsShape>): Promise<CallToolResult> {
|
|
16
|
-
this.session.
|
|
16
|
+
const orgData = await this.session.apiClient.listOrganizations();
|
|
17
|
+
|
|
18
|
+
if (!orgData?.results?.length) {
|
|
19
|
+
throw new Error("No organizations found in your MongoDB Atlas account.");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const orgs: Record<string, string> = orgData.results
|
|
23
|
+
.map((org) => [org.id || "", org.name])
|
|
24
|
+
.reduce((acc, [id, name]) => ({ ...acc, [id]: name }), {});
|
|
17
25
|
|
|
18
26
|
const data = orgId
|
|
19
27
|
? await this.session.apiClient.listOrganizationProjects({
|
|
@@ -33,11 +41,11 @@ export class ListProjectsTool extends AtlasToolBase {
|
|
|
33
41
|
const rows = data.results
|
|
34
42
|
.map((project) => {
|
|
35
43
|
const createdAt = project.created ? new Date(project.created).toLocaleString() : "N/A";
|
|
36
|
-
return `${project.name} | ${project.id} | ${createdAt}`;
|
|
44
|
+
return `${project.name} | ${project.id} | ${orgs[project.orgId]} | ${project.orgId} | ${createdAt}`;
|
|
37
45
|
})
|
|
38
46
|
.join("\n");
|
|
39
|
-
const formattedProjects = `Project Name | Project ID | Created At
|
|
40
|
-
----------------| ----------------| ----------------
|
|
47
|
+
const formattedProjects = `Project Name | Project ID | Organization Name | Organization ID | Created At
|
|
48
|
+
----------------| ----------------| ----------------| ----------------| ----------------
|
|
41
49
|
${rows}`;
|
|
42
50
|
return {
|
|
43
51
|
content: [{ type: "text", text: formattedProjects }],
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
2
2
|
import { DbOperationArgs, MongoDBToolBase } from "../mongodbTool.js";
|
|
3
3
|
import { ToolArgs, OperationType } from "../../tool.js";
|
|
4
|
-
import {
|
|
4
|
+
import { getSimplifiedSchema } from "mongodb-schema";
|
|
5
5
|
|
|
6
6
|
export class CollectionSchemaTool extends MongoDBToolBase {
|
|
7
7
|
protected name = "collection-schema";
|
|
@@ -13,29 +13,31 @@ export class CollectionSchemaTool extends MongoDBToolBase {
|
|
|
13
13
|
protected async execute({ database, collection }: ToolArgs<typeof DbOperationArgs>): Promise<CallToolResult> {
|
|
14
14
|
const provider = await this.ensureConnected();
|
|
15
15
|
const documents = await provider.find(database, collection, {}, { limit: 5 }).toArray();
|
|
16
|
-
const schema = await
|
|
16
|
+
const schema = await getSimplifiedSchema(documents);
|
|
17
|
+
|
|
18
|
+
const fieldsCount = Object.entries(schema).length;
|
|
19
|
+
if (fieldsCount === 0) {
|
|
20
|
+
return {
|
|
21
|
+
content: [
|
|
22
|
+
{
|
|
23
|
+
text: `Could not deduce the schema for "${database}.${collection}". This may be because it doesn't exist or is empty.`,
|
|
24
|
+
type: "text",
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
};
|
|
28
|
+
}
|
|
17
29
|
|
|
18
30
|
return {
|
|
19
31
|
content: [
|
|
20
32
|
{
|
|
21
|
-
text: `Found ${
|
|
33
|
+
text: `Found ${fieldsCount} fields in the schema for "${database}.${collection}"`,
|
|
22
34
|
type: "text",
|
|
23
35
|
},
|
|
24
36
|
{
|
|
25
|
-
text:
|
|
37
|
+
text: JSON.stringify(schema),
|
|
26
38
|
type: "text",
|
|
27
39
|
},
|
|
28
40
|
],
|
|
29
41
|
};
|
|
30
42
|
}
|
|
31
|
-
|
|
32
|
-
private formatFieldOutput(fields: SchemaField[]): string {
|
|
33
|
-
let result = "| Field | Type | Confidence |\n";
|
|
34
|
-
result += "|-------|------|-------------|\n";
|
|
35
|
-
for (const field of fields) {
|
|
36
|
-
const fieldType = Array.isArray(field.type) ? field.type.join(", ") : field.type;
|
|
37
|
-
result += `| ${field.name} | \`${fieldType}\` | ${(field.probability * 100).toFixed(0)}% |\n`;
|
|
38
|
-
}
|
|
39
|
-
return result;
|
|
40
|
-
}
|
|
41
43
|
}
|
|
@@ -4,7 +4,7 @@ import { ToolArgs, OperationType } from "../../tool.js";
|
|
|
4
4
|
|
|
5
5
|
export class CollectionStorageSizeTool extends MongoDBToolBase {
|
|
6
6
|
protected name = "collection-storage-size";
|
|
7
|
-
protected description = "Gets the size of the collection
|
|
7
|
+
protected description = "Gets the size of the collection";
|
|
8
8
|
protected argsShape = DbOperationArgs;
|
|
9
9
|
|
|
10
10
|
protected operationType: OperationType = "metadata";
|
|
@@ -14,17 +14,55 @@ export class CollectionStorageSizeTool extends MongoDBToolBase {
|
|
|
14
14
|
const [{ value }] = (await provider
|
|
15
15
|
.aggregate(database, collection, [
|
|
16
16
|
{ $collStats: { storageStats: {} } },
|
|
17
|
-
{ $group: { _id: null, value: { $sum: "$storageStats.
|
|
17
|
+
{ $group: { _id: null, value: { $sum: "$storageStats.size" } } },
|
|
18
18
|
])
|
|
19
19
|
.toArray()) as [{ value: number }];
|
|
20
20
|
|
|
21
|
+
const { units, value: scaledValue } = CollectionStorageSizeTool.getStats(value);
|
|
22
|
+
|
|
21
23
|
return {
|
|
22
24
|
content: [
|
|
23
25
|
{
|
|
24
|
-
text: `The size of
|
|
26
|
+
text: `The size of "${database}.${collection}" is \`${scaledValue.toFixed(2)} ${units}\``,
|
|
25
27
|
type: "text",
|
|
26
28
|
},
|
|
27
29
|
],
|
|
28
30
|
};
|
|
29
31
|
}
|
|
32
|
+
|
|
33
|
+
protected handleError(
|
|
34
|
+
error: unknown,
|
|
35
|
+
args: ToolArgs<typeof this.argsShape>
|
|
36
|
+
): Promise<CallToolResult> | CallToolResult {
|
|
37
|
+
if (error instanceof Error && "codeName" in error && error.codeName === "NamespaceNotFound") {
|
|
38
|
+
return {
|
|
39
|
+
content: [
|
|
40
|
+
{
|
|
41
|
+
text: `The size of "${args.database}.${args.collection}" cannot be determined because the collection does not exist.`,
|
|
42
|
+
type: "text",
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return super.handleError(error, args);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private static getStats(value: number): { value: number; units: string } {
|
|
52
|
+
const kb = 1024;
|
|
53
|
+
const mb = kb * 1024;
|
|
54
|
+
const gb = mb * 1024;
|
|
55
|
+
|
|
56
|
+
if (value > gb) {
|
|
57
|
+
return { value: value / gb, units: "GB" };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (value > mb) {
|
|
61
|
+
return { value: value / mb, units: "MB" };
|
|
62
|
+
}
|
|
63
|
+
if (value > kb) {
|
|
64
|
+
return { value: value / kb, units: "KB" };
|
|
65
|
+
}
|
|
66
|
+
return { value, units: "bytes" };
|
|
67
|
+
}
|
|
30
68
|
}
|
|
@@ -2,7 +2,6 @@ import { z } from "zod";
|
|
|
2
2
|
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
3
3
|
import { MongoDBToolBase } from "../mongodbTool.js";
|
|
4
4
|
import { ToolArgs, OperationType } from "../../tool.js";
|
|
5
|
-
import config from "../../../config.js";
|
|
6
5
|
import { MongoError as DriverError } from "mongodb";
|
|
7
6
|
|
|
8
7
|
export class ConnectTool extends MongoDBToolBase {
|
|
@@ -35,7 +34,7 @@ export class ConnectTool extends MongoDBToolBase {
|
|
|
35
34
|
protected async execute({ options: optionsArr }: ToolArgs<typeof this.argsShape>): Promise<CallToolResult> {
|
|
36
35
|
const options = optionsArr?.[0];
|
|
37
36
|
let connectionString: string;
|
|
38
|
-
if (!options && !config.connectionString) {
|
|
37
|
+
if (!options && !this.config.connectionString) {
|
|
39
38
|
return {
|
|
40
39
|
content: [
|
|
41
40
|
{ type: "text", text: "No connection details provided." },
|
|
@@ -46,7 +45,7 @@ export class ConnectTool extends MongoDBToolBase {
|
|
|
46
45
|
|
|
47
46
|
if (!options) {
|
|
48
47
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
49
|
-
connectionString = config.connectionString!;
|
|
48
|
+
connectionString = this.config.connectionString!;
|
|
50
49
|
} else if ("connectionString" in options) {
|
|
51
50
|
connectionString = options.connectionString;
|
|
52
51
|
} else {
|
|
@@ -72,9 +71,9 @@ export class ConnectTool extends MongoDBToolBase {
|
|
|
72
71
|
// Sometimes the model will supply an incorrect connection string. If the user has configured
|
|
73
72
|
// a different one as environment variable or a cli argument, suggest using that one instead.
|
|
74
73
|
if (
|
|
75
|
-
config.connectionString &&
|
|
74
|
+
this.config.connectionString &&
|
|
76
75
|
error instanceof DriverError &&
|
|
77
|
-
config.connectionString !== connectionString
|
|
76
|
+
this.config.connectionString !== connectionString
|
|
78
77
|
) {
|
|
79
78
|
return {
|
|
80
79
|
content: [
|
|
@@ -82,7 +81,7 @@ export class ConnectTool extends MongoDBToolBase {
|
|
|
82
81
|
type: "text",
|
|
83
82
|
text:
|
|
84
83
|
`Failed to connect to MongoDB at '${connectionString}' due to error: '${error.message}.` +
|
|
85
|
-
`Your config lists a different connection string: '${config.connectionString}' - do you want to try connecting to it instead?`,
|
|
84
|
+
`Your config lists a different connection string: '${this.config.connectionString}' - do you want to try connecting to it instead?`,
|
|
86
85
|
},
|
|
87
86
|
],
|
|
88
87
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
2
2
|
import { DbOperationArgs, MongoDBToolBase } from "../mongodbTool.js";
|
|
3
3
|
import { ToolArgs, OperationType } from "../../tool.js";
|
|
4
|
+
import { EJSON } from "bson";
|
|
4
5
|
|
|
5
6
|
export class DbStatsTool extends MongoDBToolBase {
|
|
6
7
|
protected name = "db-stats";
|
|
@@ -21,7 +22,11 @@ export class DbStatsTool extends MongoDBToolBase {
|
|
|
21
22
|
return {
|
|
22
23
|
content: [
|
|
23
24
|
{
|
|
24
|
-
text: `Statistics for database ${database}
|
|
25
|
+
text: `Statistics for database ${database}`,
|
|
26
|
+
type: "text",
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
text: EJSON.stringify(result),
|
|
25
30
|
type: "text",
|
|
26
31
|
},
|
|
27
32
|
],
|