Skip to content

Commit 9ba243d

Browse files
authored
feat: hide atlas tools when not configured (#96)
1 parent d548784 commit 9ba243d

20 files changed

+38
-62
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ You may experiment asking `Can you connect to my mongodb instance?`.
109109
- `atlas-list-db-users` - List MongoDB Atlas database users
110110
- `atlas-create-db-user` - List MongoDB Atlas database users
111111

112+
NOTE: atlas tools are only available when you set credentials on [configuration](#configuration) section.
113+
112114
#### MongoDB Database Tools
113115

114116
- `connect` - Connect to a MongoDB instance

src/common/atlas/apiClient.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ import { paths, operations } from "./openapi.js";
66

77
const ATLAS_API_VERSION = "2025-03-12";
88

9+
export interface ApiClientCredentials {
10+
clientId: string;
11+
clientSecret: string;
12+
}
13+
914
export interface ApiClientOptions {
10-
credentials?: {
11-
clientId: string;
12-
clientSecret: string;
13-
};
15+
credentials?: ApiClientCredentials;
1416
baseUrl?: string;
1517
userAgent?: string;
1618
}

src/session.ts

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,24 @@
11
import { NodeDriverServiceProvider } from "@mongosh/service-provider-node-driver";
2-
import { ApiClient } from "./common/atlas/apiClient.js";
2+
import { ApiClient, ApiClientCredentials } from "./common/atlas/apiClient.js";
33
import config from "./config.js";
44

55
export class Session {
66
serviceProvider?: NodeDriverServiceProvider;
7-
apiClient?: ApiClient;
7+
apiClient: ApiClient;
88

9-
ensureAuthenticated(): asserts this is { apiClient: ApiClient } {
10-
if (!this.apiClient) {
11-
if (!config.apiClientId || !config.apiClientSecret) {
12-
throw new Error(
13-
"Not authenticated make sure to configure MCP server with MDB_MCP_API_CLIENT_ID and MDB_MCP_API_CLIENT_SECRET environment variables."
14-
);
15-
}
9+
constructor() {
10+
const credentials: ApiClientCredentials | undefined =
11+
config.apiClientId && config.apiClientSecret
12+
? {
13+
clientId: config.apiClientId,
14+
clientSecret: config.apiClientSecret,
15+
}
16+
: undefined;
1617

17-
this.apiClient = new ApiClient({
18-
baseUrl: config.apiBaseUrl,
19-
credentials: {
20-
clientId: config.apiClientId,
21-
clientSecret: config.apiClientSecret,
22-
},
23-
});
24-
}
18+
this.apiClient = new ApiClient({
19+
baseUrl: config.apiBaseUrl,
20+
credentials,
21+
});
2522
}
2623

2724
async close(): Promise<void> {

src/tools/atlas/atlasTool.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
11
import { ToolBase, ToolCategory } from "../tool.js";
22
import { Session } from "../../session.js";
3+
import config from "../../config.js";
34

45
export abstract class AtlasToolBase extends ToolBase {
56
constructor(protected readonly session: Session) {
67
super(session);
78
}
89

910
protected category: ToolCategory = "atlas";
11+
12+
protected verifyAllowed(): boolean {
13+
if (!config.apiClientId || !config.apiClientSecret) {
14+
return false;
15+
}
16+
return super.verifyAllowed();
17+
}
1018
}

src/tools/atlas/createAccessList.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@ export class CreateAccessListTool extends AtlasToolBase {
2727
comment,
2828
currentIpAddress,
2929
}: ToolArgs<typeof this.argsShape>): Promise<CallToolResult> {
30-
this.session.ensureAuthenticated();
31-
3230
if (!ipAddresses?.length && !cidrBlocks?.length && !currentIpAddress) {
3331
throw new Error("One of ipAddresses, cidrBlocks, currentIpAddress must be provided.");
3432
}

src/tools/atlas/createDBUser.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,6 @@ export class CreateDBUserTool extends AtlasToolBase {
3434
roles,
3535
clusters,
3636
}: ToolArgs<typeof this.argsShape>): Promise<CallToolResult> {
37-
this.session.ensureAuthenticated();
38-
3937
const input = {
4038
groupId: projectId,
4139
awsIAMType: "NONE",

src/tools/atlas/createFreeCluster.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@ export class CreateFreeClusterTool extends AtlasToolBase {
1515
};
1616

1717
protected async execute({ projectId, name, region }: ToolArgs<typeof this.argsShape>): Promise<CallToolResult> {
18-
this.session.ensureAuthenticated();
19-
2018
const input = {
2119
groupId: projectId,
2220
name,

src/tools/atlas/createProject.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ export class CreateProjectTool extends AtlasToolBase {
1414
};
1515

1616
protected async execute({ projectName, organizationId }: ToolArgs<typeof this.argsShape>): Promise<CallToolResult> {
17-
this.session.ensureAuthenticated();
1817
let assumedOrg = false;
1918

2019
if (!projectName) {

src/tools/atlas/inspectAccessList.ts

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

1414
protected async execute({ projectId }: ToolArgs<typeof this.argsShape>): Promise<CallToolResult> {
15-
this.session.ensureAuthenticated();
16-
1715
const accessList = await this.session.apiClient.listProjectIpAccessLists({
1816
params: {
1917
path: {

src/tools/atlas/inspectCluster.ts

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

1616
protected async execute({ projectId, clusterName }: ToolArgs<typeof this.argsShape>): Promise<CallToolResult> {
17-
this.session.ensureAuthenticated();
18-
1917
const cluster = await this.session.apiClient.getCluster({
2018
params: {
2119
path: {

src/tools/atlas/listClusters.ts

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

1515
protected async execute({ projectId }: ToolArgs<typeof this.argsShape>): Promise<CallToolResult> {
16-
this.session.ensureAuthenticated();
17-
1816
if (!projectId) {
1917
const data = await this.session.apiClient.listClustersForAllProjects();
2018

src/tools/atlas/listDBUsers.ts

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

1515
protected async execute({ projectId }: ToolArgs<typeof this.argsShape>): Promise<CallToolResult> {
16-
this.session.ensureAuthenticated();
17-
1816
const data = await this.session.apiClient.listDatabaseUsers({
1917
params: {
2018
path: {

src/tools/atlas/listOrgs.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ export class ListOrganizationsTool extends AtlasToolBase {
99
protected argsShape = {};
1010

1111
protected async execute(): Promise<CallToolResult> {
12-
this.session.ensureAuthenticated();
13-
1412
const data = await this.session.apiClient.listOrganizations();
1513

1614
if (!data?.results?.length) {

src/tools/atlas/listProjects.ts

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

1515
protected async execute({ orgId }: ToolArgs<typeof this.argsShape>): Promise<CallToolResult> {
16-
this.session.ensureAuthenticated();
17-
1816
const data = orgId
1917
? await this.session.apiClient.listOrganizationProjects({
2018
params: {

src/tools/tool.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export abstract class ToolBase {
5252
}
5353

5454
// Checks if a tool is allowed to run based on the config
55-
private verifyAllowed(): boolean {
55+
protected verifyAllowed(): boolean {
5656
let errorClarification: string | undefined;
5757
if (config.disabledTools.includes(this.category)) {
5858
errorClarification = `its category, \`${this.category}\`,`;

tests/integration/tools/atlas/accessLists.test.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
2-
import { Session } from "../../../../src/session.js";
32
import { describeAtlas, withProject } from "./atlasHelpers.js";
43

54
function generateRandomIp() {
@@ -17,20 +16,18 @@ describeAtlas("ip access lists", (integration) => {
1716
const values = [...ips, ...cidrBlocks];
1817

1918
beforeAll(async () => {
20-
const session: Session = integration.mcpServer().session;
21-
session.ensureAuthenticated();
22-
const ipInfo = await session.apiClient.getIpInfo();
19+
const apiClient = integration.mcpServer().session.apiClient;
20+
const ipInfo = await apiClient.getIpInfo();
2321
values.push(ipInfo.currentIpv4Address);
2422
});
2523

2624
afterAll(async () => {
27-
const session: Session = integration.mcpServer().session;
28-
session.ensureAuthenticated();
25+
const apiClient = integration.mcpServer().session.apiClient;
2926

3027
const projectId = getProjectId();
3128

3229
for (const value of values) {
33-
await session.apiClient.deleteProjectIpAccessList({
30+
await apiClient.deleteProjectIpAccessList({
3431
params: {
3532
path: {
3633
groupId: projectId,

tests/integration/tools/atlas/atlasHelpers.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { ObjectId } from "mongodb";
22
import { Group } from "../../../../src/common/atlas/openapi.js";
33
import { ApiClient } from "../../../../src/common/atlas/apiClient.js";
44
import { setupIntegrationTest, IntegrationTest } from "../../helpers.js";
5-
import { Session } from "../../../../src/session.js";
65

76
export type IntegrationTestFunction = (integration: IntegrationTest) => void;
87

@@ -35,19 +34,15 @@ export function withProject(integration: IntegrationTest, fn: ProjectTestFunctio
3534
let projectId: string = "";
3635

3736
beforeAll(async () => {
38-
const session: Session = integration.mcpServer().session;
39-
session.ensureAuthenticated();
37+
const apiClient = integration.mcpServer().session.apiClient;
4038

41-
const apiClient = session.apiClient;
4239
const group = await createProject(apiClient);
4340
projectId = group.id || "";
4441
});
4542

4643
afterAll(async () => {
47-
const session: Session = integration.mcpServer().session;
48-
session.ensureAuthenticated();
44+
const apiClient = integration.mcpServer().session.apiClient;
4945

50-
const apiClient = session.apiClient;
5146
await apiClient.deleteProject({
5247
params: {
5348
path: {

tests/integration/tools/atlas/clusters.test.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ import { describeAtlas, withProject, sleep, randomId } from "./atlasHelpers.js";
33
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
44

55
async function deleteAndWaitCluster(session: Session, projectId: string, clusterName: string) {
6-
session.ensureAuthenticated();
7-
86
await session.apiClient.deleteCluster({
97
params: {
108
path: {

tests/integration/tools/atlas/dbUsers.test.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ describeAtlas("db users", (integration) => {
99
const projectId = getProjectId();
1010

1111
const session: Session = integration.mcpServer().session;
12-
session.ensureAuthenticated();
1312
await session.apiClient.deleteDatabaseUser({
1413
params: {
1514
path: {

tests/integration/tools/atlas/projects.test.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
2-
import { setupIntegrationTest } from "../../helpers.js";
3-
import { Session } from "../../../../src/session.js";
42
import { ObjectId } from "mongodb";
53
import { parseTable, describeAtlas } from "./atlasHelpers.js";
64

@@ -10,8 +8,7 @@ describeAtlas("projects", (integration) => {
108
const projName = "testProj-" + randomId;
119

1210
afterAll(async () => {
13-
const session: Session = integration.mcpServer().session;
14-
session.ensureAuthenticated();
11+
const session = integration.mcpServer().session;
1512

1613
const projects = await session.apiClient.listProjects();
1714
for (const project of projects?.results || []) {

0 commit comments

Comments
 (0)