kaneo-mcp 0.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/bin/kaneo-mcp.js +2 -0
- package/dist/index.js +1625 -0
- package/package.json +33 -0
- package/src/client.ts +443 -0
- package/src/index.ts +1350 -0
- package/src/types.ts +104 -0
- package/tsconfig.json +8 -0
- package/vitest.config.ts +7 -0
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "kaneo-mcp",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"kaneo-mcp": "./bin/kaneo-mcp.js"
|
|
9
|
+
},
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "esbuild src/index.ts --bundle --platform=node --outdir=dist --format=esm --packages=external --external:fs --external:path --external:crypto --external:os --external:util --external:stream --external:buffer --external:events --external:url --external:querystring --external:http --external:https --external:net --external:tls --external:zlib",
|
|
19
|
+
"lint": "biome check --write .",
|
|
20
|
+
"test": "vitest run --config vitest.config.ts"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@modelcontextprotocol/sdk": "^1.3.0",
|
|
24
|
+
"hono": "^4.12.8",
|
|
25
|
+
"zod": "^3.24.0"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@kaneo/typescript-config": "workspace:*",
|
|
29
|
+
"esbuild": "^0.27.4",
|
|
30
|
+
"typescript": "^5.9.2",
|
|
31
|
+
"vitest": "^4.1.2"
|
|
32
|
+
}
|
|
33
|
+
}
|
package/src/client.ts
ADDED
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
import type { KaneoConfig } from "./types";
|
|
2
|
+
|
|
3
|
+
export class KaneoClient {
|
|
4
|
+
private baseUrl: string;
|
|
5
|
+
private token: string;
|
|
6
|
+
|
|
7
|
+
constructor(config: KaneoConfig) {
|
|
8
|
+
this.baseUrl = config.baseUrl.replace(/\/+$/, "");
|
|
9
|
+
if (!this.baseUrl.endsWith("/api")) {
|
|
10
|
+
this.baseUrl = `${this.baseUrl}/api`;
|
|
11
|
+
}
|
|
12
|
+
this.token = config.token;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
private async request<T>(
|
|
16
|
+
path: string,
|
|
17
|
+
options: RequestInit = {},
|
|
18
|
+
): Promise<T> {
|
|
19
|
+
const url = `${this.baseUrl}${path}`;
|
|
20
|
+
const headers: Record<string, string> = {
|
|
21
|
+
"Content-Type": "application/json",
|
|
22
|
+
Authorization: `Bearer ${this.token}`,
|
|
23
|
+
...(options.headers as Record<string, string>),
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const response = await fetch(url, {
|
|
27
|
+
...options,
|
|
28
|
+
headers,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
if (!response.ok) {
|
|
32
|
+
const error = await response.text();
|
|
33
|
+
throw new Error(`Kaneo API error: ${response.status} ${error}`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return response.json();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Health
|
|
40
|
+
async health() {
|
|
41
|
+
return this.request<{ status: string }>("/health");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Config
|
|
45
|
+
async getConfig() {
|
|
46
|
+
return this.request<{ public: Record<string, unknown> }>("/config");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Projects
|
|
50
|
+
async listProjects(workspaceId: string) {
|
|
51
|
+
return this.request<{ projects: unknown[] }>(
|
|
52
|
+
`/project?workspaceId=${encodeURIComponent(workspaceId)}`,
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async createProject(data: {
|
|
57
|
+
name: string;
|
|
58
|
+
description?: string;
|
|
59
|
+
workspaceId: string;
|
|
60
|
+
}) {
|
|
61
|
+
return this.request<{ project: unknown }>("/project", {
|
|
62
|
+
method: "POST",
|
|
63
|
+
body: JSON.stringify(data),
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async getProject(id: string) {
|
|
68
|
+
return this.request<{ project: unknown }>(`/project/${id}`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async updateProject(
|
|
72
|
+
id: string,
|
|
73
|
+
data: { name?: string; description?: string },
|
|
74
|
+
) {
|
|
75
|
+
return this.request<{ project: unknown }>(`/project/${id}`, {
|
|
76
|
+
method: "PUT",
|
|
77
|
+
body: JSON.stringify(data),
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async deleteProject(id: string) {
|
|
82
|
+
return this.request<{ success: boolean }>(`/project/${id}`, {
|
|
83
|
+
method: "DELETE",
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async archiveProject(id: string) {
|
|
88
|
+
return this.request<{ project: unknown }>(`/project/${id}/archive`, {
|
|
89
|
+
method: "PUT",
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async unarchiveProject(id: string) {
|
|
94
|
+
return this.request<{ project: unknown }>(`/project/${id}/unarchive`, {
|
|
95
|
+
method: "PUT",
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Tasks
|
|
100
|
+
async listTasks(
|
|
101
|
+
projectId: string,
|
|
102
|
+
params?: {
|
|
103
|
+
status?: string;
|
|
104
|
+
priority?: string;
|
|
105
|
+
assigneeId?: string;
|
|
106
|
+
},
|
|
107
|
+
) {
|
|
108
|
+
const query = new URLSearchParams();
|
|
109
|
+
if (params?.status) query.set("status", params.status);
|
|
110
|
+
if (params?.priority) query.set("priority", params.priority);
|
|
111
|
+
if (params?.assigneeId) query.set("assigneeId", params.assigneeId);
|
|
112
|
+
|
|
113
|
+
const queryString = query.toString();
|
|
114
|
+
return this.request<{ tasks: unknown[] }>(
|
|
115
|
+
`/task/tasks/${projectId}${queryString ? `?${queryString}` : ""}`,
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async createTask(
|
|
120
|
+
projectId: string,
|
|
121
|
+
data: {
|
|
122
|
+
title: string;
|
|
123
|
+
description?: string;
|
|
124
|
+
status?: string;
|
|
125
|
+
priority?: string;
|
|
126
|
+
assigneeId?: string;
|
|
127
|
+
startDate?: string;
|
|
128
|
+
dueDate?: string;
|
|
129
|
+
},
|
|
130
|
+
) {
|
|
131
|
+
return this.request<{ task: unknown }>(`/task/${projectId}`, {
|
|
132
|
+
method: "POST",
|
|
133
|
+
body: JSON.stringify(data),
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async getTask(id: string) {
|
|
138
|
+
return this.request<{ task: unknown }>(`/task/${id}`);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async updateTask(
|
|
142
|
+
id: string,
|
|
143
|
+
data: {
|
|
144
|
+
title?: string;
|
|
145
|
+
description?: string;
|
|
146
|
+
status?: string;
|
|
147
|
+
priority?: string;
|
|
148
|
+
assigneeId?: string;
|
|
149
|
+
startDate?: string;
|
|
150
|
+
dueDate?: string;
|
|
151
|
+
},
|
|
152
|
+
) {
|
|
153
|
+
return this.request<{ task: unknown }>(`/task/${id}`, {
|
|
154
|
+
method: "PUT",
|
|
155
|
+
body: JSON.stringify(data),
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async deleteTask(id: string) {
|
|
160
|
+
return this.request<{ success: boolean }>(`/task/${id}`, {
|
|
161
|
+
method: "DELETE",
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async updateTaskStatus(id: string, status: string) {
|
|
166
|
+
return this.request<{ task: unknown }>(`/task/status/${id}`, {
|
|
167
|
+
method: "PUT",
|
|
168
|
+
body: JSON.stringify({ status }),
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async updateTaskPriority(id: string, priority: string) {
|
|
173
|
+
return this.request<{ task: unknown }>(`/task/priority/${id}`, {
|
|
174
|
+
method: "PUT",
|
|
175
|
+
body: JSON.stringify({ priority }),
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async updateTaskAssignee(id: string, userId: string | null) {
|
|
180
|
+
return this.request<{ task: unknown }>(`/task/assignee/${id}`, {
|
|
181
|
+
method: "PUT",
|
|
182
|
+
body: JSON.stringify({ userId }),
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
async updateTaskDueDate(id: string, dueDate: string | null) {
|
|
187
|
+
return this.request<{ task: unknown }>(`/task/due-date/${id}`, {
|
|
188
|
+
method: "PUT",
|
|
189
|
+
body: JSON.stringify({ dueDate }),
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async updateTaskTitle(id: string, title: string) {
|
|
194
|
+
return this.request<{ task: unknown }>(`/task/title/${id}`, {
|
|
195
|
+
method: "PUT",
|
|
196
|
+
body: JSON.stringify({ title }),
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
async updateTaskDescription(id: string, description: string) {
|
|
201
|
+
return this.request<{ task: unknown }>(`/task/description/${id}`, {
|
|
202
|
+
method: "PUT",
|
|
203
|
+
body: JSON.stringify({ description }),
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async moveTask(id: string, projectId: string) {
|
|
208
|
+
return this.request<{ task: unknown }>(`/task/move/${id}`, {
|
|
209
|
+
method: "PUT",
|
|
210
|
+
body: JSON.stringify({ projectId }),
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Columns
|
|
215
|
+
async listColumns(projectId: string) {
|
|
216
|
+
return this.request<{ columns: unknown[] }>(`/column/${projectId}`);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
async createColumn(
|
|
220
|
+
projectId: string,
|
|
221
|
+
data: { name: string; order?: number },
|
|
222
|
+
) {
|
|
223
|
+
return this.request<{ column: unknown }>(`/column/${projectId}`, {
|
|
224
|
+
method: "POST",
|
|
225
|
+
body: JSON.stringify(data),
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
async updateColumn(id: string, data: { name?: string; order?: number }) {
|
|
230
|
+
return this.request<{ column: unknown }>(`/column/${id}`, {
|
|
231
|
+
method: "PUT",
|
|
232
|
+
body: JSON.stringify(data),
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
async deleteColumn(id: string) {
|
|
237
|
+
return this.request<{ success: boolean }>(`/column/${id}`, {
|
|
238
|
+
method: "DELETE",
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
async reorderColumns(projectId: string, columnIds: string[]) {
|
|
243
|
+
return this.request<{ columns: unknown[] }>(
|
|
244
|
+
`/column/reorder/${projectId}`,
|
|
245
|
+
{
|
|
246
|
+
method: "PUT",
|
|
247
|
+
body: JSON.stringify({ columnIds }),
|
|
248
|
+
},
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Comments
|
|
253
|
+
async listComments(taskId: string) {
|
|
254
|
+
return this.request<{ comments: unknown[] }>(`/comment/${taskId}`);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
async createComment(taskId: string, content: string) {
|
|
258
|
+
return this.request<{ comment: unknown }>(`/comment/${taskId}`, {
|
|
259
|
+
method: "POST",
|
|
260
|
+
body: JSON.stringify({ content }),
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
async updateComment(id: string, content: string) {
|
|
265
|
+
return this.request<{ comment: unknown }>(`/comment/${id}`, {
|
|
266
|
+
method: "PUT",
|
|
267
|
+
body: JSON.stringify({ content }),
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
async deleteComment(id: string) {
|
|
272
|
+
return this.request<{ success: boolean }>(`/comment/${id}`, {
|
|
273
|
+
method: "DELETE",
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Labels
|
|
278
|
+
async listLabelsForTask(taskId: string) {
|
|
279
|
+
return this.request<{ labels: unknown[] }>(`/label/task/${taskId}`);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
async listWorkspaceLabels(workspaceId: string) {
|
|
283
|
+
return this.request<{ labels: unknown[] }>(
|
|
284
|
+
`/label/workspace/${workspaceId}`,
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
async createLabel(data: {
|
|
289
|
+
name: string;
|
|
290
|
+
color: string;
|
|
291
|
+
workspaceId: string;
|
|
292
|
+
}) {
|
|
293
|
+
return this.request<{ label: unknown }>("/label", {
|
|
294
|
+
method: "POST",
|
|
295
|
+
body: JSON.stringify(data),
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
async getLabel(id: string) {
|
|
300
|
+
return this.request<{ label: unknown }>(`/label/${id}`);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
async updateLabel(id: string, data: { name?: string; color?: string }) {
|
|
304
|
+
return this.request<{ label: unknown }>(`/label/${id}`, {
|
|
305
|
+
method: "PUT",
|
|
306
|
+
body: JSON.stringify(data),
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
async deleteLabel(id: string) {
|
|
311
|
+
return this.request<{ success: boolean }>(`/label/${id}`, {
|
|
312
|
+
method: "DELETE",
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
async attachLabelToTask(labelId: string, taskId: string) {
|
|
317
|
+
return this.request<{ success: boolean }>(`/label/${labelId}/task`, {
|
|
318
|
+
method: "PUT",
|
|
319
|
+
body: JSON.stringify({ taskId }),
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
async detachLabelFromTask(labelId: string, taskId: string) {
|
|
324
|
+
return this.request<{ success: boolean }>(`/label/${labelId}/task`, {
|
|
325
|
+
method: "DELETE",
|
|
326
|
+
body: JSON.stringify({ taskId }),
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Time Entries
|
|
331
|
+
async listTimeEntries(taskId: string) {
|
|
332
|
+
return this.request<{ timeEntries: unknown[] }>(
|
|
333
|
+
`/time-entry/task/${taskId}`,
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
async getTimeEntry(id: string) {
|
|
338
|
+
return this.request<{ timeEntry: unknown }>(`/time-entry/${id}`);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
async createTimeEntry(data: {
|
|
342
|
+
taskId: string;
|
|
343
|
+
duration: number;
|
|
344
|
+
description?: string;
|
|
345
|
+
}) {
|
|
346
|
+
return this.request<{ timeEntry: unknown }>("/time-entry", {
|
|
347
|
+
method: "POST",
|
|
348
|
+
body: JSON.stringify(data),
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
async updateTimeEntry(
|
|
353
|
+
id: string,
|
|
354
|
+
data: { duration?: number; description?: string },
|
|
355
|
+
) {
|
|
356
|
+
return this.request<{ timeEntry: unknown }>(`/time-entry/${id}`, {
|
|
357
|
+
method: "PUT",
|
|
358
|
+
body: JSON.stringify(data),
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Task Relations
|
|
363
|
+
async listTaskRelations(taskId: string) {
|
|
364
|
+
return this.request<{ relations: unknown[] }>(`/task-relation/${taskId}`);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
async createTaskRelation(data: {
|
|
368
|
+
taskId: string;
|
|
369
|
+
relatedTaskId: string;
|
|
370
|
+
type: "subtask" | "blocks" | "related";
|
|
371
|
+
}) {
|
|
372
|
+
return this.request<{ relation: unknown }>("/task-relation", {
|
|
373
|
+
method: "POST",
|
|
374
|
+
body: JSON.stringify(data),
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
async deleteTaskRelation(id: string) {
|
|
379
|
+
return this.request<{ success: boolean }>(`/task-relation/${id}`, {
|
|
380
|
+
method: "DELETE",
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// Notifications
|
|
385
|
+
async listNotifications() {
|
|
386
|
+
return this.request<{ notifications: unknown[] }>("/notification");
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
async markNotificationRead(id: string) {
|
|
390
|
+
return this.request<{ notification: unknown }>(`/notification/${id}/read`, {
|
|
391
|
+
method: "PATCH",
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
async markAllNotificationsRead() {
|
|
396
|
+
return this.request<{ success: boolean }>("/notification/read-all", {
|
|
397
|
+
method: "PATCH",
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
async clearAllNotifications() {
|
|
402
|
+
return this.request<{ success: boolean }>("/notification/clear-all", {
|
|
403
|
+
method: "DELETE",
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// Workspace
|
|
408
|
+
async listWorkspaceMembers(workspaceId: string) {
|
|
409
|
+
return this.request<{ members: unknown[] }>(
|
|
410
|
+
`/workspace/${workspaceId}/members`,
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// Search
|
|
415
|
+
async search(
|
|
416
|
+
query: string,
|
|
417
|
+
type?:
|
|
418
|
+
| "all"
|
|
419
|
+
| "tasks"
|
|
420
|
+
| "projects"
|
|
421
|
+
| "workspaces"
|
|
422
|
+
| "comments"
|
|
423
|
+
| "activities",
|
|
424
|
+
) {
|
|
425
|
+
const params = new URLSearchParams({ q: query });
|
|
426
|
+
if (type) params.set("type", type);
|
|
427
|
+
return this.request<{ results: unknown[] }>(`/search?${params.toString()}`);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Activity
|
|
431
|
+
async listActivities(taskId: string) {
|
|
432
|
+
return this.request<{ activities: unknown[] }>(`/activity/${taskId}`);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// Invitations
|
|
436
|
+
async listPendingInvitations() {
|
|
437
|
+
return this.request<{ invitations: unknown[] }>("/invitation/pending");
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
async getInvitation(id: string) {
|
|
441
|
+
return this.request<{ invitation: unknown }>(`/invitation/${id}`);
|
|
442
|
+
}
|
|
443
|
+
}
|