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/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
+ }