Skip to content

Commit 2a87387

Browse files
committed
refactor: adapt to the latest structure
1 parent 8e4f19c commit 2a87387

18 files changed

+54
-104
lines changed

src/common/atlas/apiClient.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,21 +55,21 @@ export class ApiClient {
5555
return this.accessToken?.token.access_token as string | undefined;
5656
};
5757

58-
private authMiddleware = (apiClient: ApiClient): Middleware => ({
59-
async onRequest({ request, schemaPath }) {
58+
private authMiddleware: Middleware = {
59+
onRequest: async ({ request, schemaPath }) => {
6060
if (schemaPath.startsWith("/api/private/unauth") || schemaPath.startsWith("/api/oauth")) {
6161
return undefined;
6262
}
6363

6464
try {
65-
const accessToken = await apiClient.getAccessToken();
65+
const accessToken = await this.getAccessToken();
6666
request.headers.set("Authorization", `Bearer ${accessToken}`);
6767
return request;
6868
} catch {
6969
// ignore not availble tokens, API will return 401
7070
}
7171
},
72-
});
72+
};
7373

7474
private readonly errorMiddleware: Middleware = {
7575
async onResponse({ response }) {
@@ -108,7 +108,7 @@ export class ApiClient {
108108
tokenPath: "/api/oauth/token",
109109
},
110110
});
111-
this.client.use(this.authMiddleware(this));
111+
this.client.use(this.authMiddleware);
112112
}
113113
this.client.use(this.errorMiddleware);
114114
}

src/index.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,26 @@
33
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
44
import logger from "./logger.js";
55
import { mongoLogId } from "mongodb-log-writer";
6-
import { ApiClient } from "./common/atlas/apiClient.js";
76
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
87
import config from "./config.js";
98
import { Session } from "./session.js";
109
import { Server } from "./server.js";
1110

1211
try {
13-
const state = new Session();
12+
const session = new Session();
1413
const mcpServer = new McpServer({
1514
name: "MongoDB Atlas",
1615
version: config.version,
1716
});
18-
const transport = new StdioServerTransport();
1917

20-
const server = new Seriver({
18+
const server = new Server({
2119
mcpServer,
22-
state,
23-
transport,
20+
session,
2421
});
2522

26-
await server.registerAndConnect();
23+
const transport = new StdioServerTransport();
24+
25+
await server.connect(transport);
2726
} catch (error) {
2827
logger.emergency(mongoLogId(1_000_004), "server", `Fatal error running server: ${error}`);
2928

src/server.ts

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,28 +9,22 @@ import { mongoLogId } from "mongodb-log-writer";
99
export class Server {
1010
public readonly session: Session;
1111
private readonly mcpServer: McpServer;
12-
private readonly transport: Transport;
1312

14-
constructor({ mcpServer, transport, session }: { mcpServer: McpServer; session: Session; transport: Transport }) {
13+
constructor({ mcpServer, session }: { mcpServer: McpServer; session: Session }) {
1514
this.mcpServer = mcpServer;
16-
this.transport = transport;
1715
this.session = session;
1816
}
1917

20-
async connect() {
18+
async connect(transport: Transport) {
2119
this.mcpServer.server.registerCapabilities({ logging: {} });
2220

2321
this.registerTools();
2422

2523
await initializeLogger(this.mcpServer);
2624

27-
await this.mcpServer.connect(this.transport);
25+
await this.mcpServer.connect(transport);
2826

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

3630
async close(): Promise<void> {

src/session.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export class Session {
66
serviceProvider?: NodeDriverServiceProvider;
77
apiClient?: ApiClient;
88

9-
ensureApiClient(): asserts this is { apiClient: ApiClient } {
9+
ensureAuthenticated(): asserts this is { apiClient: ApiClient } {
1010
if (!this.apiClient) {
1111
if (!config.apiClientId || !config.apiClientSecret) {
1212
throw new Error(

src/tools/atlas/atlasTool.ts

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,7 @@ import { Session } from "../../session.js";
55
export abstract class AtlasToolBase extends ToolBase {
66
private apiClient?: ApiClient;
77

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-
268
constructor(protected readonly session: Session) {
27-
super(state);
9+
super(session);
2810
}
2911
}

src/tools/atlas/createAccessList.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export class CreateAccessListTool extends AtlasToolBase {
2626
comment,
2727
currentIpAddress,
2828
}: ToolArgs<typeof this.argsShape>): Promise<CallToolResult> {
29-
this.state.ensureApiClient();
29+
this.session.ensureAuthenticated();
3030

3131
if (!ipAddresses?.length && !cidrBlocks?.length && !currentIpAddress) {
3232
throw new Error("One of ipAddresses, cidrBlocks, currentIpAddress must be provided.");
@@ -39,7 +39,7 @@ export class CreateAccessListTool extends AtlasToolBase {
3939
}));
4040

4141
if (currentIpAddress) {
42-
const currentIp = await this.state.apiClient.getIpInfo();
42+
const currentIp = await this.session.apiClient.getIpInfo();
4343
const input = {
4444
groupId: projectId,
4545
ipAddress: currentIp.currentIpv4Address,
@@ -56,7 +56,7 @@ export class CreateAccessListTool extends AtlasToolBase {
5656

5757
const inputs = [...ipInputs, ...cidrInputs];
5858

59-
await this.state.apiClient.createProjectIpAccessList({
59+
await this.session.apiClient.createProjectIpAccessList({
6060
params: {
6161
path: {
6262
groupId: projectId,

src/tools/atlas/createDBUser.ts

Lines changed: 2 additions & 2 deletions
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.ensureApiClient();
36+
this.session.ensureAuthenticated();
3737

3838
const input = {
3939
groupId: projectId,
@@ -53,7 +53,7 @@ export class CreateDBUserTool extends AtlasToolBase {
5353
: undefined,
5454
} as CloudDatabaseUser;
5555

56-
await this.state.apiClient.createDatabaseUser({
56+
await this.session.apiClient.createDatabaseUser({
5757
params: {
5858
path: {
5959
groupId: projectId,

src/tools/atlas/createFreeCluster.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export class CreateFreeClusterTool extends AtlasToolBase {
1414
};
1515

1616
protected async execute({ projectId, name, region }: ToolArgs<typeof this.argsShape>): Promise<CallToolResult> {
17-
this.state.ensureApiClient();
17+
this.session.ensureAuthenticated();
1818

1919
const input = {
2020
groupId: projectId,
@@ -38,7 +38,7 @@ export class CreateFreeClusterTool extends AtlasToolBase {
3838
terminationProtectionEnabled: false,
3939
} as unknown as ClusterDescription20240805;
4040

41-
await this.state.apiClient.createCluster({
41+
await this.session.apiClient.createCluster({
4242
params: {
4343
path: {
4444
groupId: projectId,

src/tools/atlas/inspectAccessList.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ export class InspectAccessListTool extends AtlasToolBase {
1111
};
1212

1313
protected async execute({ projectId }: ToolArgs<typeof this.argsShape>): Promise<CallToolResult> {
14-
this.state.ensureApiClient();
14+
this.session.ensureAuthenticated();
1515

16-
const accessList = await this.state.apiClient.listProjectIpAccessLists({
16+
const accessList = await this.session.apiClient.listProjectIpAccessLists({
1717
params: {
1818
path: {
1919
groupId: projectId,

src/tools/atlas/inspectCluster.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ export class InspectClusterTool extends AtlasToolBase {
1313
};
1414

1515
protected async execute({ projectId, clusterName }: ToolArgs<typeof this.argsShape>): Promise<CallToolResult> {
16-
this.state.ensureApiClient();
16+
this.session.ensureAuthenticated();
1717

18-
const cluster = await this.state.apiClient.getCluster({
18+
const cluster = await this.session.apiClient.getCluster({
1919
params: {
2020
path: {
2121
groupId: projectId,

src/tools/atlas/listClusters.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@ export class ListClustersTool extends AtlasToolBase {
1212
};
1313

1414
protected async execute({ projectId }: ToolArgs<typeof this.argsShape>): Promise<CallToolResult> {
15-
this.state.ensureApiClient();
15+
this.session.ensureAuthenticated();
1616

1717
if (!projectId) {
18-
const data = await this.state.apiClient.listClustersForAllProjects();
18+
const data = await this.session.apiClient.listClustersForAllProjects();
1919

2020
return this.formatAllClustersTable(data);
2121
} else {
22-
const project = await this.state.apiClient.getProject({
22+
const project = await this.session.apiClient.getProject({
2323
params: {
2424
path: {
2525
groupId: projectId,
@@ -31,7 +31,7 @@ export class ListClustersTool extends AtlasToolBase {
3131
throw new Error(`Project with ID "${projectId}" not found.`);
3232
}
3333

34-
const data = await this.state.apiClient.listClusters({
34+
const data = await this.session.apiClient.listClusters({
3535
params: {
3636
path: {
3737
groupId: project.id || "",

src/tools/atlas/listDBUsers.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ export class ListDBUsersTool extends AtlasToolBase {
1212
};
1313

1414
protected async execute({ projectId }: ToolArgs<typeof this.argsShape>): Promise<CallToolResult> {
15-
this.state.ensureApiClient();
15+
this.session.ensureAuthenticated();
1616

17-
const data = await this.state.apiClient.listDatabaseUsers({
17+
const data = await this.session.apiClient.listDatabaseUsers({
1818
params: {
1919
path: {
2020
groupId: projectId,

src/tools/atlas/listProjects.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ export class ListProjectsTool extends AtlasToolBase {
77
protected argsShape = {};
88

99
protected async execute(): Promise<CallToolResult> {
10-
this.state.ensureApiClient();
10+
this.session.ensureAuthenticated();
1111

12-
const data = await this.state.apiClient.listProjects();
12+
const data = await this.session.apiClient.listProjects();
1313

1414
if (!data?.results?.length) {
1515
throw new Error("No projects found in your MongoDB Atlas account.");

src/tools/mongodb/index.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2-
import { State } from "../../session.js";
31
import { ConnectTool } from "./connect.js";
42
import { ListCollectionsTool } from "./metadata/listCollections.js";
53
import { CollectionIndexesTool } from "./collectionIndexes.js";

src/tools/mongodb/mongodbTool.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { z } from "zod";
22
import { ToolBase } from "../tool.js";
3-
import { State } from "../../state.js";
3+
import { Session } from "../../session.js";
44
import { NodeDriverServiceProvider } from "@mongosh/service-provider-node-driver";
55
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
66
import { ErrorCodes, MongoDBError } from "../../errors.js";
@@ -14,16 +14,16 @@ export const DbOperationArgs = {
1414
export type DbOperationType = "metadata" | "read" | "create" | "update" | "delete";
1515

1616
export abstract class MongoDBToolBase extends ToolBase {
17-
constructor(state: State) {
18-
super(state);
17+
constructor(session: Session) {
18+
super(session);
1919
}
2020

2121
protected abstract operationType: DbOperationType;
2222

2323
protected async ensureConnected(): Promise<NodeDriverServiceProvider> {
24-
const provider = this.state.serviceProvider;
24+
const provider = this.session.serviceProvider;
2525
if (!provider && config.connectionString) {
26-
await this.connectToMongoDB(config.connectionString, this.state);
26+
await this.connectToMongoDB(config.connectionString);
2727
}
2828

2929
if (!provider) {
@@ -53,7 +53,7 @@ export abstract class MongoDBToolBase extends ToolBase {
5353
return super.handleError(error);
5454
}
5555

56-
protected async connectToMongoDB(connectionString: string, state: State): Promise<void> {
56+
protected async connectToMongoDB(connectionString: string): Promise<void> {
5757
const provider = await NodeDriverServiceProvider.connect(connectionString, {
5858
productDocsLink: "https://docs.mongodb.com/todo-mcp",
5959
productName: "MongoDB MCP",
@@ -67,6 +67,6 @@ export abstract class MongoDBToolBase extends ToolBase {
6767
timeoutMS: config.connectOptions.timeoutMS,
6868
});
6969

70-
state.serviceProvider = provider;
70+
this.session.serviceProvider = provider;
7171
}
7272
}

src/tools/tool.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +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";
4+
import { Session } from "../session.js";
55
import logger from "../logger.js";
66
import { mongoLogId } from "mongodb-log-writer";
77

@@ -16,7 +16,7 @@ export abstract class ToolBase {
1616

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

19-
protected constructor(protected state: State) {}
19+
protected constructor(protected session: Session) {}
2020

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

tests/integration/helpers.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { Server } from "../../src/server.js";
44
import runner, { MongoCluster } from "mongodb-runner";
55
import path from "path";
66
import fs from "fs/promises";
7+
import { Session } from "../../src/session.js";
8+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
79

810
export async function setupIntegrationTest(): Promise<{
911
client: Client;
@@ -29,7 +31,13 @@ export async function setupIntegrationTest(): Promise<{
2931
}
3032
);
3133

32-
const server = new Server();
34+
const server = new Server({
35+
mcpServer: new McpServer({
36+
name: "test-server",
37+
version: "1.2.3",
38+
}),
39+
session: new Session(),
40+
});
3341
await server.connect(serverTransport);
3442
await client.connect(clientTransport);
3543

0 commit comments

Comments
 (0)