@programinglive/dev-workflow-mcp-server 1.0.1 → 1.0.2

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.
Files changed (4) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +15 -0
  3. package/index.js +130 -52
  4. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,19 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ### [1.0.2](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.0.1...v1.0.2) (2025-10-19)
6
+
7
+
8
+ ### šŸ› Bug Fixes
9
+
10
+ * guard run_tests arguments ([2004c59](https://github.com/programinglive/dev-workflow-mcp-server/commit/2004c590fabe359d068122cf240a731382f81ca0))
11
+ * normalize tool call arguments ([44d5a3d](https://github.com/programinglive/dev-workflow-mcp-server/commit/44d5a3d35f191012445fa02cee6254afbc993c98))
12
+
13
+
14
+ ### šŸ“ Documentation
15
+
16
+ * clarify tool argument requirements ([4528614](https://github.com/programinglive/dev-workflow-mcp-server/commit/45286146661ad850eccb04849c11819e91ddd580))
17
+
5
18
  ### [1.0.1](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.0.0...v1.0.1) (2025-10-18)
6
19
 
7
20
 
package/README.md CHANGED
@@ -57,6 +57,21 @@ After adding the configuration, restart the application to load the MCP server.
57
57
 
58
58
  ## šŸ› ļø Available Tools
59
59
 
60
+ ### Tool Argument Requirements
61
+
62
+ All tool invocations validate their `arguments` payload before running:
63
+ - Strings are parsed as JSON and must resolve to an object (key/value pairs).
64
+ - Passing non-object data (numbers, arrays, plain text) triggers a guidance error.
65
+ - Missing or malformed arguments safely default to empty input so the tool can respond with actionable reminders.
66
+
67
+ Example (stringified JSON object):
68
+ ```
69
+ {
70
+ "name": "start_task",
71
+ "arguments": "{\"description\":\"Add reporting endpoint\",\"type\":\"feature\"}"
72
+ }
73
+ ```
74
+
60
75
  ### `start_task`
61
76
  Start a new coding task. This is your first step - be conscious about what you're coding.
62
77
 
package/index.js CHANGED
@@ -70,6 +70,42 @@ await workflowState.load();
70
70
 
71
71
  const exec = promisify(execCallback);
72
72
 
73
+ function normalizeRequestArgs(rawArgs) {
74
+ if (rawArgs === undefined || rawArgs === null) {
75
+ return { args: {}, error: null };
76
+ }
77
+
78
+ if (typeof rawArgs === "string") {
79
+ try {
80
+ const parsed = JSON.parse(rawArgs);
81
+ if (parsed && typeof parsed === "object") {
82
+ return { args: parsed, error: null };
83
+ }
84
+
85
+ return {
86
+ args: {},
87
+ error:
88
+ "āš ļø Tool arguments must be a JSON object. Please provide key/value pairs.",
89
+ };
90
+ } catch (parseError) {
91
+ return {
92
+ args: {},
93
+ error:
94
+ "āš ļø Unable to parse tool arguments. Please provide valid JSON-formatted data.",
95
+ };
96
+ }
97
+ }
98
+
99
+ if (typeof rawArgs === "object") {
100
+ return { args: rawArgs, error: null };
101
+ }
102
+
103
+ return {
104
+ args: {},
105
+ error: "āš ļø Unsupported tool arguments format. Expected a JSON object.",
106
+ };
107
+ }
108
+
73
109
  async function hasStagedChanges() {
74
110
  try {
75
111
  const { stdout } = await exec("git status --porcelain");
@@ -232,96 +268,127 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
232
268
 
233
269
  // Handle tool calls
234
270
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
235
- const { name, arguments: args } = request.params;
236
-
237
- switch (name) {
238
- case "start_task": {
239
- workflowState.reset();
240
- workflowState.state.currentPhase = "coding";
241
- workflowState.state.taskDescription = args.description;
242
- workflowState.state.taskType = args.type;
243
- await workflowState.save();
271
+ try {
272
+ const { name } = request.params;
273
+ const { args, error: argumentsError } = normalizeRequestArgs(
274
+ request.params.arguments
275
+ );
244
276
 
277
+ if (argumentsError) {
245
278
  return {
246
279
  content: [
247
280
  {
248
281
  type: "text",
249
- 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!`,
282
+ text: argumentsError,
250
283
  },
251
284
  ],
252
285
  };
253
286
  }
254
287
 
255
- case "mark_bug_fixed": {
256
- if (workflowState.state.currentPhase === "idle") {
288
+ switch (name) {
289
+ case "start_task": {
290
+ workflowState.reset();
291
+ workflowState.state.currentPhase = "coding";
292
+ workflowState.state.taskDescription = args.description;
293
+ workflowState.state.taskType = args.type;
294
+ await workflowState.save();
295
+
257
296
  return {
258
297
  content: [
259
298
  {
260
299
  type: "text",
261
- text: "āš ļø Please start a task first using 'start_task'!",
300
+ 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!`,
262
301
  },
263
302
  ],
264
303
  };
265
304
  }
266
305
 
267
- workflowState.state.bugFixed = true;
268
- workflowState.state.currentPhase = "testing";
269
- workflowState.state.fixSummary = args.summary;
270
- await workflowState.save();
271
-
272
- return {
273
- content: [
274
- {
275
- type: "text",
276
- 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!`,
277
- },
278
- ],
279
- };
280
- }
306
+ case "mark_bug_fixed": {
307
+ if (workflowState.state.currentPhase === "idle") {
308
+ return {
309
+ content: [
310
+ {
311
+ type: "text",
312
+ text: "āš ļø Please start a task first using 'start_task'!",
313
+ },
314
+ ],
315
+ };
316
+ }
317
+
318
+ workflowState.state.bugFixed = true;
319
+ workflowState.state.currentPhase = "testing";
320
+ workflowState.state.fixSummary = args.summary;
321
+ await workflowState.save();
281
322
 
282
- case "run_tests": {
283
- if (!workflowState.state.bugFixed) {
284
323
  return {
285
324
  content: [
286
325
  {
287
326
  type: "text",
288
- text: "āš ļø Please mark your feature/bug as fixed first using 'mark_bug_fixed'!",
327
+ 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!`,
289
328
  },
290
329
  ],
291
330
  };
292
331
  }
293
332
 
294
- workflowState.state.testsPassed = args.passed;
295
- workflowState.state.testCommand = args.testCommand;
296
- workflowState.state.testDetails = args.details || "";
297
-
298
- if (!args.passed) {
299
- workflowState.state.currentPhase = "testing";
333
+ case "run_tests": {
334
+ if (!workflowState.state.bugFixed) {
335
+ return {
336
+ content: [
337
+ {
338
+ type: "text",
339
+ text: "āš ļø Please mark your feature/bug as fixed first using 'mark_bug_fixed'!",
340
+ },
341
+ ],
342
+ };
343
+ }
344
+
345
+ if (
346
+ typeof args.passed !== "boolean" ||
347
+ typeof args.testCommand !== "string" ||
348
+ args.testCommand.trim() === ""
349
+ ) {
350
+ return {
351
+ content: [
352
+ {
353
+ type: "text",
354
+ text: "āš ļø Please provide both 'passed' (boolean) and 'testCommand' (non-empty string) when recording test results.",
355
+ },
356
+ ],
357
+ };
358
+ }
359
+
360
+ workflowState.state.testsPassed = args.passed;
361
+ workflowState.state.testCommand = args.testCommand;
362
+ workflowState.state.testDetails =
363
+ typeof args.details === "string" ? args.details : "";
364
+
365
+ if (!args.passed) {
366
+ workflowState.state.currentPhase = "testing";
367
+ await workflowState.save();
368
+
369
+ return {
370
+ content: [
371
+ {
372
+ type: "text",
373
+ 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!`,
374
+ },
375
+ ],
376
+ };
377
+ }
378
+
379
+ workflowState.state.currentPhase = "documentation";
300
380
  await workflowState.save();
301
381
 
302
382
  return {
303
383
  content: [
304
384
  {
305
385
  type: "text",
306
- 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!`,
386
+ 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!`,
307
387
  },
308
388
  ],
309
389
  };
310
390
  }
311
391
 
312
- workflowState.state.currentPhase = "documentation";
313
- await workflowState.save();
314
-
315
- return {
316
- content: [
317
- {
318
- type: "text",
319
- 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!`,
320
- },
321
- ],
322
- };
323
- }
324
-
325
392
  case "create_documentation": {
326
393
  if (!workflowState.state.testsPassed) {
327
394
  return {
@@ -496,8 +563,19 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
496
563
  };
497
564
  }
498
565
 
499
- default:
500
- throw new Error(`Unknown tool: ${name}`);
566
+ default:
567
+ throw new Error(`Unknown tool: ${name}`);
568
+ }
569
+ } catch (error) {
570
+ console.error("Tool handler error:", error);
571
+ return {
572
+ content: [
573
+ {
574
+ type: "text",
575
+ text: "āš ļø Internal server error while processing the tool request. Please try again.",
576
+ },
577
+ ],
578
+ };
501
579
  }
502
580
  });
503
581
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@programinglive/dev-workflow-mcp-server",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "MCP server to enforce development workflow discipline",
5
5
  "type": "module",
6
6
  "main": "index.js",