@doist/todoist-ai 3.0.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -18
- package/dist/index.d.ts +175 -47
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -6
- package/dist/main.js +2 -1
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +16 -4
- package/dist/tools/__tests__/add-comments.test.d.ts +2 -0
- package/dist/tools/__tests__/add-comments.test.d.ts.map +1 -0
- package/dist/tools/__tests__/add-comments.test.js +241 -0
- package/dist/tools/__tests__/add-projects.test.d.ts +2 -0
- package/dist/tools/__tests__/add-projects.test.d.ts.map +1 -0
- package/dist/tools/__tests__/add-projects.test.js +152 -0
- package/dist/tools/__tests__/add-sections.test.d.ts +2 -0
- package/dist/tools/__tests__/add-sections.test.d.ts.map +1 -0
- package/dist/tools/__tests__/add-sections.test.js +181 -0
- package/dist/tools/__tests__/add-tasks.test.js +16 -10
- package/dist/tools/__tests__/find-comments.test.d.ts +2 -0
- package/dist/tools/__tests__/find-comments.test.d.ts.map +1 -0
- package/dist/tools/__tests__/find-comments.test.js +242 -0
- package/dist/tools/__tests__/find-sections.test.js +2 -2
- package/dist/tools/__tests__/update-comments.test.d.ts +2 -0
- package/dist/tools/__tests__/update-comments.test.d.ts.map +1 -0
- package/dist/tools/__tests__/update-comments.test.js +296 -0
- package/dist/tools/__tests__/update-projects.test.d.ts +2 -0
- package/dist/tools/__tests__/update-projects.test.d.ts.map +1 -0
- package/dist/tools/__tests__/update-projects.test.js +205 -0
- package/dist/tools/__tests__/update-sections.test.d.ts +2 -0
- package/dist/tools/__tests__/update-sections.test.d.ts.map +1 -0
- package/dist/tools/__tests__/update-sections.test.js +156 -0
- package/dist/tools/add-comments.d.ts +51 -0
- package/dist/tools/add-comments.d.ts.map +1 -0
- package/dist/tools/add-comments.js +79 -0
- package/dist/tools/add-projects.d.ts +50 -0
- package/dist/tools/add-projects.d.ts.map +1 -0
- package/dist/tools/add-projects.js +59 -0
- package/dist/tools/{manage-sections.d.ts → add-sections.d.ts} +21 -13
- package/dist/tools/add-sections.d.ts.map +1 -0
- package/dist/tools/add-sections.js +61 -0
- package/dist/tools/add-tasks.d.ts +15 -8
- package/dist/tools/add-tasks.d.ts.map +1 -1
- package/dist/tools/add-tasks.js +46 -37
- package/dist/tools/delete-object.d.ts +3 -3
- package/dist/tools/delete-object.d.ts.map +1 -1
- package/dist/tools/delete-object.js +13 -3
- package/dist/tools/find-comments.d.ts +46 -0
- package/dist/tools/find-comments.d.ts.map +1 -0
- package/dist/tools/find-comments.js +143 -0
- package/dist/tools/find-projects.js +2 -2
- package/dist/tools/find-sections.js +4 -4
- package/dist/tools/update-comments.d.ts +50 -0
- package/dist/tools/update-comments.d.ts.map +1 -0
- package/dist/tools/update-comments.js +82 -0
- package/dist/tools/update-projects.d.ts +59 -0
- package/dist/tools/update-projects.d.ts.map +1 -0
- package/dist/tools/update-projects.js +84 -0
- package/dist/tools/update-sections.d.ts +47 -0
- package/dist/tools/update-sections.d.ts.map +1 -0
- package/dist/tools/update-sections.js +70 -0
- 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 +7 -2
- package/dist/utils/tool-names.d.ts.map +1 -1
- package/dist/utils/tool-names.js +8 -2
- package/package.json +1 -1
- package/dist/tools/__tests__/manage-projects.test.d.ts +0 -2
- package/dist/tools/__tests__/manage-projects.test.d.ts.map +0 -1
- package/dist/tools/__tests__/manage-projects.test.js +0 -109
- package/dist/tools/__tests__/manage-sections.test.d.ts +0 -2
- package/dist/tools/__tests__/manage-sections.test.d.ts.map +0 -1
- package/dist/tools/__tests__/manage-sections.test.js +0 -162
- package/dist/tools/manage-projects.d.ts +0 -35
- package/dist/tools/manage-projects.d.ts.map +0 -1
- package/dist/tools/manage-projects.js +0 -63
- package/dist/tools/manage-sections.d.ts.map +0 -1
- package/dist/tools/manage-sections.js +0 -78
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"add-sections.d.ts","sourceRoot":"","sources":["../../src/tools/add-sections.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAA;AAC5D,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAiBvB,QAAA,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgByB,CAAA;AAiD1C,OAAO,EAAE,WAAW,EAAE,CAAA"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { getToolOutput } from '../mcp-helpers.js';
|
|
3
|
+
import { formatNextSteps } from '../utils/response-builders.js';
|
|
4
|
+
import { ToolNames } from '../utils/tool-names.js';
|
|
5
|
+
const { ADD_TASKS, FIND_TASKS, GET_OVERVIEW, FIND_SECTIONS } = ToolNames;
|
|
6
|
+
const SectionSchema = z.object({
|
|
7
|
+
name: z.string().min(1).describe('The name of the section.'),
|
|
8
|
+
projectId: z.string().min(1).describe('The ID of the project to add the section to.'),
|
|
9
|
+
});
|
|
10
|
+
const ArgsSchema = {
|
|
11
|
+
sections: z.array(SectionSchema).min(1).describe('The array of sections to add.'),
|
|
12
|
+
};
|
|
13
|
+
const addSections = {
|
|
14
|
+
name: ToolNames.ADD_SECTIONS,
|
|
15
|
+
description: 'Add one or more new sections to projects.',
|
|
16
|
+
parameters: ArgsSchema,
|
|
17
|
+
async execute({ sections }, client) {
|
|
18
|
+
const newSections = await Promise.all(sections.map((section) => client.addSection(section)));
|
|
19
|
+
const textContent = generateTextContent({ sections: newSections });
|
|
20
|
+
return getToolOutput({
|
|
21
|
+
textContent,
|
|
22
|
+
structuredContent: {
|
|
23
|
+
sections: newSections,
|
|
24
|
+
totalCount: newSections.length,
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
function generateTextContent({ sections, }) {
|
|
30
|
+
const count = sections.length;
|
|
31
|
+
const sectionList = sections
|
|
32
|
+
.map((section) => `• ${section.name} (id=${section.id}, projectId=${section.projectId})`)
|
|
33
|
+
.join('\n');
|
|
34
|
+
const summary = `Added ${count} section${count === 1 ? '' : 's'}:\n${sectionList}`;
|
|
35
|
+
// Context-aware next steps for new sections
|
|
36
|
+
const nextSteps = [];
|
|
37
|
+
if (count === 1) {
|
|
38
|
+
const section = sections[0];
|
|
39
|
+
if (section) {
|
|
40
|
+
nextSteps.push(`Use ${ADD_TASKS} with sectionId=${section.id} to add your first tasks`);
|
|
41
|
+
nextSteps.push(`Use ${FIND_TASKS} with sectionId=${section.id} to verify setup`);
|
|
42
|
+
nextSteps.push(`Use ${GET_OVERVIEW} with projectId=${section.projectId} to see project organization`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
// Group sections by project for better guidance
|
|
47
|
+
const projectIds = [...new Set(sections.map((s) => s.projectId))];
|
|
48
|
+
nextSteps.push(`Use ${ADD_TASKS} to add tasks to these new sections`);
|
|
49
|
+
if (projectIds.length === 1) {
|
|
50
|
+
nextSteps.push(`Use ${GET_OVERVIEW} with projectId=${projectIds[0]} to see updated project structure`);
|
|
51
|
+
nextSteps.push(`Use ${FIND_SECTIONS} with projectId=${projectIds[0]} to see all sections`);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
nextSteps.push(`Use ${GET_OVERVIEW} to see updated project structures`);
|
|
55
|
+
nextSteps.push(`Use ${FIND_SECTIONS} to see sections in specific projects`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const next = formatNextSteps(nextSteps);
|
|
59
|
+
return `${summary}\n${next}`;
|
|
60
|
+
}
|
|
61
|
+
export { addSections };
|
|
@@ -1,43 +1,50 @@
|
|
|
1
|
+
import type { TodoistApi } from '@doist/todoist-api-typescript';
|
|
1
2
|
import { z } from 'zod';
|
|
2
3
|
declare const addTasks: {
|
|
3
4
|
name: "add-tasks";
|
|
4
5
|
description: string;
|
|
5
6
|
parameters: {
|
|
6
|
-
projectId: z.ZodOptional<z.ZodString>;
|
|
7
|
-
sectionId: z.ZodOptional<z.ZodString>;
|
|
8
|
-
parentId: z.ZodOptional<z.ZodString>;
|
|
9
7
|
tasks: z.ZodArray<z.ZodObject<{
|
|
10
8
|
content: z.ZodString;
|
|
11
9
|
description: z.ZodOptional<z.ZodString>;
|
|
12
10
|
priority: z.ZodOptional<z.ZodNumber>;
|
|
13
11
|
dueString: z.ZodOptional<z.ZodString>;
|
|
14
12
|
duration: z.ZodOptional<z.ZodString>;
|
|
13
|
+
projectId: z.ZodOptional<z.ZodString>;
|
|
14
|
+
sectionId: z.ZodOptional<z.ZodString>;
|
|
15
|
+
parentId: z.ZodOptional<z.ZodString>;
|
|
15
16
|
}, "strip", z.ZodTypeAny, {
|
|
16
17
|
content: string;
|
|
17
18
|
description?: string | undefined;
|
|
19
|
+
parentId?: string | undefined;
|
|
20
|
+
projectId?: string | undefined;
|
|
21
|
+
sectionId?: string | undefined;
|
|
18
22
|
priority?: number | undefined;
|
|
19
23
|
dueString?: string | undefined;
|
|
20
24
|
duration?: string | undefined;
|
|
21
25
|
}, {
|
|
22
26
|
content: string;
|
|
23
27
|
description?: string | undefined;
|
|
28
|
+
parentId?: string | undefined;
|
|
29
|
+
projectId?: string | undefined;
|
|
30
|
+
sectionId?: string | undefined;
|
|
24
31
|
priority?: number | undefined;
|
|
25
32
|
dueString?: string | undefined;
|
|
26
33
|
duration?: string | undefined;
|
|
27
34
|
}>, "many">;
|
|
28
35
|
};
|
|
29
|
-
execute(
|
|
36
|
+
execute({ tasks }: {
|
|
30
37
|
tasks: {
|
|
31
38
|
content: string;
|
|
32
39
|
description?: string | undefined;
|
|
40
|
+
parentId?: string | undefined;
|
|
41
|
+
projectId?: string | undefined;
|
|
42
|
+
sectionId?: string | undefined;
|
|
33
43
|
priority?: number | undefined;
|
|
34
44
|
dueString?: string | undefined;
|
|
35
45
|
duration?: string | undefined;
|
|
36
46
|
}[];
|
|
37
|
-
|
|
38
|
-
projectId?: string | undefined;
|
|
39
|
-
sectionId?: string | undefined;
|
|
40
|
-
}, client: import("@doist/todoist-api-typescript").TodoistApi): Promise<{
|
|
47
|
+
}, client: TodoistApi): Promise<{
|
|
41
48
|
content: {
|
|
42
49
|
type: "text";
|
|
43
50
|
text: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"add-tasks.d.ts","sourceRoot":"","sources":["../../src/tools/add-tasks.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"add-tasks.d.ts","sourceRoot":"","sources":["../../src/tools/add-tasks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAqB,UAAU,EAAE,MAAM,+BAA+B,CAAA;AAClF,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAgCvB,QAAA,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsB4B,CAAA;AA8D1C,OAAO,EAAE,QAAQ,EAAE,CAAA"}
|
package/dist/tools/add-tasks.js
CHANGED
|
@@ -13,46 +13,24 @@ const TaskSchema = z.object({
|
|
|
13
13
|
.string()
|
|
14
14
|
.optional()
|
|
15
15
|
.describe('The duration of the task. Use format: "2h" (hours), "90m" (minutes), "2h30m" (combined), or "1.5h" (decimal hours). Max 24h.'),
|
|
16
|
+
projectId: z.string().optional().describe('The project ID to add this task to.'),
|
|
17
|
+
sectionId: z.string().optional().describe('The section ID to add this task to.'),
|
|
18
|
+
parentId: z.string().optional().describe('The parent task ID (for subtasks).'),
|
|
16
19
|
});
|
|
17
20
|
const ArgsSchema = {
|
|
18
|
-
projectId: z.string().optional().describe('The project ID to add the tasks to.'),
|
|
19
|
-
sectionId: z.string().optional().describe('The section ID to add the tasks to.'),
|
|
20
|
-
parentId: z.string().optional().describe('The parent task ID (for subtasks).'),
|
|
21
21
|
tasks: z.array(TaskSchema).min(1).describe('The array of tasks to add.'),
|
|
22
22
|
};
|
|
23
23
|
const addTasks = {
|
|
24
24
|
name: ToolNames.ADD_TASKS,
|
|
25
25
|
description: 'Add one or more tasks to a project, section, or parent.',
|
|
26
26
|
parameters: ArgsSchema,
|
|
27
|
-
async execute(
|
|
28
|
-
const
|
|
29
|
-
const newTasks =
|
|
30
|
-
for (const task of tasks) {
|
|
31
|
-
const { duration: durationStr, ...otherTaskArgs } = task;
|
|
32
|
-
let taskArgs = { ...otherTaskArgs, projectId, sectionId, parentId };
|
|
33
|
-
// Parse duration if provided
|
|
34
|
-
if (durationStr) {
|
|
35
|
-
try {
|
|
36
|
-
const { minutes } = parseDuration(durationStr);
|
|
37
|
-
taskArgs = {
|
|
38
|
-
...taskArgs,
|
|
39
|
-
duration: minutes,
|
|
40
|
-
durationUnit: 'minute',
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
catch (error) {
|
|
44
|
-
if (error instanceof DurationParseError) {
|
|
45
|
-
throw new Error(`Task "${task.content}": ${error.message}`);
|
|
46
|
-
}
|
|
47
|
-
throw error;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
newTasks.push(await client.addTask(taskArgs));
|
|
51
|
-
}
|
|
27
|
+
async execute({ tasks }, client) {
|
|
28
|
+
const addTaskPromises = tasks.map((task) => processTask(task, client));
|
|
29
|
+
const newTasks = await Promise.all(addTaskPromises);
|
|
52
30
|
const mappedTasks = newTasks.map(mapTask);
|
|
53
31
|
const textContent = generateTextContent({
|
|
54
32
|
tasks: mappedTasks,
|
|
55
|
-
args,
|
|
33
|
+
args: { tasks },
|
|
56
34
|
});
|
|
57
35
|
return getToolOutput({
|
|
58
36
|
textContent,
|
|
@@ -63,20 +41,51 @@ const addTasks = {
|
|
|
63
41
|
});
|
|
64
42
|
},
|
|
65
43
|
};
|
|
44
|
+
async function processTask(task, client) {
|
|
45
|
+
const { duration: durationStr, projectId, sectionId, parentId, ...otherTaskArgs } = task;
|
|
46
|
+
let taskArgs = { ...otherTaskArgs, projectId, sectionId, parentId };
|
|
47
|
+
// Parse duration if provided
|
|
48
|
+
if (durationStr) {
|
|
49
|
+
try {
|
|
50
|
+
const { minutes } = parseDuration(durationStr);
|
|
51
|
+
taskArgs = {
|
|
52
|
+
...taskArgs,
|
|
53
|
+
duration: minutes,
|
|
54
|
+
durationUnit: 'minute',
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
if (error instanceof DurationParseError) {
|
|
59
|
+
throw new Error(`Task "${task.content}": ${error.message}`);
|
|
60
|
+
}
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return await client.addTask(taskArgs);
|
|
65
|
+
}
|
|
66
66
|
function generateTextContent({ tasks, args, }) {
|
|
67
67
|
// Get context for smart next steps
|
|
68
68
|
const todayStr = getDateString();
|
|
69
69
|
const hasToday = tasks.some((task) => task.dueDate === todayStr);
|
|
70
|
-
// Generate context description
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
70
|
+
// Generate context description for mixed contexts
|
|
71
|
+
const contextTypes = new Set();
|
|
72
|
+
for (const task of args.tasks) {
|
|
73
|
+
if (task.projectId)
|
|
74
|
+
contextTypes.add('projects');
|
|
75
|
+
else if (task.sectionId)
|
|
76
|
+
contextTypes.add('sections');
|
|
77
|
+
else if (task.parentId)
|
|
78
|
+
contextTypes.add('subtasks');
|
|
79
|
+
else
|
|
80
|
+
contextTypes.add('inbox');
|
|
74
81
|
}
|
|
75
|
-
|
|
76
|
-
|
|
82
|
+
let projectContext = '';
|
|
83
|
+
if (contextTypes.size === 1) {
|
|
84
|
+
const contextType = Array.from(contextTypes)[0];
|
|
85
|
+
projectContext = contextType === 'inbox' ? '' : `to ${contextType}`;
|
|
77
86
|
}
|
|
78
|
-
else if (
|
|
79
|
-
projectContext = '
|
|
87
|
+
else if (contextTypes.size > 1) {
|
|
88
|
+
projectContext = 'to multiple contexts';
|
|
80
89
|
}
|
|
81
90
|
return summarizeTaskOperation('Added', tasks, {
|
|
82
91
|
context: projectContext,
|
|
@@ -3,11 +3,11 @@ declare const deleteObject: {
|
|
|
3
3
|
name: "delete-object";
|
|
4
4
|
description: string;
|
|
5
5
|
parameters: {
|
|
6
|
-
type: z.ZodEnum<["project", "section", "task"]>;
|
|
6
|
+
type: z.ZodEnum<["project", "section", "task", "comment"]>;
|
|
7
7
|
id: z.ZodString;
|
|
8
8
|
};
|
|
9
9
|
execute(args: {
|
|
10
|
-
type: "task" | "project" | "section";
|
|
10
|
+
type: "task" | "comment" | "project" | "section";
|
|
11
11
|
id: string;
|
|
12
12
|
}, client: import("@doist/todoist-api-typescript").TodoistApi): Promise<{
|
|
13
13
|
content: {
|
|
@@ -16,7 +16,7 @@ declare const deleteObject: {
|
|
|
16
16
|
}[];
|
|
17
17
|
structuredContent: {
|
|
18
18
|
deletedEntity: {
|
|
19
|
-
type: "task" | "project" | "section";
|
|
19
|
+
type: "task" | "comment" | "project" | "section";
|
|
20
20
|
id: string;
|
|
21
21
|
};
|
|
22
22
|
success: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"delete-object.d.ts","sourceRoot":"","sources":["../../src/tools/delete-object.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;
|
|
1
|
+
{"version":3,"file":"delete-object.d.ts","sourceRoot":"","sources":["../../src/tools/delete-object.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAsBvB,QAAA,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoCwB,CAAA;AA+C1C,OAAO,EAAE,YAAY,EAAE,CAAA"}
|
|
@@ -2,14 +2,16 @@ import { z } from 'zod';
|
|
|
2
2
|
import { getToolOutput } from '../mcp-helpers.js';
|
|
3
3
|
import { formatNextSteps } from '../utils/response-builders.js';
|
|
4
4
|
import { ToolNames } from '../utils/tool-names.js';
|
|
5
|
-
const { FIND_PROJECTS, GET_OVERVIEW, FIND_SECTIONS, FIND_TASKS, FIND_TASKS_BY_DATE } = ToolNames;
|
|
5
|
+
const { FIND_PROJECTS, GET_OVERVIEW, FIND_SECTIONS, FIND_TASKS, FIND_TASKS_BY_DATE, FIND_COMMENTS, } = ToolNames;
|
|
6
6
|
const ArgsSchema = {
|
|
7
|
-
type: z
|
|
7
|
+
type: z
|
|
8
|
+
.enum(['project', 'section', 'task', 'comment'])
|
|
9
|
+
.describe('The type of entity to delete.'),
|
|
8
10
|
id: z.string().min(1).describe('The ID of the entity to delete.'),
|
|
9
11
|
};
|
|
10
12
|
const deleteObject = {
|
|
11
13
|
name: ToolNames.DELETE_OBJECT,
|
|
12
|
-
description: 'Delete a project, section, or
|
|
14
|
+
description: 'Delete a project, section, task, or comment by its ID.',
|
|
13
15
|
parameters: ArgsSchema,
|
|
14
16
|
async execute(args, client) {
|
|
15
17
|
switch (args.type) {
|
|
@@ -22,6 +24,9 @@ const deleteObject = {
|
|
|
22
24
|
case 'task':
|
|
23
25
|
await client.deleteTask(args.id);
|
|
24
26
|
break;
|
|
27
|
+
case 'comment':
|
|
28
|
+
await client.deleteComment(args.id);
|
|
29
|
+
break;
|
|
25
30
|
}
|
|
26
31
|
const textContent = generateTextContent({
|
|
27
32
|
type: args.type,
|
|
@@ -62,6 +67,11 @@ function generateTextContent({ type, id, }) {
|
|
|
62
67
|
nextSteps.push(`Use ${GET_OVERVIEW} to check if this affects any dependent tasks`);
|
|
63
68
|
nextSteps.push('Note: Any subtasks of this task were also deleted');
|
|
64
69
|
break;
|
|
70
|
+
case 'comment':
|
|
71
|
+
// Help user understand comment deletion impact
|
|
72
|
+
nextSteps.push(`Use ${FIND_COMMENTS} to see remaining comments on the task/project`);
|
|
73
|
+
nextSteps.push('Note: Comment attachments were also deleted');
|
|
74
|
+
break;
|
|
65
75
|
}
|
|
66
76
|
const next = formatNextSteps(nextSteps);
|
|
67
77
|
return `${summary}\n${next}`;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { Comment } from '@doist/todoist-api-typescript';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
declare const findComments: {
|
|
4
|
+
name: "find-comments";
|
|
5
|
+
description: string;
|
|
6
|
+
parameters: {
|
|
7
|
+
taskId: z.ZodOptional<z.ZodString>;
|
|
8
|
+
projectId: z.ZodOptional<z.ZodString>;
|
|
9
|
+
commentId: z.ZodOptional<z.ZodString>;
|
|
10
|
+
cursor: z.ZodOptional<z.ZodString>;
|
|
11
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
12
|
+
};
|
|
13
|
+
execute(args: {
|
|
14
|
+
projectId?: string | undefined;
|
|
15
|
+
limit?: number | undefined;
|
|
16
|
+
cursor?: string | undefined;
|
|
17
|
+
taskId?: string | undefined;
|
|
18
|
+
commentId?: string | undefined;
|
|
19
|
+
}, client: import("@doist/todoist-api-typescript").TodoistApi): Promise<{
|
|
20
|
+
content: {
|
|
21
|
+
type: "text";
|
|
22
|
+
text: string;
|
|
23
|
+
}[];
|
|
24
|
+
structuredContent: {
|
|
25
|
+
comments: Comment[];
|
|
26
|
+
searchType: string;
|
|
27
|
+
searchId: string;
|
|
28
|
+
hasMore: boolean;
|
|
29
|
+
nextCursor: string | null;
|
|
30
|
+
totalCount: number;
|
|
31
|
+
};
|
|
32
|
+
} | {
|
|
33
|
+
content: ({
|
|
34
|
+
type: "text";
|
|
35
|
+
text: string;
|
|
36
|
+
mimeType?: undefined;
|
|
37
|
+
} | {
|
|
38
|
+
type: "text";
|
|
39
|
+
mimeType: string;
|
|
40
|
+
text: string;
|
|
41
|
+
})[];
|
|
42
|
+
structuredContent?: undefined;
|
|
43
|
+
}>;
|
|
44
|
+
};
|
|
45
|
+
export { findComments };
|
|
46
|
+
//# sourceMappingURL=find-comments.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"find-comments.d.ts","sourceRoot":"","sources":["../../src/tools/find-comments.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAA;AAC5D,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAuBvB,QAAA,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsEwB,CAAA;AAoF1C,OAAO,EAAE,YAAY,EAAE,CAAA"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { getToolOutput } from '../mcp-helpers.js';
|
|
3
|
+
import { ApiLimits } from '../utils/constants.js';
|
|
4
|
+
import { formatNextSteps } from '../utils/response-builders.js';
|
|
5
|
+
import { ToolNames } from '../utils/tool-names.js';
|
|
6
|
+
const { ADD_COMMENTS, UPDATE_COMMENTS, DELETE_OBJECT } = ToolNames;
|
|
7
|
+
const ArgsSchema = {
|
|
8
|
+
taskId: z.string().optional().describe('Find comments for a specific task.'),
|
|
9
|
+
projectId: z.string().optional().describe('Find comments for a specific project.'),
|
|
10
|
+
commentId: z.string().optional().describe('Get a specific comment by ID.'),
|
|
11
|
+
cursor: z.string().optional().describe('Pagination cursor for retrieving more results.'),
|
|
12
|
+
limit: z
|
|
13
|
+
.number()
|
|
14
|
+
.int()
|
|
15
|
+
.min(1)
|
|
16
|
+
.max(ApiLimits.COMMENTS_MAX)
|
|
17
|
+
.optional()
|
|
18
|
+
.describe('Maximum number of comments to return'),
|
|
19
|
+
};
|
|
20
|
+
const findComments = {
|
|
21
|
+
name: ToolNames.FIND_COMMENTS,
|
|
22
|
+
description: 'Find comments by task, project, or get a specific comment by ID. Exactly one of taskId, projectId, or commentId must be provided.',
|
|
23
|
+
parameters: ArgsSchema,
|
|
24
|
+
async execute(args, client) {
|
|
25
|
+
// Validate that exactly one search parameter is provided
|
|
26
|
+
const searchParams = [args.taskId, args.projectId, args.commentId].filter(Boolean);
|
|
27
|
+
if (searchParams.length === 0) {
|
|
28
|
+
throw new Error('Must provide exactly one of: taskId, projectId, or commentId.');
|
|
29
|
+
}
|
|
30
|
+
if (searchParams.length > 1) {
|
|
31
|
+
throw new Error('Cannot provide multiple search parameters. Choose one of: taskId, projectId, or commentId.');
|
|
32
|
+
}
|
|
33
|
+
let comments;
|
|
34
|
+
let hasMore = false;
|
|
35
|
+
let nextCursor = null;
|
|
36
|
+
if (args.commentId) {
|
|
37
|
+
// Get single comment
|
|
38
|
+
const comment = await client.getComment(args.commentId);
|
|
39
|
+
comments = [comment];
|
|
40
|
+
}
|
|
41
|
+
else if (args.taskId) {
|
|
42
|
+
// Get comments by task
|
|
43
|
+
const response = await client.getComments({
|
|
44
|
+
taskId: args.taskId,
|
|
45
|
+
cursor: args.cursor || null,
|
|
46
|
+
limit: args.limit || ApiLimits.COMMENTS_DEFAULT,
|
|
47
|
+
});
|
|
48
|
+
comments = response.results;
|
|
49
|
+
hasMore = response.nextCursor !== null;
|
|
50
|
+
nextCursor = response.nextCursor;
|
|
51
|
+
}
|
|
52
|
+
else if (args.projectId) {
|
|
53
|
+
// Get comments by project
|
|
54
|
+
const response = await client.getComments({
|
|
55
|
+
projectId: args.projectId,
|
|
56
|
+
cursor: args.cursor || null,
|
|
57
|
+
limit: args.limit || ApiLimits.COMMENTS_DEFAULT,
|
|
58
|
+
});
|
|
59
|
+
comments = response.results;
|
|
60
|
+
hasMore = response.nextCursor !== null;
|
|
61
|
+
nextCursor = response.nextCursor;
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
// This should never happen due to validation, but TypeScript needs it
|
|
65
|
+
throw new Error('Invalid state: no search parameter provided');
|
|
66
|
+
}
|
|
67
|
+
const textContent = generateTextContent({
|
|
68
|
+
comments,
|
|
69
|
+
searchType: args.commentId ? 'single' : args.taskId ? 'task' : 'project',
|
|
70
|
+
searchId: args.commentId || args.taskId || args.projectId || '',
|
|
71
|
+
hasMore,
|
|
72
|
+
nextCursor,
|
|
73
|
+
});
|
|
74
|
+
return getToolOutput({
|
|
75
|
+
textContent,
|
|
76
|
+
structuredContent: {
|
|
77
|
+
comments,
|
|
78
|
+
searchType: args.commentId ? 'single' : args.taskId ? 'task' : 'project',
|
|
79
|
+
searchId: args.commentId || args.taskId || args.projectId || '',
|
|
80
|
+
hasMore,
|
|
81
|
+
nextCursor,
|
|
82
|
+
totalCount: comments.length,
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
function generateTextContent({ comments, searchType, searchId, hasMore, nextCursor, }) {
|
|
88
|
+
if (comments.length === 0) {
|
|
89
|
+
return `No comments found for ${searchType}${searchType !== 'single' ? ` ${searchId}` : ''}`;
|
|
90
|
+
}
|
|
91
|
+
// Build summary
|
|
92
|
+
let summary;
|
|
93
|
+
if (searchType === 'single') {
|
|
94
|
+
const comment = comments[0];
|
|
95
|
+
if (!comment) {
|
|
96
|
+
return 'Comment not found';
|
|
97
|
+
}
|
|
98
|
+
const hasAttachment = comment.fileAttachment !== null;
|
|
99
|
+
const attachmentInfo = hasAttachment
|
|
100
|
+
? ` • Has attachment: ${comment.fileAttachment?.fileName || 'file'}`
|
|
101
|
+
: '';
|
|
102
|
+
summary = `Found comment${attachmentInfo} • id=${comment.id}`;
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
const attachmentCount = comments.filter((c) => c.fileAttachment !== null).length;
|
|
106
|
+
const attachmentInfo = attachmentCount > 0 ? ` (${attachmentCount} with attachments)` : '';
|
|
107
|
+
const commentsLabel = comments.length === 1 ? 'comment' : 'comments';
|
|
108
|
+
summary = `Found ${comments.length} ${commentsLabel} for ${searchType} ${searchId}${attachmentInfo}`;
|
|
109
|
+
if (hasMore) {
|
|
110
|
+
summary += ' • More available';
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// Context-aware next steps
|
|
114
|
+
const nextSteps = [];
|
|
115
|
+
if (searchType === 'single') {
|
|
116
|
+
const comment = comments[0];
|
|
117
|
+
if (comment) {
|
|
118
|
+
nextSteps.push(`Use ${UPDATE_COMMENTS} with id=${comment.id} to edit content`);
|
|
119
|
+
nextSteps.push(`Use ${DELETE_OBJECT} with type=comment id=${comment.id} to remove`);
|
|
120
|
+
// Suggest viewing related comments
|
|
121
|
+
if (comment.taskId) {
|
|
122
|
+
nextSteps.push(`Use ${ToolNames.FIND_COMMENTS} with taskId=${comment.taskId} to see all task comments`);
|
|
123
|
+
}
|
|
124
|
+
else if (comment.projectId) {
|
|
125
|
+
nextSteps.push(`Use ${ToolNames.FIND_COMMENTS} with projectId=${comment.projectId} to see all project comments`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
// Multiple comments
|
|
131
|
+
nextSteps.push(`Use ${ADD_COMMENTS} with ${searchType}Id=${searchId} to add new comment`);
|
|
132
|
+
if (comments.length > 0) {
|
|
133
|
+
nextSteps.push(`Use ${ToolNames.FIND_COMMENTS} with commentId to view specific comment details`);
|
|
134
|
+
}
|
|
135
|
+
// Pagination
|
|
136
|
+
if (hasMore && nextCursor) {
|
|
137
|
+
nextSteps.push(`Use ${ToolNames.FIND_COMMENTS} with cursor="${nextCursor}" to get more results`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const next = formatNextSteps(nextSteps);
|
|
141
|
+
return `${summary}\n${next}`;
|
|
142
|
+
}
|
|
143
|
+
export { findComments };
|
|
@@ -4,7 +4,7 @@ import { mapProject } from '../tool-helpers.js';
|
|
|
4
4
|
import { ApiLimits } from '../utils/constants.js';
|
|
5
5
|
import { formatProjectPreview, summarizeList } from '../utils/response-builders.js';
|
|
6
6
|
import { ToolNames } from '../utils/tool-names.js';
|
|
7
|
-
const {
|
|
7
|
+
const { ADD_PROJECTS, FIND_TASKS } = ToolNames;
|
|
8
8
|
const ArgsSchema = {
|
|
9
9
|
search: z
|
|
10
10
|
.string()
|
|
@@ -76,7 +76,7 @@ function generateTextContent({ projects, args, nextCursor, }) {
|
|
|
76
76
|
}
|
|
77
77
|
else {
|
|
78
78
|
zeroReasonHints.push('No projects created yet');
|
|
79
|
-
zeroReasonHints.push(`Use ${
|
|
79
|
+
zeroReasonHints.push(`Use ${ADD_PROJECTS} to create a project`);
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
82
|
// Generate contextual next steps
|
|
@@ -2,7 +2,7 @@ import { z } from 'zod';
|
|
|
2
2
|
import { getToolOutput } from '../mcp-helpers.js';
|
|
3
3
|
import { summarizeList } from '../utils/response-builders.js';
|
|
4
4
|
import { ToolNames } from '../utils/tool-names.js';
|
|
5
|
-
const {
|
|
5
|
+
const { ADD_SECTIONS, UPDATE_SECTIONS, FIND_TASKS, UPDATE_TASKS, DELETE_OBJECT } = ToolNames;
|
|
6
6
|
const ArgsSchema = {
|
|
7
7
|
projectId: z.string().min(1).describe('The ID of the project to search sections in.'),
|
|
8
8
|
search: z
|
|
@@ -50,7 +50,7 @@ function generateTextContent({ sections, projectId, search, }) {
|
|
|
50
50
|
}
|
|
51
51
|
else {
|
|
52
52
|
zeroReasonHints.push('Project has no sections yet');
|
|
53
|
-
zeroReasonHints.push(`Use ${
|
|
53
|
+
zeroReasonHints.push(`Use ${ADD_SECTIONS} to create sections`);
|
|
54
54
|
}
|
|
55
55
|
// Data-driven next steps based on results
|
|
56
56
|
const nextSteps = [];
|
|
@@ -59,7 +59,7 @@ function generateTextContent({ sections, projectId, search, }) {
|
|
|
59
59
|
if (sections.length === 1) {
|
|
60
60
|
const sectionId = sections[0]?.id;
|
|
61
61
|
nextSteps.push(`Use ${FIND_TASKS} with sectionId=${sectionId} to see tasks`);
|
|
62
|
-
nextSteps.push(`Use ${
|
|
62
|
+
nextSteps.push(`Use ${ADD_SECTIONS} to create additional sections for organization`);
|
|
63
63
|
}
|
|
64
64
|
else if (sections.length > 8) {
|
|
65
65
|
nextSteps.push('Consider consolidating sections - many small sections can reduce productivity');
|
|
@@ -68,7 +68,7 @@ function generateTextContent({ sections, projectId, search, }) {
|
|
|
68
68
|
}
|
|
69
69
|
else {
|
|
70
70
|
nextSteps.push(`Use ${FIND_TASKS} with sectionId to see tasks in specific sections`);
|
|
71
|
-
nextSteps.push(`Use ${
|
|
71
|
+
nextSteps.push(`Use ${UPDATE_SECTIONS} to modify section names`);
|
|
72
72
|
}
|
|
73
73
|
// Search-specific suggestions
|
|
74
74
|
if (search) {
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { Comment } from '@doist/todoist-api-typescript';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
declare const updateComments: {
|
|
4
|
+
name: "update-comments";
|
|
5
|
+
description: string;
|
|
6
|
+
parameters: {
|
|
7
|
+
comments: z.ZodArray<z.ZodObject<{
|
|
8
|
+
id: z.ZodString;
|
|
9
|
+
content: z.ZodString;
|
|
10
|
+
}, "strip", z.ZodTypeAny, {
|
|
11
|
+
content: string;
|
|
12
|
+
id: string;
|
|
13
|
+
}, {
|
|
14
|
+
content: string;
|
|
15
|
+
id: string;
|
|
16
|
+
}>, "many">;
|
|
17
|
+
};
|
|
18
|
+
execute(args: {
|
|
19
|
+
comments: {
|
|
20
|
+
content: string;
|
|
21
|
+
id: string;
|
|
22
|
+
}[];
|
|
23
|
+
}, client: import("@doist/todoist-api-typescript").TodoistApi): Promise<{
|
|
24
|
+
content: {
|
|
25
|
+
type: "text";
|
|
26
|
+
text: string;
|
|
27
|
+
}[];
|
|
28
|
+
structuredContent: {
|
|
29
|
+
comments: Comment[];
|
|
30
|
+
totalCount: number;
|
|
31
|
+
updatedCommentIds: string[];
|
|
32
|
+
appliedOperations: {
|
|
33
|
+
updateCount: number;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
} | {
|
|
37
|
+
content: ({
|
|
38
|
+
type: "text";
|
|
39
|
+
text: string;
|
|
40
|
+
mimeType?: undefined;
|
|
41
|
+
} | {
|
|
42
|
+
type: "text";
|
|
43
|
+
mimeType: string;
|
|
44
|
+
text: string;
|
|
45
|
+
})[];
|
|
46
|
+
structuredContent?: undefined;
|
|
47
|
+
}>;
|
|
48
|
+
};
|
|
49
|
+
export { updateComments };
|
|
50
|
+
//# sourceMappingURL=update-comments.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-comments.d.ts","sourceRoot":"","sources":["../../src/tools/update-comments.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAA;AAC5D,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAiBvB,QAAA,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6BsB,CAAA;AA2D1C,OAAO,EAAE,cAAc,EAAE,CAAA"}
|