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

mongodb-js / mongodb-mcp-server / 14714557796

28 Apr 2025 05:59PM UTC coverage: 82.713%. First build
14714557796

Pull #151

github

blva
chore: default telemetry
Pull Request #151: chore: default telemetry

142 of 235 branches covered (60.43%)

Branch coverage included in aggregate %.

791 of 893 relevant lines covered (88.58%)

48.19 hits per line

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

78.57
/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";
31✔
5
import { MongoDbTools } from "./tools/mongodb/tools.js";
31✔
6
import logger, { initializeLogger, LogId } from "./logger.js";
31✔
7
import { ObjectId } from "mongodb";
31✔
8
import { Telemetry } from "./telemetry/telemetry.js";
31✔
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";
31✔
13
import assert from "assert";
31✔
14

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

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

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

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

39
        this.registerTools();
27✔
40
        this.registerResources();
27✔
41

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

55
        assert(existingHandler, "No existing handler found for CallToolRequestSchema");
27✔
56

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

62
            return existingHandler(request, extra);
200✔
63
        });
64

65
        await initializeLogger(this.mcpServer, this.userConfig.logPath);
27✔
66

67
        await this.mcpServer.connect(transport);
27✔
68

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

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

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

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

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

92
        await this.validateConfig();
27✔
93
    }
94

95
    async close(): Promise<void> {
96
        await this.session.close();
27✔
97
        await this.mcpServer.close();
27✔
98
    }
99

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

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

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

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

141
    private registerResources() {
142
        this.mcpServer.resource(
27✔
143
            "config",
144
            "config://config",
145
            {
146
                description:
147
                    "Server configuration, supplied by the user either as environment variables or as startup arguments",
148
            },
149
            (uri) => {
150
                const result = {
×
151
                    telemetry: this.userConfig.telemetry,
152
                    logPath: this.userConfig.logPath,
153
                    connectionString: this.userConfig.connectionString
×
154
                        ? "set; no explicit connect needed, use switch-connection tool to connect to a different connection if necessary"
155
                        : "not set; before using any mongodb tool, you need to call the connect tool with a connection string",
156
                    connectOptions: this.userConfig.connectOptions,
157
                };
158
                return {
×
159
                    contents: [
160
                        {
161
                            text: JSON.stringify(result),
162
                            uri: uri.href,
163
                        },
164
                    ],
165
                };
166
            }
167
        );
168
        if (this.userConfig.connectionString) {
27✔
169
            this.mcpServer.resource(
20✔
170
                "connection-string",
171
                "config://connection-string",
172
                {
173
                    description: "Preconfigured connection string that will be used as a default in the `connect` tool",
174
                },
175
                (uri) => {
176
                    return {
×
177
                        contents: [
178
                            {
179
                                text: `Preconfigured connection string: ${this.userConfig.connectionString}`,
180
                                uri: uri.href,
181
                            },
182
                        ],
183
                    };
184
                }
185
            );
186
        }
187
    }
188

189
    private async validateConfig(): Promise<void> {
190
        const isAtlasConfigured = this.userConfig.apiClientId && this.userConfig.apiClientSecret;
27✔
191
        const isMongoDbConfigured = this.userConfig.connectionString;
27✔
192
        if (!isAtlasConfigured && !isMongoDbConfigured) {
27!
193
            console.error(
×
194
                "Either Atlas Client Id or a MongoDB connection string must be configured - you can provide them as environment variables or as startup arguments. \n" +
195
                    "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" +
196
                    "Provide the MongoDB connection string as `MDB_MCP_CONNECTION_STRING` environment variable or as `--connectionString` startup argument."
197
            );
198
            throw new Error("Either Atlas Client Id or a MongoDB connection string must be configured");
×
199
        }
200

201
        if (this.userConfig.connectionString) {
27✔
202
            try {
20✔
203
                await this.session.connectToMongoDB(this.userConfig.connectionString, this.userConfig.connectOptions);
20✔
204
            } catch (error) {
205
                console.error(
×
206
                    "Failed to connect to MongoDB instance using the connection string from the config: ",
207
                    error
208
                );
209
                throw new Error("Failed to connect to MongoDB instance using the connection string from the config");
×
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

© 2026 Coveralls, Inc