Skip to content

Commit 71a3885

Browse files
committed
refactor: adapt to latest structure
1 parent c202de5 commit 71a3885

File tree

10 files changed

+101
-107
lines changed

10 files changed

+101
-107
lines changed

src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ import { mongoLogId } from "mongodb-log-writer";
66
import { ApiClient } from "./common/atlas/apiClient.js";
77
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
88
import config from "./config.js";
9-
import { State } from "./state.js";
9+
import { Session } from "./session.js";
1010
import { Server } from "./server.js";
1111

1212
try {
13-
const state = new State();
13+
const state = new Session();
1414
const mcpServer = new McpServer({
1515
name: "MongoDB Atlas",
1616
version: config.version,

src/server.ts

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,50 @@
11
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2-
import { State } from "./state.js";
2+
import { Session } from "./session.js";
33
import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
4-
import { registerAtlasTools } from "./tools/atlas/tools.js";
5-
import { registerMongoDBTools } from "./tools/mongodb/index.js";
4+
import { AtlasTools } from "./tools/atlas/tools.js";
5+
import { MongoDbTools } from "./tools/mongodb/index.js";
66
import logger, { initializeLogger } from "./logger.js";
77
import { mongoLogId } from "mongodb-log-writer";
8-
import { ApiClient } from "./common/atlas/apiClient.js";
98

109
export class Server {
11-
public readonly state: State;
10+
public readonly session: Session;
1211
private readonly mcpServer: McpServer;
12+
private readonly transport: Transport;
1313

14-
constructor({ mcpServer, state, transport }: { mcpServer: McpServer; state: State; transport: Transport }) {
15-
this.mcpServer = server;
16-
this.state = new State();
14+
constructor({ mcpServer, transport, session }: { mcpServer: McpServer; session: Session; transport: Transport }) {
15+
this.mcpServer = mcpServer;
16+
this.transport = transport;
17+
this.session = session;
1718
}
1819

19-
async connect(transport: Transport) {
20-
this.server = new McpServer({
21-
name: "MongoDB Atlas",
22-
version: config.version,
23-
});
20+
async connect() {
21+
this.mcpServer.server.registerCapabilities({ logging: {} });
2422

25-
this.server.server.registerCapabilities({ logging: {} });
23+
this.registerTools();
2624

27-
registerAtlasTools(this.server, this.state);
28-
registerMongoDBTools(this.server, this.state);
25+
await initializeLogger(this.mcpServer);
2926

30-
await initializeLogger(this.server);
31-
await this.server.connect(transport);
27+
await this.mcpServer.connect(this.transport);
3228

33-
logger.info(mongoLogId(1_000_004), "server", `Server started with transport ${transport.constructor.name}`);
29+
logger.info(
30+
mongoLogId(1_000_004),
31+
"server",
32+
`Server started with transport ${this.transport.constructor.name}`
33+
);
3434
}
3535

3636
async close(): Promise<void> {
3737
try {
38-
await this.state.serviceProvider?.close(true);
38+
await this.session.serviceProvider?.close(true);
3939
} catch {
4040
// Ignore errors during service provider close
4141
}
42-
await this.server?.close();
42+
await this.mcpServer?.close();
43+
}
44+
45+
private registerTools() {
46+
for (const tool of [...AtlasTools, ...MongoDbTools]) {
47+
new tool(this.session).register(this.mcpServer);
48+
}
4349
}
4450
}

src/state.ts renamed to src/session.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import { NodeDriverServiceProvider } from "@mongosh/service-provider-node-driver
22
import { ApiClient } from "./common/atlas/apiClient.js";
33
import config from "./config.js";
44

5-
export class State {
5+
export class Session {
6+
serviceProvider?: NodeDriverServiceProvider;
67
apiClient?: ApiClient;
78

89
ensureApiClient(): asserts this is { apiClient: ApiClient } {

src/tools/atlas/atlasTool.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,29 @@
11
import { ToolBase } from "../tool.js";
2-
import { State } from "../../state.js";
2+
import { ApiClient } from "../../common/atlas/apiClient.js";
3+
import { Session } from "../../session.js";
34

45
export abstract class AtlasToolBase extends ToolBase {
5-
constructor(state: State) {
6+
private apiClient?: ApiClient;
7+
8+
ensureApiClient(): asserts this is { apiClient: ApiClient } {
9+
if (!this.apiClient) {
10+
if (!config.apiClientId || !config.apiClientSecret) {
11+
throw new Error(
12+
"Not authenticated make sure to configure MCP server with MDB_MCP_API_CLIENT_ID and MDB_MCP_API_CLIENT_SECRET environment variables."
13+
);
14+
}
15+
16+
this.apiClient = new ApiClient({
17+
baseUrl: config.apiBaseUrl,
18+
credentials: {
19+
clientId: config.apiClientId,
20+
clientSecret: config.apiClientSecret,
21+
},
22+
});
23+
}
24+
}
25+
26+
constructor(protected readonly session: Session) {
627
super(state);
728
}
829
}

src/tools/atlas/createDBUser.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export class CreateDBUserTool extends AtlasToolBase {
3333
roles,
3434
clusters,
3535
}: ToolArgs<typeof this.argsShape>): Promise<CallToolResult> {
36-
this.state.ensureApiClient();
36+
this.ensureApiClient();
3737

3838
const input = {
3939
groupId: projectId,

src/tools/atlas/tools.ts

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
import { ToolBase } from "../tool.js";
2-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3-
import { State } from "../../state.js";
41
import { ListClustersTool } from "./listClusters.js";
52
import { ListProjectsTool } from "./listProjects.js";
63
import { InspectClusterTool } from "./inspectCluster.js";
@@ -10,19 +7,13 @@ import { InspectAccessListTool } from "./inspectAccessList.js";
107
import { ListDBUsersTool } from "./listDBUsers.js";
118
import { CreateDBUserTool } from "./createDBUser.js";
129

13-
export function registerAtlasTools(server: McpServer, state: State) {
14-
const tools: ToolBase[] = [
15-
new ListClustersTool(state),
16-
new ListProjectsTool(state),
17-
new InspectClusterTool(state),
18-
new CreateFreeClusterTool(state),
19-
new CreateAccessListTool(state),
20-
new InspectAccessListTool(state),
21-
new ListDBUsersTool(state),
22-
new CreateDBUserTool(state),
23-
];
24-
25-
for (const tool of tools) {
26-
tool.register(server);
27-
}
28-
}
10+
export const AtlasTools = [
11+
ListClustersTool,
12+
ListProjectsTool,
13+
InspectClusterTool,
14+
CreateFreeClusterTool,
15+
CreateAccessListTool,
16+
InspectAccessListTool,
17+
ListDBUsersTool,
18+
CreateDBUserTool,
19+
];

src/tools/mongodb/index.ts

Lines changed: 23 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2-
import { State } from "../../state.js";
2+
import { State } from "../../session.js";
33
import { ConnectTool } from "./connect.js";
44
import { ListCollectionsTool } from "./metadata/listCollections.js";
55
import { CollectionIndexesTool } from "./collectionIndexes.js";
@@ -21,32 +21,25 @@ import { RenameCollectionTool } from "./update/renameCollection.js";
2121
import { DropDatabaseTool } from "./delete/dropDatabase.js";
2222
import { DropCollectionTool } from "./delete/dropCollection.js";
2323

24-
export function registerMongoDBTools(server: McpServer, state: State) {
25-
const tools = [
26-
ConnectTool,
27-
ListCollectionsTool,
28-
ListDatabasesTool,
29-
CollectionIndexesTool,
30-
CreateIndexTool,
31-
CollectionSchemaTool,
32-
InsertOneTool,
33-
FindTool,
34-
InsertManyTool,
35-
DeleteManyTool,
36-
DeleteOneTool,
37-
CollectionStorageSizeTool,
38-
CountTool,
39-
DbStatsTool,
40-
AggregateTool,
41-
UpdateOneTool,
42-
UpdateManyTool,
43-
RenameCollectionTool,
44-
DropDatabaseTool,
45-
DropCollectionTool,
46-
];
47-
48-
for (const tool of tools) {
49-
const instance = new tool(state);
50-
instance.register(server);
51-
}
52-
}
24+
export const MongoDbTools = [
25+
ConnectTool,
26+
ListCollectionsTool,
27+
ListDatabasesTool,
28+
CollectionIndexesTool,
29+
CreateIndexTool,
30+
CollectionSchemaTool,
31+
InsertOneTool,
32+
FindTool,
33+
InsertManyTool,
34+
DeleteManyTool,
35+
DeleteOneTool,
36+
CollectionStorageSizeTool,
37+
CountTool,
38+
DbStatsTool,
39+
AggregateTool,
40+
UpdateOneTool,
41+
UpdateManyTool,
42+
RenameCollectionTool,
43+
DropDatabaseTool,
44+
DropCollectionTool,
45+
];

src/tools/mongodb/mongodbTool.ts

Lines changed: 9 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { z } from "zod";
22
import { ToolBase } from "../tool.js";
3+
import { State } from "../../state.js";
34
import { NodeDriverServiceProvider } from "@mongosh/service-provider-node-driver";
45
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
56
import { ErrorCodes, MongoDBError } from "../../errors.js";
6-
import config from "../../config.js";
77

88
export const DbOperationArgs = {
99
database: z.string().describe("Database name"),
@@ -13,25 +13,22 @@ export const DbOperationArgs = {
1313
export type DbOperationType = "metadata" | "read" | "create" | "update" | "delete";
1414

1515
export abstract class MongoDBToolBase extends ToolBase {
16-
constructor(private serviceProvider: NodeDriverServiceProvider | undefined) {
17-
super();
16+
constructor(state: State) {
17+
super(state);
1818
}
1919

2020
protected abstract operationType: DbOperationType;
2121

22-
protected async ensureConnected(): Promise<NodeDriverServiceProvider> {
23-
if (!this.serviceProvider && config.connectionString) {
24-
this.serviceProvider = await this.connectToMongoDB(config.connectionString);
25-
}
26-
27-
if (!this.serviceProvider) {
22+
protected ensureConnected(): NodeDriverServiceProvider {
23+
const provider = this.state.serviceProvider;
24+
if (!provider) {
2825
throw new MongoDBError(ErrorCodes.NotConnectedToMongoDB, "Not connected to MongoDB");
2926
}
3027

31-
return this.serviceProvider;
28+
return provider;
3229
}
3330

34-
protected handleError(error: unknown): Promise<CallToolResult> | CallToolResult {
31+
protected handleError(error: unknown): CallToolResult | undefined {
3532
if (error instanceof MongoDBError && error.code === ErrorCodes.NotConnectedToMongoDB) {
3633
return {
3734
content: [
@@ -44,25 +41,9 @@ export abstract class MongoDBToolBase extends ToolBase {
4441
text: "Please use the 'connect' tool to connect to a MongoDB instance.",
4542
},
4643
],
47-
isError: true,
4844
};
4945
}
5046

51-
return super.handleError(error);
52-
}
53-
54-
protected async connectToMongoDB(connectionString: string): Promise<NodeDriverServiceProvider> {
55-
return NodeDriverServiceProvider.connect(connectionString, {
56-
productDocsLink: "https://docs.mongodb.com/todo-mcp",
57-
productName: "MongoDB MCP",
58-
readConcern: {
59-
level: config.connectOptions.readConcern,
60-
},
61-
readPreference: config.connectOptions.readPreference,
62-
writeConcern: {
63-
w: config.connectOptions.writeConcern,
64-
},
65-
timeoutMS: config.connectOptions.timeoutMS,
66-
});
47+
return undefined;
6748
}
6849
}

src/tools/tool.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { McpServer, ToolCallback } from "@modelcontextprotocol/sdk/server/mcp.js";
22
import { z, ZodNever, ZodRawShape } from "zod";
33
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
4+
import { State } from "../session.js";
45
import logger from "../logger.js";
56
import { mongoLogId } from "mongodb-log-writer";
67

@@ -15,7 +16,7 @@ export abstract class ToolBase {
1516

1617
protected abstract execute(...args: Parameters<ToolCallback<typeof this.argsShape>>): Promise<CallToolResult>;
1718

18-
protected constructor() {}
19+
protected constructor(protected state: State) {}
1920

2021
public register(server: McpServer): void {
2122
const callback: ToolCallback<typeof this.argsShape> = async (...args) => {

tests/unit/index.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, it } from "@jest/globals";
2-
import { State } from "../../src/state";
2+
import { Session } from "../../src/session";
33

44
// mock the StdioServerTransport
55
jest.mock("@modelcontextprotocol/sdk/server/stdio");
@@ -20,7 +20,7 @@ jest.mock("../../src/server.ts", () => {
2020

2121
describe("Server initialization", () => {
2222
it("should define a default state", async () => {
23-
const state = new State();
23+
const state = new Session();
2424

2525
expect(state.credentials).toEqual({
2626
auth: {

0 commit comments

Comments
 (0)