-
Notifications
You must be signed in to change notification settings - Fork 208
feat: add Atlas Stream Processing tools MCP-406 #967
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
d6b816f
155c541
120c54a
ca7ec77
543d993
00e0375
8ef6217
2d0efc9
6735f6c
3747b80
b74eb35
46469bc
afdb691
0e8c519
68efdc6
7e60d80
82e7842
3f75134
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -78,6 +78,8 @@ const ServerConfigSchema = z4.object({ | |
| "drop-collection", | ||
| "delete-many", | ||
| "drop-index", | ||
| "atlas-streams-manage", | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is needed for elicitation to the users that modifies or deletes their streams resources. |
||
| "atlas-streams-teardown", | ||
| ]) | ||
| .describe( | ||
| "An array of tool names that require user confirmation before execution. Requires the client to support elicitation." | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| export const previewFeatureValues = ["search", "mcpUI"] as const; | ||
| export const previewFeatureValues = ["search", "mcpUI", "streams"] as const; | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this will be removed after the PR to add the Accuracy test is created. |
||
| export type PreviewFeature = (typeof previewFeatureValues)[number]; | ||
|
|
||
| export const monitoringServerFeatureValues = ["health-check", "metrics"] as const; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,12 @@ | ||
| import type { ElicitRequestFormParams } from "@modelcontextprotocol/sdk/types.js"; | ||
| import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; | ||
|
|
||
| export type ElicitedInputResult = | ||
| | { accepted: true; fields: Record<string, string> } | ||
| | { accepted: false; fields?: undefined }; | ||
|
|
||
| const ELICITATION_TIMEOUT_MS = 300_000; // 5 minutes for user interaction | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The timeout was added to the existing confirmAction method in elicitation.ts — the server.elicitInput() call previously had no timeout, which means it would use the SDK's default (which could hang indefinitely if a client never responds). We added { timeout: 300_000 } (5 minutes) to give users reasonable time to respond while preventing the server from hanging forever. |
||
|
|
||
| export class Elicitation { | ||
| private readonly server: McpServer["server"]; | ||
| constructor({ server }: { server: McpServer["server"] }) { | ||
|
|
@@ -26,14 +32,56 @@ export class Elicitation { | |
| return true; | ||
| } | ||
|
|
||
| const result = await this.server.elicitInput({ | ||
| mode: "form", | ||
| message, | ||
| requestedSchema: Elicitation.CONFIRMATION_SCHEMA, | ||
| }); | ||
| const result = await this.server.elicitInput( | ||
| { | ||
| mode: "form", | ||
| message, | ||
| requestedSchema: Elicitation.CONFIRMATION_SCHEMA, | ||
| }, | ||
| { timeout: ELICITATION_TIMEOUT_MS } | ||
| ); | ||
| return result.action === "accept" && result.content?.confirmation === "Yes"; | ||
| } | ||
|
|
||
| /** | ||
| * Requests structured input from the user via a form. | ||
| * Returns the accepted fields, or { accepted: false } if the client doesn't | ||
| * support elicitation or the user declined. | ||
| * | ||
| * @param message - The message/title to display in the form. | ||
| * @param schema - A JSON Schema describing the fields to collect. | ||
| * @returns The user-provided values keyed by field name, or null if declined/unsupported. | ||
| */ | ||
| public async requestInput( | ||
| message: string, | ||
| schema: ElicitRequestFormParams["requestedSchema"] | ||
| ): Promise<ElicitedInputResult> { | ||
| if (!this.supportsElicitation()) { | ||
| return { accepted: false }; | ||
| } | ||
|
|
||
| const result = await this.server.elicitInput( | ||
| { | ||
| mode: "form", | ||
| message, | ||
| requestedSchema: schema, | ||
| }, | ||
| { timeout: ELICITATION_TIMEOUT_MS } | ||
| ); | ||
|
|
||
| if (result.action !== "accept" || !result.content) { | ||
| return { accepted: false }; | ||
| } | ||
|
|
||
| const fields: Record<string, string> = {}; | ||
| for (const [key, value] of Object.entries(result.content)) { | ||
| if (typeof value === "string") { | ||
| fields[key] = value; | ||
| } | ||
| } | ||
| return { accepted: true, fields }; | ||
| } | ||
|
|
||
| /** | ||
| * The schema for the confirmation question. | ||
| * TODO: In the future would be good to use Zod 4's toJSONSchema() to generate the schema. | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Atlas enforces a dependency chain: a project cannot be deleted while it has clusters, and clusters cannot be deleted while a stream workspace holds a connection to them.
Phase 1 issues all the delete requests (processors → workspaces → clusters) in parallel across projects. Phase 2 waits for clusters to fully terminate before attempting to delete each project — without this wait, the project deletion fails with a 409 because the async cluster teardown hasn't finished yet.
Both phases run all projects in parallel so the total wait time is bounded by the slowest single project, not the sum.