We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/arebury/ivr-flow-linter'
If you have feedback or need assistance with the MCP directory API, please join our Discord server
{"jsonrpc":"2.0","result":{"data":{"schema_version":"0.1","flow_hash":"38ef6234d0845f397b1e6069647a9a98d08ad45df9620c0af4b1f29ad766b207","score":25,"errors":[{"code":"unreachable_node","node_id":"orphan_node","message":"Node 'orphan_node' is not reachable from Start","fix":"Connect this node to the main flow or remove it","severity":"error"},{"code":"dead_end_not_end","node_id":"dead_end","message":"Node 'dead_end' (Speak) has no outgoing edges but is not an End node","fix":"Add an outgoing edge or change type to End","severity":"error"},{"code":"dead_end_not_end","node_id":"orphan_node","message":"Node 'orphan_node' (Speak) has no outgoing edges but is not an End node","fix":"Add an outgoing edge or change type to End","severity":"error"},{"code":"missing_fallback_on_ask","node_id":"ask_account","message":"Ask node 'ask_account' missing fallback (timeout/no-match) path","fix":"Add an edge with is_fallback=true or label='timeout'","severity":"error"},{"code":"var_used_not_defined","node_id":"check_balance","message":"Variable 'account_num' used in 'check_balance' is never defined in flow","fix":"Define 'account_num' in a SetVar or Input node","severity":"error"}],"warnings":[],"quick_fixes":[{"node_id":"orphan_node","patch_type":"instructions","patch":"Connect this node to the main flow or remove it"},{"node_id":"dead_end","patch_type":"instructions","patch":"Add an outgoing edge or change type to End"},{"node_id":"orphan_node","patch_type":"instructions","patch":"Add an outgoing edge or change type to End"},{"node_id":"ask_account","patch_type":"instructions","patch":"Add an edge with is_fallback=true or label='timeout'"},{"node_id":"check_balance","patch_type":"instructions","patch":"Define 'account_num' in a SetVar or Input node"}],"flow_meta":{"nodes":6,"edges":4,"unreachable_count":1,"dead_end_count":2,"created_at":"2025-12-14T21:34:56.663902"}},"ui":{"type":"text/html+skybridge","html":"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>IVR Flow Linter</title>\n <style>\n :root {\n --bg-color: #ffffff;\n --text-color: #333333;\n --border-color: #e0e0e0;\n --primary-color: #007aff;\n --danger-color: #ff3b30;\n --warning-color: #ffcc00;\n --success-color: #34c759;\n --code-bg: #f5f5f5;\n }\n body {\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n margin: 0;\n padding: 0;\n background: var(--bg-color);\n color: var(--text-color);\n height: 100vh;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n .header {\n padding: 12px 16px;\n border-bottom: 1px solid var(--border-color);\n display: flex;\n align-items: center;\n justify-content: space-between;\n background: #fafafa;\n }\n .title { font-weight: 600; font-size: 16px; }\n .badge-group { display: flex; gap: 8px; }\n .badge {\n padding: 4px 8px;\n border-radius: 12px;\n font-size: 12px;\n font-weight: 500;\n display: flex;\n align-items: center;\n gap: 4px;\n }\n .score { background: #eee; border: 1px solid #ccc; }\n .score.good { background: #d4f7dc; color: #006400; border-color: #34c759; }\n .score.ok { background: #fffbe6; color: #856404; border-color: #ffcc00; }\n .score.bad { background: #ffeef0; color: #721c24; border-color: #f5c6cb; }\n \n .errors { background: #ffeef0; color: #cc0000; }\n .warnings { background: #fffbe6; color: #856404; }\n \n .container { display: flex; flex: 1; overflow: hidden; height: 100%; }\n \n .sidebar {\n width: 300px;\n border-right: 1px solid var(--border-color);\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n }\n .sidebar-header {\n padding: 8px 12px;\n background: #f0f0f0;\n font-size: 12px;\n font-weight: 600;\n color: #666;\n text-transform: uppercase;\n }\n .node-item {\n padding: 10px 12px;\n border-bottom: 1px solid #f0f0f0;\n cursor: pointer;\n transition: background 0.1s;\n }\n .node-item:hover { background: #f9f9f9; }\n .node-item.selected { background: #e6f2ff; border-left: 3px solid var(--primary-color); }\n .node-item.has-error { border-left-color: var(--danger-color); }\n .node-id { font-weight: 500; font-size: 14px; margin-bottom: 2px; display: flex; align-items: center; justify-content: space-between; }\n .node-type { font-size: 11px; color: #888; background: #eee; padding: 2px 4px; border-radius: 4px; }\n .issue-indicator { font-size: 12px; margin-top: 4px; display: flex; gap: 6px; }\n \n .main-panel {\n flex: 1;\n padding: 20px;\n overflow-y: auto;\n }\n .detail-card {\n border: 1px solid var(--border-color);\n border-radius: 8px;\n padding: 16px;\n margin-bottom: 16px;\n }\n .detail-header { border-bottom: 1px solid #eee; padding-bottom: 8px; margin-bottom: 12px; font-weight: 600; font-size: 15px; }\n .finding {\n background: #fafafa;\n border-left: 3px solid #ccc;\n padding: 10px 12px;\n margin-bottom: 8px;\n font-size: 14px;\n }\n .finding.error { border-left-color: var(--danger-color); background: #fff5f5; }\n .finding.warning { border-left-color: var(--warning-color); background: #fffbeb; }\n .finding-code { font-family: monospace; font-size: 11px; color: #666; display: block; margin-bottom: 4px; }\n .finding-msg { margin-bottom: 6px; display: block; }\n .finding-fix { font-size: 13px; color: #006400; font-style: italic; }\n \n .empty-state { text-align: center; color: #999; margin-top: 40px; }\n \n .footer {\n padding: 12px;\n border-top: 1px solid var(--border-color);\n background: #fafafa;\n text-align: right;\n }\n button {\n background: var(--primary-color);\n color: white;\n border: none;\n padding: 8px 16px;\n border-radius: 6px;\n cursor: pointer;\n font-size: 13px;\n }\n button:hover { background: #0056b3; }\n\n @media (max-width: 600px) {\n .container { flex-direction: column; }\n .sidebar { width: 100%; height: 40%; }\n .main-panel { height: 60%; }\n }\n </style>\n</head>\n<body>\n <div class=\"header\">\n <div class=\"title\">IVR Linter</div>\n <div class=\"badge-group\">\n <div id=\"score-badge\" class=\"badge score\">Score: <span id=\"score-val\">-</span></div>\n <div class=\"badge errors\">🔴 <span id=\"error-count\">0</span></div>\n <div class=\"badge warnings\">⚠️ <span id=\"warn-count\">0</span></div>\n </div>\n </div>\n \n <div class=\"container\">\n <div class=\"sidebar\">\n <div class=\"sidebar-header\">Nodes with Issues</div>\n <div id=\"node-list\"></div>\n </div>\n <div class=\"main-panel\">\n <div id=\"detail-view\">\n <div class=\"empty-state\">Select a node to view details</div>\n </div>\n </div>\n </div>\n \n <div class=\"footer\">\n <button id=\"export-btn\">📋 Export Markdown</button>\n </div>\n\n <!-- INJECTED DATA -->\n <script>\n window.__IVR_LINT_RESULT__ = {\"schema_version\":\"0.1\",\"flow_hash\":\"38ef6234d0845f397b1e6069647a9a98d08ad45df9620c0af4b1f29ad766b207\",\"score\":25,\"errors\":[{\"code\":\"unreachable_node\",\"node_id\":\"orphan_node\",\"message\":\"Node 'orphan_node' is not reachable from Start\",\"fix\":\"Connect this node to the main flow or remove it\",\"severity\":\"error\"},{\"code\":\"dead_end_not_end\",\"node_id\":\"dead_end\",\"message\":\"Node 'dead_end' (Speak) has no outgoing edges but is not an End node\",\"fix\":\"Add an outgoing edge or change type to End\",\"severity\":\"error\"},{\"code\":\"dead_end_not_end\",\"node_id\":\"orphan_node\",\"message\":\"Node 'orphan_node' (Speak) has no outgoing edges but is not an End node\",\"fix\":\"Add an outgoing edge or change type to End\",\"severity\":\"error\"},{\"code\":\"missing_fallback_on_ask\",\"node_id\":\"ask_account\",\"message\":\"Ask node 'ask_account' missing fallback (timeout/no-match) path\",\"fix\":\"Add an edge with is_fallback=true or label='timeout'\",\"severity\":\"error\"},{\"code\":\"var_used_not_defined\",\"node_id\":\"check_balance\",\"message\":\"Variable 'account_num' used in 'check_balance' is never defined in flow\",\"fix\":\"Define 'account_num' in a SetVar or Input node\",\"severity\":\"error\"}],\"warnings\":[],\"quick_fixes\":[{\"node_id\":\"orphan_node\",\"patch_type\":\"instructions\",\"patch\":\"Connect this node to the main flow or remove it\"},{\"node_id\":\"dead_end\",\"patch_type\":\"instructions\",\"patch\":\"Add an outgoing edge or change type to End\"},{\"node_id\":\"orphan_node\",\"patch_type\":\"instructions\",\"patch\":\"Add an outgoing edge or change type to End\"},{\"node_id\":\"ask_account\",\"patch_type\":\"instructions\",\"patch\":\"Add an edge with is_fallback=true or label='timeout'\"},{\"node_id\":\"check_balance\",\"patch_type\":\"instructions\",\"patch\":\"Define 'account_num' in a SetVar or Input node\"}],\"flow_meta\":{\"nodes\":6,\"edges\":4,\"unreachable_count\":1,\"dead_end_count\":2,\"created_at\":\"2025-12-14T21:34:56.663902\"}};\n </script>\n \n <script>\n (function() {\n const data = window.__IVR_LINT_RESULT__;\n const scoreVal = document.getElementById('score-val');\n const scoreBadge = document.getElementById('score-badge');\n \n // Header Stats\n scoreVal.textContent = data.score;\n document.getElementById('error-count').textContent = data.errors.length;\n document.getElementById('warn-count').textContent = data.warnings.length;\n \n if(data.score >= 90) scoreBadge.classList.add('good');\n else if(data.score >= 50) scoreBadge.classList.add('ok');\n else scoreBadge.classList.add('bad');\n \n // Group findings by node\n const nodesMap = {};\n [...data.errors, ...data.warnings].forEach(f => {\n const nid = f.node_id || '_global_';\n if(!nodesMap[nid]) nodesMap[nid] = { errors: [], warnings: [] };\n if(f.severity === 'error') nodesMap[nid].errors.push(f);\n else nodesMap[nid].warnings.push(f);\n });\n \n // Render List\n const listContainer = document.getElementById('node-list');\n const sortedNodes = Object.keys(nodesMap).sort();\n \n if (sortedNodes.length === 0) {\n listContainer.innerHTML = '<div style=\"padding:20px; color:#888; text-align:center\">No issues found! 🎉</div>';\n document.getElementById('detail-view').innerHTML = '<div class=\"empty-state\">Great job! This flow looks clean.</div>';\n }\n\n sortedNodes.forEach(nid => {\n const item = document.createElement('div');\n item.className = 'node-item';\n const issueCount = nodesMap[nid].errors.length + nodesMap[nid].warnings.length;\n const hasErr = nodesMap[nid].errors.length > 0;\n \n if(hasErr) item.classList.add('has-error');\n \n item.innerHTML = `\n <div class=\"node-id\">\n ${nid === '_global_' ? 'Global Issues' : nid}\n </div>\n <div class=\"issue-indicator\">\n ${nodesMap[nid].errors.length > 0 ? `<span style=\"color:#cc0000\">🔴 ${nodesMap[nid].errors.length}</span>` : ''}\n ${nodesMap[nid].warnings.length > 0 ? `<span style=\"color:#856404\">⚠️ ${nodesMap[nid].warnings.length}</span>` : ''}\n </div>\n `;\n \n item.onclick = () => {\n document.querySelectorAll('.node-item').forEach(el => el.classList.remove('selected'));\n item.classList.add('selected');\n renderDetail(nid, nodesMap[nid]);\n };\n \n listContainer.appendChild(item);\n });\n \n function renderDetail(nid, issues) {\n const container = document.getElementById('detail-view');\n let html = `<div class=\"detail-header\">Details for ${nid}</div>`;\n \n if(issues.errors.length > 0) {\n html += `<h4>Errors</h4>`;\n issues.errors.forEach(e => {\n html += `\n <div class=\"finding error\">\n <span class=\"finding-code\">${e.code}</span>\n <span class=\"finding-msg\">${e.message}</span>\n ${e.fix ? `<div class=\"finding-fix\">💡 Fix: ${e.fix}</div>` : ''}\n </div>`;\n });\n }\n \n if(issues.warnings.length > 0) {\n html += `<h4>Warnings</h4>`;\n issues.warnings.forEach(w => {\n html += `\n <div class=\"finding warning\">\n <span class=\"finding-code\">${w.code}</span>\n <span class=\"finding-msg\">${w.message}</span>\n ${w.fix ? `<div class=\"finding-fix\">💡 Fix: ${w.fix}</div>` : ''}\n </div>`;\n });\n }\n \n container.innerHTML = html;\n }\n \n // Export Markdown\n document.getElementById('export-btn').onclick = () => {\n let md = `# IVR Flow Analysis\n\n`;\n md += `- Score: **${data.score}**\n`;\n md += `- Errors: ${data.errors.length}\n`;\n md += `- Warnings: ${data.warnings.length}\n\n`;\n \n md += `## Findings\n\n`;\n const all = [...data.errors, ...data.warnings];\n if(all.length === 0) md += \"No issues found.\n\";\n else {\n all.forEach(f => {\n md += `- **[${f.severity.toUpperCase()}]** ${f.node_id}: ${f.message}\n`;\n if(f.fix) md += ` - Suggestion: ${f.fix}\n`;\n });\n }\n \n navigator.clipboard.writeText(md).then(() => {\n const btn = document.getElementById('export-btn');\n const originalText = btn.textContent;\n btn.textContent = '✅ Copied!';\n setTimeout(() => btn.textContent = originalText, 2000);\n }).catch(err => {\n alert(\"Failed to copy to clipboard\");\n console.error(err);\n });\n };\n \n })();\n </script>\n</body>\n</html>"}},"error":null,"id":1}