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 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.6.1",
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.6.1", "", { "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-4GhOdrAMo99JIXv9GZg7+NQJu1uPRgGbmnKFWTCNzXYiSVicNPNv27vgqHa3kJtPo19x8sBf4gOYZYlw6b1Wgw=="],
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.7.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>;
@@ -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
  */
@@ -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.7.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.6.1",
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.7.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
@@ -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>;