MCP Calc Tools

#!/usr/bin/env node import { genkit, z } from 'genkit'; import { mcpServer } from 'genkitx-mcp'; import * as math from 'mathjs'; const ai = genkit({}); // Helper functions for mathematical operations const derivative = (expr, variable = 'x') => { try { const node = math.parse(expr); const derivativeExpr = math.derivative(node, variable); return derivativeExpr.toString(); } catch (e) { return `Error: ${e.message}`; } }; const integral = (expr, variable = 'x') => { try { // For basic polynomials const powerMatch = expr.match(new RegExp(`${variable}\\^(\\d+)`)); if (powerMatch) { const n = parseInt(powerMatch[1]); return `${variable}^${n + 1}/${n + 1}`; } // For basic functions const rules = { 'e^x': 'e^x', '1/x': `ln|${variable}|`, [`sin(${variable})`]: `-cos(${variable})`, [`cos(${variable})`]: `sin(${variable})`, 'x': 'x^2/2' }; return rules[expr] || 'Cannot compute integral'; } catch (e) { return `Error: ${e.message}`; } }; const riemannSum = (expr, variable, a, b, n, method = 'midpoint') => { try { const deltaX = (b - a) / n; let sum = 0; const node = math.parse(expr); const scope = {}; if (method === 'left' || method === 'right') { const offset = method === 'right' ? 1 : 0; for (let i = 0; i < n; i++) { const x = a + (i + offset) * deltaX; scope[variable] = x; sum += math.evaluate(node, scope) * deltaX; } } else if (method === 'midpoint') { for (let i = 0; i < n; i++) { const x = a + (i + 0.5) * deltaX; scope[variable] = x; sum += math.evaluate(node, scope) * deltaX; } } else if (method === 'trapezoid') { for (let i = 0; i <= n; i++) { const x = a + i * deltaX; scope[variable] = x; const coef = (i === 0 || i === n) ? 0.5 : 1; sum += coef * math.evaluate(node, scope) * deltaX; } } return sum; } catch (e) { return `Error: ${e.message}`; } }; // Define mathematical tools ai.defineTool( { name: 'derivative', description: 'Calculate the derivative of a mathematical expression', inputSchema: z.object({ expression: z.string().describe('Mathematical expression (e.g., "x^2", "e^x", "sin(x)")'), variable: z.string().optional().describe('Variable to differentiate with respect to (default: x)') }), outputSchema: z.string(), }, async ({ expression, variable = 'x' }) => { return derivative(expression, variable); } ); ai.defineTool( { name: 'integral', description: 'Calculate the indefinite integral of a mathematical expression', inputSchema: z.object({ expression: z.string().describe('Mathematical expression (e.g., "x^2", "e^x", "sin(x)")'), variable: z.string().optional().describe('Variable to integrate with respect to (default: x)') }), outputSchema: z.string(), }, async ({ expression, variable = 'x' }) => { return integral(expression, variable); } ); ai.defineTool( { name: 'riemann_sum', description: 'Calculate the Riemann sum of a function using different methods', inputSchema: z.object({ expression: z.string().describe('Function to integrate'), variable: z.string().describe('Variable of integration'), a: z.number().describe('Lower limit of integration'), b: z.number().describe('Upper limit of integration'), n: z.number().describe('Number of subintervals'), method: z.enum(['left', 'right', 'midpoint', 'trapezoid']).default('midpoint') .describe('Method: left, right, midpoint, or trapezoid') }), outputSchema: z.number(), }, async ({ expression, variable, a, b, n, method }) => { return riemannSum(expression, variable, a, b, n, method); } ); ai.defineTool( { name: 'area', description: 'Calculate the area under a curve between two points', inputSchema: z.object({ expression: z.string(), start: z.number(), end: z.number(), n: z.number().optional().describe('Number of subintervals (default: 1000)') }), outputSchema: z.number(), }, async ({ expression, start, end, n = 1000 }) => { return riemannSum(expression, 'x', start, end, n, 'trapezoid'); } ); ai.defineTool( { name: 'volume', description: 'Calculate the volume of revolution around x-axis', inputSchema: z.object({ expression: z.string(), start: z.number(), end: z.number() }), outputSchema: z.number(), }, async ({ expression, start, end }) => { // Volume of revolution using disk method const steps = 1000; const dx = (end - start) / steps; let volume = 0; for (let i = 0; i < steps; i++) { const x = start + i * dx; const y = eval(expression.replace(/x/g, `(${x})`)); volume += Math.PI * y * y * dx; } return volume; } ); ai.defineTool( { name: 'logarithm', description: 'Calculate logarithm with any base', inputSchema: z.object({ value: z.number(), base: z.number().optional() }), outputSchema: z.number(), }, async ({ value, base = Math.E }) => { return Math.log(value) / Math.log(base); } ); ai.defineTool( { name: 'exponential', description: 'Calculate exponential function (e^x)', inputSchema: z.object({ power: z.number() }), outputSchema: z.number(), }, async ({ power }) => { return Math.exp(power); } ); // Financial analysis tools ai.defineTool( { name: 'compound_interest', description: 'Calculate compound interest', inputSchema: z.object({ principal: z.number(), rate: z.number(), // annual interest rate as decimal time: z.number(), // years compounds: z.number().optional() // times per year }), outputSchema: z.number(), }, async ({ principal, rate, time, compounds = 12 }) => { return principal * Math.pow(1 + rate/compounds, compounds * time); } ); ai.defineTool( { name: 'present_value', description: 'Calculate present value of future cash flows', inputSchema: z.object({ futureValue: z.number(), rate: z.number(), // discount rate as decimal time: z.number() // years }), outputSchema: z.number(), }, async ({ futureValue, rate, time }) => { return futureValue / Math.pow(1 + rate, time); } ); ai.defineTool( { name: 'npv', description: 'Calculate Net Present Value of cash flows', inputSchema: z.object({ cashFlows: z.array(z.number()), rate: z.number() // discount rate as decimal }), outputSchema: z.number(), }, async ({ cashFlows, rate }) => { return cashFlows.reduce((npv, cf, t) => npv + cf / Math.pow(1 + rate, t), 0); } ); const darbouxSum = (expr, variable, a, b, n, type = 'upper') => { try { const deltaX = (b - a) / n; let sum = 0; const node = math.parse(expr); const scope = {}; for (let i = 0; i < n; i++) { const x1 = a + i * deltaX; const x2 = x1 + deltaX; scope[variable] = x1; const y1 = math.evaluate(node, scope); scope[variable] = x2; const y2 = math.evaluate(node, scope); const value = type === 'upper' ? Math.max(y1, y2) : Math.min(y1, y2); sum += value * deltaX; } return sum; } catch (e) { return `Error: ${e.message}`; } }; const findLimit = (expr, variable, approach) => { try { const node = math.parse(expr); const scope = {}; const epsilon = 1e-10; // Evaluate near the approach point scope[variable] = approach + epsilon; const rightLimit = math.evaluate(node, scope); scope[variable] = approach - epsilon; const leftLimit = math.evaluate(node, scope); // Check if limits from both sides are approximately equal if (Math.abs(rightLimit - leftLimit) < epsilon) { return (rightLimit + leftLimit) / 2; } return 'Limit does not exist'; } catch (e) { return `Error: ${e.message}`; } }; // Define additional calculus tools ai.defineTool( { name: 'darboux_sum', description: 'Calculate the Darboux sum of a function', inputSchema: z.object({ expression: z.string().describe('Function to integrate'), variable: z.string().describe('Variable of integration'), a: z.number().describe('Lower limit of integration'), b: z.number().describe('Upper limit of integration'), n: z.number().describe('Number of subintervals'), type: z.enum(['upper', 'lower']).default('upper') .describe('Type: upper or lower Darboux sum') }), outputSchema: z.union([z.number(), z.string()]), }, async ({ expression, variable, a, b, n, type }) => { return darbouxSum(expression, variable, a, b, n, type); } ); ai.defineTool( { name: 'limit', description: 'Calculate the limit of a function as it approaches a value', inputSchema: z.object({ expression: z.string().describe('Function to evaluate limit'), variable: z.string().describe('Variable approaching the limit'), approach: z.number().describe('Value the variable approaches') }), outputSchema: z.union([z.number(), z.string()]), }, async ({ expression, variable, approach }) => { return findLimit(expression, variable, approach); } ); ai.defineTool( { name: 'solve', description: 'Solve an equation for a variable', inputSchema: z.object({ expression: z.string().describe('Equation to solve (e.g., "x^2 = 4")'), variable: z.string().describe('Variable to solve for') }), outputSchema: z.array(z.string()), }, async ({ expression, variable }) => { try { // Convert equation to standard form (expression = 0) const sides = expression.split('=').map(s => s.trim()); if (sides.length !== 2) { throw new Error('Invalid equation format. Use "expression = value"'); } const equationNode = math.parse(`${sides[0]}-(${sides[1]})`); const solutions = math.solve(equationNode, variable); return solutions.map(sol => sol.toString()); } catch (e) { return [`Error: ${e.message}`]; } } ); // Engineering functions const laplaceTransform = (expr, t, s) => { try { const node = math.parse(expr); // Using numerical integration for a basic approximation const upperLimit = 100; // Approximation of infinity const steps = 1000; const dt = upperLimit / steps; let result = math.complex(0, 0); for (let i = 0; i < steps; i++) { const time = i * dt; const scope = { [t]: time }; const ft = math.evaluate(node, scope); const expTerm = math.exp(math.multiply(math.complex(-s, 0), time)); result = math.add(result, math.multiply(ft, expTerm, dt)); } return result.toString(); } catch (e) { return `Error: ${e.message}`; } }; const fourierTransform = (expr, t, omega) => { try { const node = math.parse(expr); // Using numerical integration for a basic approximation const limit = 50; // Approximation of infinity const steps = 1000; const dt = (2 * limit) / steps; let result = math.complex(0, 0); for (let i = 0; i < steps; i++) { const time = -limit + i * dt; const scope = { [t]: time }; const ft = math.evaluate(node, scope); const expTerm = math.exp( math.multiply( math.complex(0, -1), omega, time ) ); result = math.add(result, math.multiply(ft, expTerm, dt)); } return result.toString(); } catch (e) { return `Error: ${e.message}`; } }; const zTransform = (expr, n, z, limit = 100) => { try { const node = math.parse(expr); let result = math.complex(0, 0); for (let k = 0; k <= limit; k++) { const scope = { [n]: k }; const fn = math.evaluate(node, scope); const zTerm = math.pow(z, -k); result = math.add(result, math.multiply(fn, zTerm)); } return result.toString(); } catch (e) { return `Error: ${e.message}`; } }; // Engineering transform tools ai.defineTool( { name: 'laplace_transform', description: 'Calculate the Laplace transform of a function', inputSchema: z.object({ expression: z.string().describe('Function of time'), timeVar: z.string().describe('Time variable'), laplaceVar: z.string().describe('Laplace variable') }), outputSchema: z.string(), }, async ({ expression, timeVar, laplaceVar }) => { return laplaceTransform(expression, timeVar, laplaceVar); } ); ai.defineTool( { name: 'fourier_transform', description: 'Calculate the Fourier transform of a function', inputSchema: z.object({ expression: z.string().describe('Function of time'), timeVar: z.string().describe('Time variable'), freqVar: z.string().describe('Frequency variable') }), outputSchema: z.string(), }, async ({ expression, timeVar, freqVar }) => { return fourierTransform(expression, timeVar, freqVar); } ); ai.defineTool( { name: 'z_transform', description: 'Calculate the Z-transform of a function', inputSchema: z.object({ expression: z.string().describe('Function of discrete time'), timeVar: z.string().describe('Discrete time variable'), zVar: z.string().describe('Z-transform variable'), limit: z.number().optional().describe('Upper limit for summation (default: 100)') }), outputSchema: z.string(), }, async ({ expression, timeVar, zVar, limit = 100 }) => { return zTransform(expression, timeVar, zVar, limit); } ); // Financial helper functions const normalCDF = (x) => { const t = 1 / (1 + 0.2316419 * Math.abs(x)); const d = 0.3989423 * Math.exp(-x * x / 2); const p = d * t * (0.319381530 + t * (-0.356563782 + t * (1.781477937 + t * (-1.821255978 + t * 1.330274429)))); return x >= 0 ? 1 - p : p; }; const normalPDF = (x) => { return (1 / Math.sqrt(2 * Math.PI)) * Math.exp(-0.5 * x * x); }; const blackScholes = (S, K, T, r, sigma, optionType = 'call') => { try { const d1 = (Math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * Math.sqrt(T)); const d2 = d1 - sigma * Math.sqrt(T); if (optionType === 'call') { return S * normalCDF(d1) - K * Math.exp(-r * T) * normalCDF(d2); } else if (optionType === 'put') { return K * Math.exp(-r * T) * normalCDF(-d2) - S * normalCDF(-d1); } else { throw new Error('Invalid option type. Must be "call" or "put".'); } } catch (e) { return `Error: ${e.message}`; } }; const optionGreeks = (S, K, T, r, sigma, optionType = 'call') => { try { const d1 = (Math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * Math.sqrt(T)); const d2 = d1 - sigma * Math.sqrt(T); let delta, gamma, vega, theta, rho; if (optionType === 'call') { delta = normalCDF(d1); gamma = normalPDF(d1) / (S * sigma * Math.sqrt(T)); vega = S * normalPDF(d1) * Math.sqrt(T); theta = -(S * normalPDF(d1) * sigma) / (2 * Math.sqrt(T)) - r * K * Math.exp(-r * T) * normalCDF(d2); rho = K * T * Math.exp(-r * T) * normalCDF(d2); } else if (optionType === 'put') { delta = normalCDF(d1) - 1; gamma = normalPDF(d1) / (S * sigma * Math.sqrt(T)); vega = S * normalPDF(d1) * Math.sqrt(T); theta = -(S * normalPDF(d1) * sigma) / (2 * Math.sqrt(T)) + r * K * Math.exp(-r * T) * normalCDF(-d2); rho = -K * T * Math.exp(-r * T) * normalCDF(-d2); } else { throw new Error('Invalid option type. Must be "call" or "put".'); } return { delta, gamma, vega, theta, rho }; } catch (e) { return `Error: ${e.message}`; } }; // Define additional financial tools ai.defineTool( { name: 'black_scholes', description: 'Calculate Black-Scholes option price', inputSchema: z.object({ S: z.number().describe('Current price of the asset'), K: z.number().describe('Strike price of the option'), T: z.number().describe('Time to expiration in years'), r: z.number().describe('Risk-free interest rate'), sigma: z.number().describe('Volatility of the asset'), optionType: z.enum(['call', 'put']).default('call') .describe('Option type: "call" or "put"') }), outputSchema: z.union([z.number(), z.string()]), }, async ({ S, K, T, r, sigma, optionType }) => { return blackScholes(S, K, T, r, sigma, optionType); } ); ai.defineTool( { name: 'option_greeks', description: 'Calculate the Greeks for a Black-Scholes option', inputSchema: z.object({ S: z.number().describe('Current price of the asset'), K: z.number().describe('Strike price of the option'), T: z.number().describe('Time to expiration in years'), r: z.number().describe('Risk-free interest rate'), sigma: z.number().describe('Volatility of the asset'), optionType: z.enum(['call', 'put']).default('call') .describe('Option type: "call" or "put"') }), outputSchema: z.union([ z.object({ delta: z.number(), gamma: z.number(), vega: z.number(), theta: z.number(), rho: z.number() }), z.string() ]), }, async ({ S, K, T, r, sigma, optionType }) => { return optionGreeks(S, K, T, r, sigma, optionType); } ); const server = mcpServer(ai, { name: 'genkit_mcp', version: '0.1.0' }); server.start(); // Log when server starts console.log('Genkit MCP server started. Waiting for connections...');