calculate
Perform basic arithmetic calculations with two numbers using addition, subtraction, multiplication, or division operations.
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 handler function for the 'calculate' tool. It handles input parameters, optional streaming progress, calls the core calculation helper, manages history, and returns structured output with error handling.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 input and output schemas for the 'calculate' tool, defining parameters a, b, op (enum of arithmetic operations), optional stream flag, and output value with 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)Registration of the 'calculate' tool with the MCP server, including title, description, 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 helper function that executes the basic arithmetic logic based on the operation type, with validation for division by zero and unknown operations.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}`); } }