ken-you-think
Version:
A single-tool MCP that guides coding agents through implementation thinking - TypeScript edition
334 lines (329 loc) • 12.1 kB
JavaScript
#!/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