Skip to main content
Glama

Decision Tree MCP Server

by psikosen
dt_mcp.js5.13 kB
// dt_mcp.js import { promises as fs } from 'fs'; /** * Asynchronous parser for .rtdq files (v2.1 format). * Throws an error if the file cannot be read or parsed. */ async function parseRTDQFile(filePath) { if (typeof filePath !== 'string' || !filePath.endsWith('.rtdq')) { throw new Error('Invalid file path or extension.'); } const text = await fs.readFile(filePath, 'utf-8'); const data = { rtdq_name: '', description: '', suggested_tools: [], tasks: [], missing_info: [], }; let lines = text.split(/\r?\n/).map(l => l.trim()); let idx = 0; let currentTask = null; let currentNode = null; let inSection = null; // null | 'DESCRIPTION' | 'SUGGESTED_TOOLS' | 'MISSING_INFO' while (idx < lines.length) { const line = lines[idx]; if (line.startsWith('BEGIN_RTDQ:')) { data.rtdq_name = line.replace('BEGIN_RTDQ:', '').trim(); inSection = null; } else if (line.startsWith('END_RTDQ')) { break; } else if (line.startsWith('DESCRIPTION:')) { inSection = 'DESCRIPTION'; // Expecting single quoted line immediately after for simplicity here if (lines[idx + 1]?.startsWith('"') && lines[idx + 1]?.endsWith('"')) { data.description = lines[idx + 1].slice(1, -1); idx++; // Skip description line } } else if (line.startsWith('SUGGESTED_TOOLS:')) { inSection = 'SUGGESTED_TOOLS'; data.suggested_tools = []; } else if (line.startsWith('TASKS:')) { inSection = null; } else if (line.startsWith('MISSING_INFO:')) { inSection = 'MISSING_INFO'; data.missing_info = []; } else if (inSection === 'SUGGESTED_TOOLS' && line.startsWith('- ')) { const toolMatch = line.match(/^\-\s*"(.*)"$/); if (toolMatch) data.suggested_tools.push(toolMatch[1]); } else if (inSection === 'MISSING_INFO' && line.startsWith('- ')) { const missingMatch = line.match(/^\-\s*"(.*)"$/); if (missingMatch) data.missing_info.push(missingMatch[1]); } else if (line === 'BEGIN_TASK') { currentTask = { name: '', purpose: '', nodes: [] }; inSection = null; } else if (line.startsWith('END_TASK') && currentTask) { data.tasks.push(currentTask); currentTask = null; } else if (currentTask && !currentNode && line.startsWith('NAME:')) { currentTask.name = line.replace('NAME:', '').trim(); } else if (currentTask && !currentNode && line.startsWith('PURPOSE:')) { const purposeMatch = line.match(/^PURPOSE:\s*"(.*)"$/); if (purposeMatch) currentTask.purpose = purposeMatch[1]; } else if (currentTask && line === 'BEGIN_DT') { // Context marker } else if (currentTask && line === 'END_DT') { // Context marker } else if (currentTask && line.startsWith('NODE:')) { currentNode = { id: '', prompt: '', children: [] }; } else if (currentNode && line.startsWith('END_NODE')) { if (currentTask) currentTask.nodes.push(currentNode); currentNode = null; } else if (currentNode && line.startsWith('ID:')) { currentNode.id = line.replace('ID:', '').trim(); } else if (currentNode && line.startsWith('PROMPT:')) { const promptMatch = line.match(/^\s*PROMPT:\s*"(.*)"$/); if (promptMatch) currentNode.prompt = promptMatch[1]; } else if (currentNode && line.startsWith('CHILD:')) { idx++; let answer = '', nextID = ''; // Note: A* cost/heuristic parsing could be added here based on Story #11 while (idx < lines.length && !lines[idx].match(/^\s*(CHILD:|END_NODE)/)) { const childLine = lines[idx].trim(); if (childLine.startsWith('ANSWER:')) { const answerMatch = childLine.match(/^ANSWER:\s*"(.*)"$/); if (answerMatch) answer = answerMatch[1]; } else if (childLine.startsWith('NEXT:')) { nextID = childLine.replace('NEXT:', '').trim(); } idx++; } if (answer && nextID) currentNode.children.push({ answer, next: nextID }); idx--; } if (line === 'BEGIN_TASK' || line.startsWith('NODE:')) { inSection = null; } idx++; } if (!data.rtdq_name) { throw new Error('Parsing failed: Missing BEGIN_RTDQ marker or name.'); } return data; } /** * Helper to find a node by ID within a specific task in the parsed data. */ function getNodeById(rtdqData, taskName, nodeId) { if (!rtdqData || !rtdqData.tasks) return null; const task = rtdqData.tasks.find(t => t.name === taskName); if (!task || !task.nodes) return null; return task.nodes.find(n => n.id === nodeId) || null; } export { parseRTDQFile, getNodeById };

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/psikosen/dt_mcp'

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