mcp-sunsama 0.5.2 → 0.7.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/CHANGELOG.md +24 -0
- package/CLAUDE.md +2 -2
- package/README.md +3 -1
- package/dist/main.js +109 -3
- package/dist/schemas.d.ts +26 -5
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +11 -4
- package/dist/schemas.test.d.ts +2 -0
- package/dist/schemas.test.d.ts.map +1 -0
- package/dist/schemas.test.js +486 -0
- package/mcp-inspector.json +1 -1
- package/package.json +2 -2
- package/src/main.ts +130 -2
- package/src/schemas.test.ts +608 -0
- package/src/schemas.ts +14 -4
- package/.claude/settings.local.json +0 -37
|
@@ -0,0 +1,608 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import {
|
|
3
|
+
completionFilterSchema,
|
|
4
|
+
getTasksByDaySchema,
|
|
5
|
+
getTasksBacklogSchema,
|
|
6
|
+
getArchivedTasksSchema,
|
|
7
|
+
getUserSchema,
|
|
8
|
+
getStreamsSchema,
|
|
9
|
+
createTaskSchema,
|
|
10
|
+
updateTaskCompleteSchema,
|
|
11
|
+
deleteTaskSchema,
|
|
12
|
+
updateTaskSnoozeDateSchema,
|
|
13
|
+
updateTaskBacklogSchema,
|
|
14
|
+
userProfileSchema,
|
|
15
|
+
groupSchema,
|
|
16
|
+
userSchema,
|
|
17
|
+
taskSchema,
|
|
18
|
+
streamSchema,
|
|
19
|
+
userResponseSchema,
|
|
20
|
+
tasksResponseSchema,
|
|
21
|
+
streamsResponseSchema,
|
|
22
|
+
errorResponseSchema,
|
|
23
|
+
} from "./schemas.js";
|
|
24
|
+
|
|
25
|
+
describe("Tool Parameter Schemas", () => {
|
|
26
|
+
describe("completionFilterSchema", () => {
|
|
27
|
+
test("should accept valid completion filter values", () => {
|
|
28
|
+
expect(() => completionFilterSchema.parse("all")).not.toThrow();
|
|
29
|
+
expect(() => completionFilterSchema.parse("incomplete")).not.toThrow();
|
|
30
|
+
expect(() => completionFilterSchema.parse("completed")).not.toThrow();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("should reject invalid completion filter values", () => {
|
|
34
|
+
expect(() => completionFilterSchema.parse("invalid")).toThrow();
|
|
35
|
+
expect(() => completionFilterSchema.parse("")).toThrow();
|
|
36
|
+
expect(() => completionFilterSchema.parse(null)).toThrow();
|
|
37
|
+
expect(() => completionFilterSchema.parse(undefined)).toThrow();
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe("getTasksByDaySchema", () => {
|
|
42
|
+
test("should accept valid day format", () => {
|
|
43
|
+
const validInput = {
|
|
44
|
+
day: "2024-01-15",
|
|
45
|
+
timezone: "America/New_York",
|
|
46
|
+
completionFilter: "all" as const,
|
|
47
|
+
};
|
|
48
|
+
expect(() => getTasksByDaySchema.parse(validInput)).not.toThrow();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("should accept minimal required input", () => {
|
|
52
|
+
const minimalInput = { day: "2024-01-15" };
|
|
53
|
+
expect(() => getTasksByDaySchema.parse(minimalInput)).not.toThrow();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("should reject invalid day formats", () => {
|
|
57
|
+
expect(() => getTasksByDaySchema.parse({ day: "2024/01/15" })).toThrow();
|
|
58
|
+
expect(() => getTasksByDaySchema.parse({ day: "01-15-2024" })).toThrow();
|
|
59
|
+
expect(() => getTasksByDaySchema.parse({ day: "2024-1-15" })).toThrow();
|
|
60
|
+
expect(() => getTasksByDaySchema.parse({ day: "2024-01-5" })).toThrow();
|
|
61
|
+
expect(() => getTasksByDaySchema.parse({ day: "" })).toThrow();
|
|
62
|
+
expect(() => getTasksByDaySchema.parse({})).toThrow();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("should reject invalid completion filters", () => {
|
|
66
|
+
expect(() =>
|
|
67
|
+
getTasksByDaySchema.parse({
|
|
68
|
+
day: "2024-01-15",
|
|
69
|
+
completionFilter: "invalid",
|
|
70
|
+
})
|
|
71
|
+
).toThrow();
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe("getTasksBacklogSchema", () => {
|
|
76
|
+
test("should accept empty object", () => {
|
|
77
|
+
expect(() => getTasksBacklogSchema.parse({})).not.toThrow();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("should ignore extra properties", () => {
|
|
81
|
+
expect(() =>
|
|
82
|
+
getTasksBacklogSchema.parse({ extra: "property" })
|
|
83
|
+
).not.toThrow();
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe("getArchivedTasksSchema", () => {
|
|
88
|
+
test("should accept valid pagination parameters", () => {
|
|
89
|
+
const validInput = { offset: 0, limit: 100 };
|
|
90
|
+
expect(() => getArchivedTasksSchema.parse(validInput)).not.toThrow();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("should accept empty object", () => {
|
|
94
|
+
expect(() => getArchivedTasksSchema.parse({})).not.toThrow();
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("should accept valid ranges", () => {
|
|
98
|
+
expect(() => getArchivedTasksSchema.parse({ offset: 0 })).not.toThrow();
|
|
99
|
+
expect(() => getArchivedTasksSchema.parse({ limit: 1 })).not.toThrow();
|
|
100
|
+
expect(() => getArchivedTasksSchema.parse({ limit: 1000 })).not.toThrow();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("should reject invalid offset values", () => {
|
|
104
|
+
expect(() => getArchivedTasksSchema.parse({ offset: -1 })).toThrow();
|
|
105
|
+
expect(() => getArchivedTasksSchema.parse({ offset: 1.5 })).toThrow();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("should reject invalid limit values", () => {
|
|
109
|
+
expect(() => getArchivedTasksSchema.parse({ limit: 0 })).toThrow();
|
|
110
|
+
expect(() => getArchivedTasksSchema.parse({ limit: 1001 })).toThrow();
|
|
111
|
+
expect(() => getArchivedTasksSchema.parse({ limit: -1 })).toThrow();
|
|
112
|
+
expect(() => getArchivedTasksSchema.parse({ limit: 1.5 })).toThrow();
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
describe("getUserSchema", () => {
|
|
117
|
+
test("should accept empty object", () => {
|
|
118
|
+
expect(() => getUserSchema.parse({})).not.toThrow();
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
describe("getStreamsSchema", () => {
|
|
123
|
+
test("should accept empty object", () => {
|
|
124
|
+
expect(() => getStreamsSchema.parse({})).not.toThrow();
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
describe("createTaskSchema", () => {
|
|
129
|
+
test("should accept valid task creation input", () => {
|
|
130
|
+
const validInput = {
|
|
131
|
+
text: "Test task",
|
|
132
|
+
notes: "Task notes",
|
|
133
|
+
streamIds: ["stream1", "stream2"],
|
|
134
|
+
timeEstimate: 60,
|
|
135
|
+
dueDate: "2024-01-15T10:00:00Z",
|
|
136
|
+
snoozeUntil: "2024-01-16T09:00:00Z",
|
|
137
|
+
private: true,
|
|
138
|
+
taskId: "custom-task-id",
|
|
139
|
+
};
|
|
140
|
+
expect(() => createTaskSchema.parse(validInput)).not.toThrow();
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
test("should accept minimal required input", () => {
|
|
144
|
+
const minimalInput = { text: "Test task" };
|
|
145
|
+
expect(() => createTaskSchema.parse(minimalInput)).not.toThrow();
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test("should reject empty text", () => {
|
|
149
|
+
expect(() => createTaskSchema.parse({ text: "" })).toThrow();
|
|
150
|
+
expect(() => createTaskSchema.parse({})).toThrow();
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test("should reject invalid time estimate", () => {
|
|
154
|
+
expect(() =>
|
|
155
|
+
createTaskSchema.parse({ text: "Test", timeEstimate: 0 })
|
|
156
|
+
).toThrow();
|
|
157
|
+
expect(() =>
|
|
158
|
+
createTaskSchema.parse({ text: "Test", timeEstimate: -1 })
|
|
159
|
+
).toThrow();
|
|
160
|
+
expect(() =>
|
|
161
|
+
createTaskSchema.parse({ text: "Test", timeEstimate: 1.5 })
|
|
162
|
+
).toThrow();
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
describe("updateTaskCompleteSchema", () => {
|
|
167
|
+
test("should accept valid task completion input", () => {
|
|
168
|
+
const validInput = {
|
|
169
|
+
taskId: "task-123",
|
|
170
|
+
completeOn: "2024-01-15T10:00:00Z",
|
|
171
|
+
limitResponsePayload: true,
|
|
172
|
+
};
|
|
173
|
+
expect(() => updateTaskCompleteSchema.parse(validInput)).not.toThrow();
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
test("should accept minimal required input", () => {
|
|
177
|
+
const minimalInput = { taskId: "task-123" };
|
|
178
|
+
expect(() => updateTaskCompleteSchema.parse(minimalInput)).not.toThrow();
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
test("should reject empty task ID", () => {
|
|
182
|
+
expect(() => updateTaskCompleteSchema.parse({ taskId: "" })).toThrow();
|
|
183
|
+
expect(() => updateTaskCompleteSchema.parse({})).toThrow();
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
describe("deleteTaskSchema", () => {
|
|
188
|
+
test("should accept valid task deletion input", () => {
|
|
189
|
+
const validInput = {
|
|
190
|
+
taskId: "task-123",
|
|
191
|
+
limitResponsePayload: true,
|
|
192
|
+
wasTaskMerged: false,
|
|
193
|
+
};
|
|
194
|
+
expect(() => deleteTaskSchema.parse(validInput)).not.toThrow();
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
test("should accept minimal required input", () => {
|
|
198
|
+
const minimalInput = { taskId: "task-123" };
|
|
199
|
+
expect(() => deleteTaskSchema.parse(minimalInput)).not.toThrow();
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
test("should reject empty task ID", () => {
|
|
203
|
+
expect(() => deleteTaskSchema.parse({ taskId: "" })).toThrow();
|
|
204
|
+
expect(() => deleteTaskSchema.parse({})).toThrow();
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
describe("updateTaskSnoozeDateSchema", () => {
|
|
209
|
+
test("should accept valid date input", () => {
|
|
210
|
+
const validInput = {
|
|
211
|
+
taskId: "task-123",
|
|
212
|
+
newDay: "2024-01-15",
|
|
213
|
+
timezone: "America/New_York",
|
|
214
|
+
limitResponsePayload: true,
|
|
215
|
+
};
|
|
216
|
+
expect(() => updateTaskSnoozeDateSchema.parse(validInput)).not.toThrow();
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
test("should accept minimal required input", () => {
|
|
221
|
+
const minimalInput = { taskId: "task-123", newDay: "2024-01-15" };
|
|
222
|
+
expect(() => updateTaskSnoozeDateSchema.parse(minimalInput)).not.toThrow();
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
test("should reject empty task ID", () => {
|
|
226
|
+
expect(() =>
|
|
227
|
+
updateTaskSnoozeDateSchema.parse({ taskId: "", newDay: "2024-01-15" })
|
|
228
|
+
).toThrow();
|
|
229
|
+
expect(() =>
|
|
230
|
+
updateTaskSnoozeDateSchema.parse({ newDay: "2024-01-15" })
|
|
231
|
+
).toThrow();
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
test("should reject invalid date formats", () => {
|
|
235
|
+
expect(() =>
|
|
236
|
+
updateTaskSnoozeDateSchema.parse({
|
|
237
|
+
taskId: "task-123",
|
|
238
|
+
newDay: "2024/01/15",
|
|
239
|
+
})
|
|
240
|
+
).toThrow();
|
|
241
|
+
expect(() =>
|
|
242
|
+
updateTaskSnoozeDateSchema.parse({
|
|
243
|
+
taskId: "task-123",
|
|
244
|
+
newDay: "01-15-2024",
|
|
245
|
+
})
|
|
246
|
+
).toThrow();
|
|
247
|
+
expect(() =>
|
|
248
|
+
updateTaskSnoozeDateSchema.parse({
|
|
249
|
+
taskId: "task-123",
|
|
250
|
+
newDay: "invalid-date",
|
|
251
|
+
})
|
|
252
|
+
).toThrow();
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
test("should reject missing newDay", () => {
|
|
256
|
+
expect(() =>
|
|
257
|
+
updateTaskSnoozeDateSchema.parse({ taskId: "task-123" })
|
|
258
|
+
).toThrow();
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
describe("updateTaskBacklogSchema", () => {
|
|
263
|
+
test("should accept valid task backlog input", () => {
|
|
264
|
+
const validInput = {
|
|
265
|
+
taskId: "task-123",
|
|
266
|
+
timezone: "America/New_York",
|
|
267
|
+
limitResponsePayload: true,
|
|
268
|
+
};
|
|
269
|
+
expect(() => updateTaskBacklogSchema.parse(validInput)).not.toThrow();
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
test("should accept minimal required input", () => {
|
|
273
|
+
const minimalInput = { taskId: "task-123" };
|
|
274
|
+
expect(() => updateTaskBacklogSchema.parse(minimalInput)).not.toThrow();
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
test("should reject empty task ID", () => {
|
|
278
|
+
expect(() =>
|
|
279
|
+
updateTaskBacklogSchema.parse({ taskId: "" })
|
|
280
|
+
).toThrow();
|
|
281
|
+
expect(() =>
|
|
282
|
+
updateTaskBacklogSchema.parse({})
|
|
283
|
+
).toThrow();
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
describe("Response Schemas", () => {
|
|
289
|
+
describe("userProfileSchema", () => {
|
|
290
|
+
test("should accept valid user profile", () => {
|
|
291
|
+
const validProfile = {
|
|
292
|
+
_id: "user-123",
|
|
293
|
+
email: "test@example.com",
|
|
294
|
+
firstName: "John",
|
|
295
|
+
lastName: "Doe",
|
|
296
|
+
timezone: "America/New_York",
|
|
297
|
+
avatarUrl: "https://example.com/avatar.jpg",
|
|
298
|
+
};
|
|
299
|
+
expect(() => userProfileSchema.parse(validProfile)).not.toThrow();
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
test("should accept profile without optional fields", () => {
|
|
303
|
+
const minimalProfile = {
|
|
304
|
+
_id: "user-123",
|
|
305
|
+
email: "test@example.com",
|
|
306
|
+
firstName: "John",
|
|
307
|
+
lastName: "Doe",
|
|
308
|
+
timezone: "America/New_York",
|
|
309
|
+
};
|
|
310
|
+
expect(() => userProfileSchema.parse(minimalProfile)).not.toThrow();
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
test("should reject invalid email", () => {
|
|
314
|
+
const invalidProfile = {
|
|
315
|
+
_id: "user-123",
|
|
316
|
+
email: "invalid-email",
|
|
317
|
+
firstName: "John",
|
|
318
|
+
lastName: "Doe",
|
|
319
|
+
timezone: "America/New_York",
|
|
320
|
+
};
|
|
321
|
+
expect(() => userProfileSchema.parse(invalidProfile)).toThrow();
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
test("should reject invalid avatar URL", () => {
|
|
325
|
+
const invalidProfile = {
|
|
326
|
+
_id: "user-123",
|
|
327
|
+
email: "test@example.com",
|
|
328
|
+
firstName: "John",
|
|
329
|
+
lastName: "Doe",
|
|
330
|
+
timezone: "America/New_York",
|
|
331
|
+
avatarUrl: "invalid-url",
|
|
332
|
+
};
|
|
333
|
+
expect(() => userProfileSchema.parse(invalidProfile)).toThrow();
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
describe("groupSchema", () => {
|
|
338
|
+
test("should accept valid group", () => {
|
|
339
|
+
const validGroup = {
|
|
340
|
+
groupId: "group-123",
|
|
341
|
+
name: "Test Group",
|
|
342
|
+
role: "admin",
|
|
343
|
+
};
|
|
344
|
+
expect(() => groupSchema.parse(validGroup)).not.toThrow();
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
test("should accept group without optional role", () => {
|
|
348
|
+
const minimalGroup = {
|
|
349
|
+
groupId: "group-123",
|
|
350
|
+
name: "Test Group",
|
|
351
|
+
};
|
|
352
|
+
expect(() => groupSchema.parse(minimalGroup)).not.toThrow();
|
|
353
|
+
});
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
describe("userSchema", () => {
|
|
357
|
+
test("should accept valid user", () => {
|
|
358
|
+
const validUser = {
|
|
359
|
+
_id: "user-123",
|
|
360
|
+
email: "test@example.com",
|
|
361
|
+
profile: {
|
|
362
|
+
_id: "user-123",
|
|
363
|
+
email: "test@example.com",
|
|
364
|
+
firstName: "John",
|
|
365
|
+
lastName: "Doe",
|
|
366
|
+
timezone: "America/New_York",
|
|
367
|
+
},
|
|
368
|
+
primaryGroup: {
|
|
369
|
+
groupId: "group-123",
|
|
370
|
+
name: "Test Group",
|
|
371
|
+
},
|
|
372
|
+
};
|
|
373
|
+
expect(() => userSchema.parse(validUser)).not.toThrow();
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
test("should accept user without primary group", () => {
|
|
377
|
+
const userWithoutGroup = {
|
|
378
|
+
_id: "user-123",
|
|
379
|
+
email: "test@example.com",
|
|
380
|
+
profile: {
|
|
381
|
+
_id: "user-123",
|
|
382
|
+
email: "test@example.com",
|
|
383
|
+
firstName: "John",
|
|
384
|
+
lastName: "Doe",
|
|
385
|
+
timezone: "America/New_York",
|
|
386
|
+
},
|
|
387
|
+
};
|
|
388
|
+
expect(() => userSchema.parse(userWithoutGroup)).not.toThrow();
|
|
389
|
+
});
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
describe("taskSchema", () => {
|
|
393
|
+
test("should accept valid task", () => {
|
|
394
|
+
const validTask = {
|
|
395
|
+
_id: "task-123",
|
|
396
|
+
title: "Test Task",
|
|
397
|
+
description: "Task description",
|
|
398
|
+
status: "active",
|
|
399
|
+
createdAt: "2024-01-15T10:00:00Z",
|
|
400
|
+
updatedAt: "2024-01-15T11:00:00Z",
|
|
401
|
+
scheduledDate: "2024-01-16",
|
|
402
|
+
completedAt: "2024-01-16T12:00:00Z",
|
|
403
|
+
streamId: "stream-123",
|
|
404
|
+
userId: "user-123",
|
|
405
|
+
groupId: "group-123",
|
|
406
|
+
};
|
|
407
|
+
expect(() => taskSchema.parse(validTask)).not.toThrow();
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
test("should accept task with minimal required fields", () => {
|
|
411
|
+
const minimalTask = {
|
|
412
|
+
_id: "task-123",
|
|
413
|
+
title: "Test Task",
|
|
414
|
+
status: "active",
|
|
415
|
+
createdAt: "2024-01-15T10:00:00Z",
|
|
416
|
+
updatedAt: "2024-01-15T11:00:00Z",
|
|
417
|
+
userId: "user-123",
|
|
418
|
+
groupId: "group-123",
|
|
419
|
+
};
|
|
420
|
+
expect(() => taskSchema.parse(minimalTask)).not.toThrow();
|
|
421
|
+
});
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
describe("streamSchema", () => {
|
|
425
|
+
test("should accept valid stream", () => {
|
|
426
|
+
const validStream = {
|
|
427
|
+
_id: "stream-123",
|
|
428
|
+
name: "Test Stream",
|
|
429
|
+
color: "#FF0000",
|
|
430
|
+
groupId: "group-123",
|
|
431
|
+
isActive: true,
|
|
432
|
+
createdAt: "2024-01-15T10:00:00Z",
|
|
433
|
+
updatedAt: "2024-01-15T11:00:00Z",
|
|
434
|
+
};
|
|
435
|
+
expect(() => streamSchema.parse(validStream)).not.toThrow();
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
test("should accept stream without optional color", () => {
|
|
439
|
+
const minimalStream = {
|
|
440
|
+
_id: "stream-123",
|
|
441
|
+
name: "Test Stream",
|
|
442
|
+
groupId: "group-123",
|
|
443
|
+
isActive: true,
|
|
444
|
+
createdAt: "2024-01-15T10:00:00Z",
|
|
445
|
+
updatedAt: "2024-01-15T11:00:00Z",
|
|
446
|
+
};
|
|
447
|
+
expect(() => streamSchema.parse(minimalStream)).not.toThrow();
|
|
448
|
+
});
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
describe("userResponseSchema", () => {
|
|
452
|
+
test("should accept valid user response", () => {
|
|
453
|
+
const validResponse = {
|
|
454
|
+
user: {
|
|
455
|
+
_id: "user-123",
|
|
456
|
+
email: "test@example.com",
|
|
457
|
+
profile: {
|
|
458
|
+
_id: "user-123",
|
|
459
|
+
email: "test@example.com",
|
|
460
|
+
firstName: "John",
|
|
461
|
+
lastName: "Doe",
|
|
462
|
+
timezone: "America/New_York",
|
|
463
|
+
},
|
|
464
|
+
},
|
|
465
|
+
};
|
|
466
|
+
expect(() => userResponseSchema.parse(validResponse)).not.toThrow();
|
|
467
|
+
});
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
describe("tasksResponseSchema", () => {
|
|
471
|
+
test("should accept valid tasks response", () => {
|
|
472
|
+
const validResponse = {
|
|
473
|
+
tasks: [
|
|
474
|
+
{
|
|
475
|
+
_id: "task-123",
|
|
476
|
+
title: "Test Task",
|
|
477
|
+
status: "active",
|
|
478
|
+
createdAt: "2024-01-15T10:00:00Z",
|
|
479
|
+
updatedAt: "2024-01-15T11:00:00Z",
|
|
480
|
+
userId: "user-123",
|
|
481
|
+
groupId: "group-123",
|
|
482
|
+
},
|
|
483
|
+
],
|
|
484
|
+
count: 1,
|
|
485
|
+
};
|
|
486
|
+
expect(() => tasksResponseSchema.parse(validResponse)).not.toThrow();
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
test("should accept empty tasks response", () => {
|
|
490
|
+
const emptyResponse = {
|
|
491
|
+
tasks: [],
|
|
492
|
+
count: 0,
|
|
493
|
+
};
|
|
494
|
+
expect(() => tasksResponseSchema.parse(emptyResponse)).not.toThrow();
|
|
495
|
+
});
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
describe("streamsResponseSchema", () => {
|
|
499
|
+
test("should accept valid streams response", () => {
|
|
500
|
+
const validResponse = {
|
|
501
|
+
streams: [
|
|
502
|
+
{
|
|
503
|
+
_id: "stream-123",
|
|
504
|
+
name: "Test Stream",
|
|
505
|
+
groupId: "group-123",
|
|
506
|
+
isActive: true,
|
|
507
|
+
createdAt: "2024-01-15T10:00:00Z",
|
|
508
|
+
updatedAt: "2024-01-15T11:00:00Z",
|
|
509
|
+
},
|
|
510
|
+
],
|
|
511
|
+
count: 1,
|
|
512
|
+
};
|
|
513
|
+
expect(() => streamsResponseSchema.parse(validResponse)).not.toThrow();
|
|
514
|
+
});
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
describe("errorResponseSchema", () => {
|
|
518
|
+
test("should accept valid error response", () => {
|
|
519
|
+
const validError = {
|
|
520
|
+
error: "ValidationError",
|
|
521
|
+
message: "Invalid input provided",
|
|
522
|
+
code: "400",
|
|
523
|
+
};
|
|
524
|
+
expect(() => errorResponseSchema.parse(validError)).not.toThrow();
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
test("should accept error without optional code", () => {
|
|
528
|
+
const minimalError = {
|
|
529
|
+
error: "ValidationError",
|
|
530
|
+
message: "Invalid input provided",
|
|
531
|
+
};
|
|
532
|
+
expect(() => errorResponseSchema.parse(minimalError)).not.toThrow();
|
|
533
|
+
});
|
|
534
|
+
});
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
describe("Edge Cases and Error Handling", () => {
|
|
538
|
+
test("should handle null values appropriately", () => {
|
|
539
|
+
// updateTaskBacklogSchema should accept all parameters as optional except taskId
|
|
540
|
+
expect(() =>
|
|
541
|
+
updateTaskBacklogSchema.parse({
|
|
542
|
+
taskId: "task-123"
|
|
543
|
+
})
|
|
544
|
+
).not.toThrow();
|
|
545
|
+
|
|
546
|
+
// Other schemas should reject null where not expected
|
|
547
|
+
expect(() => getTasksByDaySchema.parse({ day: null })).toThrow();
|
|
548
|
+
expect(() => createTaskSchema.parse({ text: null })).toThrow();
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
test("should handle undefined values appropriately", () => {
|
|
552
|
+
// Optional fields should accept undefined
|
|
553
|
+
expect(() =>
|
|
554
|
+
getTasksByDaySchema.parse({
|
|
555
|
+
day: "2024-01-15",
|
|
556
|
+
timezone: undefined,
|
|
557
|
+
completionFilter: undefined,
|
|
558
|
+
})
|
|
559
|
+
).not.toThrow();
|
|
560
|
+
|
|
561
|
+
// Required fields should reject undefined
|
|
562
|
+
expect(() => getTasksByDaySchema.parse({ day: undefined })).toThrow();
|
|
563
|
+
expect(() => createTaskSchema.parse({ text: undefined })).toThrow();
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
test("should handle type coercion correctly", () => {
|
|
567
|
+
// Numbers as strings should be rejected where numbers are expected
|
|
568
|
+
expect(() =>
|
|
569
|
+
getArchivedTasksSchema.parse({ offset: "0", limit: "100" })
|
|
570
|
+
).toThrow();
|
|
571
|
+
|
|
572
|
+
// Boolean strings should be rejected where booleans are expected
|
|
573
|
+
expect(() =>
|
|
574
|
+
createTaskSchema.parse({ text: "Test", private: "true" })
|
|
575
|
+
).toThrow();
|
|
576
|
+
});
|
|
577
|
+
|
|
578
|
+
test("should validate string formats correctly", () => {
|
|
579
|
+
// Email validation
|
|
580
|
+
expect(() =>
|
|
581
|
+
userProfileSchema.parse({
|
|
582
|
+
_id: "user-123",
|
|
583
|
+
email: "@example.com",
|
|
584
|
+
firstName: "John",
|
|
585
|
+
lastName: "Doe",
|
|
586
|
+
timezone: "America/New_York",
|
|
587
|
+
})
|
|
588
|
+
).toThrow();
|
|
589
|
+
|
|
590
|
+
// URL validation
|
|
591
|
+
expect(() =>
|
|
592
|
+
userProfileSchema.parse({
|
|
593
|
+
_id: "user-123",
|
|
594
|
+
email: "test@example.com",
|
|
595
|
+
firstName: "John",
|
|
596
|
+
lastName: "Doe",
|
|
597
|
+
timezone: "America/New_York",
|
|
598
|
+
avatarUrl: "not-a-url",
|
|
599
|
+
})
|
|
600
|
+
).toThrow();
|
|
601
|
+
|
|
602
|
+
// Date regex validation
|
|
603
|
+
expect(() =>
|
|
604
|
+
getTasksByDaySchema.parse({ day: "2024-13-01" })
|
|
605
|
+
).not.toThrow(); // Regex allows invalid month/day numbers
|
|
606
|
+
expect(() => getTasksByDaySchema.parse({ day: "24-01-01" })).toThrow(); // But rejects wrong format
|
|
607
|
+
});
|
|
608
|
+
});
|
package/src/schemas.ts
CHANGED
|
@@ -23,6 +23,11 @@ export const getArchivedTasksSchema = z.object({
|
|
|
23
23
|
limit: z.number().int().min(1).max(1000).optional().describe("Maximum number of tasks to return (defaults to 100)"),
|
|
24
24
|
});
|
|
25
25
|
|
|
26
|
+
// Get task by ID parameters
|
|
27
|
+
export const getTaskByIdSchema = z.object({
|
|
28
|
+
taskId: z.string().min(1, "Task ID is required").describe("The ID of the task to retrieve"),
|
|
29
|
+
});
|
|
30
|
+
|
|
26
31
|
/**
|
|
27
32
|
* User Operation Schemas
|
|
28
33
|
*/
|
|
@@ -70,10 +75,14 @@ export const deleteTaskSchema = z.object({
|
|
|
70
75
|
// Update task snooze date parameters
|
|
71
76
|
export const updateTaskSnoozeDateSchema = z.object({
|
|
72
77
|
taskId: z.string().min(1, "Task ID is required").describe("The ID of the task to reschedule"),
|
|
73
|
-
newDay: z.
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
78
|
+
newDay: z.string().date("Must be a valid date in YYYY-MM-DD format").describe("Target date in YYYY-MM-DD format"),
|
|
79
|
+
timezone: z.string().optional().describe("Timezone string (e.g., 'America/New_York'). If not provided, uses user's default timezone"),
|
|
80
|
+
limitResponsePayload: z.boolean().optional().describe("Whether to limit the response payload size"),
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// Update task backlog parameters
|
|
84
|
+
export const updateTaskBacklogSchema = z.object({
|
|
85
|
+
taskId: z.string().min(1, "Task ID is required").describe("The ID of the task to move to backlog"),
|
|
77
86
|
timezone: z.string().optional().describe("Timezone string (e.g., 'America/New_York'). If not provided, uses user's default timezone"),
|
|
78
87
|
limitResponsePayload: z.boolean().optional().describe("Whether to limit the response payload size"),
|
|
79
88
|
});
|
|
@@ -171,6 +180,7 @@ export type CompletionFilter = z.infer<typeof completionFilterSchema>;
|
|
|
171
180
|
export type GetTasksByDayInput = z.infer<typeof getTasksByDaySchema>;
|
|
172
181
|
export type GetTasksBacklogInput = z.infer<typeof getTasksBacklogSchema>;
|
|
173
182
|
export type GetArchivedTasksInput = z.infer<typeof getArchivedTasksSchema>;
|
|
183
|
+
export type GetTaskByIdInput = z.infer<typeof getTaskByIdSchema>;
|
|
174
184
|
export type GetUserInput = z.infer<typeof getUserSchema>;
|
|
175
185
|
export type GetStreamsInput = z.infer<typeof getStreamsSchema>;
|
|
176
186
|
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"includeCoAuthoredBy": false,
|
|
3
|
-
"permissions": {
|
|
4
|
-
"allow": [
|
|
5
|
-
"mcp__context7__resolve-library-id",
|
|
6
|
-
"mcp__context7__get-library-docs",
|
|
7
|
-
"mcp__sunsama__get-tasks-by-day",
|
|
8
|
-
"mcp__sunsama__get-user",
|
|
9
|
-
"mcp__time__get_current_time",
|
|
10
|
-
"mcp__sunsama__get-tasks-backlog",
|
|
11
|
-
"Bash(npm run typecheck:*)",
|
|
12
|
-
"Bash(find:*)",
|
|
13
|
-
"Bash(ls:*)",
|
|
14
|
-
"Bash(bun run build:*)",
|
|
15
|
-
"Bash(npm pack:*)",
|
|
16
|
-
"mcp__sequential-thinking__sequentialthinking",
|
|
17
|
-
"Bash(gh auth:*)",
|
|
18
|
-
"Bash(npm ls:*)",
|
|
19
|
-
"Bash(cat:*)",
|
|
20
|
-
"Bash(grep:*)",
|
|
21
|
-
"Bash(mkdir:*)",
|
|
22
|
-
"mcp__fetch__fetch",
|
|
23
|
-
"Bash(npm view:*)",
|
|
24
|
-
"Bash(npm info:*)"
|
|
25
|
-
],
|
|
26
|
-
"deny": []
|
|
27
|
-
},
|
|
28
|
-
"enabledMcpjsonServers": [
|
|
29
|
-
"brave-search",
|
|
30
|
-
"context7",
|
|
31
|
-
"fetch",
|
|
32
|
-
"puppeteer",
|
|
33
|
-
"sequential-thinking",
|
|
34
|
-
"sunsama",
|
|
35
|
-
"time"
|
|
36
|
-
]
|
|
37
|
-
}
|