@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.
Files changed (167) hide show
  1. package/dist/filter-helpers.d.ts +1 -1
  2. package/dist/index.d.ts +175 -175
  3. package/dist/index.js +61 -81
  4. package/dist/main.js +15 -23
  5. package/dist/mcp-helpers.d.ts +4 -4
  6. package/dist/mcp-server-6tm7Rhyz.js +2840 -0
  7. package/dist/todoist-tool.d.ts +2 -2
  8. package/dist/tool-helpers.d.ts +1 -1
  9. package/dist/tools/add-comments.d.ts +1 -1
  10. package/dist/tools/add-comments.d.ts.map +1 -1
  11. package/dist/tools/add-projects.d.ts +4 -4
  12. package/dist/tools/add-projects.d.ts.map +1 -1
  13. package/dist/tools/add-sections.d.ts +1 -1
  14. package/dist/tools/add-sections.d.ts.map +1 -1
  15. package/dist/tools/add-tasks.d.ts +4 -4
  16. package/dist/tools/add-tasks.d.ts.map +1 -1
  17. package/dist/tools/complete-tasks.d.ts +1 -1
  18. package/dist/tools/complete-tasks.d.ts.map +1 -1
  19. package/dist/tools/delete-object.d.ts +3 -3
  20. package/dist/tools/delete-object.d.ts.map +1 -1
  21. package/dist/tools/fetch.d.ts +1 -1
  22. package/dist/tools/find-activity.d.ts +5 -5
  23. package/dist/tools/find-activity.d.ts.map +1 -1
  24. package/dist/tools/find-comments.d.ts +2 -2
  25. package/dist/tools/find-comments.d.ts.map +1 -1
  26. package/dist/tools/find-completed-tasks.d.ts +3 -3
  27. package/dist/tools/find-completed-tasks.d.ts.map +1 -1
  28. package/dist/tools/find-project-collaborators.d.ts +2 -2
  29. package/dist/tools/find-projects.d.ts +1 -1
  30. package/dist/tools/find-projects.d.ts.map +1 -1
  31. package/dist/tools/find-sections.d.ts +1 -1
  32. package/dist/tools/find-sections.d.ts.map +1 -1
  33. package/dist/tools/find-tasks-by-date.d.ts +1 -1
  34. package/dist/tools/find-tasks-by-date.d.ts.map +1 -1
  35. package/dist/tools/find-tasks.d.ts +3 -3
  36. package/dist/tools/find-tasks.d.ts.map +1 -1
  37. package/dist/tools/get-overview.d.ts +1 -1
  38. package/dist/tools/manage-assignments.d.ts +1 -1
  39. package/dist/tools/search.d.ts +1 -1
  40. package/dist/tools/update-comments.d.ts +4 -4
  41. package/dist/tools/update-comments.d.ts.map +1 -1
  42. package/dist/tools/update-projects.d.ts +1 -1
  43. package/dist/tools/update-projects.d.ts.map +1 -1
  44. package/dist/tools/update-sections.d.ts +4 -4
  45. package/dist/tools/update-sections.d.ts.map +1 -1
  46. package/dist/tools/update-tasks.d.ts +7 -7
  47. package/dist/tools/update-tasks.d.ts.map +1 -1
  48. package/dist/tools/user-info.d.ts +1 -1
  49. package/dist/utils/assignment-validator.d.ts +2 -2
  50. package/dist/utils/response-builders.d.ts +1 -3
  51. package/dist/utils/response-builders.d.ts.map +1 -1
  52. package/dist/utils/test-helpers.d.ts +1 -1
  53. package/dist/utils/user-resolver.d.ts +1 -1
  54. package/package.json +11 -9
  55. package/dist/filter-helpers.js +0 -79
  56. package/dist/mcp-helpers.js +0 -71
  57. package/dist/mcp-server.js +0 -142
  58. package/dist/todoist-tool.js +0 -1
  59. package/dist/tool-helpers.js +0 -125
  60. package/dist/tool-helpers.test.d.ts +0 -2
  61. package/dist/tool-helpers.test.d.ts.map +0 -1
  62. package/dist/tool-helpers.test.js +0 -223
  63. package/dist/tools/__tests__/add-comments.test.d.ts +0 -2
  64. package/dist/tools/__tests__/add-comments.test.d.ts.map +0 -1
  65. package/dist/tools/__tests__/add-comments.test.js +0 -241
  66. package/dist/tools/__tests__/add-projects.test.d.ts +0 -2
  67. package/dist/tools/__tests__/add-projects.test.d.ts.map +0 -1
  68. package/dist/tools/__tests__/add-projects.test.js +0 -174
  69. package/dist/tools/__tests__/add-sections.test.d.ts +0 -2
  70. package/dist/tools/__tests__/add-sections.test.d.ts.map +0 -1
  71. package/dist/tools/__tests__/add-sections.test.js +0 -185
  72. package/dist/tools/__tests__/add-tasks.test.d.ts +0 -2
  73. package/dist/tools/__tests__/add-tasks.test.d.ts.map +0 -1
  74. package/dist/tools/__tests__/add-tasks.test.js +0 -533
  75. package/dist/tools/__tests__/assignment-integration.test.d.ts +0 -2
  76. package/dist/tools/__tests__/assignment-integration.test.d.ts.map +0 -1
  77. package/dist/tools/__tests__/assignment-integration.test.js +0 -428
  78. package/dist/tools/__tests__/complete-tasks.test.d.ts +0 -2
  79. package/dist/tools/__tests__/complete-tasks.test.d.ts.map +0 -1
  80. package/dist/tools/__tests__/complete-tasks.test.js +0 -206
  81. package/dist/tools/__tests__/delete-object.test.d.ts +0 -2
  82. package/dist/tools/__tests__/delete-object.test.d.ts.map +0 -1
  83. package/dist/tools/__tests__/delete-object.test.js +0 -110
  84. package/dist/tools/__tests__/fetch.test.d.ts +0 -2
  85. package/dist/tools/__tests__/fetch.test.d.ts.map +0 -1
  86. package/dist/tools/__tests__/fetch.test.js +0 -279
  87. package/dist/tools/__tests__/find-activity.test.d.ts +0 -2
  88. package/dist/tools/__tests__/find-activity.test.d.ts.map +0 -1
  89. package/dist/tools/__tests__/find-activity.test.js +0 -229
  90. package/dist/tools/__tests__/find-comments.test.d.ts +0 -2
  91. package/dist/tools/__tests__/find-comments.test.d.ts.map +0 -1
  92. package/dist/tools/__tests__/find-comments.test.js +0 -236
  93. package/dist/tools/__tests__/find-completed-tasks.test.d.ts +0 -2
  94. package/dist/tools/__tests__/find-completed-tasks.test.d.ts.map +0 -1
  95. package/dist/tools/__tests__/find-completed-tasks.test.js +0 -324
  96. package/dist/tools/__tests__/find-projects.test.d.ts +0 -2
  97. package/dist/tools/__tests__/find-projects.test.d.ts.map +0 -1
  98. package/dist/tools/__tests__/find-projects.test.js +0 -154
  99. package/dist/tools/__tests__/find-sections.test.d.ts +0 -2
  100. package/dist/tools/__tests__/find-sections.test.d.ts.map +0 -1
  101. package/dist/tools/__tests__/find-sections.test.js +0 -245
  102. package/dist/tools/__tests__/find-tasks-by-date.test.d.ts +0 -2
  103. package/dist/tools/__tests__/find-tasks-by-date.test.d.ts.map +0 -1
  104. package/dist/tools/__tests__/find-tasks-by-date.test.js +0 -528
  105. package/dist/tools/__tests__/find-tasks.test.d.ts +0 -2
  106. package/dist/tools/__tests__/find-tasks.test.d.ts.map +0 -1
  107. package/dist/tools/__tests__/find-tasks.test.js +0 -771
  108. package/dist/tools/__tests__/get-overview.test.d.ts +0 -2
  109. package/dist/tools/__tests__/get-overview.test.d.ts.map +0 -1
  110. package/dist/tools/__tests__/get-overview.test.js +0 -225
  111. package/dist/tools/__tests__/search.test.d.ts +0 -2
  112. package/dist/tools/__tests__/search.test.d.ts.map +0 -1
  113. package/dist/tools/__tests__/search.test.js +0 -206
  114. package/dist/tools/__tests__/update-comments.test.d.ts +0 -2
  115. package/dist/tools/__tests__/update-comments.test.d.ts.map +0 -1
  116. package/dist/tools/__tests__/update-comments.test.js +0 -294
  117. package/dist/tools/__tests__/update-projects.test.d.ts +0 -2
  118. package/dist/tools/__tests__/update-projects.test.d.ts.map +0 -1
  119. package/dist/tools/__tests__/update-projects.test.js +0 -217
  120. package/dist/tools/__tests__/update-sections.test.d.ts +0 -2
  121. package/dist/tools/__tests__/update-sections.test.d.ts.map +0 -1
  122. package/dist/tools/__tests__/update-sections.test.js +0 -169
  123. package/dist/tools/__tests__/update-tasks.test.d.ts +0 -2
  124. package/dist/tools/__tests__/update-tasks.test.d.ts.map +0 -1
  125. package/dist/tools/__tests__/update-tasks.test.js +0 -788
  126. package/dist/tools/__tests__/user-info.test.d.ts +0 -2
  127. package/dist/tools/__tests__/user-info.test.d.ts.map +0 -1
  128. package/dist/tools/__tests__/user-info.test.js +0 -139
  129. package/dist/tools/add-comments.js +0 -79
  130. package/dist/tools/add-projects.js +0 -63
  131. package/dist/tools/add-sections.js +0 -61
  132. package/dist/tools/add-tasks.js +0 -160
  133. package/dist/tools/complete-tasks.js +0 -68
  134. package/dist/tools/delete-object.js +0 -79
  135. package/dist/tools/fetch.js +0 -102
  136. package/dist/tools/find-activity.js +0 -221
  137. package/dist/tools/find-comments.js +0 -143
  138. package/dist/tools/find-completed-tasks.js +0 -161
  139. package/dist/tools/find-project-collaborators.js +0 -151
  140. package/dist/tools/find-projects.js +0 -101
  141. package/dist/tools/find-sections.js +0 -96
  142. package/dist/tools/find-tasks-by-date.js +0 -198
  143. package/dist/tools/find-tasks.js +0 -329
  144. package/dist/tools/get-overview.js +0 -249
  145. package/dist/tools/manage-assignments.js +0 -337
  146. package/dist/tools/search.js +0 -65
  147. package/dist/tools/update-comments.js +0 -82
  148. package/dist/tools/update-projects.js +0 -84
  149. package/dist/tools/update-sections.js +0 -70
  150. package/dist/tools/update-tasks.js +0 -170
  151. package/dist/tools/user-info.js +0 -142
  152. package/dist/utils/assignment-validator.js +0 -253
  153. package/dist/utils/constants.js +0 -45
  154. package/dist/utils/duration-parser.js +0 -96
  155. package/dist/utils/duration-parser.test.d.ts +0 -2
  156. package/dist/utils/duration-parser.test.d.ts.map +0 -1
  157. package/dist/utils/duration-parser.test.js +0 -147
  158. package/dist/utils/labels.js +0 -18
  159. package/dist/utils/priorities.js +0 -20
  160. package/dist/utils/response-builders.js +0 -210
  161. package/dist/utils/sanitize-data.js +0 -37
  162. package/dist/utils/sanitize-data.test.d.ts +0 -2
  163. package/dist/utils/sanitize-data.test.d.ts.map +0 -1
  164. package/dist/utils/sanitize-data.test.js +0 -93
  165. package/dist/utils/test-helpers.js +0 -237
  166. package/dist/utils/tool-names.js +0 -40
  167. package/dist/utils/user-resolver.js +0 -179
@@ -1,151 +0,0 @@
1
- import { z } from 'zod';
2
- import { getToolOutput } from '../mcp-helpers.js';
3
- import { summarizeList } from '../utils/response-builders.js';
4
- import { ToolNames } from '../utils/tool-names.js';
5
- import { userResolver } from '../utils/user-resolver.js';
6
- const { FIND_PROJECTS, ADD_TASKS, UPDATE_TASKS } = ToolNames;
7
- const ArgsSchema = {
8
- projectId: z.string().min(1).describe('The ID of the project to search for collaborators in.'),
9
- searchTerm: z
10
- .string()
11
- .optional()
12
- .describe('Search for a collaborator by name or email (partial and case insensitive match). If omitted, all collaborators in the project are returned.'),
13
- };
14
- const findProjectCollaborators = {
15
- name: ToolNames.FIND_PROJECT_COLLABORATORS,
16
- description: 'Search for collaborators by name or other criteria in a project.',
17
- parameters: ArgsSchema,
18
- async execute(args, client) {
19
- const { projectId, searchTerm } = args;
20
- // First, validate that the project exists and get basic info
21
- let projectName = projectId;
22
- let project;
23
- try {
24
- project = await client.getProject(projectId);
25
- if (!project) {
26
- throw new Error(`Project with ID "${projectId}" not found or not accessible`);
27
- }
28
- projectName = project.name;
29
- if (!project.isShared) {
30
- const textContent = `Project "${projectName}" is not shared and has no collaborators.\n\n**Next steps:**\n• Share the project to enable collaboration\n• Use ${ADD_TASKS} and ${UPDATE_TASKS} for assignment features once shared`;
31
- return getToolOutput({
32
- textContent,
33
- structuredContent: {
34
- collaborators: [],
35
- projectInfo: {
36
- id: projectId,
37
- name: projectName,
38
- isShared: false,
39
- },
40
- totalCount: 0,
41
- appliedFilters: args,
42
- },
43
- });
44
- }
45
- }
46
- catch (error) {
47
- throw new Error(`Failed to access project "${projectId}": ${error instanceof Error ? error.message : 'Unknown error'}`);
48
- }
49
- // Get collaborators for the project
50
- const allCollaborators = await userResolver.getProjectCollaborators(client, projectId);
51
- if (allCollaborators.length === 0) {
52
- const textContent = `Project "${projectName}" has no collaborators or collaborator data is not accessible.\n\n**Next steps:**\n• Check project sharing settings\n• Ensure you have permission to view collaborators\n• Try refreshing or re-sharing the project`;
53
- return getToolOutput({
54
- textContent,
55
- structuredContent: {
56
- collaborators: [],
57
- projectInfo: {
58
- id: projectId,
59
- name: projectName,
60
- isShared: true,
61
- },
62
- totalCount: 0,
63
- appliedFilters: args,
64
- },
65
- });
66
- }
67
- // Filter collaborators if search term provided
68
- let filteredCollaborators = allCollaborators;
69
- if (searchTerm) {
70
- const searchLower = searchTerm.toLowerCase().trim();
71
- filteredCollaborators = allCollaborators.filter((collaborator) => collaborator.name.toLowerCase().includes(searchLower) ||
72
- collaborator.email.toLowerCase().includes(searchLower));
73
- }
74
- const textContent = generateTextContent({
75
- collaborators: filteredCollaborators,
76
- projectName,
77
- searchTerm,
78
- totalAvailable: allCollaborators.length,
79
- });
80
- return getToolOutput({
81
- textContent,
82
- structuredContent: {
83
- collaborators: filteredCollaborators,
84
- projectInfo: {
85
- id: projectId,
86
- name: projectName,
87
- isShared: true,
88
- },
89
- totalCount: filteredCollaborators.length,
90
- totalAvailable: allCollaborators.length,
91
- appliedFilters: args,
92
- },
93
- });
94
- },
95
- };
96
- function generateTextContent({ collaborators, projectName, searchTerm, totalAvailable, }) {
97
- const subject = searchTerm
98
- ? `Project collaborators matching "${searchTerm}"`
99
- : 'Project collaborators';
100
- const filterHints = [];
101
- if (searchTerm) {
102
- filterHints.push(`matching "${searchTerm}"`);
103
- }
104
- filterHints.push(`in project "${projectName}"`);
105
- let previewLines = [];
106
- if (collaborators.length > 0) {
107
- previewLines = collaborators.slice(0, 10).map((collaborator) => {
108
- const displayName = collaborator.name || 'Unknown Name';
109
- const email = collaborator.email || 'No email';
110
- return `• ${displayName} (${email}) - ID: ${collaborator.id}`;
111
- });
112
- if (collaborators.length > 10) {
113
- previewLines.push(`... and ${collaborators.length - 10} more`);
114
- }
115
- }
116
- const zeroReasonHints = [];
117
- if (collaborators.length === 0) {
118
- if (searchTerm) {
119
- zeroReasonHints.push(`No collaborators match "${searchTerm}"`);
120
- zeroReasonHints.push('Try a broader search term or check spelling');
121
- if (totalAvailable > 0) {
122
- zeroReasonHints.push(`${totalAvailable} collaborators available without filter`);
123
- }
124
- }
125
- else {
126
- zeroReasonHints.push('Project has no collaborators');
127
- zeroReasonHints.push('Share the project to add collaborators');
128
- }
129
- }
130
- const nextSteps = [];
131
- if (collaborators.length > 0) {
132
- nextSteps.push(`Use ${ADD_TASKS} with responsibleUser to assign new tasks`);
133
- nextSteps.push(`Use ${UPDATE_TASKS} with responsibleUser to reassign existing tasks`);
134
- nextSteps.push('Use collaborator names, emails, or IDs for assignments');
135
- }
136
- else {
137
- nextSteps.push(`Use ${FIND_PROJECTS} to find other projects`);
138
- if (searchTerm && totalAvailable > 0) {
139
- nextSteps.push('Try searching without filters to see all collaborators');
140
- }
141
- }
142
- return summarizeList({
143
- subject,
144
- count: collaborators.length,
145
- filterHints,
146
- previewLines: previewLines.join('\n'),
147
- zeroReasonHints,
148
- nextSteps,
149
- });
150
- }
151
- export { findProjectCollaborators };
@@ -1,101 +0,0 @@
1
- import { z } from 'zod';
2
- import { getToolOutput } from '../mcp-helpers.js';
3
- import { mapProject } from '../tool-helpers.js';
4
- import { ApiLimits } from '../utils/constants.js';
5
- import { formatProjectPreview, summarizeList } from '../utils/response-builders.js';
6
- import { ToolNames } from '../utils/tool-names.js';
7
- const { ADD_PROJECTS, FIND_TASKS } = ToolNames;
8
- const ArgsSchema = {
9
- search: z
10
- .string()
11
- .optional()
12
- .describe('Search for a project by name (partial and case insensitive match). If omitted, all projects are returned.'),
13
- limit: z
14
- .number()
15
- .int()
16
- .min(1)
17
- .max(ApiLimits.PROJECTS_MAX)
18
- .default(ApiLimits.PROJECTS_DEFAULT)
19
- .describe('The maximum number of projects to return.'),
20
- cursor: z
21
- .string()
22
- .optional()
23
- .describe('The cursor to get the next page of projects (cursor is obtained from the previous call to this tool, with the same parameters).'),
24
- };
25
- const findProjects = {
26
- name: ToolNames.FIND_PROJECTS,
27
- description: 'List all projects or search for projects by name. If search parameter is omitted, all projects are returned.',
28
- parameters: ArgsSchema,
29
- async execute(args, client) {
30
- const { results, nextCursor } = await client.getProjects({
31
- limit: args.limit,
32
- cursor: args.cursor ?? null,
33
- });
34
- const searchLower = args.search ? args.search.toLowerCase() : undefined;
35
- const filtered = searchLower
36
- ? results.filter((project) => project.name.toLowerCase().includes(searchLower))
37
- : results;
38
- const projects = filtered.map(mapProject);
39
- return getToolOutput({
40
- textContent: generateTextContent({
41
- projects,
42
- args,
43
- nextCursor,
44
- }),
45
- structuredContent: {
46
- projects,
47
- nextCursor,
48
- totalCount: projects.length,
49
- hasMore: Boolean(nextCursor),
50
- appliedFilters: args,
51
- },
52
- });
53
- },
54
- };
55
- function generateTextContent({ projects, args, nextCursor, }) {
56
- // Generate subject description
57
- const subject = args.search ? `Projects matching "${args.search}"` : 'Projects';
58
- // Generate filter hints
59
- const filterHints = [];
60
- if (args.search) {
61
- filterHints.push(`search: "${args.search}"`);
62
- }
63
- // Generate project preview lines
64
- const previewLimit = 10;
65
- const previewProjects = projects.slice(0, previewLimit);
66
- const previewLines = previewProjects.map(formatProjectPreview).join('\n');
67
- const remainingCount = projects.length - previewLimit;
68
- const previewWithMore = remainingCount > 0 ? `${previewLines}\n …and ${remainingCount} more` : previewLines;
69
- // Generate helpful suggestions for empty results
70
- const zeroReasonHints = [];
71
- if (projects.length === 0) {
72
- if (args.search) {
73
- zeroReasonHints.push('Try broader search terms');
74
- zeroReasonHints.push('Check spelling');
75
- zeroReasonHints.push('Remove search to see all projects');
76
- }
77
- else {
78
- zeroReasonHints.push('No projects created yet');
79
- zeroReasonHints.push(`Use ${ADD_PROJECTS} to create a project`);
80
- }
81
- }
82
- // Generate contextual next steps
83
- const nextSteps = [];
84
- if (projects.length > 0) {
85
- nextSteps.push(`Use ${FIND_TASKS} with projectId to see tasks in specific projects.`);
86
- if (projects.some((p) => p.isFavorite)) {
87
- nextSteps.push('Favorite projects appear first in most Todoist views.');
88
- }
89
- }
90
- return summarizeList({
91
- subject,
92
- count: projects.length,
93
- limit: args.limit,
94
- nextCursor: nextCursor ?? undefined,
95
- filterHints,
96
- previewLines: previewWithMore,
97
- zeroReasonHints,
98
- nextSteps,
99
- });
100
- }
101
- export { findProjects };
@@ -1,96 +0,0 @@
1
- import { z } from 'zod';
2
- import { getToolOutput } from '../mcp-helpers.js';
3
- import { summarizeList } from '../utils/response-builders.js';
4
- import { ToolNames } from '../utils/tool-names.js';
5
- const { ADD_SECTIONS, UPDATE_SECTIONS, FIND_TASKS, UPDATE_TASKS, DELETE_OBJECT } = ToolNames;
6
- const ArgsSchema = {
7
- projectId: z.string().min(1).describe('The ID of the project to search sections in.'),
8
- search: z
9
- .string()
10
- .optional()
11
- .describe('Search for a section by name (partial and case insensitive match). If omitted, all sections in the project are returned.'),
12
- };
13
- const findSections = {
14
- name: ToolNames.FIND_SECTIONS,
15
- description: 'Search for sections by name or other criteria in a project.',
16
- parameters: ArgsSchema,
17
- async execute(args, client) {
18
- const { results } = await client.getSections({
19
- projectId: args.projectId,
20
- });
21
- const searchLower = args.search ? args.search.toLowerCase() : undefined;
22
- const filtered = searchLower
23
- ? results.filter((section) => section.name.toLowerCase().includes(searchLower))
24
- : results;
25
- const sections = filtered.map((section) => ({
26
- id: section.id,
27
- name: section.name,
28
- }));
29
- const textContent = generateTextContent({
30
- sections,
31
- projectId: args.projectId,
32
- search: args.search,
33
- });
34
- return getToolOutput({
35
- textContent,
36
- structuredContent: {
37
- sections,
38
- totalCount: sections.length,
39
- appliedFilters: args,
40
- },
41
- });
42
- },
43
- };
44
- function generateTextContent({ sections, projectId, search, }) {
45
- const zeroReasonHints = [];
46
- if (search) {
47
- zeroReasonHints.push('Try broader search terms');
48
- zeroReasonHints.push('Check spelling');
49
- zeroReasonHints.push('Remove search to see all sections');
50
- }
51
- else {
52
- zeroReasonHints.push('Project has no sections yet');
53
- zeroReasonHints.push(`Use ${ADD_SECTIONS} to create sections`);
54
- }
55
- // Data-driven next steps based on results
56
- const nextSteps = [];
57
- if (sections.length > 0) {
58
- // Suggestions based on number of sections found
59
- if (sections.length === 1) {
60
- const sectionId = sections[0]?.id;
61
- nextSteps.push(`Use ${FIND_TASKS} with sectionId=${sectionId} to see tasks`);
62
- nextSteps.push(`Use ${ADD_SECTIONS} to create additional sections for organization`);
63
- }
64
- else if (sections.length > 8) {
65
- nextSteps.push('Consider consolidating sections - many small sections can reduce productivity');
66
- nextSteps.push(`Use ${UPDATE_TASKS} to move tasks between sections`);
67
- nextSteps.push(`Use ${DELETE_OBJECT} with type=section to delete empty sections`);
68
- }
69
- else {
70
- nextSteps.push(`Use ${FIND_TASKS} with sectionId to see tasks in specific sections`);
71
- nextSteps.push(`Use ${UPDATE_SECTIONS} to modify section names`);
72
- }
73
- // Search-specific suggestions
74
- if (search) {
75
- nextSteps.push('Remove search parameter to see all sections in this project');
76
- }
77
- }
78
- else {
79
- // Empty result suggestions are already handled in zeroReasonHints
80
- // No additional nextSteps needed for empty results
81
- }
82
- const subject = search
83
- ? `Sections in project ${projectId} matching "${search}"`
84
- : `Sections in project ${projectId}`;
85
- const previewLines = sections.length > 0
86
- ? sections.map((section) => ` ${section.name} • id=${section.id}`).join('\n')
87
- : undefined;
88
- return summarizeList({
89
- subject,
90
- count: sections.length,
91
- previewLines,
92
- zeroReasonHints,
93
- nextSteps,
94
- });
95
- }
96
- export { findSections };
@@ -1,198 +0,0 @@
1
- import { addDays, formatISO } from 'date-fns';
2
- import { z } from 'zod';
3
- import { appendToQuery, buildResponsibleUserQueryFilter, RESPONSIBLE_USER_FILTERING, resolveResponsibleUser, } from '../filter-helpers.js';
4
- import { getToolOutput } from '../mcp-helpers.js';
5
- import { getTasksByFilter } from '../tool-helpers.js';
6
- import { ApiLimits } from '../utils/constants.js';
7
- import { generateLabelsFilter, LabelsSchema } from '../utils/labels.js';
8
- import { generateTaskNextSteps, getDateString, previewTasks, summarizeList, } from '../utils/response-builders.js';
9
- import { ToolNames } from '../utils/tool-names.js';
10
- const ArgsSchema = {
11
- startDate: z
12
- .string()
13
- .regex(/^(\d{4}-\d{2}-\d{2}|today)$/)
14
- .optional()
15
- .describe("The start date to get the tasks for. Format: YYYY-MM-DD or 'today'."),
16
- overdueOption: z
17
- .enum(['overdue-only', 'include-overdue', 'exclude-overdue'])
18
- .optional()
19
- .describe("How to handle overdue tasks. 'overdue-only' to get only overdue tasks, 'include-overdue' to include overdue tasks along with tasks for the specified date(s), and 'exclude-overdue' to exclude overdue tasks. Default is 'include-overdue'."),
20
- daysCount: z
21
- .number()
22
- .int()
23
- .min(1)
24
- .max(30)
25
- .default(1)
26
- .describe('The number of days to get the tasks for, starting from the start date. Default is 1 which means only tasks for the start date.'),
27
- limit: z
28
- .number()
29
- .int()
30
- .min(1)
31
- .max(ApiLimits.TASKS_MAX)
32
- .default(ApiLimits.TASKS_DEFAULT)
33
- .describe('The maximum number of tasks to return.'),
34
- cursor: z
35
- .string()
36
- .optional()
37
- .describe('The cursor to get the next page of tasks (cursor is obtained from the previous call to this tool, with the same parameters).'),
38
- responsibleUser: z
39
- .string()
40
- .optional()
41
- .describe('Find tasks assigned to this user. Can be a user ID, name, or email address.'),
42
- responsibleUserFiltering: z
43
- .enum(RESPONSIBLE_USER_FILTERING)
44
- .optional()
45
- .describe('How to filter by responsible user when responsibleUser is not provided. "assigned" = only tasks assigned to others; "unassignedOrMe" = only unassigned tasks or tasks assigned to me; "all" = all tasks regardless of assignment. Default is "unassignedOrMe".'),
46
- ...LabelsSchema,
47
- };
48
- const findTasksByDate = {
49
- name: ToolNames.FIND_TASKS_BY_DATE,
50
- description: "Get tasks by date range. Use startDate 'today' to get today's tasks including overdue items, or provide a specific date/date range.",
51
- parameters: ArgsSchema,
52
- async execute(args, client) {
53
- if (!args.startDate && args.overdueOption !== 'overdue-only') {
54
- throw new Error('Either startDate must be provided or overdueOption must be set to overdue-only');
55
- }
56
- // Resolve assignee name to user ID if provided
57
- const resolved = await resolveResponsibleUser(client, args.responsibleUser);
58
- const resolvedAssigneeId = resolved?.userId;
59
- const assigneeEmail = resolved?.email;
60
- let query = '';
61
- if (args.overdueOption === 'overdue-only') {
62
- query = 'overdue';
63
- }
64
- else if (args.startDate === 'today') {
65
- // For 'today', include overdue unless explicitly excluded
66
- // Use parentheses to ensure correct operator precedence when combining with other filters
67
- query = args.overdueOption === 'exclude-overdue' ? 'today' : '(today | overdue)';
68
- }
69
- else if (args.startDate) {
70
- // For specific dates, never include overdue tasks
71
- const startDate = args.startDate;
72
- const endDate = addDays(startDate, args.daysCount);
73
- const endDateStr = formatISO(endDate, { representation: 'date' });
74
- query = `(due after: ${startDate} | due: ${startDate}) & due before: ${endDateStr}`;
75
- }
76
- // Add labels filter
77
- const labelsFilter = generateLabelsFilter(args.labels, args.labelsOperator);
78
- if (labelsFilter.length > 0) {
79
- query = appendToQuery(query, `(${labelsFilter})`);
80
- }
81
- // Add responsible user filtering to the query (backend filtering)
82
- const responsibleUserFilter = buildResponsibleUserQueryFilter({
83
- resolvedAssigneeId,
84
- assigneeEmail,
85
- responsibleUserFiltering: args.responsibleUserFiltering,
86
- });
87
- query = appendToQuery(query, responsibleUserFilter);
88
- const result = await getTasksByFilter({
89
- client,
90
- query,
91
- cursor: args.cursor,
92
- limit: args.limit,
93
- });
94
- // No need for post-fetch filtering since it's handled in the query
95
- const filteredTasks = result.tasks;
96
- const textContent = generateTextContent({
97
- tasks: filteredTasks,
98
- args,
99
- nextCursor: result.nextCursor,
100
- assigneeEmail,
101
- });
102
- return getToolOutput({
103
- textContent,
104
- structuredContent: {
105
- tasks: filteredTasks,
106
- nextCursor: result.nextCursor,
107
- totalCount: filteredTasks.length,
108
- hasMore: Boolean(result.nextCursor),
109
- appliedFilters: args,
110
- },
111
- });
112
- },
113
- };
114
- function generateTextContent({ tasks, args, nextCursor, assigneeEmail, }) {
115
- // Generate filter description
116
- const filterHints = [];
117
- if (args.overdueOption === 'overdue-only') {
118
- filterHints.push('overdue tasks only');
119
- }
120
- else if (args.startDate === 'today') {
121
- const overdueText = args.overdueOption === 'exclude-overdue' ? '' : ' + overdue tasks';
122
- filterHints.push(`today${overdueText}${args.daysCount > 1 ? ` + ${args.daysCount - 1} more days` : ''}`);
123
- }
124
- else if (args.startDate) {
125
- const dateRange = args.daysCount > 1
126
- ? ` to ${getDateString(addDays(args.startDate, args.daysCount))}`
127
- : '';
128
- filterHints.push(`${args.startDate}${dateRange}`);
129
- }
130
- // Add label filter information
131
- if (args.labels && args.labels.length > 0) {
132
- const labelText = args.labels
133
- .map((label) => `@${label}`)
134
- .join(args.labelsOperator === 'and' ? ' & ' : ' | ');
135
- filterHints.push(`labels: ${labelText}`);
136
- }
137
- // Add responsible user filter information
138
- if (args.responsibleUser) {
139
- const email = assigneeEmail || args.responsibleUser;
140
- filterHints.push(`assigned to: ${email}`);
141
- }
142
- // Generate subject description
143
- let subject = '';
144
- if (args.overdueOption === 'overdue-only') {
145
- subject = 'Overdue tasks';
146
- }
147
- else if (args.startDate === 'today') {
148
- subject =
149
- args.overdueOption === 'exclude-overdue' ? `Today's tasks` : `Today's tasks + overdue`;
150
- }
151
- else if (args.startDate) {
152
- subject = `Tasks for ${args.startDate}`;
153
- }
154
- else {
155
- subject = 'Tasks';
156
- }
157
- // Append responsible user to subject if provided
158
- if (args.responsibleUser) {
159
- const email = assigneeEmail || args.responsibleUser;
160
- subject += ` assigned to ${email}`;
161
- }
162
- // Generate helpful suggestions for empty results
163
- const zeroReasonHints = [];
164
- if (tasks.length === 0) {
165
- if (args.overdueOption === 'overdue-only') {
166
- zeroReasonHints.push('Great job! No overdue tasks');
167
- }
168
- else if (args.startDate === 'today') {
169
- const overdueNote = args.overdueOption === 'exclude-overdue' ? '' : ' or overdue';
170
- zeroReasonHints.push(`Great job! No tasks for today${overdueNote}`);
171
- }
172
- else {
173
- zeroReasonHints.push("Expand date range with larger 'daysCount'");
174
- zeroReasonHints.push("Check today's tasks with startDate='today'");
175
- }
176
- }
177
- // Generate contextual next steps
178
- const now = new Date();
179
- const todayStr = getDateString(now);
180
- const hasOverdue = args.overdueOption === 'overdue-only' ||
181
- args.startDate === 'today' ||
182
- tasks.some((task) => task.dueDate && new Date(task.dueDate) < now);
183
- const nextSteps = generateTaskNextSteps('listed', tasks, {
184
- hasToday: args.startDate === 'today' || tasks.some((task) => task.dueDate === todayStr),
185
- hasOverdue,
186
- });
187
- return summarizeList({
188
- subject,
189
- count: tasks.length,
190
- limit: args.limit,
191
- nextCursor: nextCursor ?? undefined,
192
- filterHints,
193
- previewLines: previewTasks(tasks, Math.min(tasks.length, args.limit)),
194
- zeroReasonHints,
195
- nextSteps,
196
- });
197
- }
198
- export { findTasksByDate };