Skip to main content
Glama

IT Tools MCP Server

index.ts•7.33 kB
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { z } from "zod"; export function registerAnsibleInventoryParser(server: McpServer) { server.registerTool("generate_ansible_inventory", { description: "Parse and validate Ansible inventory files", inputSchema: { inventory: z.string().describe("Ansible inventory content (INI or YAML format)"), }, // VS Code compliance annotations annotations: { title: "Generate Ansible Inventory", description: "Parse and validate Ansible inventory files", readOnlyHint: false } }, async ({ inventory }) => { if (!inventory?.trim()) { return { content: [ { type: "text", text: "Please provide inventory content to parse", }, ], }; } try { let parsed: any = {}; const trimmed = inventory.trim(); // Check if it's YAML format if (trimmed.startsWith('{') || trimmed.includes('---') || trimmed.includes(':')) { try { const yaml = await import('js-yaml'); parsed = yaml.load(inventory); } catch (yamlError) { // Fall back to INI parsing parsed = parseINIInventory(inventory); } } else { // Parse as INI format parsed = parseINIInventory(inventory); } // Validate and format the result const result = formatInventoryResult(parsed); return { content: [ { type: "text", text: result, }, ], }; } catch (error: any) { return { content: [ { type: "text", text: `Error parsing inventory: ${error.message}`, }, ], }; } } ); } // Helper function to parse INI format inventory function parseINIInventory(inventory: string): any { const result: any = { all: { children: {}, hosts: {} } }; const lines = inventory.split('\n'); let currentGroup = 'all'; for (const line of lines) { const trimmed = line.trim(); // Skip empty lines and comments if (!trimmed || trimmed.startsWith('#') || trimmed.startsWith(';')) { continue; } // Group header if (trimmed.startsWith('[') && trimmed.endsWith(']')) { currentGroup = trimmed.slice(1, -1); // Handle group variables and children sections if (currentGroup.includes(':')) { const [groupName, section] = currentGroup.split(':'); if (section === 'vars') { if (!result.all.children[groupName]) { result.all.children[groupName] = { hosts: {}, vars: {} }; } currentGroup = `${groupName}:vars`; } else if (section === 'children') { if (!result.all.children[groupName]) { result.all.children[groupName] = { hosts: {}, children: {} }; } currentGroup = `${groupName}:children`; } } else { if (!result.all.children[currentGroup]) { result.all.children[currentGroup] = { hosts: {} }; } } continue; } // Parse host or variable lines if (currentGroup.endsWith(':vars')) { // Group variables const groupName = currentGroup.replace(':vars', ''); const [key, ...valueParts] = trimmed.split('='); if (key && valueParts.length > 0) { const value = valueParts.join('='); if (!result.all.children[groupName].vars) { result.all.children[groupName].vars = {}; } result.all.children[groupName].vars[key.trim()] = value.trim(); } } else if (currentGroup.endsWith(':children')) { // Child groups const groupName = currentGroup.replace(':children', ''); if (!result.all.children[groupName].children) { result.all.children[groupName].children = {}; } result.all.children[groupName].children[trimmed] = {}; } else { // Host entries const [hostname, ...varParts] = trimmed.split(/\s+/); const hostVars: any = {}; // Parse host variables for (const varPart of varParts) { const [key, ...valueParts] = varPart.split('='); if (key && valueParts.length > 0) { hostVars[key] = valueParts.join('='); } } if (currentGroup === 'all') { result.all.hosts[hostname] = hostVars; } else { result.all.children[currentGroup].hosts[hostname] = hostVars; } } } return result; } // Helper function to format inventory parsing results function formatInventoryResult(parsed: any): string { let result = '# Parsed Ansible Inventory\n\n'; try { // Count totals let totalHosts = 0; let totalGroups = 0; function countHosts(obj: any): number { let count = 0; if (obj.hosts) { count += Object.keys(obj.hosts).length; } if (obj.children) { for (const child of Object.values(obj.children)) { count += countHosts(child); } } return count; } function countGroups(obj: any): number { let count = 0; if (obj.children) { count += Object.keys(obj.children).length; for (const child of Object.values(obj.children)) { count += countGroups(child); } } return count; } totalHosts = countHosts(parsed.all || parsed); totalGroups = countGroups(parsed.all || parsed); result += `## Summary\n`; result += `- Total Hosts: ${totalHosts}\n`; result += `- Total Groups: ${totalGroups}\n\n`; // List all groups and hosts function formatGroup(groupName: string, group: any, indent = 0): string { const spaces = ' '.repeat(indent); let output = `${spaces}## ${groupName}\n`; if (group.hosts && Object.keys(group.hosts).length > 0) { output += `${spaces}### Hosts:\n`; for (const [hostname, vars] of Object.entries(group.hosts)) { output += `${spaces}- ${hostname}`; if (vars && typeof vars === 'object' && Object.keys(vars).length > 0) { output += ` (vars: ${Object.keys(vars).join(', ')})`; } output += '\n'; } } if (group.vars && Object.keys(group.vars).length > 0) { output += `${spaces}### Variables:\n`; for (const [key, value] of Object.entries(group.vars)) { output += `${spaces}- ${key}: ${value}\n`; } } if (group.children && Object.keys(group.children).length > 0) { output += `${spaces}### Child Groups:\n`; for (const [childName, child] of Object.entries(group.children)) { output += formatGroup(childName, child, indent + 1); } } return output + '\n'; } if (parsed.all) { result += formatGroup('all', parsed.all); } else { // Handle direct group format for (const [groupName, group] of Object.entries(parsed)) { result += formatGroup(groupName, group); } } } catch (error: any) { result += `Error formatting inventory: ${error.message}`; } return result; }

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/wrenchpilot/it-tools-mcp'

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