ticktick-mcp 0.1.0 → 0.2.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 (115) hide show
  1. package/dist/index.js +47 -1096
  2. package/dist/index.js.map +1 -1
  3. package/dist/sdk/__tests__/client.test.d.ts +2 -0
  4. package/dist/sdk/__tests__/client.test.d.ts.map +1 -0
  5. package/dist/sdk/__tests__/client.test.js +251 -0
  6. package/dist/sdk/__tests__/client.test.js.map +1 -0
  7. package/dist/sdk/__tests__/errors.test.d.ts +2 -0
  8. package/dist/sdk/__tests__/errors.test.d.ts.map +1 -0
  9. package/dist/sdk/__tests__/errors.test.js +164 -0
  10. package/dist/sdk/__tests__/errors.test.js.map +1 -0
  11. package/dist/sdk/__tests__/types.test.d.ts +2 -0
  12. package/dist/sdk/__tests__/types.test.d.ts.map +1 -0
  13. package/dist/sdk/__tests__/types.test.js +134 -0
  14. package/dist/sdk/__tests__/types.test.js.map +1 -0
  15. package/dist/sdk/client.d.ts +10 -2
  16. package/dist/sdk/client.d.ts.map +1 -1
  17. package/dist/sdk/client.js +14 -3
  18. package/dist/sdk/client.js.map +1 -1
  19. package/dist/sdk/index.d.ts +5 -3
  20. package/dist/sdk/index.d.ts.map +1 -1
  21. package/dist/sdk/index.js +10 -2
  22. package/dist/sdk/index.js.map +1 -1
  23. package/dist/sdk/types.d.ts +215 -23
  24. package/dist/sdk/types.d.ts.map +1 -1
  25. package/dist/sdk/types.js +169 -6
  26. package/dist/sdk/types.js.map +1 -1
  27. package/dist/tools/authExchangeCode.d.ts +9 -0
  28. package/dist/tools/authExchangeCode.d.ts.map +1 -0
  29. package/dist/tools/authExchangeCode.js +49 -0
  30. package/dist/tools/authExchangeCode.js.map +1 -0
  31. package/dist/tools/authGetAuthorizationUrl.d.ts +10 -0
  32. package/dist/tools/authGetAuthorizationUrl.d.ts.map +1 -0
  33. package/dist/tools/authGetAuthorizationUrl.js +53 -0
  34. package/dist/tools/authGetAuthorizationUrl.js.map +1 -0
  35. package/dist/tools/authLogout.d.ts +9 -0
  36. package/dist/tools/authLogout.d.ts.map +1 -0
  37. package/dist/tools/authLogout.js +43 -0
  38. package/dist/tools/authLogout.js.map +1 -0
  39. package/dist/tools/authRefreshToken.d.ts +9 -0
  40. package/dist/tools/authRefreshToken.d.ts.map +1 -0
  41. package/dist/tools/authRefreshToken.js +60 -0
  42. package/dist/tools/authRefreshToken.js.map +1 -0
  43. package/dist/tools/authStatus.d.ts +9 -0
  44. package/dist/tools/authStatus.d.ts.map +1 -0
  45. package/dist/tools/authStatus.js +63 -0
  46. package/dist/tools/authStatus.js.map +1 -0
  47. package/dist/tools/batchCreateTasks.d.ts +9 -0
  48. package/dist/tools/batchCreateTasks.d.ts.map +1 -0
  49. package/dist/tools/batchCreateTasks.js +75 -0
  50. package/dist/tools/batchCreateTasks.js.map +1 -0
  51. package/dist/tools/completeTask.d.ts +9 -0
  52. package/dist/tools/completeTask.d.ts.map +1 -0
  53. package/dist/tools/completeTask.js +47 -0
  54. package/dist/tools/completeTask.js.map +1 -0
  55. package/dist/tools/createProject.d.ts +9 -0
  56. package/dist/tools/createProject.d.ts.map +1 -0
  57. package/dist/tools/createProject.js +64 -0
  58. package/dist/tools/createProject.js.map +1 -0
  59. package/dist/tools/createTask.d.ts +9 -0
  60. package/dist/tools/createTask.d.ts.map +1 -0
  61. package/dist/tools/createTask.js +131 -0
  62. package/dist/tools/createTask.js.map +1 -0
  63. package/dist/tools/deleteProject.d.ts +9 -0
  64. package/dist/tools/deleteProject.d.ts.map +1 -0
  65. package/dist/tools/deleteProject.js +46 -0
  66. package/dist/tools/deleteProject.js.map +1 -0
  67. package/dist/tools/deleteTask.d.ts +9 -0
  68. package/dist/tools/deleteTask.d.ts.map +1 -0
  69. package/dist/tools/deleteTask.js +47 -0
  70. package/dist/tools/deleteTask.js.map +1 -0
  71. package/dist/tools/getHighPriorityTasks.d.ts +9 -0
  72. package/dist/tools/getHighPriorityTasks.d.ts.map +1 -0
  73. package/dist/tools/getHighPriorityTasks.js +114 -0
  74. package/dist/tools/getHighPriorityTasks.js.map +1 -0
  75. package/dist/tools/getProject.d.ts +9 -0
  76. package/dist/tools/getProject.d.ts.map +1 -0
  77. package/dist/tools/getProject.js +62 -0
  78. package/dist/tools/getProject.js.map +1 -0
  79. package/dist/tools/getProjectById.d.ts +9 -0
  80. package/dist/tools/getProjectById.d.ts.map +1 -0
  81. package/dist/tools/getProjectById.js +52 -0
  82. package/dist/tools/getProjectById.js.map +1 -0
  83. package/dist/tools/getTask.d.ts +9 -0
  84. package/dist/tools/getTask.d.ts.map +1 -0
  85. package/dist/tools/getTask.js +65 -0
  86. package/dist/tools/getTask.js.map +1 -0
  87. package/dist/tools/getTasksDueSoon.d.ts +9 -0
  88. package/dist/tools/getTasksDueSoon.d.ts.map +1 -0
  89. package/dist/tools/getTasksDueSoon.js +118 -0
  90. package/dist/tools/getTasksDueSoon.js.map +1 -0
  91. package/dist/tools/getUser.d.ts +9 -0
  92. package/dist/tools/getUser.d.ts.map +1 -0
  93. package/dist/tools/getUser.js +47 -0
  94. package/dist/tools/getUser.js.map +1 -0
  95. package/dist/tools/listProjects.d.ts +9 -0
  96. package/dist/tools/listProjects.d.ts.map +1 -0
  97. package/dist/tools/listProjects.js +78 -0
  98. package/dist/tools/listProjects.js.map +1 -0
  99. package/dist/tools/listTasksInProject.d.ts +10 -0
  100. package/dist/tools/listTasksInProject.d.ts.map +1 -0
  101. package/dist/tools/listTasksInProject.js +177 -0
  102. package/dist/tools/listTasksInProject.js.map +1 -0
  103. package/dist/tools/searchTasks.d.ts +9 -0
  104. package/dist/tools/searchTasks.d.ts.map +1 -0
  105. package/dist/tools/searchTasks.js +134 -0
  106. package/dist/tools/searchTasks.js.map +1 -0
  107. package/dist/tools/updateProject.d.ts +9 -0
  108. package/dist/tools/updateProject.d.ts.map +1 -0
  109. package/dist/tools/updateProject.js +66 -0
  110. package/dist/tools/updateProject.js.map +1 -0
  111. package/dist/tools/updateTask.d.ts +9 -0
  112. package/dist/tools/updateTask.d.ts.map +1 -0
  113. package/dist/tools/updateTask.js +132 -0
  114. package/dist/tools/updateTask.js.map +1 -0
  115. package/package.json +14 -4
package/dist/index.js CHANGED
@@ -7,9 +7,31 @@
7
7
  */
8
8
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
9
9
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
10
- import { z } from "zod";
11
10
  import { createOAuthFromEnv } from "./oauth.js";
12
11
  import { TickTickClient } from "./sdk/client.js";
12
+ // Import all tool registration functions
13
+ import { registerAuthGetAuthorizationUrlTool } from "./tools/authGetAuthorizationUrl.js";
14
+ import { registerAuthExchangeCodeTool } from "./tools/authExchangeCode.js";
15
+ import { registerAuthStatusTool } from "./tools/authStatus.js";
16
+ import { registerAuthRefreshTokenTool } from "./tools/authRefreshToken.js";
17
+ import { registerAuthLogoutTool } from "./tools/authLogout.js";
18
+ import { registerGetUserTool } from "./tools/getUser.js";
19
+ import { registerListProjectsTool } from "./tools/listProjects.js";
20
+ import { registerGetProjectTool } from "./tools/getProject.js";
21
+ import { registerGetProjectByIdTool } from "./tools/getProjectById.js";
22
+ import { registerCreateProjectTool } from "./tools/createProject.js";
23
+ import { registerUpdateProjectTool } from "./tools/updateProject.js";
24
+ import { registerDeleteProjectTool } from "./tools/deleteProject.js";
25
+ import { registerListTasksInProjectTool } from "./tools/listTasksInProject.js";
26
+ import { registerCreateTaskTool } from "./tools/createTask.js";
27
+ import { registerUpdateTaskTool } from "./tools/updateTask.js";
28
+ import { registerCompleteTaskTool } from "./tools/completeTask.js";
29
+ import { registerDeleteTaskTool } from "./tools/deleteTask.js";
30
+ import { registerGetTaskTool } from "./tools/getTask.js";
31
+ import { registerBatchCreateTasksTool } from "./tools/batchCreateTasks.js";
32
+ import { registerGetTasksDueSoonTool } from "./tools/getTasksDueSoon.js";
33
+ import { registerSearchTasksTool } from "./tools/searchTasks.js";
34
+ import { registerGetHighPriorityTasksTool } from "./tools/getHighPriorityTasks.js";
13
35
  // =============================================================================
14
36
  // Server Setup
15
37
  // =============================================================================
@@ -38,1105 +60,34 @@ async function getClient() {
38
60
  return new TickTickClient({ accessToken, region });
39
61
  }
40
62
  // =============================================================================
41
- // OAuth Authentication Tools
42
- // =============================================================================
43
- /**
44
- * Tool: Get OAuth authorization URL
45
- *
46
- * Returns the URL that users should visit to authorize the application.
47
- * This initiates the OAuth 2.0 authorization code flow.
48
- */
49
- server.tool("auth_get_authorization_url", "Get the OAuth authorization URL for TickTick. Returns a URL that the user should visit in their browser to authorize the application.", {}, async () => {
50
- try {
51
- const oauth = getOAuthHelper();
52
- const { url, state } = oauth.getAuthorizationUrl();
53
- return {
54
- content: [
55
- {
56
- type: "text",
57
- text: JSON.stringify({
58
- success: true,
59
- authorizationUrl: url,
60
- state: state,
61
- instructions: [
62
- "1. Open the authorization URL in your browser",
63
- "2. Log in to TickTick and authorize the application",
64
- "3. After authorization, you will be redirected to a URL containing a 'code' parameter",
65
- "4. Copy the 'code' value from the URL",
66
- "5. Use the auth_exchange_code tool with the code to complete authentication",
67
- ],
68
- }, null, 2),
69
- },
70
- ],
71
- };
72
- }
73
- catch (error) {
74
- const errorMessage = error instanceof Error ? error.message : String(error);
75
- return {
76
- content: [
77
- {
78
- type: "text",
79
- text: JSON.stringify({
80
- success: false,
81
- error: errorMessage,
82
- hint: "Make sure TICKTICK_CLIENT_ID and TICKTICK_CLIENT_SECRET environment variables are set",
83
- }, null, 2),
84
- },
85
- ],
86
- isError: true,
87
- };
88
- }
89
- });
90
- /**
91
- * Tool: Exchange authorization code for tokens
92
- *
93
- * Exchanges an authorization code (from the OAuth callback) for access and refresh tokens.
94
- */
95
- server.tool("auth_exchange_code", "Exchange an OAuth authorization code for access tokens. Use this after the user has authorized the application and received a code.", {
96
- code: z.string().describe("The authorization code from the OAuth callback URL"),
97
- }, async ({ code }) => {
98
- try {
99
- const oauth = getOAuthHelper();
100
- const tokens = await oauth.exchangeCode(code);
101
- await oauth.storeToken(tokens);
102
- return {
103
- content: [
104
- {
105
- type: "text",
106
- text: JSON.stringify({
107
- success: true,
108
- message: "Authentication successful! Tokens have been stored.",
109
- expiresIn: tokens.expires_in,
110
- tokenType: tokens.token_type,
111
- }, null, 2),
112
- },
113
- ],
114
- };
115
- }
116
- catch (error) {
117
- const errorMessage = error instanceof Error ? error.message : String(error);
118
- return {
119
- content: [
120
- {
121
- type: "text",
122
- text: JSON.stringify({
123
- success: false,
124
- error: errorMessage,
125
- }, null, 2),
126
- },
127
- ],
128
- isError: true,
129
- };
130
- }
131
- });
132
- /**
133
- * Tool: Check authentication status
134
- *
135
- * Returns information about the current authentication state.
136
- */
137
- server.tool("auth_status", "Check the current authentication status. Shows whether the user is authenticated and when the token expires.", {}, async () => {
138
- try {
139
- const oauth = getOAuthHelper();
140
- const status = await oauth.getAuthStatus();
141
- if (!status.isAuthenticated) {
142
- return {
143
- content: [
144
- {
145
- type: "text",
146
- text: JSON.stringify({
147
- success: true,
148
- isAuthenticated: false,
149
- message: "Not authenticated. Use auth_get_authorization_url to start the OAuth flow.",
150
- }, null, 2),
151
- },
152
- ],
153
- };
154
- }
155
- return {
156
- content: [
157
- {
158
- type: "text",
159
- text: JSON.stringify({
160
- success: true,
161
- isAuthenticated: true,
162
- isExpired: status.isExpired,
163
- expiresAt: status.expiresAt,
164
- expiresIn: status.expiresIn,
165
- message: status.isExpired
166
- ? "Token is expired. It will be automatically refreshed on next API call."
167
- : `Token is valid for ${status.expiresIn} more seconds.`,
168
- }, null, 2),
169
- },
170
- ],
171
- };
172
- }
173
- catch (error) {
174
- const errorMessage = error instanceof Error ? error.message : String(error);
175
- return {
176
- content: [
177
- {
178
- type: "text",
179
- text: JSON.stringify({
180
- success: false,
181
- error: errorMessage,
182
- }, null, 2),
183
- },
184
- ],
185
- isError: true,
186
- };
187
- }
188
- });
189
- /**
190
- * Tool: Refresh access token
191
- *
192
- * Manually refresh the access token using the stored refresh token.
193
- */
194
- server.tool("auth_refresh_token", "Manually refresh the OAuth access token. Usually not needed as tokens are automatically refreshed when expired.", {}, async () => {
195
- try {
196
- const oauth = getOAuthHelper();
197
- const storedToken = await oauth.loadToken();
198
- if (!storedToken) {
199
- return {
200
- content: [
201
- {
202
- type: "text",
203
- text: JSON.stringify({
204
- success: false,
205
- error: "Not authenticated. No stored token found.",
206
- }, null, 2),
207
- },
208
- ],
209
- isError: true,
210
- };
211
- }
212
- const newTokens = await oauth.refreshToken(storedToken.refreshToken);
213
- await oauth.storeToken(newTokens);
214
- return {
215
- content: [
216
- {
217
- type: "text",
218
- text: JSON.stringify({
219
- success: true,
220
- message: "Token refreshed successfully!",
221
- expiresIn: newTokens.expires_in,
222
- }, null, 2),
223
- },
224
- ],
225
- };
226
- }
227
- catch (error) {
228
- const errorMessage = error instanceof Error ? error.message : String(error);
229
- return {
230
- content: [
231
- {
232
- type: "text",
233
- text: JSON.stringify({
234
- success: false,
235
- error: errorMessage,
236
- }, null, 2),
237
- },
238
- ],
239
- isError: true,
240
- };
241
- }
242
- });
243
- /**
244
- * Tool: Logout / Clear tokens
245
- *
246
- * Remove stored authentication tokens.
247
- */
248
- server.tool("auth_logout", "Remove stored authentication tokens. This will log out the user from the TickTick integration.", {}, async () => {
249
- try {
250
- const oauth = getOAuthHelper();
251
- await oauth.clearToken();
252
- return {
253
- content: [
254
- {
255
- type: "text",
256
- text: JSON.stringify({
257
- success: true,
258
- message: "Logged out successfully. Stored tokens have been cleared.",
259
- }, null, 2),
260
- },
261
- ],
262
- };
263
- }
264
- catch (error) {
265
- const errorMessage = error instanceof Error ? error.message : String(error);
266
- return {
267
- content: [
268
- {
269
- type: "text",
270
- text: JSON.stringify({
271
- success: false,
272
- error: errorMessage,
273
- }, null, 2),
274
- },
275
- ],
276
- isError: true,
277
- };
278
- }
279
- });
63
+ // Register All Tools
280
64
  // =============================================================================
65
+ // OAuth Authentication Tools
66
+ registerAuthGetAuthorizationUrlTool(server, getOAuthHelper);
67
+ registerAuthExchangeCodeTool(server, getOAuthHelper);
68
+ registerAuthStatusTool(server, getOAuthHelper);
69
+ registerAuthRefreshTokenTool(server, getOAuthHelper);
70
+ registerAuthLogoutTool(server, getOAuthHelper);
281
71
  // User Tools
282
- // =============================================================================
283
- /**
284
- * Tool: Get current user
285
- *
286
- * Returns information about the authenticated TickTick user.
287
- */
288
- server.tool("get_user", "Get the current authenticated user's information from TickTick.", {}, async () => {
289
- try {
290
- const client = await getClient();
291
- const user = await client.getUser();
292
- return {
293
- content: [
294
- {
295
- type: "text",
296
- text: JSON.stringify({
297
- success: true,
298
- user: {
299
- id: user.id,
300
- username: user.username,
301
- name: user.name,
302
- },
303
- }, null, 2),
304
- },
305
- ],
306
- };
307
- }
308
- catch (error) {
309
- const errorMessage = error instanceof Error ? error.message : String(error);
310
- return {
311
- content: [
312
- {
313
- type: "text",
314
- text: JSON.stringify({
315
- success: false,
316
- error: errorMessage,
317
- }, null, 2),
318
- },
319
- ],
320
- isError: true,
321
- };
322
- }
323
- });
324
- // =============================================================================
72
+ registerGetUserTool(server, getClient);
325
73
  // Project Tools
326
- // =============================================================================
327
- /**
328
- * Tool: List all projects
329
- *
330
- * Returns all projects for the authenticated user.
331
- */
332
- server.tool("list_projects", "List all projects in the user's TickTick account.", {}, async () => {
333
- try {
334
- const client = await getClient();
335
- const projects = await client.listProjects();
336
- return {
337
- content: [
338
- {
339
- type: "text",
340
- text: JSON.stringify({
341
- success: true,
342
- count: projects.length,
343
- projects: projects.map((p) => ({
344
- id: p.id,
345
- name: p.name,
346
- color: p.color,
347
- viewMode: p.viewMode,
348
- closed: p.closed,
349
- })),
350
- }, null, 2),
351
- },
352
- ],
353
- };
354
- }
355
- catch (error) {
356
- const errorMessage = error instanceof Error ? error.message : String(error);
357
- return {
358
- content: [
359
- {
360
- type: "text",
361
- text: JSON.stringify({
362
- success: false,
363
- error: errorMessage,
364
- }, null, 2),
365
- },
366
- ],
367
- isError: true,
368
- };
369
- }
370
- });
371
- /**
372
- * Tool: Get project with tasks
373
- *
374
- * Returns a specific project and all its tasks.
375
- */
376
- server.tool("get_project", "Get a specific project and all its tasks.", {
377
- projectId: z.string().describe("The ID of the project to retrieve"),
378
- }, async ({ projectId }) => {
379
- try {
380
- const client = await getClient();
381
- const data = await client.getProjectWithTasks(projectId);
382
- return {
383
- content: [
384
- {
385
- type: "text",
386
- text: JSON.stringify({
387
- success: true,
388
- project: {
389
- id: data.project.id,
390
- name: data.project.name,
391
- color: data.project.color,
392
- viewMode: data.project.viewMode,
393
- closed: data.project.closed,
394
- },
395
- taskCount: data.tasks.length,
396
- tasks: data.tasks.map((t) => ({
397
- id: t.id,
398
- title: t.title,
399
- content: t.content,
400
- priority: t.priority,
401
- status: t.status,
402
- dueDate: t.dueDate,
403
- tags: t.tags,
404
- })),
405
- }, null, 2),
406
- },
407
- ],
408
- };
409
- }
410
- catch (error) {
411
- const errorMessage = error instanceof Error ? error.message : String(error);
412
- return {
413
- content: [
414
- {
415
- type: "text",
416
- text: JSON.stringify({
417
- success: false,
418
- error: errorMessage,
419
- }, null, 2),
420
- },
421
- ],
422
- isError: true,
423
- };
424
- }
425
- });
426
- /**
427
- * Tool: Get project by ID (metadata only)
428
- *
429
- * Returns a specific project's metadata without its tasks.
430
- */
431
- server.tool("get_project_by_id", "Get a specific project's metadata by ID (without tasks). Use this when you only need project info, not its tasks.", {
432
- projectId: z.string().describe("The ID of the project to retrieve"),
433
- }, async ({ projectId }) => {
434
- try {
435
- const client = await getClient();
436
- const project = await client.getProject(projectId);
437
- return {
438
- content: [
439
- {
440
- type: "text",
441
- text: JSON.stringify({
442
- success: true,
443
- project: {
444
- id: project.id,
445
- name: project.name,
446
- color: project.color,
447
- viewMode: project.viewMode,
448
- closed: project.closed,
449
- },
450
- }, null, 2),
451
- },
452
- ],
453
- };
454
- }
455
- catch (error) {
456
- const errorMessage = error instanceof Error ? error.message : String(error);
457
- return {
458
- content: [
459
- {
460
- type: "text",
461
- text: JSON.stringify({
462
- success: false,
463
- error: errorMessage,
464
- }, null, 2),
465
- },
466
- ],
467
- isError: true,
468
- };
469
- }
470
- });
471
- /**
472
- * Tool: Create project
473
- *
474
- * Creates a new project.
475
- */
476
- server.tool("create_project", "Create a new project in TickTick.", {
477
- name: z.string().describe("The name of the project"),
478
- color: z
479
- .string()
480
- .optional()
481
- .describe('Hex color code (e.g., "#ff6b6b")'),
482
- viewMode: z
483
- .enum(["list", "kanban", "timeline"])
484
- .optional()
485
- .describe("View mode for the project"),
486
- }, async ({ name, color, viewMode }) => {
487
- try {
488
- const client = await getClient();
489
- const project = await client.createProject({
490
- name,
491
- color,
492
- viewMode,
493
- });
494
- return {
495
- content: [
496
- {
497
- type: "text",
498
- text: JSON.stringify({
499
- success: true,
500
- message: `Project "${project.name}" created successfully!`,
501
- project: {
502
- id: project.id,
503
- name: project.name,
504
- color: project.color,
505
- viewMode: project.viewMode,
506
- },
507
- }, null, 2),
508
- },
509
- ],
510
- };
511
- }
512
- catch (error) {
513
- const errorMessage = error instanceof Error ? error.message : String(error);
514
- return {
515
- content: [
516
- {
517
- type: "text",
518
- text: JSON.stringify({
519
- success: false,
520
- error: errorMessage,
521
- }, null, 2),
522
- },
523
- ],
524
- isError: true,
525
- };
526
- }
527
- });
528
- /**
529
- * Tool: Update project
530
- *
531
- * Updates an existing project.
532
- */
533
- server.tool("update_project", "Update an existing project in TickTick.", {
534
- projectId: z.string().describe("The ID of the project to update"),
535
- name: z.string().optional().describe("New name for the project"),
536
- color: z
537
- .string()
538
- .optional()
539
- .describe('New hex color code (e.g., "#ff6b6b")'),
540
- viewMode: z
541
- .enum(["list", "kanban", "timeline"])
542
- .optional()
543
- .describe("New view mode for the project"),
544
- }, async ({ projectId, name, color, viewMode }) => {
545
- try {
546
- const client = await getClient();
547
- const project = await client.updateProject(projectId, {
548
- name,
549
- color,
550
- viewMode,
551
- });
552
- return {
553
- content: [
554
- {
555
- type: "text",
556
- text: JSON.stringify({
557
- success: true,
558
- message: `Project "${project.name}" updated successfully!`,
559
- project: {
560
- id: project.id,
561
- name: project.name,
562
- color: project.color,
563
- viewMode: project.viewMode,
564
- closed: project.closed,
565
- },
566
- }, null, 2),
567
- },
568
- ],
569
- };
570
- }
571
- catch (error) {
572
- const errorMessage = error instanceof Error ? error.message : String(error);
573
- return {
574
- content: [
575
- {
576
- type: "text",
577
- text: JSON.stringify({
578
- success: false,
579
- error: errorMessage,
580
- }, null, 2),
581
- },
582
- ],
583
- isError: true,
584
- };
585
- }
586
- });
587
- /**
588
- * Tool: Delete project
589
- *
590
- * Deletes a project.
591
- */
592
- server.tool("delete_project", "Delete a project from TickTick.", {
593
- projectId: z.string().describe("The ID of the project to delete"),
594
- }, async ({ projectId }) => {
595
- try {
596
- const client = await getClient();
597
- await client.deleteProject(projectId);
598
- return {
599
- content: [
600
- {
601
- type: "text",
602
- text: JSON.stringify({
603
- success: true,
604
- message: "Project deleted successfully!",
605
- }, null, 2),
606
- },
607
- ],
608
- };
609
- }
610
- catch (error) {
611
- const errorMessage = error instanceof Error ? error.message : String(error);
612
- return {
613
- content: [
614
- {
615
- type: "text",
616
- text: JSON.stringify({
617
- success: false,
618
- error: errorMessage,
619
- }, null, 2),
620
- },
621
- ],
622
- isError: true,
623
- };
624
- }
625
- });
626
- // =============================================================================
74
+ registerListProjectsTool(server, getClient);
75
+ registerGetProjectTool(server, getClient);
76
+ registerGetProjectByIdTool(server, getClient);
77
+ registerCreateProjectTool(server, getClient);
78
+ registerUpdateProjectTool(server, getClient);
79
+ registerDeleteProjectTool(server, getClient);
627
80
  // Task Tools
628
- // =============================================================================
629
- /**
630
- * Tool: List tasks in project
631
- *
632
- * Returns all tasks in a specific project (without project metadata).
633
- * This is a convenience tool that wraps getProjectWithTasks but only returns tasks.
634
- */
635
- server.tool("list_tasks_in_project", "List all tasks in a specific project. Returns only the tasks array without project metadata.", {
636
- projectId: z.string().describe("The ID of the project to list tasks from"),
637
- }, async ({ projectId }) => {
638
- try {
639
- const client = await getClient();
640
- const data = await client.getProjectWithTasks(projectId);
641
- return {
642
- content: [
643
- {
644
- type: "text",
645
- text: JSON.stringify({
646
- success: true,
647
- count: data.tasks.length,
648
- tasks: data.tasks.map((t) => ({
649
- id: t.id,
650
- title: t.title,
651
- content: t.content,
652
- priority: t.priority,
653
- status: t.status,
654
- dueDate: t.dueDate,
655
- startDate: t.startDate,
656
- allDay: t.allDay,
657
- tags: t.tags,
658
- items: t.items?.map((item) => ({
659
- id: item.id,
660
- title: item.title,
661
- status: item.status,
662
- })),
663
- })),
664
- }, null, 2),
665
- },
666
- ],
667
- };
668
- }
669
- catch (error) {
670
- const errorMessage = error instanceof Error ? error.message : String(error);
671
- return {
672
- content: [
673
- {
674
- type: "text",
675
- text: JSON.stringify({
676
- success: false,
677
- error: errorMessage,
678
- }, null, 2),
679
- },
680
- ],
681
- isError: true,
682
- };
683
- }
684
- });
685
- /**
686
- * Tool: Create task
687
- *
688
- * Creates a new task.
689
- */
690
- server.tool("create_task", "Create a new task in TickTick.", {
691
- title: z.string().describe("The title of the task"),
692
- projectId: z
693
- .string()
694
- .optional()
695
- .describe("The project ID (defaults to inbox if not specified)"),
696
- content: z.string().optional().describe("Task description/notes"),
697
- dueDate: z
698
- .string()
699
- .optional()
700
- .describe("Due date in ISO 8601 format (e.g., 2024-01-15T17:00:00+0000)"),
701
- priority: z
702
- .number()
703
- .min(0)
704
- .max(5)
705
- .optional()
706
- .describe("Priority: 0=None, 1=Low, 3=Medium, 5=High"),
707
- tags: z.array(z.string()).optional().describe("Array of tag names"),
708
- allDay: z
709
- .boolean()
710
- .optional()
711
- .describe("Whether this is an all-day task (no specific time)"),
712
- startDate: z
713
- .string()
714
- .optional()
715
- .describe("Start date in ISO 8601 format (e.g., 2024-01-15T09:00:00+0000)"),
716
- timeZone: z
717
- .string()
718
- .optional()
719
- .describe("IANA timezone (e.g., 'America/New_York', 'Europe/London')"),
720
- reminders: z
721
- .array(z.string())
722
- .optional()
723
- .describe("Array of reminder strings in iCalendar TRIGGER format (e.g., 'TRIGGER:P0DT9H0M0S' for 9:00 AM, 'TRIGGER:-PT15M' for 15 minutes before)"),
724
- repeat: z
725
- .string()
726
- .optional()
727
- .describe("Recurrence rule in RRULE format (e.g., 'RRULE:FREQ=DAILY;INTERVAL=1' for daily, 'RRULE:FREQ=WEEKLY;BYDAY=MO,WE,FR' for Mon/Wed/Fri)"),
728
- items: z
729
- .array(z.object({
730
- title: z.string().describe("Subtask/checklist item text"),
731
- status: z
732
- .number()
733
- .min(0)
734
- .max(1)
735
- .optional()
736
- .describe("Status: 0=Unchecked (default), 1=Checked"),
737
- }))
738
- .optional()
739
- .describe("Array of subtask/checklist items"),
740
- }, async ({ title, projectId, content, dueDate, priority, tags, allDay, startDate, timeZone, reminders, repeat, items }) => {
741
- try {
742
- const client = await getClient();
743
- const task = await client.createTask({
744
- title,
745
- projectId,
746
- content,
747
- dueDate,
748
- priority,
749
- allDay,
750
- startDate,
751
- timeZone,
752
- reminders,
753
- repeat,
754
- items,
755
- });
756
- // If tags are provided, update the task with tags
757
- let finalTask = task;
758
- if (tags && tags.length > 0) {
759
- finalTask = await client.updateTask(task.id, { tags });
760
- }
761
- return {
762
- content: [
763
- {
764
- type: "text",
765
- text: JSON.stringify({
766
- success: true,
767
- message: `Task "${finalTask.title}" created successfully!`,
768
- task: {
769
- id: finalTask.id,
770
- title: finalTask.title,
771
- projectId: finalTask.projectId,
772
- content: finalTask.content,
773
- priority: finalTask.priority,
774
- dueDate: finalTask.dueDate,
775
- startDate: finalTask.startDate,
776
- allDay: finalTask.allDay,
777
- timeZone: finalTask.timeZone,
778
- reminders: finalTask.reminders,
779
- repeat: finalTask.repeat,
780
- status: finalTask.status,
781
- tags: finalTask.tags,
782
- items: finalTask.items?.map((item) => ({
783
- id: item.id,
784
- title: item.title,
785
- status: item.status,
786
- })),
787
- },
788
- }, null, 2),
789
- },
790
- ],
791
- };
792
- }
793
- catch (error) {
794
- const errorMessage = error instanceof Error ? error.message : String(error);
795
- return {
796
- content: [
797
- {
798
- type: "text",
799
- text: JSON.stringify({
800
- success: false,
801
- error: errorMessage,
802
- }, null, 2),
803
- },
804
- ],
805
- isError: true,
806
- };
807
- }
808
- });
809
- /**
810
- * Tool: Update task
811
- *
812
- * Updates an existing task.
813
- */
814
- server.tool("update_task", "Update an existing task in TickTick. Supports all task fields including scheduling, reminders, recurrence, and subtasks.", {
815
- taskId: z.string().describe("The ID of the task to update"),
816
- title: z.string().optional().describe("New title for the task"),
817
- content: z.string().optional().describe("New description/notes"),
818
- dueDate: z
819
- .string()
820
- .nullable()
821
- .optional()
822
- .describe("New due date (ISO 8601) or null to remove"),
823
- priority: z
824
- .number()
825
- .min(0)
826
- .max(5)
827
- .optional()
828
- .describe("New priority: 0=None, 1=Low, 3=Medium, 5=High"),
829
- tags: z.array(z.string()).optional().describe("Array of tag names"),
830
- projectId: z
831
- .string()
832
- .optional()
833
- .describe("Move task to a different project by specifying the target project ID"),
834
- allDay: z
835
- .boolean()
836
- .optional()
837
- .describe("Whether this is an all-day task (no specific time)"),
838
- startDate: z
839
- .string()
840
- .nullable()
841
- .optional()
842
- .describe("Start date in ISO 8601 format (e.g., 2024-01-15T09:00:00+0000) or null to remove"),
843
- timeZone: z
844
- .string()
845
- .optional()
846
- .describe("IANA timezone (e.g., 'America/New_York', 'Europe/London')"),
847
- reminders: z
848
- .array(z.string())
849
- .nullable()
850
- .optional()
851
- .describe("Array of reminder strings in iCalendar TRIGGER format (e.g., 'TRIGGER:P0DT9H0M0S' for 9:00 AM, 'TRIGGER:-PT15M' for 15 minutes before) or null to clear all reminders"),
852
- repeat: z
853
- .string()
854
- .nullable()
855
- .optional()
856
- .describe("Recurrence rule in RRULE format (e.g., 'RRULE:FREQ=DAILY;INTERVAL=1' for daily, 'RRULE:FREQ=WEEKLY;BYDAY=MO,WE,FR' for Mon/Wed/Fri) or null to remove recurrence"),
857
- items: z
858
- .array(z.object({
859
- title: z.string().describe("Subtask/checklist item text"),
860
- status: z
861
- .number()
862
- .min(0)
863
- .max(1)
864
- .optional()
865
- .describe("Status: 0=Unchecked (default), 1=Checked"),
866
- }))
867
- .optional()
868
- .describe("Array of subtask/checklist items. Note: This replaces existing items when provided."),
869
- }, async ({ taskId, title, content, dueDate, priority, tags, projectId, allDay, startDate, timeZone, reminders, repeat, items }) => {
870
- try {
871
- const client = await getClient();
872
- const task = await client.updateTask(taskId, {
873
- title,
874
- content,
875
- dueDate,
876
- priority,
877
- tags,
878
- projectId,
879
- allDay,
880
- startDate,
881
- timeZone,
882
- reminders: reminders === null ? undefined : reminders,
883
- repeat: repeat === null ? "" : repeat,
884
- items,
885
- });
886
- return {
887
- content: [
888
- {
889
- type: "text",
890
- text: JSON.stringify({
891
- success: true,
892
- message: `Task "${task.title}" updated successfully!`,
893
- task: {
894
- id: task.id,
895
- title: task.title,
896
- projectId: task.projectId,
897
- content: task.content,
898
- priority: task.priority,
899
- dueDate: task.dueDate,
900
- startDate: task.startDate,
901
- allDay: task.allDay,
902
- timeZone: task.timeZone,
903
- reminders: task.reminders,
904
- repeat: task.repeat,
905
- status: task.status,
906
- tags: task.tags,
907
- items: task.items?.map((item) => ({
908
- id: item.id,
909
- title: item.title,
910
- status: item.status,
911
- })),
912
- },
913
- }, null, 2),
914
- },
915
- ],
916
- };
917
- }
918
- catch (error) {
919
- const errorMessage = error instanceof Error ? error.message : String(error);
920
- return {
921
- content: [
922
- {
923
- type: "text",
924
- text: JSON.stringify({
925
- success: false,
926
- error: errorMessage,
927
- }, null, 2),
928
- },
929
- ],
930
- isError: true,
931
- };
932
- }
933
- });
934
- /**
935
- * Tool: Complete task
936
- *
937
- * Marks a task as complete.
938
- */
939
- server.tool("complete_task", "Mark a task as complete in TickTick.", {
940
- projectId: z.string().describe("The project ID containing the task"),
941
- taskId: z.string().describe("The ID of the task to complete"),
942
- }, async ({ projectId, taskId }) => {
943
- try {
944
- const client = await getClient();
945
- await client.completeTask(projectId, taskId);
946
- return {
947
- content: [
948
- {
949
- type: "text",
950
- text: JSON.stringify({
951
- success: true,
952
- message: "Task completed successfully!",
953
- }, null, 2),
954
- },
955
- ],
956
- };
957
- }
958
- catch (error) {
959
- const errorMessage = error instanceof Error ? error.message : String(error);
960
- return {
961
- content: [
962
- {
963
- type: "text",
964
- text: JSON.stringify({
965
- success: false,
966
- error: errorMessage,
967
- }, null, 2),
968
- },
969
- ],
970
- isError: true,
971
- };
972
- }
973
- });
974
- /**
975
- * Tool: Delete task
976
- *
977
- * Deletes a task.
978
- */
979
- server.tool("delete_task", "Delete a task from TickTick.", {
980
- projectId: z.string().describe("The project ID containing the task"),
981
- taskId: z.string().describe("The ID of the task to delete"),
982
- }, async ({ projectId, taskId }) => {
983
- try {
984
- const client = await getClient();
985
- await client.deleteTask(projectId, taskId);
986
- return {
987
- content: [
988
- {
989
- type: "text",
990
- text: JSON.stringify({
991
- success: true,
992
- message: "Task deleted successfully!",
993
- }, null, 2),
994
- },
995
- ],
996
- };
997
- }
998
- catch (error) {
999
- const errorMessage = error instanceof Error ? error.message : String(error);
1000
- return {
1001
- content: [
1002
- {
1003
- type: "text",
1004
- text: JSON.stringify({
1005
- success: false,
1006
- error: errorMessage,
1007
- }, null, 2),
1008
- },
1009
- ],
1010
- isError: true,
1011
- };
1012
- }
1013
- });
1014
- /**
1015
- * Tool: Get task
1016
- *
1017
- * Gets a specific task by ID.
1018
- */
1019
- server.tool("get_task", "Get a specific task from TickTick.", {
1020
- projectId: z.string().describe("The project ID containing the task"),
1021
- taskId: z.string().describe("The ID of the task to retrieve"),
1022
- }, async ({ projectId, taskId }) => {
1023
- try {
1024
- const client = await getClient();
1025
- const task = await client.getTask(projectId, taskId);
1026
- return {
1027
- content: [
1028
- {
1029
- type: "text",
1030
- text: JSON.stringify({
1031
- success: true,
1032
- task: {
1033
- id: task.id,
1034
- title: task.title,
1035
- projectId: task.projectId,
1036
- content: task.content,
1037
- priority: task.priority,
1038
- status: task.status,
1039
- dueDate: task.dueDate,
1040
- startDate: task.startDate,
1041
- allDay: task.allDay,
1042
- tags: task.tags,
1043
- items: task.items.map((item) => ({
1044
- id: item.id,
1045
- title: item.title,
1046
- status: item.status,
1047
- })),
1048
- createdTime: task.createdTime,
1049
- modifiedTime: task.modifiedTime,
1050
- },
1051
- }, null, 2),
1052
- },
1053
- ],
1054
- };
1055
- }
1056
- catch (error) {
1057
- const errorMessage = error instanceof Error ? error.message : String(error);
1058
- return {
1059
- content: [
1060
- {
1061
- type: "text",
1062
- text: JSON.stringify({
1063
- success: false,
1064
- error: errorMessage,
1065
- }, null, 2),
1066
- },
1067
- ],
1068
- isError: true,
1069
- };
1070
- }
1071
- });
1072
- /**
1073
- * Tool: Batch create tasks
1074
- *
1075
- * Creates multiple tasks at once.
1076
- */
1077
- server.tool("batch_create_tasks", "Create multiple tasks at once in TickTick. More efficient than creating tasks one by one.", {
1078
- tasks: z
1079
- .array(z.object({
1080
- title: z.string().describe("The title of the task (required)"),
1081
- projectId: z
1082
- .string()
1083
- .optional()
1084
- .describe("The project ID (defaults to inbox if not specified)"),
1085
- content: z.string().optional().describe("Task description/notes"),
1086
- priority: z
1087
- .number()
1088
- .min(0)
1089
- .max(5)
1090
- .optional()
1091
- .describe("Priority: 0=None, 1=Low, 3=Medium, 5=High"),
1092
- dueDate: z
1093
- .string()
1094
- .optional()
1095
- .describe("Due date in ISO 8601 format (e.g., 2024-01-15T17:00:00+0000)"),
1096
- }))
1097
- .describe("Array of task objects to create"),
1098
- }, async ({ tasks }) => {
1099
- try {
1100
- const client = await getClient();
1101
- const createdTasks = await client.batchCreateTasks(tasks);
1102
- return {
1103
- content: [
1104
- {
1105
- type: "text",
1106
- text: JSON.stringify({
1107
- success: true,
1108
- message: `Successfully created ${createdTasks.length} tasks!`,
1109
- count: createdTasks.length,
1110
- tasks: createdTasks.map((t) => ({
1111
- id: t.id,
1112
- title: t.title,
1113
- projectId: t.projectId,
1114
- content: t.content,
1115
- priority: t.priority,
1116
- dueDate: t.dueDate,
1117
- status: t.status,
1118
- })),
1119
- }, null, 2),
1120
- },
1121
- ],
1122
- };
1123
- }
1124
- catch (error) {
1125
- const errorMessage = error instanceof Error ? error.message : String(error);
1126
- return {
1127
- content: [
1128
- {
1129
- type: "text",
1130
- text: JSON.stringify({
1131
- success: false,
1132
- error: errorMessage,
1133
- }, null, 2),
1134
- },
1135
- ],
1136
- isError: true,
1137
- };
1138
- }
1139
- });
81
+ registerListTasksInProjectTool(server, getClient);
82
+ registerCreateTaskTool(server, getClient);
83
+ registerUpdateTaskTool(server, getClient);
84
+ registerCompleteTaskTool(server, getClient);
85
+ registerDeleteTaskTool(server, getClient);
86
+ registerGetTaskTool(server, getClient);
87
+ registerBatchCreateTasksTool(server, getClient);
88
+ registerGetTasksDueSoonTool(server, getClient);
89
+ registerSearchTasksTool(server, getClient);
90
+ registerGetHighPriorityTasksTool(server, getClient);
1140
91
  // =============================================================================
1141
92
  // Main Entry Point
1142
93
  // =============================================================================