mcp-sunsama 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 (53) hide show
  1. package/.changeset/README.md +8 -0
  2. package/.changeset/config.json +11 -0
  3. package/.claude/settings.local.json +29 -0
  4. package/.env.example +10 -0
  5. package/CHANGELOG.md +29 -0
  6. package/CLAUDE.md +137 -0
  7. package/LICENSE +21 -0
  8. package/README.md +143 -0
  9. package/TODO_PREPUBLISH.md +182 -0
  10. package/bun.lock +515 -0
  11. package/dist/auth/http.d.ts +20 -0
  12. package/dist/auth/http.d.ts.map +1 -0
  13. package/dist/auth/http.js +52 -0
  14. package/dist/auth/stdio.d.ts +13 -0
  15. package/dist/auth/stdio.d.ts.map +1 -0
  16. package/dist/auth/stdio.js +27 -0
  17. package/dist/auth/types.d.ts +9 -0
  18. package/dist/auth/types.d.ts.map +1 -0
  19. package/dist/auth/types.js +1 -0
  20. package/dist/config/transport.d.ts +32 -0
  21. package/dist/config/transport.d.ts.map +1 -0
  22. package/dist/config/transport.js +62 -0
  23. package/dist/main.d.ts +2 -0
  24. package/dist/main.d.ts.map +1 -0
  25. package/dist/main.js +473 -0
  26. package/dist/schemas.d.ts +522 -0
  27. package/dist/schemas.d.ts.map +1 -0
  28. package/dist/schemas.js +124 -0
  29. package/dist/utils/client-resolver.d.ts +10 -0
  30. package/dist/utils/client-resolver.d.ts.map +1 -0
  31. package/dist/utils/client-resolver.js +19 -0
  32. package/dist/utils/task-filters.d.ts +29 -0
  33. package/dist/utils/task-filters.d.ts.map +1 -0
  34. package/dist/utils/task-filters.js +42 -0
  35. package/dist/utils/task-trimmer.d.ts +47 -0
  36. package/dist/utils/task-trimmer.d.ts.map +1 -0
  37. package/dist/utils/task-trimmer.js +50 -0
  38. package/dist/utils/to-tsv.d.ts +8 -0
  39. package/dist/utils/to-tsv.d.ts.map +1 -0
  40. package/dist/utils/to-tsv.js +64 -0
  41. package/mcp-inspector.json +14 -0
  42. package/package.json +56 -0
  43. package/src/auth/http.ts +61 -0
  44. package/src/auth/stdio.ts +33 -0
  45. package/src/auth/types.ts +9 -0
  46. package/src/config/transport.ts +80 -0
  47. package/src/main.ts +542 -0
  48. package/src/schemas.ts +169 -0
  49. package/src/utils/client-resolver.ts +23 -0
  50. package/src/utils/task-filters.ts +49 -0
  51. package/src/utils/task-trimmer.ts +81 -0
  52. package/src/utils/to-tsv.ts +73 -0
  53. package/tsconfig.json +36 -0
package/src/main.ts ADDED
@@ -0,0 +1,542 @@
1
+ import { FastMCP } from "fastmcp";
2
+ import type { CreateTaskOptions } from "sunsama-api";
3
+ import { httpStreamAuthenticator } from "./auth/http.js";
4
+ import { initializeStdioAuth } from "./auth/stdio.js";
5
+ import type { SessionData } from "./auth/types.js";
6
+ import { getTransportConfig } from "./config/transport.js";
7
+ import {
8
+ createTaskSchema,
9
+ deleteTaskSchema,
10
+ getStreamsSchema,
11
+ getTasksBacklogSchema,
12
+ getTasksByDaySchema,
13
+ getUserSchema,
14
+ updateTaskCompleteSchema
15
+ } from "./schemas.js";
16
+ import { getSunsamaClient } from "./utils/client-resolver.js";
17
+ import { filterTasksByCompletion } from "./utils/task-filters.js";
18
+ import { trimTasksForResponse } from "./utils/task-trimmer.js";
19
+ import { toTsv } from "./utils/to-tsv.js";
20
+
21
+ // Get transport configuration with validation
22
+ const transportConfig = getTransportConfig();
23
+
24
+ // For stdio transport, authenticate at startup with environment variables
25
+ if (transportConfig.transportType === "stdio") {
26
+ await initializeStdioAuth();
27
+ }
28
+
29
+
30
+ const server = new FastMCP({
31
+ name: "Sunsama API Server",
32
+ version: "0.2.0",
33
+ instructions: `
34
+ This MCP server provides access to the Sunsama API for task and project management.
35
+
36
+ Available tools:
37
+ - Authentication: login, logout, check authentication status
38
+ - User operations: get current user information
39
+ - Task operations: get tasks by day, get backlog tasks
40
+ - Stream operations: get streams/channels for the user's group
41
+
42
+ Authentication is required for all operations. You can either:
43
+ 1. Login with email/password using the 'login' tool
44
+ 2. Use a session token if you have one
45
+
46
+ The server maintains session state per MCP connection, so you only need to authenticate once per session.
47
+ `.trim(),
48
+ // dynamically handle authentication
49
+ ...(transportConfig.transportType === "httpStream" ? {
50
+ authenticate: httpStreamAuthenticator,
51
+ } : {})
52
+ });
53
+
54
+ // User Operations
55
+ server.addTool({
56
+ name: "get-user",
57
+ description: "Get current user information including profile, timezone, and group details",
58
+ parameters: getUserSchema,
59
+ execute: async (_args, {session, log}) => {
60
+ try {
61
+ log.info("Getting user information");
62
+
63
+ // Get the appropriate client based on transport type
64
+ const sunsamaClient = getSunsamaClient(session as SessionData | null);
65
+
66
+ // Get user information
67
+ const user = await sunsamaClient.getUser();
68
+
69
+ log.info("Successfully retrieved user information", {userId: user._id});
70
+
71
+ return {
72
+ content: [
73
+ {
74
+ type: "text",
75
+ text: JSON.stringify(user, null, 2)
76
+ }
77
+ ]
78
+ };
79
+
80
+ } catch (error) {
81
+ log.error("Failed to get user information", {error: error instanceof Error ? error.message : 'Unknown error'});
82
+
83
+ throw new Error(`Failed to get user information: ${error instanceof Error ? error.message : 'Unknown error'}`);
84
+ }
85
+ }
86
+ });
87
+
88
+ // Task Operations
89
+ server.addTool({
90
+ name: "get-tasks-backlog",
91
+ description: "Get tasks from the backlog",
92
+ parameters: getTasksBacklogSchema,
93
+ execute: async (_args, {session, log}) => {
94
+ try {
95
+ log.info("Getting backlog tasks");
96
+
97
+ // Get the appropriate client based on transport type
98
+ const sunsamaClient = getSunsamaClient(session as SessionData | null);
99
+
100
+ // Get backlog tasks
101
+ const tasks = await sunsamaClient.getTasksBacklog();
102
+
103
+ // Trim tasks to reduce response size while preserving essential data
104
+ const trimmedTasks = trimTasksForResponse(tasks);
105
+
106
+ log.info("Successfully retrieved backlog tasks", {count: tasks.length});
107
+
108
+ return {
109
+ content: [
110
+ {
111
+ type: "text",
112
+ text: toTsv(trimmedTasks)
113
+ }
114
+ ]
115
+ };
116
+
117
+ } catch (error) {
118
+ log.error("Failed to get backlog tasks", {error: error instanceof Error ? error.message : 'Unknown error'});
119
+
120
+ throw new Error(`Failed to get backlog tasks: ${error instanceof Error ? error.message : 'Unknown error'}`);
121
+ }
122
+ }
123
+ });
124
+
125
+ server.addTool({
126
+ name: "get-tasks-by-day",
127
+ description: "Get tasks for a specific day with optional filtering by completion status",
128
+ parameters: getTasksByDaySchema,
129
+ execute: async (args, {session, log}) => {
130
+ try {
131
+ // Extract and set defaults for parameters
132
+ const completionFilter = args.completionFilter || "all";
133
+
134
+ log.info("Getting tasks for day", {
135
+ day: args.day,
136
+ timezone: args.timezone,
137
+ completionFilter: completionFilter
138
+ });
139
+
140
+ // Get the appropriate client based on transport type
141
+ const sunsamaClient = getSunsamaClient(session as SessionData | null);
142
+
143
+ // If no timezone provided, we need to get the user's default timezone
144
+ let timezone = args.timezone;
145
+ if (!timezone) {
146
+ timezone = await sunsamaClient.getUserTimezone();
147
+ log.info("Using user's default timezone", {timezone});
148
+ }
149
+
150
+ // Get tasks for the specified day with the determined timezone
151
+ const tasks = await sunsamaClient.getTasksByDay(args.day, timezone);
152
+
153
+ // Apply completion filter BEFORE trimming for efficiency
154
+ const filteredTasks = filterTasksByCompletion(tasks, completionFilter);
155
+
156
+ // Trim tasks to reduce response size while preserving essential data
157
+ const trimmedTasks = trimTasksForResponse(filteredTasks);
158
+
159
+ log.info("Successfully retrieved tasks for day", {
160
+ day: args.day,
161
+ totalCount: tasks.length,
162
+ filteredCount: filteredTasks.length,
163
+ filter: completionFilter,
164
+ timezone: timezone
165
+ });
166
+
167
+ return {
168
+ content: [
169
+ {
170
+ type: "text",
171
+ text: toTsv(trimmedTasks)
172
+ }
173
+ ]
174
+ };
175
+
176
+ } catch (error) {
177
+ log.error("Failed to get tasks by day", {
178
+ day: args.day,
179
+ timezone: args.timezone,
180
+ completionFilter: args.completionFilter,
181
+ error: error instanceof Error ? error.message : 'Unknown error'
182
+ });
183
+
184
+ throw new Error(`Failed to get tasks for ${args.day}: ${error instanceof Error ? error.message : 'Unknown error'}`);
185
+ }
186
+ }
187
+ });
188
+
189
+ // Task Mutation Operations
190
+ server.addTool({
191
+ name: "create-task",
192
+ description: "Create a new task with optional properties",
193
+ parameters: createTaskSchema,
194
+ execute: async (args, {session, log}) => {
195
+ try {
196
+ // Extract parameters from args
197
+ const {text, notes, streamIds, timeEstimate, dueDate, snoozeUntil, private: isPrivate, taskId} = args;
198
+
199
+ log.info("Creating new task", {
200
+ text: text,
201
+ hasNotes: !!notes,
202
+ streamCount: streamIds?.length || 0,
203
+ timeEstimate: timeEstimate,
204
+ hasDueDate: !!dueDate,
205
+ hasSnooze: !!snoozeUntil,
206
+ isPrivate: isPrivate,
207
+ customTaskId: !!taskId
208
+ });
209
+
210
+ // Get the appropriate client based on transport type
211
+ const sunsamaClient = getSunsamaClient(session as SessionData | null);
212
+
213
+ // Build options object for createTask
214
+ const options: CreateTaskOptions = {};
215
+ if (notes) options.notes = notes;
216
+ if (streamIds) options.streamIds = streamIds;
217
+ if (timeEstimate) options.timeEstimate = timeEstimate;
218
+ if (dueDate) options.dueDate = dueDate;
219
+ if (snoozeUntil) options.snoozeUntil = snoozeUntil;
220
+ if (isPrivate !== undefined) options.private = isPrivate;
221
+ if (taskId) options.taskId = taskId;
222
+
223
+ // Call sunsamaClient.createTask(text, options)
224
+ const result = await sunsamaClient.createTask(text, options);
225
+
226
+ log.info("Successfully created task", {
227
+ taskId: result.updatedFields?._id || 'unknown',
228
+ title: text,
229
+ success: result.success
230
+ });
231
+
232
+ return {
233
+ content: [
234
+ {
235
+ type: "text",
236
+ text: JSON.stringify({
237
+ success: result.success,
238
+ taskId: result.updatedFields?._id,
239
+ title: text,
240
+ created: true,
241
+ updatedFields: result.updatedFields
242
+ })
243
+ }
244
+ ]
245
+ };
246
+
247
+ } catch (error) {
248
+ log.error("Failed to create task", {
249
+ text: args.text,
250
+ error: error instanceof Error ? error.message : 'Unknown error'
251
+ });
252
+
253
+ throw new Error(`Failed to create task: ${error instanceof Error ? error.message : 'Unknown error'}`);
254
+ }
255
+ }
256
+ });
257
+
258
+ server.addTool({
259
+ name: "update-task-complete",
260
+ description: "Mark a task as complete with optional completion timestamp",
261
+ parameters: updateTaskCompleteSchema,
262
+ execute: async (args, {session, log}) => {
263
+ try {
264
+ // Extract taskId and optional parameters
265
+ const {taskId, completeOn, limitResponsePayload} = args;
266
+
267
+ log.info("Marking task as complete", {
268
+ taskId: taskId,
269
+ hasCustomCompleteOn: !!completeOn,
270
+ limitResponsePayload: limitResponsePayload
271
+ });
272
+
273
+ // Get the appropriate client based on transport type
274
+ const sunsamaClient = getSunsamaClient(session as SessionData | null);
275
+
276
+ // Call sunsamaClient.updateTaskComplete(taskId, completeOn, limitResponsePayload)
277
+ const result = await sunsamaClient.updateTaskComplete(
278
+ taskId,
279
+ completeOn,
280
+ limitResponsePayload
281
+ );
282
+
283
+ log.info("Successfully marked task as complete", {
284
+ taskId: taskId,
285
+ success: result.success,
286
+ updatedFields: !!result.updatedFields
287
+ });
288
+
289
+ return {
290
+ content: [
291
+ {
292
+ type: "text",
293
+ text: JSON.stringify({
294
+ success: result.success,
295
+ taskId: taskId,
296
+ completed: true,
297
+ updatedFields: result.updatedFields
298
+ })
299
+ }
300
+ ]
301
+ };
302
+
303
+ } catch (error) {
304
+ log.error("Failed to mark task as complete", {
305
+ taskId: args.taskId,
306
+ error: error instanceof Error ? error.message : 'Unknown error'
307
+ });
308
+
309
+ throw new Error(`Failed to mark task as complete: ${error instanceof Error ? error.message : 'Unknown error'}`);
310
+ }
311
+ }
312
+ });
313
+
314
+ server.addTool({
315
+ name: "delete-task",
316
+ description: "Delete a task permanently",
317
+ parameters: deleteTaskSchema,
318
+ execute: async (args, {session, log}) => {
319
+ try {
320
+ // Extract taskId and optional parameters
321
+ const {taskId, limitResponsePayload, wasTaskMerged} = args;
322
+
323
+ log.info("Deleting task", {
324
+ taskId: taskId,
325
+ limitResponsePayload: limitResponsePayload,
326
+ wasTaskMerged: wasTaskMerged
327
+ });
328
+
329
+ // Get the appropriate client based on transport type
330
+ const sunsamaClient = getSunsamaClient(session as SessionData | null);
331
+
332
+ // Call sunsamaClient.deleteTask(taskId, limitResponsePayload, wasTaskMerged)
333
+ const result = await sunsamaClient.deleteTask(
334
+ taskId,
335
+ limitResponsePayload,
336
+ wasTaskMerged
337
+ );
338
+
339
+ log.info("Successfully deleted task", {
340
+ taskId: taskId,
341
+ success: result.success
342
+ });
343
+
344
+ return {
345
+ content: [
346
+ {
347
+ type: "text",
348
+ text: JSON.stringify({
349
+ success: result.success,
350
+ taskId: taskId,
351
+ deleted: true,
352
+ updatedFields: result.updatedFields
353
+ })
354
+ }
355
+ ]
356
+ };
357
+
358
+ } catch (error) {
359
+ log.error("Failed to delete task", {
360
+ taskId: args.taskId,
361
+ error: error instanceof Error ? error.message : 'Unknown error'
362
+ });
363
+
364
+ throw new Error(`Failed to delete task: ${error instanceof Error ? error.message : 'Unknown error'}`);
365
+ }
366
+ }
367
+ });
368
+
369
+ // Stream Operations
370
+ server.addTool({
371
+ name: "get-streams",
372
+ description: "Get streams for the user's group (streams are called 'channels' in the Sunsama UI)",
373
+ parameters: getStreamsSchema,
374
+ execute: async (_args, {session, log}) => {
375
+ try {
376
+ log.info("Getting streams for user's group");
377
+
378
+ // Get the appropriate client based on transport type
379
+ const sunsamaClient = getSunsamaClient(session as SessionData | null);
380
+
381
+ // Get streams for the user's group
382
+ const streams = await sunsamaClient.getStreamsByGroupId();
383
+
384
+ log.info("Successfully retrieved streams", {count: streams.length});
385
+
386
+ return {
387
+ content: [
388
+ {
389
+ type: "text",
390
+ text: toTsv(streams)
391
+ }
392
+ ]
393
+ };
394
+
395
+ } catch (error) {
396
+ log.error("Failed to get streams", {error: error instanceof Error ? error.message : 'Unknown error'});
397
+
398
+ throw new Error(`Failed to get streams: ${error instanceof Error ? error.message : 'Unknown error'}`);
399
+ }
400
+ }
401
+ });
402
+
403
+ server.addResource({
404
+ uri: "sunsama://api/docs",
405
+ name: "Sunsama API Documentation",
406
+ description: "Documentation for the Sunsama API endpoints and data structures",
407
+ mimeType: "text/markdown",
408
+ load: async () => {
409
+ return {
410
+ text: `# Sunsama MCP Server Documentation
411
+
412
+ ## Overview
413
+ This MCP server provides access to the Sunsama API for task and project management.
414
+ Authentication is handled server-side using environment variables.
415
+
416
+ ## Authentication
417
+ The server authenticates to Sunsama using environment variables:
418
+ - \`SUNSAMA_EMAIL\`: Your Sunsama account email
419
+ - \`SUNSAMA_PASSWORD\`: Your Sunsama account password
420
+
421
+ Authentication happens automatically on server startup. No client-side authentication is required.
422
+
423
+ ## Available Tools
424
+
425
+ ### User Operations
426
+ - **get-user**: Get current user information
427
+ - Parameters: none
428
+ - Returns: User object with profile, timezone, and primary group details
429
+
430
+ ### Task Operations
431
+ - **get-tasks-by-day**: Get tasks for a specific day with optional filtering
432
+ - Parameters:
433
+ - \`day\` (required): Date in YYYY-MM-DD format
434
+ - \`timezone\` (optional): Timezone string (e.g., "America/New_York")
435
+ - \`completionFilter\` (optional): Filter by completion status
436
+ - \`"all"\` (default): Return all tasks
437
+ - \`"incomplete"\`: Return only incomplete tasks
438
+ - \`"completed"\`: Return only completed tasks
439
+ - Returns: Array of filtered Task objects for the specified day
440
+
441
+ - **get-tasks-backlog**: Get tasks from the backlog
442
+ - Parameters: none
443
+ - Returns: Array of Task objects from the backlog
444
+
445
+ ### Stream Operations
446
+ - **get-streams**: Get streams for the user's group
447
+ - Parameters: none
448
+ - Returns: Array of Stream objects
449
+ - Note: Streams are called "channels" in the Sunsama UI. If a user requests channels, use this tool.
450
+
451
+ ## Data Types
452
+
453
+ ### User Object
454
+ \`\`\`typescript
455
+ {
456
+ _id: string;
457
+ email: string;
458
+ profile: {
459
+ _id: string;
460
+ email: string;
461
+ firstName: string;
462
+ lastName: string;
463
+ timezone: string;
464
+ avatarUrl?: string;
465
+ };
466
+ primaryGroup?: {
467
+ groupId: string;
468
+ name: string;
469
+ role?: string;
470
+ };
471
+ }
472
+ \`\`\`
473
+
474
+ ### Task Object
475
+ \`\`\`typescript
476
+ {
477
+ _id: string;
478
+ title: string;
479
+ description?: string;
480
+ status: string;
481
+ createdAt: string;
482
+ updatedAt: string;
483
+ scheduledDate?: string;
484
+ completedAt?: string;
485
+ streamId?: string;
486
+ userId: string;
487
+ groupId: string;
488
+ }
489
+ \`\`\`
490
+
491
+ ### Stream Object
492
+ Note: Streams are called "channels" in the Sunsama UI.
493
+ \`\`\`typescript
494
+ {
495
+ _id: string;
496
+ name: string;
497
+ color?: string;
498
+ groupId: string;
499
+ isActive: boolean;
500
+ createdAt: string;
501
+ updatedAt: string;
502
+ }
503
+ \`\`\`
504
+
505
+ ## Error Handling
506
+ - All operations require valid Sunsama authentication
507
+ - Invalid dates will return validation errors
508
+ - Network errors are handled gracefully with descriptive messages
509
+ - Server maintains session state across tool calls
510
+
511
+ ## Environment Setup
512
+ Required environment variables:
513
+ - \`API_KEY\`: MCP server authentication key
514
+ - \`SUNSAMA_EMAIL\`: Sunsama account email
515
+ - \`SUNSAMA_PASSWORD\`: Sunsama account password
516
+ - \`PORT\`: Server port (default: 3000)
517
+ `.trim()
518
+ };
519
+ }
520
+ });
521
+
522
+
523
+ // Start server with dynamic transport configuration
524
+ if (transportConfig.transportType === "httpStream") {
525
+ // Log startup information
526
+ console.log(`HTTP Stream configuration: port=${transportConfig.httpStream?.port}, endpoint=${transportConfig.httpStream?.endpoint}`);
527
+
528
+ server.start({
529
+ transportType: "httpStream",
530
+ httpStream: {
531
+ port: transportConfig.httpStream!.port
532
+ }
533
+ }).then(() => {
534
+ console.log(`Sunsama MCP Server running on port ${transportConfig.httpStream!.port}`);
535
+ console.log(`HTTP endpoint: ${transportConfig.httpStream!.endpoint}`);
536
+ console.log("Authentication: HTTP Basic Auth with Sunsama credentials");
537
+ });
538
+ } else {
539
+ server.start({
540
+ transportType: "stdio"
541
+ });
542
+ }
package/src/schemas.ts ADDED
@@ -0,0 +1,169 @@
1
+ import { z } from "zod";
2
+
3
+ /**
4
+ * Task Operation Schemas
5
+ */
6
+
7
+ // Completion filter schema
8
+ export const completionFilterSchema = z.enum(["all", "incomplete", "completed"]);
9
+
10
+ // Get tasks by day parameters
11
+ export const getTasksByDaySchema = z.object({
12
+ day: z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Day must be in YYYY-MM-DD format"),
13
+ timezone: z.string().optional().describe("Timezone string (e.g., 'America/New_York'). If not provided, uses user's default timezone"),
14
+ completionFilter: completionFilterSchema.optional().describe("Filter tasks by completion status. 'all' returns all tasks, 'incomplete' returns only incomplete tasks, 'completed' returns only completed tasks. Defaults to 'all'"),
15
+ });
16
+
17
+ // Get tasks backlog parameters (no parameters needed)
18
+ export const getTasksBacklogSchema = z.object({});
19
+
20
+ /**
21
+ * User Operation Schemas
22
+ */
23
+
24
+ // Get user parameters (no parameters needed)
25
+ export const getUserSchema = z.object({});
26
+
27
+ /**
28
+ * Stream Operation Schemas
29
+ */
30
+
31
+ // Get streams parameters (no parameters needed, uses cached group ID)
32
+ export const getStreamsSchema = z.object({});
33
+
34
+ /**
35
+ * Task Mutation Operation Schemas
36
+ */
37
+
38
+ // Create task parameters
39
+ export const createTaskSchema = z.object({
40
+ text: z.string().min(1, "Task text is required").describe("Task title/description"),
41
+ notes: z.string().optional().describe("Additional task notes"),
42
+ streamIds: z.array(z.string()).optional().describe("Array of stream IDs to associate with the task"),
43
+ timeEstimate: z.number().int().positive().optional().describe("Time estimate in minutes"),
44
+ dueDate: z.string().optional().describe("Due date string (ISO format)"),
45
+ snoozeUntil: z.string().optional().describe("Snooze until date string (ISO format)"),
46
+ private: z.boolean().optional().describe("Whether the task is private"),
47
+ taskId: z.string().optional().describe("Custom task ID (auto-generated if not provided)"),
48
+ });
49
+
50
+ // Update task complete parameters
51
+ export const updateTaskCompleteSchema = z.object({
52
+ taskId: z.string().min(1, "Task ID is required").describe("The ID of the task to mark as complete"),
53
+ completeOn: z.string().optional().describe("Completion timestamp (ISO format). Defaults to current time"),
54
+ limitResponsePayload: z.boolean().optional().describe("Whether to limit the response payload size"),
55
+ });
56
+
57
+ // Delete task parameters
58
+ export const deleteTaskSchema = z.object({
59
+ taskId: z.string().min(1, "Task ID is required").describe("The ID of the task to delete"),
60
+ limitResponsePayload: z.boolean().optional().describe("Whether to limit response size"),
61
+ wasTaskMerged: z.boolean().optional().describe("Whether the task was merged before deletion"),
62
+ });
63
+
64
+ /**
65
+ * Response Type Schemas (for validation and documentation)
66
+ */
67
+
68
+ // Basic user profile schema
69
+ export const userProfileSchema = z.object({
70
+ _id: z.string(),
71
+ email: z.string().email(),
72
+ firstName: z.string(),
73
+ lastName: z.string(),
74
+ timezone: z.string(),
75
+ avatarUrl: z.string().url().optional(),
76
+ });
77
+
78
+ // Group schema
79
+ export const groupSchema = z.object({
80
+ groupId: z.string(),
81
+ name: z.string(),
82
+ role: z.string().optional(),
83
+ });
84
+
85
+ // User schema with primary group
86
+ export const userSchema = z.object({
87
+ _id: z.string(),
88
+ email: z.string().email(),
89
+ profile: userProfileSchema,
90
+ primaryGroup: groupSchema.optional(),
91
+ });
92
+
93
+ // Task schema (simplified - based on common task properties)
94
+ export const taskSchema = z.object({
95
+ _id: z.string(),
96
+ title: z.string(),
97
+ description: z.string().optional(),
98
+ status: z.string(),
99
+ createdAt: z.string(),
100
+ updatedAt: z.string(),
101
+ scheduledDate: z.string().optional(),
102
+ completedAt: z.string().optional(),
103
+ streamId: z.string().optional(),
104
+ userId: z.string(),
105
+ groupId: z.string(),
106
+ });
107
+
108
+ // Stream schema
109
+ export const streamSchema = z.object({
110
+ _id: z.string(),
111
+ name: z.string(),
112
+ color: z.string().optional(),
113
+ groupId: z.string(),
114
+ isActive: z.boolean(),
115
+ createdAt: z.string(),
116
+ updatedAt: z.string(),
117
+ });
118
+
119
+ /**
120
+ * API Response Schemas
121
+ */
122
+
123
+ // User response
124
+ export const userResponseSchema = z.object({
125
+ user: userSchema,
126
+ });
127
+
128
+ // Tasks response
129
+ export const tasksResponseSchema = z.object({
130
+ tasks: z.array(taskSchema),
131
+ count: z.number(),
132
+ });
133
+
134
+ // Streams response
135
+ export const streamsResponseSchema = z.object({
136
+ streams: z.array(streamSchema),
137
+ count: z.number(),
138
+ });
139
+
140
+ /**
141
+ * Error Response Schema
142
+ */
143
+ export const errorResponseSchema = z.object({
144
+ error: z.string(),
145
+ message: z.string(),
146
+ code: z.string().optional(),
147
+ });
148
+
149
+ /**
150
+ * Type Exports (for use in tools)
151
+ */
152
+ export type CompletionFilter = z.infer<typeof completionFilterSchema>;
153
+
154
+ export type GetTasksByDayInput = z.infer<typeof getTasksByDaySchema>;
155
+ export type GetTasksBacklogInput = z.infer<typeof getTasksBacklogSchema>;
156
+ export type GetUserInput = z.infer<typeof getUserSchema>;
157
+ export type GetStreamsInput = z.infer<typeof getStreamsSchema>;
158
+
159
+ export type CreateTaskInput = z.infer<typeof createTaskSchema>;
160
+ export type UpdateTaskCompleteInput = z.infer<typeof updateTaskCompleteSchema>;
161
+ export type DeleteTaskInput = z.infer<typeof deleteTaskSchema>;
162
+
163
+ export type User = z.infer<typeof userSchema>;
164
+ export type Task = z.infer<typeof taskSchema>;
165
+ export type Stream = z.infer<typeof streamSchema>;
166
+ export type UserResponse = z.infer<typeof userResponseSchema>;
167
+ export type TasksResponse = z.infer<typeof tasksResponseSchema>;
168
+ export type StreamsResponse = z.infer<typeof streamsResponseSchema>;
169
+ export type ErrorResponse = z.infer<typeof errorResponseSchema>;