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

mongodb-js / mongodb-mcp-server / 14669618209

25 Apr 2025 04:57PM UTC coverage: 82.257% (+0.6%) from 81.696%
14669618209

Pull #129

github

blva
remove log
Pull Request #129: chore: add config flags

134 of 210 branches covered (63.81%)

Branch coverage included in aggregate %.

1 of 1 new or added line in 1 file covered. (100.0%)

14 existing lines in 1 file now uncovered.

719 of 827 relevant lines covered (86.94%)

44.61 hits per line

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

79.17
/src/common/atlas/apiClient.ts
1
import createClient, { Client, Middleware } from "openapi-fetch";
31✔
2
import type { FetchOptions } from "openapi-fetch";
3
import { AccessToken, ClientCredentials } from "simple-oauth2";
31✔
4
import { ApiClientError } from "./apiClientError.js";
31✔
5
import { paths, operations } from "./openapi.js";
6
import { BaseEvent } from "../../telemetry/types.js";
7
import { mongoLogId } from "mongodb-log-writer";
8
import logger from "../../logger.js";
9
import { packageInfo } from "../../packageInfo.js";
31✔
10

11
const ATLAS_API_VERSION = "2025-03-12";
31✔
12

13
export interface ApiClientCredentials {
14
    clientId: string;
15
    clientSecret: string;
16
}
17

18
export interface ApiClientOptions {
19
    credentials?: ApiClientCredentials;
20
    baseUrl?: string;
21
    userAgent?: string;
22
}
23

24
export class ApiClient {
31✔
25
    private options: {
26
        baseUrl: string;
27
        userAgent: string;
28
        credentials?: {
29
            clientId: string;
30
            clientSecret: string;
31
        };
32
    };
33
    private client: Client<paths>;
34
    private oauth2Client?: ClientCredentials;
35
    private accessToken?: AccessToken;
36

37
    private getAccessToken = async () => {
27✔
38
        if (this.oauth2Client && (!this.accessToken || this.accessToken.expired())) {
47✔
39
            this.accessToken = await this.oauth2Client.getToken({});
5✔
40
        }
41
        return this.accessToken?.token.access_token as string | undefined;
47✔
42
    };
43

44
    private authMiddleware: Middleware = {
27✔
45
        onRequest: async ({ request, schemaPath }) => {
46
            if (schemaPath.startsWith("/api/private/unauth") || schemaPath.startsWith("/api/oauth")) {
45!
UNCOV
47
                return undefined;
×
48
            }
49

50
            try {
45✔
51
                const accessToken = await this.getAccessToken();
45✔
52
                request.headers.set("Authorization", `Bearer ${accessToken}`);
45✔
53
                return request;
45✔
54
            } catch {
55
                // ignore not availble tokens, API will return 401
56
            }
57
        },
58
    };
59

60
    private readonly errorMiddleware: Middleware = {
27✔
61
        async onResponse({ response }) {
62
            if (!response.ok) {
45✔
63
                throw await ApiClientError.fromResponse(response);
1✔
64
            }
65
        },
66
    };
67

68
    constructor(options?: ApiClientOptions) {
69
        this.options = {
27✔
70
            ...options,
71
            baseUrl: options?.baseUrl || "https://cloud.mongodb.com/",
49✔
72
            userAgent:
73
                options?.userAgent ||
54✔
74
                `AtlasMCP/${packageInfo.version} (${process.platform}; ${process.arch}; ${process.env.HOSTNAME || "unknown"})`,
54✔
75
        };
76

77
        this.client = createClient<paths>({
27✔
78
            baseUrl: this.options.baseUrl,
79
            headers: {
80
                "User-Agent": this.options.userAgent,
81
                Accept: `application/vnd.atlas.${ATLAS_API_VERSION}+json`,
82
            },
83
        });
84
        if (this.options.credentials?.clientId && this.options.credentials?.clientSecret) {
27✔
85
            this.oauth2Client = new ClientCredentials({
6✔
86
                client: {
87
                    id: this.options.credentials.clientId,
88
                    secret: this.options.credentials.clientSecret,
89
                },
90
                auth: {
91
                    tokenHost: this.options.baseUrl,
92
                    tokenPath: "/api/oauth/token",
93
                },
94
            });
95
            this.client.use(this.authMiddleware);
6✔
96
        }
97
        this.client.use(this.errorMiddleware);
27✔
98
    }
99

100
    public hasCredentials(): boolean {
101
        return !!(this.oauth2Client && this.accessToken);
283✔
102
    }
103

104
    public async getIpInfo(): Promise<{
105
        currentIpv4Address: string;
106
    }> {
107
        const accessToken = await this.getAccessToken();
2✔
108

109
        const endpoint = "api/private/ipinfo";
2✔
110
        const url = new URL(endpoint, this.options.baseUrl);
2✔
111
        const response = await fetch(url, {
2✔
112
            method: "GET",
113
            headers: {
114
                Accept: "application/json",
115
                Authorization: `Bearer ${accessToken}`,
116
                "User-Agent": this.options.userAgent,
117
            },
118
        });
119

120
        if (!response.ok) {
2!
UNCOV
121
            throw await ApiClientError.fromResponse(response);
×
122
        }
123

124
        return (await response.json()) as Promise<{
2✔
125
            currentIpv4Address: string;
126
        }>;
127
    }
128

129
    async sendEvents(events: BaseEvent[]): Promise<void> {
UNCOV
130
        let endpoint = "api/private/unauth/telemetry/events";
×
UNCOV
131
        const headers: Record<string, string> = {
×
132
            Accept: "application/json",
133
            "Content-Type": "application/json",
134
            "User-Agent": this.options.userAgent,
135
        };
136

137
        const accessToken = await this.getAccessToken();
×
138
        if (accessToken) {
×
UNCOV
139
            endpoint = "api/private/v1.0/telemetry/events";
×
UNCOV
140
            headers["Authorization"] = `Bearer ${accessToken}`;
×
141
        }
142

UNCOV
143
        const url = new URL(endpoint, this.options.baseUrl);
×
UNCOV
144
        const response = await fetch(url, {
×
145
            method: "POST",
146
            headers,
147
            body: JSON.stringify(events),
148
        });
149

UNCOV
150
        if (!response.ok) {
×
UNCOV
151
            throw await ApiClientError.fromResponse(response);
×
152
        }
153
    }
154

155
    // DO NOT EDIT. This is auto-generated code.
156
    async listClustersForAllProjects(options?: FetchOptions<operations["listClustersForAllProjects"]>) {
UNCOV
157
        const { data } = await this.client.GET("/api/atlas/v2/clusters", options);
×
UNCOV
158
        return data;
×
159
    }
160

161
    async listProjects(options?: FetchOptions<operations["listProjects"]>) {
162
        const { data } = await this.client.GET("/api/atlas/v2/groups", options);
2✔
163
        return data;
2✔
164
    }
165

166
    async createProject(options: FetchOptions<operations["createProject"]>) {
167
        const { data } = await this.client.POST("/api/atlas/v2/groups", options);
4✔
168
        return data;
4✔
169
    }
170

171
    async deleteProject(options: FetchOptions<operations["deleteProject"]>) {
172
        await this.client.DELETE("/api/atlas/v2/groups/{groupId}", options);
4✔
173
    }
174

175
    async getProject(options: FetchOptions<operations["getProject"]>) {
176
        const { data } = await this.client.GET("/api/atlas/v2/groups/{groupId}", options);
1✔
177
        return data;
1✔
178
    }
179

180
    async listProjectIpAccessLists(options: FetchOptions<operations["listProjectIpAccessLists"]>) {
181
        const { data } = await this.client.GET("/api/atlas/v2/groups/{groupId}/accessList", options);
1✔
182
        return data;
1✔
183
    }
184

185
    async createProjectIpAccessList(options: FetchOptions<operations["createProjectIpAccessList"]>) {
186
        const { data } = await this.client.POST("/api/atlas/v2/groups/{groupId}/accessList", options);
1✔
187
        return data;
1✔
188
    }
189

190
    async deleteProjectIpAccessList(options: FetchOptions<operations["deleteProjectIpAccessList"]>) {
191
        await this.client.DELETE("/api/atlas/v2/groups/{groupId}/accessList/{entryValue}", options);
5✔
192
    }
193

194
    async listClusters(options: FetchOptions<operations["listClusters"]>) {
195
        const { data } = await this.client.GET("/api/atlas/v2/groups/{groupId}/clusters", options);
1✔
196
        return data;
1✔
197
    }
198

199
    async createCluster(options: FetchOptions<operations["createCluster"]>) {
200
        const { data } = await this.client.POST("/api/atlas/v2/groups/{groupId}/clusters", options);
1✔
201
        return data;
1✔
202
    }
203

204
    async deleteCluster(options: FetchOptions<operations["deleteCluster"]>) {
205
        await this.client.DELETE("/api/atlas/v2/groups/{groupId}/clusters/{clusterName}", options);
1✔
206
    }
207

208
    async getCluster(options: FetchOptions<operations["getCluster"]>) {
209
        const { data } = await this.client.GET("/api/atlas/v2/groups/{groupId}/clusters/{clusterName}", options);
15✔
210
        return data;
14✔
211
    }
212

213
    async listDatabaseUsers(options: FetchOptions<operations["listDatabaseUsers"]>) {
214
        const { data } = await this.client.GET("/api/atlas/v2/groups/{groupId}/databaseUsers", options);
1✔
215
        return data;
1✔
216
    }
217

218
    async createDatabaseUser(options: FetchOptions<operations["createDatabaseUser"]>) {
219
        const { data } = await this.client.POST("/api/atlas/v2/groups/{groupId}/databaseUsers", options);
1✔
220
        return data;
1✔
221
    }
222

223
    async deleteDatabaseUser(options: FetchOptions<operations["deleteDatabaseUser"]>) {
224
        await this.client.DELETE("/api/atlas/v2/groups/{groupId}/databaseUsers/{databaseName}/{username}", options);
1✔
225
    }
226

227
    async listOrganizations(options?: FetchOptions<operations["listOrganizations"]>) {
228
        const { data } = await this.client.GET("/api/atlas/v2/orgs", options);
6✔
229
        return data;
6✔
230
    }
231

232
    async listOrganizationProjects(options: FetchOptions<operations["listOrganizationProjects"]>) {
UNCOV
233
        const { data } = await this.client.GET("/api/atlas/v2/orgs/{orgId}/groups", options);
×
UNCOV
234
        return data;
×
235
    }
236

237
    // DO NOT EDIT. This is auto-generated code.
238
}
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