Skip to main content
Glama

MCP ChatGPT Multi-Server Suite

by bobhuff0
units-server.ts17.2 kB
import express from 'express'; import cors from 'cors'; import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; import path from 'path'; const APP_NAME = 'MCP Units & Measurements Converter App'; const PORT = process.env.PORT || 3003; interface ConversionResult { from: string; to: string; value: number; result: number; category: string; formula?: string; } // Conversion rates to base unit for each category const CONVERSIONS: { [category: string]: { [unit: string]: { name: string, toBase: number, symbol: string } } } = { length: { meter: { name: 'Meter', toBase: 1, symbol: 'm' }, kilometer: { name: 'Kilometer', toBase: 1000, symbol: 'km' }, centimeter: { name: 'Centimeter', toBase: 0.01, symbol: 'cm' }, millimeter: { name: 'Millimeter', toBase: 0.001, symbol: 'mm' }, mile: { name: 'Mile', toBase: 1609.344, symbol: 'mi' }, yard: { name: 'Yard', toBase: 0.9144, symbol: 'yd' }, foot: { name: 'Foot', toBase: 0.3048, symbol: 'ft' }, inch: { name: 'Inch', toBase: 0.0254, symbol: 'in' }, nautical_mile: { name: 'Nautical Mile', toBase: 1852, symbol: 'nmi' } }, weight: { kilogram: { name: 'Kilogram', toBase: 1, symbol: 'kg' }, gram: { name: 'Gram', toBase: 0.001, symbol: 'g' }, milligram: { name: 'Milligram', toBase: 0.000001, symbol: 'mg' }, metric_ton: { name: 'Metric Ton', toBase: 1000, symbol: 't' }, pound: { name: 'Pound', toBase: 0.45359237, symbol: 'lb' }, ounce: { name: 'Ounce', toBase: 0.028349523125, symbol: 'oz' }, ton: { name: 'Ton (US)', toBase: 907.18474, symbol: 'ton' }, stone: { name: 'Stone', toBase: 6.35029318, symbol: 'st' } }, temperature: { celsius: { name: 'Celsius', toBase: 1, symbol: '°C' }, fahrenheit: { name: 'Fahrenheit', toBase: 1, symbol: '°F' }, kelvin: { name: 'Kelvin', toBase: 1, symbol: 'K' } }, volume: { liter: { name: 'Liter', toBase: 1, symbol: 'L' }, milliliter: { name: 'Milliliter', toBase: 0.001, symbol: 'mL' }, cubic_meter: { name: 'Cubic Meter', toBase: 1000, symbol: 'm³' }, gallon: { name: 'Gallon (US)', toBase: 3.785411784, symbol: 'gal' }, quart: { name: 'Quart (US)', toBase: 0.946352946, symbol: 'qt' }, pint: { name: 'Pint (US)', toBase: 0.473176473, symbol: 'pt' }, cup: { name: 'Cup (US)', toBase: 0.2365882365, symbol: 'cup' }, fluid_ounce: { name: 'Fluid Ounce (US)', toBase: 0.0295735296, symbol: 'fl oz' }, tablespoon: { name: 'Tablespoon', toBase: 0.01478676478, symbol: 'tbsp' }, teaspoon: { name: 'Teaspoon', toBase: 0.00492892159, symbol: 'tsp' } }, area: { square_meter: { name: 'Square Meter', toBase: 1, symbol: 'm²' }, square_kilometer: { name: 'Square Kilometer', toBase: 1000000, symbol: 'km²' }, square_centimeter: { name: 'Square Centimeter', toBase: 0.0001, symbol: 'cm²' }, square_mile: { name: 'Square Mile', toBase: 2589988.110336, symbol: 'mi²' }, square_yard: { name: 'Square Yard', toBase: 0.83612736, symbol: 'yd²' }, square_foot: { name: 'Square Foot', toBase: 0.09290304, symbol: 'ft²' }, square_inch: { name: 'Square Inch', toBase: 0.00064516, symbol: 'in²' }, hectare: { name: 'Hectare', toBase: 10000, symbol: 'ha' }, acre: { name: 'Acre', toBase: 4046.8564224, symbol: 'ac' } }, speed: { meter_per_second: { name: 'Meter per Second', toBase: 1, symbol: 'm/s' }, kilometer_per_hour: { name: 'Kilometer per Hour', toBase: 0.277777778, symbol: 'km/h' }, mile_per_hour: { name: 'Mile per Hour', toBase: 0.44704, symbol: 'mph' }, foot_per_second: { name: 'Foot per Second', toBase: 0.3048, symbol: 'ft/s' }, knot: { name: 'Knot', toBase: 0.514444444, symbol: 'kn' } }, pressure: { pascal: { name: 'Pascal', toBase: 1, symbol: 'Pa' }, kilopascal: { name: 'Kilopascal', toBase: 1000, symbol: 'kPa' }, bar: { name: 'Bar', toBase: 100000, symbol: 'bar' }, psi: { name: 'Pounds per Square Inch', toBase: 6894.757293168, symbol: 'psi' }, atmosphere: { name: 'Atmosphere', toBase: 101325, symbol: 'atm' }, torr: { name: 'Torr', toBase: 133.322368, symbol: 'Torr' } }, energy: { joule: { name: 'Joule', toBase: 1, symbol: 'J' }, kilojoule: { name: 'Kilojoule', toBase: 1000, symbol: 'kJ' }, calorie: { name: 'Calorie', toBase: 4.184, symbol: 'cal' }, kilocalorie: { name: 'Kilocalorie', toBase: 4184, symbol: 'kcal' }, watt_hour: { name: 'Watt Hour', toBase: 3600, symbol: 'Wh' }, kilowatt_hour: { name: 'Kilowatt Hour', toBase: 3600000, symbol: 'kWh' }, btu: { name: 'British Thermal Unit', toBase: 1055.06, symbol: 'BTU' } }, power: { watt: { name: 'Watt', toBase: 1, symbol: 'W' }, kilowatt: { name: 'Kilowatt', toBase: 1000, symbol: 'kW' }, megawatt: { name: 'Megawatt', toBase: 1000000, symbol: 'MW' }, horsepower: { name: 'Horsepower', toBase: 745.699872, symbol: 'hp' }, btu_per_hour: { name: 'BTU per Hour', toBase: 0.293071, symbol: 'BTU/h' } }, data: { byte: { name: 'Byte', toBase: 1, symbol: 'B' }, kilobyte: { name: 'Kilobyte', toBase: 1024, symbol: 'KB' }, megabyte: { name: 'Megabyte', toBase: 1048576, symbol: 'MB' }, gigabyte: { name: 'Gigabyte', toBase: 1073741824, symbol: 'GB' }, terabyte: { name: 'Terabyte', toBase: 1099511627776, symbol: 'TB' }, bit: { name: 'Bit', toBase: 0.125, symbol: 'bit' }, kilobit: { name: 'Kilobit', toBase: 128, symbol: 'Kb' }, megabit: { name: 'Megabit', toBase: 131072, symbol: 'Mb' }, gigabit: { name: 'Gigabit', toBase: 134217728, symbol: 'Gb' } } }; // Create Express app const app = express(); app.use(cors()); app.use(express.json()); app.use(express.static(path.join(__dirname, '../public-units'))); // Serve the ChatGPT app HTML app.get('/', (req, res) => { res.sendFile(path.join(__dirname, '../public-units/index.html')); }); // MCP Tool endpoint app.post('/mcp/tools/call', async (req, res) => { try { const { name, arguments: args } = req.body; if (name === 'convertUnits') { const { from, to, value, category } = args; const result = convertUnits(from, to, value, category); res.json(result); } else if (name === 'getSupportedUnits') { const { category } = args; const result = getSupportedUnits(category); res.json(result); } else if (name === 'getCategories') { const result = getCategories(); res.json(result); } else { return res.status(400).json({ error: 'Unknown tool' }); } } catch (error: any) { console.error('Error calling tool:', error.message); res.status(500).json({ error: error.message }); } }); // MCP Discovery endpoint (for ChatGPT) app.get('/mcp', (req, res) => { res.json({ jsonrpc: "2.0", result: { protocolVersion: "2024-11-05", capabilities: { tools: {} }, serverInfo: { name: "UnitsMCP", version: "1.0.0" } } }); }); // ChatGPT MCP Connector endpoint app.post('/mcp', (req, res) => { const { method, params, id } = req.body; console.log('MCP Request:', JSON.stringify({ method, params, id })); if (method === "initialize") { res.json({ jsonrpc: "2.0", id: id, result: { protocolVersion: "2024-11-05", capabilities: { tools: {} }, serverInfo: { name: "Units & Measurements MCP", version: "1.0.0" } } }); } else if (method === "tools/list") { res.json({ jsonrpc: "2.0", id: id, result: { tools: [ { name: "convertUnits", description: "Convert between different units of measurement", inputSchema: { type: "object", properties: { from: { type: "string", description: "Source unit (e.g., meter, kilogram, celsius)" }, to: { type: "string", description: "Target unit" }, value: { type: "number", description: "Value to convert" }, category: { type: "string", description: "Unit category (length, weight, temperature, etc.)" } }, required: ["from", "to", "value"] } }, { name: "getSupportedUnits", description: "Get list of supported units for a category", inputSchema: { type: "object", properties: { category: { type: "string", description: "Category name (optional, returns all if not specified)" } } } }, { name: "getCategories", description: "Get list of all available unit categories", inputSchema: { type: "object", properties: {} } } ] } }); } else if (method === "tools/call") { const { name, arguments: args } = params; try { let result; if (name === "convertUnits") { result = convertUnits(args.from, args.to, args.value, args.category); } else if (name === "getSupportedUnits") { result = getSupportedUnits(args.category); } else if (name === "getCategories") { result = getCategories(); } else { return res.json({ jsonrpc: "2.0", id: id, error: { code: -32601, message: `Unknown tool: ${name}` } }); } res.json({ jsonrpc: "2.0", id: id, result: { content: [ { type: "text", text: JSON.stringify(result, null, 2) } ] } }); } catch (error: any) { res.json({ jsonrpc: "2.0", id: id, error: { code: -32603, message: error.message } }); } } else { res.json({ jsonrpc: "2.0", id: id, error: { code: -32601, message: `Method not found: ${method}` } }); } }); // List available tools app.get('/mcp/tools/list', (req, res) => { res.json({ tools: [ { name: 'convertUnits', description: 'Convert between different units of measurement', inputSchema: { type: 'object', properties: { from: { type: 'string', description: 'Source unit (e.g., meter, kilogram, celsius)' }, to: { type: 'string', description: 'Target unit' }, value: { type: 'number', description: 'Value to convert' }, category: { type: 'string', description: 'Unit category (optional: length, weight, temperature, volume, area, speed, pressure, energy, power, data)' } }, required: ['from', 'to', 'value'] } }, { name: 'getSupportedUnits', description: 'Get list of supported units for a category', inputSchema: { type: 'object', properties: { category: { type: 'string', description: 'Category name (optional, returns all if not specified)' } } } }, { name: 'getCategories', description: 'Get list of all available unit categories', inputSchema: { type: 'object', properties: {} } } ] }); }); function convertUnits(from: string, to: string, value: number, category?: string): ConversionResult { try { from = from.toLowerCase().replace(/\s+/g, '_'); to = to.toLowerCase().replace(/\s+/g, '_'); // Find category if not provided let foundCategory = category; if (!foundCategory) { for (const cat in CONVERSIONS) { if (CONVERSIONS[cat][from] && CONVERSIONS[cat][to]) { foundCategory = cat; break; } } if (!foundCategory) { throw new Error(`Could not find units '${from}' and '${to}' in the same category`); } } const categoryData = CONVERSIONS[foundCategory]; if (!categoryData) { throw new Error(`Unknown category: ${foundCategory}`); } const fromUnit = categoryData[from]; const toUnit = categoryData[to]; if (!fromUnit) { throw new Error(`Unknown unit: ${from}`); } if (!toUnit) { throw new Error(`Unknown unit: ${to}`); } let result: number; // Special handling for temperature if (foundCategory === 'temperature') { result = convertTemperature(from, to, value); } else { // Convert to base unit, then to target unit const baseValue = value * fromUnit.toBase; result = baseValue / toUnit.toBase; } // Round to reasonable precision result = Math.round(result * 1000000) / 1000000; return { from: fromUnit.name, to: toUnit.name, value, result, category: foundCategory }; } catch (error: any) { throw new Error(`Failed to convert units: ${error.message}`); } } function convertTemperature(from: string, to: string, value: number): number { // Convert to Celsius first let celsius: number; if (from === 'celsius') { celsius = value; } else if (from === 'fahrenheit') { celsius = (value - 32) * 5 / 9; } else if (from === 'kelvin') { celsius = value - 273.15; } else { throw new Error(`Unknown temperature unit: ${from}`); } // Convert from Celsius to target if (to === 'celsius') { return celsius; } else if (to === 'fahrenheit') { return (celsius * 9 / 5) + 32; } else if (to === 'kelvin') { return celsius + 273.15; } else { throw new Error(`Unknown temperature unit: ${to}`); } } function getSupportedUnits(category?: string): any { if (category) { const cat = category.toLowerCase(); if (!CONVERSIONS[cat]) { throw new Error(`Unknown category: ${category}`); } return { category: cat, units: CONVERSIONS[cat] }; } return { categories: CONVERSIONS }; } function getCategories(): { categories: string[], count: number } { const categories = Object.keys(CONVERSIONS); return { categories, count: categories.length }; } // Create MCP Server const mcpServer = new Server( { name: APP_NAME, version: '1.0.0', }, { capabilities: { tools: {}, }, } ); // Register MCP handlers mcpServer.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: 'convertUnits', description: 'Convert between different units of measurement', inputSchema: { type: 'object', properties: { from: { type: 'string', description: 'Source unit' }, to: { type: 'string', description: 'Target unit' }, value: { type: 'number', description: 'Value to convert' }, category: { type: 'string', description: 'Unit category (optional)' } }, required: ['from', 'to', 'value'] } }, { name: 'getSupportedUnits', description: 'Get supported units', inputSchema: { type: 'object', properties: { category: { type: 'string', description: 'Category name (optional)' } } } }, { name: 'getCategories', description: 'Get all unit categories', inputSchema: { type: 'object', properties: {} } } ] }; }); mcpServer.setRequestHandler(CallToolRequestSchema, async (request) => { const args = request.params.arguments as any; if (request.params.name === 'convertUnits') { const result = convertUnits(args.from, args.to, args.value, args.category); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } else if (request.params.name === 'getSupportedUnits') { const result = getSupportedUnits(args.category); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } else if (request.params.name === 'getCategories') { const result = getCategories(); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; } throw new Error(`Unknown tool: ${request.params.name}`); }); // Start Express server app.listen(PORT, () => { console.log(`\x1b[32m✓ Units & Measurements Converter Server running on http://localhost:${PORT}\x1b[0m`); console.log(`\x1b[36mℹ Make sure to expose via ngrok: ngrok http ${PORT}\x1b[0m`); console.log(`\x1b[36mℹ Supported categories: ${Object.keys(CONVERSIONS).join(', ')}\x1b[0m`); });

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/bobhuff0/MCPAddIn'

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