UNPKG

ken-you-think

Version:

A single-tool MCP that guides coding agents through implementation thinking - TypeScript edition

334 lines (329 loc) 12.1 kB
#!/usr/bin/env node /** * Ken-You-Think MCP Server - TypeScript Edition * * A single-tool MCP that guides coding agents through implementation thinking. * No Python, no pip, no permission issues - just pure TypeScript! */ import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, ListToolsRequestSchema, McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js"; // Current session storage let currentSession = null; // Helper functions function getThoughtEmoji(thoughtNumber, totalThoughts, isInvestigating = false) { const progress = thoughtNumber / totalThoughts; if (isInvestigating) return "🔍"; if (progress <= 0.25) return "🤔"; // Early thinking if (progress <= 0.5) return "🔎"; // Exploring if (progress <= 0.75) return "🛠️"; // Building return "✨"; // Finalizing } function generateDynamicGuidance(firstThought, currentThought, thoughtNumber, totalThoughts) { if (thoughtNumber === 1 || thoughtNumber >= totalThoughts) return null; const progress = thoughtNumber / totalThoughts; const currentLower = currentThought.toLowerCase(); if (progress < 0.4) { if (currentLower.includes("need") || currentLower.includes("require")) { return "What already exists that could help with these needs?"; } return "Consider investigating before implementing"; } else if (progress < 0.7) { if (currentLower.includes("found") || currentLower.includes("discovered")) { return "How can these findings shape your approach?"; } if (currentThought.includes("?")) { return "Explore that question before moving forward"; } return "Think about how the pieces will work together"; } else { return "Get specific about implementation details"; } } // Create server instance const server = new Server({ name: "ken-you-think", version: "3.0.0", }, { capabilities: { tools: {}, resources: {}, prompts: {}, }, }); // Handle tool listing server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [ { name: "think", description: "Process a coding implementation thought", inputSchema: { type: "object", properties: { thought: { type: "string", description: "Your current thinking content (should be rich and detailed)" }, thoughtNumber: { type: "number", description: "Current thought number (1, 2, 3...)" }, totalThoughts: { type: "number", description: "Total thoughts planned for this problem" }, investigationArea: { type: "string", description: "What you're investigating (optional)" }, isInvestigating: { type: "boolean", description: "Set to true when researching/investigating" }, branchReason: { type: "string", description: "Reason for branching to alternative approach" }, confidence: { type: "number", description: "Your confidence level (0.0-1.0)", minimum: 0, maximum: 1 } }, required: ["thought", "thoughtNumber", "totalThoughts"] } }, { name: "reset_thinking", description: "Reset the thinking session", inputSchema: { type: "object", properties: {} } } ] })); // Handle tool calls server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; if (name === "think") { const { thought, thoughtNumber, totalThoughts, investigationArea, isInvestigating = false, branchReason, confidence = 1.0 } = args; try { // Initialize session on first thought if (thoughtNumber === 1) { currentSession = { problem: thought.split('.')[0], thoughts: [], startTime: new Date().toISOString(), investigations: [], branches: [] }; } if (!currentSession) { throw new McpError(ErrorCode.InternalError, "No active thinking session. Start with thoughtNumber: 1"); } // Record the thought const thoughtRecord = { number: thoughtNumber, content: thought, confidence, timestamp: new Date().toISOString() }; // Track special states if (isInvestigating && investigationArea) { thoughtRecord.investigation = investigationArea; currentSession.investigations.push({ area: investigationArea, thoughtNumber, findings: thought }); } if (branchReason) { thoughtRecord.branch = branchReason; currentSession.branches.push({ reason: branchReason, thoughtNumber }); } currentSession.thoughts.push(thoughtRecord); // Build response const response = { thoughtNumber, totalThoughts, thoughtHistory: currentSession.thoughts.length }; // Add confidence warning if low if (confidence < 0.8) { response.confidenceWarning = "Low confidence detected - consider investigation"; } // Generate dynamic guidance if (thoughtNumber < totalThoughts && currentSession.thoughts.length > 0) { const guidance = generateDynamicGuidance(currentSession.thoughts[0].content, thought, thoughtNumber, totalThoughts); if (guidance) { response.thinkingGuidance = guidance; } } // Log completion on final thought if (thoughtNumber >= totalThoughts) { console.error(`Thinking complete: ${thoughtNumber} thoughts, confidence: ${confidence.toFixed(2)}`); } return { content: [ { type: "text", text: JSON.stringify(response, null, 2) } ] }; } catch (error) { throw new McpError(ErrorCode.InternalError, `Thinking failed: ${error.message}`); } } if (name === "reset_thinking") { currentSession = null; return { content: [ { type: "text", text: JSON.stringify({ status: "Thinking session reset" }) } ] }; } throw new McpError(ErrorCode.MethodNotFound, `Tool ${name} not found`); }); // Handle resource listing server.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources: [ { uri: "thinking://current", name: "Current thinking session", description: "Get the current thinking session details", mimeType: "application/json" }, { uri: "thinking://history", name: "Thinking history", description: "Get all thoughts from current session", mimeType: "application/json" } ] })); // Handle resource reading server.setRequestHandler(ReadResourceRequestSchema, async (request) => { const { uri } = request.params; if (uri === "thinking://current") { if (!currentSession) { return { contents: [{ uri, mimeType: "application/json", text: JSON.stringify({ status: "No active thinking session" }) }] }; } return { contents: [{ uri, mimeType: "application/json", text: JSON.stringify({ problem: currentSession.problem, thoughtCount: currentSession.thoughts.length, investigations: currentSession.investigations, branches: currentSession.branches, startTime: currentSession.startTime }, null, 2) }] }; } if (uri === "thinking://history") { if (!currentSession || currentSession.thoughts.length === 0) { return { contents: [{ uri, mimeType: "application/json", text: JSON.stringify({ status: "No thoughts recorded" }) }] }; } return { contents: [{ uri, mimeType: "application/json", text: JSON.stringify(currentSession.thoughts, null, 2) }] }; } throw new McpError(ErrorCode.InvalidRequest, `Resource ${uri} not found`); }); // Handle prompt listing server.setRequestHandler(ListPromptsRequestSchema, async () => ({ prompts: [ { name: "coding_think_guide", description: "Guide for using the think tool for coding implementations" } ] })); // Handle prompt retrieval server.setRequestHandler(GetPromptRequestSchema, async (request) => { const { name } = request.params; if (name === "coding_think_guide") { return { prompt: { name: "coding_think_guide", description: "Guide for using the think tool for coding implementations", messages: [ { role: "user", content: { type: "text", text: `Ken-You-Think Coding Guide Use the 'think' tool to work through coding implementations: First Thought (Understanding): - State the problem/request clearly - Identify key requirements - Note what needs investigation Middle Thoughts (Investigation & Design): - Investigate existing patterns (set isInvestigating=true) - Explore technical approaches - Consider trade-offs - Branch if needed (set branchReason) Final Thought (Implementation Plan): - Provide complete implementation details - Include file structures - Specify integration points - List concrete next steps Tips: - Make each thought substantial and detailed - Track confidence throughout - The final thought should be immediately actionable - No separate synthesis needed - the last thought IS your implementation plan` } } ] } }; } throw new McpError(ErrorCode.InvalidRequest, `Prompt ${name} not found`); }); // Start the server async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error("Ken-You-Think MCP server running (TypeScript edition)"); } main().catch((error) => { console.error("Server error:", error); process.exit(1); }); //# sourceMappingURL=index.js.map