@codemcp/workflows 3.1.21

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 (159) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/.vibe/conversation-state.sqlite +0 -0
  3. package/LICENSE +674 -0
  4. package/dist/index.d.ts +9 -0
  5. package/dist/index.d.ts.map +1 -0
  6. package/dist/index.js +74 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/notification-service.d.ts +14 -0
  9. package/dist/notification-service.d.ts.map +1 -0
  10. package/dist/notification-service.js +18 -0
  11. package/dist/notification-service.js.map +1 -0
  12. package/dist/resource-handlers/conversation-state.d.ts +15 -0
  13. package/dist/resource-handlers/conversation-state.d.ts.map +1 -0
  14. package/dist/resource-handlers/conversation-state.js +40 -0
  15. package/dist/resource-handlers/conversation-state.js.map +1 -0
  16. package/dist/resource-handlers/development-plan.d.ts +14 -0
  17. package/dist/resource-handlers/development-plan.d.ts.map +1 -0
  18. package/dist/resource-handlers/development-plan.js +31 -0
  19. package/dist/resource-handlers/development-plan.js.map +1 -0
  20. package/dist/resource-handlers/index.d.ts +24 -0
  21. package/dist/resource-handlers/index.d.ts.map +1 -0
  22. package/dist/resource-handlers/index.js +62 -0
  23. package/dist/resource-handlers/index.js.map +1 -0
  24. package/dist/resource-handlers/system-prompt.d.ts +15 -0
  25. package/dist/resource-handlers/system-prompt.d.ts.map +1 -0
  26. package/dist/resource-handlers/system-prompt.js +40 -0
  27. package/dist/resource-handlers/system-prompt.js.map +1 -0
  28. package/dist/resource-handlers/workflow-resource.d.ts +15 -0
  29. package/dist/resource-handlers/workflow-resource.d.ts.map +1 -0
  30. package/dist/resource-handlers/workflow-resource.js +85 -0
  31. package/dist/resource-handlers/workflow-resource.js.map +1 -0
  32. package/dist/response-renderer.d.ts +30 -0
  33. package/dist/response-renderer.d.ts.map +1 -0
  34. package/dist/response-renderer.js +94 -0
  35. package/dist/response-renderer.js.map +1 -0
  36. package/dist/server-config.d.ts +34 -0
  37. package/dist/server-config.d.ts.map +1 -0
  38. package/dist/server-config.js +486 -0
  39. package/dist/server-config.js.map +1 -0
  40. package/dist/server-helpers.d.ts +62 -0
  41. package/dist/server-helpers.d.ts.map +1 -0
  42. package/dist/server-helpers.js +156 -0
  43. package/dist/server-helpers.js.map +1 -0
  44. package/dist/server-implementation.d.ts +74 -0
  45. package/dist/server-implementation.d.ts.map +1 -0
  46. package/dist/server-implementation.js +201 -0
  47. package/dist/server-implementation.js.map +1 -0
  48. package/dist/server.d.ts +6 -0
  49. package/dist/server.d.ts.map +1 -0
  50. package/dist/server.js +5 -0
  51. package/dist/server.js.map +1 -0
  52. package/dist/tool-handlers/base-tool-handler.d.ts +50 -0
  53. package/dist/tool-handlers/base-tool-handler.d.ts.map +1 -0
  54. package/dist/tool-handlers/base-tool-handler.js +74 -0
  55. package/dist/tool-handlers/base-tool-handler.js.map +1 -0
  56. package/dist/tool-handlers/conduct-review.d.ts +49 -0
  57. package/dist/tool-handlers/conduct-review.d.ts.map +1 -0
  58. package/dist/tool-handlers/conduct-review.js +105 -0
  59. package/dist/tool-handlers/conduct-review.js.map +1 -0
  60. package/dist/tool-handlers/get-tool-info.d.ts +76 -0
  61. package/dist/tool-handlers/get-tool-info.d.ts.map +1 -0
  62. package/dist/tool-handlers/get-tool-info.js +168 -0
  63. package/dist/tool-handlers/get-tool-info.js.map +1 -0
  64. package/dist/tool-handlers/index.d.ts +42 -0
  65. package/dist/tool-handlers/index.d.ts.map +1 -0
  66. package/dist/tool-handlers/index.js +74 -0
  67. package/dist/tool-handlers/index.js.map +1 -0
  68. package/dist/tool-handlers/install-workflow.d.ts +48 -0
  69. package/dist/tool-handlers/install-workflow.d.ts.map +1 -0
  70. package/dist/tool-handlers/install-workflow.js +131 -0
  71. package/dist/tool-handlers/install-workflow.js.map +1 -0
  72. package/dist/tool-handlers/list-workflows.d.ts +47 -0
  73. package/dist/tool-handlers/list-workflows.d.ts.map +1 -0
  74. package/dist/tool-handlers/list-workflows.js +58 -0
  75. package/dist/tool-handlers/list-workflows.js.map +1 -0
  76. package/dist/tool-handlers/no-idea.d.ts +41 -0
  77. package/dist/tool-handlers/no-idea.d.ts.map +1 -0
  78. package/dist/tool-handlers/no-idea.js +29 -0
  79. package/dist/tool-handlers/no-idea.js.map +1 -0
  80. package/dist/tool-handlers/proceed-to-phase.d.ts +39 -0
  81. package/dist/tool-handlers/proceed-to-phase.d.ts.map +1 -0
  82. package/dist/tool-handlers/proceed-to-phase.js +109 -0
  83. package/dist/tool-handlers/proceed-to-phase.js.map +1 -0
  84. package/dist/tool-handlers/reset-development.d.ts +31 -0
  85. package/dist/tool-handlers/reset-development.d.ts.map +1 -0
  86. package/dist/tool-handlers/reset-development.js +48 -0
  87. package/dist/tool-handlers/reset-development.js.map +1 -0
  88. package/dist/tool-handlers/resume-workflow.d.ts +88 -0
  89. package/dist/tool-handlers/resume-workflow.d.ts.map +1 -0
  90. package/dist/tool-handlers/resume-workflow.js +213 -0
  91. package/dist/tool-handlers/resume-workflow.js.map +1 -0
  92. package/dist/tool-handlers/setup-project-docs.d.ts +36 -0
  93. package/dist/tool-handlers/setup-project-docs.d.ts.map +1 -0
  94. package/dist/tool-handlers/setup-project-docs.js +136 -0
  95. package/dist/tool-handlers/setup-project-docs.js.map +1 -0
  96. package/dist/tool-handlers/start-development.d.ts +82 -0
  97. package/dist/tool-handlers/start-development.d.ts.map +1 -0
  98. package/dist/tool-handlers/start-development.js +448 -0
  99. package/dist/tool-handlers/start-development.js.map +1 -0
  100. package/dist/tool-handlers/whats-next.d.ts +42 -0
  101. package/dist/tool-handlers/whats-next.d.ts.map +1 -0
  102. package/dist/tool-handlers/whats-next.js +118 -0
  103. package/dist/tool-handlers/whats-next.js.map +1 -0
  104. package/dist/types.d.ts +114 -0
  105. package/dist/types.d.ts.map +1 -0
  106. package/dist/types.js +5 -0
  107. package/dist/types.js.map +1 -0
  108. package/package.json +29 -0
  109. package/src/index.ts +93 -0
  110. package/src/notification-service.ts +23 -0
  111. package/src/resource-handlers/conversation-state.ts +55 -0
  112. package/src/resource-handlers/development-plan.ts +48 -0
  113. package/src/resource-handlers/index.ts +73 -0
  114. package/src/resource-handlers/system-prompt.ts +55 -0
  115. package/src/resource-handlers/workflow-resource.ts +126 -0
  116. package/src/response-renderer.ts +116 -0
  117. package/src/server-config.ts +744 -0
  118. package/src/server-helpers.ts +225 -0
  119. package/src/server-implementation.ts +277 -0
  120. package/src/server.ts +9 -0
  121. package/src/tool-handlers/base-tool-handler.ts +141 -0
  122. package/src/tool-handlers/conduct-review.ts +191 -0
  123. package/src/tool-handlers/get-tool-info.ts +274 -0
  124. package/src/tool-handlers/index.ts +117 -0
  125. package/src/tool-handlers/install-workflow.ts +185 -0
  126. package/src/tool-handlers/list-workflows.ts +94 -0
  127. package/src/tool-handlers/no-idea.ts +47 -0
  128. package/src/tool-handlers/proceed-to-phase.ts +205 -0
  129. package/src/tool-handlers/reset-development.ts +90 -0
  130. package/src/tool-handlers/resume-workflow.ts +380 -0
  131. package/src/tool-handlers/setup-project-docs.ts +226 -0
  132. package/src/tool-handlers/start-development.ts +685 -0
  133. package/src/tool-handlers/whats-next.ts +235 -0
  134. package/src/types.ts +130 -0
  135. package/test/e2e/core-functionality.test.ts +176 -0
  136. package/test/e2e/mcp-contract.test.ts +540 -0
  137. package/test/e2e/plan-management.test.ts +331 -0
  138. package/test/e2e/state-management.test.ts +392 -0
  139. package/test/e2e/workflow-integration.test.ts +506 -0
  140. package/test/unit/commit-behaviour-interface.test.ts +244 -0
  141. package/test/unit/conduct-review.test.ts +151 -0
  142. package/test/unit/reset-functionality.test.ts +72 -0
  143. package/test/unit/resume-workflow.test.ts +192 -0
  144. package/test/unit/server-tools.test.ts +311 -0
  145. package/test/unit/setup-project-docs-handler.test.ts +267 -0
  146. package/test/unit/start-development-artifact-detection.test.ts +387 -0
  147. package/test/unit/start-development-gitignore.test.ts +178 -0
  148. package/test/unit/system-prompt-resource.test.ts +101 -0
  149. package/test/unit/tool-handlers/no-idea.test.ts +40 -0
  150. package/test/utils/e2e-test-setup.ts +453 -0
  151. package/test/utils/run-server-in-dir.sh +27 -0
  152. package/test/utils/temp-files.ts +308 -0
  153. package/test/utils/test-access.ts +79 -0
  154. package/test/utils/test-helpers.ts +286 -0
  155. package/test/utils/test-setup.ts +78 -0
  156. package/tsconfig.build.json +9 -0
  157. package/tsconfig.build.tsbuildinfo +1 -0
  158. package/tsconfig.json +12 -0
  159. package/vitest.config.ts +17 -0
@@ -0,0 +1,540 @@
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
3
+ import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
4
+ import path from 'node:path';
5
+ import { promises as fs } from 'node:fs';
6
+ import {
7
+ TempProject,
8
+ createTempProjectWithDefaultStateMachine,
9
+ } from '../utils/temp-files';
10
+
11
+ vi.unmock('fs');
12
+ vi.unmock('fs/promises');
13
+
14
+ /**
15
+ * MCP Contract Validation Tests
16
+ *
17
+ * Tests the Model Context Protocol contract by using a real MCP TypeScript client
18
+ * from the official SDK to connect to our responsible-vibe-mcp-server and validate:
19
+ * - Server initialization and capability negotiation
20
+ * - All exposed resources (plan://current, state://current)
21
+ * - All exposed tools (whats_next, proceed_to_phase)
22
+ * - Protocol compliance and message formats
23
+ * - Error handling and edge cases
24
+ */
25
+ describe('MCP Contract Validation', () => {
26
+ let client: Client;
27
+ let transport: StdioClientTransport;
28
+ let tempProject: TempProject;
29
+ let cleanup: () => Promise<void>;
30
+
31
+ beforeEach(async () => {
32
+ // Create temporary project for testing
33
+ const scenario = await createTempProjectWithDefaultStateMachine();
34
+ tempProject = scenario;
35
+ cleanup = async () => {
36
+ await scenario.cleanup();
37
+ };
38
+
39
+ // Build the server if needed
40
+ const serverPath = path.resolve(__dirname, '../../../cli/dist/index.js');
41
+ const serverExists = await fs
42
+ .access(serverPath)
43
+ .then(() => true)
44
+ .catch(() => false);
45
+
46
+ if (!serverExists) {
47
+ throw new Error(
48
+ `Server not built. Please run 'npm run build' first. Looking for: ${serverPath}`
49
+ );
50
+ }
51
+
52
+ // Create MCP client and transport (this will spawn the server process)
53
+ // Note: Due to limitations in StdioClientTransport, environment variables
54
+ // are not properly passed to the spawned server process, so this test
55
+ // may show INFO level logs despite our attempts to suppress them.
56
+
57
+ // Use a shell script wrapper that explicitly changes the working directory
58
+ // This ensures the server operates in the temporary directory and not the current project directory
59
+ // which is essential for clean test isolation
60
+ const wrapperScriptPath = path.resolve(
61
+ __dirname,
62
+ '../utils/run-server-in-dir.sh'
63
+ );
64
+
65
+ transport = new StdioClientTransport({
66
+ command: wrapperScriptPath,
67
+ args: [tempProject.projectPath, serverPath],
68
+ env: {
69
+ ...process.env,
70
+ LOG_LEVEL: 'ERROR',
71
+ NODE_ENV: 'test',
72
+ VITEST: 'true',
73
+ },
74
+ });
75
+
76
+ client = new Client(
77
+ {
78
+ name: 'mcp-contract-test-client',
79
+ version: '1.0.0',
80
+ },
81
+ {
82
+ capabilities: {
83
+ roots: {},
84
+ sampling: {},
85
+ },
86
+ }
87
+ );
88
+
89
+ // Connect to the server
90
+ await client.connect(transport);
91
+
92
+ // Start development for all MCP contract tests
93
+ await client.callTool({
94
+ name: 'start_development',
95
+ arguments: {
96
+ workflow: 'waterfall',
97
+ commit_behaviour: 'none', // Use 'none' for test isolation
98
+ },
99
+ });
100
+ });
101
+
102
+ afterEach(async () => {
103
+ // Clean up client and transport
104
+ if (client) {
105
+ await client.close();
106
+ }
107
+ if (transport) {
108
+ await transport.close();
109
+ }
110
+ if (cleanup) {
111
+ await cleanup();
112
+ }
113
+ });
114
+
115
+ describe('Server Initialization and Capabilities', () => {
116
+ it('should successfully initialize with proper server info', async () => {
117
+ // The connection in beforeEach validates basic initialization
118
+ // Here we verify the server info was properly exchanged
119
+ expect(client).toBeDefined();
120
+
121
+ // Verify we can make basic requests (this confirms initialization succeeded)
122
+ const tools = await client.listTools();
123
+ expect(tools).toBeDefined();
124
+ });
125
+
126
+ it('should expose correct capabilities', async () => {
127
+ // Test that server exposes the expected capabilities
128
+ // by verifying we can call the expected methods without errors
129
+
130
+ // Should support tools
131
+ const tools = await client.listTools();
132
+ expect(tools.tools).toBeDefined();
133
+ expect(Array.isArray(tools.tools)).toBe(true);
134
+
135
+ // Should support resources
136
+ const resources = await client.listResources();
137
+ expect(resources.resources).toBeDefined();
138
+ expect(Array.isArray(resources.resources)).toBe(true);
139
+ });
140
+ });
141
+
142
+ describe('Tools Contract Validation', () => {
143
+ it('should expose whats_next tool with correct schema', async () => {
144
+ const tools = await client.listTools();
145
+
146
+ const whatsNextTool = tools.tools.find(
147
+ tool => tool.name === 'whats_next'
148
+ );
149
+ expect(whatsNextTool).toBeDefined();
150
+ expect(whatsNextTool!.description).toBeTruthy();
151
+ expect(whatsNextTool!.inputSchema).toBeDefined();
152
+
153
+ // Verify the input schema allows the expected parameters
154
+ const schema = whatsNextTool!.inputSchema;
155
+ expect(schema.type).toBe('object');
156
+ expect(schema.properties).toBeDefined();
157
+
158
+ // Should accept optional parameters like user_input, context, etc.
159
+ const properties = schema.properties as Record<string, unknown>;
160
+ expect(properties.user_input).toBeDefined();
161
+ expect(properties.context).toBeDefined();
162
+ expect(properties.conversation_summary).toBeDefined();
163
+ expect(properties.recent_messages).toBeDefined();
164
+ });
165
+
166
+ it('should expose proceed_to_phase tool with correct schema', async () => {
167
+ const tools = await client.listTools();
168
+
169
+ const proceedTool = tools.tools.find(
170
+ tool => tool.name === 'proceed_to_phase'
171
+ );
172
+ expect(proceedTool).toBeDefined();
173
+ expect(proceedTool!.description).toBeTruthy();
174
+ expect(proceedTool!.inputSchema).toBeDefined();
175
+
176
+ const schema = proceedTool!.inputSchema;
177
+ expect(schema.type).toBe('object');
178
+ expect(schema.properties).toBeDefined();
179
+
180
+ const properties = schema.properties as Record<string, unknown>;
181
+ expect(properties.target_phase).toBeDefined();
182
+ expect(properties.reason).toBeDefined();
183
+
184
+ // target_phase should be required
185
+ expect(schema.required).toContain('target_phase');
186
+ });
187
+
188
+ it('should execute whats_next tool successfully', async () => {
189
+ const result = await client.callTool({
190
+ name: 'whats_next',
191
+ arguments: {
192
+ user_input: 'implement authentication system',
193
+ },
194
+ });
195
+
196
+ expect(result.content).toBeDefined();
197
+ expect(Array.isArray(result.content)).toBe(true);
198
+ expect(result.content.length).toBeGreaterThan(0);
199
+
200
+ // Should return text content with phase information
201
+ const textContent = result.content.find(c => c.type === 'text');
202
+ expect(textContent).toBeDefined();
203
+ expect(textContent!.text).toBeTruthy();
204
+
205
+ // Response should contain structured data about the phase
206
+ const responseText = textContent!.text;
207
+ expect(responseText).toContain('phase');
208
+ expect(responseText).toContain('instructions');
209
+ expect(responseText).toContain('conversation_id');
210
+ });
211
+
212
+ it('should execute proceed_to_phase tool successfully', async () => {
213
+ // First call whats_next to establish a conversation
214
+ await client.callTool({
215
+ name: 'whats_next',
216
+ arguments: {
217
+ user_input: 'start project',
218
+ },
219
+ });
220
+
221
+ // Then proceed to a different phase
222
+ const result = await client.callTool({
223
+ name: 'proceed_to_phase',
224
+ arguments: {
225
+ target_phase: 'design',
226
+ reason: 'requirements complete',
227
+ review_state: 'not-required',
228
+ },
229
+ });
230
+
231
+ expect(result.content).toBeDefined();
232
+ expect(Array.isArray(result.content)).toBe(true);
233
+ expect(result.content.length).toBeGreaterThan(0);
234
+
235
+ const textContent = result.content.find(c => c.type === 'text');
236
+ expect(textContent).toBeDefined();
237
+ expect(textContent!.text).toBeTruthy();
238
+
239
+ const responseText = textContent!.text;
240
+ expect(responseText).toContain('phase');
241
+ expect(responseText).toContain('design');
242
+ });
243
+
244
+ it('should handle tool errors gracefully', async () => {
245
+ // Test invalid phase transition
246
+ const result = await client.callTool({
247
+ name: 'proceed_to_phase',
248
+ arguments: {
249
+ target_phase: 'invalid_phase',
250
+ reason: 'test error handling',
251
+ review_state: 'not-required',
252
+ },
253
+ });
254
+
255
+ // Should return error information rather than throwing
256
+ expect(result.content).toBeDefined();
257
+ expect(result.isError).toBe(true);
258
+ });
259
+ });
260
+
261
+ describe('Resources Contract Validation', () => {
262
+ it('should expose plan://current resource', async () => {
263
+ const resources = await client.listResources();
264
+
265
+ const planResource = resources.resources.find(
266
+ r => r.uri === 'plan://current'
267
+ );
268
+ expect(planResource).toBeDefined();
269
+ expect(planResource!.name).toBeTruthy();
270
+ expect(planResource!.description).toBeTruthy();
271
+ expect(planResource!.mimeType).toBe('text/markdown');
272
+ });
273
+
274
+ it('should expose state://current resource', async () => {
275
+ const resources = await client.listResources();
276
+
277
+ const stateResource = resources.resources.find(
278
+ r => r.uri === 'state://current'
279
+ );
280
+ expect(stateResource).toBeDefined();
281
+ expect(stateResource!.name).toBeTruthy();
282
+ expect(stateResource!.description).toBeTruthy();
283
+ expect(stateResource!.mimeType).toBe('application/json');
284
+ });
285
+
286
+ it('should read plan resource successfully', async () => {
287
+ // First establish a conversation to ensure plan file exists
288
+ await client.callTool({
289
+ name: 'whats_next',
290
+ arguments: {
291
+ user_input: 'test plan resource',
292
+ },
293
+ });
294
+
295
+ const result = await client.readResource({
296
+ uri: 'plan://current',
297
+ });
298
+
299
+ expect(result.contents).toBeDefined();
300
+ expect(Array.isArray(result.contents)).toBe(true);
301
+ expect(result.contents.length).toBeGreaterThan(0);
302
+
303
+ const content = result.contents[0];
304
+ expect(content.uri).toBe('plan://current');
305
+ expect(content.mimeType).toBe('text/markdown');
306
+ expect(content.text).toBeTruthy();
307
+
308
+ // Should contain markdown plan structure
309
+ expect(content.text).toContain('# Development Plan');
310
+ expect(content.text).toContain('## Goal');
311
+ });
312
+
313
+ it('should read state resource successfully', async () => {
314
+ // First establish a conversation to ensure state exists
315
+ await client.callTool({
316
+ name: 'whats_next',
317
+ arguments: {
318
+ user_input: 'test state resource',
319
+ },
320
+ });
321
+
322
+ const result = await client.readResource({
323
+ uri: 'state://current',
324
+ });
325
+
326
+ expect(result.contents).toBeDefined();
327
+ expect(Array.isArray(result.contents)).toBe(true);
328
+ expect(result.contents.length).toBeGreaterThan(0);
329
+
330
+ const content = result.contents[0];
331
+ expect(content.uri).toBe('state://current');
332
+ expect(content.mimeType).toBe('application/json');
333
+ expect(content.text).toBeTruthy();
334
+
335
+ // Should contain valid JSON with state information
336
+ const stateData = JSON.parse(content.text);
337
+ expect(stateData.conversationId).toBeTruthy();
338
+ expect(stateData.currentPhase).toBeTruthy();
339
+ expect(stateData.projectPath).toBeTruthy();
340
+ });
341
+
342
+ it('should handle resource read errors gracefully', async () => {
343
+ // Test reading non-existent resource
344
+ try {
345
+ await client.readResource({
346
+ uri: 'nonexistent://resource',
347
+ });
348
+ // Should not reach here
349
+ expect(true).toBe(false);
350
+ } catch (error) {
351
+ // Should throw an appropriate error
352
+ expect(error).toBeDefined();
353
+ }
354
+ });
355
+ });
356
+
357
+ describe('Protocol Compliance', () => {
358
+ it('should handle concurrent requests properly', async () => {
359
+ // Make multiple concurrent requests to test protocol handling
360
+ const promises = [
361
+ client.callTool({
362
+ name: 'whats_next',
363
+ arguments: { user_input: 'concurrent test 1' },
364
+ }),
365
+ client.callTool({
366
+ name: 'whats_next',
367
+ arguments: { user_input: 'concurrent test 2' },
368
+ }),
369
+ client.listTools(),
370
+ client.listResources(),
371
+ ];
372
+
373
+ const results = await Promise.all(promises);
374
+
375
+ // All requests should succeed
376
+ for (const result of results) {
377
+ expect(result).toBeDefined();
378
+ }
379
+
380
+ // Tool results should have content
381
+ expect(results[0].content).toBeDefined();
382
+ expect(results[1].content).toBeDefined();
383
+
384
+ // List results should have arrays
385
+ expect(Array.isArray(results[2].tools)).toBe(true);
386
+ expect(Array.isArray(results[3].resources)).toBe(true);
387
+ });
388
+
389
+ it('should maintain conversation state across multiple interactions', async () => {
390
+ // First interaction
391
+ const result1 = await client.callTool({
392
+ name: 'whats_next',
393
+ arguments: {
394
+ user_input: 'start new project',
395
+ },
396
+ });
397
+
398
+ const response1 = JSON.parse(result1.content[0].text);
399
+ const conversationId1 = response1.conversation_id;
400
+
401
+ // Second interaction in the same session
402
+ const result2 = await client.callTool({
403
+ name: 'whats_next',
404
+ arguments: {
405
+ user_input: 'continue project',
406
+ },
407
+ });
408
+
409
+ const response2 = JSON.parse(result2.content[0].text);
410
+ const conversationId2 = response2.conversation_id;
411
+
412
+ // Should maintain same conversation ID within the same MCP session
413
+ // Note: Each MCP client connection maintains its own conversation context
414
+ expect(conversationId1).toBe(conversationId2);
415
+ });
416
+
417
+ it('should handle malformed requests appropriately', async () => {
418
+ // Test with missing required parameters
419
+ try {
420
+ await client.callTool({
421
+ name: 'proceed_to_phase',
422
+ arguments: {
423
+ // Missing required target_phase
424
+ reason: 'test malformed request',
425
+ review_state: 'not-required',
426
+ },
427
+ });
428
+ // Should not reach here if validation works
429
+ expect(true).toBe(false);
430
+ } catch (error) {
431
+ // Should handle validation error appropriately
432
+ expect(error).toBeDefined();
433
+ }
434
+ });
435
+ });
436
+
437
+ describe('Integration Scenarios', () => {
438
+ it('should support complete development workflow', async () => {
439
+ // Start with requirements
440
+ const start = await client.callTool({
441
+ name: 'whats_next',
442
+ arguments: {
443
+ user_input: 'implement user authentication system',
444
+ context: 'new feature development',
445
+ },
446
+ });
447
+
448
+ const startResponse = JSON.parse(start.content[0].text);
449
+
450
+ // Check if we're using a custom state machine by looking at the phase
451
+ // Default state machine uses: idle, requirements, design, implementation, qa, testing, complete
452
+ // If we get other phases, skip the test as it's using a custom state machine
453
+ const defaultPhases = [
454
+ 'idle',
455
+ 'requirements',
456
+ 'design',
457
+ 'implementation',
458
+ 'qa',
459
+ 'testing',
460
+ 'complete',
461
+ ];
462
+
463
+ if (!defaultPhases.includes(startResponse.phase)) {
464
+ console.log(
465
+ `Skipping test: Custom state machine detected (phase: ${startResponse.phase})`
466
+ );
467
+ return; // Skip test
468
+ }
469
+
470
+ // The server intelligently determines the appropriate starting phase
471
+ expect(['requirements', 'design']).toContain(startResponse.phase);
472
+
473
+ // Transition to design (if not already there)
474
+ let currentPhase = startResponse.phase;
475
+ if (currentPhase !== 'design') {
476
+ const design = await client.callTool({
477
+ name: 'proceed_to_phase',
478
+ arguments: {
479
+ target_phase: 'design',
480
+ reason: 'requirements analysis complete',
481
+ review_state: 'not-required',
482
+ },
483
+ });
484
+
485
+ const designResponse = JSON.parse(design.content[0].text);
486
+ expect(designResponse.phase).toBe('design');
487
+ currentPhase = 'design';
488
+ }
489
+
490
+ // Verify state resource reflects the current phase
491
+ const stateResult = await client.readResource({
492
+ uri: 'state://current',
493
+ });
494
+
495
+ const stateData = JSON.parse(stateResult.contents[0].text);
496
+ expect(stateData.currentPhase).toBe(currentPhase);
497
+
498
+ // Verify plan resource contains relevant phase information
499
+ const planResult = await client.readResource({
500
+ uri: 'plan://current',
501
+ });
502
+
503
+ const planContent = planResult.contents[0].text;
504
+ expect(planContent).toContain('# Development Plan');
505
+ expect(planContent).toContain('## Goal');
506
+ });
507
+
508
+ it('should handle complex conversation context', async () => {
509
+ const result = await client.callTool({
510
+ name: 'whats_next',
511
+ arguments: {
512
+ user_input: 'implement OAuth integration',
513
+ context: 'user wants third-party authentication',
514
+ conversation_summary:
515
+ 'Discussed authentication options, user prefers OAuth with Google and GitHub',
516
+ recent_messages: [
517
+ {
518
+ role: 'user',
519
+ content: 'What authentication options do we have?',
520
+ },
521
+ {
522
+ role: 'assistant',
523
+ content: 'We can use OAuth, JWT, or traditional sessions',
524
+ },
525
+ {
526
+ role: 'user',
527
+ content: 'OAuth sounds good, especially Google and GitHub',
528
+ },
529
+ ],
530
+ },
531
+ });
532
+
533
+ expect(result.content).toBeDefined();
534
+ const response = JSON.parse(result.content[0].text);
535
+ expect(response.phase).toBeTruthy();
536
+ expect(response.instructions).toBeTruthy();
537
+ expect(response.conversation_id).toBeTruthy();
538
+ });
539
+ });
540
+ });