mcp-sunsama 0.7.0 → 0.9.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 +1 -1
- package/README.md +2 -1
- package/bun.lock +8 -2
- package/dist/main.js +109 -2
- package/dist/schemas.d.ts +48 -0
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +19 -0
- package/dist/schemas.test.js +51 -1
- package/package.json +2 -2
- package/src/main.ts +134 -1
- package/src/schemas.test.ts +68 -0
- package/src/schemas.ts +23 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,36 @@
|
|
|
1
1
|
# mcp-sunsama
|
|
2
2
|
|
|
3
|
+
## 0.9.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 953ecb7: Add update-task-notes tool for updating task notes content
|
|
8
|
+
|
|
9
|
+
- New `update-task-notes` tool supports updating task notes with HTML or Markdown content
|
|
10
|
+
- Accepts either `{html: string}` or `{markdown: string}` content format (mutually exclusive)
|
|
11
|
+
- Includes optional `limitResponsePayload` parameter for response optimization
|
|
12
|
+
- Integrates with Sunsama's collaborative editing system for proper synchronization
|
|
13
|
+
- Follows established patterns for tool implementation and error handling
|
|
14
|
+
- Updates API documentation, README, and CLAUDE.md with new tool information
|
|
15
|
+
|
|
16
|
+
## 0.8.0
|
|
17
|
+
|
|
18
|
+
### Minor Changes
|
|
19
|
+
|
|
20
|
+
- 6585510: feat: add update-task-planned-time tool for updating task time estimates
|
|
21
|
+
|
|
22
|
+
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.
|
|
23
|
+
|
|
24
|
+
Features:
|
|
25
|
+
|
|
26
|
+
- Update task time estimates in minutes (converted to seconds for API)
|
|
27
|
+
- Support for clearing time estimates by setting to 0
|
|
28
|
+
- Comprehensive input validation and error handling
|
|
29
|
+
- Full test coverage including edge cases
|
|
30
|
+
- Documentation updates for README and CLAUDE.md
|
|
31
|
+
|
|
32
|
+
The implementation follows established patterns in the codebase and leverages the existing sunsama-api updateTaskPlannedTime method.
|
|
33
|
+
|
|
3
34
|
## 0.7.0
|
|
4
35
|
|
|
5
36
|
### Minor Changes
|
package/CLAUDE.md
CHANGED
|
@@ -127,7 +127,7 @@ Optional:
|
|
|
127
127
|
### Task Operations
|
|
128
128
|
Full CRUD support:
|
|
129
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-snooze-date`, `update-task-backlog`, `delete-task`
|
|
130
|
+
- **Write**: `create-task`, `update-task-complete`, `update-task-planned-time`, `update-task-notes`, `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
|
@@ -94,6 +94,8 @@ Add this configuration to your Claude Desktop MCP settings:
|
|
|
94
94
|
- `get-archived-tasks` - Get archived tasks with pagination (includes hasMore flag for LLM context)
|
|
95
95
|
- `get-task-by-id` - Get a specific task by its ID
|
|
96
96
|
- `update-task-complete` - Mark tasks as complete
|
|
97
|
+
- `update-task-planned-time` - Update the planned time (time estimate) for tasks
|
|
98
|
+
- `update-task-notes` - Update task notes content with HTML or Markdown
|
|
97
99
|
- `update-task-snooze-date` - Reschedule tasks to different dates
|
|
98
100
|
- `update-task-backlog` - Move tasks to the backlog
|
|
99
101
|
- `delete-task` - Delete tasks permanently
|
|
@@ -141,7 +143,6 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
|
|
|
141
143
|
|
|
142
144
|
## Support
|
|
143
145
|
|
|
144
|
-
- [Sunsama API Documentation](https://help.sunsama.com)
|
|
145
146
|
- [sunsama-api Library](https://github.com/robertn702/sunsama-api) - The underlying API client
|
|
146
147
|
- [Model Context Protocol Documentation](https://modelcontextprotocol.io)
|
|
147
148
|
- [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.8.0",
|
|
11
11
|
"zod": "3.24.4",
|
|
12
12
|
},
|
|
13
13
|
"devDependencies": {
|
|
@@ -58,6 +58,8 @@
|
|
|
58
58
|
|
|
59
59
|
"@manypkg/get-packages": ["@manypkg/get-packages@1.1.3", "", { "dependencies": { "@babel/runtime": "^7.5.5", "@changesets/types": "^4.0.1", "@manypkg/find-root": "^1.1.0", "fs-extra": "^8.1.0", "globby": "^11.0.0", "read-yaml-file": "^1.1.0" } }, "sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A=="],
|
|
60
60
|
|
|
61
|
+
"@mixmark-io/domino": ["@mixmark-io/domino@2.2.0", "", {}, "sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw=="],
|
|
62
|
+
|
|
61
63
|
"@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.12.1", "", { "dependencies": { "ajv": "^6.12.6", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-KG1CZhZfWg+u8pxeM/mByJDScJSrjjxLc8fwQqbsS8xCjBmQfMNEBTotYdNanKekepnfRI85GtgQlctLFpcYPw=="],
|
|
62
64
|
|
|
63
65
|
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
|
|
@@ -282,6 +284,8 @@
|
|
|
282
284
|
|
|
283
285
|
"lodash.startcase": ["lodash.startcase@4.4.0", "", {}, "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg=="],
|
|
284
286
|
|
|
287
|
+
"marked": ["marked@14.1.4", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-vkVZ8ONmUdPnjCKc5uTRvmkRbx4EAi2OkTOXmfTDhZz3OFqMNBM1oTTWwTr4HY4uAEojhzPf+Fy8F1DWa3Sndg=="],
|
|
288
|
+
|
|
285
289
|
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
|
|
286
290
|
|
|
287
291
|
"mcp-proxy": ["mcp-proxy@5.1.0", "", { "dependencies": { "@modelcontextprotocol/sdk": "^1.12.1", "eventsource": "^4.0.0", "yargs": "^18.0.0" }, "bin": { "mcp-proxy": "dist/bin/mcp-proxy.js" } }, "sha512-6pfAdXFOvfpqse9dMLuQo8WTSFdD0al8vhr0Nx5EWv+dR6aTAHphdQj9NUP2xej2bhaWzJ5p8BRwbvXufOjJHA=="],
|
|
@@ -426,7 +430,7 @@
|
|
|
426
430
|
|
|
427
431
|
"strtok3": ["strtok3@10.3.1", "", { "dependencies": { "@tokenizer/token": "^0.3.0" } }, "sha512-3JWEZM6mfix/GCJBBUrkA8p2Id2pBkyTkVCJKto55w080QBKZ+8R171fGrbiSp+yMO/u6F8/yUh7K4V9K+YCnw=="],
|
|
428
432
|
|
|
429
|
-
"sunsama-api": ["sunsama-api@0.
|
|
433
|
+
"sunsama-api": ["sunsama-api@0.8.0", "", { "dependencies": { "graphql": "^16.11.0", "graphql-tag": "^2.12.6", "marked": "^14.1.3", "tough-cookie": "^5.1.2", "tslib": "^2.8.1", "turndown": "^7.2.0", "yjs": "^13.6.27", "zod": "^3.25.64" } }, "sha512-EiUqEc2OIDsmfCHQX54Pu+xZDfJBTMjHAcwhWqHVIWTp2emMyitUuSDvABEqupoXrzITZw+XW0lxNZUQaW4lqQ=="],
|
|
430
434
|
|
|
431
435
|
"term-size": ["term-size@2.2.1", "", {}, "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg=="],
|
|
432
436
|
|
|
@@ -446,6 +450,8 @@
|
|
|
446
450
|
|
|
447
451
|
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
|
448
452
|
|
|
453
|
+
"turndown": ["turndown@7.2.0", "", { "dependencies": { "@mixmark-io/domino": "^2.2.0" } }, "sha512-eCZGBN4nNNqM9Owkv9HAtWRYfLA4h909E/WGAWWBpmB275ehNhZyk87/Tpvjbp0jjNl9XwCsbe6bm6CqFsgD+A=="],
|
|
454
|
+
|
|
449
455
|
"type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="],
|
|
450
456
|
|
|
451
457
|
"typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="],
|
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, getTaskByIdSchema, getTasksBacklogSchema, getTasksByDaySchema, getUserSchema, updateTaskBacklogSchema, updateTaskCompleteSchema, updateTaskSnoozeDateSchema } from "./schemas.js";
|
|
6
|
+
import { createTaskSchema, deleteTaskSchema, getArchivedTasksSchema, getStreamsSchema, getTaskByIdSchema, getTasksBacklogSchema, getTasksByDaySchema, getUserSchema, updateTaskBacklogSchema, updateTaskCompleteSchema, updateTaskNotesSchema, 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,7 +16,7 @@ if (transportConfig.transportType === "stdio") {
|
|
|
16
16
|
}
|
|
17
17
|
const server = new FastMCP({
|
|
18
18
|
name: "Sunsama API Server",
|
|
19
|
-
version: "0.
|
|
19
|
+
version: "0.9.0",
|
|
20
20
|
instructions: `
|
|
21
21
|
This MCP server provides access to the Sunsama API for task and project management.
|
|
22
22
|
|
|
@@ -24,6 +24,7 @@ Available tools:
|
|
|
24
24
|
- Authentication: login, logout, check authentication status
|
|
25
25
|
- User operations: get current user information
|
|
26
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, update task notes
|
|
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:
|
|
@@ -521,6 +522,103 @@ server.addTool({
|
|
|
521
522
|
}
|
|
522
523
|
}
|
|
523
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
|
+
});
|
|
571
|
+
server.addTool({
|
|
572
|
+
name: "update-task-notes",
|
|
573
|
+
description: "Update the notes content for a task",
|
|
574
|
+
parameters: updateTaskNotesSchema,
|
|
575
|
+
execute: async (args, { session, log }) => {
|
|
576
|
+
try {
|
|
577
|
+
// Extract parameters
|
|
578
|
+
const { taskId, content, limitResponsePayload } = args;
|
|
579
|
+
log.info("Updating task notes", {
|
|
580
|
+
taskId: taskId,
|
|
581
|
+
contentType: 'html' in content ? 'html' : 'markdown',
|
|
582
|
+
contentLength: ('html' in content ? content.html : content.markdown).length,
|
|
583
|
+
limitResponsePayload: limitResponsePayload
|
|
584
|
+
});
|
|
585
|
+
// Get the appropriate client based on transport type
|
|
586
|
+
const sunsamaClient = getSunsamaClient(session);
|
|
587
|
+
// Build options object
|
|
588
|
+
const options = {};
|
|
589
|
+
if (limitResponsePayload !== undefined)
|
|
590
|
+
options.limitResponsePayload = limitResponsePayload;
|
|
591
|
+
// Call sunsamaClient.updateTaskNotes(taskId, content, options)
|
|
592
|
+
const result = await sunsamaClient.updateTaskNotes(taskId, content, options);
|
|
593
|
+
log.info("Successfully updated task notes", {
|
|
594
|
+
taskId: taskId,
|
|
595
|
+
success: result.success,
|
|
596
|
+
contentType: 'html' in content ? 'html' : 'markdown'
|
|
597
|
+
});
|
|
598
|
+
return {
|
|
599
|
+
content: [
|
|
600
|
+
{
|
|
601
|
+
type: "text",
|
|
602
|
+
text: JSON.stringify({
|
|
603
|
+
success: result.success,
|
|
604
|
+
taskId: taskId,
|
|
605
|
+
notesUpdated: true,
|
|
606
|
+
updatedFields: result.updatedFields
|
|
607
|
+
})
|
|
608
|
+
}
|
|
609
|
+
]
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
catch (error) {
|
|
613
|
+
log.error("Failed to update task notes", {
|
|
614
|
+
taskId: args.taskId,
|
|
615
|
+
contentType: 'html' in args.content ? 'html' : 'markdown',
|
|
616
|
+
error: error instanceof Error ? error.message : 'Unknown error'
|
|
617
|
+
});
|
|
618
|
+
throw new Error(`Failed to update task notes: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
});
|
|
524
622
|
// Stream Operations
|
|
525
623
|
server.addTool({
|
|
526
624
|
name: "get-streams",
|
|
@@ -651,6 +749,15 @@ Uses HTTP Basic Auth headers (per-request authentication):
|
|
|
651
749
|
- \`limitResponsePayload\` (optional): Whether to limit response size
|
|
652
750
|
- Returns: JSON with update result
|
|
653
751
|
|
|
752
|
+
- **update-task-notes**: Update task notes content
|
|
753
|
+
- Parameters:
|
|
754
|
+
- \`taskId\` (required): The ID of the task to update notes for
|
|
755
|
+
- \`content\` (required): Task notes content in either HTML or Markdown format
|
|
756
|
+
- \`{html: string}\` OR \`{markdown: string}\` (mutually exclusive)
|
|
757
|
+
- \`limitResponsePayload\` (optional): Whether to limit response size (defaults to true)
|
|
758
|
+
- Returns: JSON with update result
|
|
759
|
+
- Note: Supports both HTML and Markdown content with automatic format conversion
|
|
760
|
+
|
|
654
761
|
### Stream Operations
|
|
655
762
|
- **get-streams**: Get streams for the user's group
|
|
656
763
|
- Parameters: none
|
package/dist/schemas.d.ts
CHANGED
|
@@ -128,6 +128,52 @@ export declare const updateTaskBacklogSchema: z.ZodObject<{
|
|
|
128
128
|
timezone?: string | undefined;
|
|
129
129
|
limitResponsePayload?: boolean | undefined;
|
|
130
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
|
+
}>;
|
|
144
|
+
export declare const updateTaskNotesSchema: z.ZodObject<{
|
|
145
|
+
taskId: z.ZodString;
|
|
146
|
+
content: z.ZodUnion<[z.ZodObject<{
|
|
147
|
+
html: z.ZodString;
|
|
148
|
+
}, "strip", z.ZodTypeAny, {
|
|
149
|
+
html: string;
|
|
150
|
+
}, {
|
|
151
|
+
html: string;
|
|
152
|
+
}>, z.ZodObject<{
|
|
153
|
+
markdown: z.ZodString;
|
|
154
|
+
}, "strip", z.ZodTypeAny, {
|
|
155
|
+
markdown: string;
|
|
156
|
+
}, {
|
|
157
|
+
markdown: string;
|
|
158
|
+
}>]>;
|
|
159
|
+
limitResponsePayload: z.ZodOptional<z.ZodBoolean>;
|
|
160
|
+
}, "strip", z.ZodTypeAny, {
|
|
161
|
+
taskId: string;
|
|
162
|
+
content: {
|
|
163
|
+
html: string;
|
|
164
|
+
} | {
|
|
165
|
+
markdown: string;
|
|
166
|
+
};
|
|
167
|
+
limitResponsePayload?: boolean | undefined;
|
|
168
|
+
}, {
|
|
169
|
+
taskId: string;
|
|
170
|
+
content: {
|
|
171
|
+
html: string;
|
|
172
|
+
} | {
|
|
173
|
+
markdown: string;
|
|
174
|
+
};
|
|
175
|
+
limitResponsePayload?: boolean | undefined;
|
|
176
|
+
}>;
|
|
131
177
|
/**
|
|
132
178
|
* Response Type Schemas (for validation and documentation)
|
|
133
179
|
*/
|
|
@@ -561,6 +607,8 @@ export type CreateTaskInput = z.infer<typeof createTaskSchema>;
|
|
|
561
607
|
export type UpdateTaskCompleteInput = z.infer<typeof updateTaskCompleteSchema>;
|
|
562
608
|
export type DeleteTaskInput = z.infer<typeof deleteTaskSchema>;
|
|
563
609
|
export type UpdateTaskSnoozeDateInput = z.infer<typeof updateTaskSnoozeDateSchema>;
|
|
610
|
+
export type UpdateTaskPlannedTimeInput = z.infer<typeof updateTaskPlannedTimeSchema>;
|
|
611
|
+
export type UpdateTaskNotesInput = z.infer<typeof updateTaskNotesSchema>;
|
|
564
612
|
export type User = z.infer<typeof userSchema>;
|
|
565
613
|
export type Task = z.infer<typeof taskSchema>;
|
|
566
614
|
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":"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
|
|
1
|
+
{"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../src/schemas.ts"],"names":[],"mappings":"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"}
|
package/dist/schemas.js
CHANGED
|
@@ -70,6 +70,25 @@ export const updateTaskBacklogSchema = z.object({
|
|
|
70
70
|
timezone: z.string().optional().describe("Timezone string (e.g., 'America/New_York'). If not provided, uses user's default timezone"),
|
|
71
71
|
limitResponsePayload: z.boolean().optional().describe("Whether to limit the response payload size"),
|
|
72
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
|
+
});
|
|
79
|
+
// Update task notes parameters
|
|
80
|
+
export const updateTaskNotesSchema = z.object({
|
|
81
|
+
taskId: z.string().min(1, "Task ID is required").describe("The ID of the task to update notes for"),
|
|
82
|
+
content: z.union([
|
|
83
|
+
z.object({
|
|
84
|
+
html: z.string().describe("HTML content for the task notes")
|
|
85
|
+
}),
|
|
86
|
+
z.object({
|
|
87
|
+
markdown: z.string().describe("Markdown content for the task notes")
|
|
88
|
+
})
|
|
89
|
+
]).describe("Task notes content in either HTML or Markdown format"),
|
|
90
|
+
limitResponsePayload: z.boolean().optional().describe("Whether to limit the response payload size (defaults to true)"),
|
|
91
|
+
});
|
|
73
92
|
/**
|
|
74
93
|
* Response Type Schemas (for validation and documentation)
|
|
75
94
|
*/
|
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.9.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.8.0",
|
|
30
30
|
"zod": "3.24.4"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
package/src/main.ts
CHANGED
|
@@ -16,6 +16,8 @@ import {
|
|
|
16
16
|
getUserSchema,
|
|
17
17
|
updateTaskBacklogSchema,
|
|
18
18
|
updateTaskCompleteSchema,
|
|
19
|
+
updateTaskNotesSchema,
|
|
20
|
+
updateTaskPlannedTimeSchema,
|
|
19
21
|
updateTaskSnoozeDateSchema
|
|
20
22
|
} from "./schemas.js";
|
|
21
23
|
import { getSunsamaClient } from "./utils/client-resolver.js";
|
|
@@ -33,7 +35,7 @@ if (transportConfig.transportType === "stdio") {
|
|
|
33
35
|
|
|
34
36
|
const server = new FastMCP({
|
|
35
37
|
name: "Sunsama API Server",
|
|
36
|
-
version: "0.
|
|
38
|
+
version: "0.9.0",
|
|
37
39
|
instructions: `
|
|
38
40
|
This MCP server provides access to the Sunsama API for task and project management.
|
|
39
41
|
|
|
@@ -41,6 +43,7 @@ Available tools:
|
|
|
41
43
|
- Authentication: login, logout, check authentication status
|
|
42
44
|
- User operations: get current user information
|
|
43
45
|
- Task operations: get tasks by day, get backlog tasks, get archived tasks, get task by ID
|
|
46
|
+
- Task mutations: create tasks, mark complete, delete tasks, reschedule tasks, update planned time, update task notes
|
|
44
47
|
- Stream operations: get streams/channels for the user's group
|
|
45
48
|
|
|
46
49
|
Authentication is required for all operations. You can either:
|
|
@@ -630,6 +633,127 @@ server.addTool({
|
|
|
630
633
|
}
|
|
631
634
|
});
|
|
632
635
|
|
|
636
|
+
server.addTool({
|
|
637
|
+
name: "update-task-planned-time",
|
|
638
|
+
description: "Update the planned time (time estimate) for a task",
|
|
639
|
+
parameters: updateTaskPlannedTimeSchema,
|
|
640
|
+
execute: async (args, {session, log}) => {
|
|
641
|
+
try {
|
|
642
|
+
// Extract parameters
|
|
643
|
+
const {taskId, timeEstimateMinutes, limitResponsePayload} = args;
|
|
644
|
+
|
|
645
|
+
log.info("Updating task planned time", {
|
|
646
|
+
taskId: taskId,
|
|
647
|
+
timeEstimateMinutes: timeEstimateMinutes,
|
|
648
|
+
limitResponsePayload: limitResponsePayload
|
|
649
|
+
});
|
|
650
|
+
|
|
651
|
+
// Get the appropriate client based on transport type
|
|
652
|
+
const sunsamaClient = getSunsamaClient(session as SessionData | null);
|
|
653
|
+
|
|
654
|
+
// Call sunsamaClient.updateTaskPlannedTime(taskId, timeEstimateMinutes, limitResponsePayload)
|
|
655
|
+
const result = await sunsamaClient.updateTaskPlannedTime(
|
|
656
|
+
taskId,
|
|
657
|
+
timeEstimateMinutes,
|
|
658
|
+
limitResponsePayload
|
|
659
|
+
);
|
|
660
|
+
|
|
661
|
+
log.info("Successfully updated task planned time", {
|
|
662
|
+
taskId: taskId,
|
|
663
|
+
timeEstimateMinutes: timeEstimateMinutes,
|
|
664
|
+
success: result.success
|
|
665
|
+
});
|
|
666
|
+
|
|
667
|
+
return {
|
|
668
|
+
content: [
|
|
669
|
+
{
|
|
670
|
+
type: "text",
|
|
671
|
+
text: JSON.stringify({
|
|
672
|
+
success: result.success,
|
|
673
|
+
taskId: taskId,
|
|
674
|
+
timeEstimateMinutes: timeEstimateMinutes,
|
|
675
|
+
updatedFields: result.updatedFields
|
|
676
|
+
})
|
|
677
|
+
}
|
|
678
|
+
]
|
|
679
|
+
};
|
|
680
|
+
|
|
681
|
+
} catch (error) {
|
|
682
|
+
log.error("Failed to update task planned time", {
|
|
683
|
+
taskId: args.taskId,
|
|
684
|
+
timeEstimateMinutes: args.timeEstimateMinutes,
|
|
685
|
+
error: error instanceof Error ? error.message : 'Unknown error'
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
throw new Error(`Failed to update task planned time: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
});
|
|
692
|
+
|
|
693
|
+
server.addTool({
|
|
694
|
+
name: "update-task-notes",
|
|
695
|
+
description: "Update the notes content for a task",
|
|
696
|
+
parameters: updateTaskNotesSchema,
|
|
697
|
+
execute: async (args, {session, log}) => {
|
|
698
|
+
try {
|
|
699
|
+
// Extract parameters
|
|
700
|
+
const {taskId, content, limitResponsePayload} = args;
|
|
701
|
+
|
|
702
|
+
log.info("Updating task notes", {
|
|
703
|
+
taskId: taskId,
|
|
704
|
+
contentType: 'html' in content ? 'html' : 'markdown',
|
|
705
|
+
contentLength: ('html' in content ? content.html : content.markdown).length,
|
|
706
|
+
limitResponsePayload: limitResponsePayload
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
// Get the appropriate client based on transport type
|
|
710
|
+
const sunsamaClient = getSunsamaClient(session as SessionData | null);
|
|
711
|
+
|
|
712
|
+
// Build options object
|
|
713
|
+
const options: {
|
|
714
|
+
limitResponsePayload?: boolean;
|
|
715
|
+
} = {};
|
|
716
|
+
if (limitResponsePayload !== undefined) options.limitResponsePayload = limitResponsePayload;
|
|
717
|
+
|
|
718
|
+
// Call sunsamaClient.updateTaskNotes(taskId, content, options)
|
|
719
|
+
const result = await sunsamaClient.updateTaskNotes(
|
|
720
|
+
taskId,
|
|
721
|
+
content,
|
|
722
|
+
options
|
|
723
|
+
);
|
|
724
|
+
|
|
725
|
+
log.info("Successfully updated task notes", {
|
|
726
|
+
taskId: taskId,
|
|
727
|
+
success: result.success,
|
|
728
|
+
contentType: 'html' in content ? 'html' : 'markdown'
|
|
729
|
+
});
|
|
730
|
+
|
|
731
|
+
return {
|
|
732
|
+
content: [
|
|
733
|
+
{
|
|
734
|
+
type: "text",
|
|
735
|
+
text: JSON.stringify({
|
|
736
|
+
success: result.success,
|
|
737
|
+
taskId: taskId,
|
|
738
|
+
notesUpdated: true,
|
|
739
|
+
updatedFields: result.updatedFields
|
|
740
|
+
})
|
|
741
|
+
}
|
|
742
|
+
]
|
|
743
|
+
};
|
|
744
|
+
|
|
745
|
+
} catch (error) {
|
|
746
|
+
log.error("Failed to update task notes", {
|
|
747
|
+
taskId: args.taskId,
|
|
748
|
+
contentType: 'html' in args.content ? 'html' : 'markdown',
|
|
749
|
+
error: error instanceof Error ? error.message : 'Unknown error'
|
|
750
|
+
});
|
|
751
|
+
|
|
752
|
+
throw new Error(`Failed to update task notes: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
});
|
|
756
|
+
|
|
633
757
|
// Stream Operations
|
|
634
758
|
server.addTool({
|
|
635
759
|
name: "get-streams",
|
|
@@ -766,6 +890,15 @@ Uses HTTP Basic Auth headers (per-request authentication):
|
|
|
766
890
|
- \`limitResponsePayload\` (optional): Whether to limit response size
|
|
767
891
|
- Returns: JSON with update result
|
|
768
892
|
|
|
893
|
+
- **update-task-notes**: Update task notes content
|
|
894
|
+
- Parameters:
|
|
895
|
+
- \`taskId\` (required): The ID of the task to update notes for
|
|
896
|
+
- \`content\` (required): Task notes content in either HTML or Markdown format
|
|
897
|
+
- \`{html: string}\` OR \`{markdown: string}\` (mutually exclusive)
|
|
898
|
+
- \`limitResponsePayload\` (optional): Whether to limit response size (defaults to true)
|
|
899
|
+
- Returns: JSON with update result
|
|
900
|
+
- Note: Supports both HTML and Markdown content with automatic format conversion
|
|
901
|
+
|
|
769
902
|
### Stream Operations
|
|
770
903
|
- **get-streams**: Get streams for the user's group
|
|
771
904
|
- Parameters: none
|
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
|
@@ -87,6 +87,27 @@ export const updateTaskBacklogSchema = z.object({
|
|
|
87
87
|
limitResponsePayload: z.boolean().optional().describe("Whether to limit the response payload size"),
|
|
88
88
|
});
|
|
89
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
|
+
|
|
97
|
+
// Update task notes parameters
|
|
98
|
+
export const updateTaskNotesSchema = z.object({
|
|
99
|
+
taskId: z.string().min(1, "Task ID is required").describe("The ID of the task to update notes for"),
|
|
100
|
+
content: z.union([
|
|
101
|
+
z.object({
|
|
102
|
+
html: z.string().describe("HTML content for the task notes")
|
|
103
|
+
}),
|
|
104
|
+
z.object({
|
|
105
|
+
markdown: z.string().describe("Markdown content for the task notes")
|
|
106
|
+
})
|
|
107
|
+
]).describe("Task notes content in either HTML or Markdown format"),
|
|
108
|
+
limitResponsePayload: z.boolean().optional().describe("Whether to limit the response payload size (defaults to true)"),
|
|
109
|
+
});
|
|
110
|
+
|
|
90
111
|
/**
|
|
91
112
|
* Response Type Schemas (for validation and documentation)
|
|
92
113
|
*/
|
|
@@ -188,6 +209,8 @@ export type CreateTaskInput = z.infer<typeof createTaskSchema>;
|
|
|
188
209
|
export type UpdateTaskCompleteInput = z.infer<typeof updateTaskCompleteSchema>;
|
|
189
210
|
export type DeleteTaskInput = z.infer<typeof deleteTaskSchema>;
|
|
190
211
|
export type UpdateTaskSnoozeDateInput = z.infer<typeof updateTaskSnoozeDateSchema>;
|
|
212
|
+
export type UpdateTaskPlannedTimeInput = z.infer<typeof updateTaskPlannedTimeSchema>;
|
|
213
|
+
export type UpdateTaskNotesInput = z.infer<typeof updateTaskNotesSchema>;
|
|
191
214
|
|
|
192
215
|
export type User = z.infer<typeof userSchema>;
|
|
193
216
|
export type Task = z.infer<typeof taskSchema>;
|