todoist-mcp 1.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.
Files changed (44) hide show
  1. package/README.md +130 -0
  2. package/dist/index.d.ts +2 -0
  3. package/dist/index.js +53 -0
  4. package/dist/tools/comments.d.ts +4 -0
  5. package/dist/tools/comments.js +141 -0
  6. package/dist/tools/create_task.d.ts +4 -0
  7. package/dist/tools/create_task.js +179 -0
  8. package/dist/tools/example.d.ts +14 -0
  9. package/dist/tools/example.js +99 -0
  10. package/dist/tools/example.test.d.ts +1 -0
  11. package/dist/tools/example.test.js +123 -0
  12. package/dist/tools/get_tasks.d.ts +4 -0
  13. package/dist/tools/get_tasks.js +92 -0
  14. package/dist/tools/labels.d.ts +4 -0
  15. package/dist/tools/labels.js +207 -0
  16. package/dist/tools/projects.d.ts +4 -0
  17. package/dist/tools/projects.js +171 -0
  18. package/dist/tools/sections.d.ts +4 -0
  19. package/dist/tools/sections.js +131 -0
  20. package/dist/tools/task_tools.d.ts +4 -0
  21. package/dist/tools/task_tools.js +284 -0
  22. package/dist/tools/tasks.d.ts +4 -0
  23. package/dist/tools/tasks.js +283 -0
  24. package/dist/tools/tests/example_data.d.ts +21 -0
  25. package/dist/tools/tests/example_data.js +65 -0
  26. package/dist/tools/tests/get_tasks.test.d.ts +1 -0
  27. package/dist/tools/tests/get_tasks.test.js +60 -0
  28. package/dist/tools/utils.d.ts +4 -0
  29. package/dist/tools/utils.js +31 -0
  30. package/dist/tools.d.ts +15 -0
  31. package/dist/tools.js +22 -0
  32. package/dist/utils/TestClient.d.ts +17 -0
  33. package/dist/utils/TestClient.js +23 -0
  34. package/dist/utils/TodoistClient.d.ts +26 -0
  35. package/dist/utils/TodoistClient.js +87 -0
  36. package/dist/utils/handlers.d.ts +14 -0
  37. package/dist/utils/handlers.js +69 -0
  38. package/dist/utils/helpers.d.ts +7 -0
  39. package/dist/utils/helpers.js +13 -0
  40. package/dist/utils/types.d.ts +10 -0
  41. package/dist/utils/types.js +1 -0
  42. package/dist/utils/version.d.ts +1 -0
  43. package/dist/utils/version.js +7 -0
  44. package/package.json +60 -0
package/README.md ADDED
@@ -0,0 +1,130 @@
1
+ <div align="center">
2
+ <h1></h1>
3
+ <img src="https://static-00.iconduck.com/assets.00/todoist-icon-512x512-v3a6dxo9.png" width="120"/>
4
+ <h1>Todoist MCP Server</h1>
5
+ <p>A Model Context Protocol (MCP) server implementation that integrates Claude and other AI assistants with Todoist, enabling natural language task management.</p>
6
+ </div>
7
+
8
+ ## Features
9
+
10
+ * **Complete Todoist API Integration**: Access to the full Todoist REST API v2 through natural language
11
+ * **Tasks Management**: Create, update, close, reopen, and delete tasks using conversational language
12
+ * **Project Management**: Create and manage projects and sections
13
+ * **Comments Support**: Add and manage comments on tasks and projects
14
+ * **Label Management**: Create and manage personal and shared labels
15
+
16
+ ## Configuration
17
+
18
+ You'll need a Todoist API token to use this MCP server.
19
+
20
+ ### Getting a Todoist API Token
21
+
22
+ 1. Log in to your Todoist account
23
+ 2. Navigate to Settings → Integrations
24
+ 3. Find your API token under "Developer"
25
+
26
+ ### Usage with Claude Desktop
27
+
28
+ Add to your `claude_desktop_config.json`:
29
+
30
+ ```json
31
+ {
32
+ "mcpServers": {
33
+ "todoist": {
34
+ "command": "npx",
35
+ "args": [
36
+ "-y",
37
+ "todoist-mcp"
38
+ ],
39
+ "env": {
40
+ "API_KEY": "your_todoist_api_token_here"
41
+ }
42
+ }
43
+ }
44
+ }
45
+ ```
46
+
47
+ ## Available Tools
48
+
49
+ ### Tasks
50
+
51
+ - `get_tasks`: Retrieve tasks with optional filtering
52
+ - `create_task`: Create a new task with various attributes
53
+ - `get_task`: Get a specific task by ID
54
+ - `update_task`: Update an existing task
55
+ - `close_task`: Mark a task as complete
56
+ - `reopen_task`: Reopen a completed task
57
+ - `delete_task`: Delete a task
58
+
59
+ ### Projects
60
+
61
+ - `get_projects`: Get all projects
62
+ - `create_project`: Create a new project
63
+ - `get_project`: Get a specific project by ID
64
+ - `update_project`: Update an existing project
65
+ - `delete_project`: Delete a project
66
+ - `get_collaborators`: Get all collaborators for a project
67
+
68
+ ### Sections
69
+
70
+ - `get_sections`: Get all sections or filter by project
71
+ - `create_section`: Create a new section
72
+ - `get_section`: Get a specific section by ID
73
+ - `update_section`: Update a section
74
+ - `delete_section`: Delete a section
75
+
76
+ ### Comments
77
+
78
+ - `get_comments`: Get comments for a project or task
79
+ - `create_comment`: Create a new comment
80
+ - `get_comment`: Get a specific comment by ID
81
+ - `update_comment`: Update a comment
82
+ - `delete_comment`: Delete a comment
83
+
84
+ ### Labels
85
+
86
+ - `get_labels`: Get all personal labels
87
+ - `create_label`: Create a new personal label
88
+ - `get_label`: Get a personal label by ID
89
+ - `update_label`: Update a personal label
90
+ - `delete_label`: Delete a personal label
91
+ - `get_shared_labels`: Get all shared labels
92
+ - `rename_shared_label`: Rename a shared label
93
+ - `remove_shared_label`: Remove a shared label
94
+
95
+ ### Utilities
96
+
97
+ - `utils_get_colors`: Get available colors for projects, labels, and filters
98
+
99
+ ## Example Usage
100
+
101
+ Ask your AI assistant (like Claude) questions such as:
102
+
103
+ ```
104
+ "What tasks do I have due today?"
105
+ "Create a task to review the quarterly report by next Friday"
106
+ "Make a new project called 'Home Renovation'"
107
+ "Add a comment to my meeting prep task"
108
+ "Show me all my high priority tasks"
109
+ "Create a label for 'Urgent' tasks with a red color"
110
+ "What projects do I have in my Todoist?"
111
+ "Mark my dentist appointment task as complete"
112
+ ```
113
+
114
+ ## Development
115
+
116
+ ```bash
117
+ # Install dependencies
118
+ npm install
119
+
120
+ # Build the project and run inspector
121
+ npm run build && npx @modelcontextprotocol/inspector -e API_KEY=YOUR_API_KEY_HERE node dist/index.js
122
+ ```
123
+
124
+ ## License
125
+
126
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
127
+
128
+ ## Issues and Support
129
+
130
+ If you encounter any issues or need support, please file an issue on the GitHub repository.
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export declare function main(): Promise<void>;
package/dist/index.js ADDED
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env node
2
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
+ import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
5
+ import { config, log } from './utils/helpers.js';
6
+ import { version } from './utils/version.js';
7
+ import { ALL_HANDLERS, ALL_TOOLS } from "./tools.js";
8
+ const server = new Server({
9
+ name: 'todoist-mcp',
10
+ version
11
+ }, { capabilities: { tools: {} } });
12
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
13
+ return { tools: ALL_TOOLS };
14
+ });
15
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
16
+ const toolName = request.params.name;
17
+ log('Tool call: ', toolName);
18
+ try {
19
+ const handler = ALL_HANDLERS[toolName];
20
+ if (!handler) {
21
+ throw new Error(`Unknown tool: ${toolName}`);
22
+ }
23
+ return await handler(request);
24
+ }
25
+ catch (error) {
26
+ log('Error handling tool call:', error);
27
+ return {
28
+ content: [
29
+ {
30
+ type: 'text',
31
+ text: `Error: ${error instanceof Error ? error.message : String(error)}`,
32
+ }
33
+ ],
34
+ isError: true,
35
+ };
36
+ }
37
+ });
38
+ export async function main() {
39
+ if (!config.API_KEY || config.API_KEY.length === 0) {
40
+ log('Missing required configuration: TODOIST_API_TOKEN');
41
+ process.exit(1);
42
+ }
43
+ try {
44
+ const transport = new StdioServerTransport();
45
+ await server.connect(transport);
46
+ log('Server connected and running');
47
+ }
48
+ catch (error) {
49
+ log('Fatal error:', error);
50
+ process.exit(1);
51
+ }
52
+ }
53
+ main().then();
@@ -0,0 +1,4 @@
1
+ import { Tool } from '@modelcontextprotocol/sdk/types.js';
2
+ import { ToolHandlers } from '../utils/types.js';
3
+ export declare const COMMENTS_TOOLS: Tool[];
4
+ export declare const COMMENT_HANDLERS: ToolHandlers;
@@ -0,0 +1,141 @@
1
+ import z from 'zod';
2
+ import { createApiHandler } from "../utils/handlers.js";
3
+ export const COMMENTS_TOOLS = [
4
+ {
5
+ name: 'get_comments',
6
+ description: 'Get all comments from Todoist, must be provided or `project_id` or `task_id`',
7
+ inputSchema: {
8
+ type: "object",
9
+ required: [],
10
+ properties: {
11
+ project_id: {
12
+ type: "string",
13
+ description: 'ID of the project used to filter comments'
14
+ },
15
+ task_id: {
16
+ type: "string",
17
+ description: 'ID of the task used to filter comments'
18
+ }
19
+ }
20
+ }
21
+ },
22
+ {
23
+ name: 'create_comment',
24
+ description: 'Create a new comment in Todoist, must be provided or `project_id` or `task_id`',
25
+ inputSchema: {
26
+ type: "object",
27
+ required: ["content"],
28
+ properties: {
29
+ task_id: {
30
+ type: "string",
31
+ description: 'Comment\'s task ID (for task comments)'
32
+ },
33
+ project_id: {
34
+ type: "string",
35
+ description: 'Comment\'s project ID (for project comments)'
36
+ },
37
+ content: {
38
+ type: "string",
39
+ description: 'Comment markdown-formatted text and hyperlinks'
40
+ },
41
+ // attachment: {
42
+ // type: "object",
43
+ // description: 'Object for attachment object'
44
+ // }
45
+ }
46
+ }
47
+ },
48
+ {
49
+ name: 'get_comment',
50
+ description: 'Get a comment from Todoist by ID',
51
+ inputSchema: {
52
+ type: "object",
53
+ required: ["id"],
54
+ properties: {
55
+ id: {
56
+ type: "string",
57
+ description: 'ID of the comment to retrieve'
58
+ }
59
+ }
60
+ }
61
+ },
62
+ {
63
+ name: 'update_comment',
64
+ description: 'Update a comment in Todoist',
65
+ inputSchema: {
66
+ type: "object",
67
+ required: ["id", "content"],
68
+ properties: {
69
+ id: {
70
+ type: "string",
71
+ description: 'ID of the comment to update'
72
+ },
73
+ content: {
74
+ type: "string",
75
+ description: 'New content, markdown-formatted text and hyperlinks'
76
+ }
77
+ }
78
+ }
79
+ },
80
+ {
81
+ name: 'delete_comment',
82
+ description: 'Delete a comment in Todoist',
83
+ inputSchema: {
84
+ type: "object",
85
+ required: ["id"],
86
+ properties: {
87
+ id: {
88
+ type: "string",
89
+ description: 'ID of the comment to delete'
90
+ }
91
+ }
92
+ }
93
+ }
94
+ ];
95
+ export const COMMENT_HANDLERS = {
96
+ get_comments: createApiHandler({
97
+ schemaShape: {
98
+ project_id: z.string().optional(),
99
+ task_id: z.string().optional(),
100
+ },
101
+ method: 'GET',
102
+ path: '/comments',
103
+ errorPrefix: 'Failed to get comments',
104
+ }),
105
+ create_comment: createApiHandler({
106
+ schemaShape: {
107
+ task_id: z.string().optional(),
108
+ project_id: z.string().optional(),
109
+ content: z.string(),
110
+ // attachment: z.object({}).optional(),
111
+ },
112
+ method: 'POST',
113
+ path: '/comments',
114
+ errorPrefix: 'Failed to create comment',
115
+ }),
116
+ get_comment: createApiHandler({
117
+ schemaShape: {
118
+ id: z.string(),
119
+ },
120
+ method: 'GET',
121
+ path: '/comments/{id}',
122
+ errorPrefix: 'Failed to get comment',
123
+ }),
124
+ update_comment: createApiHandler({
125
+ schemaShape: {
126
+ id: z.string(),
127
+ content: z.string(),
128
+ },
129
+ method: 'POST',
130
+ path: '/comments/{id}',
131
+ errorPrefix: 'Failed to update comment',
132
+ }),
133
+ delete_comment: createApiHandler({
134
+ schemaShape: {
135
+ id: z.string(),
136
+ },
137
+ method: 'DELETE',
138
+ path: '/comments/{id}',
139
+ errorPrefix: 'Failed to delete comment',
140
+ }),
141
+ };
@@ -0,0 +1,4 @@
1
+ import { Tool } from '@modelcontextprotocol/sdk/types.js';
2
+ import { ToolHandlers } from '../utils/types.js';
3
+ export declare const CREATE_TASK_TOOLS: Tool[];
4
+ export declare const CREATE_TASK_HANDLER: ToolHandlers;
@@ -0,0 +1,179 @@
1
+ import { log, todoistApi } from '../utils/helpers.js';
2
+ import z from 'zod';
3
+ export const CREATE_TASK_TOOLS = [
4
+ {
5
+ name: 'create_task',
6
+ description: 'Create a new task in Todoist with various parameters',
7
+ inputSchema: {
8
+ type: "object",
9
+ properties: {
10
+ content: {
11
+ type: "string",
12
+ description: 'Task content. This value may contain markdown-formatted text and hyperlinks.',
13
+ },
14
+ description: {
15
+ type: "string",
16
+ description: 'A description for the task. This value may contain markdown-formatted text and hyperlinks.',
17
+ },
18
+ project_id: {
19
+ type: "string",
20
+ description: 'Task project ID. If not set, task is put to user\'s Inbox.',
21
+ },
22
+ section_id: {
23
+ type: "string",
24
+ description: 'ID of section to put task into.',
25
+ },
26
+ parent_id: {
27
+ type: "string",
28
+ description: 'Parent task ID.',
29
+ },
30
+ order: {
31
+ type: "integer",
32
+ description: 'Non-zero integer value used by clients to sort tasks under the same parent.',
33
+ },
34
+ labels: {
35
+ type: "array",
36
+ items: {
37
+ type: "string"
38
+ },
39
+ description: 'The task\'s labels (a list of names that may represent either personal or shared labels).',
40
+ },
41
+ priority: {
42
+ type: "integer",
43
+ description: 'Task priority from 1 (normal) to 4 (urgent).',
44
+ enum: [1, 2, 3, 4]
45
+ },
46
+ due_string: {
47
+ type: "string",
48
+ description: 'Human defined task due date (ex.: "next Monday", "Tomorrow"). Value is set using local (not UTC) time.',
49
+ },
50
+ due_date: {
51
+ type: "string",
52
+ description: 'Specific date in YYYY-MM-DD format relative to user\'s timezone.',
53
+ },
54
+ due_datetime: {
55
+ type: "string",
56
+ description: 'Specific date and time in RFC3339 format in UTC.',
57
+ },
58
+ due_lang: {
59
+ type: "string",
60
+ description: '2-letter code specifying language in case due_string is not written in English.',
61
+ },
62
+ assignee_id: {
63
+ type: "string",
64
+ description: 'The responsible user ID (only applies to shared tasks).',
65
+ },
66
+ duration: {
67
+ type: "integer",
68
+ description: 'A positive (greater than zero) integer for the amount of duration_unit the task will take.',
69
+ },
70
+ duration_unit: {
71
+ type: "string",
72
+ description: 'The unit of time that the duration field represents. Must be either minute or day.',
73
+ enum: ["minute", "day"]
74
+ },
75
+ deadline_date: {
76
+ type: "string",
77
+ description: 'Specific date in YYYY-MM-DD format relative to user\'s timezone.',
78
+ },
79
+ deadline_lang: {
80
+ type: "string",
81
+ description: '2-letter code specifying language of deadline.',
82
+ }
83
+ },
84
+ required: ["content"]
85
+ }
86
+ }
87
+ ];
88
+ export const CREATE_TASK_HANDLER = {
89
+ 'create_task': async (request) => {
90
+ try {
91
+ const args = createTaskArgsSchema.parse(request.params.arguments);
92
+ const newTask = await handleCreateTask(args);
93
+ return {
94
+ toolResult: {
95
+ content: [{
96
+ type: 'text',
97
+ text: JSON.stringify(newTask, null, 2)
98
+ }],
99
+ },
100
+ };
101
+ }
102
+ catch (error) {
103
+ const errorMessage = error instanceof Error ? error.message : String(error);
104
+ throw new Error(`Failed to create task: ${errorMessage}`);
105
+ }
106
+ }
107
+ };
108
+ const createTaskArgsSchema = z.object({
109
+ content: z.string(),
110
+ description: z.string().optional(),
111
+ project_id: z.string().optional(),
112
+ section_id: z.string().optional(),
113
+ parent_id: z.string().optional(),
114
+ order: z.number().int().optional(),
115
+ labels: z.array(z.string()).optional(),
116
+ priority: z.number().int().min(1).max(4).optional(),
117
+ due_string: z.string().optional(),
118
+ due_date: z.string().optional(),
119
+ due_datetime: z.string().optional(),
120
+ due_lang: z.string().optional(),
121
+ assignee_id: z.string().optional(),
122
+ duration: z.number().int().positive().optional(),
123
+ duration_unit: z.enum(["minute", "day"]).optional(),
124
+ deadline_date: z.string().optional(),
125
+ deadline_lang: z.string().optional()
126
+ });
127
+ async function handleCreateTask(args) {
128
+ log('Creating task with args:', JSON.stringify(args, null, 2));
129
+ try {
130
+ // Create task data object with the parameters
131
+ const taskData = {
132
+ content: args.content
133
+ };
134
+ // Add optional parameters directly using the API field names (snake_case)
135
+ if (args.description)
136
+ taskData.description = args.description;
137
+ if (args.project_id)
138
+ taskData.project_id = args.project_id;
139
+ if (args.section_id)
140
+ taskData.section_id = args.section_id;
141
+ if (args.parent_id)
142
+ taskData.parent_id = args.parent_id;
143
+ if (args.order)
144
+ taskData.order = args.order;
145
+ if (args.labels)
146
+ taskData.labels = args.labels;
147
+ if (args.priority)
148
+ taskData.priority = args.priority;
149
+ if (args.due_string)
150
+ taskData.due_string = args.due_string;
151
+ if (args.due_date)
152
+ taskData.due_date = args.due_date;
153
+ if (args.due_datetime)
154
+ taskData.due_datetime = args.due_datetime;
155
+ if (args.due_lang)
156
+ taskData.due_lang = args.due_lang;
157
+ if (args.assignee_id)
158
+ taskData.assignee_id = args.assignee_id;
159
+ // Handle duration object
160
+ if (args.duration && args.duration_unit) {
161
+ taskData.duration = {
162
+ amount: args.duration,
163
+ unit: args.duration_unit
164
+ };
165
+ }
166
+ if (args.deadline_date)
167
+ taskData.deadline_date = args.deadline_date;
168
+ if (args.deadline_lang)
169
+ taskData.deadline_lang = args.deadline_lang;
170
+ // Make direct POST request to /tasks endpoint
171
+ const newTask = await todoistApi.post('/tasks', taskData);
172
+ // Return the response directly from the API
173
+ return newTask;
174
+ }
175
+ catch (error) {
176
+ log('Error creating task:', error);
177
+ throw error;
178
+ }
179
+ }
@@ -0,0 +1,14 @@
1
+ import { ToolHandlers } from '../utils/types.js';
2
+ export declare const EXAMPLE_TOOLS: {
3
+ [x: string]: unknown;
4
+ name: string;
5
+ inputSchema: {
6
+ [x: string]: unknown;
7
+ type: "object";
8
+ properties?: {
9
+ [x: string]: unknown;
10
+ } | undefined;
11
+ };
12
+ description?: string | undefined;
13
+ }[];
14
+ export declare const EXAMPLE_HANDLERS: ToolHandlers;
@@ -0,0 +1,99 @@
1
+ import { log, todoistApi } from '../utils/helpers.js';
2
+ import z from 'zod';
3
+ // Task list tool definition
4
+ const GET_TASKS_TOOL = {
5
+ name: 'todoist_get_tasks',
6
+ description: 'Get a list of tasks from Todoist with various filters',
7
+ inputSchema: {
8
+ type: 'object',
9
+ properties: {
10
+ project_id: {
11
+ type: 'string',
12
+ description: 'Filter tasks by project ID (optional)'
13
+ },
14
+ filter: {
15
+ type: 'string',
16
+ description: 'Natural language filter like "today", "tomorrow", "next week", "priority 1", "overdue" (optional)'
17
+ },
18
+ priority: {
19
+ type: 'number',
20
+ description: 'Filter by priority level (1-4) (optional)',
21
+ enum: [1, 2, 3, 4]
22
+ },
23
+ limit: {
24
+ type: 'number',
25
+ description: 'Maximum number of tasks to return (optional)',
26
+ default: 10
27
+ }
28
+ }
29
+ }
30
+ };
31
+ // Export all tools
32
+ export const EXAMPLE_TOOLS = [GET_TASKS_TOOL];
33
+ // Input validation schema using zod
34
+ const getTasksArgsSchema = z.object({
35
+ project_id: z.string().optional(),
36
+ filter: z.string().optional(),
37
+ priority: z.number().min(1).max(4).optional(),
38
+ limit: z.number().positive().optional()
39
+ });
40
+ // Handler function implementation
41
+ async function handleGetTasks(args) {
42
+ const { project_id, filter, priority, limit } = args;
43
+ log('Getting tasks', project_id ? `for project ${project_id}` : '', filter ? `with filter: ${filter}` : '', priority ? `with priority: ${priority}` : '', limit ? `with limit: ${limit}` : '');
44
+ try {
45
+ // Get tasks with filtering from API
46
+ const tasks = await todoistApi.getTasks({
47
+ projectId: project_id,
48
+ filter: filter
49
+ });
50
+ let filteredTasks = tasks.results;
51
+ if (priority) {
52
+ filteredTasks = filteredTasks.filter((task) => task.priority === priority);
53
+ }
54
+ // Apply limit if specified
55
+ if (limit && limit > 0) {
56
+ filteredTasks = filteredTasks.slice(0, limit);
57
+ }
58
+ // Return formatted task data
59
+ return filteredTasks.map(task => ({
60
+ id: task.id,
61
+ content: task.content,
62
+ project_id: task.projectId,
63
+ priority: task.priority,
64
+ due: task.due,
65
+ url: task.url,
66
+ labels: task.labels,
67
+ completed: task.isCompleted,
68
+ created_at: task.createdAt
69
+ }));
70
+ }
71
+ catch (error) {
72
+ log('Error fetching tasks:', error);
73
+ throw error;
74
+ }
75
+ }
76
+ // Export handlers
77
+ export const EXAMPLE_HANDLERS = {
78
+ 'todoist_get_tasks': async (request) => {
79
+ try {
80
+ // Parse and validate arguments
81
+ const args = getTasksArgsSchema.parse(request.params.arguments);
82
+ // Get tasks with all filters applied
83
+ const tasks = await handleGetTasks(args);
84
+ // Return formatted response
85
+ return {
86
+ toolResult: {
87
+ content: [{
88
+ type: 'text',
89
+ text: JSON.stringify(tasks, null, 2)
90
+ }],
91
+ },
92
+ };
93
+ }
94
+ catch (error) {
95
+ const errorMessage = error instanceof Error ? error.message : String(error);
96
+ throw new Error(`Failed to get tasks: ${errorMessage}`);
97
+ }
98
+ }
99
+ };
@@ -0,0 +1 @@
1
+ export {};