@doist/todoist-ai 4.15.1 → 4.16.1
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/dist/filter-helpers.d.ts +1 -1
- package/dist/index.d.ts +175 -175
- package/dist/index.js +61 -81
- package/dist/main.js +15 -23
- package/dist/mcp-helpers.d.ts +4 -4
- package/dist/mcp-server-6tm7Rhyz.js +2840 -0
- package/dist/todoist-tool.d.ts +2 -2
- package/dist/tool-helpers.d.ts +1 -1
- package/dist/tools/add-comments.d.ts +1 -1
- package/dist/tools/add-comments.d.ts.map +1 -1
- package/dist/tools/add-projects.d.ts +4 -4
- package/dist/tools/add-projects.d.ts.map +1 -1
- package/dist/tools/add-sections.d.ts +1 -1
- package/dist/tools/add-sections.d.ts.map +1 -1
- package/dist/tools/add-tasks.d.ts +4 -4
- package/dist/tools/add-tasks.d.ts.map +1 -1
- package/dist/tools/complete-tasks.d.ts +1 -1
- package/dist/tools/complete-tasks.d.ts.map +1 -1
- package/dist/tools/delete-object.d.ts +3 -3
- package/dist/tools/delete-object.d.ts.map +1 -1
- package/dist/tools/fetch.d.ts +1 -1
- package/dist/tools/find-activity.d.ts +5 -5
- package/dist/tools/find-activity.d.ts.map +1 -1
- package/dist/tools/find-comments.d.ts +2 -2
- package/dist/tools/find-comments.d.ts.map +1 -1
- package/dist/tools/find-completed-tasks.d.ts +3 -3
- package/dist/tools/find-completed-tasks.d.ts.map +1 -1
- package/dist/tools/find-project-collaborators.d.ts +2 -2
- package/dist/tools/find-projects.d.ts +1 -1
- package/dist/tools/find-projects.d.ts.map +1 -1
- package/dist/tools/find-sections.d.ts +1 -1
- package/dist/tools/find-sections.d.ts.map +1 -1
- package/dist/tools/find-tasks-by-date.d.ts +1 -1
- package/dist/tools/find-tasks-by-date.d.ts.map +1 -1
- package/dist/tools/find-tasks.d.ts +3 -3
- package/dist/tools/find-tasks.d.ts.map +1 -1
- package/dist/tools/get-overview.d.ts +1 -1
- package/dist/tools/manage-assignments.d.ts +1 -1
- package/dist/tools/search.d.ts +1 -1
- package/dist/tools/update-comments.d.ts +4 -4
- package/dist/tools/update-comments.d.ts.map +1 -1
- package/dist/tools/update-projects.d.ts +1 -1
- package/dist/tools/update-projects.d.ts.map +1 -1
- package/dist/tools/update-sections.d.ts +4 -4
- package/dist/tools/update-sections.d.ts.map +1 -1
- package/dist/tools/update-tasks.d.ts +7 -7
- package/dist/tools/update-tasks.d.ts.map +1 -1
- package/dist/tools/user-info.d.ts +1 -1
- package/dist/utils/assignment-validator.d.ts +2 -2
- package/dist/utils/response-builders.d.ts +1 -3
- package/dist/utils/response-builders.d.ts.map +1 -1
- package/dist/utils/test-helpers.d.ts +1 -1
- package/dist/utils/user-resolver.d.ts +1 -1
- package/package.json +11 -9
- package/dist/filter-helpers.js +0 -79
- package/dist/mcp-helpers.js +0 -71
- package/dist/mcp-server.js +0 -142
- package/dist/todoist-tool.js +0 -1
- package/dist/tool-helpers.js +0 -125
- package/dist/tool-helpers.test.d.ts +0 -2
- package/dist/tool-helpers.test.d.ts.map +0 -1
- package/dist/tool-helpers.test.js +0 -223
- package/dist/tools/__tests__/add-comments.test.d.ts +0 -2
- package/dist/tools/__tests__/add-comments.test.d.ts.map +0 -1
- package/dist/tools/__tests__/add-comments.test.js +0 -241
- package/dist/tools/__tests__/add-projects.test.d.ts +0 -2
- package/dist/tools/__tests__/add-projects.test.d.ts.map +0 -1
- package/dist/tools/__tests__/add-projects.test.js +0 -174
- package/dist/tools/__tests__/add-sections.test.d.ts +0 -2
- package/dist/tools/__tests__/add-sections.test.d.ts.map +0 -1
- package/dist/tools/__tests__/add-sections.test.js +0 -185
- package/dist/tools/__tests__/add-tasks.test.d.ts +0 -2
- package/dist/tools/__tests__/add-tasks.test.d.ts.map +0 -1
- package/dist/tools/__tests__/add-tasks.test.js +0 -533
- package/dist/tools/__tests__/assignment-integration.test.d.ts +0 -2
- package/dist/tools/__tests__/assignment-integration.test.d.ts.map +0 -1
- package/dist/tools/__tests__/assignment-integration.test.js +0 -428
- package/dist/tools/__tests__/complete-tasks.test.d.ts +0 -2
- package/dist/tools/__tests__/complete-tasks.test.d.ts.map +0 -1
- package/dist/tools/__tests__/complete-tasks.test.js +0 -206
- package/dist/tools/__tests__/delete-object.test.d.ts +0 -2
- package/dist/tools/__tests__/delete-object.test.d.ts.map +0 -1
- package/dist/tools/__tests__/delete-object.test.js +0 -110
- package/dist/tools/__tests__/fetch.test.d.ts +0 -2
- package/dist/tools/__tests__/fetch.test.d.ts.map +0 -1
- package/dist/tools/__tests__/fetch.test.js +0 -279
- package/dist/tools/__tests__/find-activity.test.d.ts +0 -2
- package/dist/tools/__tests__/find-activity.test.d.ts.map +0 -1
- package/dist/tools/__tests__/find-activity.test.js +0 -229
- package/dist/tools/__tests__/find-comments.test.d.ts +0 -2
- package/dist/tools/__tests__/find-comments.test.d.ts.map +0 -1
- package/dist/tools/__tests__/find-comments.test.js +0 -236
- package/dist/tools/__tests__/find-completed-tasks.test.d.ts +0 -2
- package/dist/tools/__tests__/find-completed-tasks.test.d.ts.map +0 -1
- package/dist/tools/__tests__/find-completed-tasks.test.js +0 -324
- package/dist/tools/__tests__/find-projects.test.d.ts +0 -2
- package/dist/tools/__tests__/find-projects.test.d.ts.map +0 -1
- package/dist/tools/__tests__/find-projects.test.js +0 -154
- package/dist/tools/__tests__/find-sections.test.d.ts +0 -2
- package/dist/tools/__tests__/find-sections.test.d.ts.map +0 -1
- package/dist/tools/__tests__/find-sections.test.js +0 -245
- package/dist/tools/__tests__/find-tasks-by-date.test.d.ts +0 -2
- package/dist/tools/__tests__/find-tasks-by-date.test.d.ts.map +0 -1
- package/dist/tools/__tests__/find-tasks-by-date.test.js +0 -528
- package/dist/tools/__tests__/find-tasks.test.d.ts +0 -2
- package/dist/tools/__tests__/find-tasks.test.d.ts.map +0 -1
- package/dist/tools/__tests__/find-tasks.test.js +0 -771
- package/dist/tools/__tests__/get-overview.test.d.ts +0 -2
- package/dist/tools/__tests__/get-overview.test.d.ts.map +0 -1
- package/dist/tools/__tests__/get-overview.test.js +0 -225
- package/dist/tools/__tests__/search.test.d.ts +0 -2
- package/dist/tools/__tests__/search.test.d.ts.map +0 -1
- package/dist/tools/__tests__/search.test.js +0 -206
- package/dist/tools/__tests__/update-comments.test.d.ts +0 -2
- package/dist/tools/__tests__/update-comments.test.d.ts.map +0 -1
- package/dist/tools/__tests__/update-comments.test.js +0 -294
- package/dist/tools/__tests__/update-projects.test.d.ts +0 -2
- package/dist/tools/__tests__/update-projects.test.d.ts.map +0 -1
- package/dist/tools/__tests__/update-projects.test.js +0 -217
- package/dist/tools/__tests__/update-sections.test.d.ts +0 -2
- package/dist/tools/__tests__/update-sections.test.d.ts.map +0 -1
- package/dist/tools/__tests__/update-sections.test.js +0 -169
- package/dist/tools/__tests__/update-tasks.test.d.ts +0 -2
- package/dist/tools/__tests__/update-tasks.test.d.ts.map +0 -1
- package/dist/tools/__tests__/update-tasks.test.js +0 -788
- package/dist/tools/__tests__/user-info.test.d.ts +0 -2
- package/dist/tools/__tests__/user-info.test.d.ts.map +0 -1
- package/dist/tools/__tests__/user-info.test.js +0 -139
- package/dist/tools/add-comments.js +0 -79
- package/dist/tools/add-projects.js +0 -63
- package/dist/tools/add-sections.js +0 -61
- package/dist/tools/add-tasks.js +0 -160
- package/dist/tools/complete-tasks.js +0 -68
- package/dist/tools/delete-object.js +0 -79
- package/dist/tools/fetch.js +0 -102
- package/dist/tools/find-activity.js +0 -221
- package/dist/tools/find-comments.js +0 -143
- package/dist/tools/find-completed-tasks.js +0 -161
- package/dist/tools/find-project-collaborators.js +0 -151
- package/dist/tools/find-projects.js +0 -101
- package/dist/tools/find-sections.js +0 -96
- package/dist/tools/find-tasks-by-date.js +0 -198
- package/dist/tools/find-tasks.js +0 -329
- package/dist/tools/get-overview.js +0 -249
- package/dist/tools/manage-assignments.js +0 -337
- package/dist/tools/search.js +0 -65
- package/dist/tools/update-comments.js +0 -82
- package/dist/tools/update-projects.js +0 -84
- package/dist/tools/update-sections.js +0 -70
- package/dist/tools/update-tasks.js +0 -170
- package/dist/tools/user-info.js +0 -142
- package/dist/utils/assignment-validator.js +0 -253
- package/dist/utils/constants.js +0 -45
- package/dist/utils/duration-parser.js +0 -96
- package/dist/utils/duration-parser.test.d.ts +0 -2
- package/dist/utils/duration-parser.test.d.ts.map +0 -1
- package/dist/utils/duration-parser.test.js +0 -147
- package/dist/utils/labels.js +0 -18
- package/dist/utils/priorities.js +0 -20
- package/dist/utils/response-builders.js +0 -210
- package/dist/utils/sanitize-data.js +0 -37
- package/dist/utils/sanitize-data.test.d.ts +0 -2
- package/dist/utils/sanitize-data.test.d.ts.map +0 -1
- package/dist/utils/sanitize-data.test.js +0 -93
- package/dist/utils/test-helpers.js +0 -237
- package/dist/utils/tool-names.js +0 -40
- package/dist/utils/user-resolver.js +0 -179
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@doist/todoist-ai",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.16.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -27,11 +27,13 @@
|
|
|
27
27
|
"tools"
|
|
28
28
|
],
|
|
29
29
|
"scripts": {
|
|
30
|
-
"test": "
|
|
31
|
-
"
|
|
30
|
+
"test": "vitest run",
|
|
31
|
+
"test:watch": "vitest",
|
|
32
|
+
"test:coverage": "vitest run --coverage",
|
|
33
|
+
"build": "vite build",
|
|
32
34
|
"postbuild": "chmod +x dist/main.js",
|
|
33
35
|
"start": "npm run build && npx @modelcontextprotocol/inspector node dist/main.js",
|
|
34
|
-
"dev": "concurrently \"
|
|
36
|
+
"dev": "concurrently \"vite build --watch\" \"npx @modelcontextprotocol/inspector npx nodemon --quiet --watch dist --ext js --exec node dist/main.js\"",
|
|
35
37
|
"setup": "cp .env.example .env && npm install && npm run build",
|
|
36
38
|
"test:executable": "npm run build && node scripts/test-executable.cjs",
|
|
37
39
|
"type-check": "npx tsc --noEmit",
|
|
@@ -46,28 +48,28 @@
|
|
|
46
48
|
"prepare": "husky"
|
|
47
49
|
},
|
|
48
50
|
"dependencies": {
|
|
51
|
+
"@doist/todoist-api-typescript": "6.0.0",
|
|
49
52
|
"@modelcontextprotocol/sdk": "^1.11.1",
|
|
50
53
|
"date-fns": "^4.1.0",
|
|
51
|
-
"@doist/todoist-api-typescript": "5.9.0",
|
|
52
54
|
"dotenv": "^17.0.0",
|
|
53
55
|
"zod": "^3.25.7"
|
|
54
56
|
},
|
|
55
57
|
"devDependencies": {
|
|
56
58
|
"@biomejs/biome": "2.3.2",
|
|
57
59
|
"@types/express": "^5.0.2",
|
|
58
|
-
"@types/jest": "30.0.0",
|
|
59
60
|
"@types/morgan": "^1.9.9",
|
|
60
61
|
"@types/node": "^22.15.17",
|
|
61
62
|
"concurrently": "^9.0.0",
|
|
62
63
|
"express": "^5.0.0",
|
|
63
64
|
"husky": "^9.1.7",
|
|
64
|
-
"jest": "30.2.0",
|
|
65
65
|
"lint-staged": "^16.0.0",
|
|
66
66
|
"morgan": "^1.10.0",
|
|
67
67
|
"nodemon": "^3.1.10",
|
|
68
68
|
"rimraf": "^6.0.1",
|
|
69
|
-
"
|
|
70
|
-
"
|
|
69
|
+
"typescript": "^5.8.3",
|
|
70
|
+
"vite": "7.1.10",
|
|
71
|
+
"vite-plugin-dts": "4.5.4",
|
|
72
|
+
"vitest": "3.2.4"
|
|
71
73
|
},
|
|
72
74
|
"lint-staged": {
|
|
73
75
|
"*": [
|
package/dist/filter-helpers.js
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import { resolveUserNameToId } from './utils/user-resolver.js';
|
|
2
|
-
export const RESPONSIBLE_USER_FILTERING = ['assigned', 'unassignedOrMe', 'all'];
|
|
3
|
-
/**
|
|
4
|
-
* Resolves a responsible user name/email to user ID and email.
|
|
5
|
-
* @param client - Todoist API client
|
|
6
|
-
* @param responsibleUser - User identifier (can be user ID, name, or email)
|
|
7
|
-
* @returns Object with userId and email, or undefined if not provided
|
|
8
|
-
* @throws Error if user cannot be found
|
|
9
|
-
*/
|
|
10
|
-
export async function resolveResponsibleUser(client, responsibleUser) {
|
|
11
|
-
if (!responsibleUser) {
|
|
12
|
-
return undefined;
|
|
13
|
-
}
|
|
14
|
-
const resolved = await resolveUserNameToId(client, responsibleUser);
|
|
15
|
-
if (!resolved) {
|
|
16
|
-
throw new Error(`Could not find user: "${responsibleUser}". Make sure the user is a collaborator on a shared project.`);
|
|
17
|
-
}
|
|
18
|
-
return { userId: resolved.userId, email: resolved.email };
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Appends a filter component to a query string with proper ' & ' separator.
|
|
22
|
-
* @param query - The existing query string
|
|
23
|
-
* @param filterComponent - The filter component to append
|
|
24
|
-
* @returns The updated query string
|
|
25
|
-
*/
|
|
26
|
-
export function appendToQuery(query, filterComponent) {
|
|
27
|
-
if (filterComponent.length === 0) {
|
|
28
|
-
return query;
|
|
29
|
-
}
|
|
30
|
-
if (query.length === 0) {
|
|
31
|
-
return filterComponent;
|
|
32
|
-
}
|
|
33
|
-
return `${query} & ${filterComponent}`;
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Builds a query filter string for responsible user filtering that can be appended to a Todoist filter query.
|
|
37
|
-
* @param resolvedAssigneeId - The resolved assignee ID (if provided)
|
|
38
|
-
* @param assigneeEmail - The assignee email (if provided)
|
|
39
|
-
* @param responsibleUserFiltering - The filtering mode ('assigned', 'unassignedOrMe', 'all')
|
|
40
|
-
* @returns Query filter string (e.g., "assigned to: email@example.com" or "!assigned to: others")
|
|
41
|
-
*/
|
|
42
|
-
export function buildResponsibleUserQueryFilter({ resolvedAssigneeId, assigneeEmail, responsibleUserFiltering = 'unassignedOrMe', }) {
|
|
43
|
-
if (resolvedAssigneeId && assigneeEmail) {
|
|
44
|
-
// If specific user is provided, filter by that user
|
|
45
|
-
return `assigned to: ${assigneeEmail}`;
|
|
46
|
-
}
|
|
47
|
-
// Otherwise use the filtering mode
|
|
48
|
-
if (responsibleUserFiltering === 'unassignedOrMe') {
|
|
49
|
-
// Exclude tasks assigned to others (keeps unassigned + assigned to me)
|
|
50
|
-
return '!assigned to: others';
|
|
51
|
-
}
|
|
52
|
-
if (responsibleUserFiltering === 'assigned') {
|
|
53
|
-
// Only tasks assigned to others
|
|
54
|
-
return 'assigned to: others';
|
|
55
|
-
}
|
|
56
|
-
// For 'all', don't add any assignment filter
|
|
57
|
-
return '';
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Filters tasks based on responsible user logic:
|
|
61
|
-
* - If resolvedAssigneeId is provided: returns only tasks assigned to that user
|
|
62
|
-
* - If no resolvedAssigneeId: returns only unassigned tasks or tasks assigned to current user
|
|
63
|
-
* @param tasks - Array of tasks to filter (must have responsibleUid property)
|
|
64
|
-
* @param resolvedAssigneeId - The resolved assignee ID to filter by (optional)
|
|
65
|
-
* @param currentUserId - The current authenticated user's ID
|
|
66
|
-
* @returns Filtered array of tasks
|
|
67
|
-
*/
|
|
68
|
-
export function filterTasksByResponsibleUser({ tasks, resolvedAssigneeId, currentUserId, responsibleUserFiltering = 'unassignedOrMe', }) {
|
|
69
|
-
if (resolvedAssigneeId) {
|
|
70
|
-
// If responsibleUser provided, only return tasks assigned to that user
|
|
71
|
-
return tasks.filter((task) => task.responsibleUid === resolvedAssigneeId);
|
|
72
|
-
}
|
|
73
|
-
else {
|
|
74
|
-
// If no responsibleUser, only return unassigned tasks or tasks assigned to current user
|
|
75
|
-
return responsibleUserFiltering === 'unassignedOrMe'
|
|
76
|
-
? tasks.filter((task) => !task.responsibleUid || task.responsibleUid === currentUserId)
|
|
77
|
-
: tasks;
|
|
78
|
-
}
|
|
79
|
-
}
|
package/dist/mcp-helpers.js
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import { removeNullFields } from './utils/sanitize-data.js';
|
|
2
|
-
/**
|
|
3
|
-
* Wether to return the structured content directly, vs. in the `content` part of the output.
|
|
4
|
-
*
|
|
5
|
-
* The `structuredContent` part of the output is relatively new in the spec, and it's not yet
|
|
6
|
-
* supported by all clients. This flag controls wether we return the structured content using this
|
|
7
|
-
* new feature of the MCP protocol or not.
|
|
8
|
-
*
|
|
9
|
-
* If `false`, the `structuredContent` will be returned as stringified JSON in one of the `content`
|
|
10
|
-
* parts.
|
|
11
|
-
*
|
|
12
|
-
* Eventually we should be able to remove this, and change the code to always work with the
|
|
13
|
-
* structured content returned directly, once most or all MCP clients support it.
|
|
14
|
-
*/
|
|
15
|
-
const USE_STRUCTURED_CONTENT = process.env.USE_STRUCTURED_CONTENT === 'true' || process.env.NODE_ENV === 'test';
|
|
16
|
-
/**
|
|
17
|
-
* Get the output payload for a tool, in the correct format expected by MCP client apps.
|
|
18
|
-
*
|
|
19
|
-
* @param textContent - The text content to return.
|
|
20
|
-
* @param structuredContent - The structured content to return.
|
|
21
|
-
* @returns The output payload.
|
|
22
|
-
* @see USE_STRUCTURED_CONTENT - Wether to use the structured content feature of the MCP protocol.
|
|
23
|
-
*/
|
|
24
|
-
function getToolOutput({ textContent, structuredContent, }) {
|
|
25
|
-
// Remove null fields from structured content before returning
|
|
26
|
-
const sanitizedContent = removeNullFields(structuredContent);
|
|
27
|
-
if (USE_STRUCTURED_CONTENT) {
|
|
28
|
-
return {
|
|
29
|
-
content: [{ type: 'text', text: textContent }],
|
|
30
|
-
structuredContent: sanitizedContent,
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
const json = JSON.stringify(sanitizedContent);
|
|
34
|
-
return {
|
|
35
|
-
content: [
|
|
36
|
-
{ type: 'text', text: textContent },
|
|
37
|
-
{ type: 'text', mimeType: 'application/json', text: json },
|
|
38
|
-
],
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
function getErrorOutput(error) {
|
|
42
|
-
return {
|
|
43
|
-
content: [{ type: 'text', text: error }],
|
|
44
|
-
isError: true,
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Register a Todoist tool in an MCP server.
|
|
49
|
-
* @param tool - The tool to register.
|
|
50
|
-
* @param server - The server to register the tool on.
|
|
51
|
-
* @param client - The Todoist API client to use to execute the tool.
|
|
52
|
-
*/
|
|
53
|
-
function registerTool(tool, server, client) {
|
|
54
|
-
// @ts-expect-error I give up
|
|
55
|
-
const cb = async (args, _context) => {
|
|
56
|
-
try {
|
|
57
|
-
const result = await tool.execute(args, client);
|
|
58
|
-
return result;
|
|
59
|
-
}
|
|
60
|
-
catch (error) {
|
|
61
|
-
console.error(`Error executing tool ${tool.name}:`, {
|
|
62
|
-
args,
|
|
63
|
-
error,
|
|
64
|
-
});
|
|
65
|
-
const message = error instanceof Error ? error.message : 'An unknown error occurred';
|
|
66
|
-
return getErrorOutput(message);
|
|
67
|
-
}
|
|
68
|
-
};
|
|
69
|
-
server.tool(tool.name, tool.description, tool.parameters, cb);
|
|
70
|
-
}
|
|
71
|
-
export { registerTool, getErrorOutput, getToolOutput };
|
package/dist/mcp-server.js
DELETED
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
import { TodoistApi } from '@doist/todoist-api-typescript';
|
|
2
|
-
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
|
-
import { registerTool } from './mcp-helpers.js';
|
|
4
|
-
import { addComments } from './tools/add-comments.js';
|
|
5
|
-
import { addProjects } from './tools/add-projects.js';
|
|
6
|
-
import { addSections } from './tools/add-sections.js';
|
|
7
|
-
import { addTasks } from './tools/add-tasks.js';
|
|
8
|
-
import { completeTasks } from './tools/complete-tasks.js';
|
|
9
|
-
import { deleteObject } from './tools/delete-object.js';
|
|
10
|
-
import { fetch } from './tools/fetch.js';
|
|
11
|
-
import { findActivity } from './tools/find-activity.js';
|
|
12
|
-
import { findComments } from './tools/find-comments.js';
|
|
13
|
-
import { findCompletedTasks } from './tools/find-completed-tasks.js';
|
|
14
|
-
import { findProjectCollaborators } from './tools/find-project-collaborators.js';
|
|
15
|
-
import { findProjects } from './tools/find-projects.js';
|
|
16
|
-
import { findSections } from './tools/find-sections.js';
|
|
17
|
-
import { findTasks } from './tools/find-tasks.js';
|
|
18
|
-
import { findTasksByDate } from './tools/find-tasks-by-date.js';
|
|
19
|
-
import { getOverview } from './tools/get-overview.js';
|
|
20
|
-
import { manageAssignments } from './tools/manage-assignments.js';
|
|
21
|
-
import { search } from './tools/search.js';
|
|
22
|
-
import { updateComments } from './tools/update-comments.js';
|
|
23
|
-
import { updateProjects } from './tools/update-projects.js';
|
|
24
|
-
import { updateSections } from './tools/update-sections.js';
|
|
25
|
-
import { updateTasks } from './tools/update-tasks.js';
|
|
26
|
-
import { userInfo } from './tools/user-info.js';
|
|
27
|
-
const instructions = `
|
|
28
|
-
## Todoist Task and Project Management Tools
|
|
29
|
-
|
|
30
|
-
You have access to comprehensive Todoist management tools for personal productivity and team collaboration. Use these tools to help users manage tasks, projects, sections, comments, and assignments effectively.
|
|
31
|
-
|
|
32
|
-
### Core Capabilities:
|
|
33
|
-
- Create, update, complete, and search tasks with rich metadata (priorities, due dates, durations, assignments)
|
|
34
|
-
- Manage projects and sections with flexible organization
|
|
35
|
-
- Handle comments and collaboration features
|
|
36
|
-
- Bulk assignment operations for team workflows
|
|
37
|
-
- Get overviews and insights about workload and progress
|
|
38
|
-
|
|
39
|
-
### Tool Usage Guidelines:
|
|
40
|
-
|
|
41
|
-
**Task Management:**
|
|
42
|
-
- **add-tasks**: Create tasks with content, description, priority (p1=highest, p2=high, p3=medium, p4=lowest/default), dueString (natural language like "tomorrow", "next Friday", "2024-12-25"), deadlineDate (ISO 8601 format like "2025-12-31" for immovable constraints), duration (formats like "2h", "90m", "2h30m"), and assignments to project collaborators
|
|
43
|
-
- **update-tasks**: Modify existing tasks - get task IDs from search results first, only include fields that need changes. Supports deadlineDate (ISO 8601 format like "2025-12-31") updates and removals (use "remove" to clear)
|
|
44
|
-
- **complete-tasks**: Mark tasks as done using task IDs
|
|
45
|
-
- **find-tasks**: Search by text, project/section/parent container, responsible user, or labels. Requires at least one search parameter
|
|
46
|
-
- **find-tasks-by-date**: Get tasks by date range (startDate: YYYY-MM-DD or 'today' which includes overdue tasks) or specific day counts
|
|
47
|
-
- **find-completed-tasks**: View completed tasks by completion date or original due date (returns all collaborators unless filtered)
|
|
48
|
-
|
|
49
|
-
**Project & Organization:**
|
|
50
|
-
- **add-projects/update-projects/find-projects**: Manage project lifecycle with names, favorites, and view styles (list/board/calendar)
|
|
51
|
-
- **add-sections/update-sections/find-sections**: Organize tasks within projects using sections
|
|
52
|
-
- **get-overview**: Get comprehensive Markdown overview of entire account or specific project with task hierarchies
|
|
53
|
-
|
|
54
|
-
**Collaboration & Comments:**
|
|
55
|
-
- **add-comments/update-comments/find-comments**: Manage task and project discussions
|
|
56
|
-
- **find-project-collaborators**: Find team members by name or email for assignments
|
|
57
|
-
- **manage-assignments**: Bulk assign/unassign/reassign up to 50 tasks with atomic operations and dry-run validation
|
|
58
|
-
|
|
59
|
-
**Activity & Audit:**
|
|
60
|
-
- **find-activity**: Retrieve recent activity logs to monitor and audit changes. Shows events from all users by default; use initiatorId to filter by specific user. Filter by object type (task/project/comment), event type (added/updated/deleted/completed/uncompleted/archived/unarchived/shared/left), and specific objects (objectId, projectId, taskId). Useful for tracking who did what and when. Note: Date-based filtering is not supported.
|
|
61
|
-
|
|
62
|
-
**General Operations:**
|
|
63
|
-
- **delete-object**: Remove projects, sections, tasks, or comments by type and ID
|
|
64
|
-
- **user-info**: Get user details including timezone, goals, and plan information
|
|
65
|
-
|
|
66
|
-
### Best Practices:
|
|
67
|
-
|
|
68
|
-
1. **Task Creation**: Write clear, actionable task titles. Use natural language for due dates ("tomorrow", "next Monday"). Set appropriate priorities and include detailed descriptions when needed.
|
|
69
|
-
|
|
70
|
-
2. **Search Strategy**: Use specific search queries combining multiple filters for precise results. When searching for tasks, start with broader queries and narrow down as needed.
|
|
71
|
-
|
|
72
|
-
3. **Assignments**: Always validate project collaborators exist before assigning tasks. Use find-project-collaborators to verify user access.
|
|
73
|
-
|
|
74
|
-
4. **Bulk Operations**: When working with multiple items, prefer bulk tools (complete-tasks, manage-assignments) over individual operations for better performance.
|
|
75
|
-
|
|
76
|
-
5. **Date Handling**: All dates respect user timezone settings. Use 'today' keyword for dynamic date filtering (includes overdue tasks).
|
|
77
|
-
|
|
78
|
-
6. **Labels**: Use label filtering with AND/OR operators for advanced task organization. Most search tools support labels parameter.
|
|
79
|
-
|
|
80
|
-
7. **Pagination**: Large result sets use cursor-based pagination. Use limit parameter to control result size (default varies by tool).
|
|
81
|
-
|
|
82
|
-
8. **Error Handling**: All tools provide detailed error messages and next-step suggestions. Pay attention to validation feedback for corrective actions.
|
|
83
|
-
|
|
84
|
-
### Common Workflows:
|
|
85
|
-
|
|
86
|
-
- **Daily Planning**: Use find-tasks-by-date with 'today' and get-overview for project status
|
|
87
|
-
- **Team Assignment**: find-project-collaborators → add-tasks with responsibleUser → manage-assignments for bulk changes
|
|
88
|
-
- **Task Search**: find-tasks with multiple filters → update-tasks or complete-tasks based on results
|
|
89
|
-
- **Project Organization**: add-projects → add-sections → add-tasks with projectId and sectionId
|
|
90
|
-
- **Progress Reviews**: find-completed-tasks with date ranges → get-overview for project summaries
|
|
91
|
-
- **Activity Auditing**: find-activity with event/object filters to track changes, monitor team activity, or investigate specific actions
|
|
92
|
-
|
|
93
|
-
Always provide clear, actionable task titles and descriptions. Use the overview tools to give users context about their workload and project status.
|
|
94
|
-
`;
|
|
95
|
-
/**
|
|
96
|
-
* Create the MCP server.
|
|
97
|
-
* @param todoistApiKey - The API key for the todoist account.
|
|
98
|
-
* @param baseUrl - The base URL for the todoist API.
|
|
99
|
-
* @returns the MCP server.
|
|
100
|
-
*/
|
|
101
|
-
function getMcpServer({ todoistApiKey, baseUrl }) {
|
|
102
|
-
const server = new McpServer({ name: 'todoist-mcp-server', version: '0.1.0' }, {
|
|
103
|
-
capabilities: {
|
|
104
|
-
tools: { listChanged: true },
|
|
105
|
-
},
|
|
106
|
-
instructions,
|
|
107
|
-
});
|
|
108
|
-
const todoist = new TodoistApi(todoistApiKey, baseUrl);
|
|
109
|
-
// Task management tools
|
|
110
|
-
registerTool(addTasks, server, todoist);
|
|
111
|
-
registerTool(completeTasks, server, todoist);
|
|
112
|
-
registerTool(updateTasks, server, todoist);
|
|
113
|
-
registerTool(findTasks, server, todoist);
|
|
114
|
-
registerTool(findTasksByDate, server, todoist);
|
|
115
|
-
registerTool(findCompletedTasks, server, todoist);
|
|
116
|
-
// Project management tools
|
|
117
|
-
registerTool(addProjects, server, todoist);
|
|
118
|
-
registerTool(updateProjects, server, todoist);
|
|
119
|
-
registerTool(findProjects, server, todoist);
|
|
120
|
-
// Section management tools
|
|
121
|
-
registerTool(addSections, server, todoist);
|
|
122
|
-
registerTool(updateSections, server, todoist);
|
|
123
|
-
registerTool(findSections, server, todoist);
|
|
124
|
-
// Comment management tools
|
|
125
|
-
registerTool(addComments, server, todoist);
|
|
126
|
-
registerTool(findComments, server, todoist);
|
|
127
|
-
registerTool(updateComments, server, todoist);
|
|
128
|
-
// Activity and audit tools
|
|
129
|
-
registerTool(findActivity, server, todoist);
|
|
130
|
-
// General tools
|
|
131
|
-
registerTool(getOverview, server, todoist);
|
|
132
|
-
registerTool(deleteObject, server, todoist);
|
|
133
|
-
registerTool(userInfo, server, todoist);
|
|
134
|
-
// Assignment and collaboration tools
|
|
135
|
-
registerTool(findProjectCollaborators, server, todoist);
|
|
136
|
-
registerTool(manageAssignments, server, todoist);
|
|
137
|
-
// OpenAI MCP tools
|
|
138
|
-
registerTool(search, server, todoist);
|
|
139
|
-
registerTool(fetch, server, todoist);
|
|
140
|
-
return server;
|
|
141
|
-
}
|
|
142
|
-
export { getMcpServer };
|
package/dist/todoist-tool.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/tool-helpers.js
DELETED
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
import z from 'zod';
|
|
2
|
-
import { formatDuration } from './utils/duration-parser.js';
|
|
3
|
-
// Re-export filter helpers for backward compatibility
|
|
4
|
-
export { appendToQuery, buildResponsibleUserQueryFilter, filterTasksByResponsibleUser, RESPONSIBLE_USER_FILTERING, resolveResponsibleUser, } from './filter-helpers.js';
|
|
5
|
-
export function isPersonalProject(project) {
|
|
6
|
-
return 'inboxProject' in project;
|
|
7
|
-
}
|
|
8
|
-
export function isWorkspaceProject(project) {
|
|
9
|
-
return 'accessLevel' in project;
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
* Creates a MoveTaskArgs object from move parameters, validating that exactly one is provided.
|
|
13
|
-
* @param taskId - The task ID (used for error messages)
|
|
14
|
-
* @param projectId - Optional project ID to move to
|
|
15
|
-
* @param sectionId - Optional section ID to move to
|
|
16
|
-
* @param parentId - Optional parent ID to move to
|
|
17
|
-
* @returns MoveTaskArgs object with exactly one destination
|
|
18
|
-
* @throws Error if multiple move parameters are provided or none are provided
|
|
19
|
-
*/
|
|
20
|
-
export function createMoveTaskArgs(taskId, projectId, sectionId, parentId) {
|
|
21
|
-
// Validate that only one move parameter is provided (RequireExactlyOne constraint)
|
|
22
|
-
const moveParams = [projectId, sectionId, parentId].filter(Boolean);
|
|
23
|
-
if (moveParams.length > 1) {
|
|
24
|
-
throw new Error(`Task ${taskId}: Only one of projectId, sectionId, or parentId can be specified at a time. The Todoist API requires exactly one destination for move operations.`);
|
|
25
|
-
}
|
|
26
|
-
if (moveParams.length === 0) {
|
|
27
|
-
throw new Error(`Task ${taskId}: At least one of projectId, sectionId, or parentId must be provided for move operations.`);
|
|
28
|
-
}
|
|
29
|
-
// Build moveArgs with the single defined value
|
|
30
|
-
if (projectId)
|
|
31
|
-
return { projectId };
|
|
32
|
-
if (sectionId)
|
|
33
|
-
return { sectionId };
|
|
34
|
-
if (parentId)
|
|
35
|
-
return { parentId };
|
|
36
|
-
// This should never be reached due to the validation above
|
|
37
|
-
throw new Error('Unexpected error: No valid move parameter found');
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Map a single Todoist task to a more structured format, for LLM consumption.
|
|
41
|
-
* @param task - The task to map.
|
|
42
|
-
* @returns The mapped task.
|
|
43
|
-
*/
|
|
44
|
-
function mapTask(task) {
|
|
45
|
-
return {
|
|
46
|
-
id: task.id,
|
|
47
|
-
content: task.content,
|
|
48
|
-
description: task.description,
|
|
49
|
-
dueDate: task.due?.date,
|
|
50
|
-
recurring: task.due?.isRecurring && task.due.string ? task.due.string : false,
|
|
51
|
-
deadlineDate: task.deadline?.date,
|
|
52
|
-
priority: task.priority,
|
|
53
|
-
projectId: task.projectId,
|
|
54
|
-
sectionId: task.sectionId,
|
|
55
|
-
parentId: task.parentId,
|
|
56
|
-
labels: task.labels,
|
|
57
|
-
duration: task.duration ? formatDuration(task.duration.amount) : null,
|
|
58
|
-
responsibleUid: task.responsibleUid,
|
|
59
|
-
assignedByUid: task.assignedByUid,
|
|
60
|
-
checked: task.checked,
|
|
61
|
-
completedAt: task.completedAt,
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Map a single Todoist project to a more structured format, for LLM consumption.
|
|
66
|
-
* @param project - The project to map.
|
|
67
|
-
* @returns The mapped project.
|
|
68
|
-
*/
|
|
69
|
-
function mapProject(project) {
|
|
70
|
-
return {
|
|
71
|
-
id: project.id,
|
|
72
|
-
name: project.name,
|
|
73
|
-
color: project.color,
|
|
74
|
-
isFavorite: project.isFavorite,
|
|
75
|
-
isShared: project.isShared,
|
|
76
|
-
parentId: isPersonalProject(project) ? (project.parentId ?? null) : null,
|
|
77
|
-
inboxProject: isPersonalProject(project) ? (project.inboxProject ?? false) : false,
|
|
78
|
-
viewStyle: project.viewStyle,
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Map a single Todoist activity event to a more structured format, for LLM consumption.
|
|
83
|
-
* @param event - The activity event to map.
|
|
84
|
-
* @returns The mapped activity event.
|
|
85
|
-
*/
|
|
86
|
-
function mapActivityEvent(event) {
|
|
87
|
-
return {
|
|
88
|
-
id: event.id,
|
|
89
|
-
objectType: event.objectType,
|
|
90
|
-
objectId: event.objectId,
|
|
91
|
-
eventType: event.eventType,
|
|
92
|
-
eventDate: event.eventDate,
|
|
93
|
-
parentProjectId: event.parentProjectId,
|
|
94
|
-
parentItemId: event.parentItemId,
|
|
95
|
-
initiatorId: event.initiatorId,
|
|
96
|
-
extraData: event.extraData,
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
const ErrorSchema = z.object({
|
|
100
|
-
httpStatusCode: z.number(),
|
|
101
|
-
responseData: z.object({
|
|
102
|
-
error: z.string(),
|
|
103
|
-
errorCode: z.number(),
|
|
104
|
-
errorTag: z.string(),
|
|
105
|
-
}),
|
|
106
|
-
});
|
|
107
|
-
async function getTasksByFilter({ client, query, limit, cursor, }) {
|
|
108
|
-
try {
|
|
109
|
-
const { results, nextCursor } = await client.getTasksByFilter({ query, cursor, limit });
|
|
110
|
-
const tasks = results.map(mapTask);
|
|
111
|
-
return { tasks, nextCursor };
|
|
112
|
-
}
|
|
113
|
-
catch (error) {
|
|
114
|
-
const parsedError = ErrorSchema.safeParse(error);
|
|
115
|
-
if (!parsedError.success) {
|
|
116
|
-
throw error;
|
|
117
|
-
}
|
|
118
|
-
const { responseData } = parsedError.data;
|
|
119
|
-
if (responseData.errorTag === 'INVALID_SEARCH_QUERY') {
|
|
120
|
-
throw new Error(`Invalid filter query: ${query}`);
|
|
121
|
-
}
|
|
122
|
-
throw new Error(`${responseData.error} (tag: ${responseData.errorTag}, code: ${responseData.errorCode})`);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
export { getTasksByFilter, mapActivityEvent, mapProject, mapTask };
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tool-helpers.test.d.ts","sourceRoot":"","sources":["../src/tool-helpers.test.ts"],"names":[],"mappings":""}
|