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

mongodb-js / mongodb-mcp-server / 16135806470

08 Jul 2025 06:34AM UTC coverage: 75.613% (+1.4%) from 74.187%
16135806470

Pull #340

github

web-flow
Merge 76e006853 into 5b7ba55e0
Pull Request #340: chore: avoid logging files on disk and move them to console for containers

237 of 397 branches covered (59.7%)

Branch coverage included in aggregate %.

15 of 22 new or added lines in 3 files covered. (68.18%)

3 existing lines in 2 files now uncovered.

842 of 1030 relevant lines covered (81.75%)

58.93 hits per line

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

72.29
/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, { setStdioPreset, setContainerPreset, 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
import { detectContainerEnv } from "./common/container.js";
15

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

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

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

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

41
        this.registerTools();
33✔
42
        this.registerResources();
33✔
43

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

57
        assert(existingHandler, "No existing handler found for CallToolRequestSchema");
33✔
58

59
        this.mcpServer.server.setRequestHandler(CallToolRequestSchema, (request, extra): Promise<CallToolResult> => {
33✔
60
            if (!request.params.arguments) {
370✔
61
                request.params.arguments = {};
1✔
62
            }
63

64
            return existingHandler(request, extra);
370✔
65
        });
66

67
        const containerEnv = await detectContainerEnv();
33✔
68

69
        if (containerEnv) {
33!
NEW
70
            setContainerPreset(this.mcpServer);
×
71
        } else {
72
            await setStdioPreset(this.mcpServer, this.userConfig.logPath);
33✔
73
        }
74

75
        await this.mcpServer.connect(transport);
33✔
76

77
        this.mcpServer.server.oninitialized = () => {
33✔
78
            this.session.setAgentRunner(this.mcpServer.server.getClientVersion());
33✔
79
            this.session.sessionId = new ObjectId().toString();
33✔
80

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

87
            this.emitServerEvent("start", Date.now() - this.startTime);
33✔
88
        };
89

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

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

100
        await this.validateConfig();
33✔
101
    }
102

103
    async close(): Promise<void> {
104
        await this.telemetry.close();
33✔
105
        await this.session.close();
33✔
106
        await this.mcpServer.close();
33✔
107
    }
108

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

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

140
        this.telemetry.emitEvents([event]).catch(() => {});
66✔
141
    }
142

143
    private registerTools() {
144
        for (const tool of [...AtlasTools, ...MongoDbTools]) {
33✔
145
            new tool(this.session, this.userConfig, this.telemetry).register(this.mcpServer);
1,056✔
146
        }
147
    }
148

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

183
    private async validateConfig(): Promise<void> {
184
        if (this.userConfig.connectionString) {
33✔
185
            try {
1✔
186
                await this.session.connectToMongoDB(this.userConfig.connectionString, this.userConfig.connectOptions);
1✔
187
            } catch (error) {
188
                console.error(
×
189
                    "Failed to connect to MongoDB instance using the connection string from the config: ",
190
                    error
191
                );
192
                throw new Error("Failed to connect to MongoDB instance using the connection string from the config");
×
193
            }
194
        }
195

196
        if (this.userConfig.apiClientId && this.userConfig.apiClientSecret) {
33✔
197
            try {
8✔
198
                await this.session.apiClient.validateAccessToken();
8✔
199
            } catch (error) {
200
                if (this.userConfig.connectionString === undefined) {
×
201
                    console.error("Failed to validate MongoDB Atlas the credentials from the config: ", error);
×
202

203
                    throw new Error(
×
204
                        "Failed to connect to MongoDB Atlas instance using the credentials from the config"
205
                    );
206
                }
207
                console.error(
×
208
                    "Failed to validate MongoDB Atlas the credentials from the config, but validated the connection string."
209
                );
210
            }
211
        }
212
    }
213
}
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