UNPKG

@modelcontextprotocol/server-everything

Version:

MCP server that exercises all the features of the MCP protocol

90 lines (89 loc) • 3.53 kB
import { setTimeout as sleep } from "node:timers/promises"; import { z } from "zod"; import { TOOL_ICONS } from "../server/icons.js"; // Tool input schema const TriggerLongRunningOperationSchema = z.object({ duration: z .number() .default(10) .describe("Duration of the operation in seconds"), steps: z.number().default(5).describe("Number of steps in the operation"), }); // Tool configuration const name = "trigger-long-running-operation"; const config = { title: "Trigger Long Running Operation Tool", description: "Demonstrates a long running operation with progress updates.", inputSchema: TriggerLongRunningOperationSchema, icons: TOOL_ICONS, annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false, }, }; /** * Registers the 'trigger-long-running-operation' tool. * * The registered tool starts a long-running operation defined by a specific duration and * number of steps. * * Progress notifications are sent back to the client at each step if a `progressToken` * is provided in the metadata. * * At the end of the operation, the tool returns a message indicating the completion of the * operation, including the total duration and steps. * * The operation watches the request's abort signal, which the SDK fires when the client * sends `notifications/cancelled` or the connection closes, in either protocol era. On * cancellation it clears its pending timer and stops without sending further progress, so * a cancelled operation never holds the process open (#1124, #4846). * * @param {McpServer} server - The McpServer instance where the tool will be registered. */ export const registerTriggerLongRunningOperationTool = (server) => { server.registerTool(name, config, async (args, ctx) => { const validatedArgs = TriggerLongRunningOperationSchema.parse(args); const { duration, steps } = validatedArgs; const stepDuration = duration / steps; const progressToken = ctx.mcpReq._meta?.progressToken; const signal = ctx.mcpReq.signal; for (let i = 1; i < steps + 1; i++) { try { // Rejects at once, clearing its timer, when the request is cancelled. await sleep(stepDuration * 1000, undefined, { signal }); } catch { // The SDK drops the response to a cancelled request, so this result // never reaches the client; returning simply ends the handler. return { content: [ { type: "text", text: `Long running operation cancelled after ${i - 1} of ${steps} steps.`, }, ], }; } if (progressToken !== undefined) { await server.server.notification({ method: "notifications/progress", params: { progress: i, total: steps, progressToken, }, }, { relatedRequestId: ctx.mcpReq.id }); } } return { content: [ { type: "text", text: `Long running operation completed. Duration: ${duration} seconds, Steps: ${steps}.`, }, ], }; }); };