Skip to main content
Glama
to-mcp-tools.ts3.77 kB
import type { NormalizedOperation } from '../types/index.js'; import type { MCPToolDefinition } from '../types/mcp-tool.js'; import { generateInputSchema, generateProxyMetadata } from './schema-generator.js'; import { isDangerousOperation } from '../parser/normalize.js'; import { log } from '../utils/logger.js'; /** * Convert operationId to snake_case tool name */ export function generateToolName(operationId: string): string { return operationId .replace(/([a-z])([A-Z])/g, '$1_$2') // camelCase -> camel_Case .toLowerCase() .replace(/[^a-z0-9_]/g, '_') // Non-alphanumeric -> _ .replace(/_+/g, '_') // Collapse underscores .replace(/^_|_$/g, '') // Trim leading/trailing .slice(0, 64); // Limit length } /** * Generate tool description from operation */ export function generateDescription(operation: NormalizedOperation): string { const parts: string[] = []; // Primary: use description or summary if (operation.description) { parts.push(operation.description); } else if (operation.summary) { parts.push(operation.summary); } else { // Fallback: generate from method and path parts.push(`${operation.method} ${operation.path}`); } // Add deprecation warning if (operation.deprecated) { parts.push('DEPRECATED: This endpoint may be removed in future versions.'); } // Limit length for AI context let description = parts.join('\n\n'); if (description.length > 1000) { description = description.slice(0, 997) + '...'; } return description; } /** * Generate title from operation */ export function generateTitle(operation: NormalizedOperation): string { if (operation.summary) { return operation.summary.slice(0, 100); } // Generate title from operationId return operation.operationId .replace(/_/g, ' ') .replace(/([a-z])([A-Z])/g, '$1 $2') .replace(/\b\w/g, (c) => c.toUpperCase()) .slice(0, 100); } /** * Determine if tool should be enabled by default */ export function shouldEnableByDefault(operation: NormalizedOperation): boolean { // Disable deprecated operations if (operation.deprecated) { return false; } // Disable dangerous operations (DELETE, etc.) if (isDangerousOperation(operation)) { return false; } return true; } /** * Convert a single operation to MCP tool definition */ export function convertOperation(operation: NormalizedOperation): MCPToolDefinition { const name = generateToolName(operation.operationId); return { name, title: generateTitle(operation), description: generateDescription(operation), inputSchema: generateInputSchema(operation), _proxy: generateProxyMetadata(operation), _ui: { enabled: shouldEnableByDefault(operation), originalOperationId: operation.operationId, modified: false, }, }; } /** * Convert all operations to MCP tool definitions */ export function convertToMCPTools(operations: NormalizedOperation[]): MCPToolDefinition[] { const tools: MCPToolDefinition[] = []; const usedNames = new Set<string>(); for (const operation of operations) { const tool = convertOperation(operation); // Ensure unique name let name = tool.name; let counter = 1; while (usedNames.has(name)) { name = `${tool.name}_${counter++}`; } usedNames.add(name); tools.push({ ...tool, name, }); } log.info('Converted operations to MCP tools', { total: tools.length, enabled: tools.filter((t) => t._ui.enabled).length, disabled: tools.filter((t) => !t._ui.enabled).length, }); return tools; } /** * Get enabled tools only */ export function getEnabledTools(tools: MCPToolDefinition[]): MCPToolDefinition[] { return tools.filter((t) => t._ui.enabled); }

Latest Blog Posts

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/procoders/openapi-mcp-ts'

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