• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mongodb-js / mongodb-mcp-server / 16292877152

15 Jul 2025 12:10PM UTC coverage: 77.385% (+2.1%) from 75.27%
16292877152

push

github

web-flow
chore(tests): switch to vitest (#363)

498 of 687 branches covered (72.49%)

Branch coverage included in aggregate %.

0 of 27 new or added lines in 2 files covered. (0.0%)

293 existing lines in 26 files now uncovered.

2828 of 3611 relevant lines covered (78.32%)

28.46 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

71.73
/src/server.ts
1
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
import { Session } from "./common/session.js";
3
import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
4
import { AtlasTools } from "./tools/atlas/tools.js";
2✔
5
import { MongoDbTools } from "./tools/mongodb/tools.js";
2✔
6
import logger, { setStdioPreset, setContainerPreset, LogId } from "./common/logger.js";
2✔
7
import { ObjectId } from "mongodb";
2✔
8
import { Telemetry } from "./telemetry/telemetry.js";
9
import { UserConfig } from "./common/config.js";
10
import { type ServerEvent } from "./telemetry/types.js";
11
import { type ServerCommand } from "./telemetry/types.js";
12
import { CallToolRequestSchema, CallToolResult } from "@modelcontextprotocol/sdk/types.js";
2✔
13
import assert from "assert";
2✔
14
import { detectContainerEnv } from "./helpers/container.js";
2✔
15
import { ToolBase } from "./tools/tool.js";
16

17
export interface ServerOptions {
18
    session: Session;
19
    userConfig: UserConfig;
20
    mcpServer: McpServer;
21
    telemetry: Telemetry;
22
}
23

24
export class Server {
2✔
25
    public readonly session: Session;
26
    public readonly mcpServer: McpServer;
27
    private readonly telemetry: Telemetry;
28
    public readonly userConfig: UserConfig;
29
    public readonly tools: ToolBase[] = [];
2✔
30
    private readonly startTime: number;
31

32
    constructor({ session, mcpServer, userConfig, telemetry }: ServerOptions) {
2✔
33
        this.startTime = Date.now();
34✔
34
        this.session = session;
34✔
35
        this.telemetry = telemetry;
34✔
36
        this.mcpServer = mcpServer;
34✔
37
        this.userConfig = userConfig;
34✔
38
    }
34✔
39

40
    async connect(transport: Transport): Promise<void> {
2✔
41
        this.mcpServer.server.registerCapabilities({ logging: {} });
34✔
42

43
        this.registerTools();
34✔
44
        this.registerResources();
34✔
45

46
        // This is a workaround for an issue we've seen with some models, where they'll see that everything in the `arguments`
47
        // object is optional, and then not pass it at all. However, the MCP server expects the `arguments` object to be if
48
        // the tool accepts any arguments, even if they're all optional.
49
        //
50
        // see: https://github.com/modelcontextprotocol/typescript-sdk/blob/131776764536b5fdca642df51230a3746fb4ade0/src/server/mcp.ts#L705
51
        // Since paramsSchema here is not undefined, the server will create a non-optional z.object from it.
52
        const existingHandler = (
34✔
53
            this.mcpServer.server["_requestHandlers"] as Map<
34✔
54
                string,
55
                (request: unknown, extra: unknown) => Promise<CallToolResult>
56
            >
57
        ).get(CallToolRequestSchema.shape.method.value);
34✔
58

59
        assert(existingHandler, "No existing handler found for CallToolRequestSchema");
34✔
60

61
        this.mcpServer.server.setRequestHandler(CallToolRequestSchema, (request, extra): Promise<CallToolResult> => {
34✔
62
            if (!request.params.arguments) {
372✔
63
                request.params.arguments = {};
1✔
64
            }
1✔
65

66
            return existingHandler(request, extra);
372✔
67
        });
34✔
68

69
        const containerEnv = await detectContainerEnv();
34✔
70

71
        if (containerEnv) {
34!
72
            setContainerPreset(this.mcpServer);
×
73
        } else {
34✔
74
            await setStdioPreset(this.mcpServer, this.userConfig.logPath);
34✔
75
        }
34✔
76

77
        await this.mcpServer.connect(transport);
34✔
78

79
        this.mcpServer.server.oninitialized = () => {
34✔
80
            this.session.setAgentRunner(this.mcpServer.server.getClientVersion());
34✔
81
            this.session.sessionId = new ObjectId().toString();
34✔
82

83
            logger.info(
34✔
84
                LogId.serverInitialized,
34✔
85
                "server",
34✔
86
                `Server started with transport ${transport.constructor.name} and agent runner ${this.session.agentRunner?.name}`
34✔
87
            );
34✔
88

89
            this.emitServerEvent("start", Date.now() - this.startTime);
34✔
90
        };
34✔
91

92
        this.mcpServer.server.onclose = () => {
34✔
93
            const closeTime = Date.now();
34✔
94
            this.emitServerEvent("stop", Date.now() - closeTime);
34✔
95
        };
34✔
96

97
        this.mcpServer.server.onerror = (error: Error) => {
34✔
98
            const closeTime = Date.now();
×
99
            this.emitServerEvent("stop", Date.now() - closeTime, error);
×
UNCOV
100
        };
×
101

102
        await this.validateConfig();
34✔
103
    }
34✔
104

105
    async close(): Promise<void> {
2✔
106
        await this.telemetry.close();
34✔
107
        await this.session.close();
34✔
108
        await this.mcpServer.close();
34✔
109
    }
34✔
110

111
    /**
112
     * Emits a server event
113
     * @param command - The server command (e.g., "start", "stop", "register", "deregister")
114
     * @param additionalProperties - Additional properties specific to the event
115
     */
116
    private emitServerEvent(command: ServerCommand, commandDuration: number, error?: Error) {
2✔
117
        const event: ServerEvent = {
68✔
118
            timestamp: new Date().toISOString(),
68✔
119
            source: "mdbmcp",
68✔
120
            properties: {
68✔
121
                result: "success",
68✔
122
                duration_ms: commandDuration,
68✔
123
                component: "server",
68✔
124
                category: "other",
68✔
125
                command: command,
68✔
126
            },
68✔
127
        };
68✔
128

129
        if (command === "start") {
68✔
130
            event.properties.startup_time_ms = commandDuration;
34✔
131
            event.properties.read_only_mode = this.userConfig.readOnly || false;
34✔
132
            event.properties.disabled_tools = this.userConfig.disabledTools || [];
34!
133
        }
34✔
134
        if (command === "stop") {
68✔
135
            event.properties.runtime_duration_ms = Date.now() - this.startTime;
34✔
136
            if (error) {
34!
137
                event.properties.result = "failure";
×
138
                event.properties.reason = error.message;
×
UNCOV
139
            }
×
140
        }
34✔
141

142
        this.telemetry.emitEvents([event]).catch(() => {});
68✔
143
    }
68✔
144

145
    private registerTools() {
2✔
146
        for (const toolConstructor of [...AtlasTools, ...MongoDbTools]) {
34✔
147
            const tool = new toolConstructor(this.session, this.userConfig, this.telemetry);
1,088✔
148
            if (tool.register(this)) {
1,088✔
149
                this.tools.push(tool);
761✔
150
            }
761✔
151
        }
1,088✔
152
    }
34✔
153

154
    private registerResources() {
2✔
155
        this.mcpServer.resource(
34✔
156
            "config",
34✔
157
            "config://config",
34✔
158
            {
34✔
159
                description:
34✔
160
                    "Server configuration, supplied by the user either as environment variables or as startup arguments",
34✔
161
            },
34✔
162
            (uri) => {
34✔
163
                const result = {
×
UNCOV
164
                    telemetry: this.userConfig.telemetry,
×
UNCOV
165
                    logPath: this.userConfig.logPath,
×
UNCOV
166
                    connectionString: this.userConfig.connectionString
×
UNCOV
167
                        ? "set; access to MongoDB tools are currently available to use"
×
UNCOV
168
                        : "not set; before using any MongoDB tool, you need to configure a connection string, alternatively you can setup MongoDB Atlas access, more info at 'https://github.com/mongodb-js/mongodb-mcp-server'.",
×
UNCOV
169
                    connectOptions: this.userConfig.connectOptions,
×
UNCOV
170
                    atlas:
×
UNCOV
171
                        this.userConfig.apiClientId && this.userConfig.apiClientSecret
×
UNCOV
172
                            ? "set; MongoDB Atlas tools are currently available to use"
×
UNCOV
173
                            : "not set; MongoDB Atlas tools are currently unavailable, to have access to MongoDB Atlas tools like creating clusters or connecting to clusters make sure to setup credentials, more info at 'https://github.com/mongodb-js/mongodb-mcp-server'.",
×
UNCOV
174
                };
×
175
                return {
×
UNCOV
176
                    contents: [
×
UNCOV
177
                        {
×
UNCOV
178
                            text: JSON.stringify(result),
×
UNCOV
179
                            mimeType: "application/json",
×
UNCOV
180
                            uri: uri.href,
×
UNCOV
181
                        },
×
UNCOV
182
                    ],
×
UNCOV
183
                };
×
UNCOV
184
            }
×
185
        );
34✔
186
    }
34✔
187

188
    private async validateConfig(): Promise<void> {
2✔
189
        if (this.userConfig.connectionString) {
34!
190
            try {
1✔
191
                await this.session.connectToMongoDB(this.userConfig.connectionString, this.userConfig.connectOptions);
1✔
192
            } catch (error) {
1!
193
                console.error(
×
UNCOV
194
                    "Failed to connect to MongoDB instance using the connection string from the config: ",
×
UNCOV
195
                    error
×
UNCOV
196
                );
×
197
                throw new Error("Failed to connect to MongoDB instance using the connection string from the config");
×
UNCOV
198
            }
×
199
        }
1✔
200

201
        if (this.userConfig.apiClientId && this.userConfig.apiClientSecret) {
34✔
202
            try {
8✔
203
                await this.session.apiClient.validateAccessToken();
8✔
204
            } catch (error) {
8!
205
                if (this.userConfig.connectionString === undefined) {
×
206
                    console.error("Failed to validate MongoDB Atlas the credentials from the config: ", error);
×
207

208
                    throw new Error(
×
UNCOV
209
                        "Failed to connect to MongoDB Atlas instance using the credentials from the config"
×
UNCOV
210
                    );
×
UNCOV
211
                }
×
212
                console.error(
×
UNCOV
213
                    "Failed to validate MongoDB Atlas the credentials from the config, but validated the connection string."
×
UNCOV
214
                );
×
UNCOV
215
            }
×
216
        }
8✔
217
    }
34✔
218
}
2✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc