UNPKG

semantic-prompt-mcp

Version:

MCP server for semantic prompt framework - NLP-inspired adaptive reasoning engine for LLM orchestration

885 lines 42.8 kB
#!/usr/bin/env node import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js"; // Fixed chalk import for ESM import chalk from 'chalk'; import { homedir } from 'os'; import { join, dirname } from 'path'; import { readFileSync, existsSync, readdirSync } from 'fs'; import { fileURLToPath } from 'url'; // Get directory of current module const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); // Load prompts configuration with flexible priority const loadPrompts = () => { let promptFile; // Priority 1: Command line argument (--prompt filename.json or just filename.json) const args = process.argv.slice(2); const promptArgIndex = args.findIndex(arg => arg === '--prompt'); if (promptArgIndex !== -1 && args[promptArgIndex + 1]) { promptFile = args[promptArgIndex + 1]; } else if (args.length > 0 && args[0].endsWith('.json')) { promptFile = args[0]; } // Priority 2: Environment variable if (!promptFile && process.env.CHAIN_OF_THOUGHT_CONFIG) { promptFile = process.env.CHAIN_OF_THOUGHT_CONFIG; } // Priority 3: Default fallback (no custom.json auto-detection) if (!promptFile) { promptFile = 'default.json'; } // Resolve the path - look for prompts in the source directory const promptPath = promptFile.includes('/') || promptFile.includes('\\') ? promptFile : join(__dirname, '..', 'prompts', promptFile); try { return JSON.parse(readFileSync(promptPath, 'utf-8')); } catch (error) { console.error(`Failed to load prompts from ${promptPath}:`, error); // Fallback to hardcoded minimal prompts (provide keys that code expects) return { errors: { invalidThought: "Invalid thought: must be a string", invalidThoughtNumber: "Invalid thoughtNumber: must be a number", invalidTotalThoughts: "Invalid totalThoughts: must be a number", invalidNextThoughtNeeded: "Invalid nextThoughtNeeded: must be a boolean", commandFieldRequired: "commandSelection type \"command\" requires a \"command\" field", reasonFieldRequired: "commandSelection type \"{type}\" requires a \"reason\" field", invalidSelectionType: "Invalid commandSelection type: {type}. Must be \"command\", \"skip\", or \"skip_reason\"", unexpectedThoughtNumber: "Unexpected thought number: {number}.", mandatoryViolation: "Step {step} requires commandSelection", unknownTool: "Unknown tool: {name}" }, messages: { duplicateDocumentRead: "Document already read this session: {command}", serverRunning: "MCP Server running", fatalError: "Fatal error: {error}" }, console: { thoughtPrefix: { default: "Thought", revision: "Revision", branch: "Branch" }, thoughtContext: { revision: " (revising thought {number})", branch: " (from thought {from}, ID: {id})" } }, config: {} }; } }; const prompts = loadPrompts(); class SemanticPromptServer { thoughtHistory = []; branches = {}; disableThoughtLogging; selectedCommand = null; readFiles = new Set(); config; hasCommandBeenSelected = false; // Agents extracted from last-read TOML command extractedAgentsFromToml = []; extractedFromCommand = null; constructor(config) { this.disableThoughtLogging = (process.env.DISABLE_THOUGHT_LOGGING || "").toLowerCase() === "true"; // Default configuration (can be overridden) this.config = { commandPath: config?.commandPath || '', commandExtension: config?.commandExtension || '.md', // Default to .md availableCommands: config?.availableCommands || [], requireSelectionByStep: config?.requireSelectionByStep ?? 2, enableAutoRead: config?.enableAutoRead ?? true, agentsPath: config?.agentsPath || '~/.gemini/agents/', agentsExtension: config?.agentsExtension || '.md', historyMaxLength: typeof config?.historyMaxLength === 'number' ? config?.historyMaxLength : 200, ...config }; } // Extract agents array from TOML content in a permissive way parseAgentsFromToml(tomlContent) { try { const match = tomlContent.match(/\bagents\s*=\s*\[([\s\S]*?)\]/i); if (!match) return []; const inner = match[1]; const items = inner.split(','); const safe = /^[a-z0-9-]+$/; const agents = items .map(s => s.replace(/#.*$/m, '')) // remove comments in the same slice .map(s => s.trim()) .map(s => s.replace(/^['"]/, '').replace(/['"]$/, '')) .map(s => s.toLowerCase()) .filter(Boolean) .filter(s => safe.test(s)); const seen = new Set(); return agents.filter(a => (seen.has(a) ? false : (seen.add(a), true))); } catch { return []; } } expandPath(path) { if (path.startsWith('~/')) { return join(homedir(), path.slice(2)); } return path; } // Resolve available agents from config or by scanning the agents directory resolveAvailableAgents() { const safe = /^[a-z0-9-]+$/; if (this.config.availableAgents && this.config.availableAgents.length > 0) { return this.config.availableAgents .map(a => a.toLowerCase()) .filter(a => safe.test(a)); } try { const base = this.expandPath(this.config.agentsPath || '~/.gemini/agents/'); const ext = (this.config.agentsExtension || '.md').toLowerCase(); const entries = readdirSync(base, { withFileTypes: true }); return entries .filter(e => e.isFile()) .map(e => e.name) .filter(name => name.toLowerCase().endsWith(ext)) .map(name => name.slice(0, name.length - ext.length).toLowerCase()) .filter(a => safe.test(a)); } catch { return []; } } // Simple parameter normalizer for handling various AI input formats normalizeParameters(input) { if (!input || typeof input !== 'object') return input; const result = { ...input }; // Handle XML-like strings in commandSelection/agentSelection ['commandSelection', 'agentSelection'].forEach(key => { if (typeof result[key] === 'string' && result[key].includes('<parameter')) { const parsed = {}; // Extract XML parameters const matches = result[key].matchAll(/name=["']([^"']+)["']>([^<]+)</g); for (const match of matches) { parsed[match[1]] = match[2].trim(); } result[key] = parsed; } else if (typeof result[key] === 'string') { // Try JSON parse try { result[key] = JSON.parse(result[key]); } catch { } } }); // Convert string numbers to numbers ['thoughtNumber', 'totalThoughts', 'revisesThought', 'branchFromThought'].forEach(key => { if (typeof result[key] === 'string') { const num = parseInt(result[key], 10); if (!isNaN(num)) result[key] = num; } }); // Convert string booleans to booleans ['nextThoughtNeeded', 'isRevision', 'needsMoreThoughts'].forEach(key => { if (result[key] === 'true') result[key] = true; else if (result[key] === 'false') result[key] = false; }); return result; } validateThoughtData(input) { // Apply normalization first const normalized = this.normalizeParameters(input); const data = normalized; if (!data.thought || typeof data.thought !== 'string') { throw new Error(prompts.errors.invalidThought); } // Coerce numeric strings to numbers for robustness if (typeof data.thoughtNumber === 'string') { const n = parseInt(data.thoughtNumber, 10); if (!Number.isNaN(n)) data.thoughtNumber = n; } if (!data.thoughtNumber || typeof data.thoughtNumber !== 'number') { throw new Error(prompts.errors.invalidThoughtNumber); } if (typeof data.totalThoughts === 'string') { const n = parseInt(data.totalThoughts, 10); if (!Number.isNaN(n)) data.totalThoughts = n; } if (!data.totalThoughts || typeof data.totalThoughts !== 'number') { throw new Error(prompts.errors.invalidTotalThoughts); } // Fix: Handle various formats of nextThoughtNeeded (Gemini sends different types) if (data.nextThoughtNeeded === undefined || data.nextThoughtNeeded === null) { data.nextThoughtNeeded = true; // Default to true if missing } if (typeof data.nextThoughtNeeded !== 'boolean') { // Try to convert to boolean data.nextThoughtNeeded = Boolean(data.nextThoughtNeeded); } return { thought: data.thought, thoughtNumber: data.thoughtNumber, totalThoughts: data.totalThoughts, nextThoughtNeeded: Boolean(data.nextThoughtNeeded), // Ensure it's always boolean commandSelection: data.commandSelection, agentSelection: data.agentSelection, isRevision: data.isRevision, revisesThought: data.revisesThought, branchFromThought: data.branchFromThought, branchId: data.branchId, needsMoreThoughts: data.needsMoreThoughts, }; } recordThought(thoughtData) { this.thoughtHistory.push(thoughtData); const maxLength = this.config.historyMaxLength ?? 200; if (maxLength > 0 && this.thoughtHistory.length > maxLength) { const overflow = this.thoughtHistory.length - maxLength; this.thoughtHistory.splice(0, overflow); } } formatThought(thoughtData) { const { thoughtNumber, totalThoughts, thought, isRevision, revisesThought, branchFromThought, branchId } = thoughtData; let prefix = ''; let context = ''; if (isRevision) { prefix = chalk.yellow(prompts.console.thoughtPrefix.revision); context = prompts.console.thoughtContext.revision.replace('{number}', String(revisesThought)); } else if (branchFromThought) { prefix = chalk.green(prompts.console.thoughtPrefix.branch); context = prompts.console.thoughtContext.branch .replace('{from}', String(branchFromThought)) .replace('{id}', branchId || ''); } else { prefix = chalk.blue(prompts.console.thoughtPrefix.default); context = ''; } const header = `${prefix} ${thoughtNumber}/${totalThoughts}${context}`; const border = '─'.repeat(Math.max(header.length, thought.length) + 4); return ` ┌${border}┐ │ ${header} │ ├${border}┤ │ ${thought.padEnd(border.length - 2)} │ └${border}┘`; } processCommandSelection(selection) { let documentStatus = null; let documentContent; // Fix: Handle when Claude/Gemini sends command name as type instead of "command" // e.g., type: "cleanup" instead of type: "command", command: "cleanup" if (selection && selection.type) { const rawType = String(selection.type).toLowerCase(); if (rawType !== "command" && rawType !== "skip" && rawType !== "skip_reason") { // If type is actually a command name if (this.config.availableCommands && this.config.availableCommands.includes(rawType)) { selection = { type: "command", command: rawType }; } else if (rawType.includes('command')) { // Handle markup like "<parameter name=\"type\">command" selection.type = 'command'; } } } else if (selection?.command && !selection?.type) { // If type missing but command present, assume command selection.type = 'command'; } switch (selection.type) { case "command": if (!selection.command) { throw new Error(prompts.errors.commandFieldRequired); } // Normalize and validate command name (security) // Remove leading slash if present (AI sometimes adds it by mistake) selection.command = selection.command.toLowerCase().replace(/^\/+/, ''); const safeNamePattern = /^[a-z0-9-]+$/; // Validate command exists in availableCommands list before processing if (this.config.availableCommands && this.config.availableCommands.length > 0) { const commandExists = this.config.availableCommands.includes(selection.command); if (!commandExists) { throw new Error(`Command '${selection.command}' not found. Available commands: ${this.config.availableCommands.join(', ')}`); } } else { // If no allow-list configured, enforce safe pattern to prevent path traversal if (!safeNamePattern.test(selection.command)) { throw new Error(`Invalid command name '${selection.command}'. Use lowercase letters, numbers, and hyphens only.`); } } // Check for duplicate document read (SuperClaude Framework SSOT principle - Single Source of Truth) const commandKey = selection.command.toLowerCase(); if (this.readFiles.has(commandKey)) { // Document already read - return system reminder message documentStatus = "system-reminder"; documentContent = prompts.messages?.duplicateDocumentRead?.replace('{command}', selection.command) || `Document already read this session: ${selection.command}.md\n\nPlease refer to system-reminder content and apply that information to proceed with next step analysis.`; this.selectedCommand = commandKey; this.hasCommandBeenSelected = true; return { documentStatus, documentContent }; } this.selectedCommand = commandKey; this.hasCommandBeenSelected = true; const extension = this.config.commandExtension || '.md'; documentStatus = `${selection.command}${extension}`; // Use configured path and extension (safe join) const basePath = this.expandPath(this.config.commandPath || ''); const expandedFileToRead = join(basePath, `${selection.command}${extension}`); // Try to read the document content directly try { if (existsSync(expandedFileToRead)) { documentContent = readFileSync(expandedFileToRead, 'utf-8'); // Add to read files set (SSOT principle) this.readFiles.add(commandKey); // If TOML, extract agents for Step 3 suggestions const isToml = (this.config.commandExtension || '.md').toLowerCase() === '.toml' || expandedFileToRead.toLowerCase().endsWith('.toml'); if (isToml && typeof documentContent === 'string') { const extracted = this.parseAgentsFromToml(documentContent); this.extractedAgentsFromToml = extracted; this.extractedFromCommand = commandKey; } else { this.extractedAgentsFromToml = []; this.extractedFromCommand = null; } } } catch (error) { console.error(`Failed to read command document ${expandedFileToRead}:`, error); // If file doesn't exist or can't be read, set a fallback message documentContent = `Error: Could not read command document at ${expandedFileToRead}`; } break; case "skip": if (!selection.reason) { throw new Error(prompts.errors.reasonFieldRequired.replace('{type}', 'skip')); } this.selectedCommand = "skip"; this.hasCommandBeenSelected = true; documentStatus = "skip"; documentContent = `Skip reason: ${selection.reason}`; break; case "skip_reason": if (!selection.reason) { throw new Error(prompts.errors.reasonFieldRequired.replace('{type}', 'skip_reason')); } // Extract command from reason if it follows pattern const commandFromReason = selection.reason.match(/(\w+)\.md/); if (commandFromReason) { this.selectedCommand = commandFromReason[1].toLowerCase(); } this.hasCommandBeenSelected = true; documentStatus = "system-reminder"; documentContent = `System reminder: ${selection.reason}`; break; default: throw new Error(prompts.errors.invalidSelectionType.replace('{type}', selection.type)); } return { documentStatus, documentContent }; } processAgentSelection(selection) { let documentStatus = null; let documentContent; switch (selection.type) { case "agents": if (!selection.agents || selection.agents.length === 0) { throw new Error("Agent selection type 'agents' requires an 'agents' array"); } // Validate against available agents and provide guidance on invalid ones { const available = new Set(this.resolveAvailableAgents()); const invalid = selection.agents .map(a => a.toLowerCase()) .filter(a => !available.has(a)); if (invalid.length > 0) { const availableList = Array.from(available).join(', ') || 'N/A'; documentStatus = "agents-invalid"; documentContent = `Invalid agents: ${invalid.join(', ')}\n\nPlease choose within: ${availableList}`; break; } } // If all selected agents already read, consolidate into system-reminder response { const selectedLower = selection.agents.map(a => a.toLowerCase()); const allAlreadyRead = selectedLower.every(a => this.readFiles.has(`agent-${a}`)); if (allAlreadyRead) { documentStatus = "system-reminder"; const selectedList = selection.agents.join(', '); const template = prompts.templates?.agentDuplicateReminder || prompts.messages?.useSystemReminderAgents || `Agent documents already read: ${selectedList}\n\nPlease refer to system-reminder content and apply agent perspectives to proceed.`; documentContent = template .replaceAll('{agents}', selectedList) .replaceAll('{agentList}', selectedList); break; } } documentStatus = "agents-selected"; { // Check if agent documents should be returned if (this.config.returnAgentDocuments === false) { // SuperClaude mode: Task tool command format const agentList = selection.agents.join(', '); if (selection.agents.length === 1) { documentContent = `**Agent Selection Complete**\n\nSelected agent: ${selection.agents[0]}. Use Task tool with ${selection.agents[0]} agent.`; } else { documentContent = `**Agent Selection Complete**\n\nSelected agents: ${agentList}. Use Task tool with appropriate agent based on task needs.`; } // Still mark as read for tracking for (const agent of selection.agents) { const agentKey = agent.toLowerCase(); const safeNamePattern = /^[a-z0-9-]+$/; if (safeNamePattern.test(agentKey)) { this.readFiles.add(`agent-${agentKey}`); } } } else { // SuperGemini mode: Return full agent document contents let agentContents = `**SuperGemini Agent Selection Complete**\n\n`; agentContents += `Selected agents: [${selection.agents.map(a => `"${a}"`).join(', ')}]\n\n`; for (const agent of selection.agents) { const agentKey = agent.toLowerCase(); const safeNamePattern = /^[a-z0-9-]+$/; if (!safeNamePattern.test(agentKey)) { continue; } if (this.readFiles.has(`agent-${agentKey}`)) { agentContents += `\n### ${agent}.md (already read)\nRefer to system-reminder content for this agent.\n`; continue; } const agentsBase = this.expandPath(this.config.agentsPath || '~/.gemini/agents/'); const agentsExt = this.config.agentsExtension || '.md'; const agentPath = join(agentsBase, `${agent}${agentsExt}`); try { if (existsSync(agentPath)) { const content = readFileSync(agentPath, 'utf-8'); agentContents += `\n### ${agent}.md\n${content}\n`; this.readFiles.add(`agent-${agentKey}`); } else { agentContents += `\n### ${agent}.md\nError: Agent file not found at ${agentPath}\n`; } } catch (error) { agentContents += `\n### ${agent}.md\nError: Could not read agent file - ${error}\n`; } } documentContent = agentContents; } } break; case "skip": if (!selection.reason) { throw new Error("Agent selection type 'skip' requires a 'reason' field"); } documentStatus = "skip"; documentContent = `Skip reason: ${selection.reason}`; break; case "skip_reason": if (!selection.reason) { throw new Error("Agent selection type 'skip_reason' requires a 'reason' field"); } documentStatus = "system-reminder"; documentContent = `System reminder: ${selection.reason}`; break; default: throw new Error(`Invalid agent selection type: ${selection.type}`); } return { documentStatus, documentContent }; } processStep1(thoughtData) { // STEP 1: Focus only on understanding user request // Optional command selection processing (not mandatory) let documentStatus = null; let documentContent; // Optional commandSelection processing for Step 1 if (thoughtData.commandSelection) { const result = this.processCommandSelection(thoughtData.commandSelection); documentStatus = result.documentStatus; documentContent = result.documentContent; } this.recordThought(thoughtData); if (!this.disableThoughtLogging) { const formattedThought = this.formatThought(thoughtData); console.error(formattedThought); } const response = { thoughtNumber: 1, totalThoughts: thoughtData.totalThoughts, nextThoughtNeeded: thoughtData.nextThoughtNeeded, documentStatus }; // Add document content to response if available if (documentContent) { response.documentContent = documentContent; } const contents = [{ type: "text", text: JSON.stringify(response, null, 2) }]; return { content: contents }; } validateAndProcessStep(thoughtData, stepNumber) { let documentStatus = null; let documentContent; let forceNextThought = false; // Check if command selection is needed at this step const needsCommand = stepNumber >= this.config.requireSelectionByStep && !this.hasCommandBeenSelected; if (needsCommand && !thoughtData.commandSelection) { const availableCommands = this.config.availableCommands?.join(', ') || 'No commands configured'; // Provide clearer error message with example const errorMessage = prompts.errors.mandatoryViolation .replace('{step}', String(stepNumber)) .replace('{commands}', availableCommands) .replace('{path}', this.config.commandPath); // Add example to help AI understand the format const exampleCommand = (this.config.availableCommands && this.config.availableCommands.length > 0) ? this.config.availableCommands[0] : 'command-name'; const examplePayload = { commandSelection: { type: 'command', command: exampleCommand } }; throw new Error(errorMessage + '\n\nExample:\n' + JSON.stringify(examplePayload)); } // Process commandSelection if provided if (thoughtData.commandSelection) { const result = this.processCommandSelection(thoughtData.commandSelection); documentStatus = result.documentStatus; documentContent = result.documentContent; } this.recordThought(thoughtData); if (!this.disableThoughtLogging) { const formattedThought = this.formatThought(thoughtData); console.error(formattedThought); } const response = { thoughtNumber: stepNumber, totalThoughts: thoughtData.totalThoughts, nextThoughtNeeded: forceNextThought ? true : thoughtData.nextThoughtNeeded, documentStatus: documentStatus || null }; // Add document content to response if available if (documentContent) { response.documentContent = documentContent; } const contents = [{ type: "text", text: JSON.stringify(response, null, 2) }]; return { content: contents }; } processStep2(thoughtData) { return this.validateAndProcessStep(thoughtData, 2); } processStep3(thoughtData) { // STEP 3: Agent Persona Selection & Reading (same pattern as step 2) let documentStatus = null; let documentContent; let forceNextThought = false; let suggestedAgents; let availableAgentsForGuidance; let extractedFromCommandForGuidance; // Process agent selection based on configuration if (!this.config.step3RequiresAgentSelection) { // Agent selection not required - just continue if (thoughtData.agentSelection) { const result = this.processAgentSelection(thoughtData.agentSelection); documentStatus = result.documentStatus; documentContent = result.documentContent; } else { documentStatus = 'step3_complete'; documentContent = ''; } } else if (thoughtData.agentSelection) { // Agent selection provided - process it const result = this.processAgentSelection(thoughtData.agentSelection); documentStatus = result.documentStatus; documentContent = result.documentContent; if (documentStatus === 'agents-invalid') { forceNextThought = true; availableAgentsForGuidance = this.resolveAvailableAgents(); } } else { // Agent selection required but not provided if (this.extractedAgentsFromToml?.length > 0) { // Suggest agents from TOML const from = this.extractedFromCommand || this.selectedCommand || 'unknown'; const list = this.extractedAgentsFromToml.join(', '); documentStatus = "agents-suggested"; const extractedMsg = prompts.messages?.agentExtracted || `Agents extracted from {command}.toml: {agents}`; documentContent = extractedMsg.replace('{command}', from).replace('{agents}', list) + '\n\nPlease select agent personas at Step 3 by sending agentSelection { type: "agents", agents: [...] }.'; forceNextThought = true; suggestedAgents = [...this.extractedAgentsFromToml]; extractedFromCommandForGuidance = from; } else { // No agents found - show available agents const availableList = this.config.availableAgents?.join(', ') || 'N/A'; documentStatus = "agents-needed"; const msg = prompts.errors?.missingAgents || `No agents found in Step 3. Available agents: {availableAgents}`; documentContent = msg.replace('{availableAgents}', availableList); forceNextThought = true; availableAgentsForGuidance = this.resolveAvailableAgents(); } } this.thoughtHistory.push(thoughtData); if (!this.disableThoughtLogging) { const formattedThought = this.formatThought(thoughtData); console.error(formattedThought); } const response = { thoughtNumber: 3, totalThoughts: thoughtData.totalThoughts, nextThoughtNeeded: forceNextThought ? true : thoughtData.nextThoughtNeeded, documentStatus }; // Add document content to response if available if (documentContent) { response.documentContent = documentContent; } // Add machine-friendly guidance to help clients proceed reliably if (suggestedAgents && suggestedAgents.length > 0) { response.suggestedAgents = suggestedAgents; } if (availableAgentsForGuidance && availableAgentsForGuidance.length > 0) { response.availableAgents = availableAgentsForGuidance; } if (extractedFromCommandForGuidance) { response.extractedFromCommand = extractedFromCommandForGuidance; } if (forceNextThought) { response.requiredAction = 'select_agents'; } const contents = [{ type: "text", text: JSON.stringify(response, null, 2) }]; return { content: contents }; } processThought(input) { try { const validatedInput = this.validateThoughtData(input); // Respect client-provided totalThoughts (no forced override) // STEP 1: User Input Analysis Only (no command selection yet) if (validatedInput.thoughtNumber === 1) { return this.processStep1(validatedInput); } // STEP 2: MANDATORY Command/Document Selection if (validatedInput.thoughtNumber === 2) { return this.processStep2(validatedInput); } // STEP 3: Agent Persona Extraction & Reading if (validatedInput.thoughtNumber === 3) { return this.processStep3(validatedInput); } // STEP 4: Agent Embodiment & Problem Solving Execution if (validatedInput.thoughtNumber === 4) { return this.validateAndProcessStep(validatedInput, 4); } // Handle thoughts beyond 4 (expansion if needed) if (validatedInput.thoughtNumber > 4) { if (validatedInput.thoughtNumber >= validatedInput.totalThoughts && validatedInput.nextThoughtNeeded) { validatedInput.totalThoughts = validatedInput.thoughtNumber + 1; } return this.validateAndProcessStep(validatedInput, validatedInput.thoughtNumber); } // Fallback for unexpected thought numbers throw new Error(prompts.errors.unexpectedThoughtNumber.replace('{number}', String(validatedInput.thoughtNumber))); } catch (error) { // Simple error handling - just restart the same step const inputData = this.extractInputData(input); return { content: [{ type: "text", text: JSON.stringify({ error: error instanceof Error ? error.message : String(error), status: 'failed', availableCommands: this.config.availableCommands || [], commandsDefinedIn: this.config.commandPath, thoughtNumber: inputData.thoughtNumber, totalThoughts: inputData.totalThoughts, nextThoughtNeeded: true }, null, 2) }], isError: true }; } } extractInputData(input) { try { const inputData = input; return { thoughtNumber: inputData?.thoughtNumber || 1, totalThoughts: inputData?.totalThoughts || 4 }; } catch { return { thoughtNumber: 1, totalThoughts: 4 }; } } } const SUPERCLAUDE_THINKING_TOOL = { name: prompts.tool?.name || 'chain_of_thought', description: prompts.tool?.description || 'Dynamic thinking tool', inputSchema: { type: "object", properties: { thought: { type: "string", description: prompts.inputSchema?.thought?.description || 'Your current thinking step and analysis' }, nextThoughtNeeded: { type: "boolean", description: prompts.inputSchema?.nextThoughtNeeded?.description || 'Whether another thought step is needed' }, thoughtNumber: { type: "integer", description: prompts.inputSchema?.thoughtNumber?.description || 'Current step number', minimum: 1 }, totalThoughts: { type: "integer", description: prompts.inputSchema?.totalThoughts?.description || 'Estimated total steps', minimum: 1 }, commandSelection: { type: "object", description: prompts.inputSchema?.commandSelection?.description || 'Optional command or document selection at any step', properties: { type: { type: "string", enum: ["command", "skip", "skip_reason"], description: prompts.inputSchema?.commandSelection?.type?.description || 'Type of selection' }, command: { type: "string", description: prompts.inputSchema?.commandSelection?.command?.description || 'Command name' }, reason: { type: "string", description: prompts.inputSchema?.commandSelection?.reason?.description || 'Skip reason' } }, required: ["type"] }, agentSelection: { type: "object", description: "Agent selection for SuperGemini Step 3: Agent Persona Selection & Reading", properties: { type: { type: "string", enum: ["agents", "skip", "skip_reason"], description: "Type of agent selection" }, agents: { type: "array", items: { type: "string" }, description: "Array of agent names to select" }, reason: { type: "string", description: "Skip reason if not selecting agents" } }, required: ["type"] }, isRevision: { type: "boolean", description: prompts.inputSchema?.isRevision?.description || 'Whether this revises previous thinking' }, revisesThought: { type: "integer", description: prompts.inputSchema?.revisesThought?.description || 'Which thought is being reconsidered', minimum: 1 }, branchFromThought: { type: "integer", description: prompts.inputSchema?.branchFromThought?.description || 'Branching point thought number', minimum: 1 }, branchId: { type: "string", description: prompts.inputSchema?.branchId?.description || 'Branch identifier' }, needsMoreThoughts: { type: "boolean", description: prompts.inputSchema?.needsMoreThoughts?.description || 'If more thoughts are needed beyond initial estimate' } }, required: ["thought", "nextThoughtNeeded", "thoughtNumber", "totalThoughts"] } }; const server = new Server({ name: "semantic-prompt", version: "1.0.0", }, { capabilities: { tools: {}, }, }); // Create server with configuration from prompts file or environment const serverConfig = { commandPath: prompts.config?.commandPath || process.env.CHAIN_OF_THOUGHT_COMMAND_PATH || '', commandExtension: prompts.config?.commandExtension || process.env.CHAIN_OF_THOUGHT_EXTENSION || '.md', availableCommands: prompts.config?.availableCommands || (process.env.CHAIN_OF_THOUGHT_COMMANDS ? process.env.CHAIN_OF_THOUGHT_COMMANDS.split(',') : []), requireSelectionByStep: prompts.config?.requireSelectionByStep ?? prompts.config?.requireSelectionFromStep ?? (process.env.CHAIN_OF_THOUGHT_REQUIRE_BY_STEP ? parseInt(process.env.CHAIN_OF_THOUGHT_REQUIRE_BY_STEP, 10) : 2), enableAutoRead: prompts.config?.enableAutoRead ?? (process.env.CHAIN_OF_THOUGHT_AUTO_READ !== 'false'), agentsPath: prompts.config?.agentsPath || process.env.CHAIN_OF_THOUGHT_AGENTS_PATH || '~/.gemini/agents/', agentsExtension: prompts.config?.agentsExtension || process.env.CHAIN_OF_THOUGHT_AGENTS_EXT || '.md', historyMaxLength: typeof prompts.config?.historyMaxLength === 'number' ? prompts.config?.historyMaxLength : (process.env.CHAIN_OF_THOUGHT_HISTORY_MAX ? parseInt(process.env.CHAIN_OF_THOUGHT_HISTORY_MAX, 10) : 200), availableAgents: prompts.config?.availableAgents || (process.env.CHAIN_OF_THOUGHT_AVAILABLE_AGENTS ? process.env.CHAIN_OF_THOUGHT_AVAILABLE_AGENTS.split(',') : undefined), step3RequiresAgentSelection: prompts.config?.step3RequiresAgentSelection ?? true, returnAgentDocuments: prompts.config?.returnAgentDocuments ?? true }; const thinkingServer = new SemanticPromptServer(serverConfig); // Type assertion to bypass TypeScript errors with MCP SDK const mcpServer = server; mcpServer.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [SUPERCLAUDE_THINKING_TOOL], })); mcpServer.setRequestHandler(CallToolRequestSchema, async (request) => { if (request.params.name === prompts.tool.name) { // Normalize parameters before processing const normalizedArgs = thinkingServer.normalizeParameters(request.params.arguments); return thinkingServer.processThought(normalizedArgs); } return { content: [{ type: "text", text: prompts.errors.unknownTool.replace('{name}', request.params.name) }], isError: true }; }); async function runServer() { const transport = new StdioServerTransport(); await mcpServer.connect(transport); console.error(prompts.messages.serverRunning); } runServer().catch((error) => { console.error(prompts.messages.fatalError.replace('{error}', String(error))); process.exit(1); }); //# sourceMappingURL=index.js.map