Skip to main content
Glama

MCP Utility Tools

by haasonsaas
index.tsβ€’11.3 kB
#!/usr/bin/env node import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema, McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js"; import { setTimeout } from "node:timers/promises"; // Cache storage with TTL support interface CacheEntry { value: any; expiresAt: number; } const cache = new Map<string, CacheEntry>(); // Clean up expired cache entries periodically setInterval(() => { const now = Date.now(); for (const [key, entry] of cache.entries()) { if (entry.expiresAt <= now) { cache.delete(key); } } }, 60000); // Clean every minute // Create server const server = new Server({ name: "mcp-utility-tools", version: "1.0.0", }, { capabilities: { tools: {} } }); // Define tools const tools = [ { name: "retry_with_backoff", description: "Execute a tool with automatic retry and exponential backoff for failures", inputSchema: { type: "object", properties: { tool_name: { type: "string", description: "Name of the tool to execute" }, args: { type: "object", description: "Arguments to pass to the tool" }, max_retries: { type: "number", description: "Maximum number of retry attempts", default: 3, minimum: 1, maximum: 10 }, initial_delay_ms: { type: "number", description: "Initial delay in milliseconds before first retry", default: 1000, minimum: 100, maximum: 60000 }, max_delay_ms: { type: "number", description: "Maximum delay between retries", default: 30000 }, jitter: { type: "boolean", description: "Add random jitter to retry delays", default: true } }, required: ["tool_name", "args"] } }, { name: "cache_get", description: "Get a value from the cache by key", inputSchema: { type: "object", properties: { key: { type: "string", description: "Cache key to retrieve" } }, required: ["key"] } }, { name: "cache_put", description: "Store a value in the cache with TTL", inputSchema: { type: "object", properties: { key: { type: "string", description: "Cache key" }, value: { description: "Value to cache (any JSON-serializable data)" }, ttl_seconds: { type: "number", description: "Time to live in seconds", default: 300, minimum: 1, maximum: 86400 // 24 hours } }, required: ["key", "value"] } }, { name: "cache_delete", description: "Delete a key from the cache", inputSchema: { type: "object", properties: { key: { type: "string", description: "Cache key to delete" } }, required: ["key"] } }, { name: "cache_clear", description: "Clear all entries from the cache", inputSchema: { type: "object", properties: {} } }, { name: "fork_join", description: "Execute multiple tool calls in parallel and return all results", inputSchema: { type: "object", properties: { calls: { type: "array", description: "Array of tool calls to execute in parallel", items: { type: "object", properties: { tool_name: { type: "string", description: "Name of the tool to execute" }, args: { type: "object", description: "Arguments for the tool" } }, required: ["tool_name", "args"] }, minItems: 1, maxItems: 10 }, timeout_ms: { type: "number", description: "Overall timeout for all operations", default: 30000, minimum: 1000, maximum: 300000 // 5 minutes }, continue_on_error: { type: "boolean", description: "Continue executing even if some calls fail", default: true } }, required: ["calls"] } } ]; // Register list handler server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools }; }); // Helper function to execute a tool call async function executeToolCall(toolName: string, args: any): Promise<any[]> { // This would normally call back into the MCP client to execute the tool // For this utility server, we'll simulate tool execution // In a real implementation, this would need access to the client's tool execution context // For now, return a mock response indicating the limitation return [{ type: "text", text: JSON.stringify({ error: "Tool execution requires integration with MCP client context", tool: toolName, args: args }) }]; } // Register call handler server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { switch (name) { case "retry_with_backoff": { const { tool_name, args: toolArgs, max_retries = 3, initial_delay_ms = 1000, max_delay_ms = 30000, jitter = true } = args as any; let lastError: Error | null = null; let delay = initial_delay_ms; for (let attempt = 0; attempt <= max_retries; attempt++) { try { if (attempt > 0) { // Add jitter if enabled const jitterAmount = jitter ? Math.random() * 0.3 * delay : 0; const actualDelay = delay + jitterAmount; console.error(`Retry attempt ${attempt} after ${actualDelay}ms delay`); await setTimeout(actualDelay); } // Execute the tool const result = await executeToolCall(tool_name, toolArgs); return { content: [{ type: "text", text: JSON.stringify({ success: true, attempts: attempt + 1, result: result }, null, 2) }] }; } catch (error) { lastError = error as Error; console.error(`Attempt ${attempt + 1} failed:`, error); // Calculate next delay with exponential backoff delay = Math.min(delay * 2, max_delay_ms); } } throw new McpError( ErrorCode.InternalError, `Failed after ${max_retries + 1} attempts: ${lastError?.message}` ); } case "cache_get": { const { key } = args as { key: string }; const entry = cache.get(key); if (!entry) { return { content: [{ type: "text", text: JSON.stringify({ found: false, key }) }] }; } // Check if expired if (entry.expiresAt <= Date.now()) { cache.delete(key); return { content: [{ type: "text", text: JSON.stringify({ found: false, key, reason: "expired" }) }] }; } return { content: [{ type: "text", text: JSON.stringify({ found: true, key, value: entry.value, expires_in_seconds: Math.floor((entry.expiresAt - Date.now()) / 1000) }) }] }; } case "cache_put": { const { key, value, ttl_seconds = 300 } = args as any; const expiresAt = Date.now() + (ttl_seconds * 1000); cache.set(key, { value, expiresAt }); return { content: [{ type: "text", text: JSON.stringify({ success: true, key, ttl_seconds, expires_at: new Date(expiresAt).toISOString() }) }] }; } case "cache_delete": { const { key } = args as { key: string }; const existed = cache.delete(key); return { content: [{ type: "text", text: JSON.stringify({ success: true, key, existed }) }] }; } case "cache_clear": { const size = cache.size; cache.clear(); return { content: [{ type: "text", text: JSON.stringify({ success: true, cleared_entries: size }) }] }; } case "fork_join": { const { calls, timeout_ms = 30000, continue_on_error = true } = args as any; // Create promises for all calls const promises = calls.map(async (call: any, index: number) => { try { const result = await executeToolCall(call.tool_name, call.args); return { index, success: true, tool_name: call.tool_name, result }; } catch (error) { const errorResult = { index, success: false, tool_name: call.tool_name, error: (error as Error).message }; if (!continue_on_error) { throw error; } return errorResult; } }); // Execute with timeout const timeoutPromise = setTimeout(timeout_ms).then(() => { throw new Error(`Fork-join timeout after ${timeout_ms}ms`); }); try { const results = await Promise.race([ Promise.all(promises), timeoutPromise ]); return { content: [{ type: "text", text: JSON.stringify({ success: true, total_calls: calls.length, results: results }, null, 2) }] }; } catch (error) { if ((error as Error).message.includes('timeout')) { throw new McpError( ErrorCode.InternalError, `Fork-join operation timed out after ${timeout_ms}ms` ); } throw error; } } default: throw new McpError( ErrorCode.MethodNotFound, `Unknown tool: ${name}` ); } } catch (error) { if (error instanceof McpError) { throw error; } throw new McpError( ErrorCode.InternalError, `Tool execution failed: ${(error as Error).message}` ); } }); // Start server const transport = new StdioServerTransport(); await server.connect(transport); console.error("MCP Utility Tools Server running on stdio");

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/haasonsaas/mcp-utility-tools'

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