@doist/todoist-ai 4.13.4 → 4.14.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/index.d.ts +68 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +8 -1
- package/dist/tool-helpers.d.ts +18 -2
- package/dist/tool-helpers.d.ts.map +1 -1
- package/dist/tool-helpers.js +19 -1
- package/dist/tools/__tests__/find-activity.test.d.ts +2 -0
- package/dist/tools/__tests__/find-activity.test.d.ts.map +1 -0
- package/dist/tools/__tests__/find-activity.test.js +229 -0
- package/dist/tools/find-activity.d.ts +69 -0
- package/dist/tools/find-activity.d.ts.map +1 -0
- package/dist/tools/find-activity.js +221 -0
- package/dist/tools/find-completed-tasks.d.ts.map +1 -1
- package/dist/tools/find-completed-tasks.js +2 -2
- package/dist/utils/constants.d.ts +4 -0
- package/dist/utils/constants.d.ts.map +1 -1
- package/dist/utils/constants.js +4 -0
- package/dist/utils/tool-names.d.ts +1 -0
- package/dist/utils/tool-names.d.ts.map +1 -1
- package/dist/utils/tool-names.js +2 -0
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { addTasks } from './tools/add-tasks.js';
|
|
|
6
6
|
import { completeTasks } from './tools/complete-tasks.js';
|
|
7
7
|
import { deleteObject } from './tools/delete-object.js';
|
|
8
8
|
import { fetch } from './tools/fetch.js';
|
|
9
|
+
import { findActivity } from './tools/find-activity.js';
|
|
9
10
|
import { findComments } from './tools/find-comments.js';
|
|
10
11
|
import { findCompletedTasks } from './tools/find-completed-tasks.js';
|
|
11
12
|
import { findProjectCollaborators } from './tools/find-project-collaborators.js';
|
|
@@ -1091,6 +1092,72 @@ declare const tools: {
|
|
|
1091
1092
|
structuredContent?: undefined;
|
|
1092
1093
|
}>;
|
|
1093
1094
|
};
|
|
1095
|
+
findActivity: {
|
|
1096
|
+
name: "find-activity";
|
|
1097
|
+
description: string;
|
|
1098
|
+
parameters: {
|
|
1099
|
+
objectType: import("zod").ZodOptional<import("zod").ZodEnum<["task", "project", "comment"]>>;
|
|
1100
|
+
objectId: import("zod").ZodOptional<import("zod").ZodString>;
|
|
1101
|
+
eventType: import("zod").ZodOptional<import("zod").ZodEnum<["added", "updated", "deleted", "completed", "uncompleted", "archived", "unarchived", "shared", "left"]>>;
|
|
1102
|
+
projectId: import("zod").ZodOptional<import("zod").ZodString>;
|
|
1103
|
+
taskId: import("zod").ZodOptional<import("zod").ZodString>;
|
|
1104
|
+
initiatorId: import("zod").ZodOptional<import("zod").ZodString>;
|
|
1105
|
+
limit: import("zod").ZodDefault<import("zod").ZodNumber>;
|
|
1106
|
+
cursor: import("zod").ZodOptional<import("zod").ZodString>;
|
|
1107
|
+
};
|
|
1108
|
+
execute(args: {
|
|
1109
|
+
limit: number;
|
|
1110
|
+
taskId?: string | undefined;
|
|
1111
|
+
projectId?: string | undefined;
|
|
1112
|
+
objectType?: "task" | "comment" | "project" | undefined;
|
|
1113
|
+
objectId?: string | undefined;
|
|
1114
|
+
eventType?: "added" | "updated" | "completed" | "deleted" | "uncompleted" | "archived" | "unarchived" | "shared" | "left" | undefined;
|
|
1115
|
+
initiatorId?: string | undefined;
|
|
1116
|
+
cursor?: string | undefined;
|
|
1117
|
+
}, client: import("@doist/todoist-api-typescript").TodoistApi): Promise<{
|
|
1118
|
+
content: {
|
|
1119
|
+
type: "text";
|
|
1120
|
+
text: string;
|
|
1121
|
+
}[];
|
|
1122
|
+
structuredContent: {
|
|
1123
|
+
events: {
|
|
1124
|
+
id: string | null;
|
|
1125
|
+
objectType: string;
|
|
1126
|
+
objectId: string;
|
|
1127
|
+
eventType: string;
|
|
1128
|
+
eventDate: string;
|
|
1129
|
+
parentProjectId: string | null;
|
|
1130
|
+
parentItemId: string | null;
|
|
1131
|
+
initiatorId: string | null;
|
|
1132
|
+
extraData: Record<string, any> | null;
|
|
1133
|
+
}[];
|
|
1134
|
+
nextCursor: string | null;
|
|
1135
|
+
totalCount: number;
|
|
1136
|
+
hasMore: boolean;
|
|
1137
|
+
appliedFilters: {
|
|
1138
|
+
limit: number;
|
|
1139
|
+
taskId?: string | undefined;
|
|
1140
|
+
projectId?: string | undefined;
|
|
1141
|
+
objectType?: "task" | "comment" | "project" | undefined;
|
|
1142
|
+
objectId?: string | undefined;
|
|
1143
|
+
eventType?: "added" | "updated" | "completed" | "deleted" | "uncompleted" | "archived" | "unarchived" | "shared" | "left" | undefined;
|
|
1144
|
+
initiatorId?: string | undefined;
|
|
1145
|
+
cursor?: string | undefined;
|
|
1146
|
+
};
|
|
1147
|
+
};
|
|
1148
|
+
} | {
|
|
1149
|
+
content: ({
|
|
1150
|
+
type: "text";
|
|
1151
|
+
text: string;
|
|
1152
|
+
mimeType?: undefined;
|
|
1153
|
+
} | {
|
|
1154
|
+
type: "text";
|
|
1155
|
+
mimeType: string;
|
|
1156
|
+
text: string;
|
|
1157
|
+
})[];
|
|
1158
|
+
structuredContent?: undefined;
|
|
1159
|
+
}>;
|
|
1160
|
+
};
|
|
1094
1161
|
getOverview: {
|
|
1095
1162
|
name: "get-overview";
|
|
1096
1163
|
description: string;
|
|
@@ -1312,5 +1379,5 @@ declare const tools: {
|
|
|
1312
1379
|
};
|
|
1313
1380
|
};
|
|
1314
1381
|
export { tools, getMcpServer };
|
|
1315
|
-
export { addTasks, completeTasks, updateTasks, findTasks, findTasksByDate, findCompletedTasks, addProjects, updateProjects, findProjects, addSections, updateSections, findSections, addComments, updateComments, findComments, getOverview, deleteObject, userInfo, findProjectCollaborators, manageAssignments, search, fetch, };
|
|
1382
|
+
export { addTasks, completeTasks, updateTasks, findTasks, findTasksByDate, findCompletedTasks, addProjects, updateProjects, findProjects, addSections, updateSections, findSections, addComments, updateComments, findComments, findActivity, getOverview, deleteObject, userInfo, findProjectCollaborators, manageAssignments, search, fetch, };
|
|
1316
1383
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAE9C,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAErD,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAErD,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAErD,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAA;AAEzD,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AACvD,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -11,6 +11,8 @@ import { completeTasks } from './tools/complete-tasks.js';
|
|
|
11
11
|
// General tools
|
|
12
12
|
import { deleteObject } from './tools/delete-object.js';
|
|
13
13
|
import { fetch } from './tools/fetch.js';
|
|
14
|
+
// Activity and audit tools
|
|
15
|
+
import { findActivity } from './tools/find-activity.js';
|
|
14
16
|
import { findComments } from './tools/find-comments.js';
|
|
15
17
|
import { findCompletedTasks } from './tools/find-completed-tasks.js';
|
|
16
18
|
// Assignment and collaboration tools
|
|
@@ -47,6 +49,8 @@ const tools = {
|
|
|
47
49
|
addComments,
|
|
48
50
|
updateComments,
|
|
49
51
|
findComments,
|
|
52
|
+
// Activity and audit tools
|
|
53
|
+
findActivity,
|
|
50
54
|
// General tools
|
|
51
55
|
getOverview,
|
|
52
56
|
deleteObject,
|
|
@@ -68,6 +72,8 @@ addProjects, updateProjects, findProjects,
|
|
|
68
72
|
addSections, updateSections, findSections,
|
|
69
73
|
// Comment management tools
|
|
70
74
|
addComments, updateComments, findComments,
|
|
75
|
+
// Activity and audit tools
|
|
76
|
+
findActivity,
|
|
71
77
|
// General tools
|
|
72
78
|
getOverview, deleteObject, userInfo,
|
|
73
79
|
// Assignment and collaboration tools
|
package/dist/mcp-server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-server.d.ts","sourceRoot":"","sources":["../src/mcp-server.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;
|
|
1
|
+
{"version":3,"file":"mcp-server.d.ts","sourceRoot":"","sources":["../src/mcp-server.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AA+FnE;;;;;GAKG;AACH,iBAAS,YAAY,CAAC,EAAE,aAAa,EAAE,OAAO,EAAE,EAAE;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,aAqD5F;AAED,OAAO,EAAE,YAAY,EAAE,CAAA"}
|
package/dist/mcp-server.js
CHANGED
|
@@ -8,6 +8,7 @@ import { addTasks } from './tools/add-tasks.js';
|
|
|
8
8
|
import { completeTasks } from './tools/complete-tasks.js';
|
|
9
9
|
import { deleteObject } from './tools/delete-object.js';
|
|
10
10
|
import { fetch } from './tools/fetch.js';
|
|
11
|
+
import { findActivity } from './tools/find-activity.js';
|
|
11
12
|
import { findComments } from './tools/find-comments.js';
|
|
12
13
|
import { findCompletedTasks } from './tools/find-completed-tasks.js';
|
|
13
14
|
import { findProjectCollaborators } from './tools/find-project-collaborators.js';
|
|
@@ -43,7 +44,7 @@ You have access to comprehensive Todoist management tools for personal productiv
|
|
|
43
44
|
- **complete-tasks**: Mark tasks as done using task IDs
|
|
44
45
|
- **find-tasks**: Search by text, project/section/parent container, responsible user, or labels. Requires at least one search parameter
|
|
45
46
|
- **find-tasks-by-date**: Get tasks by date range (startDate: YYYY-MM-DD or 'today' which includes overdue tasks) or specific day counts
|
|
46
|
-
- **find-completed-tasks**: View completed tasks by completion date or original due date
|
|
47
|
+
- **find-completed-tasks**: View completed tasks by completion date or original due date (returns all collaborators unless filtered)
|
|
47
48
|
|
|
48
49
|
**Project & Organization:**
|
|
49
50
|
- **add-projects/update-projects/find-projects**: Manage project lifecycle with names, favorites, and view styles (list/board/calendar)
|
|
@@ -55,6 +56,9 @@ You have access to comprehensive Todoist management tools for personal productiv
|
|
|
55
56
|
- **find-project-collaborators**: Find team members by name or email for assignments
|
|
56
57
|
- **manage-assignments**: Bulk assign/unassign/reassign up to 50 tasks with atomic operations and dry-run validation
|
|
57
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
|
+
|
|
58
62
|
**General Operations:**
|
|
59
63
|
- **delete-object**: Remove projects, sections, tasks, or comments by type and ID
|
|
60
64
|
- **user-info**: Get user details including timezone, goals, and plan information
|
|
@@ -84,6 +88,7 @@ You have access to comprehensive Todoist management tools for personal productiv
|
|
|
84
88
|
- **Task Search**: find-tasks with multiple filters → update-tasks or complete-tasks based on results
|
|
85
89
|
- **Project Organization**: add-projects → add-sections → add-tasks with projectId and sectionId
|
|
86
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
|
|
87
92
|
|
|
88
93
|
Always provide clear, actionable task titles and descriptions. Use the overview tools to give users context about their workload and project status.
|
|
89
94
|
`;
|
|
@@ -120,6 +125,8 @@ function getMcpServer({ todoistApiKey, baseUrl }) {
|
|
|
120
125
|
registerTool(addComments, server, todoist);
|
|
121
126
|
registerTool(findComments, server, todoist);
|
|
122
127
|
registerTool(updateComments, server, todoist);
|
|
128
|
+
// Activity and audit tools
|
|
129
|
+
registerTool(findActivity, server, todoist);
|
|
123
130
|
// General tools
|
|
124
131
|
registerTool(getOverview, server, todoist);
|
|
125
132
|
registerTool(deleteObject, server, todoist);
|
package/dist/tool-helpers.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { MoveTaskArgs, PersonalProject, Task, TodoistApi, WorkspaceProject } from '@doist/todoist-api-typescript';
|
|
1
|
+
import type { ActivityEvent, MoveTaskArgs, PersonalProject, Task, TodoistApi, WorkspaceProject } from '@doist/todoist-api-typescript';
|
|
2
2
|
export { appendToQuery, buildResponsibleUserQueryFilter, filterTasksByResponsibleUser, RESPONSIBLE_USER_FILTERING, type ResponsibleUserFiltering, resolveResponsibleUser, } from './filter-helpers.js';
|
|
3
3
|
export type Project = PersonalProject | WorkspaceProject;
|
|
4
4
|
export declare function isPersonalProject(project: Project): project is PersonalProject;
|
|
@@ -50,6 +50,22 @@ declare function mapProject(project: Project): {
|
|
|
50
50
|
inboxProject: boolean;
|
|
51
51
|
viewStyle: string;
|
|
52
52
|
};
|
|
53
|
+
/**
|
|
54
|
+
* Map a single Todoist activity event to a more structured format, for LLM consumption.
|
|
55
|
+
* @param event - The activity event to map.
|
|
56
|
+
* @returns The mapped activity event.
|
|
57
|
+
*/
|
|
58
|
+
declare function mapActivityEvent(event: ActivityEvent): {
|
|
59
|
+
id: string | null;
|
|
60
|
+
objectType: string;
|
|
61
|
+
objectId: string;
|
|
62
|
+
eventType: string;
|
|
63
|
+
eventDate: string;
|
|
64
|
+
parentProjectId: string | null;
|
|
65
|
+
parentItemId: string | null;
|
|
66
|
+
initiatorId: string | null;
|
|
67
|
+
extraData: Record<string, any> | null;
|
|
68
|
+
};
|
|
53
69
|
declare function getTasksByFilter({ client, query, limit, cursor, }: {
|
|
54
70
|
client: TodoistApi;
|
|
55
71
|
query: string;
|
|
@@ -82,5 +98,5 @@ declare function getTasksByFilter({ client, query, limit, cursor, }: {
|
|
|
82
98
|
* @returns The URL string
|
|
83
99
|
*/
|
|
84
100
|
declare function buildTodoistUrl(type: 'task' | 'project', id: string): string;
|
|
85
|
-
export { buildTodoistUrl, getTasksByFilter, mapProject, mapTask };
|
|
101
|
+
export { buildTodoistUrl, getTasksByFilter, mapActivityEvent, mapProject, mapTask };
|
|
86
102
|
//# sourceMappingURL=tool-helpers.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-helpers.d.ts","sourceRoot":"","sources":["../src/tool-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACR,YAAY,EACZ,eAAe,EACf,IAAI,EACJ,UAAU,EACV,gBAAgB,EACnB,MAAM,+BAA+B,CAAA;AAKtC,OAAO,EACH,aAAa,EACb,+BAA+B,EAC/B,4BAA4B,EAC5B,0BAA0B,EAC1B,KAAK,wBAAwB,EAC7B,sBAAsB,GACzB,MAAM,qBAAqB,CAAA;AAE5B,MAAM,MAAM,OAAO,GAAG,eAAe,GAAG,gBAAgB,CAAA;AAExD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,IAAI,eAAe,CAE9E;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,IAAI,gBAAgB,CAEhF;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAC9B,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE,MAAM,GAClB,YAAY,CAsBd;AAED;;;;GAIG;AACH,iBAAS,OAAO,CAAC,IAAI,EAAE,IAAI;;;;;;;;;;;;;;;;EAkB1B;AAED;;;;GAIG;AACH,iBAAS,UAAU,CAAC,OAAO,EAAE,OAAO;;;;;;;;;EAWnC;AAWD,iBAAe,gBAAgB,CAAC,EAC5B,MAAM,EACN,KAAK,EACL,KAAK,EACL,MAAM,GACT,EAAE;IACC,MAAM,EAAE,UAAU,CAAA;IAClB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;IACzB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;CAC7B;;;;;;;;;;;;;;;;;;;GAkBA;AAED;;;;;GAKG;AACH,iBAAS,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAErE;AAED,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,UAAU,EAAE,OAAO,EAAE,CAAA"}
|
|
1
|
+
{"version":3,"file":"tool-helpers.d.ts","sourceRoot":"","sources":["../src/tool-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACR,aAAa,EACb,YAAY,EACZ,eAAe,EACf,IAAI,EACJ,UAAU,EACV,gBAAgB,EACnB,MAAM,+BAA+B,CAAA;AAKtC,OAAO,EACH,aAAa,EACb,+BAA+B,EAC/B,4BAA4B,EAC5B,0BAA0B,EAC1B,KAAK,wBAAwB,EAC7B,sBAAsB,GACzB,MAAM,qBAAqB,CAAA;AAE5B,MAAM,MAAM,OAAO,GAAG,eAAe,GAAG,gBAAgB,CAAA;AAExD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,IAAI,eAAe,CAE9E;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,IAAI,gBAAgB,CAEhF;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAC9B,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE,MAAM,GAClB,YAAY,CAsBd;AAED;;;;GAIG;AACH,iBAAS,OAAO,CAAC,IAAI,EAAE,IAAI;;;;;;;;;;;;;;;;EAkB1B;AAED;;;;GAIG;AACH,iBAAS,UAAU,CAAC,OAAO,EAAE,OAAO;;;;;;;;;EAWnC;AAED;;;;GAIG;AACH,iBAAS,gBAAgB,CAAC,KAAK,EAAE,aAAa;;;;;;;;;;EAY7C;AAWD,iBAAe,gBAAgB,CAAC,EAC5B,MAAM,EACN,KAAK,EACL,KAAK,EACL,MAAM,GACT,EAAE;IACC,MAAM,EAAE,UAAU,CAAA;IAClB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;IACzB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;CAC7B;;;;;;;;;;;;;;;;;;;GAkBA;AAED;;;;;GAKG;AACH,iBAAS,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAErE;AAED,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,UAAU,EAAE,OAAO,EAAE,CAAA"}
|
package/dist/tool-helpers.js
CHANGED
|
@@ -77,6 +77,24 @@ function mapProject(project) {
|
|
|
77
77
|
viewStyle: project.viewStyle,
|
|
78
78
|
};
|
|
79
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* Map a single Todoist activity event to a more structured format, for LLM consumption.
|
|
82
|
+
* @param event - The activity event to map.
|
|
83
|
+
* @returns The mapped activity event.
|
|
84
|
+
*/
|
|
85
|
+
function mapActivityEvent(event) {
|
|
86
|
+
return {
|
|
87
|
+
id: event.id,
|
|
88
|
+
objectType: event.objectType,
|
|
89
|
+
objectId: event.objectId,
|
|
90
|
+
eventType: event.eventType,
|
|
91
|
+
eventDate: event.eventDate,
|
|
92
|
+
parentProjectId: event.parentProjectId,
|
|
93
|
+
parentItemId: event.parentItemId,
|
|
94
|
+
initiatorId: event.initiatorId,
|
|
95
|
+
extraData: event.extraData,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
80
98
|
const ErrorSchema = z.object({
|
|
81
99
|
httpStatusCode: z.number(),
|
|
82
100
|
responseData: z.object({
|
|
@@ -112,4 +130,4 @@ async function getTasksByFilter({ client, query, limit, cursor, }) {
|
|
|
112
130
|
function buildTodoistUrl(type, id) {
|
|
113
131
|
return `https://app.todoist.com/app/${type}/${id}`;
|
|
114
132
|
}
|
|
115
|
-
export { buildTodoistUrl, getTasksByFilter, mapProject, mapTask };
|
|
133
|
+
export { buildTodoistUrl, getTasksByFilter, mapActivityEvent, mapProject, mapTask };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"find-activity.test.d.ts","sourceRoot":"","sources":["../../../src/tools/__tests__/find-activity.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { jest } from '@jest/globals';
|
|
2
|
+
import { extractTextContent } from '../../utils/test-helpers.js';
|
|
3
|
+
import { ToolNames } from '../../utils/tool-names.js';
|
|
4
|
+
import { findActivity } from '../find-activity.js';
|
|
5
|
+
// Mock the Todoist API
|
|
6
|
+
const mockTodoistApi = {
|
|
7
|
+
getActivityLogs: jest.fn(),
|
|
8
|
+
};
|
|
9
|
+
const { FIND_ACTIVITY } = ToolNames;
|
|
10
|
+
/**
|
|
11
|
+
* Helper to create a mock activity event
|
|
12
|
+
*/
|
|
13
|
+
function createMockActivityEvent(overrides = {}) {
|
|
14
|
+
return {
|
|
15
|
+
id: 'event-123',
|
|
16
|
+
objectType: 'task',
|
|
17
|
+
objectId: 'task-456',
|
|
18
|
+
eventType: 'added',
|
|
19
|
+
eventDate: '2024-10-23T10:30:00Z',
|
|
20
|
+
parentProjectId: 'project-789',
|
|
21
|
+
parentItemId: null,
|
|
22
|
+
initiatorId: 'user-001',
|
|
23
|
+
extraData: null,
|
|
24
|
+
...overrides,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
describe(`${FIND_ACTIVITY} tool`, () => {
|
|
28
|
+
beforeEach(() => {
|
|
29
|
+
jest.clearAllMocks();
|
|
30
|
+
});
|
|
31
|
+
describe('basic functionality', () => {
|
|
32
|
+
it('should retrieve activity events with default parameters', async () => {
|
|
33
|
+
const mockEvents = [
|
|
34
|
+
createMockActivityEvent({
|
|
35
|
+
id: 'event-1',
|
|
36
|
+
eventType: 'added',
|
|
37
|
+
eventDate: '2024-10-23T10:00:00Z',
|
|
38
|
+
}),
|
|
39
|
+
createMockActivityEvent({
|
|
40
|
+
id: 'event-2',
|
|
41
|
+
eventType: 'completed',
|
|
42
|
+
eventDate: '2024-10-23T11:00:00Z',
|
|
43
|
+
}),
|
|
44
|
+
];
|
|
45
|
+
mockTodoistApi.getActivityLogs.mockResolvedValue({
|
|
46
|
+
results: mockEvents,
|
|
47
|
+
nextCursor: null,
|
|
48
|
+
});
|
|
49
|
+
const result = await findActivity.execute({ limit: 20 }, mockTodoistApi);
|
|
50
|
+
expect(mockTodoistApi.getActivityLogs).toHaveBeenCalledWith({
|
|
51
|
+
limit: 20,
|
|
52
|
+
cursor: null,
|
|
53
|
+
});
|
|
54
|
+
expect(extractTextContent(result)).toMatchSnapshot();
|
|
55
|
+
});
|
|
56
|
+
it('should handle empty results', async () => {
|
|
57
|
+
mockTodoistApi.getActivityLogs.mockResolvedValue({
|
|
58
|
+
results: [],
|
|
59
|
+
nextCursor: null,
|
|
60
|
+
});
|
|
61
|
+
const result = await findActivity.execute({ limit: 20 }, mockTodoistApi);
|
|
62
|
+
expect(extractTextContent(result)).toMatchSnapshot();
|
|
63
|
+
});
|
|
64
|
+
it('should handle pagination with cursor', async () => {
|
|
65
|
+
const mockEvents = Array.from({ length: 20 }, (_, i) => createMockActivityEvent({
|
|
66
|
+
id: `event-${i}`,
|
|
67
|
+
objectId: `task-${i}`,
|
|
68
|
+
}));
|
|
69
|
+
mockTodoistApi.getActivityLogs.mockResolvedValue({
|
|
70
|
+
results: mockEvents,
|
|
71
|
+
nextCursor: 'next-page-cursor',
|
|
72
|
+
});
|
|
73
|
+
const result = await findActivity.execute({ limit: 20, cursor: 'current-cursor' }, mockTodoistApi);
|
|
74
|
+
expect(mockTodoistApi.getActivityLogs).toHaveBeenCalledWith({
|
|
75
|
+
limit: 20,
|
|
76
|
+
cursor: 'current-cursor',
|
|
77
|
+
});
|
|
78
|
+
expect(extractTextContent(result)).toContain('Pass cursor');
|
|
79
|
+
expect(extractTextContent(result)).toContain('next-page-cursor');
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
describe('filtering', () => {
|
|
83
|
+
it.each([
|
|
84
|
+
['task', 'added'],
|
|
85
|
+
['project', 'updated'],
|
|
86
|
+
['comment', 'deleted'],
|
|
87
|
+
])('should filter by object type: %s', async (objectType, eventType) => {
|
|
88
|
+
const mockEvents = [
|
|
89
|
+
createMockActivityEvent({
|
|
90
|
+
objectType: objectType,
|
|
91
|
+
eventType: eventType,
|
|
92
|
+
}),
|
|
93
|
+
];
|
|
94
|
+
mockTodoistApi.getActivityLogs.mockResolvedValue({
|
|
95
|
+
results: mockEvents,
|
|
96
|
+
nextCursor: null,
|
|
97
|
+
});
|
|
98
|
+
const result = await findActivity.execute({ objectType: objectType, limit: 20 }, mockTodoistApi);
|
|
99
|
+
expect(mockTodoistApi.getActivityLogs).toHaveBeenCalledWith({
|
|
100
|
+
objectType,
|
|
101
|
+
limit: 20,
|
|
102
|
+
cursor: null,
|
|
103
|
+
});
|
|
104
|
+
expect(extractTextContent(result)).toContain(objectType);
|
|
105
|
+
});
|
|
106
|
+
it.each([
|
|
107
|
+
['added', 'task-1'],
|
|
108
|
+
['completed', 'task-2'],
|
|
109
|
+
['updated', 'task-3'],
|
|
110
|
+
['deleted', 'task-4'],
|
|
111
|
+
])('should filter by event type: %s', async (eventType, objectId) => {
|
|
112
|
+
const mockEvents = [
|
|
113
|
+
createMockActivityEvent({
|
|
114
|
+
eventType: eventType,
|
|
115
|
+
objectId,
|
|
116
|
+
}),
|
|
117
|
+
];
|
|
118
|
+
mockTodoistApi.getActivityLogs.mockResolvedValue({
|
|
119
|
+
results: mockEvents,
|
|
120
|
+
nextCursor: null,
|
|
121
|
+
});
|
|
122
|
+
const result = await findActivity.execute({
|
|
123
|
+
eventType: eventType,
|
|
124
|
+
limit: 20,
|
|
125
|
+
}, mockTodoistApi);
|
|
126
|
+
expect(mockTodoistApi.getActivityLogs).toHaveBeenCalledWith({
|
|
127
|
+
eventType,
|
|
128
|
+
limit: 20,
|
|
129
|
+
cursor: null,
|
|
130
|
+
});
|
|
131
|
+
expect(extractTextContent(result)).toContain(eventType);
|
|
132
|
+
});
|
|
133
|
+
it.each([
|
|
134
|
+
['objectId', 'task-123', { objectId: 'task-123' }],
|
|
135
|
+
['projectId', 'project-abc', { parentProjectId: 'project-abc' }],
|
|
136
|
+
['taskId', 'parent-task-789', { parentItemId: 'parent-task-789' }],
|
|
137
|
+
['initiatorId', 'user-alice', { initiatorId: 'user-alice' }],
|
|
138
|
+
])('should filter by %s', async (filterName, filterId, expectedApiCall) => {
|
|
139
|
+
const mockEvents = [createMockActivityEvent()];
|
|
140
|
+
mockTodoistApi.getActivityLogs.mockResolvedValue({
|
|
141
|
+
results: mockEvents,
|
|
142
|
+
nextCursor: null,
|
|
143
|
+
});
|
|
144
|
+
const args = { limit: 20 };
|
|
145
|
+
args[filterName] = filterId;
|
|
146
|
+
await findActivity.execute(args, mockTodoistApi);
|
|
147
|
+
expect(mockTodoistApi.getActivityLogs).toHaveBeenCalledWith({
|
|
148
|
+
...expectedApiCall,
|
|
149
|
+
limit: 20,
|
|
150
|
+
cursor: null,
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
it('should support multiple filters simultaneously', async () => {
|
|
154
|
+
const mockEvents = [
|
|
155
|
+
createMockActivityEvent({
|
|
156
|
+
objectType: 'task',
|
|
157
|
+
eventType: 'completed',
|
|
158
|
+
parentProjectId: 'project-work',
|
|
159
|
+
initiatorId: 'user-bob',
|
|
160
|
+
}),
|
|
161
|
+
];
|
|
162
|
+
mockTodoistApi.getActivityLogs.mockResolvedValue({
|
|
163
|
+
results: mockEvents,
|
|
164
|
+
nextCursor: null,
|
|
165
|
+
});
|
|
166
|
+
const result = await findActivity.execute({
|
|
167
|
+
objectType: 'task',
|
|
168
|
+
eventType: 'completed',
|
|
169
|
+
projectId: 'project-work',
|
|
170
|
+
initiatorId: 'user-bob',
|
|
171
|
+
limit: 50,
|
|
172
|
+
}, mockTodoistApi);
|
|
173
|
+
expect(mockTodoistApi.getActivityLogs).toHaveBeenCalledWith({
|
|
174
|
+
objectType: 'task',
|
|
175
|
+
eventType: 'completed',
|
|
176
|
+
parentProjectId: 'project-work',
|
|
177
|
+
initiatorId: 'user-bob',
|
|
178
|
+
limit: 50,
|
|
179
|
+
cursor: null,
|
|
180
|
+
});
|
|
181
|
+
expect(extractTextContent(result)).toMatchSnapshot();
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
describe('content extraction', () => {
|
|
185
|
+
it('should extract task content from extraData', async () => {
|
|
186
|
+
const mockEvents = [
|
|
187
|
+
createMockActivityEvent({
|
|
188
|
+
eventType: 'added',
|
|
189
|
+
extraData: { content: 'Buy groceries' },
|
|
190
|
+
}),
|
|
191
|
+
];
|
|
192
|
+
mockTodoistApi.getActivityLogs.mockResolvedValue({
|
|
193
|
+
results: mockEvents,
|
|
194
|
+
nextCursor: null,
|
|
195
|
+
});
|
|
196
|
+
const result = await findActivity.execute({ limit: 20 }, mockTodoistApi);
|
|
197
|
+
expect(extractTextContent(result)).toContain('Buy groceries');
|
|
198
|
+
});
|
|
199
|
+
it('should handle system-generated events with no initiator', async () => {
|
|
200
|
+
const mockEvents = [
|
|
201
|
+
createMockActivityEvent({
|
|
202
|
+
initiatorId: null,
|
|
203
|
+
eventType: 'completed',
|
|
204
|
+
}),
|
|
205
|
+
];
|
|
206
|
+
mockTodoistApi.getActivityLogs.mockResolvedValue({
|
|
207
|
+
results: mockEvents,
|
|
208
|
+
nextCursor: null,
|
|
209
|
+
});
|
|
210
|
+
const result = await findActivity.execute({ limit: 20 }, mockTodoistApi);
|
|
211
|
+
expect(extractTextContent(result)).toContain('system');
|
|
212
|
+
});
|
|
213
|
+
it('should truncate long content', async () => {
|
|
214
|
+
const longContent = 'A'.repeat(100);
|
|
215
|
+
const mockEvents = [
|
|
216
|
+
createMockActivityEvent({
|
|
217
|
+
extraData: { content: longContent },
|
|
218
|
+
}),
|
|
219
|
+
];
|
|
220
|
+
mockTodoistApi.getActivityLogs.mockResolvedValue({
|
|
221
|
+
results: mockEvents,
|
|
222
|
+
nextCursor: null,
|
|
223
|
+
});
|
|
224
|
+
const result = await findActivity.execute({ limit: 20 }, mockTodoistApi);
|
|
225
|
+
expect(extractTextContent(result)).toContain('...');
|
|
226
|
+
expect(extractTextContent(result)).not.toContain(longContent);
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
declare const findActivity: {
|
|
3
|
+
name: "find-activity";
|
|
4
|
+
description: string;
|
|
5
|
+
parameters: {
|
|
6
|
+
objectType: z.ZodOptional<z.ZodEnum<["task", "project", "comment"]>>;
|
|
7
|
+
objectId: z.ZodOptional<z.ZodString>;
|
|
8
|
+
eventType: z.ZodOptional<z.ZodEnum<["added", "updated", "deleted", "completed", "uncompleted", "archived", "unarchived", "shared", "left"]>>;
|
|
9
|
+
projectId: z.ZodOptional<z.ZodString>;
|
|
10
|
+
taskId: z.ZodOptional<z.ZodString>;
|
|
11
|
+
initiatorId: z.ZodOptional<z.ZodString>;
|
|
12
|
+
limit: z.ZodDefault<z.ZodNumber>;
|
|
13
|
+
cursor: z.ZodOptional<z.ZodString>;
|
|
14
|
+
};
|
|
15
|
+
execute(args: {
|
|
16
|
+
limit: number;
|
|
17
|
+
taskId?: string | undefined;
|
|
18
|
+
projectId?: string | undefined;
|
|
19
|
+
objectType?: "task" | "comment" | "project" | undefined;
|
|
20
|
+
objectId?: string | undefined;
|
|
21
|
+
eventType?: "added" | "updated" | "completed" | "deleted" | "uncompleted" | "archived" | "unarchived" | "shared" | "left" | undefined;
|
|
22
|
+
initiatorId?: string | undefined;
|
|
23
|
+
cursor?: string | undefined;
|
|
24
|
+
}, client: import("@doist/todoist-api-typescript").TodoistApi): Promise<{
|
|
25
|
+
content: {
|
|
26
|
+
type: "text";
|
|
27
|
+
text: string;
|
|
28
|
+
}[];
|
|
29
|
+
structuredContent: {
|
|
30
|
+
events: {
|
|
31
|
+
id: string | null;
|
|
32
|
+
objectType: string;
|
|
33
|
+
objectId: string;
|
|
34
|
+
eventType: string;
|
|
35
|
+
eventDate: string;
|
|
36
|
+
parentProjectId: string | null;
|
|
37
|
+
parentItemId: string | null;
|
|
38
|
+
initiatorId: string | null;
|
|
39
|
+
extraData: Record<string, any> | null;
|
|
40
|
+
}[];
|
|
41
|
+
nextCursor: string | null;
|
|
42
|
+
totalCount: number;
|
|
43
|
+
hasMore: boolean;
|
|
44
|
+
appliedFilters: {
|
|
45
|
+
limit: number;
|
|
46
|
+
taskId?: string | undefined;
|
|
47
|
+
projectId?: string | undefined;
|
|
48
|
+
objectType?: "task" | "comment" | "project" | undefined;
|
|
49
|
+
objectId?: string | undefined;
|
|
50
|
+
eventType?: "added" | "updated" | "completed" | "deleted" | "uncompleted" | "archived" | "unarchived" | "shared" | "left" | undefined;
|
|
51
|
+
initiatorId?: string | undefined;
|
|
52
|
+
cursor?: string | undefined;
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
} | {
|
|
56
|
+
content: ({
|
|
57
|
+
type: "text";
|
|
58
|
+
text: string;
|
|
59
|
+
mimeType?: undefined;
|
|
60
|
+
} | {
|
|
61
|
+
type: "text";
|
|
62
|
+
mimeType: string;
|
|
63
|
+
text: string;
|
|
64
|
+
})[];
|
|
65
|
+
structuredContent?: undefined;
|
|
66
|
+
}>;
|
|
67
|
+
};
|
|
68
|
+
export { findActivity };
|
|
69
|
+
//# sourceMappingURL=find-activity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"find-activity.d.ts","sourceRoot":"","sources":["../../src/tools/find-activity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAwDvB,QAAA,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6CwB,CAAA;AA+J1C,OAAO,EAAE,YAAY,EAAE,CAAA"}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { getToolOutput } from '../mcp-helpers.js';
|
|
3
|
+
import { mapActivityEvent } from '../tool-helpers.js';
|
|
4
|
+
import { ApiLimits } from '../utils/constants.js';
|
|
5
|
+
import { summarizeList } from '../utils/response-builders.js';
|
|
6
|
+
import { ToolNames } from '../utils/tool-names.js';
|
|
7
|
+
const { FIND_TASKS, USER_INFO } = ToolNames;
|
|
8
|
+
const ArgsSchema = {
|
|
9
|
+
objectType: z
|
|
10
|
+
.enum(['task', 'project', 'comment'])
|
|
11
|
+
.optional()
|
|
12
|
+
.describe('Type of object to filter by.'),
|
|
13
|
+
objectId: z
|
|
14
|
+
.string()
|
|
15
|
+
.optional()
|
|
16
|
+
.describe('Filter by specific object ID (task, project, or comment).'),
|
|
17
|
+
eventType: z
|
|
18
|
+
.enum([
|
|
19
|
+
'added',
|
|
20
|
+
'updated',
|
|
21
|
+
'deleted',
|
|
22
|
+
'completed',
|
|
23
|
+
'uncompleted',
|
|
24
|
+
'archived',
|
|
25
|
+
'unarchived',
|
|
26
|
+
'shared',
|
|
27
|
+
'left',
|
|
28
|
+
])
|
|
29
|
+
.optional()
|
|
30
|
+
.describe('Type of event to filter by.'),
|
|
31
|
+
projectId: z.string().optional().describe('Filter events by parent project ID.'),
|
|
32
|
+
taskId: z.string().optional().describe('Filter events by parent task ID (for subtask events).'),
|
|
33
|
+
initiatorId: z.string().optional().describe('Filter by the user ID who initiated the event.'),
|
|
34
|
+
limit: z
|
|
35
|
+
.number()
|
|
36
|
+
.int()
|
|
37
|
+
.min(1)
|
|
38
|
+
.max(ApiLimits.ACTIVITY_MAX)
|
|
39
|
+
.default(ApiLimits.ACTIVITY_DEFAULT)
|
|
40
|
+
.describe('Maximum number of activity events to return.'),
|
|
41
|
+
cursor: z
|
|
42
|
+
.string()
|
|
43
|
+
.optional()
|
|
44
|
+
.describe('Pagination cursor for retrieving the next page of results.'),
|
|
45
|
+
};
|
|
46
|
+
const findActivity = {
|
|
47
|
+
name: ToolNames.FIND_ACTIVITY,
|
|
48
|
+
description: 'Retrieve recent activity logs to monitor and audit changes in Todoist. Shows events from all users by default (use initiatorId to filter by specific user). Track task completions, updates, deletions, project changes, and more with flexible filtering. Note: Date-based filtering is not supported by the Todoist API.',
|
|
49
|
+
parameters: ArgsSchema,
|
|
50
|
+
async execute(args, client) {
|
|
51
|
+
const { objectType, objectId, eventType, projectId, taskId, initiatorId, limit, cursor } = args;
|
|
52
|
+
// Build API arguments
|
|
53
|
+
const apiArgs = {
|
|
54
|
+
limit,
|
|
55
|
+
cursor: cursor ?? null,
|
|
56
|
+
};
|
|
57
|
+
// Add optional filters
|
|
58
|
+
if (objectType)
|
|
59
|
+
apiArgs.objectType = objectType;
|
|
60
|
+
if (objectId)
|
|
61
|
+
apiArgs.objectId = objectId;
|
|
62
|
+
if (eventType)
|
|
63
|
+
apiArgs.eventType = eventType;
|
|
64
|
+
if (projectId)
|
|
65
|
+
apiArgs.parentProjectId = projectId;
|
|
66
|
+
if (taskId)
|
|
67
|
+
apiArgs.parentItemId = taskId;
|
|
68
|
+
if (initiatorId)
|
|
69
|
+
apiArgs.initiatorId = initiatorId;
|
|
70
|
+
// Fetch activity logs from API
|
|
71
|
+
const { results, nextCursor } = await client.getActivityLogs(apiArgs);
|
|
72
|
+
const events = results.map(mapActivityEvent);
|
|
73
|
+
// Generate text content
|
|
74
|
+
const textContent = generateTextContent({
|
|
75
|
+
events,
|
|
76
|
+
args,
|
|
77
|
+
nextCursor,
|
|
78
|
+
});
|
|
79
|
+
return getToolOutput({
|
|
80
|
+
textContent,
|
|
81
|
+
structuredContent: {
|
|
82
|
+
events,
|
|
83
|
+
nextCursor,
|
|
84
|
+
totalCount: events.length,
|
|
85
|
+
hasMore: Boolean(nextCursor),
|
|
86
|
+
appliedFilters: args,
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
function generateTextContent({ events, args, nextCursor, }) {
|
|
92
|
+
// Generate subject description
|
|
93
|
+
let subject = 'Activity events';
|
|
94
|
+
// Build subject based on filters
|
|
95
|
+
const subjectParts = [];
|
|
96
|
+
if (args.eventType) {
|
|
97
|
+
subjectParts.push(`${args.eventType}`);
|
|
98
|
+
}
|
|
99
|
+
if (args.objectType) {
|
|
100
|
+
const objectLabel = args.objectType === 'task' ? 'tasks' : `${args.objectType}s`;
|
|
101
|
+
subjectParts.push(objectLabel);
|
|
102
|
+
}
|
|
103
|
+
if (subjectParts.length > 0) {
|
|
104
|
+
subject = `Activity: ${subjectParts.join(' ')}`;
|
|
105
|
+
}
|
|
106
|
+
// Generate filter hints
|
|
107
|
+
const filterHints = [];
|
|
108
|
+
if (args.objectId) {
|
|
109
|
+
filterHints.push(`object ID: ${args.objectId}`);
|
|
110
|
+
}
|
|
111
|
+
if (args.projectId) {
|
|
112
|
+
filterHints.push(`project: ${args.projectId}`);
|
|
113
|
+
}
|
|
114
|
+
if (args.taskId) {
|
|
115
|
+
filterHints.push(`task: ${args.taskId}`);
|
|
116
|
+
}
|
|
117
|
+
if (args.initiatorId) {
|
|
118
|
+
filterHints.push(`initiator: ${args.initiatorId}`);
|
|
119
|
+
}
|
|
120
|
+
// Generate helpful suggestions for empty results
|
|
121
|
+
const zeroReasonHints = [];
|
|
122
|
+
if (events.length === 0) {
|
|
123
|
+
zeroReasonHints.push('No activity events match the specified filters');
|
|
124
|
+
zeroReasonHints.push('Note: Activity logs only show recent events');
|
|
125
|
+
if (args.eventType) {
|
|
126
|
+
zeroReasonHints.push(`Try removing the eventType filter (${args.eventType})`);
|
|
127
|
+
}
|
|
128
|
+
if (args.objectType) {
|
|
129
|
+
zeroReasonHints.push(`Try removing the objectType filter (${args.objectType})`);
|
|
130
|
+
}
|
|
131
|
+
if (args.objectId || args.projectId || args.taskId) {
|
|
132
|
+
zeroReasonHints.push('Verify the object ID is correct');
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// Generate contextual next steps
|
|
136
|
+
const nextSteps = [];
|
|
137
|
+
if (events.length > 0) {
|
|
138
|
+
// Suggest related tools based on what was found
|
|
139
|
+
const hasTaskEvents = events.some((e) => e.objectType === 'task' || e.objectType === 'item');
|
|
140
|
+
const hasCompletions = events.some((e) => e.eventType === 'completed');
|
|
141
|
+
if (hasTaskEvents) {
|
|
142
|
+
nextSteps.push(`Use ${FIND_TASKS} to view current task details`);
|
|
143
|
+
}
|
|
144
|
+
if (hasCompletions) {
|
|
145
|
+
nextSteps.push('Review completed tasks to track productivity');
|
|
146
|
+
}
|
|
147
|
+
if (args.initiatorId) {
|
|
148
|
+
nextSteps.push(`Use ${USER_INFO} to get details about the user`);
|
|
149
|
+
}
|
|
150
|
+
// Suggest narrowing down if too many results
|
|
151
|
+
if (events.length >= args.limit && !nextCursor) {
|
|
152
|
+
nextSteps.push('Add more specific filters to narrow down results');
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return summarizeList({
|
|
156
|
+
subject,
|
|
157
|
+
count: events.length,
|
|
158
|
+
limit: args.limit,
|
|
159
|
+
nextCursor: nextCursor ?? undefined,
|
|
160
|
+
filterHints,
|
|
161
|
+
previewLines: previewActivityEvents(events, Math.min(events.length, args.limit)),
|
|
162
|
+
zeroReasonHints,
|
|
163
|
+
nextSteps,
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Formats activity events into readable preview lines
|
|
168
|
+
*/
|
|
169
|
+
function previewActivityEvents(events, limit = 10) {
|
|
170
|
+
const previewEvents = events.slice(0, limit);
|
|
171
|
+
const lines = previewEvents.map(formatActivityEventPreview).join('\n');
|
|
172
|
+
// If we're showing fewer events than the total, add an indicator
|
|
173
|
+
if (events.length > limit) {
|
|
174
|
+
const remaining = events.length - limit;
|
|
175
|
+
return `${lines}\n ... and ${remaining} more event${remaining === 1 ? '' : 's'}`;
|
|
176
|
+
}
|
|
177
|
+
return lines;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Formats a single activity event into a readable preview line
|
|
181
|
+
*/
|
|
182
|
+
function formatActivityEventPreview(event) {
|
|
183
|
+
const date = formatEventDate(event.eventDate);
|
|
184
|
+
const eventLabel = `${event.eventType} ${event.objectType}`;
|
|
185
|
+
// Extract useful content from extraData if available
|
|
186
|
+
let contentInfo = '';
|
|
187
|
+
if (event.extraData) {
|
|
188
|
+
const content = event.extraData.content || event.extraData.name || event.extraData.last_content;
|
|
189
|
+
if (content && typeof content === 'string') {
|
|
190
|
+
// Truncate long content
|
|
191
|
+
const truncated = content.length > 50 ? `${content.substring(0, 47)}...` : content;
|
|
192
|
+
contentInfo = ` • "${truncated}"`;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
const objectId = event.objectId ? ` • id=${event.objectId}` : '';
|
|
196
|
+
const initiator = event.initiatorId ? ` • by=${event.initiatorId}` : ' • system';
|
|
197
|
+
const projectInfo = event.parentProjectId ? ` • project=${event.parentProjectId}` : '';
|
|
198
|
+
return ` [${date}] ${eventLabel}${contentInfo}${objectId}${initiator}${projectInfo}`;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Formats an ISO date string to a more readable format
|
|
202
|
+
*/
|
|
203
|
+
function formatEventDate(isoDate) {
|
|
204
|
+
try {
|
|
205
|
+
const date = new Date(isoDate);
|
|
206
|
+
// Format as: Oct 23, 14:30 (in UTC for deterministic snapshots)
|
|
207
|
+
const month = date.toLocaleDateString('en-US', { month: 'short', timeZone: 'UTC' });
|
|
208
|
+
const day = date.toLocaleDateString('en-US', { day: 'numeric', timeZone: 'UTC' });
|
|
209
|
+
const time = date.toLocaleTimeString('en-US', {
|
|
210
|
+
hour: '2-digit',
|
|
211
|
+
minute: '2-digit',
|
|
212
|
+
hour12: false,
|
|
213
|
+
timeZone: 'UTC',
|
|
214
|
+
});
|
|
215
|
+
return `${month} ${day}, ${time}`;
|
|
216
|
+
}
|
|
217
|
+
catch {
|
|
218
|
+
return isoDate;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
export { findActivity };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"find-completed-tasks.d.ts","sourceRoot":"","sources":["../../src/tools/find-completed-tasks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;
|
|
1
|
+
{"version":3,"file":"find-completed-tasks.d.ts","sourceRoot":"","sources":["../../src/tools/find-completed-tasks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAwDvB,QAAA,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmEkB,CAAA;AA2E1C,OAAO,EAAE,kBAAkB,EAAE,CAAA"}
|
|
@@ -29,7 +29,7 @@ const ArgsSchema = {
|
|
|
29
29
|
responsibleUser: z
|
|
30
30
|
.string()
|
|
31
31
|
.optional()
|
|
32
|
-
.describe('Find tasks assigned to this user. Can be a user ID, name, or email address.'),
|
|
32
|
+
.describe('Find tasks assigned to this user. Can be a user ID, name, or email address. Defaults to all collaborators when omitted.'),
|
|
33
33
|
limit: z
|
|
34
34
|
.number()
|
|
35
35
|
.int()
|
|
@@ -45,7 +45,7 @@ const ArgsSchema = {
|
|
|
45
45
|
};
|
|
46
46
|
const findCompletedTasks = {
|
|
47
47
|
name: ToolNames.FIND_COMPLETED_TASKS,
|
|
48
|
-
description: 'Get completed tasks.',
|
|
48
|
+
description: 'Get completed tasks (includes all collaborators by default—use responsibleUser to narrow).',
|
|
49
49
|
parameters: ArgsSchema,
|
|
50
50
|
async execute(args, client) {
|
|
51
51
|
const { getBy, labels, labelsOperator, since, until, responsibleUser, ...rest } = args;
|
|
@@ -23,6 +23,10 @@ export declare const ApiLimits: {
|
|
|
23
23
|
readonly COMMENTS_DEFAULT: 10;
|
|
24
24
|
/** Maximum limit for comment search and list operations */
|
|
25
25
|
readonly COMMENTS_MAX: 10;
|
|
26
|
+
/** Default limit for activity log listings */
|
|
27
|
+
readonly ACTIVITY_DEFAULT: 20;
|
|
28
|
+
/** Maximum limit for activity log search and list operations */
|
|
29
|
+
readonly ACTIVITY_MAX: 100;
|
|
26
30
|
};
|
|
27
31
|
export declare const DisplayLimits: {
|
|
28
32
|
/** Maximum number of failures to show in detailed error messages */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/utils/constants.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,eAAO,MAAM,SAAS;IAClB,sCAAsC;;IAEtC,wDAAwD;;IAExD,wCAAwC;;IAExC,wCAAwC;;IAExC,yCAAyC;;IAEzC,yCAAyC;;IAEzC,qDAAqD;;IAErD,yCAAyC;;IAEzC,2DAA2D;;
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/utils/constants.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,eAAO,MAAM,SAAS;IAClB,sCAAsC;;IAEtC,wDAAwD;;IAExD,wCAAwC;;IAExC,wCAAwC;;IAExC,yCAAyC;;IAEzC,yCAAyC;;IAEzC,qDAAqD;;IAErD,yCAAyC;;IAEzC,2DAA2D;;IAE3D,8CAA8C;;IAE9C,gEAAgE;;CAE1D,CAAA;AAGV,eAAO,MAAM,aAAa;IACtB,oEAAoE;;IAEpE,gDAAgD;;CAE1C,CAAA;AAGV,eAAO,MAAM,cAAc;IACvB,oDAAoD;;IAEpD,mCAAmC;;CAE7B,CAAA"}
|
package/dist/utils/constants.js
CHANGED
|
@@ -24,6 +24,10 @@ export const ApiLimits = {
|
|
|
24
24
|
COMMENTS_DEFAULT: 10,
|
|
25
25
|
/** Maximum limit for comment search and list operations */
|
|
26
26
|
COMMENTS_MAX: 10,
|
|
27
|
+
/** Default limit for activity log listings */
|
|
28
|
+
ACTIVITY_DEFAULT: 20,
|
|
29
|
+
/** Maximum limit for activity log search and list operations */
|
|
30
|
+
ACTIVITY_MAX: 100,
|
|
27
31
|
};
|
|
28
32
|
// UI Display Limits
|
|
29
33
|
export const DisplayLimits = {
|
|
@@ -23,6 +23,7 @@ export declare const ToolNames: {
|
|
|
23
23
|
readonly FIND_COMMENTS: "find-comments";
|
|
24
24
|
readonly FIND_PROJECT_COLLABORATORS: "find-project-collaborators";
|
|
25
25
|
readonly MANAGE_ASSIGNMENTS: "manage-assignments";
|
|
26
|
+
readonly FIND_ACTIVITY: "find-activity";
|
|
26
27
|
readonly GET_OVERVIEW: "get-overview";
|
|
27
28
|
readonly DELETE_OBJECT: "delete-object";
|
|
28
29
|
readonly USER_INFO: "user-info";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-names.d.ts","sourceRoot":"","sources":["../../src/utils/tool-names.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,SAAS
|
|
1
|
+
{"version":3,"file":"tool-names.d.ts","sourceRoot":"","sources":["../../src/utils/tool-names.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;CAuCZ,CAAA;AAGV,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,OAAO,SAAS,CAAC,CAAA"}
|
package/dist/utils/tool-names.js
CHANGED
|
@@ -28,6 +28,8 @@ export const ToolNames = {
|
|
|
28
28
|
// Assignment and collaboration tools
|
|
29
29
|
FIND_PROJECT_COLLABORATORS: 'find-project-collaborators',
|
|
30
30
|
MANAGE_ASSIGNMENTS: 'manage-assignments',
|
|
31
|
+
// Activity and audit tools
|
|
32
|
+
FIND_ACTIVITY: 'find-activity',
|
|
31
33
|
// General tools
|
|
32
34
|
GET_OVERVIEW: 'get-overview',
|
|
33
35
|
DELETE_OBJECT: 'delete-object',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@doist/todoist-ai",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.14.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -45,9 +45,9 @@
|
|
|
45
45
|
"prepare": "husky"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@doist/todoist-api-typescript": "5.7.0",
|
|
49
48
|
"@modelcontextprotocol/sdk": "^1.11.1",
|
|
50
49
|
"date-fns": "^4.1.0",
|
|
50
|
+
"@doist/todoist-api-typescript": "5.7.1",
|
|
51
51
|
"dotenv": "^17.0.0",
|
|
52
52
|
"zod": "^3.25.7"
|
|
53
53
|
},
|