UNPKG

@nicolasmirson/plan-mode-claude

Version:

Structured planning workflow for AI assistants with MCP integration

233 lines 10.6 kB
import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from '@modelcontextprotocol/sdk/types.js'; import { PlanManager } from './PlanManager.js'; export class PlanModeMCPServer { constructor() { this.server = new Server({ name: 'plan-mode-server', version: '1.0.0', capabilities: { tools: {}, }, }); this.planManager = new PlanManager(); this.setupTools(); this.setupApprovalCallback(); } setupTools() { this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: 'enter_plan_mode', description: 'Enter plan mode to start planning a task', inputSchema: { type: 'object', properties: {}, required: [], }, }, { name: 'create_plan', description: 'Create a new plan with title and description', inputSchema: { type: 'object', properties: { title: { type: 'string', description: 'The title of the plan', }, description: { type: 'string', description: 'The description of what the plan will accomplish', }, }, required: ['title', 'description'], }, }, { name: 'add_plan_step', description: 'Add a step to the current plan', inputSchema: { type: 'object', properties: { content: { type: 'string', description: 'The description of the step', }, priority: { type: 'string', enum: ['high', 'medium', 'low'], description: 'The priority of the step', default: 'medium', }, dependencies: { type: 'array', items: { type: 'string', }, description: 'Array of step IDs that this step depends on', }, }, required: ['content'], }, }, { name: 'exit_plan_mode', description: 'Exit plan mode and present the plan for approval', inputSchema: { type: 'object', properties: { plan: { type: 'string', description: 'The plan in markdown format to present to the user', }, }, required: ['plan'], }, }, { name: 'update_step_status', description: 'Update the status of a plan step', inputSchema: { type: 'object', properties: { stepId: { type: 'string', description: 'The ID of the step to update', }, status: { type: 'string', enum: ['pending', 'in_progress', 'completed'], description: 'The new status of the step', }, }, required: ['stepId', 'status'], }, }, { name: 'get_plan_status', description: 'Get the current plan and its status', inputSchema: { type: 'object', properties: {}, required: [], }, }, ], }; }); this.server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { switch (name) { case 'enter_plan_mode': this.planManager.enterPlanMode(); return { content: [ { type: 'text', text: 'Entered plan mode. You can now create a plan and add steps.', }, ], }; case 'create_plan': const plan = this.planManager.createPlan(args?.title || '', args?.description || ''); return { content: [ { type: 'text', text: `Created plan: ${plan.title} (ID: ${plan.id})`, }, ], }; case 'add_plan_step': const step = this.planManager.addStep(args?.content || '', args?.priority || 'medium', args?.dependencies); return { content: [ { type: 'text', text: `Added step: ${step.content} (ID: ${step.id})`, }, ], }; case 'exit_plan_mode': const response = await this.planManager.exitPlanMode(); return { content: [ { type: 'text', text: response.approved ? 'Plan approved! Ready to execute.' : `Plan needs modifications: ${response.feedback}`, }, ], }; case 'update_step_status': this.planManager.updateStepStatus(args?.stepId || '', args?.status || 'pending'); return { content: [ { type: 'text', text: `Updated step ${args?.stepId} status to ${args?.status}`, }, ], }; case 'get_plan_status': const currentPlan = this.planManager.getCurrentPlan(); const state = this.planManager.getState(); if (!currentPlan) { return { content: [ { type: 'text', text: `Plan mode: ${state.isInPlanMode ? 'active' : 'inactive'}. No current plan.`, }, ], }; } const markdown = this.planManager.planToMarkdown(currentPlan); return { content: [ { type: 'text', text: `Plan mode: ${state.isInPlanMode ? 'active' : 'inactive'}\n\n${markdown}`, }, ], }; default: throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`); } } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'; throw new McpError(ErrorCode.InternalError, errorMessage); } }); } setupApprovalCallback() { // This would typically integrate with the client's approval mechanism // For this example, we'll simulate approval this.planManager.setApprovalCallback(async (request) => { // In a real implementation, this would show the plan to the user // and wait for their response through the MCP client console.log('Plan ready for approval:'); console.log(request.markdown); // For now, auto-approve (in real usage, this would be handled by the client) return { approved: true, }; }); } async start() { const transport = new StdioServerTransport(); await this.server.connect(transport); } } // Start the server if this file is run directly if (import.meta.url === `file://${process.argv[1]}`) { const server = new PlanModeMCPServer(); server.start().catch(console.error); } //# sourceMappingURL=mcpServer.js.map