mcp-sunsama 0.16.1 → 0.17.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 +17 -0
- package/CLAUDE.md +3 -2
- package/README.md +25 -0
- package/bun.lock +2 -2
- package/dist/auth/http.d.ts +1 -0
- package/dist/auth/http.d.ts.map +1 -1
- package/dist/auth/http.js +79 -9
- package/dist/auth/stdio.d.ts +1 -0
- package/dist/auth/stdio.d.ts.map +1 -1
- package/dist/auth/stdio.js +10 -1
- package/dist/auth/types.d.ts +1 -1
- package/dist/auth/types.d.ts.map +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/schemas.d.ts +79 -6
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +33 -0
- package/dist/schemas.test.js +8 -6
- package/dist/tools/task-tools.d.ts +30 -0
- package/dist/tools/task-tools.d.ts.map +1 -1
- package/dist/tools/task-tools.js +87 -1
- package/package.json +3 -3
- package/src/auth/http.ts +88 -9
- package/src/auth/stdio.ts +11 -1
- package/src/auth/types.ts +1 -1
- package/src/constants.ts +1 -1
- package/src/schemas.test.ts +8 -6
- package/src/schemas.ts +75 -0
- package/src/tools/task-tools.ts +139 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# mcp-sunsama
|
|
2
2
|
|
|
3
|
+
## 0.17.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 26000bd: Add subtask management support
|
|
8
|
+
|
|
9
|
+
- Update sunsama-api dependency to 0.13.1
|
|
10
|
+
- Add 5 new subtask management tools:
|
|
11
|
+
- `add-subtask` - Create a subtask with a title in one call (recommended)
|
|
12
|
+
- `create-subtasks` - Create multiple subtasks for a task (bulk operations)
|
|
13
|
+
- `update-subtask-title` - Update the title of a subtask
|
|
14
|
+
- `complete-subtask` - Mark a subtask as complete
|
|
15
|
+
- `uncomplete-subtask` - Mark a subtask as incomplete
|
|
16
|
+
- Update README and CLAUDE.md documentation
|
|
17
|
+
|
|
18
|
+
- e3336cf: Add session token authentication support for users with SSO/OAuth login. Users can now authenticate using `SUNSAMA_SESSION_TOKEN` environment variable (stdio) or Bearer token authentication (HTTP) as an alternative to email/password credentials.
|
|
19
|
+
|
|
3
20
|
## 0.16.1
|
|
4
21
|
|
|
5
22
|
### Patch Changes
|
package/CLAUDE.md
CHANGED
|
@@ -182,7 +182,7 @@ src/
|
|
|
182
182
|
├── tools/
|
|
183
183
|
│ ├── shared.ts # Common utilities and tool wrapper patterns
|
|
184
184
|
│ ├── user-tools.ts # User operations (get-user)
|
|
185
|
-
│ ├── task-tools.ts # Task operations (
|
|
185
|
+
│ ├── task-tools.ts # Task operations (20 tools)
|
|
186
186
|
│ ├── stream-tools.ts # Stream operations (get-streams)
|
|
187
187
|
│ └── index.ts # Export all tools
|
|
188
188
|
├── resources/
|
|
@@ -226,7 +226,7 @@ __tests__/
|
|
|
226
226
|
|
|
227
227
|
**Resource-Based Organization**:
|
|
228
228
|
- **User Tools**: Single tool for user operations
|
|
229
|
-
- **Task Tools**:
|
|
229
|
+
- **Task Tools**: 20 tools organized by function (query, lifecycle, update, subtasks)
|
|
230
230
|
- **Stream Tools**: Single tool for stream operations
|
|
231
231
|
|
|
232
232
|
**Type Safety Improvements**:
|
|
@@ -278,6 +278,7 @@ Optional:
|
|
|
278
278
|
Full CRUD support:
|
|
279
279
|
- **Read**: `get-tasks-by-day`, `get-tasks-backlog`, `get-archived-tasks`, `get-task-by-id`, `get-streams`
|
|
280
280
|
- **Write**: `create-task` (with GitHub/Gmail integration support), `update-task-complete`, `update-task-planned-time`, `update-task-notes`, `update-task-snooze-date`, `update-task-backlog`, `update-task-stream`, `update-task-text`, `update-task-due-date`, `delete-task`
|
|
281
|
+
- **Subtasks**: `add-subtask` (recommended), `create-subtasks` (bulk), `update-subtask-title`, `complete-subtask`, `uncomplete-subtask`
|
|
281
282
|
|
|
282
283
|
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.
|
|
283
284
|
|
package/README.md
CHANGED
|
@@ -12,6 +12,7 @@ A Model Context Protocol (MCP) server that provides comprehensive task managemen
|
|
|
12
12
|
- **Create Tasks** - Create new tasks with notes, time estimates, due dates, stream assignments, and GitHub/Gmail integrations
|
|
13
13
|
- **Read Tasks** - Get tasks by day with completion filtering, access backlog tasks, retrieve archived task history
|
|
14
14
|
- **Update Tasks** - Mark tasks as complete with custom timestamps, reschedule tasks or move to backlog
|
|
15
|
+
- **Subtasks** - Add, update, complete, and manage subtasks within tasks
|
|
15
16
|
- **Delete Tasks** - Permanently remove tasks from your workspace
|
|
16
17
|
|
|
17
18
|
### User & Stream Operations
|
|
@@ -111,6 +112,23 @@ Add this configuration to your Claude Desktop MCP settings:
|
|
|
111
112
|
}
|
|
112
113
|
```
|
|
113
114
|
|
|
115
|
+
### Claude Code Configuration
|
|
116
|
+
|
|
117
|
+
Add the Sunsama MCP server using the Claude Code CLI:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
claude mcp add sunsama --scope user \
|
|
121
|
+
-e SUNSAMA_EMAIL=your-email@example.com \
|
|
122
|
+
-e SUNSAMA_PASSWORD=your-password \
|
|
123
|
+
-- npx mcp-sunsama
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**Scope Options:**
|
|
127
|
+
- `--scope user` - Available across all projects (recommended)
|
|
128
|
+
- `--scope project` - Only available in the current project
|
|
129
|
+
|
|
130
|
+
After adding the server, restart Claude Code to connect to the Sunsama MCP server.
|
|
131
|
+
|
|
114
132
|
## API Tools
|
|
115
133
|
|
|
116
134
|
### Task Management
|
|
@@ -129,6 +147,13 @@ Add this configuration to your Claude Desktop MCP settings:
|
|
|
129
147
|
- `update-task-backlog` - Move tasks to the backlog
|
|
130
148
|
- `delete-task` - Delete tasks permanently
|
|
131
149
|
|
|
150
|
+
#### Subtask Management
|
|
151
|
+
- `add-subtask` - Create a subtask with a title in one call (recommended for single subtask creation)
|
|
152
|
+
- `create-subtasks` - Create multiple subtasks for a task (low-level API for bulk operations)
|
|
153
|
+
- `update-subtask-title` - Update the title of a subtask
|
|
154
|
+
- `complete-subtask` - Mark a subtask as complete with optional completion timestamp
|
|
155
|
+
- `uncomplete-subtask` - Mark a subtask as incomplete
|
|
156
|
+
|
|
132
157
|
### User & Stream Operations
|
|
133
158
|
- `get-user` - Get current user information
|
|
134
159
|
- `get-streams` - Get streams/channels for project organization
|
package/bun.lock
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"cors": "^2.8.5",
|
|
11
11
|
"express": "^5.1.0",
|
|
12
12
|
"papaparse": "^5.5.3",
|
|
13
|
-
"sunsama-api": "0.
|
|
13
|
+
"sunsama-api": "0.13.1",
|
|
14
14
|
"zod": "3.24.4",
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
@@ -397,7 +397,7 @@
|
|
|
397
397
|
|
|
398
398
|
"strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="],
|
|
399
399
|
|
|
400
|
-
"sunsama-api": ["sunsama-api@0.
|
|
400
|
+
"sunsama-api": ["sunsama-api@0.13.1", "", { "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-d60j9tLvH/HoF1K/CD8B5rphQ6UW+9XOkJHdABFVfmKCRvA+XNhN73ZzSm78dd+nRJXzaZqKG96+saN8jyBjCg=="],
|
|
401
401
|
|
|
402
402
|
"term-size": ["term-size@2.2.1", "", {}, "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg=="],
|
|
403
403
|
|
package/dist/auth/http.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export declare function stopClientCacheCleanup(): void;
|
|
|
17
17
|
export declare function cleanupAllClients(): void;
|
|
18
18
|
/**
|
|
19
19
|
* Authenticate HTTP request and get or create cached client
|
|
20
|
+
* Supports both Basic Auth (email/password) and Bearer token authentication
|
|
20
21
|
* Uses secure cache key (password hash) and race condition protection
|
|
21
22
|
*/
|
|
22
23
|
export declare function authenticateHttpRequest(authHeader?: string): Promise<SessionData>;
|
package/dist/auth/http.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/auth/http.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAe9C;;GAEG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAiBtF;
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/auth/http.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAe9C;;GAEG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAiBtF;AAwDD,wBAAgB,uBAAuB,IAAI,IAAI,CAQ9C;AAED;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,IAAI,CAM7C;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAYxC;AAED;;;;GAIG;AACH,wBAAsB,uBAAuB,CAC3C,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,WAAW,CAAC,CAsItB"}
|
package/dist/auth/http.js
CHANGED
|
@@ -36,6 +36,14 @@ function getCacheKey(email, password) {
|
|
|
36
36
|
.update(`${email}:${password}`)
|
|
37
37
|
.digest('hex');
|
|
38
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Generate secure cache key from session token
|
|
41
|
+
*/
|
|
42
|
+
function getTokenCacheKey(token) {
|
|
43
|
+
return createHash('sha256')
|
|
44
|
+
.update(`token:${token}`)
|
|
45
|
+
.digest('hex');
|
|
46
|
+
}
|
|
39
47
|
/**
|
|
40
48
|
* Check if a cached client is still valid based on TTL
|
|
41
49
|
*/
|
|
@@ -102,18 +110,80 @@ export function cleanupAllClients() {
|
|
|
102
110
|
}
|
|
103
111
|
/**
|
|
104
112
|
* Authenticate HTTP request and get or create cached client
|
|
113
|
+
* Supports both Basic Auth (email/password) and Bearer token authentication
|
|
105
114
|
* Uses secure cache key (password hash) and race condition protection
|
|
106
115
|
*/
|
|
107
116
|
export async function authenticateHttpRequest(authHeader) {
|
|
108
|
-
if (!authHeader
|
|
109
|
-
throw new Error("Basic
|
|
117
|
+
if (!authHeader) {
|
|
118
|
+
throw new Error("Authorization header required (Basic or Bearer)");
|
|
119
|
+
}
|
|
120
|
+
const isBearer = authHeader.startsWith('Bearer ');
|
|
121
|
+
const isBasic = authHeader.startsWith('Basic ');
|
|
122
|
+
if (!isBearer && !isBasic) {
|
|
123
|
+
throw new Error("Authorization header must be Basic or Bearer");
|
|
110
124
|
}
|
|
111
|
-
const { email, password } = parseBasicAuth(authHeader);
|
|
112
|
-
const cacheKey = getCacheKey(email, password);
|
|
113
125
|
const now = Date.now();
|
|
126
|
+
let cacheKey;
|
|
127
|
+
let identifier;
|
|
128
|
+
if (isBearer) {
|
|
129
|
+
const token = authHeader.replace('Bearer ', '').trim();
|
|
130
|
+
if (!token) {
|
|
131
|
+
throw new Error("Invalid Bearer token");
|
|
132
|
+
}
|
|
133
|
+
cacheKey = getTokenCacheKey(token);
|
|
134
|
+
identifier = 'token-user';
|
|
135
|
+
// Check for pending authentication (race condition protection)
|
|
136
|
+
if (authPromises.has(cacheKey)) {
|
|
137
|
+
console.error(`[Client Cache] Waiting for pending authentication for ${identifier}`);
|
|
138
|
+
return await authPromises.get(cacheKey);
|
|
139
|
+
}
|
|
140
|
+
// Check cache first
|
|
141
|
+
if (clientCache.has(cacheKey)) {
|
|
142
|
+
const cached = clientCache.get(cacheKey);
|
|
143
|
+
if (isClientValid(cached)) {
|
|
144
|
+
console.error(`[Client Cache] Reusing cached client for ${identifier}`);
|
|
145
|
+
cached.lastAccessedAt = now;
|
|
146
|
+
return cached;
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
console.error(`[Client Cache] Cached client expired for ${identifier}, re-authenticating`);
|
|
150
|
+
try {
|
|
151
|
+
cached.sunsamaClient.logout();
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
154
|
+
console.error(`[Client Cache] Error logging out expired client:`, err);
|
|
155
|
+
}
|
|
156
|
+
clientCache.delete(cacheKey);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// Create authentication promise for Bearer token
|
|
160
|
+
console.error(`[Client Cache] Creating new client for ${identifier}`);
|
|
161
|
+
const authPromise = (async () => {
|
|
162
|
+
try {
|
|
163
|
+
const sunsamaClient = new SunsamaClient({ sessionToken: token });
|
|
164
|
+
const sessionData = {
|
|
165
|
+
sunsamaClient,
|
|
166
|
+
createdAt: now,
|
|
167
|
+
lastAccessedAt: now
|
|
168
|
+
};
|
|
169
|
+
clientCache.set(cacheKey, sessionData);
|
|
170
|
+
console.error(`[Client Cache] Cached new client for ${identifier} (total: ${clientCache.size})`);
|
|
171
|
+
return sessionData;
|
|
172
|
+
}
|
|
173
|
+
finally {
|
|
174
|
+
authPromises.delete(cacheKey);
|
|
175
|
+
}
|
|
176
|
+
})();
|
|
177
|
+
authPromises.set(cacheKey, authPromise);
|
|
178
|
+
return authPromise;
|
|
179
|
+
}
|
|
180
|
+
// Basic Auth flow
|
|
181
|
+
const { email, password } = parseBasicAuth(authHeader);
|
|
182
|
+
cacheKey = getCacheKey(email, password);
|
|
183
|
+
identifier = email;
|
|
114
184
|
// Check for pending authentication (race condition protection)
|
|
115
185
|
if (authPromises.has(cacheKey)) {
|
|
116
|
-
console.error(`[Client Cache] Waiting for pending authentication for ${
|
|
186
|
+
console.error(`[Client Cache] Waiting for pending authentication for ${identifier}`);
|
|
117
187
|
return await authPromises.get(cacheKey);
|
|
118
188
|
}
|
|
119
189
|
// Check cache first
|
|
@@ -121,13 +191,13 @@ export async function authenticateHttpRequest(authHeader) {
|
|
|
121
191
|
const cached = clientCache.get(cacheKey);
|
|
122
192
|
// Check if still valid (lazy expiration)
|
|
123
193
|
if (isClientValid(cached)) {
|
|
124
|
-
console.error(`[Client Cache] Reusing cached client for ${
|
|
194
|
+
console.error(`[Client Cache] Reusing cached client for ${identifier}`);
|
|
125
195
|
// Update last accessed time (sliding window)
|
|
126
196
|
cached.lastAccessedAt = now;
|
|
127
197
|
return cached;
|
|
128
198
|
}
|
|
129
199
|
else {
|
|
130
|
-
console.error(`[Client Cache] Cached client expired for ${
|
|
200
|
+
console.error(`[Client Cache] Cached client expired for ${identifier}, re-authenticating`);
|
|
131
201
|
// Cleanup expired client
|
|
132
202
|
try {
|
|
133
203
|
cached.sunsamaClient.logout();
|
|
@@ -139,7 +209,7 @@ export async function authenticateHttpRequest(authHeader) {
|
|
|
139
209
|
}
|
|
140
210
|
}
|
|
141
211
|
// Create authentication promise to prevent concurrent authentications
|
|
142
|
-
console.error(`[Client Cache] Creating new client for ${
|
|
212
|
+
console.error(`[Client Cache] Creating new client for ${identifier}`);
|
|
143
213
|
const authPromise = (async () => {
|
|
144
214
|
try {
|
|
145
215
|
const sunsamaClient = new SunsamaClient();
|
|
@@ -151,7 +221,7 @@ export async function authenticateHttpRequest(authHeader) {
|
|
|
151
221
|
lastAccessedAt: now
|
|
152
222
|
};
|
|
153
223
|
clientCache.set(cacheKey, sessionData);
|
|
154
|
-
console.error(`[Client Cache] Cached new client for ${
|
|
224
|
+
console.error(`[Client Cache] Cached new client for ${identifier} (total: ${clientCache.size})`);
|
|
155
225
|
return sessionData;
|
|
156
226
|
}
|
|
157
227
|
finally {
|
package/dist/auth/stdio.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { SunsamaClient } from "sunsama-api/client";
|
|
2
2
|
/**
|
|
3
3
|
* Initialize stdio authentication using environment variables
|
|
4
|
+
* Supports session token (SUNSAMA_SESSION_TOKEN) or email/password (SUNSAMA_EMAIL, SUNSAMA_PASSWORD)
|
|
4
5
|
* @throws {Error} If credentials are missing or authentication fails
|
|
5
6
|
*/
|
|
6
7
|
export declare function initializeStdioAuth(): Promise<SunsamaClient>;
|
package/dist/auth/stdio.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stdio.d.ts","sourceRoot":"","sources":["../../src/auth/stdio.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAOnD
|
|
1
|
+
{"version":3,"file":"stdio.d.ts","sourceRoot":"","sources":["../../src/auth/stdio.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAOnD;;;;GAIG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,aAAa,CAAC,CAoBlE;AAED;;;;GAIG;AACH,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,aAAa,CAAC,CAMrE"}
|
package/dist/auth/stdio.js
CHANGED
|
@@ -5,11 +5,20 @@ import { SunsamaClient } from "sunsama-api/client";
|
|
|
5
5
|
let authenticationPromise = null;
|
|
6
6
|
/**
|
|
7
7
|
* Initialize stdio authentication using environment variables
|
|
8
|
+
* Supports session token (SUNSAMA_SESSION_TOKEN) or email/password (SUNSAMA_EMAIL, SUNSAMA_PASSWORD)
|
|
8
9
|
* @throws {Error} If credentials are missing or authentication fails
|
|
9
10
|
*/
|
|
10
11
|
export async function initializeStdioAuth() {
|
|
12
|
+
// Prefer session token if available (useful for Google SSO users)
|
|
13
|
+
if (process.env.SUNSAMA_SESSION_TOKEN) {
|
|
14
|
+
const sunsamaClient = new SunsamaClient({
|
|
15
|
+
sessionToken: process.env.SUNSAMA_SESSION_TOKEN
|
|
16
|
+
});
|
|
17
|
+
return sunsamaClient;
|
|
18
|
+
}
|
|
19
|
+
// Fall back to email/password authentication
|
|
11
20
|
if (!process.env.SUNSAMA_EMAIL || !process.env.SUNSAMA_PASSWORD) {
|
|
12
|
-
throw new Error("Sunsama credentials not configured. Please set SUNSAMA_EMAIL and SUNSAMA_PASSWORD environment variables.");
|
|
21
|
+
throw new Error("Sunsama credentials not configured. Please set SUNSAMA_SESSION_TOKEN or both SUNSAMA_EMAIL and SUNSAMA_PASSWORD environment variables.");
|
|
13
22
|
}
|
|
14
23
|
const sunsamaClient = new SunsamaClient();
|
|
15
24
|
await sunsamaClient.login(process.env.SUNSAMA_EMAIL, process.env.SUNSAMA_PASSWORD);
|
package/dist/auth/types.d.ts
CHANGED
package/dist/auth/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/auth/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD;;GAEG;AACH,MAAM,WAAW,WAAY,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC1D,aAAa,EAAE,aAAa,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/auth/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD;;GAEG;AACH,MAAM,WAAW,WAAY,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC1D,aAAa,EAAE,aAAa,CAAC;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;CACxB"}
|
package/dist/constants.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* VERSION is automatically synced from package.json by scripts/sync-version.ts
|
|
5
5
|
* when running `bun run version`
|
|
6
6
|
*/
|
|
7
|
-
export declare const VERSION = "0.
|
|
7
|
+
export declare const VERSION = "0.17.0";
|
|
8
8
|
export declare const SERVER_NAME = "Sunsama API Server";
|
|
9
9
|
export declare const PACKAGE_NAME = "mcp-sunsama";
|
|
10
10
|
//# sourceMappingURL=constants.d.ts.map
|
package/dist/constants.js
CHANGED
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
* VERSION is automatically synced from package.json by scripts/sync-version.ts
|
|
5
5
|
* when running `bun run version`
|
|
6
6
|
*/
|
|
7
|
-
export const VERSION = "0.
|
|
7
|
+
export const VERSION = "0.17.0";
|
|
8
8
|
export const SERVER_NAME = "Sunsama API Server";
|
|
9
9
|
export const PACKAGE_NAME = "mcp-sunsama";
|
package/dist/schemas.d.ts
CHANGED
|
@@ -340,6 +340,74 @@ export declare const updateTaskStreamSchema: z.ZodObject<{
|
|
|
340
340
|
streamId: string;
|
|
341
341
|
limitResponsePayload?: boolean | undefined;
|
|
342
342
|
}>;
|
|
343
|
+
/**
|
|
344
|
+
* Subtask Operation Schemas
|
|
345
|
+
*/
|
|
346
|
+
export declare const createSubtasksSchema: z.ZodObject<{
|
|
347
|
+
taskId: z.ZodString;
|
|
348
|
+
subtaskIds: z.ZodArray<z.ZodString, "many">;
|
|
349
|
+
limitResponsePayload: z.ZodOptional<z.ZodBoolean>;
|
|
350
|
+
}, "strip", z.ZodTypeAny, {
|
|
351
|
+
taskId: string;
|
|
352
|
+
subtaskIds: string[];
|
|
353
|
+
limitResponsePayload?: boolean | undefined;
|
|
354
|
+
}, {
|
|
355
|
+
taskId: string;
|
|
356
|
+
subtaskIds: string[];
|
|
357
|
+
limitResponsePayload?: boolean | undefined;
|
|
358
|
+
}>;
|
|
359
|
+
export declare const updateSubtaskTitleSchema: z.ZodObject<{
|
|
360
|
+
taskId: z.ZodString;
|
|
361
|
+
subtaskId: z.ZodString;
|
|
362
|
+
title: z.ZodString;
|
|
363
|
+
}, "strip", z.ZodTypeAny, {
|
|
364
|
+
taskId: string;
|
|
365
|
+
subtaskId: string;
|
|
366
|
+
title: string;
|
|
367
|
+
}, {
|
|
368
|
+
taskId: string;
|
|
369
|
+
subtaskId: string;
|
|
370
|
+
title: string;
|
|
371
|
+
}>;
|
|
372
|
+
export declare const completeSubtaskSchema: z.ZodObject<{
|
|
373
|
+
taskId: z.ZodString;
|
|
374
|
+
subtaskId: z.ZodString;
|
|
375
|
+
completedDate: z.ZodOptional<z.ZodString>;
|
|
376
|
+
limitResponsePayload: z.ZodOptional<z.ZodBoolean>;
|
|
377
|
+
}, "strip", z.ZodTypeAny, {
|
|
378
|
+
taskId: string;
|
|
379
|
+
subtaskId: string;
|
|
380
|
+
limitResponsePayload?: boolean | undefined;
|
|
381
|
+
completedDate?: string | undefined;
|
|
382
|
+
}, {
|
|
383
|
+
taskId: string;
|
|
384
|
+
subtaskId: string;
|
|
385
|
+
limitResponsePayload?: boolean | undefined;
|
|
386
|
+
completedDate?: string | undefined;
|
|
387
|
+
}>;
|
|
388
|
+
export declare const uncompleteSubtaskSchema: z.ZodObject<{
|
|
389
|
+
taskId: z.ZodString;
|
|
390
|
+
subtaskId: z.ZodString;
|
|
391
|
+
limitResponsePayload: z.ZodOptional<z.ZodBoolean>;
|
|
392
|
+
}, "strip", z.ZodTypeAny, {
|
|
393
|
+
taskId: string;
|
|
394
|
+
subtaskId: string;
|
|
395
|
+
limitResponsePayload?: boolean | undefined;
|
|
396
|
+
}, {
|
|
397
|
+
taskId: string;
|
|
398
|
+
subtaskId: string;
|
|
399
|
+
limitResponsePayload?: boolean | undefined;
|
|
400
|
+
}>;
|
|
401
|
+
export declare const addSubtaskSchema: z.ZodObject<{
|
|
402
|
+
taskId: z.ZodString;
|
|
403
|
+
title: z.ZodString;
|
|
404
|
+
}, "strip", z.ZodTypeAny, {
|
|
405
|
+
taskId: string;
|
|
406
|
+
title: string;
|
|
407
|
+
}, {
|
|
408
|
+
taskId: string;
|
|
409
|
+
title: string;
|
|
410
|
+
}>;
|
|
343
411
|
/**
|
|
344
412
|
* Response Type Schemas (for validation and documentation)
|
|
345
413
|
*/
|
|
@@ -464,9 +532,9 @@ export declare const taskSchema: z.ZodObject<{
|
|
|
464
532
|
}, "strip", z.ZodTypeAny, {
|
|
465
533
|
status: string;
|
|
466
534
|
createdAt: string;
|
|
535
|
+
title: string;
|
|
467
536
|
_id: string;
|
|
468
537
|
groupId: string;
|
|
469
|
-
title: string;
|
|
470
538
|
updatedAt: string;
|
|
471
539
|
userId: string;
|
|
472
540
|
streamId?: string | undefined;
|
|
@@ -476,9 +544,9 @@ export declare const taskSchema: z.ZodObject<{
|
|
|
476
544
|
}, {
|
|
477
545
|
status: string;
|
|
478
546
|
createdAt: string;
|
|
547
|
+
title: string;
|
|
479
548
|
_id: string;
|
|
480
549
|
groupId: string;
|
|
481
|
-
title: string;
|
|
482
550
|
updatedAt: string;
|
|
483
551
|
userId: string;
|
|
484
552
|
streamId?: string | undefined;
|
|
@@ -639,9 +707,9 @@ export declare const tasksResponseSchema: z.ZodObject<{
|
|
|
639
707
|
}, "strip", z.ZodTypeAny, {
|
|
640
708
|
status: string;
|
|
641
709
|
createdAt: string;
|
|
710
|
+
title: string;
|
|
642
711
|
_id: string;
|
|
643
712
|
groupId: string;
|
|
644
|
-
title: string;
|
|
645
713
|
updatedAt: string;
|
|
646
714
|
userId: string;
|
|
647
715
|
streamId?: string | undefined;
|
|
@@ -651,9 +719,9 @@ export declare const tasksResponseSchema: z.ZodObject<{
|
|
|
651
719
|
}, {
|
|
652
720
|
status: string;
|
|
653
721
|
createdAt: string;
|
|
722
|
+
title: string;
|
|
654
723
|
_id: string;
|
|
655
724
|
groupId: string;
|
|
656
|
-
title: string;
|
|
657
725
|
updatedAt: string;
|
|
658
726
|
userId: string;
|
|
659
727
|
streamId?: string | undefined;
|
|
@@ -666,9 +734,9 @@ export declare const tasksResponseSchema: z.ZodObject<{
|
|
|
666
734
|
tasks: {
|
|
667
735
|
status: string;
|
|
668
736
|
createdAt: string;
|
|
737
|
+
title: string;
|
|
669
738
|
_id: string;
|
|
670
739
|
groupId: string;
|
|
671
|
-
title: string;
|
|
672
740
|
updatedAt: string;
|
|
673
741
|
userId: string;
|
|
674
742
|
streamId?: string | undefined;
|
|
@@ -681,9 +749,9 @@ export declare const tasksResponseSchema: z.ZodObject<{
|
|
|
681
749
|
tasks: {
|
|
682
750
|
status: string;
|
|
683
751
|
createdAt: string;
|
|
752
|
+
title: string;
|
|
684
753
|
_id: string;
|
|
685
754
|
groupId: string;
|
|
686
|
-
title: string;
|
|
687
755
|
updatedAt: string;
|
|
688
756
|
userId: string;
|
|
689
757
|
streamId?: string | undefined;
|
|
@@ -779,6 +847,11 @@ export type UpdateTaskNotesInput = z.infer<typeof updateTaskNotesSchema>;
|
|
|
779
847
|
export type UpdateTaskDueDateInput = z.infer<typeof updateTaskDueDateSchema>;
|
|
780
848
|
export type UpdateTaskTextInput = z.infer<typeof updateTaskTextSchema>;
|
|
781
849
|
export type UpdateTaskStreamInput = z.infer<typeof updateTaskStreamSchema>;
|
|
850
|
+
export type CreateSubtasksInput = z.infer<typeof createSubtasksSchema>;
|
|
851
|
+
export type UpdateSubtaskTitleInput = z.infer<typeof updateSubtaskTitleSchema>;
|
|
852
|
+
export type CompleteSubtaskInput = z.infer<typeof completeSubtaskSchema>;
|
|
853
|
+
export type UncompleteSubtaskInput = z.infer<typeof uncompleteSubtaskSchema>;
|
|
854
|
+
export type AddSubtaskInput = z.infer<typeof addSubtaskSchema>;
|
|
782
855
|
export type User = z.infer<typeof userSchema>;
|
|
783
856
|
export type Task = z.infer<typeof taskSchema>;
|
|
784
857
|
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,+CAIjC,CAAC;AAGH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;EAW9B,CAAC;AAGH,eAAO,MAAM,qBAAqB,gDAAe,CAAC;AAGlD,eAAO,MAAM,sBAAsB;;;;;;;;;EAOjC,CAAC;AAGH,eAAO,MAAM,iBAAiB;;;;;;EAI5B,CAAC;AAEH;;GAEG;AAGH,eAAO,MAAM,aAAa,gDAAe,CAAC;AAE1C;;GAEG;AAGH,eAAO,MAAM,gBAAgB,gDAAe,CAAC;AA8C7C;;GAEG;AAGH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsB3B,CAAC;AAGH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;EAUnC,CAAC;AAGH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;EAU3B,CAAC;AAGH,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;EAarC,CAAC;AAGH,eAAO,MAAM,uBAAuB;;;;;;;;;;;;EAUlC,CAAC;AAGH,eAAO,MAAM,2BAA2B;;;;;;;;;;;;EAUtC,CAAC;AAKH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;EAahC,CAAC;AAGH,eAAO,MAAM,uBAAuB;;;;;;;;;;;;EAalC,CAAC;AAGH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;EAa/B,CAAC;AAGH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;EAUjC,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,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AACjE,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,CAC7C,OAAO,0BAA0B,CAClC,CAAC;AACF,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAC7E,MAAM,MAAM,0BAA0B,GAAG,CAAC,CAAC,KAAK,CAC9C,OAAO,2BAA2B,CACnC,CAAC;AACF,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AACzE,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAC7E,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AACvE,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAE3E,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAC9C,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAC9C,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAClD,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAC9D,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAChE,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AACpE,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,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
|
@@ -150,6 +150,39 @@ export const updateTaskStreamSchema = z.object({
|
|
|
150
150
|
streamId: z.string().min(1, "Stream ID is required").describe("Stream ID to assign to the task"),
|
|
151
151
|
limitResponsePayload: z.boolean().optional().describe("Whether to limit the response payload size"),
|
|
152
152
|
});
|
|
153
|
+
/**
|
|
154
|
+
* Subtask Operation Schemas
|
|
155
|
+
*/
|
|
156
|
+
// Create subtasks parameters
|
|
157
|
+
export const createSubtasksSchema = z.object({
|
|
158
|
+
taskId: z.string().min(1, "Task ID is required").describe("The ID of the parent task"),
|
|
159
|
+
subtaskIds: z.array(z.string().min(1)).min(1, "At least one subtask ID is required").describe("Array of subtask IDs to create (use SunsamaClient.generateTaskId() to generate IDs)"),
|
|
160
|
+
limitResponsePayload: z.boolean().optional().describe("Whether to limit the response payload size"),
|
|
161
|
+
});
|
|
162
|
+
// Update subtask title parameters
|
|
163
|
+
export const updateSubtaskTitleSchema = z.object({
|
|
164
|
+
taskId: z.string().min(1, "Task ID is required").describe("The ID of the parent task"),
|
|
165
|
+
subtaskId: z.string().min(1, "Subtask ID is required").describe("The ID of the subtask to update"),
|
|
166
|
+
title: z.string().min(1, "Subtask title is required").describe("The new title for the subtask"),
|
|
167
|
+
});
|
|
168
|
+
// Complete subtask parameters
|
|
169
|
+
export const completeSubtaskSchema = z.object({
|
|
170
|
+
taskId: z.string().min(1, "Task ID is required").describe("The ID of the parent task"),
|
|
171
|
+
subtaskId: z.string().min(1, "Subtask ID is required").describe("The ID of the subtask to mark as complete"),
|
|
172
|
+
completedDate: z.string().optional().describe("Completion timestamp (ISO format). Defaults to current time"),
|
|
173
|
+
limitResponsePayload: z.boolean().optional().describe("Whether to limit the response payload size"),
|
|
174
|
+
});
|
|
175
|
+
// Uncomplete subtask parameters
|
|
176
|
+
export const uncompleteSubtaskSchema = z.object({
|
|
177
|
+
taskId: z.string().min(1, "Task ID is required").describe("The ID of the parent task"),
|
|
178
|
+
subtaskId: z.string().min(1, "Subtask ID is required").describe("The ID of the subtask to mark as incomplete"),
|
|
179
|
+
limitResponsePayload: z.boolean().optional().describe("Whether to limit the response payload size"),
|
|
180
|
+
});
|
|
181
|
+
// Add subtask parameters (convenience method)
|
|
182
|
+
export const addSubtaskSchema = z.object({
|
|
183
|
+
taskId: z.string().min(1, "Task ID is required").describe("The ID of the parent task"),
|
|
184
|
+
title: z.string().min(1, "Subtask title is required").describe("The title for the new subtask"),
|
|
185
|
+
});
|
|
153
186
|
/**
|
|
154
187
|
* Response Type Schemas (for validation and documentation)
|
|
155
188
|
*/
|
package/dist/schemas.test.js
CHANGED
|
@@ -283,20 +283,22 @@ describe("Tool Parameter Schemas", () => {
|
|
|
283
283
|
};
|
|
284
284
|
expect(() => updateTaskNotesSchema.parse(minimalMarkdownInput)).not.toThrow();
|
|
285
285
|
});
|
|
286
|
-
test("should
|
|
287
|
-
const
|
|
286
|
+
test("should accept both HTML and Markdown provided (XOR validation is in tool execute)", () => {
|
|
287
|
+
const inputWithBoth = {
|
|
288
288
|
taskId: "task-123",
|
|
289
289
|
html: "<p>HTML content</p>",
|
|
290
290
|
markdown: "Markdown content",
|
|
291
291
|
};
|
|
292
|
-
|
|
292
|
+
// Schema accepts both - XOR validation happens in the tool's execute function
|
|
293
|
+
expect(() => updateTaskNotesSchema.parse(inputWithBoth)).not.toThrow();
|
|
293
294
|
});
|
|
294
|
-
test("should
|
|
295
|
-
const
|
|
295
|
+
test("should accept neither HTML nor Markdown provided (XOR validation is in tool execute)", () => {
|
|
296
|
+
const inputWithNeither = {
|
|
296
297
|
taskId: "task-123",
|
|
297
298
|
limitResponsePayload: true,
|
|
298
299
|
};
|
|
299
|
-
|
|
300
|
+
// Schema accepts neither - XOR validation happens in the tool's execute function
|
|
301
|
+
expect(() => updateTaskNotesSchema.parse(inputWithNeither)).not.toThrow();
|
|
300
302
|
});
|
|
301
303
|
test("should reject empty task ID", () => {
|
|
302
304
|
expect(() => updateTaskNotesSchema.parse({
|
|
@@ -82,6 +82,36 @@ export declare const updateTaskStreamTool: {
|
|
|
82
82
|
parameters: any;
|
|
83
83
|
execute: (args: any, extra?: any) => Promise<any>;
|
|
84
84
|
};
|
|
85
|
+
export declare const createSubtasksTool: {
|
|
86
|
+
name: string;
|
|
87
|
+
description: string;
|
|
88
|
+
parameters: any;
|
|
89
|
+
execute: (args: any, extra?: any) => Promise<any>;
|
|
90
|
+
};
|
|
91
|
+
export declare const updateSubtaskTitleTool: {
|
|
92
|
+
name: string;
|
|
93
|
+
description: string;
|
|
94
|
+
parameters: any;
|
|
95
|
+
execute: (args: any, extra?: any) => Promise<any>;
|
|
96
|
+
};
|
|
97
|
+
export declare const completeSubtaskTool: {
|
|
98
|
+
name: string;
|
|
99
|
+
description: string;
|
|
100
|
+
parameters: any;
|
|
101
|
+
execute: (args: any, extra?: any) => Promise<any>;
|
|
102
|
+
};
|
|
103
|
+
export declare const uncompleteSubtaskTool: {
|
|
104
|
+
name: string;
|
|
105
|
+
description: string;
|
|
106
|
+
parameters: any;
|
|
107
|
+
execute: (args: any, extra?: any) => Promise<any>;
|
|
108
|
+
};
|
|
109
|
+
export declare const addSubtaskTool: {
|
|
110
|
+
name: string;
|
|
111
|
+
description: string;
|
|
112
|
+
parameters: any;
|
|
113
|
+
execute: (args: any, extra?: any) => Promise<any>;
|
|
114
|
+
};
|
|
85
115
|
export declare const taskTools: {
|
|
86
116
|
name: string;
|
|
87
117
|
description: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task-tools.d.ts","sourceRoot":"","sources":["../../src/tools/task-tools.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"task-tools.d.ts","sourceRoot":"","sources":["../../src/tools/task-tools.ts"],"names":[],"mappings":"AAoDA,eAAO,MAAM,mBAAmB;;;;;CAU9B,CAAC;AAEH,eAAO,MAAM,iBAAiB;;;;;CAqB5B,CAAC;AAEH,eAAO,MAAM,oBAAoB;;;;;CA2B/B,CAAC;AAEH,eAAO,MAAM,eAAe;;;;;CAS1B,CAAC;AAGH,eAAO,MAAM,cAAc;;;;;CAsCzB,CAAC;AAEH,eAAO,MAAM,cAAc;;;;;CAqBzB,CAAC;AAGH,eAAO,MAAM,sBAAsB;;;;;CAqBjC,CAAC;AAEH,eAAO,MAAM,wBAAwB;;;;;CA6BnC,CAAC;AAEH,eAAO,MAAM,qBAAqB;;;;;CA2BhC,CAAC;AAEH,eAAO,MAAM,yBAAyB;;;;;CAsBpC,CAAC;AAEH,eAAO,MAAM,mBAAmB;;;;;CAwC9B,CAAC;AAEH,eAAO,MAAM,qBAAqB;;;;;CAsBhC,CAAC;AAEH,eAAO,MAAM,kBAAkB;;;;;CA8B7B,CAAC;AAEH,eAAO,MAAM,oBAAoB;;;;;CAsB/B,CAAC;AAGH,eAAO,MAAM,kBAAkB;;;;;CAuB7B,CAAC;AAEH,eAAO,MAAM,sBAAsB;;;;;CAuBjC,CAAC;AAEH,eAAO,MAAM,mBAAmB;;;;;CAwB9B,CAAC;AAEH,eAAO,MAAM,qBAAqB;;;;;CAsBhC,CAAC;AAEH,eAAO,MAAM,cAAc;;;;;CAmBzB,CAAC;AAGH,eAAO,MAAM,SAAS;;;;;GA2BrB,CAAC"}
|
package/dist/tools/task-tools.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createTaskSchema, deleteTaskSchema, getArchivedTasksSchema, getTaskByIdSchema, getTasksBacklogSchema, getTasksByDaySchema, updateTaskBacklogSchema, updateTaskCompleteSchema, updateTaskDueDateSchema, updateTaskNotesSchema, updateTaskPlannedTimeSchema, updateTaskSnoozeDateSchema, updateTaskStreamSchema, updateTaskTextSchema, } from "../schemas.js";
|
|
1
|
+
import { addSubtaskSchema, completeSubtaskSchema, createSubtasksSchema, createTaskSchema, deleteTaskSchema, getArchivedTasksSchema, getTaskByIdSchema, getTasksBacklogSchema, getTasksByDaySchema, uncompleteSubtaskSchema, updateSubtaskTitleSchema, updateTaskBacklogSchema, updateTaskCompleteSchema, updateTaskDueDateSchema, updateTaskNotesSchema, updateTaskPlannedTimeSchema, updateTaskSnoozeDateSchema, updateTaskStreamSchema, updateTaskTextSchema, } from "../schemas.js";
|
|
2
2
|
import { filterTasksByCompletion } from "../utils/task-filters.js";
|
|
3
3
|
import { trimTasksForResponse } from "../utils/task-trimmer.js";
|
|
4
4
|
import { formatJsonResponse, formatPaginatedTsvResponse, formatTsvResponse, withTransportClient, } from "./shared.js";
|
|
@@ -257,6 +257,86 @@ export const updateTaskStreamTool = withTransportClient({
|
|
|
257
257
|
});
|
|
258
258
|
},
|
|
259
259
|
});
|
|
260
|
+
// Subtask Management Tools
|
|
261
|
+
export const createSubtasksTool = withTransportClient({
|
|
262
|
+
name: "create-subtasks",
|
|
263
|
+
description: "Create multiple subtasks for a task (low-level API for bulk operations)",
|
|
264
|
+
parameters: createSubtasksSchema,
|
|
265
|
+
execute: async ({ taskId, subtaskIds, limitResponsePayload }, context) => {
|
|
266
|
+
const result = await context.client.createSubtasks(taskId, subtaskIds, limitResponsePayload);
|
|
267
|
+
return formatJsonResponse({
|
|
268
|
+
success: result.success,
|
|
269
|
+
taskId,
|
|
270
|
+
subtaskIds,
|
|
271
|
+
subtasksCreated: true,
|
|
272
|
+
count: subtaskIds.length,
|
|
273
|
+
updatedFields: result.updatedFields,
|
|
274
|
+
});
|
|
275
|
+
},
|
|
276
|
+
});
|
|
277
|
+
export const updateSubtaskTitleTool = withTransportClient({
|
|
278
|
+
name: "update-subtask-title",
|
|
279
|
+
description: "Update the title of a subtask",
|
|
280
|
+
parameters: updateSubtaskTitleSchema,
|
|
281
|
+
execute: async ({ taskId, subtaskId, title }, context) => {
|
|
282
|
+
const result = await context.client.updateSubtaskTitle(taskId, subtaskId, title);
|
|
283
|
+
return formatJsonResponse({
|
|
284
|
+
success: result.success,
|
|
285
|
+
taskId,
|
|
286
|
+
subtaskId,
|
|
287
|
+
title,
|
|
288
|
+
subtaskTitleUpdated: true,
|
|
289
|
+
updatedFields: result.updatedFields,
|
|
290
|
+
});
|
|
291
|
+
},
|
|
292
|
+
});
|
|
293
|
+
export const completeSubtaskTool = withTransportClient({
|
|
294
|
+
name: "complete-subtask",
|
|
295
|
+
description: "Mark a subtask as complete with optional completion timestamp",
|
|
296
|
+
parameters: completeSubtaskSchema,
|
|
297
|
+
execute: async ({ taskId, subtaskId, completedDate, limitResponsePayload }, context) => {
|
|
298
|
+
const result = await context.client.completeSubtask(taskId, subtaskId, completedDate, limitResponsePayload);
|
|
299
|
+
return formatJsonResponse({
|
|
300
|
+
success: result.success,
|
|
301
|
+
taskId,
|
|
302
|
+
subtaskId,
|
|
303
|
+
subtaskCompleted: true,
|
|
304
|
+
completedDate: completedDate || new Date().toISOString(),
|
|
305
|
+
updatedFields: result.updatedFields,
|
|
306
|
+
});
|
|
307
|
+
},
|
|
308
|
+
});
|
|
309
|
+
export const uncompleteSubtaskTool = withTransportClient({
|
|
310
|
+
name: "uncomplete-subtask",
|
|
311
|
+
description: "Mark a subtask as incomplete (uncomplete it)",
|
|
312
|
+
parameters: uncompleteSubtaskSchema,
|
|
313
|
+
execute: async ({ taskId, subtaskId, limitResponsePayload }, context) => {
|
|
314
|
+
const result = await context.client.uncompleteSubtask(taskId, subtaskId, limitResponsePayload);
|
|
315
|
+
return formatJsonResponse({
|
|
316
|
+
success: result.success,
|
|
317
|
+
taskId,
|
|
318
|
+
subtaskId,
|
|
319
|
+
subtaskUncompleted: true,
|
|
320
|
+
updatedFields: result.updatedFields,
|
|
321
|
+
});
|
|
322
|
+
},
|
|
323
|
+
});
|
|
324
|
+
export const addSubtaskTool = withTransportClient({
|
|
325
|
+
name: "add-subtask",
|
|
326
|
+
description: "Convenience method to create a subtask with a title in one call (recommended for single subtask creation)",
|
|
327
|
+
parameters: addSubtaskSchema,
|
|
328
|
+
execute: async ({ taskId, title }, context) => {
|
|
329
|
+
const result = await context.client.addSubtask(taskId, title);
|
|
330
|
+
return formatJsonResponse({
|
|
331
|
+
success: result.result.success,
|
|
332
|
+
taskId,
|
|
333
|
+
subtaskId: result.subtaskId,
|
|
334
|
+
title,
|
|
335
|
+
subtaskAdded: true,
|
|
336
|
+
updatedFields: result.result.updatedFields,
|
|
337
|
+
});
|
|
338
|
+
},
|
|
339
|
+
});
|
|
260
340
|
// Export all task tools
|
|
261
341
|
export const taskTools = [
|
|
262
342
|
// Query tools
|
|
@@ -276,4 +356,10 @@ export const taskTools = [
|
|
|
276
356
|
updateTaskDueDateTool,
|
|
277
357
|
updateTaskTextTool,
|
|
278
358
|
updateTaskStreamTool,
|
|
359
|
+
// Subtask management tools
|
|
360
|
+
createSubtasksTool,
|
|
361
|
+
updateSubtaskTitleTool,
|
|
362
|
+
completeSubtaskTool,
|
|
363
|
+
uncompleteSubtaskTool,
|
|
364
|
+
addSubtaskTool,
|
|
279
365
|
];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-sunsama",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "MCP server for Sunsama API integration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"cors": "^2.8.5",
|
|
35
35
|
"express": "^5.1.0",
|
|
36
36
|
"papaparse": "^5.5.3",
|
|
37
|
-
"sunsama-api": "0.
|
|
37
|
+
"sunsama-api": "0.13.1",
|
|
38
38
|
"zod": "3.24.4"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
@@ -64,4 +64,4 @@
|
|
|
64
64
|
"publishConfig": {
|
|
65
65
|
"access": "public"
|
|
66
66
|
}
|
|
67
|
-
}
|
|
67
|
+
}
|
package/src/auth/http.ts
CHANGED
|
@@ -47,6 +47,15 @@ function getCacheKey(email: string, password: string): string {
|
|
|
47
47
|
.digest('hex');
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Generate secure cache key from session token
|
|
52
|
+
*/
|
|
53
|
+
function getTokenCacheKey(token: string): string {
|
|
54
|
+
return createHash('sha256')
|
|
55
|
+
.update(`token:${token}`)
|
|
56
|
+
.digest('hex');
|
|
57
|
+
}
|
|
58
|
+
|
|
50
59
|
/**
|
|
51
60
|
* Check if a cached client is still valid based on TTL
|
|
52
61
|
*/
|
|
@@ -122,22 +131,92 @@ export function cleanupAllClients(): void {
|
|
|
122
131
|
|
|
123
132
|
/**
|
|
124
133
|
* Authenticate HTTP request and get or create cached client
|
|
134
|
+
* Supports both Basic Auth (email/password) and Bearer token authentication
|
|
125
135
|
* Uses secure cache key (password hash) and race condition protection
|
|
126
136
|
*/
|
|
127
137
|
export async function authenticateHttpRequest(
|
|
128
138
|
authHeader?: string
|
|
129
139
|
): Promise<SessionData> {
|
|
130
|
-
if (!authHeader
|
|
131
|
-
throw new Error("Basic
|
|
140
|
+
if (!authHeader) {
|
|
141
|
+
throw new Error("Authorization header required (Basic or Bearer)");
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const isBearer = authHeader.startsWith('Bearer ');
|
|
145
|
+
const isBasic = authHeader.startsWith('Basic ');
|
|
146
|
+
|
|
147
|
+
if (!isBearer && !isBasic) {
|
|
148
|
+
throw new Error("Authorization header must be Basic or Bearer");
|
|
132
149
|
}
|
|
133
150
|
|
|
134
|
-
const { email, password } = parseBasicAuth(authHeader);
|
|
135
|
-
const cacheKey = getCacheKey(email, password);
|
|
136
151
|
const now = Date.now();
|
|
152
|
+
let cacheKey: string;
|
|
153
|
+
let identifier: string;
|
|
154
|
+
|
|
155
|
+
if (isBearer) {
|
|
156
|
+
const token = authHeader.replace('Bearer ', '').trim();
|
|
157
|
+
if (!token) {
|
|
158
|
+
throw new Error("Invalid Bearer token");
|
|
159
|
+
}
|
|
160
|
+
cacheKey = getTokenCacheKey(token);
|
|
161
|
+
identifier = 'token-user';
|
|
162
|
+
|
|
163
|
+
// Check for pending authentication (race condition protection)
|
|
164
|
+
if (authPromises.has(cacheKey)) {
|
|
165
|
+
console.error(`[Client Cache] Waiting for pending authentication for ${identifier}`);
|
|
166
|
+
return await authPromises.get(cacheKey)!;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Check cache first
|
|
170
|
+
if (clientCache.has(cacheKey)) {
|
|
171
|
+
const cached = clientCache.get(cacheKey)!;
|
|
172
|
+
if (isClientValid(cached)) {
|
|
173
|
+
console.error(`[Client Cache] Reusing cached client for ${identifier}`);
|
|
174
|
+
cached.lastAccessedAt = now;
|
|
175
|
+
return cached;
|
|
176
|
+
} else {
|
|
177
|
+
console.error(`[Client Cache] Cached client expired for ${identifier}, re-authenticating`);
|
|
178
|
+
try {
|
|
179
|
+
cached.sunsamaClient.logout();
|
|
180
|
+
} catch (err) {
|
|
181
|
+
console.error(`[Client Cache] Error logging out expired client:`, err);
|
|
182
|
+
}
|
|
183
|
+
clientCache.delete(cacheKey);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Create authentication promise for Bearer token
|
|
188
|
+
console.error(`[Client Cache] Creating new client for ${identifier}`);
|
|
189
|
+
const authPromise = (async () => {
|
|
190
|
+
try {
|
|
191
|
+
const sunsamaClient = new SunsamaClient({ sessionToken: token });
|
|
192
|
+
|
|
193
|
+
const sessionData: SessionData = {
|
|
194
|
+
sunsamaClient,
|
|
195
|
+
createdAt: now,
|
|
196
|
+
lastAccessedAt: now
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
clientCache.set(cacheKey, sessionData);
|
|
200
|
+
console.error(`[Client Cache] Cached new client for ${identifier} (total: ${clientCache.size})`);
|
|
201
|
+
|
|
202
|
+
return sessionData;
|
|
203
|
+
} finally {
|
|
204
|
+
authPromises.delete(cacheKey);
|
|
205
|
+
}
|
|
206
|
+
})();
|
|
207
|
+
|
|
208
|
+
authPromises.set(cacheKey, authPromise);
|
|
209
|
+
return authPromise;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Basic Auth flow
|
|
213
|
+
const { email, password } = parseBasicAuth(authHeader);
|
|
214
|
+
cacheKey = getCacheKey(email, password);
|
|
215
|
+
identifier = email;
|
|
137
216
|
|
|
138
217
|
// Check for pending authentication (race condition protection)
|
|
139
218
|
if (authPromises.has(cacheKey)) {
|
|
140
|
-
console.error(`[Client Cache] Waiting for pending authentication for ${
|
|
219
|
+
console.error(`[Client Cache] Waiting for pending authentication for ${identifier}`);
|
|
141
220
|
return await authPromises.get(cacheKey)!;
|
|
142
221
|
}
|
|
143
222
|
|
|
@@ -147,12 +226,12 @@ export async function authenticateHttpRequest(
|
|
|
147
226
|
|
|
148
227
|
// Check if still valid (lazy expiration)
|
|
149
228
|
if (isClientValid(cached)) {
|
|
150
|
-
console.error(`[Client Cache] Reusing cached client for ${
|
|
229
|
+
console.error(`[Client Cache] Reusing cached client for ${identifier}`);
|
|
151
230
|
// Update last accessed time (sliding window)
|
|
152
231
|
cached.lastAccessedAt = now;
|
|
153
232
|
return cached;
|
|
154
233
|
} else {
|
|
155
|
-
console.error(`[Client Cache] Cached client expired for ${
|
|
234
|
+
console.error(`[Client Cache] Cached client expired for ${identifier}, re-authenticating`);
|
|
156
235
|
// Cleanup expired client
|
|
157
236
|
try {
|
|
158
237
|
cached.sunsamaClient.logout();
|
|
@@ -164,7 +243,7 @@ export async function authenticateHttpRequest(
|
|
|
164
243
|
}
|
|
165
244
|
|
|
166
245
|
// Create authentication promise to prevent concurrent authentications
|
|
167
|
-
console.error(`[Client Cache] Creating new client for ${
|
|
246
|
+
console.error(`[Client Cache] Creating new client for ${identifier}`);
|
|
168
247
|
const authPromise = (async () => {
|
|
169
248
|
try {
|
|
170
249
|
const sunsamaClient = new SunsamaClient();
|
|
@@ -178,7 +257,7 @@ export async function authenticateHttpRequest(
|
|
|
178
257
|
};
|
|
179
258
|
|
|
180
259
|
clientCache.set(cacheKey, sessionData);
|
|
181
|
-
console.error(`[Client Cache] Cached new client for ${
|
|
260
|
+
console.error(`[Client Cache] Cached new client for ${identifier} (total: ${clientCache.size})`);
|
|
182
261
|
|
|
183
262
|
return sessionData;
|
|
184
263
|
} finally {
|
package/src/auth/stdio.ts
CHANGED
|
@@ -7,12 +7,22 @@ let authenticationPromise: Promise<SunsamaClient> | null = null;
|
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Initialize stdio authentication using environment variables
|
|
10
|
+
* Supports session token (SUNSAMA_SESSION_TOKEN) or email/password (SUNSAMA_EMAIL, SUNSAMA_PASSWORD)
|
|
10
11
|
* @throws {Error} If credentials are missing or authentication fails
|
|
11
12
|
*/
|
|
12
13
|
export async function initializeStdioAuth(): Promise<SunsamaClient> {
|
|
14
|
+
// Prefer session token if available (useful for Google SSO users)
|
|
15
|
+
if (process.env.SUNSAMA_SESSION_TOKEN) {
|
|
16
|
+
const sunsamaClient = new SunsamaClient({
|
|
17
|
+
sessionToken: process.env.SUNSAMA_SESSION_TOKEN
|
|
18
|
+
});
|
|
19
|
+
return sunsamaClient;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Fall back to email/password authentication
|
|
13
23
|
if (!process.env.SUNSAMA_EMAIL || !process.env.SUNSAMA_PASSWORD) {
|
|
14
24
|
throw new Error(
|
|
15
|
-
"Sunsama credentials not configured. Please set SUNSAMA_EMAIL and SUNSAMA_PASSWORD environment variables."
|
|
25
|
+
"Sunsama credentials not configured. Please set SUNSAMA_SESSION_TOKEN or both SUNSAMA_EMAIL and SUNSAMA_PASSWORD environment variables."
|
|
16
26
|
);
|
|
17
27
|
}
|
|
18
28
|
|
package/src/auth/types.ts
CHANGED
package/src/constants.ts
CHANGED
package/src/schemas.test.ts
CHANGED
|
@@ -391,21 +391,23 @@ describe("Tool Parameter Schemas", () => {
|
|
|
391
391
|
expect(() => updateTaskNotesSchema.parse(minimalMarkdownInput)).not.toThrow();
|
|
392
392
|
});
|
|
393
393
|
|
|
394
|
-
test("should
|
|
395
|
-
const
|
|
394
|
+
test("should accept both HTML and Markdown provided (XOR validation is in tool execute)", () => {
|
|
395
|
+
const inputWithBoth = {
|
|
396
396
|
taskId: "task-123",
|
|
397
397
|
html: "<p>HTML content</p>",
|
|
398
398
|
markdown: "Markdown content",
|
|
399
399
|
};
|
|
400
|
-
|
|
400
|
+
// Schema accepts both - XOR validation happens in the tool's execute function
|
|
401
|
+
expect(() => updateTaskNotesSchema.parse(inputWithBoth)).not.toThrow();
|
|
401
402
|
});
|
|
402
403
|
|
|
403
|
-
test("should
|
|
404
|
-
const
|
|
404
|
+
test("should accept neither HTML nor Markdown provided (XOR validation is in tool execute)", () => {
|
|
405
|
+
const inputWithNeither = {
|
|
405
406
|
taskId: "task-123",
|
|
406
407
|
limitResponsePayload: true,
|
|
407
408
|
};
|
|
408
|
-
|
|
409
|
+
// Schema accepts neither - XOR validation happens in the tool's execute function
|
|
410
|
+
expect(() => updateTaskNotesSchema.parse(inputWithNeither)).not.toThrow();
|
|
409
411
|
});
|
|
410
412
|
|
|
411
413
|
test("should reject empty task ID", () => {
|
package/src/schemas.ts
CHANGED
|
@@ -263,6 +263,75 @@ export const updateTaskStreamSchema = z.object({
|
|
|
263
263
|
),
|
|
264
264
|
});
|
|
265
265
|
|
|
266
|
+
/**
|
|
267
|
+
* Subtask Operation Schemas
|
|
268
|
+
*/
|
|
269
|
+
|
|
270
|
+
// Create subtasks parameters
|
|
271
|
+
export const createSubtasksSchema = z.object({
|
|
272
|
+
taskId: z.string().min(1, "Task ID is required").describe(
|
|
273
|
+
"The ID of the parent task",
|
|
274
|
+
),
|
|
275
|
+
subtaskIds: z.array(z.string().min(1)).min(1, "At least one subtask ID is required").describe(
|
|
276
|
+
"Array of subtask IDs to create (use SunsamaClient.generateTaskId() to generate IDs)",
|
|
277
|
+
),
|
|
278
|
+
limitResponsePayload: z.boolean().optional().describe(
|
|
279
|
+
"Whether to limit the response payload size",
|
|
280
|
+
),
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
// Update subtask title parameters
|
|
284
|
+
export const updateSubtaskTitleSchema = z.object({
|
|
285
|
+
taskId: z.string().min(1, "Task ID is required").describe(
|
|
286
|
+
"The ID of the parent task",
|
|
287
|
+
),
|
|
288
|
+
subtaskId: z.string().min(1, "Subtask ID is required").describe(
|
|
289
|
+
"The ID of the subtask to update",
|
|
290
|
+
),
|
|
291
|
+
title: z.string().min(1, "Subtask title is required").describe(
|
|
292
|
+
"The new title for the subtask",
|
|
293
|
+
),
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
// Complete subtask parameters
|
|
297
|
+
export const completeSubtaskSchema = z.object({
|
|
298
|
+
taskId: z.string().min(1, "Task ID is required").describe(
|
|
299
|
+
"The ID of the parent task",
|
|
300
|
+
),
|
|
301
|
+
subtaskId: z.string().min(1, "Subtask ID is required").describe(
|
|
302
|
+
"The ID of the subtask to mark as complete",
|
|
303
|
+
),
|
|
304
|
+
completedDate: z.string().optional().describe(
|
|
305
|
+
"Completion timestamp (ISO format). Defaults to current time",
|
|
306
|
+
),
|
|
307
|
+
limitResponsePayload: z.boolean().optional().describe(
|
|
308
|
+
"Whether to limit the response payload size",
|
|
309
|
+
),
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
// Uncomplete subtask parameters
|
|
313
|
+
export const uncompleteSubtaskSchema = z.object({
|
|
314
|
+
taskId: z.string().min(1, "Task ID is required").describe(
|
|
315
|
+
"The ID of the parent task",
|
|
316
|
+
),
|
|
317
|
+
subtaskId: z.string().min(1, "Subtask ID is required").describe(
|
|
318
|
+
"The ID of the subtask to mark as incomplete",
|
|
319
|
+
),
|
|
320
|
+
limitResponsePayload: z.boolean().optional().describe(
|
|
321
|
+
"Whether to limit the response payload size",
|
|
322
|
+
),
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
// Add subtask parameters (convenience method)
|
|
326
|
+
export const addSubtaskSchema = z.object({
|
|
327
|
+
taskId: z.string().min(1, "Task ID is required").describe(
|
|
328
|
+
"The ID of the parent task",
|
|
329
|
+
),
|
|
330
|
+
title: z.string().min(1, "Subtask title is required").describe(
|
|
331
|
+
"The title for the new subtask",
|
|
332
|
+
),
|
|
333
|
+
});
|
|
334
|
+
|
|
266
335
|
/**
|
|
267
336
|
* Response Type Schemas (for validation and documentation)
|
|
268
337
|
*/
|
|
@@ -375,6 +444,12 @@ export type UpdateTaskDueDateInput = z.infer<typeof updateTaskDueDateSchema>;
|
|
|
375
444
|
export type UpdateTaskTextInput = z.infer<typeof updateTaskTextSchema>;
|
|
376
445
|
export type UpdateTaskStreamInput = z.infer<typeof updateTaskStreamSchema>;
|
|
377
446
|
|
|
447
|
+
export type CreateSubtasksInput = z.infer<typeof createSubtasksSchema>;
|
|
448
|
+
export type UpdateSubtaskTitleInput = z.infer<typeof updateSubtaskTitleSchema>;
|
|
449
|
+
export type CompleteSubtaskInput = z.infer<typeof completeSubtaskSchema>;
|
|
450
|
+
export type UncompleteSubtaskInput = z.infer<typeof uncompleteSubtaskSchema>;
|
|
451
|
+
export type AddSubtaskInput = z.infer<typeof addSubtaskSchema>;
|
|
452
|
+
|
|
378
453
|
export type User = z.infer<typeof userSchema>;
|
|
379
454
|
export type Task = z.infer<typeof taskSchema>;
|
|
380
455
|
export type Stream = z.infer<typeof streamSchema>;
|
package/src/tools/task-tools.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import type { CreateTaskOptions } from "sunsama-api/types";
|
|
2
2
|
import {
|
|
3
|
+
type AddSubtaskInput,
|
|
4
|
+
addSubtaskSchema,
|
|
5
|
+
type CompleteSubtaskInput,
|
|
6
|
+
completeSubtaskSchema,
|
|
7
|
+
type CreateSubtasksInput,
|
|
8
|
+
createSubtasksSchema,
|
|
3
9
|
type CreateTaskInput,
|
|
4
10
|
createTaskSchema,
|
|
5
11
|
type DeleteTaskInput,
|
|
@@ -12,6 +18,10 @@ import {
|
|
|
12
18
|
getTasksBacklogSchema,
|
|
13
19
|
type GetTasksByDayInput,
|
|
14
20
|
getTasksByDaySchema,
|
|
21
|
+
type UncompleteSubtaskInput,
|
|
22
|
+
uncompleteSubtaskSchema,
|
|
23
|
+
type UpdateSubtaskTitleInput,
|
|
24
|
+
updateSubtaskTitleSchema,
|
|
15
25
|
type UpdateTaskBacklogInput,
|
|
16
26
|
updateTaskBacklogSchema,
|
|
17
27
|
type UpdateTaskCompleteInput,
|
|
@@ -409,6 +419,128 @@ export const updateTaskStreamTool = withTransportClient({
|
|
|
409
419
|
},
|
|
410
420
|
});
|
|
411
421
|
|
|
422
|
+
// Subtask Management Tools
|
|
423
|
+
export const createSubtasksTool = withTransportClient({
|
|
424
|
+
name: "create-subtasks",
|
|
425
|
+
description: "Create multiple subtasks for a task (low-level API for bulk operations)",
|
|
426
|
+
parameters: createSubtasksSchema,
|
|
427
|
+
execute: async (
|
|
428
|
+
{ taskId, subtaskIds, limitResponsePayload }: CreateSubtasksInput,
|
|
429
|
+
context: ToolContext,
|
|
430
|
+
) => {
|
|
431
|
+
const result = await context.client.createSubtasks(
|
|
432
|
+
taskId,
|
|
433
|
+
subtaskIds,
|
|
434
|
+
limitResponsePayload,
|
|
435
|
+
);
|
|
436
|
+
|
|
437
|
+
return formatJsonResponse({
|
|
438
|
+
success: result.success,
|
|
439
|
+
taskId,
|
|
440
|
+
subtaskIds,
|
|
441
|
+
subtasksCreated: true,
|
|
442
|
+
count: subtaskIds.length,
|
|
443
|
+
updatedFields: result.updatedFields,
|
|
444
|
+
});
|
|
445
|
+
},
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
export const updateSubtaskTitleTool = withTransportClient({
|
|
449
|
+
name: "update-subtask-title",
|
|
450
|
+
description: "Update the title of a subtask",
|
|
451
|
+
parameters: updateSubtaskTitleSchema,
|
|
452
|
+
execute: async (
|
|
453
|
+
{ taskId, subtaskId, title }: UpdateSubtaskTitleInput,
|
|
454
|
+
context: ToolContext,
|
|
455
|
+
) => {
|
|
456
|
+
const result = await context.client.updateSubtaskTitle(
|
|
457
|
+
taskId,
|
|
458
|
+
subtaskId,
|
|
459
|
+
title,
|
|
460
|
+
);
|
|
461
|
+
|
|
462
|
+
return formatJsonResponse({
|
|
463
|
+
success: result.success,
|
|
464
|
+
taskId,
|
|
465
|
+
subtaskId,
|
|
466
|
+
title,
|
|
467
|
+
subtaskTitleUpdated: true,
|
|
468
|
+
updatedFields: result.updatedFields,
|
|
469
|
+
});
|
|
470
|
+
},
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
export const completeSubtaskTool = withTransportClient({
|
|
474
|
+
name: "complete-subtask",
|
|
475
|
+
description: "Mark a subtask as complete with optional completion timestamp",
|
|
476
|
+
parameters: completeSubtaskSchema,
|
|
477
|
+
execute: async (
|
|
478
|
+
{ taskId, subtaskId, completedDate, limitResponsePayload }: CompleteSubtaskInput,
|
|
479
|
+
context: ToolContext,
|
|
480
|
+
) => {
|
|
481
|
+
const result = await context.client.completeSubtask(
|
|
482
|
+
taskId,
|
|
483
|
+
subtaskId,
|
|
484
|
+
completedDate,
|
|
485
|
+
limitResponsePayload,
|
|
486
|
+
);
|
|
487
|
+
|
|
488
|
+
return formatJsonResponse({
|
|
489
|
+
success: result.success,
|
|
490
|
+
taskId,
|
|
491
|
+
subtaskId,
|
|
492
|
+
subtaskCompleted: true,
|
|
493
|
+
completedDate: completedDate || new Date().toISOString(),
|
|
494
|
+
updatedFields: result.updatedFields,
|
|
495
|
+
});
|
|
496
|
+
},
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
export const uncompleteSubtaskTool = withTransportClient({
|
|
500
|
+
name: "uncomplete-subtask",
|
|
501
|
+
description: "Mark a subtask as incomplete (uncomplete it)",
|
|
502
|
+
parameters: uncompleteSubtaskSchema,
|
|
503
|
+
execute: async (
|
|
504
|
+
{ taskId, subtaskId, limitResponsePayload }: UncompleteSubtaskInput,
|
|
505
|
+
context: ToolContext,
|
|
506
|
+
) => {
|
|
507
|
+
const result = await context.client.uncompleteSubtask(
|
|
508
|
+
taskId,
|
|
509
|
+
subtaskId,
|
|
510
|
+
limitResponsePayload,
|
|
511
|
+
);
|
|
512
|
+
|
|
513
|
+
return formatJsonResponse({
|
|
514
|
+
success: result.success,
|
|
515
|
+
taskId,
|
|
516
|
+
subtaskId,
|
|
517
|
+
subtaskUncompleted: true,
|
|
518
|
+
updatedFields: result.updatedFields,
|
|
519
|
+
});
|
|
520
|
+
},
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
export const addSubtaskTool = withTransportClient({
|
|
524
|
+
name: "add-subtask",
|
|
525
|
+
description: "Convenience method to create a subtask with a title in one call (recommended for single subtask creation)",
|
|
526
|
+
parameters: addSubtaskSchema,
|
|
527
|
+
execute: async (
|
|
528
|
+
{ taskId, title }: AddSubtaskInput,
|
|
529
|
+
context: ToolContext,
|
|
530
|
+
) => {
|
|
531
|
+
const result = await context.client.addSubtask(taskId, title);
|
|
532
|
+
|
|
533
|
+
return formatJsonResponse({
|
|
534
|
+
success: result.result.success,
|
|
535
|
+
taskId,
|
|
536
|
+
subtaskId: result.subtaskId,
|
|
537
|
+
title,
|
|
538
|
+
subtaskAdded: true,
|
|
539
|
+
updatedFields: result.result.updatedFields,
|
|
540
|
+
});
|
|
541
|
+
},
|
|
542
|
+
});
|
|
543
|
+
|
|
412
544
|
// Export all task tools
|
|
413
545
|
export const taskTools = [
|
|
414
546
|
// Query tools
|
|
@@ -430,4 +562,11 @@ export const taskTools = [
|
|
|
430
562
|
updateTaskDueDateTool,
|
|
431
563
|
updateTaskTextTool,
|
|
432
564
|
updateTaskStreamTool,
|
|
565
|
+
|
|
566
|
+
// Subtask management tools
|
|
567
|
+
createSubtasksTool,
|
|
568
|
+
updateSubtaskTitleTool,
|
|
569
|
+
completeSubtaskTool,
|
|
570
|
+
uncompleteSubtaskTool,
|
|
571
|
+
addSubtaskTool,
|
|
433
572
|
];
|