Skip to content

Commit 197b800

Browse files
committed
Merge branch 'main' into fmenezes/atlas_connect_tool
2 parents bd25b0c + 355fbf2 commit 197b800

File tree

22 files changed

+368
-113
lines changed

22 files changed

+368
-113
lines changed

.github/workflows/code_health.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ jobs:
6363
path: coverage/lcov.info
6464

6565
coverage:
66-
name: Run MongoDB tests
66+
name: Report Coverage
6767
if: always() && github.event.pull_request.user.login != 'dependabot[bot]' && github.event.pull_request.head.repo.full_name == github.repository
6868
runs-on: ubuntu-latest
6969
needs: [run-tests, run-atlas-tests]

src/common/atlas/apiClient.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export interface ApiClientCredentials {
1515

1616
export interface ApiClientOptions {
1717
credentials?: ApiClientCredentials;
18-
baseUrl?: string;
18+
baseUrl: string;
1919
userAgent?: string;
2020
}
2121

@@ -63,12 +63,11 @@ export class ApiClient {
6363
},
6464
};
6565

66-
constructor(options?: ApiClientOptions) {
66+
constructor(options: ApiClientOptions) {
6767
this.options = {
6868
...options,
69-
baseUrl: options?.baseUrl || "https://cloud.mongodb.com/",
7069
userAgent:
71-
options?.userAgent ||
70+
options.userAgent ||
7271
`AtlasMCP/${packageInfo.version} (${process.platform}; ${process.arch}; ${process.env.HOSTNAME || "unknown"})`,
7372
};
7473

src/config.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,29 @@ import argv from "yargs-parser";
44

55
import { ReadConcernLevel, ReadPreferenceMode, W } from "mongodb";
66

7+
export interface ConnectOptions {
8+
readConcern: ReadConcernLevel;
9+
readPreference: ReadPreferenceMode;
10+
writeConcern: W;
11+
timeoutMS: number;
12+
}
13+
714
// If we decide to support non-string config options, we'll need to extend the mechanism for parsing
815
// env variables.
916
export interface UserConfig {
10-
apiBaseUrl?: string;
17+
apiBaseUrl: string;
1118
apiClientId?: string;
1219
apiClientSecret?: string;
1320
telemetry?: "enabled" | "disabled";
1421
logPath: string;
1522
connectionString?: string;
16-
connectOptions: {
17-
readConcern: ReadConcernLevel;
18-
readPreference: ReadPreferenceMode;
19-
writeConcern: W;
20-
timeoutMS: number;
21-
};
23+
connectOptions: ConnectOptions;
2224
disabledTools: Array<string>;
2325
readOnly?: boolean;
2426
}
2527

2628
const defaults: UserConfig = {
29+
apiBaseUrl: "https://cloud.mongodb.com/",
2730
logPath: getLogPath(),
2831
connectOptions: {
2932
readConcern: "local",

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ try {
1818
name: packageInfo.mcpServerName,
1919
version: packageInfo.version,
2020
});
21+
2122
const server = new Server({
2223
mcpServer,
2324
session,

src/server.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export class Server {
3333
this.userConfig = userConfig;
3434
}
3535

36-
async connect(transport: Transport) {
36+
async connect(transport: Transport): Promise<void> {
3737
this.mcpServer.server.registerCapabilities({ logging: {} });
3838

3939
this.registerTools();
@@ -88,6 +88,8 @@ export class Server {
8888
const closeTime = Date.now();
8989
this.emitServerEvent("stop", Date.now() - closeTime, error);
9090
};
91+
92+
await this.validateConfig();
9193
}
9294

9395
async close(): Promise<void> {
@@ -183,4 +185,29 @@ export class Server {
183185
);
184186
}
185187
}
188+
189+
private async validateConfig(): Promise<void> {
190+
const isAtlasConfigured = this.userConfig.apiClientId && this.userConfig.apiClientSecret;
191+
const isMongoDbConfigured = this.userConfig.connectionString;
192+
if (!isAtlasConfigured && !isMongoDbConfigured) {
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) {
202+
try {
203+
await this.session.connectToMongoDB(this.userConfig.connectionString, this.userConfig.connectOptions);
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+
}
186213
}

src/session.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ import { ApiClient, ApiClientCredentials } from "./common/atlas/apiClient.js";
33
import { Implementation } from "@modelcontextprotocol/sdk/types.js";
44
import logger, { LogId } from "./logger.js";
55
import EventEmitter from "events";
6+
import { ConnectOptions } from "./config.js";
67

78
export interface SessionOptions {
8-
apiBaseUrl?: string;
9+
apiBaseUrl: string;
910
apiClientId?: string;
1011
apiClientSecret?: string;
1112
}
@@ -28,7 +29,7 @@ export class Session extends EventEmitter<{
2829
expiryDate: Date;
2930
};
3031

31-
constructor({ apiBaseUrl, apiClientId, apiClientSecret }: SessionOptions = {}) {
32+
constructor({ apiBaseUrl, apiClientId, apiClientSecret }: SessionOptions) {
3233
super();
3334

3435
const credentials: ApiClientCredentials | undefined =
@@ -96,4 +97,21 @@ export class Session extends EventEmitter<{
9697
await this.disconnect();
9798
this.emit("close");
9899
}
100+
101+
async connectToMongoDB(connectionString: string, connectOptions: ConnectOptions): Promise<void> {
102+
const provider = await NodeDriverServiceProvider.connect(connectionString, {
103+
productDocsLink: "https://docs.mongodb.com/todo-mcp",
104+
productName: "MongoDB MCP",
105+
readConcern: {
106+
level: connectOptions.readConcern,
107+
},
108+
readPreference: connectOptions.readPreference,
109+
writeConcern: {
110+
w: connectOptions.writeConcern,
111+
},
112+
timeoutMS: connectOptions.timeoutMS,
113+
});
114+
115+
this.serviceProvider = provider;
116+
}
99117
}

src/tools/atlas/metadata/connectCluster.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ export class ConnectClusterTool extends AtlasToolBase {
100100
cn.searchParams.set("authSource", "admin");
101101
const connectionString = cn.toString();
102102

103-
await this.connectToMongoDB(connectionString);
103+
await this.session.connectToMongoDB(connectionString, this.config.connectOptions);
104104

105105
return {
106106
content: [

src/tools/mongodb/create/insertMany.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export class InsertManyTool extends MongoDBToolBase {
99
protected argsShape = {
1010
...DbOperationArgs,
1111
documents: z
12-
.array(z.object({}).passthrough().describe("An individual MongoDB document"))
12+
.array(z.record(z.string(), z.unknown()).describe("An individual MongoDB document"))
1313
.describe(
1414
"The array of documents to insert, matching the syntax of the document argument of db.collection.insertMany()"
1515
),

src/tools/mongodb/delete/deleteMany.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@ export class DeleteManyTool extends MongoDBToolBase {
99
protected argsShape = {
1010
...DbOperationArgs,
1111
filter: z
12-
.object({})
13-
.passthrough()
12+
.record(z.string(), z.unknown())
1413
.optional()
1514
.describe(
1615
"The query filter, specifying the deletion criteria. Matches the syntax of the filter argument of db.collection.deleteMany()"

src/tools/mongodb/mongodbTool.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,8 @@ export abstract class MongoDBToolBase extends ToolBase {
6969

7070
return super.handleError(error, args);
7171
}
72+
73+
protected connectToMongoDB(connectionString: string): Promise<void> {
74+
return this.session.connectToMongoDB(connectionString, this.config.connectOptions);
75+
}
7276
}

0 commit comments

Comments
 (0)