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

mongodb-js / mongodb-mcp-server / 14788116886

02 May 2025 03:32AM UTC coverage: 74.619% (-7.8%) from 82.446%
14788116886

push

github

web-flow
Incorrect link of VSCode's MCP usage

- Change link of MCP usage

164 of 296 branches covered (55.41%)

Branch coverage included in aggregate %.

668 of 819 relevant lines covered (81.56%)

82.5 hits per line

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

76.4
/src/common/atlas/apiClient.ts
1
import createClient, { Client, Middleware } from "openapi-fetch";
2
import type { FetchOptions } from "openapi-fetch";
3
import { AccessToken, ClientCredentials } from "simple-oauth2";
4
import { ApiClientError } from "./apiClientError.js";
5
import { paths, operations } from "./openapi.js";
6
import { CommonProperties, TelemetryEvent } from "../../telemetry/types.js";
7
import { packageInfo } from "../../packageInfo.js";
8

9
const ATLAS_API_VERSION = "2025-03-12";
68✔
10

11
export interface ApiClientCredentials {
12
    clientId: string;
13
    clientSecret: string;
14
}
15

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

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

35
    private getAccessToken = async () => {
92✔
36
        if (this.oauth2Client && (!this.accessToken || this.accessToken.expired())) {
168✔
37
            this.accessToken = await this.oauth2Client.getToken({});
10✔
38
        }
39
        return this.accessToken?.token.access_token as string | undefined;
168✔
40
    };
41

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

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

58
    private readonly errorMiddleware: Middleware = {
92✔
59
        async onResponse({ response }) {
60
            if (!response.ok) {
164✔
61
                throw await ApiClientError.fromResponse(response);
6✔
62
            }
63
        },
64
    };
65

66
    constructor(options: ApiClientOptions) {
67
        this.options = {
92✔
68
            ...options,
69
            userAgent:
70
                options.userAgent ||
184✔
71
                `AtlasMCP/${packageInfo.version} (${process.platform}; ${process.arch}; ${process.env.HOSTNAME || "unknown"})`,
184✔
72
        };
73

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

97
    public hasCredentials(): boolean {
98
        return !!(this.oauth2Client && this.accessToken);
4!
99
    }
100

101
    public async getIpInfo(): Promise<{
102
        currentIpv4Address: string;
103
    }> {
104
        const accessToken = await this.getAccessToken();
4✔
105

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

117
        if (!response.ok) {
4!
118
            throw await ApiClientError.fromResponse(response);
×
119
        }
120

121
        return (await response.json()) as Promise<{
4✔
122
            currentIpv4Address: string;
123
        }>;
124
    }
125

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

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

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

147
        if (!response.ok) {
×
148
            throw await ApiClientError.fromResponse(response);
×
149
        }
150
    }
151

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

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

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

168
    async deleteProject(options: FetchOptions<operations["deleteProject"]>) {
169
        await this.client.DELETE("/api/atlas/v2/groups/{groupId}", options);
8✔
170
    }
171

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

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

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

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

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

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

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

205
    async getCluster(options: FetchOptions<operations["getCluster"]>) {
206
        const { data } = await this.client.GET("/api/atlas/v2/groups/{groupId}/clusters/{clusterName}", options);
86✔
207
        return data;
84✔
208
    }
209

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

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

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

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

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

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