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

mongodb-js / mongodb-mcp-server / 14788116886

02 May 2025 03:32AM UTC coverage: 74.619% (-7.8%) from 82.446%
14788116886

push

github

web-flow
Incorrect link of VSCode's MCP usage

- Change link of MCP usage

164 of 296 branches covered (55.41%)

Branch coverage included in aggregate %.

668 of 819 relevant lines covered (81.56%)

82.5 hits per line

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

72.73
/src/server.ts
1
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
import { Session } from "./session.js";
3
import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
4
import { AtlasTools } from "./tools/atlas/tools.js";
5
import { MongoDbTools } from "./tools/mongodb/tools.js";
6
import logger, { initializeLogger, LogId } from "./logger.js";
7
import { ObjectId } from "mongodb";
8
import { Telemetry } from "./telemetry/telemetry.js";
9
import { UserConfig } from "./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";
13
import assert from "assert";
14

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

22
export class Server {
23
    public readonly session: Session;
24
    private readonly mcpServer: McpServer;
25
    private readonly telemetry: Telemetry;
26
    public readonly userConfig: UserConfig;
27
    private readonly startTime: number;
28

29
    constructor({ session, mcpServer, userConfig, telemetry }: ServerOptions) {
30
        this.startTime = Date.now();
54✔
31
        this.session = session;
54✔
32
        this.telemetry = telemetry;
54✔
33
        this.mcpServer = mcpServer;
54✔
34
        this.userConfig = userConfig;
54✔
35
    }
36

37
    async connect(transport: Transport): Promise<void> {
38
        this.mcpServer.server.registerCapabilities({ logging: {} });
54✔
39

40
        this.registerTools();
54✔
41
        this.registerResources();
54✔
42

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

56
        assert(existingHandler, "No existing handler found for CallToolRequestSchema");
54✔
57

58
        this.mcpServer.server.setRequestHandler(CallToolRequestSchema, (request, extra): Promise<CallToolResult> => {
54✔
59
            if (!request.params.arguments) {
404!
60
                request.params.arguments = {};
×
61
            }
62

63
            return existingHandler(request, extra);
404✔
64
        });
65

66
        await initializeLogger(this.mcpServer, this.userConfig.logPath);
54✔
67

68
        await this.mcpServer.connect(transport);
54✔
69

70
        this.mcpServer.server.oninitialized = () => {
54✔
71
            this.session.setAgentRunner(this.mcpServer.server.getClientVersion());
54✔
72
            this.session.sessionId = new ObjectId().toString();
54✔
73

74
            logger.info(
54✔
75
                LogId.serverInitialized,
76
                "server",
77
                `Server started with transport ${transport.constructor.name} and agent runner ${this.session.agentRunner?.name}`
78
            );
79

80
            this.emitServerEvent("start", Date.now() - this.startTime);
54✔
81
        };
82

83
        this.mcpServer.server.onclose = () => {
54✔
84
            const closeTime = Date.now();
54✔
85
            this.emitServerEvent("stop", Date.now() - closeTime);
54✔
86
        };
87

88
        this.mcpServer.server.onerror = (error: Error) => {
54✔
89
            const closeTime = Date.now();
×
90
            this.emitServerEvent("stop", Date.now() - closeTime, error);
×
91
        };
92

93
        await this.validateConfig();
54✔
94
    }
95

96
    async close(): Promise<void> {
97
        await this.telemetry.close();
54✔
98
        await this.session.close();
54✔
99
        await this.mcpServer.close();
54✔
100
    }
101

102
    /**
103
     * Emits a server event
104
     * @param command - The server command (e.g., "start", "stop", "register", "deregister")
105
     * @param additionalProperties - Additional properties specific to the event
106
     */
107
    emitServerEvent(command: ServerCommand, commandDuration: number, error?: Error) {
108
        const event: ServerEvent = {
108✔
109
            timestamp: new Date().toISOString(),
110
            source: "mdbmcp",
111
            properties: {
112
                result: "success",
113
                duration_ms: commandDuration,
114
                component: "server",
115
                category: "other",
116
                command: command,
117
            },
118
        };
119

120
        if (command === "start") {
108✔
121
            event.properties.startup_time_ms = commandDuration;
54✔
122
            event.properties.read_only_mode = this.userConfig.readOnly || false;
54✔
123
            event.properties.disabled_tools = this.userConfig.disabledTools || [];
54!
124
        }
125
        if (command === "stop") {
108✔
126
            event.properties.runtime_duration_ms = Date.now() - this.startTime;
54✔
127
            if (error) {
54!
128
                event.properties.result = "failure";
×
129
                event.properties.reason = error.message;
×
130
            }
131
        }
132

133
        this.telemetry.emitEvents([event]).catch(() => {});
108✔
134
    }
135

136
    private registerTools() {
137
        for (const tool of [...AtlasTools, ...MongoDbTools]) {
54✔
138
            new tool(this.session, this.userConfig, this.telemetry).register(this.mcpServer);
1,620✔
139
        }
140
    }
141

142
    private registerResources() {
143
        this.mcpServer.resource(
54✔
144
            "config",
145
            "config://config",
146
            {
147
                description:
148
                    "Server configuration, supplied by the user either as environment variables or as startup arguments",
149
            },
150
            (uri) => {
151
                const result = {
×
152
                    telemetry: this.userConfig.telemetry,
153
                    logPath: this.userConfig.logPath,
154
                    connectionString: this.userConfig.connectionString
×
155
                        ? "set; access to MongoDB tools are currently available to use"
156
                        : "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'.",
157
                    connectOptions: this.userConfig.connectOptions,
158
                    atlas:
159
                        this.userConfig.apiClientId && this.userConfig.apiClientSecret
×
160
                            ? "set; MongoDB Atlas tools are currently available to use"
161
                            : "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'.",
162
                };
163
                return {
×
164
                    contents: [
165
                        {
166
                            text: JSON.stringify(result),
167
                            mimeType: "application/json",
168
                            uri: uri.href,
169
                        },
170
                    ],
171
                };
172
            }
173
        );
174
    }
175

176
    private async validateConfig(): Promise<void> {
177
        const isAtlasConfigured = this.userConfig.apiClientId && this.userConfig.apiClientSecret;
54✔
178
        const isMongoDbConfigured = this.userConfig.connectionString;
54✔
179
        if (!isAtlasConfigured && !isMongoDbConfigured) {
54!
180
            console.error(
×
181
                "Either Atlas Client Id or a MongoDB connection string must be configured - you can provide them as environment variables or as startup arguments. \n" +
182
                    "Provide the Atlas credentials as `MDB_MCP_API_CLIENT_ID` and `MDB_MCP_API_CLIENT_SECRET` environment variables or as `--apiClientId` and `--apiClientSecret` startup arguments. \n" +
183
                    "Provide the MongoDB connection string as `MDB_MCP_CONNECTION_STRING` environment variable or as `--connectionString` startup argument."
184
            );
185
            throw new Error("Either Atlas Client Id or a MongoDB connection string must be configured");
×
186
        }
187

188
        if (this.userConfig.connectionString) {
54✔
189
            try {
40✔
190
                await this.session.connectToMongoDB(this.userConfig.connectionString, this.userConfig.connectOptions);
40✔
191
            } catch (error) {
192
                console.error(
×
193
                    "Failed to connect to MongoDB instance using the connection string from the config: ",
194
                    error
195
                );
196
                throw new Error("Failed to connect to MongoDB instance using the connection string from the config");
×
197
            }
198
        }
199
    }
200
}
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

© 2026 Coveralls, Inc