# Directory Structure
```
├── .gitignore
├── Dockerfile
├── LICENSE
├── package.json
├── pnpm-lock.yaml
├── README.md
├── smithery.yaml
├── software-planning-tool-logo.png
├── src
│ ├── index.ts
│ ├── prompts.ts
│ ├── storage.ts
│ └── types.ts
└── tsconfig.json
```
# Files
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
```
1 | node_modules/
2 | build/
3 | *.log
4 | .env*
```
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # Software Planning Tool 🚀
2 | [](https://smithery.ai/server/@NightTrek/Software-planning-mcp)
3 |
4 | A Model Context Protocol (MCP) server designed to facilitate software development planning through an interactive, structured approach. This tool helps break down complex software projects into manageable tasks, track implementation progress, and maintain detailed development plans.
5 |
6 | <a href="https://glama.ai/mcp/servers/a35c7qc7ie">
7 | <img width="380" height="200" src="https://glama.ai/mcp/servers/a35c7qc7ie/badge" alt="Software Planning Tool MCP server" />
8 | </a>
9 |
10 | ## Features ✨
11 |
12 | - **Interactive Planning Sessions**: Start and manage development planning sessions
13 | - **Todo Management**: Create, update, and track development tasks
14 | - **Complexity Scoring**: Assign complexity scores to tasks for better estimation
15 | - **Code Examples**: Include relevant code snippets in task descriptions
16 | - **Implementation Plans**: Save and manage detailed implementation plans
17 |
18 | ## Installation 🛠️
19 |
20 | ### Installing via Smithery
21 |
22 | To install Software Planning Tool for Claude Desktop automatically via [Smithery](https://smithery.ai/server/@NightTrek/Software-planning-mcp):
23 |
24 | ```bash
25 | npx -y @smithery/cli install @NightTrek/Software-planning-mcp --client claude
26 | ```
27 |
28 | ### Manual Installation
29 | 1. Clone the repository
30 | 2. Install dependencies:
31 | ```bash
32 | pnpm install
33 | ```
34 | 3. Build the project:
35 | ```bash
36 | pnpm run build
37 | ```
38 | 4. Add to your MCP settings configuration (typically located at `~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`):
39 | ```json
40 | {
41 | "mcpServers": {
42 | "software-planning-tool": {
43 | "command": "node",
44 | "args": [
45 | "/path/to/software-planning-tool/build/index.js"
46 | ],
47 | "disabled": false,
48 | "autoApprove": []
49 | }
50 | }
51 | }
52 | ```
53 |
54 | ## Available Tools 🔧
55 |
56 | ### start_planning
57 | Start a new planning session with a specific goal.
58 | ```typescript
59 | {
60 | goal: string // The software development goal to plan
61 | }
62 | ```
63 |
64 | ### add_todo
65 | Add a new todo item to the current plan.
66 | ```typescript
67 | {
68 | title: string, // Title of the todo item
69 | description: string, // Detailed description
70 | complexity: number, // Complexity score (0-10)
71 | codeExample?: string // Optional code example
72 | }
73 | ```
74 |
75 | ### get_todos
76 | Retrieve all todos in the current plan.
77 | ```typescript
78 | // No parameters required
79 | ```
80 |
81 | ### update_todo_status
82 | Update the completion status of a todo item.
83 | ```typescript
84 | {
85 | todoId: string, // ID of the todo item
86 | isComplete: boolean // New completion status
87 | }
88 | ```
89 |
90 | ### save_plan
91 | Save the current implementation plan.
92 | ```typescript
93 | {
94 | plan: string // The implementation plan text
95 | }
96 | ```
97 |
98 | ### remove_todo
99 | Remove a todo item from the current plan.
100 | ```typescript
101 | {
102 | todoId: string // ID of the todo item to remove
103 | }
104 | ```
105 |
106 | ## Example Usage 📝
107 |
108 | Here's a complete example of using the software planning tool:
109 |
110 | 1. Start a planning session:
111 | ```typescript
112 | await client.callTool("software-planning-tool", "start_planning", {
113 | goal: "Create a React-based dashboard application"
114 | });
115 | ```
116 |
117 | 2. Add a todo item:
118 | ```typescript
119 | const todo = await client.callTool("software-planning-tool", "add_todo", {
120 | title: "Set up project structure",
121 | description: "Initialize React project with necessary dependencies",
122 | complexity: 3,
123 | codeExample: `
124 | npx create-react-app dashboard
125 | cd dashboard
126 | npm install @material-ui/core @material-ui/icons
127 | `
128 | });
129 | ```
130 |
131 | 3. Update todo status:
132 | ```typescript
133 | await client.callTool("software-planning-tool", "update_todo_status", {
134 | todoId: todo.id,
135 | isComplete: true
136 | });
137 | ```
138 |
139 | 4. Save the implementation plan:
140 | ```typescript
141 | await client.callTool("software-planning-tool", "save_plan", {
142 | plan: `
143 | # Dashboard Implementation Plan
144 |
145 | ## Phase 1: Setup (Complexity: 3)
146 | - Initialize React project
147 | - Install dependencies
148 | - Set up routing
149 |
150 | ## Phase 2: Core Features (Complexity: 5)
151 | - Implement authentication
152 | - Create dashboard layout
153 | - Add data visualization components
154 | `
155 | });
156 | ```
157 |
158 | ## Development 🔨
159 |
160 | ### Project Structure
161 | ```
162 | software-planning-tool/
163 | ├── src/
164 | │ ├── index.ts # Main server implementation
165 | │ ├── prompts.ts # Planning prompts and templates
166 | │ ├── storage.ts # Data persistence
167 | │ └── types.ts # TypeScript type definitions
168 | ├── build/ # Compiled JavaScript
169 | ├── package.json
170 | └── tsconfig.json
171 | ```
172 |
173 | ### Building
174 | ```bash
175 | pnpm run build
176 | ```
177 |
178 | ### Testing
179 | Test all features using the MCP inspector:
180 | ```bash
181 | pnpm run inspector
182 | ```
183 |
184 | ## License 📄
185 |
186 | MIT
187 |
188 | ---
189 |
190 | Made with ❤️ using the Model Context Protocol
```
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "compilerOptions": {
3 | "target": "ES2020",
4 | "module": "NodeNext",
5 | "moduleResolution": "NodeNext",
6 | "esModuleInterop": true,
7 | "strict": true,
8 | "skipLibCheck": true,
9 | "forceConsistentCasingInFileNames": true,
10 | "outDir": "build"
11 | },
12 | "include": ["src/**/*"],
13 | "exclude": ["node_modules"]
14 | }
15 |
```
--------------------------------------------------------------------------------
/smithery.yaml:
--------------------------------------------------------------------------------
```yaml
1 | # Smithery configuration file: https://smithery.ai/docs/config#smitheryyaml
2 |
3 | startCommand:
4 | type: stdio
5 | configSchema:
6 | # JSON Schema defining the configuration options for the MCP.
7 | type: object
8 | properties: {}
9 | commandFunction:
10 | # A function that produces the CLI command to start the MCP on stdio.
11 | |-
12 | () => ({ command: 'node', args: ['build/index.js'] })
13 |
```
--------------------------------------------------------------------------------
/src/types.ts:
--------------------------------------------------------------------------------
```typescript
1 | export interface Todo {
2 | id: string;
3 | title: string;
4 | description: string;
5 | complexity: number;
6 | codeExample?: string;
7 | isComplete: boolean;
8 | createdAt: string;
9 | updatedAt: string;
10 | }
11 |
12 | export interface Goal {
13 | id: string;
14 | description: string;
15 | createdAt: string;
16 | }
17 |
18 | export interface ImplementationPlan {
19 | goalId: string;
20 | todos: Todo[];
21 | updatedAt: string;
22 | }
23 |
24 | export interface StorageData {
25 | goals: Record<string, Goal>;
26 | plans: Record<string, ImplementationPlan>;
27 | }
28 |
```
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "name": "software-planning-tool",
3 | "version": "0.1.0",
4 | "description": "A Model Context Protocol server",
5 | "private": true,
6 | "type": "module",
7 | "bin": {
8 | "software-planning-tool": "./build/index.js"
9 | },
10 | "files": [
11 | "build"
12 | ],
13 | "scripts": {
14 | "build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
15 | "prepare": "npm run build",
16 | "watch": "tsc --watch",
17 | "inspector": "npx @modelcontextprotocol/inspector build/index.js"
18 | },
19 | "dependencies": {
20 | "@modelcontextprotocol/sdk": "0.6.0"
21 | },
22 | "devDependencies": {
23 | "@types/node": "^20.11.24",
24 | "typescript": "^5.3.3"
25 | }
26 | }
27 |
```
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
```dockerfile
1 | # Generated by https://smithery.ai. See: https://smithery.ai/docs/config#dockerfile
2 | # Use the official Node.js image.
3 | FROM node:18-alpine
4 |
5 | # Set the working directory inside the container.
6 | WORKDIR /app
7 |
8 | # Copy over only package.json and pnpm-lock.yaml to install dependencies.
9 | COPY package.json pnpm-lock.yaml ./
10 |
11 | # Install pnpm globally and then install dependencies.
12 | RUN npm install -g pnpm && pnpm install --frozen-lockfile
13 |
14 | # Copy the rest of the application code.
15 | COPY . .
16 |
17 | # Build the application.
18 | RUN pnpm run build
19 |
20 | # Change the permissions for the build script to make it executable.
21 | RUN chmod +x build/index.js
22 |
23 | # Expose the port the app runs on
24 | EXPOSE 3000
25 |
26 | # Command to run the application
27 | ENTRYPOINT ["node", "build/index.js"]
28 |
```
--------------------------------------------------------------------------------
/src/prompts.ts:
--------------------------------------------------------------------------------
```typescript
1 | export const SEQUENTIAL_THINKING_PROMPT = `You are a senior software architect guiding the development of a software feature through a question-based sequential thinking process. Your role is to:
2 |
3 | 1. UNDERSTAND THE GOAL
4 | - Start by thoroughly understanding the provided goal
5 | - Break down complex requirements into manageable components
6 | - Identify potential challenges and constraints
7 |
8 | 2. ASK STRATEGIC QUESTIONS
9 | Ask focused questions about:
10 | - System architecture and design patterns
11 | - Technical requirements and constraints
12 | - Integration points with existing systems
13 | - Security considerations
14 | - Performance requirements
15 | - Scalability needs
16 | - Data management and storage
17 | - User experience requirements
18 | - Testing strategy
19 | - Deployment considerations
20 |
21 | 3. ANALYZE RESPONSES
22 | - Process user responses to refine understanding
23 | - Identify gaps in information
24 | - Surface potential risks or challenges
25 | - Consider alternative approaches
26 | - Validate assumptions
27 |
28 | 4. DEVELOP THE PLAN
29 | As understanding develops:
30 | - Create detailed, actionable implementation steps
31 | - Include complexity scores (0-10) for each task
32 | - Provide code examples where helpful
33 | - Consider dependencies between tasks
34 | - Break down large tasks into smaller subtasks
35 | - Include testing and validation steps
36 | - Document architectural decisions
37 |
38 | 5. ITERATE AND REFINE
39 | - Continue asking questions until all aspects are clear
40 | - Refine the plan based on new information
41 | - Adjust task breakdown and complexity scores
42 | - Add implementation details as they emerge
43 |
44 | 6. COMPLETION
45 | The process continues until the user indicates they are satisfied with the plan. The final plan should be:
46 | - Comprehensive and actionable
47 | - Well-structured and prioritized
48 | - Clear in its technical requirements
49 | - Specific in its implementation details
50 | - Realistic in its complexity assessments
51 |
52 | GUIDELINES:
53 | - Ask one focused question at a time
54 | - Maintain context from previous responses
55 | - Be specific and technical in questions
56 | - Consider both immediate and long-term implications
57 | - Document key decisions and their rationale
58 | - Include relevant code examples in task descriptions
59 | - Consider security, performance, and maintainability
60 | - Focus on practical, implementable solutions
61 |
62 | Begin by analyzing the provided goal and asking your first strategic question.`;
63 |
64 | export const formatPlanAsTodos = (plan: string): Array<{
65 | title: string;
66 | description: string;
67 | complexity: number;
68 | codeExample?: string;
69 | }> => {
70 | // This is a placeholder implementation
71 | // In a real system, this would use more sophisticated parsing
72 | // to extract todos from the plan text
73 | const todos = plan.split('\n\n')
74 | .filter(section => section.trim().length > 0)
75 | .map(section => {
76 | const lines = section.split('\n');
77 | const title = lines[0].replace(/^[0-9]+\.\s*/, '').trim();
78 | const complexity = parseInt(section.match(/Complexity:\s*([0-9]+)/)?.[1] || '5');
79 | const codeExample = section.match(/\`\`\`[^\`]*\`\`\`/)?.[0];
80 | const description = section
81 | .replace(/^[0-9]+\.\s*[^\n]*\n/, '')
82 | .replace(/Complexity:\s*[0-9]+/, '')
83 | .replace(/\`\`\`[^\`]*\`\`\`/, '')
84 | .trim();
85 |
86 | return {
87 | title,
88 | description,
89 | complexity,
90 | codeExample: codeExample?.replace(/^\`\`\`|\`\`\`$/g, ''),
91 | };
92 | });
93 |
94 | return todos;
95 | };
96 |
```
--------------------------------------------------------------------------------
/src/storage.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { promises as fs } from 'fs';
2 | import path from 'path';
3 | import os from 'os';
4 | import { StorageData, Goal, ImplementationPlan, Todo } from './types.js';
5 |
6 | export class Storage {
7 | private storagePath: string;
8 | private data: StorageData;
9 |
10 | constructor() {
11 | // Store data in user's home directory under .software-planning-tool
12 | const dataDir = path.join(os.homedir(), '.software-planning-tool');
13 | this.storagePath = path.join(dataDir, 'data.json');
14 | this.data = {
15 | goals: {},
16 | plans: {},
17 | };
18 | }
19 |
20 | async initialize(): Promise<void> {
21 | try {
22 | // Create data directory if it doesn't exist
23 | const dataDir = path.dirname(this.storagePath);
24 | await fs.mkdir(dataDir, { recursive: true });
25 |
26 | // Try to read existing data
27 | const data = await fs.readFile(this.storagePath, 'utf-8');
28 | this.data = JSON.parse(data);
29 | } catch (error) {
30 | // If file doesn't exist or can't be read, use default empty data
31 | await this.save();
32 | }
33 | }
34 |
35 | private async save(): Promise<void> {
36 | await fs.writeFile(this.storagePath, JSON.stringify(this.data, null, 2));
37 | }
38 |
39 | async createGoal(description: string): Promise<Goal> {
40 | const goal: Goal = {
41 | id: Date.now().toString(),
42 | description,
43 | createdAt: new Date().toISOString(),
44 | };
45 |
46 | this.data.goals[goal.id] = goal;
47 | await this.save();
48 | return goal;
49 | }
50 |
51 | async getGoal(id: string): Promise<Goal | null> {
52 | return this.data.goals[id] || null;
53 | }
54 |
55 | async createPlan(goalId: string): Promise<ImplementationPlan> {
56 | const plan: ImplementationPlan = {
57 | goalId,
58 | todos: [],
59 | updatedAt: new Date().toISOString(),
60 | };
61 |
62 | this.data.plans[goalId] = plan;
63 | await this.save();
64 | return plan;
65 | }
66 |
67 | async getPlan(goalId: string): Promise<ImplementationPlan | null> {
68 | return this.data.plans[goalId] || null;
69 | }
70 |
71 | async addTodo(
72 | goalId: string,
73 | { title, description, complexity, codeExample }: Omit<Todo, 'id' | 'isComplete' | 'createdAt' | 'updatedAt'>
74 | ): Promise<Todo> {
75 | const plan = await this.getPlan(goalId);
76 | if (!plan) {
77 | throw new Error(`No plan found for goal ${goalId}`);
78 | }
79 |
80 | const todo: Todo = {
81 | id: Date.now().toString(),
82 | title,
83 | description,
84 | complexity,
85 | codeExample,
86 | isComplete: false,
87 | createdAt: new Date().toISOString(),
88 | updatedAt: new Date().toISOString(),
89 | };
90 |
91 | plan.todos.push(todo);
92 | plan.updatedAt = new Date().toISOString();
93 | await this.save();
94 | return todo;
95 | }
96 |
97 | async removeTodo(goalId: string, todoId: string): Promise<void> {
98 | const plan = await this.getPlan(goalId);
99 | if (!plan) {
100 | throw new Error(`No plan found for goal ${goalId}`);
101 | }
102 |
103 | plan.todos = plan.todos.filter((todo: Todo) => todo.id !== todoId);
104 | plan.updatedAt = new Date().toISOString();
105 | await this.save();
106 | }
107 |
108 | async updateTodoStatus(goalId: string, todoId: string, isComplete: boolean): Promise<Todo> {
109 | const plan = await this.getPlan(goalId);
110 | if (!plan) {
111 | throw new Error(`No plan found for goal ${goalId}`);
112 | }
113 |
114 | const todo = plan.todos.find((t: Todo) => t.id === todoId);
115 | if (!todo) {
116 | throw new Error(`No todo found with id ${todoId}`);
117 | }
118 |
119 | todo.isComplete = isComplete;
120 | todo.updatedAt = new Date().toISOString();
121 | plan.updatedAt = new Date().toISOString();
122 | await this.save();
123 | return todo;
124 | }
125 |
126 | async getTodos(goalId: string): Promise<Todo[]> {
127 | const plan = await this.getPlan(goalId);
128 | return plan?.todos || [];
129 | }
130 | }
131 |
132 | export const storage = new Storage();
133 |
```
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | #!/usr/bin/env node
2 | import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3 | import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4 | import {
5 | CallToolRequestSchema,
6 | ErrorCode,
7 | ListResourcesRequestSchema,
8 | ListToolsRequestSchema,
9 | McpError,
10 | ReadResourceRequestSchema,
11 | } from '@modelcontextprotocol/sdk/types.js';
12 | import { storage } from './storage.js';
13 | import { SEQUENTIAL_THINKING_PROMPT, formatPlanAsTodos } from './prompts.js';
14 | import { Goal, Todo } from './types.js';
15 |
16 | class SoftwarePlanningServer {
17 | private server: Server;
18 | private currentGoal: Goal | null = null;
19 |
20 | constructor() {
21 | this.server = new Server(
22 | {
23 | name: 'software-planning-tool',
24 | version: '0.1.0',
25 | },
26 | {
27 | capabilities: {
28 | resources: {},
29 | tools: {},
30 | },
31 | }
32 | );
33 |
34 | this.setupResourceHandlers();
35 | this.setupToolHandlers();
36 |
37 | this.server.onerror = (error) => console.error('[MCP Error]', error);
38 | }
39 |
40 | private setupResourceHandlers() {
41 | this.server.setRequestHandler(ListResourcesRequestSchema, async () => ({
42 | resources: [
43 | {
44 | uri: 'planning://current-goal',
45 | name: 'Current Goal',
46 | description: 'The current software development goal being planned',
47 | mimeType: 'application/json',
48 | },
49 | {
50 | uri: 'planning://implementation-plan',
51 | name: 'Implementation Plan',
52 | description: 'The current implementation plan with todos',
53 | mimeType: 'application/json',
54 | },
55 | ],
56 | }));
57 |
58 | this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
59 | switch (request.params.uri) {
60 | case 'planning://current-goal': {
61 | if (!this.currentGoal) {
62 | throw new McpError(
63 | ErrorCode.InvalidParams,
64 | 'No active goal. Start a new planning session first.'
65 | );
66 | }
67 | return {
68 | contents: [
69 | {
70 | uri: request.params.uri,
71 | mimeType: 'application/json',
72 | text: JSON.stringify(this.currentGoal, null, 2),
73 | },
74 | ],
75 | };
76 | }
77 | case 'planning://implementation-plan': {
78 | if (!this.currentGoal) {
79 | throw new McpError(
80 | ErrorCode.InvalidParams,
81 | 'No active goal. Start a new planning session first.'
82 | );
83 | }
84 | const plan = await storage.getPlan(this.currentGoal.id);
85 | if (!plan) {
86 | throw new McpError(
87 | ErrorCode.InvalidParams,
88 | 'No implementation plan found for current goal.'
89 | );
90 | }
91 | return {
92 | contents: [
93 | {
94 | uri: request.params.uri,
95 | mimeType: 'application/json',
96 | text: JSON.stringify(plan, null, 2),
97 | },
98 | ],
99 | };
100 | }
101 | default:
102 | throw new McpError(
103 | ErrorCode.InvalidRequest,
104 | `Unknown resource URI: ${request.params.uri}`
105 | );
106 | }
107 | });
108 | }
109 |
110 | private setupToolHandlers() {
111 | this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
112 | tools: [
113 | {
114 | name: 'start_planning',
115 | description: 'Start a new planning session with a goal',
116 | inputSchema: {
117 | type: 'object',
118 | properties: {
119 | goal: {
120 | type: 'string',
121 | description: 'The software development goal to plan',
122 | },
123 | },
124 | required: ['goal'],
125 | },
126 | },
127 | {
128 | name: 'save_plan',
129 | description: 'Save the current implementation plan',
130 | inputSchema: {
131 | type: 'object',
132 | properties: {
133 | plan: {
134 | type: 'string',
135 | description: 'The implementation plan text to save',
136 | },
137 | },
138 | required: ['plan'],
139 | },
140 | },
141 | {
142 | name: 'add_todo',
143 | description: 'Add a new todo item to the current plan',
144 | inputSchema: {
145 | type: 'object',
146 | properties: {
147 | title: {
148 | type: 'string',
149 | description: 'Title of the todo item',
150 | },
151 | description: {
152 | type: 'string',
153 | description: 'Detailed description of the todo item',
154 | },
155 | complexity: {
156 | type: 'number',
157 | description: 'Complexity score (0-10)',
158 | minimum: 0,
159 | maximum: 10,
160 | },
161 | codeExample: {
162 | type: 'string',
163 | description: 'Optional code example',
164 | },
165 | },
166 | required: ['title', 'description', 'complexity'],
167 | },
168 | },
169 | {
170 | name: 'remove_todo',
171 | description: 'Remove a todo item from the current plan',
172 | inputSchema: {
173 | type: 'object',
174 | properties: {
175 | todoId: {
176 | type: 'string',
177 | description: 'ID of the todo item to remove',
178 | },
179 | },
180 | required: ['todoId'],
181 | },
182 | },
183 | {
184 | name: 'get_todos',
185 | description: 'Get all todos in the current plan',
186 | inputSchema: {
187 | type: 'object',
188 | properties: {},
189 | },
190 | },
191 | {
192 | name: 'update_todo_status',
193 | description: 'Update the completion status of a todo item',
194 | inputSchema: {
195 | type: 'object',
196 | properties: {
197 | todoId: {
198 | type: 'string',
199 | description: 'ID of the todo item',
200 | },
201 | isComplete: {
202 | type: 'boolean',
203 | description: 'New completion status',
204 | },
205 | },
206 | required: ['todoId', 'isComplete'],
207 | },
208 | },
209 | ],
210 | }));
211 |
212 | this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
213 | switch (request.params.name) {
214 | case 'start_planning': {
215 | const { goal } = request.params.arguments as { goal: string };
216 | this.currentGoal = await storage.createGoal(goal);
217 | await storage.createPlan(this.currentGoal.id);
218 |
219 | return {
220 | content: [
221 | {
222 | type: 'text',
223 | text: SEQUENTIAL_THINKING_PROMPT,
224 | },
225 | ],
226 | };
227 | }
228 |
229 | case 'save_plan': {
230 | if (!this.currentGoal) {
231 | throw new McpError(
232 | ErrorCode.InvalidRequest,
233 | 'No active goal. Start a new planning session first.'
234 | );
235 | }
236 |
237 | const { plan } = request.params.arguments as { plan: string };
238 | const todos = formatPlanAsTodos(plan);
239 |
240 | for (const todo of todos) {
241 | await storage.addTodo(this.currentGoal.id, todo);
242 | }
243 |
244 | return {
245 | content: [
246 | {
247 | type: 'text',
248 | text: `Successfully saved ${todos.length} todo items to the implementation plan.`,
249 | },
250 | ],
251 | };
252 | }
253 |
254 | case 'add_todo': {
255 | if (!this.currentGoal) {
256 | throw new McpError(
257 | ErrorCode.InvalidRequest,
258 | 'No active goal. Start a new planning session first.'
259 | );
260 | }
261 |
262 | const todo = request.params.arguments as Omit<
263 | Todo,
264 | 'id' | 'isComplete' | 'createdAt' | 'updatedAt'
265 | >;
266 | const newTodo = await storage.addTodo(this.currentGoal.id, todo);
267 |
268 | return {
269 | content: [
270 | {
271 | type: 'text',
272 | text: JSON.stringify(newTodo, null, 2),
273 | },
274 | ],
275 | };
276 | }
277 |
278 | case 'remove_todo': {
279 | if (!this.currentGoal) {
280 | throw new McpError(
281 | ErrorCode.InvalidRequest,
282 | 'No active goal. Start a new planning session first.'
283 | );
284 | }
285 |
286 | const { todoId } = request.params.arguments as { todoId: string };
287 | await storage.removeTodo(this.currentGoal.id, todoId);
288 |
289 | return {
290 | content: [
291 | {
292 | type: 'text',
293 | text: `Successfully removed todo ${todoId}`,
294 | },
295 | ],
296 | };
297 | }
298 |
299 | case 'get_todos': {
300 | if (!this.currentGoal) {
301 | throw new McpError(
302 | ErrorCode.InvalidRequest,
303 | 'No active goal. Start a new planning session first.'
304 | );
305 | }
306 |
307 | const todos = await storage.getTodos(this.currentGoal.id);
308 |
309 | return {
310 | content: [
311 | {
312 | type: 'text',
313 | text: JSON.stringify(todos, null, 2),
314 | },
315 | ],
316 | };
317 | }
318 |
319 | case 'update_todo_status': {
320 | if (!this.currentGoal) {
321 | throw new McpError(
322 | ErrorCode.InvalidRequest,
323 | 'No active goal. Start a new planning session first.'
324 | );
325 | }
326 |
327 | const { todoId, isComplete } = request.params.arguments as {
328 | todoId: string;
329 | isComplete: boolean;
330 | };
331 | const updatedTodo = await storage.updateTodoStatus(
332 | this.currentGoal.id,
333 | todoId,
334 | isComplete
335 | );
336 |
337 | return {
338 | content: [
339 | {
340 | type: 'text',
341 | text: JSON.stringify(updatedTodo, null, 2),
342 | },
343 | ],
344 | };
345 | }
346 |
347 | default:
348 | throw new McpError(
349 | ErrorCode.MethodNotFound,
350 | `Unknown tool: ${request.params.name}`
351 | );
352 | }
353 | });
354 | }
355 |
356 | async run() {
357 | await storage.initialize();
358 | const transport = new StdioServerTransport();
359 | await this.server.connect(transport);
360 | console.error('Software Planning MCP server running on stdio');
361 | }
362 | }
363 |
364 | const server = new SoftwarePlanningServer();
365 | server.run().catch(console.error);
366 |
```