calculate
Perform basic arithmetic operations like addition, subtraction, multiplication, and division with two operands. Supports progress notifications for streaming calculations directly from the MCP server.
Instructions
Perform a basic arithmetic calculation
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| a | Yes | First operand | |
| b | Yes | Second operand | |
| op | Yes | Operation to perform | |
| stream | No | If true, emit progress notifications |
Implementation Reference
- src/server.ts:286-354 (handler)The primary handler function for the 'calculate' tool. Handles input validation (already done by SDK), optional progress streaming, delegates to performBasicCalculation, manages history, and returns protocol-compliant response with both text content and structured data.async ({ a, b, op, stream }, { sendNotification }) => { log.info(`Executing calculate: ${a} ${op} ${b}`); requestCount++; try { // FEATURE: Progress notifications for streaming calculations // WHY: Demonstrates real-time feedback using sendNotification if (stream) { const progressId = `calc-${Date.now()}`; await sendNotification({ method: 'notifications/progress', params: { progressToken: progressId, progress: 33, message: `Calculating ${a} ${op} ${b}`, }, }); await new Promise((resolve) => setTimeout(resolve, 100)); await sendNotification({ method: 'notifications/progress', params: { progressToken: progressId, progress: 66 }, }); await new Promise((resolve) => setTimeout(resolve, 100)); await sendNotification({ method: 'notifications/progress', params: { progressToken: progressId, progress: 100, message: 'Complete' }, }); await new Promise((resolve) => setTimeout(resolve, 100)); } // Core business logic: perform the calculation const result = performBasicCalculation(op, a, b); // Create and store history entry const historyEntry = createHistoryEntry(op, a, b, result); addToHistory(historyEntry); // SDK-NOTE: The return object must match the `outputSchema`. // The SDK will validate this before sending the response. // We provide both a simple text `content` for basic UIs and a // `structuredContent` for richer clients. return { content: [ { type: 'text', text: historyEntry.expression, }, ], structuredContent: { value: result, meta: { calculationId: historyEntry.id, timestamp: historyEntry.timestamp, }, }, }; } catch (error) { // CAVEAT: We catch the error from our business logic and re-throw it. // While `performBasicCalculation` already returns an McpError, this pattern // is crucial for wrapping any *unexpected* errors that might occur, // preventing stack traces from leaking to the client. log.error(`Calculation failed: ${error instanceof Error ? error.message : String(error)}`); throw new McpError( ErrorCode.InvalidParams, `Calculation failed: ${error instanceof Error ? error.message : 'Unknown error'}`, { operation: op, a, b }, ); } },
- src/server.ts:259-272 (schema)Zod-based input and output schemas defining the contract for the 'calculate' tool. Input includes operands a/b, operation op, and optional stream flag. Output includes the result value and metadata.const calculateInputSchema = { a: z.number().describe('First operand'), b: z.number().describe('Second operand'), op: z.enum(['add', 'subtract', 'multiply', 'divide']).describe('Operation to perform'), stream: z.boolean().optional().describe('If true, emit progress notifications'), }; const calculateOutputSchema = { value: z.number(), meta: z.object({ calculationId: z.string(), timestamp: z.string(), }), };
- src/server.ts:274-355 (registration)Registers the 'calculate' tool on the MCP server instance, specifying name, metadata, schemas, and handler function.server.registerTool( 'calculate', { title: 'Calculate', description: 'Perform a basic arithmetic calculation', inputSchema: calculateInputSchema, outputSchema: calculateOutputSchema, }, // HANDLER LOGIC: // - Async function, receives validated & typed parameters. // - The SDK automatically parses the input against `inputSchema`. // - If validation fails, the SDK sends the error; this code doesn't run. async ({ a, b, op, stream }, { sendNotification }) => { log.info(`Executing calculate: ${a} ${op} ${b}`); requestCount++; try { // FEATURE: Progress notifications for streaming calculations // WHY: Demonstrates real-time feedback using sendNotification if (stream) { const progressId = `calc-${Date.now()}`; await sendNotification({ method: 'notifications/progress', params: { progressToken: progressId, progress: 33, message: `Calculating ${a} ${op} ${b}`, }, }); await new Promise((resolve) => setTimeout(resolve, 100)); await sendNotification({ method: 'notifications/progress', params: { progressToken: progressId, progress: 66 }, }); await new Promise((resolve) => setTimeout(resolve, 100)); await sendNotification({ method: 'notifications/progress', params: { progressToken: progressId, progress: 100, message: 'Complete' }, }); await new Promise((resolve) => setTimeout(resolve, 100)); } // Core business logic: perform the calculation const result = performBasicCalculation(op, a, b); // Create and store history entry const historyEntry = createHistoryEntry(op, a, b, result); addToHistory(historyEntry); // SDK-NOTE: The return object must match the `outputSchema`. // The SDK will validate this before sending the response. // We provide both a simple text `content` for basic UIs and a // `structuredContent` for richer clients. return { content: [ { type: 'text', text: historyEntry.expression, }, ], structuredContent: { value: result, meta: { calculationId: historyEntry.id, timestamp: historyEntry.timestamp, }, }, }; } catch (error) { // CAVEAT: We catch the error from our business logic and re-throw it. // While `performBasicCalculation` already returns an McpError, this pattern // is crucial for wrapping any *unexpected* errors that might occur, // preventing stack traces from leaking to the client. log.error(`Calculation failed: ${error instanceof Error ? error.message : String(error)}`); throw new McpError( ErrorCode.InvalidParams, `Calculation failed: ${error instanceof Error ? error.message : 'Unknown error'}`, { operation: op, a, b }, ); } }, );
- src/server.ts:200-214 (helper)Core pure function implementing the basic arithmetic operations (add, subtract, multiply, divide) with fail-fast error handling for invalid operations and division by zero. Called by the 'calculate' handler.function performBasicCalculation(op: string, a: number, b: number): number { switch (op) { case 'add': return a + b; case 'subtract': return a - b; case 'multiply': return a * b; case 'divide': if (b === 0) throw new McpError(ErrorCode.InvalidParams, 'Division by zero'); return a / b; default: throw new McpError(ErrorCode.InvalidParams, `Unknown operation: ${op}`); } }