Skip to main content
Glama
index.ts10.8 kB
#!/usr/bin/env node /** * BC Calculator MCP Server * Provides arbitrary precision mathematical calculations using the BC calculator */ import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, Tool, } from '@modelcontextprotocol/sdk/types.js'; import { BCProcessPool } from './bc-process-pool.js'; import { InputValidator } from './input-validator.js'; import { BCCalculatorError } from './types.js'; // Initialize process pool with configuration const pool = new BCProcessPool({ poolSize: 3, // 3 concurrent BC processes defaultPrecision: 20, // 20 decimal places by default defaultTimeout: 30000 // 30 second timeout }); // Track global precision setting let globalPrecision = 20; // Create MCP server const server = new Server( { name: 'bc-calculator', version: '1.0.0', }, { capabilities: { tools: {}, }, } ); /** * Tool definitions for the BC calculator */ const TOOLS: Tool[] = [ { name: 'calculate', description: 'Evaluate mathematical expressions using BC calculator with arbitrary precision arithmetic. ' + 'Supports basic operations (+, -, *, /, ^, %), comparisons, and math library functions ' + '(sqrt, sine, cosine, arctan, log, exp).', inputSchema: { type: 'object', properties: { expression: { type: 'string', description: 'Mathematical expression to evaluate (e.g., "2+2", "sqrt(144)", "355/113")' }, precision: { type: 'number', description: 'Number of decimal places for the result (0-100, default: 20)', minimum: 0, maximum: 100 } }, required: ['expression'] } }, { name: 'calculate_advanced', description: 'Execute advanced BC scripts with variables, functions, and control flow. ' + 'Supports multi-line scripts, variable assignments, loops, and conditionals.', inputSchema: { type: 'object', properties: { script: { type: 'string', description: 'Multi-line BC script with variables, loops, or functions' }, precision: { type: 'number', description: 'Number of decimal places for results (0-100, default: 20)', minimum: 0, maximum: 100 } }, required: ['script'] } }, { name: 'set_precision', description: 'Set the default precision (decimal places) for subsequent calculations. ' + 'This affects all calculations until changed again.', inputSchema: { type: 'object', properties: { precision: { type: 'number', description: 'Number of decimal places (0-100)', minimum: 0, maximum: 100 } }, required: ['precision'] } } ]; /** * List available tools */ server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: TOOLS }; }); /** * Handle tool execution requests */ server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { switch (name) { case 'calculate': return await handleCalculate(args); case 'calculate_advanced': return await handleCalculateAdvanced(args); case 'set_precision': return await handleSetPrecision(args); default: return { content: [{ type: 'text', text: `Unknown tool: ${name}` }], isError: true }; } } catch (error) { return { content: [{ type: 'text', text: `Error: ${error instanceof Error ? error.message : String(error)}` }], isError: true }; } }); /** * Handle basic calculation requests */ async function handleCalculate(args: unknown): Promise<any> { const startTime = Date.now(); // Validate arguments if (!args || typeof args !== 'object') { return { content: [{ type: 'text', text: 'Invalid arguments: expected an object' }], isError: true }; } const { expression, precision = globalPrecision } = args as { expression?: string; precision?: number; }; if (!expression) { return { content: [{ type: 'text', text: 'Missing required argument: expression' }], isError: true }; } // Validate precision if (precision !== undefined && (precision < 0 || precision > 100)) { return { content: [{ type: 'text', text: 'Invalid precision: must be between 0 and 100' }], isError: true }; } try { // Validate input expression const validation = InputValidator.validate(expression); if (!validation.valid) { return { content: [{ type: 'text', text: `Validation error: ${validation.error}` }], isError: true }; } // Acquire process from pool const process = await pool.acquireProcess(); try { // Set precision if different from current await process.setPrecision(precision); // Evaluate expression const result = await process.evaluate(validation.sanitized!); // Release process back to pool pool.releaseProcess(process); const executionTime = Date.now() - startTime; return { content: [{ type: 'text', text: JSON.stringify({ result, expression, precision, executionTimeMs: executionTime }, null, 2) }] }; } catch (error) { // Always release process even on error pool.releaseProcess(process); throw error; } } catch (error) { if (error instanceof BCCalculatorError) { return { content: [{ type: 'text', text: `BC Calculator Error [${error.code}]: ${error.message}` }], isError: true }; } return { content: [{ type: 'text', text: `Calculation error: ${error instanceof Error ? error.message : String(error)}` }], isError: true }; } } /** * Handle advanced calculation requests with multi-line scripts */ async function handleCalculateAdvanced(args: unknown): Promise<any> { const startTime = Date.now(); // Validate arguments if (!args || typeof args !== 'object') { return { content: [{ type: 'text', text: 'Invalid arguments: expected an object' }], isError: true }; } const { script, precision = globalPrecision } = args as { script?: string; precision?: number; }; if (!script) { return { content: [{ type: 'text', text: 'Missing required argument: script' }], isError: true }; } // Validate precision if (precision !== undefined && (precision < 0 || precision > 100)) { return { content: [{ type: 'text', text: 'Invalid precision: must be between 0 and 100' }], isError: true }; } try { // Validate input script const validation = InputValidator.validate(script); if (!validation.valid) { return { content: [{ type: 'text', text: `Validation error: ${validation.error}` }], isError: true }; } // Acquire process from pool const process = await pool.acquireProcess(); try { // Set precision await process.setPrecision(precision); // Execute script const result = await process.evaluate(validation.sanitized!); // Release process pool.releaseProcess(process); const executionTime = Date.now() - startTime; return { content: [{ type: 'text', text: JSON.stringify({ result, script: script.substring(0, 100) + (script.length > 100 ? '...' : ''), precision, executionTimeMs: executionTime }, null, 2) }] }; } catch (error) { pool.releaseProcess(process); throw error; } } catch (error) { if (error instanceof BCCalculatorError) { return { content: [{ type: 'text', text: `BC Calculator Error [${error.code}]: ${error.message}` }], isError: true }; } return { content: [{ type: 'text', text: `Script execution error: ${error instanceof Error ? error.message : String(error)}` }], isError: true }; } } /** * Handle precision setting requests */ async function handleSetPrecision(args: unknown): Promise<any> { // Validate arguments if (!args || typeof args !== 'object') { return { content: [{ type: 'text', text: 'Invalid arguments: expected an object' }], isError: true }; } const { precision } = args as { precision?: number }; if (precision === undefined) { return { content: [{ type: 'text', text: 'Missing required argument: precision' }], isError: true }; } // Validate precision if (precision < 0 || precision > 100) { return { content: [{ type: 'text', text: 'Invalid precision: must be between 0 and 100' }], isError: true }; } // Update global precision globalPrecision = precision; return { content: [{ type: 'text', text: JSON.stringify({ message: 'Precision updated successfully', precision: globalPrecision }, null, 2) }] }; } /** * Main server initialization */ async function main() { try { // Initialize the BC process pool await pool.initialize(); // Start the MCP server const transport = new StdioServerTransport(); await server.connect(transport); console.error('BC Calculator MCP server running on stdio'); console.error(`Pool status: ${JSON.stringify(pool.getStatus())}`); // Handle graceful shutdown process.on('SIGINT', async () => { console.error('Received SIGINT, shutting down...'); await pool.shutdown(); process.exit(0); }); process.on('SIGTERM', async () => { console.error('Received SIGTERM, shutting down...'); await pool.shutdown(); process.exit(0); }); } catch (error) { console.error('Failed to start BC Calculator MCP server:', error); process.exit(1); } } main().catch((error) => { console.error('Fatal error in main():', error); process.exit(1); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/cthunter01/MCPCalculator'

If you have feedback or need assistance with the MCP directory API, please join our Discord server