Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/common/atlas/apiClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -365,5 +365,13 @@
return data;
}

async listAlerts(options: FetchOptions<operations["listAlerts"]>) {
const { data, error, response } = await this.client.GET("/api/atlas/v2/groups/{groupId}/alerts", options);
if (error) {
throw ApiClientError.fromError(response, error);
}
return data;
}

// DO NOT EDIT. This is auto-generated code.

Check failure on line 376 in src/common/atlas/apiClient.ts

View workflow job for this annotation

GitHub Actions / Check dependencies

Property 'listAlerts' does not exist on type 'operations'.

Check failure on line 376 in src/common/atlas/apiClient.ts

View workflow job for this annotation

GitHub Actions / check-generate

Property 'listAlerts' does not exist on type 'operations'.

Check failure on line 376 in src/common/atlas/apiClient.ts

View workflow job for this annotation

GitHub Actions / check-style

Property 'listAlerts' does not exist on type 'operations'.
}

Check failure on line 377 in src/common/atlas/apiClient.ts

View workflow job for this annotation

GitHub Actions / Check dependencies

Argument of type '"/api/atlas/v2/groups/{groupId}/alerts"' is not assignable to parameter of type 'PathsWithMethod<paths, "get">'.

Check failure on line 377 in src/common/atlas/apiClient.ts

View workflow job for this annotation

GitHub Actions / check-generate

Argument of type '"/api/atlas/v2/groups/{groupId}/alerts"' is not assignable to parameter of type 'PathsWithMethod<paths, "get">'.

Check failure on line 377 in src/common/atlas/apiClient.ts

View workflow job for this annotation

GitHub Actions / check-style

Argument of type '"/api/atlas/v2/groups/{groupId}/alerts"' is not assignable to parameter of type 'PathsWithMethod<paths, "get">'.
58 changes: 58 additions & 0 deletions src/tools/atlas/read/listAlerts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { z } from "zod";
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
import { AtlasToolBase } from "../atlasTool.js";
import { ToolArgs, OperationType } from "../../tool.js";

interface Alert {
id: string;
status: string;
created: string;
updated: string;
eventTypeName: string;
summary: string;
}

interface AlertResponse {
results: Alert[];
totalCount: number;
}

export class ListAlertsTool extends AtlasToolBase {
protected name = "atlas-list-alerts";
protected description = "List MongoDB Atlas alerts";
protected operationType: OperationType = "read";
protected argsShape = {
projectId: z.string().describe("Atlas project ID to list alerts for"),
};

protected async execute({ projectId }: ToolArgs<typeof this.argsShape>): Promise<CallToolResult> {
const data = (await this.session.apiClient.listAlerts({
params: {
path: {
groupId: projectId,
},
},
})) as AlertResponse;

if (!data?.results?.length) {
throw new Error("No alerts found in your MongoDB Atlas project.");
}

// Format alerts as a table
const output =
`Alert ID | Status | Created | Updated | Type | Summary
----------|---------|----------|----------|------|--------
` +
data.results
.map((alert) => {
const created = alert.created ? new Date(alert.created).toLocaleString() : "N/A";
const updated = alert.updated ? new Date(alert.updated).toLocaleString() : "N/A";
return `${alert.id} | ${alert.status} | ${created} | ${updated} | ${alert.eventTypeName} | ${alert.summary}`;
})
.join("\n");

return {
content: [{ type: "text", text: output }],
};
}
}
2 changes: 2 additions & 0 deletions src/tools/atlas/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { CreateDBUserTool } from "./create/createDBUser.js";
import { CreateProjectTool } from "./create/createProject.js";
import { ListOrganizationsTool } from "./read/listOrgs.js";
import { ConnectClusterTool } from "./metadata/connectCluster.js";
import { ListAlertsTool } from "./read/listAlerts.js";

export const AtlasTools = [
ListClustersTool,
Expand All @@ -22,4 +23,5 @@ export const AtlasTools = [
CreateProjectTool,
ListOrganizationsTool,
ConnectClusterTool,
ListAlertsTool,
];
61 changes: 61 additions & 0 deletions tests/integration/tools/atlas/alerts.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
import { expectDefined } from "../../helpers.js";
import { parseTable, describeWithAtlas, withProject } from "./atlasHelpers.js";

describeWithAtlas("alerts", (integration) => {
describe("atlas-list-alerts", () => {
it("should have correct metadata", async () => {
const { tools } = await integration.mcpClient().listTools();
const listAlerts = tools.find((tool) => tool.name === "atlas-list-alerts");
expectDefined(listAlerts);
expect(listAlerts.inputSchema.type).toBe("object");
expectDefined(listAlerts.inputSchema.properties);
expect(listAlerts.inputSchema.properties).toHaveProperty("projectId");
});

withProject(integration, ({ getProjectId }) => {
it("returns alerts in table format", async () => {
const response = (await integration.mcpClient().callTool({
name: "atlas-list-alerts",
arguments: { projectId: getProjectId() },
})) as CallToolResult;

expect(response.content).toBeArray();
expect(response.content).toHaveLength(1);

const data = parseTable(response.content[0].text as string);
expect(data).toBeArray();

// Since we can't guarantee alerts will exist, we just verify the table structure
if (data.length > 0) {
const alert = data[0];
expect(alert).toHaveProperty("Alert ID");
expect(alert).toHaveProperty("Status");
expect(alert).toHaveProperty("Created");
expect(alert).toHaveProperty("Updated");
expect(alert).toHaveProperty("Type");
expect(alert).toHaveProperty("Summary");
}
});

it("handles no alerts gracefully", async () => {
// Mock the API client to return no alerts
const apiClient = integration.mcpServer().session.apiClient;
const originalListAlerts = apiClient.listAlerts.bind(apiClient);
apiClient.listAlerts = () => Promise.resolve({ results: [], totalCount: 0 });

try {
await expect(
integration.mcpClient().callTool({
name: "atlas-list-alerts",
arguments: { projectId: getProjectId() },
})
).rejects.toThrow("No alerts found in your MongoDB Atlas project.");
} finally {
// Restore the original method
apiClient.listAlerts = originalListAlerts;
}
});
});
});
});
Loading