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

mongodb-js / mongodb-mcp-server / 17155288437

22 Aug 2025 12:29PM UTC coverage: 82.014% (-0.03%) from 82.04%
17155288437

Pull #468

github

web-flow
Merge 3bb127c20 into ca68195c4
Pull Request #468: chore: update readme with the new tool and resources

855 of 1075 branches covered (79.53%)

Branch coverage included in aggregate %.

2 of 2 new or added lines in 2 files covered. (100.0%)

97 existing lines in 8 files now uncovered.

4357 of 5280 relevant lines covered (82.52%)

70.4 hits per line

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

89.07
/src/telemetry/telemetry.ts
1
import { Session } from "../common/session.js";
2
import { BaseEvent, CommonProperties } from "./types.js";
3
import { UserConfig } from "../common/config.js";
4
import { LogId } from "../common/logger.js";
2✔
5
import { ApiClient } from "../common/atlas/apiClient.js";
6
import { MACHINE_METADATA } from "./constants.js";
2✔
7
import { EventCache } from "./eventCache.js";
2✔
8
import { detectContainerEnv } from "../helpers/container.js";
2✔
9
import { DeviceId } from "../helpers/deviceId.js";
10

11
type EventResult = {
12
    success: boolean;
13
    error?: Error;
14
};
15

16
export class Telemetry {
2✔
17
    private isBufferingEvents: boolean = true;
2✔
18
    /** Resolves when the setup is complete or a timeout occurs */
19
    public setupPromise: Promise<[string, boolean]> | undefined;
20
    private eventCache: EventCache;
21
    private deviceId: DeviceId;
22

23
    private constructor(
2✔
24
        private readonly session: Session,
108✔
25
        private readonly userConfig: UserConfig,
108✔
26
        private readonly commonProperties: CommonProperties,
108✔
27
        { eventCache, deviceId }: { eventCache: EventCache; deviceId: DeviceId }
108✔
28
    ) {
108✔
29
        this.eventCache = eventCache;
108✔
30
        this.deviceId = deviceId;
108✔
31
    }
108✔
32

33
    static create(
2✔
34
        session: Session,
108✔
35
        userConfig: UserConfig,
108✔
36
        deviceId: DeviceId,
108✔
37
        {
108✔
38
            commonProperties = { ...MACHINE_METADATA },
108✔
39
            eventCache = EventCache.getInstance(),
108✔
40
        }: {
108✔
41
            eventCache?: EventCache;
42
            commonProperties?: CommonProperties;
43
        } = {}
108✔
44
    ): Telemetry {
108✔
45
        const instance = new Telemetry(session, userConfig, commonProperties, { eventCache, deviceId });
108✔
46

47
        void instance.setup();
108✔
48
        return instance;
108✔
49
    }
108✔
50

51
    private async setup(): Promise<void> {
2✔
52
        if (!this.isTelemetryEnabled()) {
108✔
53
            return;
82✔
54
        }
82✔
55

56
        this.setupPromise = Promise.all([this.deviceId.get(), detectContainerEnv()]);
26✔
57
        const [deviceIdValue, containerEnv] = await this.setupPromise;
26✔
58

59
        this.commonProperties.device_id = deviceIdValue;
26✔
60
        this.commonProperties.is_container_env = containerEnv;
26✔
61

62
        this.isBufferingEvents = false;
26✔
63
    }
108✔
64

65
    public async close(): Promise<void> {
2✔
66
        this.isBufferingEvents = false;
80✔
67
        await this.emitEvents(this.eventCache.getEvents());
80✔
68
    }
80✔
69

70
    /**
71
     * Emits events through the telemetry pipeline
72
     * @param events - The events to emit
73
     */
74
    public async emitEvents(events: BaseEvent[]): Promise<void> {
2✔
75
        try {
250✔
76
            if (!this.isTelemetryEnabled()) {
250✔
77
                this.session.logger.info({
244✔
78
                    id: LogId.telemetryEmitFailure,
244✔
79
                    context: "telemetry",
244✔
80
                    message: "Telemetry is disabled.",
244✔
81
                    noRedaction: true,
244✔
82
                });
244✔
83
                return;
244✔
84
            }
244✔
85

86
            await this.emit(events);
6✔
87
        } catch {
10!
UNCOV
88
            this.session.logger.debug({
×
UNCOV
89
                id: LogId.telemetryEmitFailure,
×
UNCOV
90
                context: "telemetry",
×
UNCOV
91
                message: "Error emitting telemetry events.",
×
UNCOV
92
                noRedaction: true,
×
UNCOV
93
            });
×
UNCOV
94
        }
×
95
    }
250✔
96

97
    /**
98
     * Gets the common properties for events
99
     * @returns Object containing common properties for all events
100
     */
101
    public getCommonProperties(): CommonProperties {
2✔
102
        return {
32✔
103
            ...this.commonProperties,
32✔
104
            transport: this.userConfig.transport,
32✔
105
            mcp_client_version: this.session.mcpClient?.version,
32✔
106
            mcp_client_name: this.session.mcpClient?.name,
32✔
107
            session_id: this.session.sessionId,
32✔
108
            config_atlas_auth: this.session.apiClient.hasCredentials() ? "true" : "false",
32✔
109
            config_connection_string: this.userConfig.connectionString ? "true" : "false",
32!
110
        };
32✔
111
    }
32✔
112

113
    /**
114
     * Checks if telemetry is currently enabled
115
     * This is a method rather than a constant to capture runtime config changes
116
     *
117
     * Follows the Console Do Not Track standard (https://consoledonottrack.com/)
118
     * by respecting the DO_NOT_TRACK environment variable
119
     */
120
    public isTelemetryEnabled(): boolean {
2✔
121
        // Check if telemetry is explicitly disabled in config
122
        if (this.userConfig.telemetry === "disabled") {
1,018✔
123
            return false;
984✔
124
        }
984✔
125

126
        const doNotTrack = "DO_NOT_TRACK" in process.env;
34✔
127
        return !doNotTrack;
34✔
128
    }
1,018✔
129

130
    /**
131
     * Attempts to emit events through authenticated and unauthenticated clients
132
     * Falls back to caching if both attempts fail
133
     */
134
    private async emit(events: BaseEvent[]): Promise<void> {
2✔
135
        if (this.isBufferingEvents) {
6!
UNCOV
136
            this.eventCache.appendEvents(events);
×
UNCOV
137
            return;
×
UNCOV
138
        }
×
139

140
        const cachedEvents = this.eventCache.getEvents();
6✔
141
        const allEvents = [...cachedEvents, ...events];
6✔
142

143
        this.session.logger.debug({
6✔
144
            id: LogId.telemetryEmitStart,
6✔
145
            context: "telemetry",
6✔
146
            message: `Attempting to send ${allEvents.length} events (${cachedEvents.length} cached)`,
6✔
147
        });
6✔
148

149
        const result = await this.sendEvents(this.session.apiClient, allEvents);
6✔
150
        if (result.success) {
6✔
151
            this.eventCache.clearEvents();
4✔
152
            this.session.logger.debug({
4✔
153
                id: LogId.telemetryEmitSuccess,
4✔
154
                context: "telemetry",
4✔
155
                message: `Sent ${allEvents.length} events successfully: ${JSON.stringify(allEvents, null, 2)}`,
4✔
156
            });
4✔
157
            return;
4✔
158
        }
4✔
159

160
        this.session.logger.debug({
2✔
161
            id: LogId.telemetryEmitFailure,
2✔
162
            context: "telemetry",
2✔
163
            message: `Error sending event to client: ${result.error instanceof Error ? result.error.message : String(result.error)}`,
6!
164
        });
6✔
165
        this.eventCache.appendEvents(events);
6✔
166
    }
6✔
167

168
    /**
169
     * Attempts to send events through the provided API client
170
     */
171
    private async sendEvents(client: ApiClient, events: BaseEvent[]): Promise<EventResult> {
2✔
172
        try {
6✔
173
            await client.sendEvents(
6✔
174
                events.map((event) => ({
6✔
175
                    ...event,
8✔
176
                    properties: { ...this.getCommonProperties(), ...event.properties },
8✔
177
                }))
6✔
178
            );
6✔
179
            return { success: true };
4✔
180
        } catch (error) {
6✔
181
            return {
2✔
182
                success: false,
2✔
183
                error: error instanceof Error ? error : new Error(String(error)),
2!
184
            };
2✔
185
        }
2✔
186
    }
6✔
187
}
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

© 2025 Coveralls, Inc