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

mongodb-js / mongodb-mcp-server / 14931706280

09 May 2025 02:54PM UTC coverage: 75.097%. First build
14931706280

Pull #226

github

web-flow
Merge 13201fed1 into 7b033ade4
Pull Request #226: wip

205 of 352 branches covered (58.24%)

Branch coverage included in aggregate %.

7 of 9 new or added lines in 1 file covered. (77.78%)

760 of 933 relevant lines covered (81.46%)

49.16 hits per line

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

73.26
/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();
29✔
31
        this.session = session;
29✔
32
        this.telemetry = telemetry;
29✔
33
        this.mcpServer = mcpServer;
29✔
34
        this.userConfig = userConfig;
29✔
35
    }
36

37
    async connect(transport: Transport): Promise<void> {
38
        try {
29✔
39
            await this.setupAndConnect(transport);
29✔
40
        } catch (error) {
NEW
41
            this.emitServerEvent("stop", Date.now() - this.startTime, error as Error);
×
NEW
42
            throw error;
×
43
        }
44
    }
45

46
    private async setupAndConnect(transport: Transport): Promise<void> {
47
        this.mcpServer.server.registerCapabilities({ logging: {} });
29✔
48
        await this.setServerCallbacks(transport);
29✔
49
        this.emitServerEvent("start", Date.now() - this.startTime);
29✔
50

51
        this.registerTools();
29✔
52
        this.registerResources();
29✔
53

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

67
        assert(existingHandler, "No existing handler found for CallToolRequestSchema");
29✔
68

69
        this.mcpServer.server.setRequestHandler(CallToolRequestSchema, (request, extra): Promise<CallToolResult> => {
29✔
70
            if (!request.params.arguments) {
335✔
71
                request.params.arguments = {};
1✔
72
            }
73

74
            return existingHandler(request, extra);
335✔
75
        });
76

77
        await initializeLogger(this.mcpServer, this.userConfig.logPath);
29✔
78
        await this.validateConfig();
29✔
79

80
        await this.mcpServer.connect(transport);
29✔
81
    }
82

83
    /**
84
     * Sets up the MCP serve instance by registering capabilities and setting up event listeners.
85
     * @param transport - The transport to use for connecting to the server.
86
     */
87
    async setServerCallbacks(transport: Transport) {
88
        this.mcpServer.server.oninitialized = () => {
29✔
89
            this.session.setAgentRunner(this.mcpServer.server.getClientVersion());
29✔
90
            this.session.sessionId = new ObjectId().toString();
29✔
91

92
            logger.info(
29✔
93
                LogId.serverInitialized,
94
                "server",
95
                `Server connected with transport ${transport.constructor.name} and agent runner ${this.session.agentRunner?.name}`
96
            );
97

98
            this.emitServerEvent("connect", Date.now() - this.startTime);
29✔
99
        };
100

101
        this.mcpServer.server.onclose = () => {
29✔
102
            const closeTime = Date.now();
29✔
103
            this.emitServerEvent("stop", Date.now() - closeTime);
29✔
104
        };
105

106
        this.mcpServer.server.onerror = (error: Error) => {
29✔
107
            const closeTime = Date.now();
×
108
            this.emitServerEvent("stop", Date.now() - closeTime, error);
×
109
        };            
110
    }
111

112
    async close(): Promise<void> {
113
        await this.telemetry.close();
29✔
114
        await this.session.close();
29✔
115
        await this.mcpServer.close();
29✔
116
    }
117

118
    /**
119
     * Emits a server event
120
     * @param command - The server command (e.g., "start", "stop", "connect")
121
     * @param additionalProperties - Additional properties specific to the event
122
     */
123
    private emitServerEvent(command: ServerCommand, commandDuration: number, error?: Error) {
124
        const event: ServerEvent = {
87✔
125
            timestamp: new Date().toISOString(),
126
            source: "mdbmcp",
127
            properties: {
128
                result: "success",
129
                duration_ms: commandDuration,
130
                component: "server",
131
                category: "other",
132
                command: command,
133
            },
134
        };
135

136
        if (command === "start" || command === "connect") {
87✔
137
            event.properties.startup_time_ms = commandDuration;
58✔
138
            event.properties.read_only_mode = this.userConfig.readOnly || false;
58✔
139
            event.properties.disabled_tools = this.userConfig.disabledTools || [];
58!
140
        }
141
        if (command === "stop") {
87✔
142
            event.properties.runtime_duration_ms = Date.now() - this.startTime;
29✔
143
            if (error) {
29!
144
                event.properties.result = "failure";
×
145
                event.properties.reason = error.message;
×
146
            }
147
        }
148

149
        this.telemetry.emitEvents([event]).catch(() => {});
87✔
150
    }
151

152
    private registerTools() {
153
        for (const tool of [...AtlasTools, ...MongoDbTools]) {
29✔
154
            new tool(this.session, this.userConfig, this.telemetry).register(this.mcpServer);
899✔
155
        }
156
    }
157

158
    private registerResources() {
159
        this.mcpServer.resource(
29✔
160
            "config",
161
            "config://config",
162
            {
163
                description:
164
                    "Server configuration, supplied by the user either as environment variables or as startup arguments",
165
            },
166
            (uri) => {
167
                const result = {
×
168
                    telemetry: this.userConfig.telemetry,
169
                    logPath: this.userConfig.logPath,
170
                    connectionString: this.userConfig.connectionString
×
171
                        ? "set; access to MongoDB tools are currently available to use"
172
                        : "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'.",
173
                    connectOptions: this.userConfig.connectOptions,
174
                    atlas:
175
                        this.userConfig.apiClientId && this.userConfig.apiClientSecret
×
176
                            ? "set; MongoDB Atlas tools are currently available to use"
177
                            : "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'.",
178
                };
179
                return {
×
180
                    contents: [
181
                        {
182
                            text: JSON.stringify(result),
183
                            mimeType: "application/json",
184
                            uri: uri.href,
185
                        },
186
                    ],
187
                };
188
            }
189
        );
190
    }
191

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

205
        if (this.userConfig.apiClientId && this.userConfig.apiClientSecret) {
29✔
206
            try {
7✔
207
                await this.session.apiClient.hasValidAccessToken();
7✔
208
            } catch (error) {
209
                if (this.userConfig.connectionString === undefined) {
×
210
                    console.error("Failed to validate MongoDB Atlas the credentials from the config: ", error);
×
211

212
                    throw new Error(
×
213
                        "Failed to connect to MongoDB Atlas instance using the credentials from the config"
214
                    );
215
                }
216
                console.error(
×
217
                    "Failed to validate MongoDB Atlas the credentials from the config, but validated the connection string."
218
                );
219
            }
220
        }
221
    }
222
}
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