@prmichaelsen/task-mcp 0.2.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.
Files changed (142) hide show
  1. package/.env.example +19 -0
  2. package/AGENT.md +1165 -0
  3. package/CHANGELOG.md +72 -0
  4. package/agent/commands/acp.commit.md +511 -0
  5. package/agent/commands/acp.init.md +376 -0
  6. package/agent/commands/acp.package-install.md +347 -0
  7. package/agent/commands/acp.proceed.md +311 -0
  8. package/agent/commands/acp.report.md +392 -0
  9. package/agent/commands/acp.status.md +280 -0
  10. package/agent/commands/acp.sync.md +323 -0
  11. package/agent/commands/acp.update.md +301 -0
  12. package/agent/commands/acp.validate.md +385 -0
  13. package/agent/commands/acp.version-check-for-updates.md +275 -0
  14. package/agent/commands/acp.version-check.md +190 -0
  15. package/agent/commands/acp.version-update.md +288 -0
  16. package/agent/commands/command.template.md +273 -0
  17. package/agent/commands/git.commit.md +511 -0
  18. package/agent/commands/git.init.md +513 -0
  19. package/agent/design/.gitkeep +0 -0
  20. package/agent/design/acp-task-execution-requirements.md +555 -0
  21. package/agent/design/api-dto-design.md +394 -0
  22. package/agent/design/code-extraction-guide.md +827 -0
  23. package/agent/design/design.template.md +136 -0
  24. package/agent/design/requirements.template.md +387 -0
  25. package/agent/design/rest-api-integration.md +489 -0
  26. package/agent/design/sdk-export-requirements.md +549 -0
  27. package/agent/milestones/.gitkeep +0 -0
  28. package/agent/milestones/milestone-1-{title}.template.md +206 -0
  29. package/agent/milestones/milestone-2-task-infrastructure.md +232 -0
  30. package/agent/milestones/milestone-4-autonomous-execution.md +235 -0
  31. package/agent/patterns/.gitkeep +0 -0
  32. package/agent/patterns/bootstrap.md +1271 -0
  33. package/agent/patterns/bootstrap.template.md +1237 -0
  34. package/agent/patterns/pattern.template.md +364 -0
  35. package/agent/progress.template.yaml +158 -0
  36. package/agent/progress.yaml +375 -0
  37. package/agent/scripts/check-for-updates.sh +88 -0
  38. package/agent/scripts/install.sh +157 -0
  39. package/agent/scripts/uninstall.sh +75 -0
  40. package/agent/scripts/update.sh +139 -0
  41. package/agent/scripts/version.sh +35 -0
  42. package/agent/tasks/.gitkeep +0 -0
  43. package/agent/tasks/task-1-{title}.template.md +225 -0
  44. package/agent/tasks/task-86-task-data-model-schemas.md +143 -0
  45. package/agent/tasks/task-87-task-database-service.md +220 -0
  46. package/agent/tasks/task-88-firebase-client-wrapper.md +139 -0
  47. package/agent/tasks/task-88-task-execution-engine.md +277 -0
  48. package/agent/tasks/task-89-mcp-server-implementation.md +197 -0
  49. package/agent/tasks/task-90-build-configuration.md +146 -0
  50. package/agent/tasks/task-91-deployment-configuration.md +128 -0
  51. package/coverage/base.css +224 -0
  52. package/coverage/block-navigation.js +87 -0
  53. package/coverage/favicon.png +0 -0
  54. package/coverage/index.html +191 -0
  55. package/coverage/lcov-report/base.css +224 -0
  56. package/coverage/lcov-report/block-navigation.js +87 -0
  57. package/coverage/lcov-report/favicon.png +0 -0
  58. package/coverage/lcov-report/index.html +191 -0
  59. package/coverage/lcov-report/prettify.css +1 -0
  60. package/coverage/lcov-report/prettify.js +2 -0
  61. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  62. package/coverage/lcov-report/sorter.js +210 -0
  63. package/coverage/lcov-report/src/client.ts.html +1030 -0
  64. package/coverage/lcov-report/src/constant/collections.ts.html +469 -0
  65. package/coverage/lcov-report/src/constant/index.html +116 -0
  66. package/coverage/lcov-report/src/dto/index.html +116 -0
  67. package/coverage/lcov-report/src/dto/transformers.ts.html +568 -0
  68. package/coverage/lcov-report/src/index.html +146 -0
  69. package/coverage/lcov-report/src/schemas/index.html +116 -0
  70. package/coverage/lcov-report/src/schemas/task.ts.html +547 -0
  71. package/coverage/lcov-report/src/server-factory.ts.html +418 -0
  72. package/coverage/lcov-report/src/server.ts.html +289 -0
  73. package/coverage/lcov-report/src/services/index.html +116 -0
  74. package/coverage/lcov-report/src/services/task-database.service.ts.html +1495 -0
  75. package/coverage/lcov-report/src/tools/index.html +236 -0
  76. package/coverage/lcov-report/src/tools/index.ts.html +292 -0
  77. package/coverage/lcov-report/src/tools/task-add-message.ts.html +277 -0
  78. package/coverage/lcov-report/src/tools/task-complete-task-item.ts.html +343 -0
  79. package/coverage/lcov-report/src/tools/task-create-milestone.ts.html +286 -0
  80. package/coverage/lcov-report/src/tools/task-create-task-item.ts.html +358 -0
  81. package/coverage/lcov-report/src/tools/task-get-next-step.ts.html +460 -0
  82. package/coverage/lcov-report/src/tools/task-get-status.ts.html +316 -0
  83. package/coverage/lcov-report/src/tools/task-report-completion.ts.html +343 -0
  84. package/coverage/lcov-report/src/tools/task-update-progress.ts.html +232 -0
  85. package/coverage/lcov.info +974 -0
  86. package/coverage/prettify.css +1 -0
  87. package/coverage/prettify.js +2 -0
  88. package/coverage/sort-arrow-sprite.png +0 -0
  89. package/coverage/sorter.js +210 -0
  90. package/coverage/src/client.ts.html +1030 -0
  91. package/coverage/src/constant/collections.ts.html +469 -0
  92. package/coverage/src/constant/index.html +116 -0
  93. package/coverage/src/dto/index.html +116 -0
  94. package/coverage/src/dto/transformers.ts.html +568 -0
  95. package/coverage/src/index.html +146 -0
  96. package/coverage/src/schemas/index.html +116 -0
  97. package/coverage/src/schemas/task.ts.html +547 -0
  98. package/coverage/src/server-factory.ts.html +418 -0
  99. package/coverage/src/server.ts.html +289 -0
  100. package/coverage/src/services/index.html +116 -0
  101. package/coverage/src/services/task-database.service.ts.html +1495 -0
  102. package/coverage/src/tools/index.html +236 -0
  103. package/coverage/src/tools/index.ts.html +292 -0
  104. package/coverage/src/tools/task-add-message.ts.html +277 -0
  105. package/coverage/src/tools/task-complete-task-item.ts.html +343 -0
  106. package/coverage/src/tools/task-create-milestone.ts.html +286 -0
  107. package/coverage/src/tools/task-create-task-item.ts.html +358 -0
  108. package/coverage/src/tools/task-get-next-step.ts.html +460 -0
  109. package/coverage/src/tools/task-get-status.ts.html +316 -0
  110. package/coverage/src/tools/task-report-completion.ts.html +343 -0
  111. package/coverage/src/tools/task-update-progress.ts.html +232 -0
  112. package/firestore.rules +95 -0
  113. package/jest.config.js +31 -0
  114. package/package.json +67 -0
  115. package/src/client.spec.ts +199 -0
  116. package/src/client.ts +315 -0
  117. package/src/constant/collections.ts +128 -0
  118. package/src/dto/index.ts +47 -0
  119. package/src/dto/task-api.dto.ts +219 -0
  120. package/src/dto/transformers.spec.ts +462 -0
  121. package/src/dto/transformers.ts +161 -0
  122. package/src/schemas/task.ts +154 -0
  123. package/src/server-factory.spec.ts +70 -0
  124. package/src/server-factory.ts +111 -0
  125. package/src/server.ts +68 -0
  126. package/src/services/task-database.service.e2e.ts +116 -0
  127. package/src/services/task-database.service.spec.ts +479 -0
  128. package/src/services/task-database.service.ts +470 -0
  129. package/src/test-schemas.ts +161 -0
  130. package/src/tools/index.ts +69 -0
  131. package/src/tools/task-add-message.ts +64 -0
  132. package/src/tools/task-complete-task-item.ts +86 -0
  133. package/src/tools/task-create-milestone.ts +67 -0
  134. package/src/tools/task-create-task-item.ts +91 -0
  135. package/src/tools/task-get-next-step.spec.ts +136 -0
  136. package/src/tools/task-get-next-step.ts +125 -0
  137. package/src/tools/task-get-status.spec.ts +213 -0
  138. package/src/tools/task-get-status.ts +77 -0
  139. package/src/tools/task-report-completion.ts +86 -0
  140. package/src/tools/task-update-progress.ts +49 -0
  141. package/src/tools/tools.spec.ts +194 -0
  142. package/tsconfig.json +31 -0
@@ -0,0 +1,470 @@
1
+ /**
2
+ * Task Database Service
3
+ *
4
+ * Service layer for all Firestore operations related to tasks.
5
+ * Handles CRUD operations, task messages, and progress tracking.
6
+ */
7
+
8
+ import { getFirestore, Firestore, FieldValue } from 'firebase-admin/firestore'
9
+ import { getUserTasks, getUserTaskMessages, getUserTask, getUserTaskMessage } from '../constant/collections.js'
10
+ import { TaskSchema, type Task, type Milestone, type TaskItem, type TaskMessage } from '../schemas/task.js'
11
+
12
+ export class TaskDatabaseService {
13
+ private static db: Firestore | null = null
14
+
15
+ /**
16
+ * Initialize the database connection
17
+ */
18
+ static initialize(db?: Firestore): void {
19
+ this.db = db || getFirestore()
20
+ }
21
+
22
+ /**
23
+ * Get the Firestore instance
24
+ */
25
+ private static getDb(): Firestore {
26
+ if (!this.db) {
27
+ this.db = getFirestore()
28
+ }
29
+ return this.db
30
+ }
31
+
32
+ // ==================== CRUD Operations ====================
33
+
34
+ /**
35
+ * Create a new task
36
+ */
37
+ static async createTask(
38
+ userId: string,
39
+ title: string,
40
+ description: string,
41
+ config?: Partial<Task['config']>,
42
+ metadata?: Task['metadata']
43
+ ): Promise<Task> {
44
+ const db = this.getDb()
45
+ const now = new Date().toISOString()
46
+
47
+ const taskData: Omit<Task, 'id'> = {
48
+ user_id: userId,
49
+ title,
50
+ description,
51
+ status: 'not_started',
52
+ created_at: now,
53
+ updated_at: now,
54
+ progress: {
55
+ current_milestone: '',
56
+ current_task: '',
57
+ overall_percentage: 0,
58
+ milestones: [],
59
+ tasks: {}
60
+ },
61
+ execution: {
62
+ api_messages: [],
63
+ task_messages: [],
64
+ tool_results: []
65
+ },
66
+ config: {
67
+ system_prompt: config?.system_prompt || '',
68
+ auto_approve: config?.auto_approve ?? true,
69
+ max_iterations: config?.max_iterations || 500
70
+ },
71
+ metadata: metadata
72
+ }
73
+
74
+ const tasksPath = getUserTasks(userId)
75
+ const docRef = await db.collection(tasksPath).add(taskData)
76
+
77
+ return {
78
+ id: docRef.id,
79
+ ...taskData
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Get a task by ID
85
+ */
86
+ static async getTask(userId: string, taskId: string): Promise<Task | null> {
87
+ const db = this.getDb()
88
+ const taskPath = getUserTask(userId, taskId)
89
+ const doc = await db.doc(taskPath).get()
90
+
91
+ if (!doc.exists) {
92
+ return null
93
+ }
94
+
95
+ const data = doc.data()
96
+ const result = TaskSchema.safeParse({ id: doc.id, ...data })
97
+
98
+ if (!result.success) {
99
+ console.error('Task validation failed:', result.error)
100
+ return null
101
+ }
102
+
103
+ return result.data
104
+ }
105
+
106
+ /**
107
+ * Update a task
108
+ */
109
+ static async updateTask(
110
+ userId: string,
111
+ taskId: string,
112
+ updates: Partial<Omit<Task, 'id' | 'user_id' | 'created_at'>>
113
+ ): Promise<void> {
114
+ const db = this.getDb()
115
+ const taskPath = getUserTask(userId, taskId)
116
+
117
+ await db.doc(taskPath).update({
118
+ ...updates,
119
+ updated_at: new Date().toISOString()
120
+ })
121
+ }
122
+
123
+ /**
124
+ * Update task status
125
+ */
126
+ static async updateTaskStatus(
127
+ userId: string,
128
+ taskId: string,
129
+ status: Task['status']
130
+ ): Promise<void> {
131
+ const db = this.getDb()
132
+ const taskPath = getUserTask(userId, taskId)
133
+ const updates: any = {
134
+ status,
135
+ updated_at: new Date().toISOString()
136
+ }
137
+
138
+ // Set timestamps based on status
139
+ if (status === 'in_progress') {
140
+ updates.started_at = new Date().toISOString()
141
+ } else if (status === 'completed' || status === 'failed') {
142
+ updates.completed_at = new Date().toISOString()
143
+ }
144
+
145
+ await db.doc(taskPath).update(updates)
146
+ }
147
+
148
+ /**
149
+ * Delete a task
150
+ */
151
+ static async deleteTask(userId: string, taskId: string): Promise<void> {
152
+ const db = this.getDb()
153
+ const taskPath = getUserTask(userId, taskId)
154
+
155
+ // Delete all messages first
156
+ const messagesPath = getUserTaskMessages(userId, taskId)
157
+ const messagesSnapshot = await db.collection(messagesPath).get()
158
+
159
+ const batch = db.batch()
160
+ messagesSnapshot.docs.forEach(doc => {
161
+ batch.delete(doc.ref)
162
+ })
163
+
164
+ // Delete the task
165
+ batch.delete(db.doc(taskPath))
166
+
167
+ await batch.commit()
168
+ }
169
+
170
+ /**
171
+ * List all tasks for a user
172
+ */
173
+ static async listTasks(userId: string, limit = 50): Promise<Task[]> {
174
+ const db = this.getDb()
175
+ const tasksPath = getUserTasks(userId)
176
+
177
+ const snapshot = await db.collection(tasksPath)
178
+ .orderBy('created_at', 'desc')
179
+ .limit(limit)
180
+ .get()
181
+
182
+ const tasks: Task[] = []
183
+ snapshot.docs.forEach(doc => {
184
+ const result = TaskSchema.safeParse({ id: doc.id, ...doc.data() })
185
+ if (result.success) {
186
+ tasks.push(result.data)
187
+ }
188
+ })
189
+
190
+ return tasks
191
+ }
192
+
193
+ // ==================== Task Message Operations ====================
194
+
195
+ /**
196
+ * Add a message to a task
197
+ */
198
+ static async addMessage(
199
+ userId: string,
200
+ taskId: string,
201
+ role: 'user' | 'assistant' | 'system',
202
+ content: string,
203
+ metadata?: any
204
+ ): Promise<string> {
205
+ const db = this.getDb()
206
+ const messagesPath = getUserTaskMessages(userId, taskId)
207
+
208
+ const message = {
209
+ task_id: taskId,
210
+ role,
211
+ content,
212
+ timestamp: new Date().toISOString(),
213
+ metadata: metadata || null
214
+ }
215
+
216
+ const docRef = await db.collection(messagesPath).add(message)
217
+ return docRef.id
218
+ }
219
+
220
+ /**
221
+ * Get messages for a task
222
+ */
223
+ static async getMessages(
224
+ userId: string,
225
+ taskId: string,
226
+ limit = 100
227
+ ): Promise<TaskMessage[]> {
228
+ const db = this.getDb()
229
+ const messagesPath = getUserTaskMessages(userId, taskId)
230
+
231
+ const snapshot = await db.collection(messagesPath)
232
+ .orderBy('timestamp', 'asc')
233
+ .limit(limit)
234
+ .get()
235
+
236
+ return snapshot.docs.map(doc => ({
237
+ id: doc.id,
238
+ ...doc.data()
239
+ } as TaskMessage))
240
+ }
241
+
242
+ /**
243
+ * Delete a message
244
+ */
245
+ static async deleteMessage(
246
+ userId: string,
247
+ taskId: string,
248
+ messageId: string
249
+ ): Promise<void> {
250
+ const db = this.getDb()
251
+ const messagePath = getUserTaskMessage(userId, taskId, messageId)
252
+ await db.doc(messagePath).delete()
253
+ }
254
+
255
+ // ==================== Progress Operations ====================
256
+
257
+ /**
258
+ * Update overall progress percentage
259
+ */
260
+ static async updateOverallProgress(
261
+ userId: string,
262
+ taskId: string,
263
+ percentage: number
264
+ ): Promise<void> {
265
+ const db = this.getDb()
266
+ const taskPath = getUserTask(userId, taskId)
267
+
268
+ await db.doc(taskPath).update({
269
+ 'progress.overall_percentage': Math.min(100, Math.max(0, percentage)),
270
+ updated_at: new Date().toISOString()
271
+ })
272
+ }
273
+
274
+ /**
275
+ * Create a milestone
276
+ */
277
+ static async createMilestone(
278
+ userId: string,
279
+ taskId: string,
280
+ milestone: Milestone
281
+ ): Promise<void> {
282
+ const db = this.getDb()
283
+ const taskPath = getUserTask(userId, taskId)
284
+
285
+ await db.doc(taskPath).update({
286
+ 'progress.milestones': FieldValue.arrayUnion(milestone),
287
+ updated_at: new Date().toISOString()
288
+ })
289
+ }
290
+
291
+ /**
292
+ * Update a milestone
293
+ */
294
+ static async updateMilestone(
295
+ userId: string,
296
+ taskId: string,
297
+ milestoneId: string,
298
+ updates: Partial<Milestone>
299
+ ): Promise<void> {
300
+ const task = await this.getTask(userId, taskId)
301
+ if (!task) throw new Error('Task not found')
302
+
303
+ const milestoneIndex = task.progress.milestones.findIndex(m => m.id === milestoneId)
304
+ if (milestoneIndex === -1) throw new Error('Milestone not found')
305
+
306
+ task.progress.milestones[milestoneIndex] = {
307
+ ...task.progress.milestones[milestoneIndex],
308
+ ...updates
309
+ }
310
+
311
+ const db = this.getDb()
312
+ const taskPath = getUserTask(userId, taskId)
313
+
314
+ await db.doc(taskPath).update({
315
+ 'progress.milestones': task.progress.milestones,
316
+ updated_at: new Date().toISOString()
317
+ })
318
+ }
319
+
320
+ /**
321
+ * Complete a milestone
322
+ */
323
+ static async completeMilestone(
324
+ userId: string,
325
+ taskId: string,
326
+ milestoneId: string
327
+ ): Promise<void> {
328
+ await this.updateMilestone(userId, taskId, milestoneId, {
329
+ status: 'completed',
330
+ progress: 100,
331
+ completed_at: new Date().toISOString()
332
+ })
333
+ }
334
+
335
+ /**
336
+ * Create a task item
337
+ */
338
+ static async createTaskItem(
339
+ userId: string,
340
+ taskId: string,
341
+ milestoneId: string,
342
+ taskItem: TaskItem
343
+ ): Promise<void> {
344
+ const task = await this.getTask(userId, taskId)
345
+ if (!task) throw new Error('Task not found')
346
+
347
+ if (!task.progress.tasks[milestoneId]) {
348
+ task.progress.tasks[milestoneId] = []
349
+ }
350
+
351
+ task.progress.tasks[milestoneId].push(taskItem)
352
+
353
+ const db = this.getDb()
354
+ const taskPath = getUserTask(userId, taskId)
355
+
356
+ await db.doc(taskPath).update({
357
+ 'progress.tasks': task.progress.tasks,
358
+ updated_at: new Date().toISOString()
359
+ })
360
+ }
361
+
362
+ /**
363
+ * Update a task item
364
+ */
365
+ static async updateTaskItem(
366
+ userId: string,
367
+ taskId: string,
368
+ milestoneId: string,
369
+ taskItemId: string,
370
+ updates: Partial<TaskItem>
371
+ ): Promise<void> {
372
+ const task = await this.getTask(userId, taskId)
373
+ if (!task) throw new Error('Task not found')
374
+
375
+ const items = task.progress.tasks[milestoneId]
376
+ if (!items) throw new Error('Milestone not found')
377
+
378
+ const itemIndex = items.findIndex(item => item.id === taskItemId)
379
+ if (itemIndex === -1) throw new Error('Task item not found')
380
+
381
+ items[itemIndex] = {
382
+ ...items[itemIndex],
383
+ ...updates
384
+ }
385
+
386
+ const db = this.getDb()
387
+ const taskPath = getUserTask(userId, taskId)
388
+
389
+ await db.doc(taskPath).update({
390
+ [`progress.tasks.${milestoneId}`]: items,
391
+ updated_at: new Date().toISOString()
392
+ })
393
+ }
394
+
395
+ /**
396
+ * Complete a task item
397
+ */
398
+ static async completeTaskItem(
399
+ userId: string,
400
+ taskId: string,
401
+ milestoneId: string,
402
+ taskItemId: string
403
+ ): Promise<void> {
404
+ await this.updateTaskItem(userId, taskId, milestoneId, taskItemId, {
405
+ status: 'completed',
406
+ completed_at: new Date().toISOString()
407
+ })
408
+ }
409
+
410
+ // ==================== Query Methods ====================
411
+
412
+ /**
413
+ * Get tasks by status
414
+ */
415
+ static async getTasksByStatus(
416
+ userId: string,
417
+ status: Task['status'],
418
+ limit = 50
419
+ ): Promise<Task[]> {
420
+ const db = this.getDb()
421
+ const tasksPath = getUserTasks(userId)
422
+
423
+ const snapshot = await db.collection(tasksPath)
424
+ .where('status', '==', status)
425
+ .orderBy('updated_at', 'desc')
426
+ .limit(limit)
427
+ .get()
428
+
429
+ const tasks: Task[] = []
430
+ snapshot.docs.forEach(doc => {
431
+ const result = TaskSchema.safeParse({ id: doc.id, ...doc.data() })
432
+ if (result.success) {
433
+ tasks.push(result.data)
434
+ }
435
+ })
436
+
437
+ return tasks
438
+ }
439
+
440
+ /**
441
+ * Get active tasks (in_progress status)
442
+ */
443
+ static async getActiveTasks(userId: string, limit = 50): Promise<Task[]> {
444
+ return this.getTasksByStatus(userId, 'in_progress', limit)
445
+ }
446
+
447
+ /**
448
+ * Get completed tasks
449
+ */
450
+ static async getCompletedTasks(userId: string, limit = 50): Promise<Task[]> {
451
+ return this.getTasksByStatus(userId, 'completed', limit)
452
+ }
453
+
454
+ /**
455
+ * Search tasks by title
456
+ */
457
+ static async searchTasksByTitle(
458
+ userId: string,
459
+ searchTerm: string,
460
+ limit = 50
461
+ ): Promise<Task[]> {
462
+ const allTasks = await this.listTasks(userId, limit)
463
+
464
+ const searchLower = searchTerm.toLowerCase()
465
+ return allTasks.filter(task =>
466
+ task.title.toLowerCase().includes(searchLower) ||
467
+ task.description.toLowerCase().includes(searchLower)
468
+ )
469
+ }
470
+ }
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Schema Validation Tests
3
+ *
4
+ * Test file to verify Zod schemas work correctly with valid and invalid data
5
+ */
6
+
7
+ import {
8
+ TaskSchema,
9
+ MilestoneSchema,
10
+ TaskItemSchema,
11
+ TaskMessageSchema,
12
+ type Task,
13
+ type Milestone,
14
+ type TaskItem,
15
+ type TaskMessage
16
+ } from './schemas/task.js'
17
+
18
+ // Test valid milestone
19
+ const validMilestone: Milestone = {
20
+ id: 'M1',
21
+ name: 'Foundation',
22
+ description: 'Build core infrastructure',
23
+ status: 'in_progress',
24
+ progress: 50,
25
+ tasks_completed: 2,
26
+ tasks_total: 4,
27
+ started_at: '2026-02-16',
28
+ completed_at: undefined
29
+ }
30
+
31
+ console.log('Testing valid milestone...')
32
+ try {
33
+ const result = MilestoneSchema.parse(validMilestone)
34
+ console.log('✅ Valid milestone passed:', result.name)
35
+ } catch (error) {
36
+ console.error('❌ Valid milestone failed:', error)
37
+ }
38
+
39
+ // Test valid task item
40
+ const validTaskItem: TaskItem = {
41
+ id: 'task-1',
42
+ name: 'Setup project',
43
+ description: 'Initialize Node.js project',
44
+ status: 'completed',
45
+ estimated_hours: 2,
46
+ completed_at: '2026-02-16',
47
+ notes: 'Completed successfully'
48
+ }
49
+
50
+ console.log('\nTesting valid task item...')
51
+ try {
52
+ const result = TaskItemSchema.parse(validTaskItem)
53
+ console.log('✅ Valid task item passed:', result.name)
54
+ } catch (error) {
55
+ console.error('❌ Valid task item failed:', error)
56
+ }
57
+
58
+ // Test valid task
59
+ const validTask: Task = {
60
+ id: 'task-123',
61
+ user_id: 'user-456',
62
+ title: 'Build MCP Server',
63
+ description: 'Create task-mcp server with core tools',
64
+ status: 'in_progress',
65
+ created_at: '2026-02-16T00:00:00Z',
66
+ updated_at: '2026-02-16T12:00:00Z',
67
+ started_at: '2026-02-16T01:00:00Z',
68
+
69
+ progress: {
70
+ current_milestone: 'M1',
71
+ current_task: 'task-1',
72
+ overall_percentage: 25,
73
+ milestones: [validMilestone],
74
+ tasks: {
75
+ 'M1': [validTaskItem]
76
+ }
77
+ },
78
+
79
+ execution: {
80
+ api_messages: [],
81
+ task_messages: [],
82
+ tool_results: []
83
+ },
84
+
85
+ config: {
86
+ system_prompt: 'You are a helpful assistant',
87
+ auto_approve: true,
88
+ max_iterations: 100
89
+ },
90
+
91
+ metadata: {
92
+ conversation_id: 'conv-789',
93
+ tags: ['mcp', 'server']
94
+ }
95
+ }
96
+
97
+ console.log('\nTesting valid task...')
98
+ try {
99
+ const result = TaskSchema.parse(validTask)
100
+ console.log('✅ Valid task passed:', result.title)
101
+ } catch (error) {
102
+ console.error('❌ Valid task failed:', error)
103
+ }
104
+
105
+ // Test valid task message
106
+ const validMessage: TaskMessage = {
107
+ id: 'msg-001',
108
+ task_id: 'task-123',
109
+ role: 'assistant',
110
+ content: 'Task started successfully',
111
+ timestamp: '2026-02-16T12:00:00Z'
112
+ }
113
+
114
+ console.log('\nTesting valid task message...')
115
+ try {
116
+ const result = TaskMessageSchema.parse(validMessage)
117
+ console.log('✅ Valid message passed:', result.content)
118
+ } catch (error) {
119
+ console.error('❌ Valid message failed:', error)
120
+ }
121
+
122
+ // Test invalid data (should fail)
123
+ console.log('\n--- Testing Invalid Data (should fail) ---')
124
+
125
+ // Invalid milestone (progress > 100)
126
+ console.log('\nTesting invalid milestone (progress > 100)...')
127
+ try {
128
+ MilestoneSchema.parse({
129
+ ...validMilestone,
130
+ progress: 150
131
+ })
132
+ console.error('❌ Should have failed but passed')
133
+ } catch (error) {
134
+ console.log('✅ Correctly rejected invalid milestone')
135
+ }
136
+
137
+ // Invalid task (wrong status)
138
+ console.log('\nTesting invalid task (wrong status)...')
139
+ try {
140
+ TaskSchema.parse({
141
+ ...validTask,
142
+ status: 'invalid_status'
143
+ })
144
+ console.error('❌ Should have failed but passed')
145
+ } catch (error) {
146
+ console.log('✅ Correctly rejected invalid task status')
147
+ }
148
+
149
+ // Invalid message (wrong role)
150
+ console.log('\nTesting invalid message (wrong role)...')
151
+ try {
152
+ TaskMessageSchema.parse({
153
+ ...validMessage,
154
+ role: 'invalid_role'
155
+ })
156
+ console.error('❌ Should have failed but passed')
157
+ } catch (error) {
158
+ console.log('✅ Correctly rejected invalid message role')
159
+ }
160
+
161
+ console.log('\n✅ All schema validation tests completed!')
@@ -0,0 +1,69 @@
1
+ /**
2
+ * MCP Tools Index
3
+ *
4
+ * Exports all task management tools for the MCP server.
5
+ */
6
+
7
+ import { taskGetStatusTool, handleTaskGetStatus } from './task-get-status.js'
8
+ import { taskGetNextStepTool, handleTaskGetNextStep } from './task-get-next-step.js'
9
+ import { taskUpdateProgressTool, handleTaskUpdateProgress } from './task-update-progress.js'
10
+ import { taskCompleteTaskItemTool, handleTaskCompleteTaskItem } from './task-complete-task-item.js'
11
+ import { taskCreateMilestoneTool, handleTaskCreateMilestone } from './task-create-milestone.js'
12
+ import { taskCreateTaskItemTool, handleTaskCreateTaskItem } from './task-create-task-item.js'
13
+ import { taskReportCompletionTool, handleTaskReportCompletion } from './task-report-completion.js'
14
+ import { taskAddMessageTool, handleTaskAddMessage } from './task-add-message.js'
15
+
16
+ /**
17
+ * All tool definitions
18
+ */
19
+ export const allTools = [
20
+ taskGetStatusTool,
21
+ taskGetNextStepTool,
22
+ taskUpdateProgressTool,
23
+ taskCompleteTaskItemTool,
24
+ taskCreateMilestoneTool,
25
+ taskCreateTaskItemTool,
26
+ taskReportCompletionTool,
27
+ taskAddMessageTool
28
+ ]
29
+
30
+ /**
31
+ * Tool handlers mapped by tool name
32
+ */
33
+ export const toolHandlers = {
34
+ 'task_get_status': handleTaskGetStatus,
35
+ 'task_get_next_step': handleTaskGetNextStep,
36
+ 'task_update_progress': handleTaskUpdateProgress,
37
+ 'task_complete_task_item': handleTaskCompleteTaskItem,
38
+ 'task_create_milestone': handleTaskCreateMilestone,
39
+ 'task_create_task_item': handleTaskCreateTaskItem,
40
+ 'task_report_completion': handleTaskReportCompletion,
41
+ 'task_add_message': handleTaskAddMessage
42
+ }
43
+
44
+ /**
45
+ * Get tool handler by name
46
+ */
47
+ export function getToolHandler(toolName: string) {
48
+ return toolHandlers[toolName as keyof typeof toolHandlers]
49
+ }
50
+
51
+ // Re-export individual tools for direct imports
52
+ export {
53
+ taskGetStatusTool,
54
+ handleTaskGetStatus,
55
+ taskGetNextStepTool,
56
+ handleTaskGetNextStep,
57
+ taskUpdateProgressTool,
58
+ handleTaskUpdateProgress,
59
+ taskCompleteTaskItemTool,
60
+ handleTaskCompleteTaskItem,
61
+ taskCreateMilestoneTool,
62
+ handleTaskCreateMilestone,
63
+ taskCreateTaskItemTool,
64
+ handleTaskCreateTaskItem,
65
+ taskReportCompletionTool,
66
+ handleTaskReportCompletion,
67
+ taskAddMessageTool,
68
+ handleTaskAddMessage
69
+ }