mcp-sunsama 0.3.0 → 0.5.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/.claude/settings.local.json +3 -1
- package/CHANGELOG.md +24 -0
- package/CLAUDE.md +11 -3
- package/Dockerfile +35 -0
- package/README.md +2 -1
- package/TODO-0_5_0.md +108 -0
- package/TODO.md +152 -0
- package/bun.lock +35 -23
- package/dev/prd-get-archived-tasks.md +156 -0
- package/dist/config/transport.d.ts +7 -5
- package/dist/config/transport.d.ts.map +1 -1
- package/dist/config/transport.js +3 -1
- package/dist/main.js +3 -10
- package/dist/utils/task-trimmer.d.ts +5 -2
- package/dist/utils/task-trimmer.d.ts.map +1 -1
- package/dist/utils/task-trimmer.js +10 -1
- package/package.json +3 -3
- package/src/config/transport.ts +12 -6
- package/src/main.ts +162 -87
- package/src/schemas.ts +7 -0
- package/src/utils/task-trimmer.ts +33 -19
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# Product Requirements Document: get-archived-tasks Tool
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
This document outlines the requirements for implementing the `get-archived-tasks` MCP tool for the Sunsama MCP Server, providing access to archived task history with pagination and filtering capabilities.
|
|
5
|
+
|
|
6
|
+
## Background
|
|
7
|
+
With the update to sunsama-api v0.6.1, the `getArchivedTasks()` method is now available, enabling access to tasks that have been archived in Sunsama. This is a high-priority feature that provides essential access to historical task data for productivity analysis and task retrieval.
|
|
8
|
+
|
|
9
|
+
## Objectives
|
|
10
|
+
- Provide MCP tool access to archived tasks in Sunsama
|
|
11
|
+
- Enable pagination for efficient handling of large archived task datasets
|
|
12
|
+
- Maintain response optimization patterns established in the codebase
|
|
13
|
+
|
|
14
|
+
## Functional Requirements
|
|
15
|
+
|
|
16
|
+
### FR1: Core Functionality
|
|
17
|
+
- **Tool Name**: `get-archived-tasks`
|
|
18
|
+
- **API Method**: `sunsamaClient.getArchivedTasks(offset?, limit?)`
|
|
19
|
+
- **Purpose**: Retrieve archived tasks with optional pagination and completion filtering
|
|
20
|
+
|
|
21
|
+
### FR2: Parameters
|
|
22
|
+
| Parameter | Type | Required | Default | Description |
|
|
23
|
+
|-----------|------|----------|---------|-------------|
|
|
24
|
+
| `offset` | number | No | 0 | Pagination offset for archived tasks |
|
|
25
|
+
| `limit` | number | No | 100 | Maximum number of archived tasks to return (max: 1000) |
|
|
26
|
+
|
|
27
|
+
### FR3: Response Format
|
|
28
|
+
- **Format**: TSV (Tab-Separated Values) with pagination metadata header
|
|
29
|
+
- **Content**: Trimmed Task objects using existing `trimTasksForResponse()` utility
|
|
30
|
+
- **Pagination Metadata**: Include offset, limit, count, hasMore flag, and nextOffset
|
|
31
|
+
- **Optimization**: Apply task trimming for response efficiency
|
|
32
|
+
|
|
33
|
+
### FR4: Enhanced Pagination
|
|
34
|
+
- **Pattern**: Fetch limit+1 to determine if more results are available
|
|
35
|
+
- **Context**: Provide LLM with clear indication of whether to fetch more data
|
|
36
|
+
- **Metadata**: Include pagination information in response header
|
|
37
|
+
|
|
38
|
+
### FR5: Error Handling
|
|
39
|
+
- Authentication errors from client resolver
|
|
40
|
+
- Invalid pagination parameters (negative offset, excessive limit)
|
|
41
|
+
- Network/API errors with descriptive messages
|
|
42
|
+
- Consistent error logging pattern matching existing tools
|
|
43
|
+
|
|
44
|
+
## Technical Requirements
|
|
45
|
+
|
|
46
|
+
### TR1: Schema Definition
|
|
47
|
+
```typescript
|
|
48
|
+
export const getArchivedTasksSchema = z.object({
|
|
49
|
+
offset: z.number().int().min(0).optional().describe("Pagination offset (defaults to 0)"),
|
|
50
|
+
limit: z.number().int().min(1).max(1000).optional().describe("Maximum number of tasks to return (defaults to 300)"),
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### TR2: Tool Implementation Pattern
|
|
55
|
+
- Follow existing tool structure from `get-tasks-by-day` and `get-tasks-backlog`
|
|
56
|
+
- Use `getSunsamaClient()` for transport-agnostic client resolution
|
|
57
|
+
- Apply `trimTasksForResponse()` for response optimization
|
|
58
|
+
- Use `toTsv()` for consistent array output formatting
|
|
59
|
+
|
|
60
|
+
### TR3: Logging Requirements
|
|
61
|
+
- Info logs for operation start with parameters
|
|
62
|
+
- Success logs with task counts and pagination info
|
|
63
|
+
- Error logs with parameter context and error details
|
|
64
|
+
- Consistent log structure matching existing tools
|
|
65
|
+
|
|
66
|
+
### TR4: Authentication
|
|
67
|
+
- Leverage existing dual transport authentication system
|
|
68
|
+
- Support both stdio and HTTP Stream transport modes
|
|
69
|
+
- Use session-based client resolution pattern
|
|
70
|
+
|
|
71
|
+
## Non-Functional Requirements
|
|
72
|
+
|
|
73
|
+
### NFR1: Performance
|
|
74
|
+
- **Response Optimization**: Apply task trimming to reduce payload by 60-80%
|
|
75
|
+
- **Efficient Filtering**: Filter by completion status before trimming
|
|
76
|
+
- **Pagination Support**: Handle large archived task datasets efficiently
|
|
77
|
+
|
|
78
|
+
### NFR2: Consistency
|
|
79
|
+
- **Code Patterns**: Follow established patterns in `src/main.ts`
|
|
80
|
+
- **Error Handling**: Match existing error handling approach
|
|
81
|
+
- **Response Format**: Consistent with other task list endpoints (TSV)
|
|
82
|
+
- **Logging**: Maintain logging pattern consistency
|
|
83
|
+
|
|
84
|
+
### NFR3: Maintainability
|
|
85
|
+
- **Schema Validation**: Use Zod for type-safe parameter validation
|
|
86
|
+
- **Type Safety**: Leverage TypeScript inference from schemas
|
|
87
|
+
- **Documentation**: Clear parameter descriptions and usage notes
|
|
88
|
+
|
|
89
|
+
## Integration Requirements
|
|
90
|
+
|
|
91
|
+
### IR1: Schema Integration
|
|
92
|
+
- Add `getArchivedTasksSchema` to `src/schemas.ts`
|
|
93
|
+
- Include in schema exports
|
|
94
|
+
- Import in `src/main.ts`
|
|
95
|
+
|
|
96
|
+
### IR2: Utility Integration
|
|
97
|
+
- Use existing `filterTasksByCompletion()` from `utils/task-filters.ts`
|
|
98
|
+
- Use existing `trimTasksForResponse()` from `utils/task-trimmer.ts`
|
|
99
|
+
- Use existing `toTsv()` from `utils/to-tsv.ts`
|
|
100
|
+
- Use existing `getSunsamaClient()` from `utils/client-resolver.ts`
|
|
101
|
+
|
|
102
|
+
### IR3: Documentation Updates
|
|
103
|
+
- Update server instructions to include `get-archived-tasks` tool
|
|
104
|
+
- Add tool description to embedded documentation resource
|
|
105
|
+
- Update parameter descriptions and usage examples
|
|
106
|
+
|
|
107
|
+
## Testing Requirements
|
|
108
|
+
|
|
109
|
+
### T1: Functional Testing
|
|
110
|
+
- Test with valid pagination parameters (offset/limit)
|
|
111
|
+
- Test with different completion filters ("all", "incomplete", "completed")
|
|
112
|
+
- Test with edge cases (offset=0, large limits)
|
|
113
|
+
- Verify TSV output format correctness
|
|
114
|
+
|
|
115
|
+
### T2: Error Testing
|
|
116
|
+
- Test with invalid parameters (negative offset, excessive limit)
|
|
117
|
+
- Test authentication failure scenarios
|
|
118
|
+
- Test network error handling
|
|
119
|
+
- Verify error message clarity and consistency
|
|
120
|
+
|
|
121
|
+
### T3: Integration Testing
|
|
122
|
+
- Test with MCP Inspector (`bun run inspect`)
|
|
123
|
+
- Verify compatibility with both stdio and HTTP Stream transports
|
|
124
|
+
- Test response optimization (filtering + trimming)
|
|
125
|
+
- Validate against existing sample data
|
|
126
|
+
|
|
127
|
+
## Success Criteria
|
|
128
|
+
- [ ] Tool successfully retrieves archived tasks via `getArchivedTasks()` API
|
|
129
|
+
- [ ] Pagination works correctly with offset/limit parameters
|
|
130
|
+
- [ ] Completion filtering functions consistently with other task tools
|
|
131
|
+
- [ ] Response optimization reduces payload size by 60-80%
|
|
132
|
+
- [ ] Error handling provides clear, actionable error messages
|
|
133
|
+
- [ ] Integration testing passes with MCP Inspector
|
|
134
|
+
- [ ] Code follows established patterns and maintains consistency
|
|
135
|
+
|
|
136
|
+
## Implementation Priority
|
|
137
|
+
**High Priority** - This tool provides essential access to archived task history and is identified as a critical gap in the current MCP server functionality.
|
|
138
|
+
|
|
139
|
+
## Dependencies
|
|
140
|
+
- sunsama-api v0.6.1+ (already updated)
|
|
141
|
+
- Existing utility functions in `utils/` directory
|
|
142
|
+
- Existing schema patterns in `src/schemas.ts`
|
|
143
|
+
- Existing authentication and transport infrastructure
|
|
144
|
+
|
|
145
|
+
## Risk Assessment
|
|
146
|
+
- **Low Risk**: Implementation follows well-established patterns
|
|
147
|
+
- **API Stability**: Method is available and documented in sunsama-api
|
|
148
|
+
- **Testing Coverage**: Sample data available for validation
|
|
149
|
+
- **Integration**: Minimal impact on existing functionality
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
**Document Version**: 1.0
|
|
154
|
+
**Created**: 2025-06-20
|
|
155
|
+
**Author**: Implementation Team
|
|
156
|
+
**Status**: Ready for Implementation
|
|
@@ -3,13 +3,15 @@
|
|
|
3
3
|
* Supports both stdio and httpStream transports based on environment variables
|
|
4
4
|
*/
|
|
5
5
|
export type TransportType = "stdio" | "httpStream";
|
|
6
|
-
export
|
|
7
|
-
transportType:
|
|
8
|
-
|
|
6
|
+
export type TransportConfig = {
|
|
7
|
+
transportType: "stdio";
|
|
8
|
+
} | {
|
|
9
|
+
transportType: "httpStream";
|
|
10
|
+
httpStream: {
|
|
9
11
|
port: number;
|
|
10
|
-
endpoint: string
|
|
12
|
+
endpoint: `/${string}`;
|
|
11
13
|
};
|
|
12
|
-
}
|
|
14
|
+
};
|
|
13
15
|
/**
|
|
14
16
|
* Gets the transport configuration based on environment variables
|
|
15
17
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../src/config/transport.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,YAAY,CAAC;AAEnD,MAAM,
|
|
1
|
+
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../src/config/transport.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,YAAY,CAAC;AAEnD,MAAM,MAAM,eAAe,GACvB;IACF,aAAa,EAAE,OAAO,CAAC;CACxB,GACG;IACF,aAAa,EAAE,YAAY,CAAC;IAC5B,UAAU,EAAE;QACV,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,IAAI,MAAM,EAAE,CAAC;KACxB,CAAC;CACH,CAAC;AAgBF;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,IAAI,eAAe,CA0BpD;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,OAAO,CAE/C;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAE1C"}
|
package/dist/config/transport.js
CHANGED
|
@@ -12,7 +12,9 @@ const TransportEnvSchema = z.object({
|
|
|
12
12
|
.transform(val => parseInt(val, 10))
|
|
13
13
|
.pipe(z.number().min(1).max(65535))
|
|
14
14
|
.optional(),
|
|
15
|
-
HTTP_ENDPOINT: z.string().
|
|
15
|
+
HTTP_ENDPOINT: z.string().refine(val => val.startsWith("/"), {
|
|
16
|
+
message: "HTTP_ENDPOINT must start with '/'"
|
|
17
|
+
}).transform(val => val).default("/mcp")
|
|
16
18
|
});
|
|
17
19
|
/**
|
|
18
20
|
* Gets the transport configuration based on environment variables
|
package/dist/main.js
CHANGED
|
@@ -16,7 +16,7 @@ if (transportConfig.transportType === "stdio") {
|
|
|
16
16
|
}
|
|
17
17
|
const server = new FastMCP({
|
|
18
18
|
name: "Sunsama API Server",
|
|
19
|
-
version: "0.
|
|
19
|
+
version: "0.4.0",
|
|
20
20
|
instructions: `
|
|
21
21
|
This MCP server provides access to the Sunsama API for task and project management.
|
|
22
22
|
|
|
@@ -509,19 +509,12 @@ Required environment variables:
|
|
|
509
509
|
if (transportConfig.transportType === "httpStream") {
|
|
510
510
|
// Log startup information
|
|
511
511
|
console.log(`HTTP Stream configuration: port=${transportConfig.httpStream?.port}, endpoint=${transportConfig.httpStream?.endpoint}`);
|
|
512
|
-
server.start({
|
|
513
|
-
transportType: "httpStream",
|
|
514
|
-
httpStream: {
|
|
515
|
-
port: transportConfig.httpStream.port
|
|
516
|
-
}
|
|
517
|
-
}).then(() => {
|
|
512
|
+
server.start(transportConfig).then(() => {
|
|
518
513
|
console.log(`Sunsama MCP Server running on port ${transportConfig.httpStream.port}`);
|
|
519
514
|
console.log(`HTTP endpoint: ${transportConfig.httpStream.endpoint}`);
|
|
520
515
|
console.log("Authentication: HTTP Basic Auth with Sunsama credentials");
|
|
521
516
|
});
|
|
522
517
|
}
|
|
523
518
|
else {
|
|
524
|
-
server.start(
|
|
525
|
-
transportType: "stdio"
|
|
526
|
-
});
|
|
519
|
+
server.start(transportConfig);
|
|
527
520
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Task } from "sunsama-api";
|
|
1
|
+
import type { Task, TaskIntegration } from "sunsama-api";
|
|
2
2
|
/**
|
|
3
3
|
* Trimmed task type containing only essential properties for API responses.
|
|
4
4
|
* Reduces response size by 60-80% while preserving core task information.
|
|
@@ -9,7 +9,10 @@ import type { Task } from "sunsama-api";
|
|
|
9
9
|
*/
|
|
10
10
|
export type TrimmedTask = Pick<Task, '_id' | 'text' | 'completed' | 'assigneeId' | 'createdAt' | 'lastModified' | 'objectiveId' | 'completeDate' | 'timeEstimate' | 'dueDate' | 'notes' | 'streamIds'> & {
|
|
11
11
|
/** Integration service name (e.g., 'website', 'googleCalendar') or null */
|
|
12
|
-
integration:
|
|
12
|
+
integration: {
|
|
13
|
+
service: TaskIntegration['service'];
|
|
14
|
+
url?: string;
|
|
15
|
+
} | null;
|
|
13
16
|
/** Array of subtask titles only (simplified from full subtask objects) */
|
|
14
17
|
subtasks: string[];
|
|
15
18
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task-trimmer.d.ts","sourceRoot":"","sources":["../../src/utils/task-trimmer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"task-trimmer.d.ts","sourceRoot":"","sources":["../../src/utils/task-trimmer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEzD;;;;;;;GAOG;AACH,MAAM,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,EAC/B,KAAK,GACL,MAAM,GACN,WAAW,GACX,YAAY,GACZ,WAAW,GACX,cAAc,GACd,aAAa,GACb,cAAc,GACd,cAAc,GACd,SAAS,GACT,OAAO,GACP,WAAW,CACd,GAAG;IACF,2EAA2E;IAC3E,WAAW,EAAE;QACX,OAAO,EAAE,eAAe,CAAC,SAAS,CAAC,CAAC;QACpC,GAAG,CAAC,EAAE,MAAM,CAAC;KACd,GAAG,IAAI,CAAC;IACT,0EAA0E;IAC1E,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,IAAI,GAAG,WAAW,CA4B3D;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,WAAW,EAAE,CAEjE"}
|
|
@@ -22,6 +22,15 @@
|
|
|
22
22
|
* @returns Trimmed task object with only essential properties
|
|
23
23
|
*/
|
|
24
24
|
export function trimTaskForResponse(task) {
|
|
25
|
+
let integration = null;
|
|
26
|
+
// Extract minimal integration data: service type and URL if available
|
|
27
|
+
// Integration identifiers vary by service - some have URLs (websites), others have different properties
|
|
28
|
+
if (task.integration) {
|
|
29
|
+
integration = { service: task.integration.service };
|
|
30
|
+
if ("url" in task.integration.identifier) {
|
|
31
|
+
integration.url = task.integration.identifier.url;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
25
34
|
return {
|
|
26
35
|
_id: task._id,
|
|
27
36
|
assigneeId: task.assigneeId,
|
|
@@ -29,7 +38,7 @@ export function trimTaskForResponse(task) {
|
|
|
29
38
|
completed: task.completed,
|
|
30
39
|
createdAt: task.createdAt,
|
|
31
40
|
dueDate: task.dueDate,
|
|
32
|
-
integration:
|
|
41
|
+
integration: integration,
|
|
33
42
|
lastModified: task.lastModified,
|
|
34
43
|
notes: task.notes,
|
|
35
44
|
objectiveId: task.objectiveId,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-sunsama",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "MCP server for Sunsama API integration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
@@ -23,9 +23,9 @@
|
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@types/papaparse": "^5.3.16",
|
|
26
|
-
"fastmcp": "
|
|
26
|
+
"fastmcp": "3.3.1",
|
|
27
27
|
"papaparse": "^5.5.3",
|
|
28
|
-
"sunsama-api": "0.
|
|
28
|
+
"sunsama-api": "0.6.1",
|
|
29
29
|
"zod": "3.24.4"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
package/src/config/transport.ts
CHANGED
|
@@ -7,13 +7,17 @@ import { z } from "zod";
|
|
|
7
7
|
|
|
8
8
|
export type TransportType = "stdio" | "httpStream";
|
|
9
9
|
|
|
10
|
-
export
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
export type TransportConfig =
|
|
11
|
+
| {
|
|
12
|
+
transportType: "stdio";
|
|
13
|
+
}
|
|
14
|
+
| {
|
|
15
|
+
transportType: "httpStream";
|
|
16
|
+
httpStream: {
|
|
13
17
|
port: number;
|
|
14
|
-
endpoint: string
|
|
18
|
+
endpoint: `/${string}`;
|
|
15
19
|
};
|
|
16
|
-
}
|
|
20
|
+
};
|
|
17
21
|
|
|
18
22
|
/**
|
|
19
23
|
* Zod schema for validating transport-related environment variables
|
|
@@ -24,7 +28,9 @@ const TransportEnvSchema = z.object({
|
|
|
24
28
|
.transform(val => parseInt(val, 10))
|
|
25
29
|
.pipe(z.number().min(1).max(65535))
|
|
26
30
|
.optional(),
|
|
27
|
-
HTTP_ENDPOINT: z.string().
|
|
31
|
+
HTTP_ENDPOINT: z.string().refine(val => val.startsWith("/"), {
|
|
32
|
+
message: "HTTP_ENDPOINT must start with '/'"
|
|
33
|
+
}).transform(val => val as `/${string}`).default("/mcp")
|
|
28
34
|
});
|
|
29
35
|
|
|
30
36
|
/**
|
package/src/main.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { getTransportConfig } from "./config/transport.js";
|
|
|
8
8
|
import {
|
|
9
9
|
createTaskSchema,
|
|
10
10
|
deleteTaskSchema,
|
|
11
|
+
getArchivedTasksSchema,
|
|
11
12
|
getStreamsSchema,
|
|
12
13
|
getTasksBacklogSchema,
|
|
13
14
|
getTasksByDaySchema,
|
|
@@ -28,17 +29,16 @@ if (transportConfig.transportType === "stdio") {
|
|
|
28
29
|
await initializeStdioAuth();
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
|
|
32
32
|
const server = new FastMCP({
|
|
33
33
|
name: "Sunsama API Server",
|
|
34
|
-
version: "0.
|
|
34
|
+
version: "0.5.0",
|
|
35
35
|
instructions: `
|
|
36
36
|
This MCP server provides access to the Sunsama API for task and project management.
|
|
37
37
|
|
|
38
38
|
Available tools:
|
|
39
39
|
- Authentication: login, logout, check authentication status
|
|
40
40
|
- User operations: get current user information
|
|
41
|
-
- Task operations: get tasks by day, get backlog tasks
|
|
41
|
+
- Task operations: get tasks by day, get backlog tasks, get archived tasks
|
|
42
42
|
- Stream operations: get streams/channels for the user's group
|
|
43
43
|
|
|
44
44
|
Authentication is required for all operations. You can either:
|
|
@@ -188,6 +188,79 @@ server.addTool({
|
|
|
188
188
|
}
|
|
189
189
|
});
|
|
190
190
|
|
|
191
|
+
server.addTool({
|
|
192
|
+
name: "get-archived-tasks",
|
|
193
|
+
description: "Get archived tasks with optional pagination",
|
|
194
|
+
parameters: getArchivedTasksSchema,
|
|
195
|
+
execute: async (args, {session, log}) => {
|
|
196
|
+
try {
|
|
197
|
+
// Extract and set defaults for parameters
|
|
198
|
+
const offset = args.offset || 0;
|
|
199
|
+
const requestedLimit = args.limit || 100;
|
|
200
|
+
|
|
201
|
+
// Fetch limit + 1 to determine if there are more results
|
|
202
|
+
const fetchLimit = requestedLimit + 1;
|
|
203
|
+
|
|
204
|
+
log.info("Getting archived tasks", {
|
|
205
|
+
offset,
|
|
206
|
+
requestedLimit,
|
|
207
|
+
fetchLimit
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
// Get the appropriate client based on transport type
|
|
211
|
+
const sunsamaClient = getSunsamaClient(session as SessionData | null);
|
|
212
|
+
|
|
213
|
+
// Get archived tasks (fetch limit + 1 to check for more)
|
|
214
|
+
const allTasks = await sunsamaClient.getArchivedTasks(offset, fetchLimit);
|
|
215
|
+
|
|
216
|
+
// Determine if there are more results and slice to requested limit
|
|
217
|
+
const hasMore = allTasks.length > requestedLimit;
|
|
218
|
+
const tasks = hasMore ? allTasks.slice(0, requestedLimit) : allTasks;
|
|
219
|
+
|
|
220
|
+
// Trim tasks to reduce response size while preserving essential data
|
|
221
|
+
const trimmedTasks = trimTasksForResponse(tasks);
|
|
222
|
+
|
|
223
|
+
// Create pagination metadata
|
|
224
|
+
const paginationInfo = {
|
|
225
|
+
offset,
|
|
226
|
+
limit: requestedLimit,
|
|
227
|
+
count: tasks.length,
|
|
228
|
+
hasMore,
|
|
229
|
+
nextOffset: hasMore ? offset + requestedLimit : null
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
log.info("Successfully retrieved archived tasks", {
|
|
233
|
+
totalReturned: tasks.length,
|
|
234
|
+
hasMore,
|
|
235
|
+
offset,
|
|
236
|
+
requestedLimit
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
// Create response with pagination metadata header and TSV data
|
|
240
|
+
const responseText = `# Pagination: offset=${paginationInfo.offset}, limit=${paginationInfo.limit}, count=${paginationInfo.count}, hasMore=${paginationInfo.hasMore}, nextOffset=${paginationInfo.nextOffset || 'null'}
|
|
241
|
+
${toTsv(trimmedTasks)}`;
|
|
242
|
+
|
|
243
|
+
return {
|
|
244
|
+
content: [
|
|
245
|
+
{
|
|
246
|
+
type: "text",
|
|
247
|
+
text: responseText
|
|
248
|
+
}
|
|
249
|
+
]
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
} catch (error) {
|
|
253
|
+
log.error("Failed to get archived tasks", {
|
|
254
|
+
offset: args.offset,
|
|
255
|
+
limit: args.limit,
|
|
256
|
+
error: error instanceof Error ? error.message : 'Unknown error'
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
throw new Error(`Failed to get archived tasks: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
|
|
191
264
|
// Task Mutation Operations
|
|
192
265
|
server.addTool({
|
|
193
266
|
name: "create-task",
|
|
@@ -478,15 +551,32 @@ server.addResource({
|
|
|
478
551
|
text: `# Sunsama MCP Server Documentation
|
|
479
552
|
|
|
480
553
|
## Overview
|
|
481
|
-
This MCP server provides access to the Sunsama API for task and project management.
|
|
482
|
-
|
|
554
|
+
This MCP server provides comprehensive access to the Sunsama API for task and project management.
|
|
555
|
+
Supports dual transport modes with different authentication strategies.
|
|
556
|
+
|
|
557
|
+
## Transport Modes
|
|
558
|
+
|
|
559
|
+
### Stdio Transport (Default)
|
|
560
|
+
- Single global authentication using environment variables
|
|
561
|
+
- Session maintained for entire server lifetime
|
|
562
|
+
- Best for single-user local development
|
|
563
|
+
|
|
564
|
+
### HTTP Stream Transport
|
|
565
|
+
- Per-request authentication via HTTP Basic Auth
|
|
566
|
+
- Session-isolated client instances
|
|
567
|
+
- Supports multiple concurrent users
|
|
483
568
|
|
|
484
569
|
## Authentication
|
|
485
|
-
|
|
570
|
+
|
|
571
|
+
### Stdio Transport
|
|
572
|
+
Uses environment variables (authenticated once at startup):
|
|
486
573
|
- \`SUNSAMA_EMAIL\`: Your Sunsama account email
|
|
487
574
|
- \`SUNSAMA_PASSWORD\`: Your Sunsama account password
|
|
488
575
|
|
|
489
|
-
|
|
576
|
+
### HTTP Stream Transport
|
|
577
|
+
Uses HTTP Basic Auth headers (per-request authentication):
|
|
578
|
+
- \`Authorization: Basic <base64(email:password)>\`
|
|
579
|
+
- Credentials provided in each HTTP request
|
|
490
580
|
|
|
491
581
|
## Available Tools
|
|
492
582
|
|
|
@@ -495,93 +585,85 @@ Authentication happens automatically on server startup. No client-side authentic
|
|
|
495
585
|
- Parameters: none
|
|
496
586
|
- Returns: User object with profile, timezone, and primary group details
|
|
497
587
|
|
|
498
|
-
### Task
|
|
588
|
+
### Task Management
|
|
499
589
|
- **get-tasks-by-day**: Get tasks for a specific day with optional filtering
|
|
500
590
|
- Parameters:
|
|
501
591
|
- \`day\` (required): Date in YYYY-MM-DD format
|
|
502
592
|
- \`timezone\` (optional): Timezone string (e.g., "America/New_York")
|
|
503
|
-
- \`completionFilter\` (optional): Filter by completion status
|
|
504
|
-
|
|
505
|
-
- \`"incomplete"\`: Return only incomplete tasks
|
|
506
|
-
- \`"completed"\`: Return only completed tasks
|
|
507
|
-
- Returns: Array of filtered Task objects for the specified day
|
|
593
|
+
- \`completionFilter\` (optional): Filter by completion status ("all", "incomplete", "completed")
|
|
594
|
+
- Returns: TSV of filtered Task objects for the specified day
|
|
508
595
|
|
|
509
596
|
- **get-tasks-backlog**: Get tasks from the backlog
|
|
510
597
|
- Parameters: none
|
|
511
|
-
- Returns:
|
|
598
|
+
- Returns: TSV of Task objects from the backlog
|
|
599
|
+
|
|
600
|
+
- **get-archived-tasks**: Get archived tasks with optional pagination
|
|
601
|
+
- Parameters:
|
|
602
|
+
- \`offset\` (optional): Pagination offset (defaults to 0)
|
|
603
|
+
- \`limit\` (optional): Maximum number of tasks to return (defaults to 100, max: 1000)
|
|
604
|
+
- Returns: TSV of trimmed archived Task objects with pagination metadata header
|
|
605
|
+
- Pagination: Uses limit+1 pattern to determine if more results are available
|
|
606
|
+
|
|
607
|
+
- **create-task**: Create a new task with optional properties
|
|
608
|
+
- Parameters:
|
|
609
|
+
- \`text\` (required): Task title/description
|
|
610
|
+
- \`notes\` (optional): Additional task notes
|
|
611
|
+
- \`streamIds\` (optional): Array of stream IDs to associate with task
|
|
612
|
+
- \`timeEstimate\` (optional): Time estimate in minutes
|
|
613
|
+
- \`dueDate\` (optional): Due date string (ISO format)
|
|
614
|
+
- \`snoozeUntil\` (optional): Snooze until date string (ISO format)
|
|
615
|
+
- \`private\` (optional): Whether the task is private
|
|
616
|
+
- \`taskId\` (optional): Custom task ID
|
|
617
|
+
- Returns: JSON with task creation result
|
|
618
|
+
|
|
619
|
+
- **update-task-complete**: Mark a task as complete
|
|
620
|
+
- Parameters:
|
|
621
|
+
- \`taskId\` (required): The ID of the task to mark as complete
|
|
622
|
+
- \`completeOn\` (optional): Completion timestamp (ISO format)
|
|
623
|
+
- \`limitResponsePayload\` (optional): Whether to limit response size
|
|
624
|
+
- Returns: JSON with completion result
|
|
625
|
+
|
|
626
|
+
- **delete-task**: Delete a task permanently
|
|
627
|
+
- Parameters:
|
|
628
|
+
- \`taskId\` (required): The ID of the task to delete
|
|
629
|
+
- \`limitResponsePayload\` (optional): Whether to limit response size
|
|
630
|
+
- \`wasTaskMerged\` (optional): Whether the task was merged before deletion
|
|
631
|
+
- Returns: JSON with deletion result
|
|
632
|
+
|
|
633
|
+
- **update-task-snooze-date**: Reschedule tasks or move to backlog
|
|
634
|
+
- Parameters:
|
|
635
|
+
- \`taskId\` (required): The ID of the task to reschedule
|
|
636
|
+
- \`newDay\` (required): Target date in YYYY-MM-DD format, or null for backlog
|
|
637
|
+
- \`timezone\` (optional): Timezone string
|
|
638
|
+
- \`limitResponsePayload\` (optional): Whether to limit response size
|
|
639
|
+
- Returns: JSON with update result
|
|
512
640
|
|
|
513
641
|
### Stream Operations
|
|
514
642
|
- **get-streams**: Get streams for the user's group
|
|
515
643
|
- Parameters: none
|
|
516
|
-
- Returns:
|
|
517
|
-
- Note: Streams are called "channels" in the Sunsama UI
|
|
518
|
-
|
|
519
|
-
## Data Types
|
|
520
|
-
|
|
521
|
-
### User Object
|
|
522
|
-
\`\`\`typescript
|
|
523
|
-
{
|
|
524
|
-
_id: string;
|
|
525
|
-
email: string;
|
|
526
|
-
profile: {
|
|
527
|
-
_id: string;
|
|
528
|
-
email: string;
|
|
529
|
-
firstName: string;
|
|
530
|
-
lastName: string;
|
|
531
|
-
timezone: string;
|
|
532
|
-
avatarUrl?: string;
|
|
533
|
-
};
|
|
534
|
-
primaryGroup?: {
|
|
535
|
-
groupId: string;
|
|
536
|
-
name: string;
|
|
537
|
-
role?: string;
|
|
538
|
-
};
|
|
539
|
-
}
|
|
540
|
-
\`\`\`
|
|
541
|
-
|
|
542
|
-
### Task Object
|
|
543
|
-
\`\`\`typescript
|
|
544
|
-
{
|
|
545
|
-
_id: string;
|
|
546
|
-
title: string;
|
|
547
|
-
description?: string;
|
|
548
|
-
status: string;
|
|
549
|
-
createdAt: string;
|
|
550
|
-
updatedAt: string;
|
|
551
|
-
scheduledDate?: string;
|
|
552
|
-
completedAt?: string;
|
|
553
|
-
streamId?: string;
|
|
554
|
-
userId: string;
|
|
555
|
-
groupId: string;
|
|
556
|
-
}
|
|
557
|
-
\`\`\`
|
|
558
|
-
|
|
559
|
-
### Stream Object
|
|
560
|
-
Note: Streams are called "channels" in the Sunsama UI.
|
|
561
|
-
\`\`\`typescript
|
|
562
|
-
{
|
|
563
|
-
_id: string;
|
|
564
|
-
name: string;
|
|
565
|
-
color?: string;
|
|
566
|
-
groupId: string;
|
|
567
|
-
isActive: boolean;
|
|
568
|
-
createdAt: string;
|
|
569
|
-
updatedAt: string;
|
|
570
|
-
}
|
|
571
|
-
\`\`\`
|
|
644
|
+
- Returns: TSV of Stream objects
|
|
645
|
+
- Note: Streams are called "channels" in the Sunsama UI
|
|
572
646
|
|
|
573
|
-
##
|
|
574
|
-
-
|
|
575
|
-
-
|
|
576
|
-
-
|
|
577
|
-
- Server maintains session state across tool calls
|
|
647
|
+
## Response Optimization
|
|
648
|
+
- **Task Filtering**: Applied before processing for efficiency
|
|
649
|
+
- **Task Trimming**: Removes non-essential fields, reducing payload by 60-80%
|
|
650
|
+
- **TSV Format**: Used for arrays to optimize data processing
|
|
578
651
|
|
|
579
652
|
## Environment Setup
|
|
580
|
-
|
|
581
|
-
|
|
653
|
+
|
|
654
|
+
### Required (Stdio Transport)
|
|
582
655
|
- \`SUNSAMA_EMAIL\`: Sunsama account email
|
|
583
656
|
- \`SUNSAMA_PASSWORD\`: Sunsama account password
|
|
584
|
-
|
|
657
|
+
|
|
658
|
+
### Optional Configuration
|
|
659
|
+
- \`TRANSPORT_TYPE\`: "stdio" (default) | "httpStream"
|
|
660
|
+
- \`PORT\`: Server port for HTTP transport (default: 3002)
|
|
661
|
+
|
|
662
|
+
## Error Handling
|
|
663
|
+
- Comprehensive parameter validation using Zod schemas
|
|
664
|
+
- Graceful handling of network errors with descriptive messages
|
|
665
|
+
- Session-specific error isolation in HTTP transport mode
|
|
666
|
+
- Proper authentication error responses
|
|
585
667
|
`.trim()
|
|
586
668
|
};
|
|
587
669
|
}
|
|
@@ -593,18 +675,11 @@ if (transportConfig.transportType === "httpStream") {
|
|
|
593
675
|
// Log startup information
|
|
594
676
|
console.log(`HTTP Stream configuration: port=${transportConfig.httpStream?.port}, endpoint=${transportConfig.httpStream?.endpoint}`);
|
|
595
677
|
|
|
596
|
-
server.start({
|
|
597
|
-
transportType: "httpStream",
|
|
598
|
-
httpStream: {
|
|
599
|
-
port: transportConfig.httpStream!.port
|
|
600
|
-
}
|
|
601
|
-
}).then(() => {
|
|
678
|
+
server.start(transportConfig).then(() => {
|
|
602
679
|
console.log(`Sunsama MCP Server running on port ${transportConfig.httpStream!.port}`);
|
|
603
680
|
console.log(`HTTP endpoint: ${transportConfig.httpStream!.endpoint}`);
|
|
604
681
|
console.log("Authentication: HTTP Basic Auth with Sunsama credentials");
|
|
605
682
|
});
|
|
606
683
|
} else {
|
|
607
|
-
server.start(
|
|
608
|
-
transportType: "stdio"
|
|
609
|
-
});
|
|
684
|
+
server.start(transportConfig);
|
|
610
685
|
}
|
package/src/schemas.ts
CHANGED
|
@@ -17,6 +17,12 @@ export const getTasksByDaySchema = z.object({
|
|
|
17
17
|
// Get tasks backlog parameters (no parameters needed)
|
|
18
18
|
export const getTasksBacklogSchema = z.object({});
|
|
19
19
|
|
|
20
|
+
// Get archived tasks parameters
|
|
21
|
+
export const getArchivedTasksSchema = z.object({
|
|
22
|
+
offset: z.number().int().min(0).optional().describe("Pagination offset (defaults to 0)"),
|
|
23
|
+
limit: z.number().int().min(1).max(1000).optional().describe("Maximum number of tasks to return (defaults to 100)"),
|
|
24
|
+
});
|
|
25
|
+
|
|
20
26
|
/**
|
|
21
27
|
* User Operation Schemas
|
|
22
28
|
*/
|
|
@@ -161,6 +167,7 @@ export type CompletionFilter = z.infer<typeof completionFilterSchema>;
|
|
|
161
167
|
|
|
162
168
|
export type GetTasksByDayInput = z.infer<typeof getTasksByDaySchema>;
|
|
163
169
|
export type GetTasksBacklogInput = z.infer<typeof getTasksBacklogSchema>;
|
|
170
|
+
export type GetArchivedTasksInput = z.infer<typeof getArchivedTasksSchema>;
|
|
164
171
|
export type GetUserInput = z.infer<typeof getUserSchema>;
|
|
165
172
|
export type GetStreamsInput = z.infer<typeof getStreamsSchema>;
|
|
166
173
|
|