Skip to main content
Glama
index.ts5.56 kB
#!/usr/bin/env node 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 { loadConfig } from './config'; import { buildJsonSchema, buildZodSchema, executeDynamicTool } from './tools/DynamicToolFactory'; import { RunCliToolSchema, executeRunCli } from './tools/RunCliTool'; import { ToolConfig } from './types'; /** Reserved tool name for fallback mode */ const FALLBACK_TOOL_NAME = 'run_cli'; /** * Static JSON schema for run_cli tool */ const RUN_CLI_INPUT_SCHEMA = { type: 'object' as const, properties: { args: { type: 'array', items: { type: 'string' }, description: 'Array of arguments to pass to the CLI tool (e.g., ["status"] for "git status")', }, }, required: ['args'], }; /** * Parse command line arguments */ function parseArgs(): { baseCli: string; configPath?: string } { const args = process.argv.slice(2); if (args.length === 0) { console.error( 'Usage: mcp-cli-wrapper <base-cli> [config.json]\n\n' + 'Arguments:\n' + ' base-cli The CLI command to wrap (e.g., "git", "docker")\n' + ' config.json Optional path to tool configuration file\n\n' + 'Examples:\n' + ' mcp-cli-wrapper git\n' + ' mcp-cli-wrapper git ./git-tools.json\n' + ' mcp-cli-wrapper docker ./docker-config.json' ); process.exit(1); } return { baseCli: args[0], configPath: args[1], }; } /** * Validate that config doesn't use reserved tool names */ function validateToolNames(tools: ToolConfig[]): void { for (const tool of tools) { if (tool.name === FALLBACK_TOOL_NAME) { throw new Error( `Tool name "${FALLBACK_TOOL_NAME}" is reserved for fallback mode. ` + `Please use a different name for your tool.` ); } } } /** * Build tools list for ListTools response */ function buildToolsList(baseCli: string, toolConfigs: ToolConfig[] | null): Tool[] { if (toolConfigs === null) { // Fallback mode: single run_cli tool return [ { name: FALLBACK_TOOL_NAME, description: `Executes "${baseCli}" with the provided arguments and returns the output.`, inputSchema: RUN_CLI_INPUT_SCHEMA, }, ]; } // Config mode: dynamic tools from config return toolConfigs.map((config) => ({ name: config.name, description: config.description, inputSchema: buildJsonSchema(config.parameters), })); } /** * Main entry point */ async function main(): Promise<void> { const { baseCli, configPath } = parseArgs(); // Load config if provided let toolConfigs: ToolConfig[] | null = null; if (configPath) { try { const config = loadConfig(configPath); // Validate no reserved names are used validateToolNames(config.tools); toolConfigs = config.tools; console.error(`Loaded ${toolConfigs.length} tools for "${baseCli}" from config`); } catch (error) { console.error(`Error loading config: ${(error as Error).message}`); console.error('Falling back to run_cli tool'); } } else { console.error(`Using run_cli tool for "${baseCli}"`); } // Create lookup map for tool configs const toolConfigMap = new Map<string, ToolConfig>(); if (toolConfigs) { for (const config of toolConfigs) { toolConfigMap.set(config.name, config); } } // Create MCP server const server = new Server( { name: `mcp-cli-${baseCli}`, version: '1.0.0', }, { capabilities: { tools: {}, }, } ); // Handle ListTools request server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: buildToolsList(baseCli, toolConfigs), }; }); // Handle CallTool request server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { let result: string; if (toolConfigs === null) { // Fallback mode: only run_cli is available if (name !== FALLBACK_TOOL_NAME) { throw new Error(`Unknown tool: ${name}`); } const parsed = RunCliToolSchema.parse(args); result = await executeRunCli(baseCli, parsed); } else { // Config mode: find and execute the configured tool const config = toolConfigMap.get(name); if (!config) { throw new Error(`Unknown tool: ${name}`); } // Validate input against the tool's schema const schema = buildZodSchema(config.parameters); const parsed = schema.parse(args); result = await executeDynamicTool(baseCli, config, parsed); } return { content: [ { type: 'text', text: result, }, ], }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); return { content: [ { type: 'text', text: `Error: ${errorMessage}`, }, ], isError: true, }; } }); // Connect to stdio transport const transport = new StdioServerTransport(); await server.connect(transport); console.error(`MCP server for "${baseCli}" started`); } // Run main and handle errors main().catch((error) => { console.error('Fatal error:', 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/pyrex41/mcp-cli'

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