@kydycode/todoist-mcp-server-ext 0.4.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/README.md +27 -4
- package/dist/index.js +179 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,12 +15,13 @@ A comprehensive MCP (Model Context Protocol) server implementation that provides
|
|
|
15
15
|
* **Optimized API Usage**: Efficient use of Todoist API, including `getTasksByFilter` for robust search and `moveTasks` for semantic task movement.
|
|
16
16
|
* **Better Response Formatting**: Enhanced task, project, and label formatting for better readability, including project names in search results.
|
|
17
17
|
|
|
18
|
-
### ✨ **Enhanced Task Management (
|
|
18
|
+
### ✨ **Enhanced Task Management (11 Tools)**
|
|
19
19
|
* **Direct ID-based Operations**: Efficient task operations using task IDs.
|
|
20
20
|
* **Comprehensive Task Creation**: Support for subtasks, labels, projects, sections, priorities.
|
|
21
21
|
* **Quick Add Integration**: Natural language task creation using Todoist's Quick Add.
|
|
22
22
|
* **Advanced Task Search**: Robust keyword search using Todoist's filter engine (`search: your query`).
|
|
23
23
|
* **Task Movement Capabilities**: Move tasks between projects, sections, or make them subtasks.
|
|
24
|
+
* **Bulk Task Operations**: Move multiple tasks with subtasks in a single operation.
|
|
24
25
|
* **Task State Management**: Complete, reopen, and manage task lifecycle.
|
|
25
26
|
* **Detailed Task Output**: Search and get-task operations return more task details.
|
|
26
27
|
|
|
@@ -46,9 +47,15 @@ A comprehensive MCP (Model Context Protocol) server implementation that provides
|
|
|
46
47
|
* **Flexible Targeting**: Comments can be attached to either tasks or projects.
|
|
47
48
|
* **Paginated Comment Retrieval**: Efficiently browse through comment threads.
|
|
48
49
|
|
|
49
|
-
|
|
50
|
+
### ✅ **Completed Tasks History (1 Tool)** 🆕
|
|
51
|
+
* **Sync API Integration**: Access completed task history via Todoist Sync API.
|
|
52
|
+
* **Flexible Filtering**: Filter by project, date range (since/until).
|
|
53
|
+
* **Pagination Support**: Retrieve large histories with limit/offset.
|
|
54
|
+
* **Rich Metadata**: Shows project/section names, note counts, completion timestamps.
|
|
50
55
|
|
|
51
|
-
|
|
56
|
+
## 🛠️ Available Tools (Total 31)
|
|
57
|
+
|
|
58
|
+
### Task Operations (11 tools)
|
|
52
59
|
| Tool | Description |
|
|
53
60
|
|---------------------------|-------------------------------------------------------------------------------------|
|
|
54
61
|
| `todoist_create_task` | Create tasks with full options (subtasks, labels, projects, sections, priorities). |
|
|
@@ -61,6 +68,7 @@ A comprehensive MCP (Model Context Protocol) server implementation that provides
|
|
|
61
68
|
| `todoist_reopen_task` | Reopen completed task. |
|
|
62
69
|
| `todoist_search_tasks` | Search tasks using Todoist's filter engine (e.g., `search: keyword`). |
|
|
63
70
|
| `todoist_move_task` | Move a task to a different project, section, or make it a subtask. |
|
|
71
|
+
| `todoist_bulk_move_tasks` | Move multiple tasks with their subtasks to a project, section, or parent task. |
|
|
64
72
|
|
|
65
73
|
### Project Operations (5 tools)
|
|
66
74
|
| Tool | Description |
|
|
@@ -97,6 +105,11 @@ A comprehensive MCP (Model Context Protocol) server implementation that provides
|
|
|
97
105
|
| `todoist_update_comment` | Update an existing comment by its ID. |
|
|
98
106
|
| `todoist_delete_comment` | Delete a comment by its ID. |
|
|
99
107
|
|
|
108
|
+
### Completed Tasks Operations (1 tool) 🆕
|
|
109
|
+
| Tool | Description |
|
|
110
|
+
|--------------------------------|-----------------------------------------------------------------|
|
|
111
|
+
| `todoist_get_completed_tasks` | Get completed tasks history with filtering by project, date range (since/until), and pagination. Uses Todoist Sync API. |
|
|
112
|
+
|
|
100
113
|
## 🚀 Installation & Setup
|
|
101
114
|
|
|
102
115
|
### Local Development Setup
|
|
@@ -193,6 +206,15 @@ npm install -g @kydycode/todoist-mcp-server-ext@latest
|
|
|
193
206
|
"Update comment {comment_id} with new content"
|
|
194
207
|
```
|
|
195
208
|
|
|
209
|
+
### ✅ Completed Tasks History 🆕
|
|
210
|
+
```
|
|
211
|
+
"Show my completed tasks"
|
|
212
|
+
"Get completed tasks from project {project_id}"
|
|
213
|
+
"Show tasks completed since 2024-01-01"
|
|
214
|
+
"Get completed tasks between 2024-01-01 and 2024-01-31"
|
|
215
|
+
"Show last 50 completed tasks"
|
|
216
|
+
```
|
|
217
|
+
|
|
196
218
|
## 🆚 Extended vs Original Comparison
|
|
197
219
|
|
|
198
220
|
| Feature | Original | Extended Version (`@kydycode/todoist-mcp-server-ext`) |
|
|
@@ -201,11 +223,12 @@ npm install -g @kydycode/todoist-mcp-server-ext@latest
|
|
|
201
223
|
| **MCP SDK Compatibility** | Older version | ✅ Latest MCP SDK 0.5.0+ |
|
|
202
224
|
| **Error Handling** | Basic | ✅ Comprehensive with detailed messages |
|
|
203
225
|
| **TypeScript Support** | Limited | ✅ Full type safety |
|
|
204
|
-
| **Task Operations** | Search-based, limited features | ✅
|
|
226
|
+
| **Task Operations** | Search-based, limited features | ✅ 11 Tools: Direct ID-based, `moveTasks`, bulk move, robust search, QuickAdd, full CRUD-like ops |
|
|
205
227
|
| **Project Management** | Limited | ✅ 5 Tools: Full CRUD operations, sub-projects, pagination |
|
|
206
228
|
| **Section Management** | Basic | ✅ 4 Tools: Complete section operations |
|
|
207
229
|
| **Label Management** | Not Available | ✅ 5 Tools: Full CRUD operations, pagination |
|
|
208
230
|
| **Comment Management** | Not Available | ✅ 5 Tools: Full CRUD operations, attachments, pagination |
|
|
231
|
+
| **Completed Tasks History** | Not Available | ✅ 1 Tool: Sync API integration, date filtering, pagination |
|
|
209
232
|
| **API Parameter Handling** | Inconsistent | ✅ Proper parameter validation |
|
|
210
233
|
| **Response Formatting** | Basic | ✅ Enhanced readability, more details |
|
|
211
234
|
| **Build System** | Issues | ✅ Clean compilation |
|
package/dist/index.js
CHANGED
|
@@ -650,10 +650,43 @@ const DELETE_COMMENT_TOOL = {
|
|
|
650
650
|
required: ["commentId"]
|
|
651
651
|
}
|
|
652
652
|
};
|
|
653
|
+
const GET_COMPLETED_TASKS_TOOL = {
|
|
654
|
+
name: "todoist_get_completed_tasks",
|
|
655
|
+
description: "Get completed tasks from Todoist with flexible filtering and pagination support. Uses the Todoist Sync API to retrieve task completion history.",
|
|
656
|
+
inputSchema: {
|
|
657
|
+
type: "object",
|
|
658
|
+
properties: {
|
|
659
|
+
projectId: {
|
|
660
|
+
type: "string",
|
|
661
|
+
description: "Filter completed tasks by project ID (optional)"
|
|
662
|
+
},
|
|
663
|
+
since: {
|
|
664
|
+
type: "string",
|
|
665
|
+
description: "ISO 8601 datetime to get tasks completed after this date (e.g., '2024-01-01T00:00:00Z') (optional)"
|
|
666
|
+
},
|
|
667
|
+
until: {
|
|
668
|
+
type: "string",
|
|
669
|
+
description: "ISO 8601 datetime to get tasks completed before this date (e.g., '2024-12-31T23:59:59Z') (optional)"
|
|
670
|
+
},
|
|
671
|
+
limit: {
|
|
672
|
+
type: "number",
|
|
673
|
+
description: "Maximum number of completed tasks to return (default: 30, max: 200) (optional)"
|
|
674
|
+
},
|
|
675
|
+
offset: {
|
|
676
|
+
type: "number",
|
|
677
|
+
description: "Number of completed tasks to skip for pagination (default: 0) (optional)"
|
|
678
|
+
},
|
|
679
|
+
annotateNotes: {
|
|
680
|
+
type: "boolean",
|
|
681
|
+
description: "Include note details in the response (optional)"
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
};
|
|
653
686
|
// Server implementation
|
|
654
687
|
const server = new Server({
|
|
655
688
|
name: "todoist-mcp-server-enhanced",
|
|
656
|
-
version: "0.
|
|
689
|
+
version: "0.5.0",
|
|
657
690
|
}, {
|
|
658
691
|
capabilities: {
|
|
659
692
|
tools: {},
|
|
@@ -710,6 +743,58 @@ function formatComment(comment) {
|
|
|
710
743
|
}
|
|
711
744
|
return commentDetails;
|
|
712
745
|
}
|
|
746
|
+
// Helper function to call Todoist Sync API
|
|
747
|
+
async function callSyncAPI(endpoint, params) {
|
|
748
|
+
const url = `https://api.todoist.com/sync/v9/${endpoint}`;
|
|
749
|
+
// Build URL-encoded body
|
|
750
|
+
const formBody = Object.entries(params)
|
|
751
|
+
.filter(([_, value]) => value !== undefined && value !== null)
|
|
752
|
+
.map(([key, value]) => {
|
|
753
|
+
const encodedKey = encodeURIComponent(key);
|
|
754
|
+
const encodedValue = encodeURIComponent(typeof value === 'object' ? JSON.stringify(value) : String(value));
|
|
755
|
+
return `${encodedKey}=${encodedValue}`;
|
|
756
|
+
})
|
|
757
|
+
.join('&');
|
|
758
|
+
const response = await fetch(url, {
|
|
759
|
+
method: 'POST',
|
|
760
|
+
headers: {
|
|
761
|
+
'Authorization': `Bearer ${TODOIST_API_TOKEN}`,
|
|
762
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
763
|
+
},
|
|
764
|
+
body: formBody,
|
|
765
|
+
});
|
|
766
|
+
if (!response.ok) {
|
|
767
|
+
const errorText = await response.text();
|
|
768
|
+
throw new Error(`Sync API error (${response.status}): ${errorText}`);
|
|
769
|
+
}
|
|
770
|
+
return response.json();
|
|
771
|
+
}
|
|
772
|
+
// Helper function to format completed task output
|
|
773
|
+
function formatCompletedTask(task, projectName, sectionName) {
|
|
774
|
+
let taskDetails = `- Task ID: ${task.task_id}`;
|
|
775
|
+
taskDetails += `\n Completion ID: ${task.id}`;
|
|
776
|
+
taskDetails += `\n Content: ${task.content}`;
|
|
777
|
+
taskDetails += `\n Completed At: ${task.completed_at}`;
|
|
778
|
+
if (projectName) {
|
|
779
|
+
taskDetails += `\n Project: ${projectName} (${task.project_id})`;
|
|
780
|
+
}
|
|
781
|
+
else if (task.project_id) {
|
|
782
|
+
taskDetails += `\n Project ID: ${task.project_id}`;
|
|
783
|
+
}
|
|
784
|
+
if (sectionName) {
|
|
785
|
+
taskDetails += `\n Section: ${sectionName} (${task.section_id})`;
|
|
786
|
+
}
|
|
787
|
+
else if (task.section_id) {
|
|
788
|
+
taskDetails += `\n Section ID: ${task.section_id}`;
|
|
789
|
+
}
|
|
790
|
+
if (task.note_count > 0) {
|
|
791
|
+
taskDetails += `\n Notes: ${task.note_count}`;
|
|
792
|
+
}
|
|
793
|
+
if (task.meta_data) {
|
|
794
|
+
taskDetails += `\n Metadata: ${JSON.stringify(task.meta_data)}`;
|
|
795
|
+
}
|
|
796
|
+
return taskDetails;
|
|
797
|
+
}
|
|
713
798
|
// Type guards for arguments
|
|
714
799
|
function isCreateTaskArgs(args) {
|
|
715
800
|
return (typeof args === "object" &&
|
|
@@ -876,6 +961,32 @@ function isUpdateCommentArgs(args) {
|
|
|
876
961
|
typeof args.commentId === "string" &&
|
|
877
962
|
typeof args.content === "string");
|
|
878
963
|
}
|
|
964
|
+
function isGetCompletedTasksArgs(args) {
|
|
965
|
+
if (typeof args !== "object" || args === null) {
|
|
966
|
+
return false;
|
|
967
|
+
}
|
|
968
|
+
const typed = args;
|
|
969
|
+
// All parameters are optional, so check types only if present
|
|
970
|
+
if (typed.projectId !== undefined && typeof typed.projectId !== "string") {
|
|
971
|
+
return false;
|
|
972
|
+
}
|
|
973
|
+
if (typed.since !== undefined && typeof typed.since !== "string") {
|
|
974
|
+
return false;
|
|
975
|
+
}
|
|
976
|
+
if (typed.until !== undefined && typeof typed.until !== "string") {
|
|
977
|
+
return false;
|
|
978
|
+
}
|
|
979
|
+
if (typed.limit !== undefined && typeof typed.limit !== "number") {
|
|
980
|
+
return false;
|
|
981
|
+
}
|
|
982
|
+
if (typed.offset !== undefined && typeof typed.offset !== "number") {
|
|
983
|
+
return false;
|
|
984
|
+
}
|
|
985
|
+
if (typed.annotateNotes !== undefined && typeof typed.annotateNotes !== "boolean") {
|
|
986
|
+
return false;
|
|
987
|
+
}
|
|
988
|
+
return true;
|
|
989
|
+
}
|
|
879
990
|
// Tool handlers
|
|
880
991
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
881
992
|
tools: [
|
|
@@ -914,6 +1025,8 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
914
1025
|
GET_COMMENTS_TOOL,
|
|
915
1026
|
UPDATE_COMMENT_TOOL,
|
|
916
1027
|
DELETE_COMMENT_TOOL,
|
|
1028
|
+
// Completed tasks tool (Sync API)
|
|
1029
|
+
GET_COMPLETED_TASKS_TOOL,
|
|
917
1030
|
],
|
|
918
1031
|
}));
|
|
919
1032
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
@@ -1583,6 +1696,71 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1583
1696
|
return { content: [{ type: "text", text: `Error deleting comment: ${error.message}` }], isError: true };
|
|
1584
1697
|
}
|
|
1585
1698
|
}
|
|
1699
|
+
if (name === "todoist_get_completed_tasks") {
|
|
1700
|
+
if (!isGetCompletedTasksArgs(args)) {
|
|
1701
|
+
return {
|
|
1702
|
+
content: [{ type: "text", text: "Invalid arguments for get_completed_tasks" }],
|
|
1703
|
+
isError: true
|
|
1704
|
+
};
|
|
1705
|
+
}
|
|
1706
|
+
try {
|
|
1707
|
+
// Build Sync API parameters
|
|
1708
|
+
const syncParams = {};
|
|
1709
|
+
if (args.projectId)
|
|
1710
|
+
syncParams.project_id = args.projectId;
|
|
1711
|
+
if (args.since)
|
|
1712
|
+
syncParams.since = args.since;
|
|
1713
|
+
if (args.until)
|
|
1714
|
+
syncParams.until = args.until;
|
|
1715
|
+
if (args.limit !== undefined)
|
|
1716
|
+
syncParams.limit = Math.min(args.limit, 200);
|
|
1717
|
+
if (args.offset !== undefined)
|
|
1718
|
+
syncParams.offset = args.offset;
|
|
1719
|
+
if (args.annotateNotes)
|
|
1720
|
+
syncParams.annotate_notes = true;
|
|
1721
|
+
// Call Sync API
|
|
1722
|
+
const response = await callSyncAPI('completed/get_all', syncParams);
|
|
1723
|
+
if (!response.items || response.items.length === 0) {
|
|
1724
|
+
return {
|
|
1725
|
+
content: [{
|
|
1726
|
+
type: "text",
|
|
1727
|
+
text: "No completed tasks found matching the specified criteria"
|
|
1728
|
+
}],
|
|
1729
|
+
isError: false
|
|
1730
|
+
};
|
|
1731
|
+
}
|
|
1732
|
+
// Format completed tasks with project/section names if available
|
|
1733
|
+
const formattedTasks = response.items.map(task => {
|
|
1734
|
+
const projectName = response.projects?.[task.project_id]?.name;
|
|
1735
|
+
const sectionName = response.sections?.[task.section_id || '']?.name;
|
|
1736
|
+
return formatCompletedTask(task, projectName, sectionName);
|
|
1737
|
+
});
|
|
1738
|
+
const taskList = formattedTasks.join('\n\n');
|
|
1739
|
+
// Build pagination info
|
|
1740
|
+
let paginationInfo = '';
|
|
1741
|
+
if (response.has_more || (args.limit && response.items.length === args.limit)) {
|
|
1742
|
+
const nextOffset = (args.offset || 0) + response.items.length;
|
|
1743
|
+
paginationInfo = `\n\nPagination: Showing ${response.items.length} task(s). `;
|
|
1744
|
+
paginationInfo += `Use offset=${nextOffset} to retrieve the next page.`;
|
|
1745
|
+
}
|
|
1746
|
+
return {
|
|
1747
|
+
content: [{
|
|
1748
|
+
type: "text",
|
|
1749
|
+
text: `Completed Tasks (${response.items.length} found):\n\n${taskList}${paginationInfo}`
|
|
1750
|
+
}],
|
|
1751
|
+
isError: false
|
|
1752
|
+
};
|
|
1753
|
+
}
|
|
1754
|
+
catch (error) {
|
|
1755
|
+
return {
|
|
1756
|
+
content: [{
|
|
1757
|
+
type: "text",
|
|
1758
|
+
text: `Error getting completed tasks: ${error.message}`
|
|
1759
|
+
}],
|
|
1760
|
+
isError: true
|
|
1761
|
+
};
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1586
1764
|
return {
|
|
1587
1765
|
content: [{ type: "text", text: `Unknown tool: ${name}` }],
|
|
1588
1766
|
isError: true,
|
package/package.json
CHANGED