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

mongodb-js / mongodb-mcp-server / 16650135474

31 Jul 2025 01:19PM UTC coverage: 81.208% (+0.2%) from 81.014%
16650135474

Pull #406

github

web-flow
Merge 72f1ab810 into 0083493a5
Pull Request #406: feat: update connectionString appName param - [MCP-68]

606 of 783 branches covered (77.39%)

Branch coverage included in aggregate %.

69 of 81 new or added lines in 8 files covered. (85.19%)

60 existing lines in 5 files now uncovered.

3305 of 4033 relevant lines covered (81.95%)

54.05 hits per line

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

75.8
/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 { Resources } from "./resources/resources.js";
2✔
7
import logger, { LogId, LoggerBase, McpLogger, DiskLogger, ConsoleLogger } from "./common/logger.js";
2✔
8
import { ObjectId } from "mongodb";
2✔
9
import { Telemetry } from "./telemetry/telemetry.js";
10
import { UserConfig } from "./common/config.js";
11
import { type ServerEvent } from "./telemetry/types.js";
12
import { type ServerCommand } from "./telemetry/types.js";
13
import { CallToolRequestSchema, CallToolResult } from "@modelcontextprotocol/sdk/types.js";
2✔
14
import assert from "assert";
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();
66✔
34
        this.session = session;
66✔
35
        this.telemetry = telemetry;
66✔
36
        this.mcpServer = mcpServer;
66✔
37
        this.userConfig = userConfig;
66✔
38
    }
66✔
39

40
    async connect(transport: Transport): Promise<void> {
2✔
41
        await this.validateConfig();
64✔
42

43
        this.mcpServer.server.registerCapabilities({ logging: {} });
64✔
44

45
        this.registerTools();
64✔
46
        this.registerResources();
64✔
47

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

61
        assert(existingHandler, "No existing handler found for CallToolRequestSchema");
64✔
62

63
        this.mcpServer.server.setRequestHandler(CallToolRequestSchema, (request, extra): Promise<CallToolResult> => {
64✔
64
            if (!request.params.arguments) {
730✔
65
                request.params.arguments = {};
2✔
66
            }
2✔
67

68
            return existingHandler(request, extra);
730✔
69
        });
64✔
70

71
        const loggers: LoggerBase[] = [];
64✔
72
        if (this.userConfig.loggers.includes("mcp")) {
64✔
73
            loggers.push(new McpLogger(this.mcpServer));
4✔
74
        }
4✔
75
        if (this.userConfig.loggers.includes("disk")) {
64✔
76
            loggers.push(await DiskLogger.fromPath(this.userConfig.logPath));
4✔
77
        }
4✔
78
        if (this.userConfig.loggers.includes("stderr")) {
64✔
79
            loggers.push(new ConsoleLogger());
60✔
80
        }
60✔
81
        logger.setLoggers(...loggers);
64✔
82

83
        this.mcpServer.server.oninitialized = () => {
64✔
84
            this.session.setAgentRunner(this.mcpServer.server.getClientVersion());
64✔
85
            this.session.sessionId = new ObjectId().toString();
64✔
86

87
            logger.info(
64✔
88
                LogId.serverInitialized,
64✔
89
                "server",
64✔
90
                `Server started with transport ${transport.constructor.name} and agent runner ${this.session.agentRunner?.name}`
64✔
91
            );
64✔
92

93
            this.emitServerEvent("start", Date.now() - this.startTime);
64✔
94
        };
64✔
95

96
        this.mcpServer.server.onclose = () => {
64✔
97
            const closeTime = Date.now();
64✔
98
            this.emitServerEvent("stop", Date.now() - closeTime);
64✔
99
        };
64✔
100

101
        this.mcpServer.server.onerror = (error: Error) => {
64✔
NEW
102
            const closeTime = Date.now();
×
NEW
103
            this.emitServerEvent("stop", Date.now() - closeTime, error);
×
104
        };
×
105

106
        await this.mcpServer.connect(transport);
64✔
107
    }
64✔
108

109
    async close(): Promise<void> {
2✔
110
        await this.telemetry.close();
64✔
111
        await this.session.close();
64✔
112
        await this.mcpServer.close();
63✔
113
    }
64✔
114

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

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

146
        this.telemetry.emitEvents([event]).catch(() => {});
128✔
147
    }
128✔
148

149
    private registerTools() {
2✔
150
        for (const toolConstructor of [...AtlasTools, ...MongoDbTools]) {
64✔
151
            const tool = new toolConstructor(this.session, this.userConfig, this.telemetry);
2,048✔
152
            if (tool.register(this)) {
2,048✔
153
                this.tools.push(tool);
1,681✔
154
            }
1,681✔
155
        }
2,048✔
156
    }
64✔
157

158
    private registerResources() {
2✔
159
        for (const resourceConstructor of Resources) {
64✔
160
            const resource = new resourceConstructor(this, this.telemetry);
128✔
161
            resource.register();
128✔
162
        }
128✔
163
    }
64✔
164

165
    private async validateConfig(): Promise<void> {
2✔
166
        const transport = this.userConfig.transport as string;
64✔
167
        if (transport !== "http" && transport !== "stdio") {
64!
UNCOV
168
            throw new Error(`Invalid transport: ${transport}`);
×
UNCOV
169
        }
×
170

171
        const telemetry = this.userConfig.telemetry as string;
64✔
172
        if (telemetry !== "enabled" && telemetry !== "disabled") {
64!
UNCOV
173
            throw new Error(`Invalid telemetry: ${telemetry}`);
×
UNCOV
174
        }
×
175

176
        if (this.userConfig.httpPort < 1 || this.userConfig.httpPort > 65535) {
64!
177
            throw new Error(`Invalid httpPort: ${this.userConfig.httpPort}`);
×
UNCOV
178
        }
×
179

180
        if (this.userConfig.loggers.length === 0) {
64!
181
            throw new Error("No loggers found in config");
×
UNCOV
182
        }
×
183

184
        const loggerTypes = new Set(this.userConfig.loggers);
64✔
185
        if (loggerTypes.size !== this.userConfig.loggers.length) {
64!
UNCOV
186
            throw new Error("Duplicate loggers found in config");
×
UNCOV
187
        }
×
188

189
        for (const loggerType of this.userConfig.loggers as string[]) {
64✔
190
            if (loggerType !== "mcp" && loggerType !== "disk" && loggerType !== "stderr") {
68!
UNCOV
191
                throw new Error(`Invalid logger: ${loggerType}`);
×
UNCOV
192
            }
×
193
        }
68✔
194

195
        if (this.userConfig.connectionString) {
64✔
196
            try {
2✔
197
                await this.session.connectToMongoDB(this.userConfig.connectionString, this.userConfig.connectOptions);
2✔
198
            } catch (error) {
2!
NEW
199
                console.error(
×
NEW
200
                    "Failed to connect to MongoDB instance using the connection string from the config: ",
×
NEW
201
                    error
×
NEW
202
                );
×
203
                throw new Error("Failed to connect to MongoDB instance using the connection string from the config");
×
204
            }
×
205
        }
2✔
206

207
        if (this.userConfig.apiClientId && this.userConfig.apiClientSecret) {
64✔
208
            try {
36✔
209
                await this.session.apiClient.validateAccessToken();
36✔
210
            } catch (error) {
36!
UNCOV
211
                if (this.userConfig.connectionString === undefined) {
×
UNCOV
212
                    console.error("Failed to validate MongoDB Atlas the credentials from the config: ", error);
×
213

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

© 2026 Coveralls, Inc