@programinglive/dev-workflow-mcp-server 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +237 -0
- package/index.js +588 -0
- package/package.json +30 -0
- package/tests/workflowState.test.js +87 -0
package/README.md
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
# Development Workflow MCP Server
|
|
2
|
+
|
|
3
|
+
An MCP (Model Context Protocol) server that helps enforce development discipline and workflow best practices. This server acts as your coding conscience, reminding you to follow proper development workflows.
|
|
4
|
+
|
|
5
|
+
## 🎯 Purpose
|
|
6
|
+
|
|
7
|
+
This MCP server guides you through a disciplined development workflow:
|
|
8
|
+
|
|
9
|
+
1. **Start Conscious** - Be clear about what you're coding
|
|
10
|
+
2. **Fix/Implement** - Write your code
|
|
11
|
+
3. **Create Tests** - Always test your changes
|
|
12
|
+
4. **Run Tests** - Tests must pass (GREEN)
|
|
13
|
+
5. **Document** - Update documentation
|
|
14
|
+
6. **Commit & Push** - Only when all steps are complete
|
|
15
|
+
|
|
16
|
+
## 🚀 Installation
|
|
17
|
+
|
|
18
|
+
### 1. Install Dependencies
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### 2. Configure in Windsurf/Claude Desktop
|
|
25
|
+
|
|
26
|
+
Add to your MCP settings file:
|
|
27
|
+
|
|
28
|
+
**For Windsurf** (`~/Library/Application Support/Windsurf/config.json`):
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"mcpServers": {
|
|
33
|
+
"dev-workflow": {
|
|
34
|
+
"command": "node",
|
|
35
|
+
"args": ["/Users/mahardhika/code/project/mine/app/CascadeProjects/windsurf-project/index.js"]
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**For Claude Desktop** (`~/Library/Application Support/Claude/claude_desktop_config.json`):
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{
|
|
45
|
+
"mcpServers": {
|
|
46
|
+
"dev-workflow": {
|
|
47
|
+
"command": "node",
|
|
48
|
+
"args": ["/Users/mahardhika/code/project/mine/app/CascadeProjects/windsurf-project/index.js"]
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### 3. Restart Windsurf/Claude Desktop
|
|
55
|
+
|
|
56
|
+
After adding the configuration, restart the application to load the MCP server.
|
|
57
|
+
|
|
58
|
+
## 🛠️ Available Tools
|
|
59
|
+
|
|
60
|
+
### `start_task`
|
|
61
|
+
Start a new coding task. This is your first step - be conscious about what you're coding.
|
|
62
|
+
|
|
63
|
+
**Parameters:**
|
|
64
|
+
- `description` (string, required): Clear description of what you're going to code
|
|
65
|
+
- `type` (enum, required): Type of task - "feature", "bugfix", "refactor", or "other"
|
|
66
|
+
|
|
67
|
+
**Example:**
|
|
68
|
+
```
|
|
69
|
+
Use the start_task tool with:
|
|
70
|
+
- description: "Add user authentication to the login page"
|
|
71
|
+
- type: "feature"
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### `mark_bug_fixed`
|
|
75
|
+
Mark that the bug/feature is fixed. **Reminder: Now you MUST create tests!**
|
|
76
|
+
|
|
77
|
+
**Parameters:**
|
|
78
|
+
- `summary` (string, required): Brief summary of what was fixed/implemented
|
|
79
|
+
|
|
80
|
+
### `run_tests`
|
|
81
|
+
Record test results. **NEVER commit if tests fail!** Only proceed if all tests are green.
|
|
82
|
+
|
|
83
|
+
**Parameters:**
|
|
84
|
+
- `passed` (boolean, required): Did all tests pass?
|
|
85
|
+
- `testCommand` (string, required): The test command that was run
|
|
86
|
+
- `details` (string, optional): Test results details
|
|
87
|
+
|
|
88
|
+
**Example:**
|
|
89
|
+
```
|
|
90
|
+
Use run_tests with:
|
|
91
|
+
- passed: true
|
|
92
|
+
- testCommand: "npm test"
|
|
93
|
+
- details: "All 15 tests passed"
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### `create_documentation`
|
|
97
|
+
Mark that documentation has been created/updated. This is required before committing.
|
|
98
|
+
|
|
99
|
+
**Parameters:**
|
|
100
|
+
- `documentationType` (enum, required): "README", "inline-comments", "API-docs", "changelog", or "other"
|
|
101
|
+
- `summary` (string, required): What was documented
|
|
102
|
+
|
|
103
|
+
### `check_ready_to_commit`
|
|
104
|
+
Check if all workflow steps are completed and you're ready to commit & push.
|
|
105
|
+
|
|
106
|
+
### `complete_task`
|
|
107
|
+
Mark the task as complete after successful commit & push. Resets workflow for next task.
|
|
108
|
+
|
|
109
|
+
**Parameters:**
|
|
110
|
+
- `commitMessage` (string, required): The commit message used
|
|
111
|
+
|
|
112
|
+
### `get_workflow_status`
|
|
113
|
+
Get current workflow status and what needs to be done next.
|
|
114
|
+
|
|
115
|
+
### `view_history`
|
|
116
|
+
View workflow history of completed tasks.
|
|
117
|
+
|
|
118
|
+
**Parameters:**
|
|
119
|
+
- `limit` (number, optional): Number of recent tasks to show (default: 10)
|
|
120
|
+
|
|
121
|
+
## 📋 Available Prompts
|
|
122
|
+
|
|
123
|
+
### `workflow_reminder`
|
|
124
|
+
Get a complete reminder of the development workflow discipline.
|
|
125
|
+
|
|
126
|
+
### `pre_commit_checklist`
|
|
127
|
+
Get a pre-commit checklist to ensure nothing is missed before committing.
|
|
128
|
+
|
|
129
|
+
## 🔄 Typical Workflow
|
|
130
|
+
|
|
131
|
+
Here's how you'd use this MCP server in a typical coding session:
|
|
132
|
+
|
|
133
|
+
1. **Start your task:**
|
|
134
|
+
```
|
|
135
|
+
Ask Cascade to use start_task:
|
|
136
|
+
"Start a new task: implementing user profile page, type: feature"
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
2. **Code your feature/fix**
|
|
140
|
+
- Write your code as usual
|
|
141
|
+
|
|
142
|
+
3. **Mark as fixed:**
|
|
143
|
+
```
|
|
144
|
+
"Mark the feature as fixed: User profile page with avatar and bio completed"
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
4. **Create tests:**
|
|
148
|
+
- Write your tests
|
|
149
|
+
- The server will remind you this is mandatory!
|
|
150
|
+
|
|
151
|
+
5. **Run tests:**
|
|
152
|
+
```
|
|
153
|
+
"Record test results: passed=true, command='npm test'"
|
|
154
|
+
```
|
|
155
|
+
- If tests fail, the server will **block** you from proceeding!
|
|
156
|
+
|
|
157
|
+
6. **Document:**
|
|
158
|
+
```
|
|
159
|
+
"Create documentation: type=README, summary='Added user profile section to docs'"
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
7. **Check readiness:**
|
|
163
|
+
```
|
|
164
|
+
"Check if I'm ready to commit"
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
8. **Commit & Push:**
|
|
168
|
+
```bash
|
|
169
|
+
git add .
|
|
170
|
+
git commit -m "feat: add user profile page with tests and docs"
|
|
171
|
+
git push
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
9. **Complete:**
|
|
175
|
+
```
|
|
176
|
+
"Complete the task with commit message: 'feat: add user profile page'"
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## 🎯 Key Features
|
|
180
|
+
|
|
181
|
+
- **Enforces discipline**: Won't let you skip steps
|
|
182
|
+
- **Test-driven**: Blocks commits if tests fail
|
|
183
|
+
- **Documentation reminder**: Ensures you document your work
|
|
184
|
+
- **State tracking**: Remembers where you are in the workflow
|
|
185
|
+
- **History**: Keeps track of completed tasks
|
|
186
|
+
- **Prompts**: Quick reminders of best practices
|
|
187
|
+
|
|
188
|
+
## 🚫 What This Server Prevents
|
|
189
|
+
|
|
190
|
+
- ❌ Committing without tests
|
|
191
|
+
- ❌ Committing with failing tests
|
|
192
|
+
- ❌ Committing without documentation
|
|
193
|
+
- ❌ Losing track of what you're working on
|
|
194
|
+
- ❌ Skipping important workflow steps
|
|
195
|
+
|
|
196
|
+
## 💡 Tips
|
|
197
|
+
|
|
198
|
+
1. **Always start with `start_task`** - This sets your intention
|
|
199
|
+
2. **Never skip tests** - The server will remind you!
|
|
200
|
+
3. **Use `get_workflow_status`** - Check where you are anytime
|
|
201
|
+
4. **Review history** - Learn from your past tasks
|
|
202
|
+
5. **Follow the prompts** - They contain best practices
|
|
203
|
+
|
|
204
|
+
## 🔧 Customization
|
|
205
|
+
|
|
206
|
+
You can modify the workflow in `index.js`:
|
|
207
|
+
- Add more workflow phases
|
|
208
|
+
- Customize reminders
|
|
209
|
+
- Add integration with your test runner
|
|
210
|
+
- Add custom validation rules
|
|
211
|
+
|
|
212
|
+
## 📝 State Management
|
|
213
|
+
|
|
214
|
+
The server maintains state in `.workflow-state.json`:
|
|
215
|
+
- Current phase
|
|
216
|
+
- Task description
|
|
217
|
+
- Completion status of each step
|
|
218
|
+
- History of completed tasks
|
|
219
|
+
|
|
220
|
+
This file is automatically created and managed by the server.
|
|
221
|
+
|
|
222
|
+
## 🤝 Integration with Your Rules
|
|
223
|
+
|
|
224
|
+
This MCP server aligns with your existing development rules:
|
|
225
|
+
- ✅ Enforces test-first discipline
|
|
226
|
+
- ✅ Prevents commits with failing tests
|
|
227
|
+
- ✅ Reminds about documentation
|
|
228
|
+
- ✅ Tracks workflow state
|
|
229
|
+
- ✅ Maintains history
|
|
230
|
+
|
|
231
|
+
## 📄 License
|
|
232
|
+
|
|
233
|
+
MIT
|
|
234
|
+
|
|
235
|
+
## 🙏 Contributing
|
|
236
|
+
|
|
237
|
+
Feel free to customize this server to match your specific workflow needs!
|
package/index.js
ADDED
|
@@ -0,0 +1,588 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
4
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
+
import {
|
|
6
|
+
CallToolRequestSchema,
|
|
7
|
+
ListToolsRequestSchema,
|
|
8
|
+
ListPromptsRequestSchema,
|
|
9
|
+
GetPromptRequestSchema,
|
|
10
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
11
|
+
import fs from "fs/promises";
|
|
12
|
+
import path from "path";
|
|
13
|
+
import { fileURLToPath } from "url";
|
|
14
|
+
|
|
15
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
16
|
+
const __dirname = path.dirname(__filename);
|
|
17
|
+
|
|
18
|
+
// Workflow state management
|
|
19
|
+
export class WorkflowState {
|
|
20
|
+
constructor(stateFilePath = path.join(__dirname, ".workflow-state.json")) {
|
|
21
|
+
this.stateFile = stateFilePath;
|
|
22
|
+
this.state = {
|
|
23
|
+
currentPhase: "idle",
|
|
24
|
+
taskDescription: "",
|
|
25
|
+
bugFixed: false,
|
|
26
|
+
testsPassed: false,
|
|
27
|
+
documentationCreated: false,
|
|
28
|
+
readyToCommit: false,
|
|
29
|
+
history: [],
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async load() {
|
|
34
|
+
try {
|
|
35
|
+
const data = await fs.readFile(this.stateFile, "utf-8");
|
|
36
|
+
this.state = JSON.parse(data);
|
|
37
|
+
} catch (error) {
|
|
38
|
+
// File doesn't exist yet, use default state
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async save() {
|
|
43
|
+
await fs.writeFile(this.stateFile, JSON.stringify(this.state, null, 2));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
reset() {
|
|
47
|
+
this.state = {
|
|
48
|
+
currentPhase: "idle",
|
|
49
|
+
taskDescription: "",
|
|
50
|
+
bugFixed: false,
|
|
51
|
+
testsPassed: false,
|
|
52
|
+
documentationCreated: false,
|
|
53
|
+
readyToCommit: false,
|
|
54
|
+
history: this.state.history,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
addToHistory(entry) {
|
|
59
|
+
this.state.history.push({
|
|
60
|
+
...entry,
|
|
61
|
+
timestamp: new Date().toISOString(),
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export const workflowState = new WorkflowState();
|
|
67
|
+
await workflowState.load();
|
|
68
|
+
|
|
69
|
+
// Create MCP server
|
|
70
|
+
export const server = new Server(
|
|
71
|
+
{
|
|
72
|
+
name: "@programinglive/dev-workflow-mcp-server",
|
|
73
|
+
version: "1.0.0",
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
capabilities: {
|
|
77
|
+
tools: {},
|
|
78
|
+
prompts: {},
|
|
79
|
+
},
|
|
80
|
+
}
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
// Define tools
|
|
84
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
85
|
+
return {
|
|
86
|
+
tools: [
|
|
87
|
+
{
|
|
88
|
+
name: "start_task",
|
|
89
|
+
description:
|
|
90
|
+
"Start a new coding task. This is the first step - be conscious about what you're coding.",
|
|
91
|
+
inputSchema: {
|
|
92
|
+
type: "object",
|
|
93
|
+
properties: {
|
|
94
|
+
description: {
|
|
95
|
+
type: "string",
|
|
96
|
+
description: "Clear description of what you're going to code",
|
|
97
|
+
},
|
|
98
|
+
type: {
|
|
99
|
+
type: "string",
|
|
100
|
+
enum: ["feature", "bugfix", "refactor", "other"],
|
|
101
|
+
description: "Type of task",
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
required: ["description", "type"],
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
name: "mark_bug_fixed",
|
|
109
|
+
description:
|
|
110
|
+
"Mark that the bug/feature is fixed. Reminder: Now you MUST create tests!",
|
|
111
|
+
inputSchema: {
|
|
112
|
+
type: "object",
|
|
113
|
+
properties: {
|
|
114
|
+
summary: {
|
|
115
|
+
type: "string",
|
|
116
|
+
description: "Brief summary of what was fixed/implemented",
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
required: ["summary"],
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
name: "run_tests",
|
|
124
|
+
description:
|
|
125
|
+
"Record test results. NEVER commit if tests fail! Only proceed if all tests are green.",
|
|
126
|
+
inputSchema: {
|
|
127
|
+
type: "object",
|
|
128
|
+
properties: {
|
|
129
|
+
passed: {
|
|
130
|
+
type: "boolean",
|
|
131
|
+
description: "Did all tests pass?",
|
|
132
|
+
},
|
|
133
|
+
testCommand: {
|
|
134
|
+
type: "string",
|
|
135
|
+
description: "The test command that was run",
|
|
136
|
+
},
|
|
137
|
+
details: {
|
|
138
|
+
type: "string",
|
|
139
|
+
description: "Test results details",
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
required: ["passed", "testCommand"],
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
name: "create_documentation",
|
|
147
|
+
description:
|
|
148
|
+
"Mark that documentation has been created/updated. This is required before committing.",
|
|
149
|
+
inputSchema: {
|
|
150
|
+
type: "object",
|
|
151
|
+
properties: {
|
|
152
|
+
documentationType: {
|
|
153
|
+
type: "string",
|
|
154
|
+
enum: ["README", "inline-comments", "API-docs", "changelog", "other"],
|
|
155
|
+
description: "Type of documentation created",
|
|
156
|
+
},
|
|
157
|
+
summary: {
|
|
158
|
+
type: "string",
|
|
159
|
+
description: "What was documented",
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
required: ["documentationType", "summary"],
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
name: "check_ready_to_commit",
|
|
167
|
+
description:
|
|
168
|
+
"Check if all workflow steps are completed and you're ready to commit & push.",
|
|
169
|
+
inputSchema: {
|
|
170
|
+
type: "object",
|
|
171
|
+
properties: {},
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
name: "complete_task",
|
|
176
|
+
description:
|
|
177
|
+
"Mark the task as complete after successful commit & push. Resets workflow for next task.",
|
|
178
|
+
inputSchema: {
|
|
179
|
+
type: "object",
|
|
180
|
+
properties: {
|
|
181
|
+
commitMessage: {
|
|
182
|
+
type: "string",
|
|
183
|
+
description: "The commit message used",
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
required: ["commitMessage"],
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
name: "get_workflow_status",
|
|
191
|
+
description: "Get current workflow status and what needs to be done next.",
|
|
192
|
+
inputSchema: {
|
|
193
|
+
type: "object",
|
|
194
|
+
properties: {},
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
name: "view_history",
|
|
199
|
+
description: "View workflow history of completed tasks.",
|
|
200
|
+
inputSchema: {
|
|
201
|
+
type: "object",
|
|
202
|
+
properties: {
|
|
203
|
+
limit: {
|
|
204
|
+
type: "number",
|
|
205
|
+
description: "Number of recent tasks to show (default: 10)",
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
],
|
|
211
|
+
};
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
// Handle tool calls
|
|
215
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
216
|
+
const { name, arguments: args } = request.params;
|
|
217
|
+
|
|
218
|
+
switch (name) {
|
|
219
|
+
case "start_task": {
|
|
220
|
+
workflowState.reset();
|
|
221
|
+
workflowState.state.currentPhase = "coding";
|
|
222
|
+
workflowState.state.taskDescription = args.description;
|
|
223
|
+
workflowState.state.taskType = args.type;
|
|
224
|
+
await workflowState.save();
|
|
225
|
+
|
|
226
|
+
return {
|
|
227
|
+
content: [
|
|
228
|
+
{
|
|
229
|
+
type: "text",
|
|
230
|
+
text: `✅ Task Started: ${args.description}\n\n🎯 Be conscious about what you're coding!\n\nWorkflow Steps:\n1. ✓ Start task (current)\n2. ⏳ Fix/implement the feature\n3. ⏳ Create tests\n4. ⏳ Run tests (must pass!)\n5. ⏳ Create documentation\n6. ⏳ Commit & push\n\nReminder: Focus on writing clean, maintainable code!`,
|
|
231
|
+
},
|
|
232
|
+
],
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
case "mark_bug_fixed": {
|
|
237
|
+
if (workflowState.state.currentPhase === "idle") {
|
|
238
|
+
return {
|
|
239
|
+
content: [
|
|
240
|
+
{
|
|
241
|
+
type: "text",
|
|
242
|
+
text: "⚠️ Please start a task first using 'start_task'!",
|
|
243
|
+
},
|
|
244
|
+
],
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
workflowState.state.bugFixed = true;
|
|
249
|
+
workflowState.state.currentPhase = "testing";
|
|
250
|
+
workflowState.state.fixSummary = args.summary;
|
|
251
|
+
await workflowState.save();
|
|
252
|
+
|
|
253
|
+
return {
|
|
254
|
+
content: [
|
|
255
|
+
{
|
|
256
|
+
type: "text",
|
|
257
|
+
text: `✅ Feature/Bug marked as fixed!\n\n⚠️ CRITICAL REMINDER: You MUST create tests now!\n\nNext Steps:\n1. ✓ Fix/implement feature\n2. ⏳ Create tests for: ${args.summary}\n3. ⏳ Run tests (must be green!)\n4. ⏳ Create documentation\n5. ⏳ Commit & push\n\n🚫 DO NOT SKIP TESTING!`,
|
|
258
|
+
},
|
|
259
|
+
],
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
case "run_tests": {
|
|
264
|
+
if (!workflowState.state.bugFixed) {
|
|
265
|
+
return {
|
|
266
|
+
content: [
|
|
267
|
+
{
|
|
268
|
+
type: "text",
|
|
269
|
+
text: "⚠️ Please mark your feature/bug as fixed first using 'mark_bug_fixed'!",
|
|
270
|
+
},
|
|
271
|
+
],
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
workflowState.state.testsPassed = args.passed;
|
|
276
|
+
workflowState.state.testCommand = args.testCommand;
|
|
277
|
+
workflowState.state.testDetails = args.details || "";
|
|
278
|
+
|
|
279
|
+
if (!args.passed) {
|
|
280
|
+
workflowState.state.currentPhase = "testing";
|
|
281
|
+
await workflowState.save();
|
|
282
|
+
|
|
283
|
+
return {
|
|
284
|
+
content: [
|
|
285
|
+
{
|
|
286
|
+
type: "text",
|
|
287
|
+
text: `❌ TESTS FAILED!\n\n🚫 STOP! DO NOT COMMIT OR PUSH!\n\nYou must:\n1. Fix the failing tests\n2. Run tests again until they pass\n\nTest command: ${args.testCommand}\n\nNever skip or ignore failing tests!`,
|
|
288
|
+
},
|
|
289
|
+
],
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
workflowState.state.currentPhase = "documentation";
|
|
294
|
+
await workflowState.save();
|
|
295
|
+
|
|
296
|
+
return {
|
|
297
|
+
content: [
|
|
298
|
+
{
|
|
299
|
+
type: "text",
|
|
300
|
+
text: `✅ All tests passed! 🎉\n\nTest command: ${args.testCommand}\n\nNext Steps:\n1. ✓ Fix/implement feature\n2. ✓ Create tests\n3. ✓ Run tests (GREEN!)\n4. ⏳ Create/update documentation\n5. ⏳ Commit & push\n\nReminder: Document what you did before committing!`,
|
|
301
|
+
},
|
|
302
|
+
],
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
case "create_documentation": {
|
|
307
|
+
if (!workflowState.state.testsPassed) {
|
|
308
|
+
return {
|
|
309
|
+
content: [
|
|
310
|
+
{
|
|
311
|
+
type: "text",
|
|
312
|
+
text: "⚠️ Please ensure tests are passing first! Run 'run_tests' with passed=true.",
|
|
313
|
+
},
|
|
314
|
+
],
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
workflowState.state.documentationCreated = true;
|
|
319
|
+
workflowState.state.documentationType = args.documentationType;
|
|
320
|
+
workflowState.state.documentationSummary = args.summary;
|
|
321
|
+
workflowState.state.currentPhase = "ready";
|
|
322
|
+
workflowState.state.readyToCommit = true;
|
|
323
|
+
await workflowState.save();
|
|
324
|
+
|
|
325
|
+
return {
|
|
326
|
+
content: [
|
|
327
|
+
{
|
|
328
|
+
type: "text",
|
|
329
|
+
text: `✅ Documentation created!\n\nType: ${args.documentationType}\nSummary: ${args.summary}\n\n🎉 You're ready to commit!\n\nNext Steps:\n1. ✓ Fix/implement feature\n2. ✓ Create tests\n3. ✓ Run tests (GREEN!)\n4. ✓ Create documentation\n5. ⏳ Run 'check_ready_to_commit' to verify\n6. ⏳ Commit & push\n7. ⏳ Mark as complete with 'complete_task'\n\nRemember: git add . && git commit && git push`,
|
|
330
|
+
},
|
|
331
|
+
],
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
case "check_ready_to_commit": {
|
|
336
|
+
const status = workflowState.state;
|
|
337
|
+
const checks = [
|
|
338
|
+
{ name: "Task started", done: status.currentPhase !== "idle" },
|
|
339
|
+
{ name: "Feature/bug fixed", done: status.bugFixed },
|
|
340
|
+
{ name: "Tests created and passed", done: status.testsPassed },
|
|
341
|
+
{ name: "Documentation created", done: status.documentationCreated },
|
|
342
|
+
];
|
|
343
|
+
|
|
344
|
+
const allDone = checks.every((c) => c.done);
|
|
345
|
+
const checkList = checks
|
|
346
|
+
.map((c) => `${c.done ? "✅" : "❌"} ${c.name}`)
|
|
347
|
+
.join("\n");
|
|
348
|
+
|
|
349
|
+
if (allDone) {
|
|
350
|
+
return {
|
|
351
|
+
content: [
|
|
352
|
+
{
|
|
353
|
+
type: "text",
|
|
354
|
+
text: `🎉 ALL CHECKS PASSED! You're ready to commit!\n\n${checkList}\n\n✅ You can now:\n1. git add .\n2. git commit -m "your message"\n3. git push\n4. Run 'complete_task' to finish\n\nTask: ${status.taskDescription}`,
|
|
355
|
+
},
|
|
356
|
+
],
|
|
357
|
+
};
|
|
358
|
+
} else {
|
|
359
|
+
return {
|
|
360
|
+
content: [
|
|
361
|
+
{
|
|
362
|
+
type: "text",
|
|
363
|
+
text: `⚠️ NOT READY TO COMMIT!\n\n${checkList}\n\nPlease complete all steps before committing.`,
|
|
364
|
+
},
|
|
365
|
+
],
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
case "complete_task": {
|
|
371
|
+
if (!workflowState.state.readyToCommit) {
|
|
372
|
+
return {
|
|
373
|
+
content: [
|
|
374
|
+
{
|
|
375
|
+
type: "text",
|
|
376
|
+
text: "⚠️ Not ready to complete! Run 'check_ready_to_commit' first.",
|
|
377
|
+
},
|
|
378
|
+
],
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
workflowState.addToHistory({
|
|
383
|
+
taskDescription: workflowState.state.taskDescription,
|
|
384
|
+
taskType: workflowState.state.taskType,
|
|
385
|
+
fixSummary: workflowState.state.fixSummary,
|
|
386
|
+
testCommand: workflowState.state.testCommand,
|
|
387
|
+
documentationType: workflowState.state.documentationType,
|
|
388
|
+
commitMessage: args.commitMessage,
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
const completedTask = workflowState.state.taskDescription;
|
|
392
|
+
workflowState.reset();
|
|
393
|
+
workflowState.state.currentPhase = "idle";
|
|
394
|
+
await workflowState.save();
|
|
395
|
+
|
|
396
|
+
return {
|
|
397
|
+
content: [
|
|
398
|
+
{
|
|
399
|
+
type: "text",
|
|
400
|
+
text: `🎉 Task completed successfully!\n\nCompleted: ${completedTask}\nCommit: ${args.commitMessage}\n\n✅ Workflow reset. Ready for next task!\n\nUse 'start_task' when you begin your next coding session.`,
|
|
401
|
+
},
|
|
402
|
+
],
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
case "get_workflow_status": {
|
|
407
|
+
const status = workflowState.state;
|
|
408
|
+
|
|
409
|
+
if (status.currentPhase === "idle") {
|
|
410
|
+
return {
|
|
411
|
+
content: [
|
|
412
|
+
{
|
|
413
|
+
type: "text",
|
|
414
|
+
text: "📋 No active task.\n\nUse 'start_task' to begin a new coding session.",
|
|
415
|
+
},
|
|
416
|
+
],
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const phaseMessages = {
|
|
421
|
+
coding: "🔨 Currently coding",
|
|
422
|
+
testing: "🧪 Ready for testing",
|
|
423
|
+
documentation: "📝 Ready for documentation",
|
|
424
|
+
ready: "✅ Ready to commit",
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
return {
|
|
428
|
+
content: [
|
|
429
|
+
{
|
|
430
|
+
type: "text",
|
|
431
|
+
text: `📋 Current Workflow Status\n\nTask: ${status.taskDescription}\nPhase: ${phaseMessages[status.currentPhase]}\n\nProgress:\n${status.bugFixed ? "✅" : "⏳"} Feature/bug fixed\n${status.testsPassed ? "✅" : "⏳"} Tests passed\n${status.documentationCreated ? "✅" : "⏳"} Documentation created\n${status.readyToCommit ? "✅" : "⏳"} Ready to commit\n\nNext: ${getNextStep(status)}`,
|
|
432
|
+
},
|
|
433
|
+
],
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
case "view_history": {
|
|
438
|
+
const limit = args.limit || 10;
|
|
439
|
+
const history = workflowState.state.history.slice(-limit).reverse();
|
|
440
|
+
|
|
441
|
+
if (history.length === 0) {
|
|
442
|
+
return {
|
|
443
|
+
content: [
|
|
444
|
+
{
|
|
445
|
+
type: "text",
|
|
446
|
+
text: "📜 No workflow history yet.",
|
|
447
|
+
},
|
|
448
|
+
],
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
const historyText = history
|
|
453
|
+
.map(
|
|
454
|
+
(entry, index) =>
|
|
455
|
+
`${index + 1}. [${new Date(entry.timestamp).toLocaleString()}]\n Task: ${entry.taskDescription}\n Type: ${entry.taskType}\n Commit: ${entry.commitMessage}\n`
|
|
456
|
+
)
|
|
457
|
+
.join("\n");
|
|
458
|
+
|
|
459
|
+
return {
|
|
460
|
+
content: [
|
|
461
|
+
{
|
|
462
|
+
type: "text",
|
|
463
|
+
text: `📜 Workflow History (last ${history.length} tasks)\n\n${historyText}`,
|
|
464
|
+
},
|
|
465
|
+
],
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
default:
|
|
470
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
471
|
+
}
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
// Helper function
|
|
475
|
+
export function getNextStep(status) {
|
|
476
|
+
if (!status.bugFixed) return "Mark feature/bug as fixed";
|
|
477
|
+
if (!status.testsPassed) return "Create and run tests";
|
|
478
|
+
if (!status.documentationCreated) return "Create documentation";
|
|
479
|
+
if (!status.readyToCommit) return "Check if ready to commit";
|
|
480
|
+
return "Commit and push!";
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// Define prompts
|
|
484
|
+
server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
485
|
+
return {
|
|
486
|
+
prompts: [
|
|
487
|
+
{
|
|
488
|
+
name: "workflow_reminder",
|
|
489
|
+
description: "Get a reminder of the complete development workflow",
|
|
490
|
+
},
|
|
491
|
+
{
|
|
492
|
+
name: "pre_commit_checklist",
|
|
493
|
+
description: "Pre-commit checklist to ensure nothing is missed",
|
|
494
|
+
},
|
|
495
|
+
],
|
|
496
|
+
};
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
500
|
+
const { name } = request.params;
|
|
501
|
+
|
|
502
|
+
if (name === "workflow_reminder") {
|
|
503
|
+
return {
|
|
504
|
+
messages: [
|
|
505
|
+
{
|
|
506
|
+
role: "user",
|
|
507
|
+
content: {
|
|
508
|
+
type: "text",
|
|
509
|
+
text: `Development Workflow Discipline:
|
|
510
|
+
|
|
511
|
+
1. 🎯 START CONSCIOUS
|
|
512
|
+
- Use 'start_task' to declare what you're coding
|
|
513
|
+
- Be clear about your intention
|
|
514
|
+
|
|
515
|
+
2. 🔨 CODE WITH PURPOSE
|
|
516
|
+
- Implement your feature or fix
|
|
517
|
+
- Follow best practices
|
|
518
|
+
|
|
519
|
+
3. 🧪 ALWAYS CREATE TESTS
|
|
520
|
+
- After fixing/implementing, create tests
|
|
521
|
+
- Use 'mark_bug_fixed' when done
|
|
522
|
+
|
|
523
|
+
4. ✅ TESTS MUST PASS
|
|
524
|
+
- Run your tests with 'run_tests'
|
|
525
|
+
- If tests fail: FIX THEM, never skip!
|
|
526
|
+
- Only proceed when all tests are GREEN
|
|
527
|
+
|
|
528
|
+
5. 📝 DOCUMENT YOUR WORK
|
|
529
|
+
- Update README, comments, or docs
|
|
530
|
+
- Use 'create_documentation' when done
|
|
531
|
+
|
|
532
|
+
6. 🚀 COMMIT & PUSH
|
|
533
|
+
- Use 'check_ready_to_commit' to verify
|
|
534
|
+
- git add . && git commit && git push
|
|
535
|
+
- Use 'complete_task' to finish
|
|
536
|
+
|
|
537
|
+
Remember: No shortcuts! Each step is important for code quality.`,
|
|
538
|
+
},
|
|
539
|
+
},
|
|
540
|
+
],
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
if (name === "pre_commit_checklist") {
|
|
545
|
+
return {
|
|
546
|
+
messages: [
|
|
547
|
+
{
|
|
548
|
+
role: "user",
|
|
549
|
+
content: {
|
|
550
|
+
type: "text",
|
|
551
|
+
text: `Pre-Commit Checklist:
|
|
552
|
+
|
|
553
|
+
Before you commit and push, verify:
|
|
554
|
+
|
|
555
|
+
□ Feature/bug is fully implemented
|
|
556
|
+
□ Tests are created for the changes
|
|
557
|
+
□ All tests pass (GREEN) - run 'run_tests'
|
|
558
|
+
□ Documentation is updated
|
|
559
|
+
□ Code is clean (no console.logs, unused imports)
|
|
560
|
+
□ No TypeScript 'any' types
|
|
561
|
+
□ Followed project coding standards
|
|
562
|
+
|
|
563
|
+
Use 'check_ready_to_commit' to verify workflow completion.
|
|
564
|
+
|
|
565
|
+
🚫 NEVER commit if tests are failing!
|
|
566
|
+
✅ Only commit when everything is green!`,
|
|
567
|
+
},
|
|
568
|
+
},
|
|
569
|
+
],
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
throw new Error(`Unknown prompt: ${name}`);
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
// Start server
|
|
577
|
+
export async function main() {
|
|
578
|
+
const transport = new StdioServerTransport();
|
|
579
|
+
await server.connect(transport);
|
|
580
|
+
console.error("Dev Workflow MCP Server running on stdio");
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
if (process.argv[1] === __filename) {
|
|
584
|
+
main().catch((error) => {
|
|
585
|
+
console.error("Server error:", error);
|
|
586
|
+
process.exit(1);
|
|
587
|
+
});
|
|
588
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@programinglive/dev-workflow-mcp-server",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server to enforce development workflow discipline",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"dev-workflow-mcp": "./index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"test": "node --test",
|
|
12
|
+
"check": "node --check index.js",
|
|
13
|
+
"start": "node index.js",
|
|
14
|
+
"dev": "node --watch index.js"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"mcp",
|
|
18
|
+
"workflow",
|
|
19
|
+
"development",
|
|
20
|
+
"discipline"
|
|
21
|
+
],
|
|
22
|
+
"author": "",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@modelcontextprotocol/sdk": "^1.0.4"
|
|
26
|
+
},
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { WorkflowState, getNextStep } from "../index.js";
|
|
7
|
+
|
|
8
|
+
async function withWorkflowState(callback) {
|
|
9
|
+
const tempDir = await mkdtemp(path.join(tmpdir(), "workflow-state-"));
|
|
10
|
+
const stateFile = path.join(tempDir, "state.json");
|
|
11
|
+
const state = new WorkflowState(stateFile);
|
|
12
|
+
await state.load();
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
await callback(state);
|
|
16
|
+
} finally {
|
|
17
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
test("WorkflowState persists data across save/load", async () => {
|
|
22
|
+
await withWorkflowState(async (state) => {
|
|
23
|
+
state.state.currentPhase = "testing";
|
|
24
|
+
state.state.taskDescription = "Test persistence";
|
|
25
|
+
state.state.bugFixed = true;
|
|
26
|
+
await state.save();
|
|
27
|
+
|
|
28
|
+
const reloaded = new WorkflowState(state.stateFile);
|
|
29
|
+
await reloaded.load();
|
|
30
|
+
|
|
31
|
+
assert.equal(reloaded.state.currentPhase, "testing");
|
|
32
|
+
assert.equal(reloaded.state.taskDescription, "Test persistence");
|
|
33
|
+
assert.equal(reloaded.state.bugFixed, true);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("WorkflowState reset keeps history but clears progress", async () => {
|
|
38
|
+
await withWorkflowState(async (state) => {
|
|
39
|
+
state.state.currentPhase = "ready";
|
|
40
|
+
state.state.testsPassed = true;
|
|
41
|
+
state.state.documentationCreated = true;
|
|
42
|
+
state.addToHistory({ taskDescription: "Sample task" });
|
|
43
|
+
|
|
44
|
+
state.reset();
|
|
45
|
+
|
|
46
|
+
assert.equal(state.state.currentPhase, "idle");
|
|
47
|
+
assert.equal(state.state.testsPassed, false);
|
|
48
|
+
assert.equal(state.state.documentationCreated, false);
|
|
49
|
+
assert.equal(state.state.history.length, 1);
|
|
50
|
+
assert.equal(state.state.history[0].taskDescription, "Sample task");
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test("getNextStep guides workflow progression", () => {
|
|
55
|
+
const baseState = {
|
|
56
|
+
bugFixed: false,
|
|
57
|
+
testsPassed: false,
|
|
58
|
+
documentationCreated: false,
|
|
59
|
+
readyToCommit: false,
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
assert.equal(
|
|
63
|
+
getNextStep(baseState),
|
|
64
|
+
"Mark feature/bug as fixed"
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
const afterFix = { ...baseState, bugFixed: true };
|
|
68
|
+
assert.equal(
|
|
69
|
+
getNextStep(afterFix),
|
|
70
|
+
"Create and run tests"
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const afterTests = { ...afterFix, testsPassed: true };
|
|
74
|
+
assert.equal(
|
|
75
|
+
getNextStep(afterTests),
|
|
76
|
+
"Create documentation"
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
const afterDocs = { ...afterTests, documentationCreated: true };
|
|
80
|
+
assert.equal(
|
|
81
|
+
getNextStep(afterDocs),
|
|
82
|
+
"Check if ready to commit"
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
const ready = { ...afterDocs, readyToCommit: true };
|
|
86
|
+
assert.equal(getNextStep(ready), "Commit and push!");
|
|
87
|
+
});
|