mcp-sunsama 0.6.0 → 0.8.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 +31 -0
- package/CLAUDE.md +2 -2
- package/README.md +2 -1
- package/bun.lock +2 -2
- package/dist/main.js +106 -3
- package/dist/schemas.d.ts +24 -2
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +10 -0
- package/dist/schemas.test.js +51 -1
- package/package.json +2 -2
- package/src/main.ts +125 -2
- package/src/schemas.test.ts +68 -0
- package/src/schemas.ts +14 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,36 @@
|
|
|
1
1
|
# mcp-sunsama
|
|
2
2
|
|
|
3
|
+
## 0.8.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 6585510: feat: add update-task-planned-time tool for updating task time estimates
|
|
8
|
+
|
|
9
|
+
This adds a new MCP tool that allows users to update the planned time (time estimate) for existing tasks in Sunsama. The tool accepts a task ID and time estimate in minutes, with optional response payload limiting.
|
|
10
|
+
|
|
11
|
+
Features:
|
|
12
|
+
|
|
13
|
+
- Update task time estimates in minutes (converted to seconds for API)
|
|
14
|
+
- Support for clearing time estimates by setting to 0
|
|
15
|
+
- Comprehensive input validation and error handling
|
|
16
|
+
- Full test coverage including edge cases
|
|
17
|
+
- Documentation updates for README and CLAUDE.md
|
|
18
|
+
|
|
19
|
+
The implementation follows established patterns in the codebase and leverages the existing sunsama-api updateTaskPlannedTime method.
|
|
20
|
+
|
|
21
|
+
## 0.7.0
|
|
22
|
+
|
|
23
|
+
### Minor Changes
|
|
24
|
+
|
|
25
|
+
- 3394176: Add get-task-by-id tool for retrieving specific tasks by their unique identifier
|
|
26
|
+
|
|
27
|
+
- Add `get-task-by-id` MCP tool that retrieves a specific task by its ID
|
|
28
|
+
- Add `getTaskByIdSchema` with taskId parameter validation
|
|
29
|
+
- Return complete task object if found, null if not found
|
|
30
|
+
- Follow standard tool patterns for authentication, error handling, and logging
|
|
31
|
+
- Update documentation in README.md and CLAUDE.md
|
|
32
|
+
- Maintain consistent JSON response format for single object retrieval
|
|
33
|
+
|
|
3
34
|
## 0.6.0
|
|
4
35
|
|
|
5
36
|
### Minor Changes
|
package/CLAUDE.md
CHANGED
|
@@ -126,8 +126,8 @@ Optional:
|
|
|
126
126
|
|
|
127
127
|
### Task Operations
|
|
128
128
|
Full CRUD support:
|
|
129
|
-
- **Read**: `get-tasks-by-day`, `get-tasks-backlog`, `get-archived-tasks`, `get-streams`
|
|
130
|
-
- **Write**: `create-task`, `update-task-complete`, `update-task-snooze-date`, `update-task-backlog`, `delete-task`
|
|
129
|
+
- **Read**: `get-tasks-by-day`, `get-tasks-backlog`, `get-archived-tasks`, `get-task-by-id`, `get-streams`
|
|
130
|
+
- **Write**: `create-task`, `update-task-complete`, `update-task-planned-time`, `update-task-snooze-date`, `update-task-backlog`, `delete-task`
|
|
131
131
|
|
|
132
132
|
Task read operations support response trimming. `get-tasks-by-day` includes completion filtering. `get-archived-tasks` includes enhanced pagination with hasMore flag for LLM decision-making.
|
|
133
133
|
|
package/README.md
CHANGED
|
@@ -92,7 +92,9 @@ Add this configuration to your Claude Desktop MCP settings:
|
|
|
92
92
|
- `get-tasks-by-day` - Get tasks for a specific day with completion filtering
|
|
93
93
|
- `get-tasks-backlog` - Get backlog tasks
|
|
94
94
|
- `get-archived-tasks` - Get archived tasks with pagination (includes hasMore flag for LLM context)
|
|
95
|
+
- `get-task-by-id` - Get a specific task by its ID
|
|
95
96
|
- `update-task-complete` - Mark tasks as complete
|
|
97
|
+
- `update-task-planned-time` - Update the planned time (time estimate) for tasks
|
|
96
98
|
- `update-task-snooze-date` - Reschedule tasks to different dates
|
|
97
99
|
- `update-task-backlog` - Move tasks to the backlog
|
|
98
100
|
- `delete-task` - Delete tasks permanently
|
|
@@ -140,7 +142,6 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
|
|
|
140
142
|
|
|
141
143
|
## Support
|
|
142
144
|
|
|
143
|
-
- [Sunsama API Documentation](https://help.sunsama.com)
|
|
144
145
|
- [sunsama-api Library](https://github.com/robertn702/sunsama-api) - The underlying API client
|
|
145
146
|
- [Model Context Protocol Documentation](https://modelcontextprotocol.io)
|
|
146
147
|
- [Issue Tracker](https://github.com/robertn702/mcp-sunsama/issues)
|
package/bun.lock
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"@types/papaparse": "^5.3.16",
|
|
8
8
|
"fastmcp": "3.3.1",
|
|
9
9
|
"papaparse": "^5.5.3",
|
|
10
|
-
"sunsama-api": "0.
|
|
10
|
+
"sunsama-api": "0.7.0",
|
|
11
11
|
"zod": "3.24.4",
|
|
12
12
|
},
|
|
13
13
|
"devDependencies": {
|
|
@@ -426,7 +426,7 @@
|
|
|
426
426
|
|
|
427
427
|
"strtok3": ["strtok3@10.3.1", "", { "dependencies": { "@tokenizer/token": "^0.3.0" } }, "sha512-3JWEZM6mfix/GCJBBUrkA8p2Id2pBkyTkVCJKto55w080QBKZ+8R171fGrbiSp+yMO/u6F8/yUh7K4V9K+YCnw=="],
|
|
428
428
|
|
|
429
|
-
"sunsama-api": ["sunsama-api@0.
|
|
429
|
+
"sunsama-api": ["sunsama-api@0.7.0", "", { "dependencies": { "graphql": "^16.11.0", "graphql-tag": "^2.12.6", "tough-cookie": "^5.1.2", "tslib": "^2.8.1", "yjs": "^13.6.27", "zod": "^3.25.64" } }, "sha512-/wPvJrtE0rUftl7c+OuQdu27OYkTvd23jzcQoAUP2SilQ6QYnFeG/Fjdf6nfl/MFuz5B8mpviGJdSgqYjAkd0g=="],
|
|
430
430
|
|
|
431
431
|
"term-size": ["term-size@2.2.1", "", {}, "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg=="],
|
|
432
432
|
|
package/dist/main.js
CHANGED
|
@@ -3,7 +3,7 @@ import { FastMCP } from "fastmcp";
|
|
|
3
3
|
import { httpStreamAuthenticator } from "./auth/http.js";
|
|
4
4
|
import { initializeStdioAuth } from "./auth/stdio.js";
|
|
5
5
|
import { getTransportConfig } from "./config/transport.js";
|
|
6
|
-
import { createTaskSchema, deleteTaskSchema, getArchivedTasksSchema, getStreamsSchema, getTasksBacklogSchema, getTasksByDaySchema, getUserSchema, updateTaskBacklogSchema, updateTaskCompleteSchema, updateTaskSnoozeDateSchema } from "./schemas.js";
|
|
6
|
+
import { createTaskSchema, deleteTaskSchema, getArchivedTasksSchema, getStreamsSchema, getTaskByIdSchema, getTasksBacklogSchema, getTasksByDaySchema, getUserSchema, updateTaskBacklogSchema, updateTaskCompleteSchema, updateTaskPlannedTimeSchema, updateTaskSnoozeDateSchema } from "./schemas.js";
|
|
7
7
|
import { getSunsamaClient } from "./utils/client-resolver.js";
|
|
8
8
|
import { filterTasksByCompletion } from "./utils/task-filters.js";
|
|
9
9
|
import { trimTasksForResponse } from "./utils/task-trimmer.js";
|
|
@@ -16,14 +16,15 @@ if (transportConfig.transportType === "stdio") {
|
|
|
16
16
|
}
|
|
17
17
|
const server = new FastMCP({
|
|
18
18
|
name: "Sunsama API Server",
|
|
19
|
-
version: "0.
|
|
19
|
+
version: "0.8.0",
|
|
20
20
|
instructions: `
|
|
21
21
|
This MCP server provides access to the Sunsama API for task and project management.
|
|
22
22
|
|
|
23
23
|
Available tools:
|
|
24
24
|
- Authentication: login, logout, check authentication status
|
|
25
25
|
- User operations: get current user information
|
|
26
|
-
- Task operations: get tasks by day, get backlog tasks, get archived tasks
|
|
26
|
+
- Task operations: get tasks by day, get backlog tasks, get archived tasks, get task by ID
|
|
27
|
+
- Task mutations: create tasks, mark complete, delete tasks, reschedule tasks, update planned time
|
|
27
28
|
- Stream operations: get streams/channels for the user's group
|
|
28
29
|
|
|
29
30
|
Authentication is required for all operations. You can either:
|
|
@@ -210,6 +211,57 @@ ${toTsv(trimmedTasks)}`;
|
|
|
210
211
|
}
|
|
211
212
|
}
|
|
212
213
|
});
|
|
214
|
+
server.addTool({
|
|
215
|
+
name: "get-task-by-id",
|
|
216
|
+
description: "Get a specific task by its ID",
|
|
217
|
+
parameters: getTaskByIdSchema,
|
|
218
|
+
execute: async (args, { session, log }) => {
|
|
219
|
+
try {
|
|
220
|
+
const { taskId } = args;
|
|
221
|
+
log.info("Getting task by ID", {
|
|
222
|
+
taskId: taskId
|
|
223
|
+
});
|
|
224
|
+
// Get the appropriate client based on transport type
|
|
225
|
+
const sunsamaClient = getSunsamaClient(session);
|
|
226
|
+
// Get the specific task by ID
|
|
227
|
+
const task = await sunsamaClient.getTaskById(taskId);
|
|
228
|
+
if (task) {
|
|
229
|
+
log.info("Successfully retrieved task by ID", {
|
|
230
|
+
taskId: taskId,
|
|
231
|
+
taskText: task.text
|
|
232
|
+
});
|
|
233
|
+
return {
|
|
234
|
+
content: [
|
|
235
|
+
{
|
|
236
|
+
type: "text",
|
|
237
|
+
text: JSON.stringify(task, null, 2)
|
|
238
|
+
}
|
|
239
|
+
]
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
log.info("Task not found", {
|
|
244
|
+
taskId: taskId
|
|
245
|
+
});
|
|
246
|
+
return {
|
|
247
|
+
content: [
|
|
248
|
+
{
|
|
249
|
+
type: "text",
|
|
250
|
+
text: "null"
|
|
251
|
+
}
|
|
252
|
+
]
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
catch (error) {
|
|
257
|
+
log.error("Failed to get task by ID", {
|
|
258
|
+
taskId: args.taskId,
|
|
259
|
+
error: error instanceof Error ? error.message : 'Unknown error'
|
|
260
|
+
});
|
|
261
|
+
throw new Error(`Failed to get task by ID: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
});
|
|
213
265
|
// Task Mutation Operations
|
|
214
266
|
server.addTool({
|
|
215
267
|
name: "create-task",
|
|
@@ -470,6 +522,52 @@ server.addTool({
|
|
|
470
522
|
}
|
|
471
523
|
}
|
|
472
524
|
});
|
|
525
|
+
server.addTool({
|
|
526
|
+
name: "update-task-planned-time",
|
|
527
|
+
description: "Update the planned time (time estimate) for a task",
|
|
528
|
+
parameters: updateTaskPlannedTimeSchema,
|
|
529
|
+
execute: async (args, { session, log }) => {
|
|
530
|
+
try {
|
|
531
|
+
// Extract parameters
|
|
532
|
+
const { taskId, timeEstimateMinutes, limitResponsePayload } = args;
|
|
533
|
+
log.info("Updating task planned time", {
|
|
534
|
+
taskId: taskId,
|
|
535
|
+
timeEstimateMinutes: timeEstimateMinutes,
|
|
536
|
+
limitResponsePayload: limitResponsePayload
|
|
537
|
+
});
|
|
538
|
+
// Get the appropriate client based on transport type
|
|
539
|
+
const sunsamaClient = getSunsamaClient(session);
|
|
540
|
+
// Call sunsamaClient.updateTaskPlannedTime(taskId, timeEstimateMinutes, limitResponsePayload)
|
|
541
|
+
const result = await sunsamaClient.updateTaskPlannedTime(taskId, timeEstimateMinutes, limitResponsePayload);
|
|
542
|
+
log.info("Successfully updated task planned time", {
|
|
543
|
+
taskId: taskId,
|
|
544
|
+
timeEstimateMinutes: timeEstimateMinutes,
|
|
545
|
+
success: result.success
|
|
546
|
+
});
|
|
547
|
+
return {
|
|
548
|
+
content: [
|
|
549
|
+
{
|
|
550
|
+
type: "text",
|
|
551
|
+
text: JSON.stringify({
|
|
552
|
+
success: result.success,
|
|
553
|
+
taskId: taskId,
|
|
554
|
+
timeEstimateMinutes: timeEstimateMinutes,
|
|
555
|
+
updatedFields: result.updatedFields
|
|
556
|
+
})
|
|
557
|
+
}
|
|
558
|
+
]
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
catch (error) {
|
|
562
|
+
log.error("Failed to update task planned time", {
|
|
563
|
+
taskId: args.taskId,
|
|
564
|
+
timeEstimateMinutes: args.timeEstimateMinutes,
|
|
565
|
+
error: error instanceof Error ? error.message : 'Unknown error'
|
|
566
|
+
});
|
|
567
|
+
throw new Error(`Failed to update task planned time: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
});
|
|
473
571
|
// Stream Operations
|
|
474
572
|
server.addTool({
|
|
475
573
|
name: "get-streams",
|
|
@@ -561,6 +659,11 @@ Uses HTTP Basic Auth headers (per-request authentication):
|
|
|
561
659
|
- Returns: TSV of trimmed archived Task objects with pagination metadata header
|
|
562
660
|
- Pagination: Uses limit+1 pattern to determine if more results are available
|
|
563
661
|
|
|
662
|
+
- **get-task-by-id**: Get a specific task by its ID
|
|
663
|
+
- Parameters:
|
|
664
|
+
- \`taskId\` (required): The ID of the task to retrieve
|
|
665
|
+
- Returns: JSON with complete Task object if found, or null if not found
|
|
666
|
+
|
|
564
667
|
- **create-task**: Create a new task with optional properties
|
|
565
668
|
- Parameters:
|
|
566
669
|
- \`text\` (required): Task title/description
|
package/dist/schemas.d.ts
CHANGED
|
@@ -27,6 +27,13 @@ export declare const getArchivedTasksSchema: z.ZodObject<{
|
|
|
27
27
|
offset?: number | undefined;
|
|
28
28
|
limit?: number | undefined;
|
|
29
29
|
}>;
|
|
30
|
+
export declare const getTaskByIdSchema: z.ZodObject<{
|
|
31
|
+
taskId: z.ZodString;
|
|
32
|
+
}, "strip", z.ZodTypeAny, {
|
|
33
|
+
taskId: string;
|
|
34
|
+
}, {
|
|
35
|
+
taskId: string;
|
|
36
|
+
}>;
|
|
30
37
|
/**
|
|
31
38
|
* User Operation Schemas
|
|
32
39
|
*/
|
|
@@ -49,22 +56,22 @@ export declare const createTaskSchema: z.ZodObject<{
|
|
|
49
56
|
taskId: z.ZodOptional<z.ZodString>;
|
|
50
57
|
}, "strip", z.ZodTypeAny, {
|
|
51
58
|
text: string;
|
|
59
|
+
taskId?: string | undefined;
|
|
52
60
|
notes?: string | undefined;
|
|
53
61
|
streamIds?: string[] | undefined;
|
|
54
62
|
timeEstimate?: number | undefined;
|
|
55
63
|
dueDate?: string | undefined;
|
|
56
64
|
snoozeUntil?: string | undefined;
|
|
57
65
|
private?: boolean | undefined;
|
|
58
|
-
taskId?: string | undefined;
|
|
59
66
|
}, {
|
|
60
67
|
text: string;
|
|
68
|
+
taskId?: string | undefined;
|
|
61
69
|
notes?: string | undefined;
|
|
62
70
|
streamIds?: string[] | undefined;
|
|
63
71
|
timeEstimate?: number | undefined;
|
|
64
72
|
dueDate?: string | undefined;
|
|
65
73
|
snoozeUntil?: string | undefined;
|
|
66
74
|
private?: boolean | undefined;
|
|
67
|
-
taskId?: string | undefined;
|
|
68
75
|
}>;
|
|
69
76
|
export declare const updateTaskCompleteSchema: z.ZodObject<{
|
|
70
77
|
taskId: z.ZodString;
|
|
@@ -121,6 +128,19 @@ export declare const updateTaskBacklogSchema: z.ZodObject<{
|
|
|
121
128
|
timezone?: string | undefined;
|
|
122
129
|
limitResponsePayload?: boolean | undefined;
|
|
123
130
|
}>;
|
|
131
|
+
export declare const updateTaskPlannedTimeSchema: z.ZodObject<{
|
|
132
|
+
taskId: z.ZodString;
|
|
133
|
+
timeEstimateMinutes: z.ZodNumber;
|
|
134
|
+
limitResponsePayload: z.ZodOptional<z.ZodBoolean>;
|
|
135
|
+
}, "strip", z.ZodTypeAny, {
|
|
136
|
+
taskId: string;
|
|
137
|
+
timeEstimateMinutes: number;
|
|
138
|
+
limitResponsePayload?: boolean | undefined;
|
|
139
|
+
}, {
|
|
140
|
+
taskId: string;
|
|
141
|
+
timeEstimateMinutes: number;
|
|
142
|
+
limitResponsePayload?: boolean | undefined;
|
|
143
|
+
}>;
|
|
124
144
|
/**
|
|
125
145
|
* Response Type Schemas (for validation and documentation)
|
|
126
146
|
*/
|
|
@@ -547,12 +567,14 @@ export type CompletionFilter = z.infer<typeof completionFilterSchema>;
|
|
|
547
567
|
export type GetTasksByDayInput = z.infer<typeof getTasksByDaySchema>;
|
|
548
568
|
export type GetTasksBacklogInput = z.infer<typeof getTasksBacklogSchema>;
|
|
549
569
|
export type GetArchivedTasksInput = z.infer<typeof getArchivedTasksSchema>;
|
|
570
|
+
export type GetTaskByIdInput = z.infer<typeof getTaskByIdSchema>;
|
|
550
571
|
export type GetUserInput = z.infer<typeof getUserSchema>;
|
|
551
572
|
export type GetStreamsInput = z.infer<typeof getStreamsSchema>;
|
|
552
573
|
export type CreateTaskInput = z.infer<typeof createTaskSchema>;
|
|
553
574
|
export type UpdateTaskCompleteInput = z.infer<typeof updateTaskCompleteSchema>;
|
|
554
575
|
export type DeleteTaskInput = z.infer<typeof deleteTaskSchema>;
|
|
555
576
|
export type UpdateTaskSnoozeDateInput = z.infer<typeof updateTaskSnoozeDateSchema>;
|
|
577
|
+
export type UpdateTaskPlannedTimeInput = z.infer<typeof updateTaskPlannedTimeSchema>;
|
|
556
578
|
export type User = z.infer<typeof userSchema>;
|
|
557
579
|
export type Task = z.infer<typeof taskSchema>;
|
|
558
580
|
export type Stream = z.infer<typeof streamSchema>;
|
package/dist/schemas.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../src/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;GAEG;AAGH,eAAO,MAAM,sBAAsB,+CAA6C,CAAC;AAGjF,eAAO,MAAM,mBAAmB;;;;;;;;;;;;EAI9B,CAAC;AAGH,eAAO,MAAM,qBAAqB,gDAAe,CAAC;AAGlD,eAAO,MAAM,sBAAsB;;;;;;;;;EAGjC,CAAC;AAEH;;GAEG;AAGH,eAAO,MAAM,aAAa,gDAAe,CAAC;AAE1C;;GAEG;AAGH,eAAO,MAAM,gBAAgB,gDAAe,CAAC;AAE7C;;GAEG;AAGH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;EAS3B,CAAC;AAGH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;EAInC,CAAC;AAGH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;EAI3B,CAAC;AAGH,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;EAKrC,CAAC;AAGH,eAAO,MAAM,uBAAuB;;;;;;;;;;;;EAIlC,CAAC;AAEH;;GAEG;AAGH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;EAO5B,CAAC;AAGH,eAAO,MAAM,WAAW;;;;;;;;;;;;EAItB,CAAC;AAGH,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAKrB,CAAC;AAGH,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAYrB,CAAC;AAGH,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;EAQvB,CAAC;AAEH;;GAEG;AAGH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAE7B,CAAC;AAGH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAG9B,CAAC;AAGH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAGhC,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;EAI9B,CAAC;AAEH;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AACrE,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AACzE,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAC3E,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AACzD,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE/D,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC/D,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAC/E,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC/D,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../src/schemas.ts"],"names":[],"mappings":"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"}
|
package/dist/schemas.js
CHANGED
|
@@ -17,6 +17,10 @@ export const getArchivedTasksSchema = z.object({
|
|
|
17
17
|
offset: z.number().int().min(0).optional().describe("Pagination offset (defaults to 0)"),
|
|
18
18
|
limit: z.number().int().min(1).max(1000).optional().describe("Maximum number of tasks to return (defaults to 100)"),
|
|
19
19
|
});
|
|
20
|
+
// Get task by ID parameters
|
|
21
|
+
export const getTaskByIdSchema = z.object({
|
|
22
|
+
taskId: z.string().min(1, "Task ID is required").describe("The ID of the task to retrieve"),
|
|
23
|
+
});
|
|
20
24
|
/**
|
|
21
25
|
* User Operation Schemas
|
|
22
26
|
*/
|
|
@@ -66,6 +70,12 @@ export const updateTaskBacklogSchema = z.object({
|
|
|
66
70
|
timezone: z.string().optional().describe("Timezone string (e.g., 'America/New_York'). If not provided, uses user's default timezone"),
|
|
67
71
|
limitResponsePayload: z.boolean().optional().describe("Whether to limit the response payload size"),
|
|
68
72
|
});
|
|
73
|
+
// Update task planned time parameters
|
|
74
|
+
export const updateTaskPlannedTimeSchema = z.object({
|
|
75
|
+
taskId: z.string().min(1, "Task ID is required").describe("The ID of the task to update planned time for"),
|
|
76
|
+
timeEstimateMinutes: z.number().int().min(0).describe("Time estimate in minutes (use 0 to clear the time estimate)"),
|
|
77
|
+
limitResponsePayload: z.boolean().optional().describe("Whether to limit the response payload size"),
|
|
78
|
+
});
|
|
69
79
|
/**
|
|
70
80
|
* Response Type Schemas (for validation and documentation)
|
|
71
81
|
*/
|
package/dist/schemas.test.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, test, expect } from "bun:test";
|
|
2
|
-
import { completionFilterSchema, getTasksByDaySchema, getTasksBacklogSchema, getArchivedTasksSchema, getUserSchema, getStreamsSchema, createTaskSchema, updateTaskCompleteSchema, deleteTaskSchema, updateTaskSnoozeDateSchema, updateTaskBacklogSchema, userProfileSchema, groupSchema, userSchema, taskSchema, streamSchema, userResponseSchema, tasksResponseSchema, streamsResponseSchema, errorResponseSchema, } from "./schemas.js";
|
|
2
|
+
import { completionFilterSchema, getTasksByDaySchema, getTasksBacklogSchema, getArchivedTasksSchema, getUserSchema, getStreamsSchema, createTaskSchema, updateTaskCompleteSchema, deleteTaskSchema, updateTaskSnoozeDateSchema, updateTaskBacklogSchema, updateTaskPlannedTimeSchema, userProfileSchema, groupSchema, userSchema, taskSchema, streamSchema, userResponseSchema, tasksResponseSchema, streamsResponseSchema, errorResponseSchema, } from "./schemas.js";
|
|
3
3
|
describe("Tool Parameter Schemas", () => {
|
|
4
4
|
describe("completionFilterSchema", () => {
|
|
5
5
|
test("should accept valid completion filter values", () => {
|
|
@@ -202,6 +202,56 @@ describe("Tool Parameter Schemas", () => {
|
|
|
202
202
|
expect(() => updateTaskBacklogSchema.parse({})).toThrow();
|
|
203
203
|
});
|
|
204
204
|
});
|
|
205
|
+
describe("updateTaskPlannedTimeSchema", () => {
|
|
206
|
+
test("should accept valid task planned time input", () => {
|
|
207
|
+
const validInput = {
|
|
208
|
+
taskId: "task-123",
|
|
209
|
+
timeEstimateMinutes: 45,
|
|
210
|
+
limitResponsePayload: true,
|
|
211
|
+
};
|
|
212
|
+
expect(() => updateTaskPlannedTimeSchema.parse(validInput)).not.toThrow();
|
|
213
|
+
});
|
|
214
|
+
test("should accept minimal required input", () => {
|
|
215
|
+
const minimalInput = {
|
|
216
|
+
taskId: "task-123",
|
|
217
|
+
timeEstimateMinutes: 30
|
|
218
|
+
};
|
|
219
|
+
expect(() => updateTaskPlannedTimeSchema.parse(minimalInput)).not.toThrow();
|
|
220
|
+
});
|
|
221
|
+
test("should accept zero time estimate", () => {
|
|
222
|
+
const zeroInput = {
|
|
223
|
+
taskId: "task-123",
|
|
224
|
+
timeEstimateMinutes: 0
|
|
225
|
+
};
|
|
226
|
+
expect(() => updateTaskPlannedTimeSchema.parse(zeroInput)).not.toThrow();
|
|
227
|
+
});
|
|
228
|
+
test("should reject empty task ID", () => {
|
|
229
|
+
expect(() => updateTaskPlannedTimeSchema.parse({
|
|
230
|
+
taskId: "",
|
|
231
|
+
timeEstimateMinutes: 30
|
|
232
|
+
})).toThrow();
|
|
233
|
+
expect(() => updateTaskPlannedTimeSchema.parse({
|
|
234
|
+
timeEstimateMinutes: 30
|
|
235
|
+
})).toThrow();
|
|
236
|
+
});
|
|
237
|
+
test("should reject negative time estimate", () => {
|
|
238
|
+
expect(() => updateTaskPlannedTimeSchema.parse({
|
|
239
|
+
taskId: "task-123",
|
|
240
|
+
timeEstimateMinutes: -1
|
|
241
|
+
})).toThrow();
|
|
242
|
+
});
|
|
243
|
+
test("should reject non-integer time estimate", () => {
|
|
244
|
+
expect(() => updateTaskPlannedTimeSchema.parse({
|
|
245
|
+
taskId: "task-123",
|
|
246
|
+
timeEstimateMinutes: 30.5
|
|
247
|
+
})).toThrow();
|
|
248
|
+
});
|
|
249
|
+
test("should reject missing time estimate", () => {
|
|
250
|
+
expect(() => updateTaskPlannedTimeSchema.parse({
|
|
251
|
+
taskId: "task-123"
|
|
252
|
+
})).toThrow();
|
|
253
|
+
});
|
|
254
|
+
});
|
|
205
255
|
});
|
|
206
256
|
describe("Response Schemas", () => {
|
|
207
257
|
describe("userProfileSchema", () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-sunsama",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "MCP server for Sunsama API integration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"@types/papaparse": "^5.3.16",
|
|
27
27
|
"fastmcp": "3.3.1",
|
|
28
28
|
"papaparse": "^5.5.3",
|
|
29
|
-
"sunsama-api": "0.
|
|
29
|
+
"sunsama-api": "0.7.0",
|
|
30
30
|
"zod": "3.24.4"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
package/src/main.ts
CHANGED
|
@@ -10,11 +10,13 @@ import {
|
|
|
10
10
|
deleteTaskSchema,
|
|
11
11
|
getArchivedTasksSchema,
|
|
12
12
|
getStreamsSchema,
|
|
13
|
+
getTaskByIdSchema,
|
|
13
14
|
getTasksBacklogSchema,
|
|
14
15
|
getTasksByDaySchema,
|
|
15
16
|
getUserSchema,
|
|
16
17
|
updateTaskBacklogSchema,
|
|
17
18
|
updateTaskCompleteSchema,
|
|
19
|
+
updateTaskPlannedTimeSchema,
|
|
18
20
|
updateTaskSnoozeDateSchema
|
|
19
21
|
} from "./schemas.js";
|
|
20
22
|
import { getSunsamaClient } from "./utils/client-resolver.js";
|
|
@@ -32,14 +34,15 @@ if (transportConfig.transportType === "stdio") {
|
|
|
32
34
|
|
|
33
35
|
const server = new FastMCP({
|
|
34
36
|
name: "Sunsama API Server",
|
|
35
|
-
version: "0.
|
|
37
|
+
version: "0.8.0",
|
|
36
38
|
instructions: `
|
|
37
39
|
This MCP server provides access to the Sunsama API for task and project management.
|
|
38
40
|
|
|
39
41
|
Available tools:
|
|
40
42
|
- Authentication: login, logout, check authentication status
|
|
41
43
|
- User operations: get current user information
|
|
42
|
-
- Task operations: get tasks by day, get backlog tasks, get archived tasks
|
|
44
|
+
- Task operations: get tasks by day, get backlog tasks, get archived tasks, get task by ID
|
|
45
|
+
- Task mutations: create tasks, mark complete, delete tasks, reschedule tasks, update planned time
|
|
43
46
|
- Stream operations: get streams/channels for the user's group
|
|
44
47
|
|
|
45
48
|
Authentication is required for all operations. You can either:
|
|
@@ -262,6 +265,64 @@ ${toTsv(trimmedTasks)}`;
|
|
|
262
265
|
}
|
|
263
266
|
});
|
|
264
267
|
|
|
268
|
+
server.addTool({
|
|
269
|
+
name: "get-task-by-id",
|
|
270
|
+
description: "Get a specific task by its ID",
|
|
271
|
+
parameters: getTaskByIdSchema,
|
|
272
|
+
execute: async (args, {session, log}) => {
|
|
273
|
+
try {
|
|
274
|
+
const {taskId} = args;
|
|
275
|
+
|
|
276
|
+
log.info("Getting task by ID", {
|
|
277
|
+
taskId: taskId
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
// Get the appropriate client based on transport type
|
|
281
|
+
const sunsamaClient = getSunsamaClient(session as SessionData | null);
|
|
282
|
+
|
|
283
|
+
// Get the specific task by ID
|
|
284
|
+
const task = await sunsamaClient.getTaskById(taskId);
|
|
285
|
+
|
|
286
|
+
if (task) {
|
|
287
|
+
log.info("Successfully retrieved task by ID", {
|
|
288
|
+
taskId: taskId,
|
|
289
|
+
taskText: task.text
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
return {
|
|
293
|
+
content: [
|
|
294
|
+
{
|
|
295
|
+
type: "text",
|
|
296
|
+
text: JSON.stringify(task, null, 2)
|
|
297
|
+
}
|
|
298
|
+
]
|
|
299
|
+
};
|
|
300
|
+
} else {
|
|
301
|
+
log.info("Task not found", {
|
|
302
|
+
taskId: taskId
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
return {
|
|
306
|
+
content: [
|
|
307
|
+
{
|
|
308
|
+
type: "text",
|
|
309
|
+
text: "null"
|
|
310
|
+
}
|
|
311
|
+
]
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
} catch (error) {
|
|
316
|
+
log.error("Failed to get task by ID", {
|
|
317
|
+
taskId: args.taskId,
|
|
318
|
+
error: error instanceof Error ? error.message : 'Unknown error'
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
throw new Error(`Failed to get task by ID: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
|
|
265
326
|
// Task Mutation Operations
|
|
266
327
|
server.addTool({
|
|
267
328
|
name: "create-task",
|
|
@@ -571,6 +632,63 @@ server.addTool({
|
|
|
571
632
|
}
|
|
572
633
|
});
|
|
573
634
|
|
|
635
|
+
server.addTool({
|
|
636
|
+
name: "update-task-planned-time",
|
|
637
|
+
description: "Update the planned time (time estimate) for a task",
|
|
638
|
+
parameters: updateTaskPlannedTimeSchema,
|
|
639
|
+
execute: async (args, {session, log}) => {
|
|
640
|
+
try {
|
|
641
|
+
// Extract parameters
|
|
642
|
+
const {taskId, timeEstimateMinutes, limitResponsePayload} = args;
|
|
643
|
+
|
|
644
|
+
log.info("Updating task planned time", {
|
|
645
|
+
taskId: taskId,
|
|
646
|
+
timeEstimateMinutes: timeEstimateMinutes,
|
|
647
|
+
limitResponsePayload: limitResponsePayload
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
// Get the appropriate client based on transport type
|
|
651
|
+
const sunsamaClient = getSunsamaClient(session as SessionData | null);
|
|
652
|
+
|
|
653
|
+
// Call sunsamaClient.updateTaskPlannedTime(taskId, timeEstimateMinutes, limitResponsePayload)
|
|
654
|
+
const result = await sunsamaClient.updateTaskPlannedTime(
|
|
655
|
+
taskId,
|
|
656
|
+
timeEstimateMinutes,
|
|
657
|
+
limitResponsePayload
|
|
658
|
+
);
|
|
659
|
+
|
|
660
|
+
log.info("Successfully updated task planned time", {
|
|
661
|
+
taskId: taskId,
|
|
662
|
+
timeEstimateMinutes: timeEstimateMinutes,
|
|
663
|
+
success: result.success
|
|
664
|
+
});
|
|
665
|
+
|
|
666
|
+
return {
|
|
667
|
+
content: [
|
|
668
|
+
{
|
|
669
|
+
type: "text",
|
|
670
|
+
text: JSON.stringify({
|
|
671
|
+
success: result.success,
|
|
672
|
+
taskId: taskId,
|
|
673
|
+
timeEstimateMinutes: timeEstimateMinutes,
|
|
674
|
+
updatedFields: result.updatedFields
|
|
675
|
+
})
|
|
676
|
+
}
|
|
677
|
+
]
|
|
678
|
+
};
|
|
679
|
+
|
|
680
|
+
} catch (error) {
|
|
681
|
+
log.error("Failed to update task planned time", {
|
|
682
|
+
taskId: args.taskId,
|
|
683
|
+
timeEstimateMinutes: args.timeEstimateMinutes,
|
|
684
|
+
error: error instanceof Error ? error.message : 'Unknown error'
|
|
685
|
+
});
|
|
686
|
+
|
|
687
|
+
throw new Error(`Failed to update task planned time: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
});
|
|
691
|
+
|
|
574
692
|
// Stream Operations
|
|
575
693
|
server.addTool({
|
|
576
694
|
name: "get-streams",
|
|
@@ -668,6 +786,11 @@ Uses HTTP Basic Auth headers (per-request authentication):
|
|
|
668
786
|
- Returns: TSV of trimmed archived Task objects with pagination metadata header
|
|
669
787
|
- Pagination: Uses limit+1 pattern to determine if more results are available
|
|
670
788
|
|
|
789
|
+
- **get-task-by-id**: Get a specific task by its ID
|
|
790
|
+
- Parameters:
|
|
791
|
+
- \`taskId\` (required): The ID of the task to retrieve
|
|
792
|
+
- Returns: JSON with complete Task object if found, or null if not found
|
|
793
|
+
|
|
671
794
|
- **create-task**: Create a new task with optional properties
|
|
672
795
|
- Parameters:
|
|
673
796
|
- \`text\` (required): Task title/description
|
package/src/schemas.test.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
deleteTaskSchema,
|
|
12
12
|
updateTaskSnoozeDateSchema,
|
|
13
13
|
updateTaskBacklogSchema,
|
|
14
|
+
updateTaskPlannedTimeSchema,
|
|
14
15
|
userProfileSchema,
|
|
15
16
|
groupSchema,
|
|
16
17
|
userSchema,
|
|
@@ -283,6 +284,73 @@ describe("Tool Parameter Schemas", () => {
|
|
|
283
284
|
).toThrow();
|
|
284
285
|
});
|
|
285
286
|
});
|
|
287
|
+
|
|
288
|
+
describe("updateTaskPlannedTimeSchema", () => {
|
|
289
|
+
test("should accept valid task planned time input", () => {
|
|
290
|
+
const validInput = {
|
|
291
|
+
taskId: "task-123",
|
|
292
|
+
timeEstimateMinutes: 45,
|
|
293
|
+
limitResponsePayload: true,
|
|
294
|
+
};
|
|
295
|
+
expect(() => updateTaskPlannedTimeSchema.parse(validInput)).not.toThrow();
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
test("should accept minimal required input", () => {
|
|
299
|
+
const minimalInput = {
|
|
300
|
+
taskId: "task-123",
|
|
301
|
+
timeEstimateMinutes: 30
|
|
302
|
+
};
|
|
303
|
+
expect(() => updateTaskPlannedTimeSchema.parse(minimalInput)).not.toThrow();
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
test("should accept zero time estimate", () => {
|
|
307
|
+
const zeroInput = {
|
|
308
|
+
taskId: "task-123",
|
|
309
|
+
timeEstimateMinutes: 0
|
|
310
|
+
};
|
|
311
|
+
expect(() => updateTaskPlannedTimeSchema.parse(zeroInput)).not.toThrow();
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
test("should reject empty task ID", () => {
|
|
315
|
+
expect(() =>
|
|
316
|
+
updateTaskPlannedTimeSchema.parse({
|
|
317
|
+
taskId: "",
|
|
318
|
+
timeEstimateMinutes: 30
|
|
319
|
+
})
|
|
320
|
+
).toThrow();
|
|
321
|
+
expect(() =>
|
|
322
|
+
updateTaskPlannedTimeSchema.parse({
|
|
323
|
+
timeEstimateMinutes: 30
|
|
324
|
+
})
|
|
325
|
+
).toThrow();
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
test("should reject negative time estimate", () => {
|
|
329
|
+
expect(() =>
|
|
330
|
+
updateTaskPlannedTimeSchema.parse({
|
|
331
|
+
taskId: "task-123",
|
|
332
|
+
timeEstimateMinutes: -1
|
|
333
|
+
})
|
|
334
|
+
).toThrow();
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
test("should reject non-integer time estimate", () => {
|
|
338
|
+
expect(() =>
|
|
339
|
+
updateTaskPlannedTimeSchema.parse({
|
|
340
|
+
taskId: "task-123",
|
|
341
|
+
timeEstimateMinutes: 30.5
|
|
342
|
+
})
|
|
343
|
+
).toThrow();
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
test("should reject missing time estimate", () => {
|
|
347
|
+
expect(() =>
|
|
348
|
+
updateTaskPlannedTimeSchema.parse({
|
|
349
|
+
taskId: "task-123"
|
|
350
|
+
})
|
|
351
|
+
).toThrow();
|
|
352
|
+
});
|
|
353
|
+
});
|
|
286
354
|
});
|
|
287
355
|
|
|
288
356
|
describe("Response Schemas", () => {
|
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
|
*/
|
|
@@ -82,6 +87,13 @@ export const updateTaskBacklogSchema = z.object({
|
|
|
82
87
|
limitResponsePayload: z.boolean().optional().describe("Whether to limit the response payload size"),
|
|
83
88
|
});
|
|
84
89
|
|
|
90
|
+
// Update task planned time parameters
|
|
91
|
+
export const updateTaskPlannedTimeSchema = z.object({
|
|
92
|
+
taskId: z.string().min(1, "Task ID is required").describe("The ID of the task to update planned time for"),
|
|
93
|
+
timeEstimateMinutes: z.number().int().min(0).describe("Time estimate in minutes (use 0 to clear the time estimate)"),
|
|
94
|
+
limitResponsePayload: z.boolean().optional().describe("Whether to limit the response payload size"),
|
|
95
|
+
});
|
|
96
|
+
|
|
85
97
|
/**
|
|
86
98
|
* Response Type Schemas (for validation and documentation)
|
|
87
99
|
*/
|
|
@@ -175,6 +187,7 @@ export type CompletionFilter = z.infer<typeof completionFilterSchema>;
|
|
|
175
187
|
export type GetTasksByDayInput = z.infer<typeof getTasksByDaySchema>;
|
|
176
188
|
export type GetTasksBacklogInput = z.infer<typeof getTasksBacklogSchema>;
|
|
177
189
|
export type GetArchivedTasksInput = z.infer<typeof getArchivedTasksSchema>;
|
|
190
|
+
export type GetTaskByIdInput = z.infer<typeof getTaskByIdSchema>;
|
|
178
191
|
export type GetUserInput = z.infer<typeof getUserSchema>;
|
|
179
192
|
export type GetStreamsInput = z.infer<typeof getStreamsSchema>;
|
|
180
193
|
|
|
@@ -182,6 +195,7 @@ export type CreateTaskInput = z.infer<typeof createTaskSchema>;
|
|
|
182
195
|
export type UpdateTaskCompleteInput = z.infer<typeof updateTaskCompleteSchema>;
|
|
183
196
|
export type DeleteTaskInput = z.infer<typeof deleteTaskSchema>;
|
|
184
197
|
export type UpdateTaskSnoozeDateInput = z.infer<typeof updateTaskSnoozeDateSchema>;
|
|
198
|
+
export type UpdateTaskPlannedTimeInput = z.infer<typeof updateTaskPlannedTimeSchema>;
|
|
185
199
|
|
|
186
200
|
export type User = z.infer<typeof userSchema>;
|
|
187
201
|
export type Task = z.infer<typeof taskSchema>;
|