@createtodo/mcp 0.1.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/dist/index.d.ts +1 -0
- package/dist/index.js +575 -0
- package/package.json +41 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,575 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
+
|
|
7
|
+
// src/tools/comments.ts
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
|
|
10
|
+
// src/client.ts
|
|
11
|
+
var API_URL = "https://api.createtodo.com";
|
|
12
|
+
var API_KEY = process.env.API_KEY || "";
|
|
13
|
+
async function apiRequest(path, options = {}) {
|
|
14
|
+
const { method = "GET", body, params } = options;
|
|
15
|
+
const url = new URL(path, API_URL);
|
|
16
|
+
if (params) {
|
|
17
|
+
for (const [key, value] of Object.entries(params)) {
|
|
18
|
+
url.searchParams.set(key, value);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const headers = {
|
|
22
|
+
"x-api-key": API_KEY
|
|
23
|
+
};
|
|
24
|
+
if (body) {
|
|
25
|
+
headers["Content-Type"] = "application/json";
|
|
26
|
+
}
|
|
27
|
+
const response = await fetch(url.toString(), {
|
|
28
|
+
method,
|
|
29
|
+
headers,
|
|
30
|
+
body: body ? JSON.stringify(body) : void 0
|
|
31
|
+
});
|
|
32
|
+
if (!response.ok) {
|
|
33
|
+
const errorText = await response.text();
|
|
34
|
+
throw new Error(`API error ${response.status}: ${errorText}`);
|
|
35
|
+
}
|
|
36
|
+
return response.json();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// src/tools/comments.ts
|
|
40
|
+
var commentTools = {
|
|
41
|
+
list_comments: {
|
|
42
|
+
description: "List all comments in the workspace. Returns comments from the Electric SQL shape proxy. To find comments for a specific todo, filter the results by issue_id.",
|
|
43
|
+
inputSchema: z.object({}),
|
|
44
|
+
handler: async () => {
|
|
45
|
+
const result = await apiRequest("/shape", {
|
|
46
|
+
params: { table: "comments" }
|
|
47
|
+
});
|
|
48
|
+
return JSON.stringify(result, null, 2);
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
create_comment: {
|
|
52
|
+
description: "Add a comment to a todo",
|
|
53
|
+
inputSchema: z.object({
|
|
54
|
+
todoId: z.string().describe("The ID of the todo to comment on"),
|
|
55
|
+
body: z.string().describe("The comment text (markdown supported)")
|
|
56
|
+
}),
|
|
57
|
+
handler: async ({ todoId, body }) => {
|
|
58
|
+
const id = crypto.randomUUID();
|
|
59
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
60
|
+
const result = await apiRequest("/apply-changes", {
|
|
61
|
+
method: "POST",
|
|
62
|
+
body: {
|
|
63
|
+
todos: [],
|
|
64
|
+
comments: [
|
|
65
|
+
{
|
|
66
|
+
id,
|
|
67
|
+
issue_id: todoId,
|
|
68
|
+
body,
|
|
69
|
+
body_data: null,
|
|
70
|
+
parent_id: null,
|
|
71
|
+
created_at: now,
|
|
72
|
+
updated_at: now,
|
|
73
|
+
modified_columns: ["body", "issue_id"],
|
|
74
|
+
new: true
|
|
75
|
+
}
|
|
76
|
+
],
|
|
77
|
+
containers: [],
|
|
78
|
+
teams: [],
|
|
79
|
+
labels: [],
|
|
80
|
+
workflow_states: [],
|
|
81
|
+
custom_fields: [],
|
|
82
|
+
todo_labels: []
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
return JSON.stringify({ id, ...result }, null, 2);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
// src/tools/labels.ts
|
|
91
|
+
import { z as z2 } from "zod";
|
|
92
|
+
var labelTools = {
|
|
93
|
+
list_labels: {
|
|
94
|
+
description: "List all labels in the workspace. Uses the Electric SQL shape proxy to fetch label data.",
|
|
95
|
+
inputSchema: z2.object({}),
|
|
96
|
+
handler: async () => {
|
|
97
|
+
const result = await apiRequest("/shape", {
|
|
98
|
+
params: { table: "labels" }
|
|
99
|
+
});
|
|
100
|
+
return JSON.stringify(result, null, 2);
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
create_label: {
|
|
104
|
+
description: "Create a new label",
|
|
105
|
+
inputSchema: z2.object({
|
|
106
|
+
name: z2.string().describe("Name of the label"),
|
|
107
|
+
color: z2.string().describe("Color hex code (e.g., #ff0000)"),
|
|
108
|
+
description: z2.string().optional().describe("Description of the label")
|
|
109
|
+
}),
|
|
110
|
+
handler: async (input) => {
|
|
111
|
+
const id = crypto.randomUUID();
|
|
112
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
113
|
+
const modifiedColumns = ["name", "color"];
|
|
114
|
+
if (input.description) modifiedColumns.push("description");
|
|
115
|
+
const result = await apiRequest("/apply-changes", {
|
|
116
|
+
method: "POST",
|
|
117
|
+
body: {
|
|
118
|
+
todos: [],
|
|
119
|
+
comments: [],
|
|
120
|
+
containers: [],
|
|
121
|
+
teams: [],
|
|
122
|
+
labels: [
|
|
123
|
+
{
|
|
124
|
+
id,
|
|
125
|
+
name: input.name,
|
|
126
|
+
color: input.color,
|
|
127
|
+
description: input.description ?? null,
|
|
128
|
+
created_at: now,
|
|
129
|
+
updated_at: now,
|
|
130
|
+
modified_columns: modifiedColumns,
|
|
131
|
+
new: true
|
|
132
|
+
}
|
|
133
|
+
],
|
|
134
|
+
workflow_states: [],
|
|
135
|
+
custom_fields: [],
|
|
136
|
+
todo_labels: []
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
return JSON.stringify({ id, ...result }, null, 2);
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
assign_label: {
|
|
143
|
+
description: "Assign a label to a todo",
|
|
144
|
+
inputSchema: z2.object({
|
|
145
|
+
todoId: z2.string().describe("The ID of the todo"),
|
|
146
|
+
labelId: z2.string().describe("The ID of the label to assign")
|
|
147
|
+
}),
|
|
148
|
+
handler: async ({
|
|
149
|
+
todoId,
|
|
150
|
+
labelId
|
|
151
|
+
}) => {
|
|
152
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
153
|
+
const result = await apiRequest("/apply-changes", {
|
|
154
|
+
method: "POST",
|
|
155
|
+
body: {
|
|
156
|
+
todos: [],
|
|
157
|
+
comments: [],
|
|
158
|
+
containers: [],
|
|
159
|
+
teams: [],
|
|
160
|
+
labels: [],
|
|
161
|
+
workflow_states: [],
|
|
162
|
+
custom_fields: [],
|
|
163
|
+
todo_labels: [
|
|
164
|
+
{
|
|
165
|
+
todo_id: todoId,
|
|
166
|
+
label_id: labelId,
|
|
167
|
+
created_at: now,
|
|
168
|
+
modified_columns: ["todo_id", "label_id"],
|
|
169
|
+
new: true
|
|
170
|
+
}
|
|
171
|
+
]
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
return JSON.stringify(result, null, 2);
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
remove_label: {
|
|
178
|
+
description: "Remove a label from a todo",
|
|
179
|
+
inputSchema: z2.object({
|
|
180
|
+
todoId: z2.string().describe("The ID of the todo"),
|
|
181
|
+
labelId: z2.string().describe("The ID of the label to remove")
|
|
182
|
+
}),
|
|
183
|
+
handler: async ({
|
|
184
|
+
todoId,
|
|
185
|
+
labelId
|
|
186
|
+
}) => {
|
|
187
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
188
|
+
const result = await apiRequest("/apply-changes", {
|
|
189
|
+
method: "POST",
|
|
190
|
+
body: {
|
|
191
|
+
todos: [],
|
|
192
|
+
comments: [],
|
|
193
|
+
containers: [],
|
|
194
|
+
teams: [],
|
|
195
|
+
labels: [],
|
|
196
|
+
workflow_states: [],
|
|
197
|
+
custom_fields: [],
|
|
198
|
+
todo_labels: [
|
|
199
|
+
{
|
|
200
|
+
todo_id: todoId,
|
|
201
|
+
label_id: labelId,
|
|
202
|
+
deleted_at: now,
|
|
203
|
+
modified_columns: ["deleted_at"],
|
|
204
|
+
new: false
|
|
205
|
+
}
|
|
206
|
+
]
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
return JSON.stringify(result, null, 2);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
// src/tools/lists.ts
|
|
215
|
+
import { z as z3 } from "zod";
|
|
216
|
+
var listTools = {
|
|
217
|
+
list_lists: {
|
|
218
|
+
description: "List all todo lists in the workspace",
|
|
219
|
+
inputSchema: z3.object({}),
|
|
220
|
+
handler: async () => {
|
|
221
|
+
const result = await apiRequest("/todo-lists");
|
|
222
|
+
return JSON.stringify(result.todoLists, null, 2);
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
get_list: {
|
|
226
|
+
description: "Get a specific list with its todo items",
|
|
227
|
+
inputSchema: z3.object({
|
|
228
|
+
listId: z3.string().describe("The ID of the list")
|
|
229
|
+
}),
|
|
230
|
+
handler: async ({ listId }) => {
|
|
231
|
+
const result = await apiRequest(`/todo-lists/${listId}`);
|
|
232
|
+
return JSON.stringify(result, null, 2);
|
|
233
|
+
}
|
|
234
|
+
},
|
|
235
|
+
create_list: {
|
|
236
|
+
description: "Create a new todo list",
|
|
237
|
+
inputSchema: z3.object({
|
|
238
|
+
name: z3.string().describe("Name of the list"),
|
|
239
|
+
description: z3.string().optional().describe("Description of the list"),
|
|
240
|
+
color: z3.string().optional().describe("Color hex code (e.g., #ff0000)"),
|
|
241
|
+
icon: z3.string().optional().describe("Icon identifier")
|
|
242
|
+
}),
|
|
243
|
+
handler: async (input) => {
|
|
244
|
+
const id = crypto.randomUUID();
|
|
245
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
246
|
+
const modifiedColumns = ["name", "type"];
|
|
247
|
+
if (input.description) modifiedColumns.push("description");
|
|
248
|
+
if (input.color) modifiedColumns.push("color");
|
|
249
|
+
if (input.icon) modifiedColumns.push("icon");
|
|
250
|
+
const result = await apiRequest("/apply-changes", {
|
|
251
|
+
method: "POST",
|
|
252
|
+
body: {
|
|
253
|
+
todos: [],
|
|
254
|
+
comments: [],
|
|
255
|
+
containers: [
|
|
256
|
+
{
|
|
257
|
+
id,
|
|
258
|
+
name: input.name,
|
|
259
|
+
description: input.description ?? null,
|
|
260
|
+
type: "list",
|
|
261
|
+
color: input.color ?? null,
|
|
262
|
+
icon: input.icon ?? null,
|
|
263
|
+
created_at: now,
|
|
264
|
+
updated_at: now,
|
|
265
|
+
modified_columns: modifiedColumns,
|
|
266
|
+
new: true
|
|
267
|
+
}
|
|
268
|
+
],
|
|
269
|
+
teams: [],
|
|
270
|
+
labels: [],
|
|
271
|
+
workflow_states: [],
|
|
272
|
+
custom_fields: [],
|
|
273
|
+
todo_labels: []
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
return JSON.stringify({ id, ...result }, null, 2);
|
|
277
|
+
}
|
|
278
|
+
},
|
|
279
|
+
update_list: {
|
|
280
|
+
description: "Update an existing list",
|
|
281
|
+
inputSchema: z3.object({
|
|
282
|
+
id: z3.string().describe("The ID of the list to update"),
|
|
283
|
+
name: z3.string().optional().describe("New name"),
|
|
284
|
+
description: z3.string().optional().describe("New description"),
|
|
285
|
+
color: z3.string().optional().describe("New color hex code"),
|
|
286
|
+
icon: z3.string().optional().describe("New icon"),
|
|
287
|
+
isArchived: z3.boolean().optional().describe("Archive or unarchive the list"),
|
|
288
|
+
isPinned: z3.boolean().optional().describe("Pin or unpin the list")
|
|
289
|
+
}),
|
|
290
|
+
handler: async (input) => {
|
|
291
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
292
|
+
const modifiedColumns = [];
|
|
293
|
+
const change = {
|
|
294
|
+
id: input.id,
|
|
295
|
+
updated_at: now,
|
|
296
|
+
new: false
|
|
297
|
+
};
|
|
298
|
+
if (input.name !== void 0) {
|
|
299
|
+
change.name = input.name;
|
|
300
|
+
modifiedColumns.push("name");
|
|
301
|
+
}
|
|
302
|
+
if (input.description !== void 0) {
|
|
303
|
+
change.description = input.description;
|
|
304
|
+
modifiedColumns.push("description");
|
|
305
|
+
}
|
|
306
|
+
if (input.color !== void 0) {
|
|
307
|
+
change.color = input.color;
|
|
308
|
+
modifiedColumns.push("color");
|
|
309
|
+
}
|
|
310
|
+
if (input.icon !== void 0) {
|
|
311
|
+
change.icon = input.icon;
|
|
312
|
+
modifiedColumns.push("icon");
|
|
313
|
+
}
|
|
314
|
+
if (input.isArchived !== void 0) {
|
|
315
|
+
change.is_archived = input.isArchived;
|
|
316
|
+
modifiedColumns.push("is_archived");
|
|
317
|
+
}
|
|
318
|
+
if (input.isPinned !== void 0) {
|
|
319
|
+
change.is_pinned = input.isPinned;
|
|
320
|
+
modifiedColumns.push("is_pinned");
|
|
321
|
+
}
|
|
322
|
+
change.modified_columns = modifiedColumns;
|
|
323
|
+
const result = await apiRequest("/apply-changes", {
|
|
324
|
+
method: "POST",
|
|
325
|
+
body: {
|
|
326
|
+
todos: [],
|
|
327
|
+
comments: [],
|
|
328
|
+
containers: [change],
|
|
329
|
+
teams: [],
|
|
330
|
+
labels: [],
|
|
331
|
+
workflow_states: [],
|
|
332
|
+
custom_fields: [],
|
|
333
|
+
todo_labels: []
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
return JSON.stringify(result, null, 2);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
// src/tools/todos.ts
|
|
342
|
+
import { z as z4 } from "zod";
|
|
343
|
+
var todoTools = {
|
|
344
|
+
list_todos: {
|
|
345
|
+
description: "List todos in a specific list/container",
|
|
346
|
+
inputSchema: z4.object({
|
|
347
|
+
listId: z4.string().describe("The ID of the list to get todos from")
|
|
348
|
+
}),
|
|
349
|
+
handler: async ({ listId }) => {
|
|
350
|
+
const result = await apiRequest(
|
|
351
|
+
`/todo-lists/${listId}`
|
|
352
|
+
);
|
|
353
|
+
return JSON.stringify(result.todoList, null, 2);
|
|
354
|
+
}
|
|
355
|
+
},
|
|
356
|
+
create_todo: {
|
|
357
|
+
description: "Create a new todo item in a list with optional description, priority, assignee, and state.",
|
|
358
|
+
inputSchema: z4.object({
|
|
359
|
+
name: z4.string().describe("The title of the todo"),
|
|
360
|
+
containerId: z4.string().describe("The ID of the list/container to add the todo to"),
|
|
361
|
+
description: z4.string().optional().describe("Description of the todo"),
|
|
362
|
+
priority: z4.enum(["low", "medium", "high", "urgent"]).optional().describe("Priority level"),
|
|
363
|
+
assigneeId: z4.string().optional().describe("Member ID to assign the todo to"),
|
|
364
|
+
stateId: z4.string().optional().describe("Workflow state ID")
|
|
365
|
+
}),
|
|
366
|
+
handler: async (input) => {
|
|
367
|
+
const id = crypto.randomUUID();
|
|
368
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
369
|
+
const modifiedColumns = ["name", "container_id"];
|
|
370
|
+
if (input.description) modifiedColumns.push("description");
|
|
371
|
+
if (input.priority) modifiedColumns.push("priority");
|
|
372
|
+
if (input.assigneeId) modifiedColumns.push("assignee_id");
|
|
373
|
+
if (input.stateId) modifiedColumns.push("state_id");
|
|
374
|
+
const result = await apiRequest("/apply-changes", {
|
|
375
|
+
method: "POST",
|
|
376
|
+
body: {
|
|
377
|
+
todos: [
|
|
378
|
+
{
|
|
379
|
+
id,
|
|
380
|
+
name: input.name,
|
|
381
|
+
container_id: input.containerId,
|
|
382
|
+
description: input.description ?? null,
|
|
383
|
+
priority: input.priority ?? null,
|
|
384
|
+
assignee_id: input.assigneeId ?? null,
|
|
385
|
+
state_id: input.stateId ?? null,
|
|
386
|
+
created_at: now,
|
|
387
|
+
updated_at: now,
|
|
388
|
+
modified_columns: modifiedColumns,
|
|
389
|
+
new: true
|
|
390
|
+
}
|
|
391
|
+
],
|
|
392
|
+
comments: [],
|
|
393
|
+
containers: [],
|
|
394
|
+
teams: [],
|
|
395
|
+
labels: [],
|
|
396
|
+
workflow_states: [],
|
|
397
|
+
custom_fields: [],
|
|
398
|
+
todo_labels: []
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
return JSON.stringify({ id, ...result }, null, 2);
|
|
402
|
+
}
|
|
403
|
+
},
|
|
404
|
+
update_todo: {
|
|
405
|
+
description: "Update an existing todo item",
|
|
406
|
+
inputSchema: z4.object({
|
|
407
|
+
id: z4.string().describe("The ID of the todo to update"),
|
|
408
|
+
name: z4.string().optional().describe("New title"),
|
|
409
|
+
description: z4.string().optional().describe("New description"),
|
|
410
|
+
priority: z4.enum(["low", "medium", "high", "urgent"]).optional().describe("New priority"),
|
|
411
|
+
assigneeId: z4.string().optional().describe("New assignee member ID"),
|
|
412
|
+
stateId: z4.string().optional().describe("New workflow state ID"),
|
|
413
|
+
containerId: z4.string().optional().describe("Move to a different list/container")
|
|
414
|
+
}),
|
|
415
|
+
handler: async (input) => {
|
|
416
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
417
|
+
const modifiedColumns = [];
|
|
418
|
+
const change = {
|
|
419
|
+
id: input.id,
|
|
420
|
+
updated_at: now,
|
|
421
|
+
new: false
|
|
422
|
+
};
|
|
423
|
+
if (input.name !== void 0) {
|
|
424
|
+
change.name = input.name;
|
|
425
|
+
modifiedColumns.push("name");
|
|
426
|
+
}
|
|
427
|
+
if (input.description !== void 0) {
|
|
428
|
+
change.description = input.description;
|
|
429
|
+
modifiedColumns.push("description");
|
|
430
|
+
}
|
|
431
|
+
if (input.priority !== void 0) {
|
|
432
|
+
change.priority = input.priority;
|
|
433
|
+
modifiedColumns.push("priority");
|
|
434
|
+
}
|
|
435
|
+
if (input.assigneeId !== void 0) {
|
|
436
|
+
change.assignee_id = input.assigneeId;
|
|
437
|
+
modifiedColumns.push("assignee_id");
|
|
438
|
+
}
|
|
439
|
+
if (input.stateId !== void 0) {
|
|
440
|
+
change.state_id = input.stateId;
|
|
441
|
+
modifiedColumns.push("state_id");
|
|
442
|
+
}
|
|
443
|
+
if (input.containerId !== void 0) {
|
|
444
|
+
change.container_id = input.containerId;
|
|
445
|
+
modifiedColumns.push("container_id");
|
|
446
|
+
}
|
|
447
|
+
change.modified_columns = modifiedColumns;
|
|
448
|
+
const result = await apiRequest("/apply-changes", {
|
|
449
|
+
method: "POST",
|
|
450
|
+
body: {
|
|
451
|
+
todos: [change],
|
|
452
|
+
comments: [],
|
|
453
|
+
containers: [],
|
|
454
|
+
teams: [],
|
|
455
|
+
labels: [],
|
|
456
|
+
workflow_states: [],
|
|
457
|
+
custom_fields: [],
|
|
458
|
+
todo_labels: []
|
|
459
|
+
}
|
|
460
|
+
});
|
|
461
|
+
return JSON.stringify(result, null, 2);
|
|
462
|
+
}
|
|
463
|
+
},
|
|
464
|
+
delete_todo: {
|
|
465
|
+
description: "Delete a todo item (soft delete)",
|
|
466
|
+
inputSchema: z4.object({
|
|
467
|
+
id: z4.string().describe("The ID of the todo to delete")
|
|
468
|
+
}),
|
|
469
|
+
handler: async ({ id }) => {
|
|
470
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
471
|
+
const result = await apiRequest("/apply-changes", {
|
|
472
|
+
method: "POST",
|
|
473
|
+
body: {
|
|
474
|
+
todos: [
|
|
475
|
+
{
|
|
476
|
+
id,
|
|
477
|
+
deleted_at: now,
|
|
478
|
+
updated_at: now,
|
|
479
|
+
modified_columns: ["deleted_at"],
|
|
480
|
+
new: false
|
|
481
|
+
}
|
|
482
|
+
],
|
|
483
|
+
comments: [],
|
|
484
|
+
containers: [],
|
|
485
|
+
teams: [],
|
|
486
|
+
labels: [],
|
|
487
|
+
workflow_states: [],
|
|
488
|
+
custom_fields: [],
|
|
489
|
+
todo_labels: []
|
|
490
|
+
}
|
|
491
|
+
});
|
|
492
|
+
return JSON.stringify(result, null, 2);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
// src/tools/workspace.ts
|
|
498
|
+
import { z as z5 } from "zod";
|
|
499
|
+
var workspaceTools = {
|
|
500
|
+
list_workflow_states: {
|
|
501
|
+
description: "List all workflow states (e.g., Todo, In Progress, Done) in the workspace",
|
|
502
|
+
inputSchema: z5.object({}),
|
|
503
|
+
handler: async () => {
|
|
504
|
+
const result = await apiRequest("/shape", {
|
|
505
|
+
params: { table: "workflow_states" }
|
|
506
|
+
});
|
|
507
|
+
return JSON.stringify(result, null, 2);
|
|
508
|
+
}
|
|
509
|
+
},
|
|
510
|
+
list_teams: {
|
|
511
|
+
description: "List all teams in the workspace",
|
|
512
|
+
inputSchema: z5.object({}),
|
|
513
|
+
handler: async () => {
|
|
514
|
+
const result = await apiRequest("/shape", {
|
|
515
|
+
params: { table: "teams" }
|
|
516
|
+
});
|
|
517
|
+
return JSON.stringify(result, null, 2);
|
|
518
|
+
}
|
|
519
|
+
},
|
|
520
|
+
list_members: {
|
|
521
|
+
description: "List all members in the workspace",
|
|
522
|
+
inputSchema: z5.object({}),
|
|
523
|
+
handler: async () => {
|
|
524
|
+
const result = await apiRequest("/shape", {
|
|
525
|
+
params: { table: "members" }
|
|
526
|
+
});
|
|
527
|
+
return JSON.stringify(result, null, 2);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
};
|
|
531
|
+
|
|
532
|
+
// src/index.ts
|
|
533
|
+
var server = new McpServer({
|
|
534
|
+
name: "createtodo",
|
|
535
|
+
version: "1.0.0"
|
|
536
|
+
});
|
|
537
|
+
var allTools = {
|
|
538
|
+
...todoTools,
|
|
539
|
+
...listTools,
|
|
540
|
+
...labelTools,
|
|
541
|
+
...commentTools,
|
|
542
|
+
...workspaceTools
|
|
543
|
+
};
|
|
544
|
+
for (const [name, tool] of Object.entries(allTools)) {
|
|
545
|
+
server.tool(
|
|
546
|
+
name,
|
|
547
|
+
tool.description,
|
|
548
|
+
tool.inputSchema.shape,
|
|
549
|
+
async (params) => {
|
|
550
|
+
try {
|
|
551
|
+
const result = await tool.handler(params);
|
|
552
|
+
return { content: [{ type: "text", text: result }] };
|
|
553
|
+
} catch (error) {
|
|
554
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
555
|
+
return {
|
|
556
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
557
|
+
isError: true
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
);
|
|
562
|
+
}
|
|
563
|
+
async function main() {
|
|
564
|
+
if (!process.env.API_KEY) {
|
|
565
|
+
console.error("Error: API_KEY environment variable is required");
|
|
566
|
+
process.exit(1);
|
|
567
|
+
}
|
|
568
|
+
const transport = new StdioServerTransport();
|
|
569
|
+
await server.connect(transport);
|
|
570
|
+
console.error("createtodo MCP server running on stdio");
|
|
571
|
+
}
|
|
572
|
+
main().catch((error) => {
|
|
573
|
+
console.error("Fatal error:", error);
|
|
574
|
+
process.exit(1);
|
|
575
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@createtodo/mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server for managing createtodo tasks, lists, labels, and more",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/martinory/frosa",
|
|
10
|
+
"directory": "apps/createtodo/packages/mcp"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://createtodo.com",
|
|
13
|
+
"keywords": [
|
|
14
|
+
"mcp",
|
|
15
|
+
"model-context-protocol",
|
|
16
|
+
"createtodo",
|
|
17
|
+
"todo",
|
|
18
|
+
"task-management",
|
|
19
|
+
"ai-tools"
|
|
20
|
+
],
|
|
21
|
+
"bin": {
|
|
22
|
+
"createtodo-mcp": "./dist/index.js"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsup src/index.ts --format esm --dts --clean",
|
|
29
|
+
"prepublishOnly": "pnpm build",
|
|
30
|
+
"dev": "tsx src/index.ts"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
34
|
+
"zod": "^3.24.0"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"tsup": "^8.4.0",
|
|
38
|
+
"tsx": "^4.19.0",
|
|
39
|
+
"typescript": "^5.9.3"
|
|
40
|
+
}
|
|
41
|
+
}
|