@omnicoreos/planka-mcp 0.2.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/CHANGELOG.md +86 -0
- package/CONTRIBUTING.md +37 -0
- package/CREDITS.md +39 -0
- package/LICENSE +22 -0
- package/README.es.md +213 -0
- package/README.md +213 -0
- package/dist/client.d.ts +96 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +281 -0
- package/dist/client.js.map +1 -0
- package/dist/errors.d.ts +85 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +226 -0
- package/dist/errors.js.map +1 -0
- package/dist/identity.generated.d.ts +3 -0
- package/dist/identity.generated.d.ts.map +1 -0
- package/dist/identity.generated.js +4 -0
- package/dist/identity.generated.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +93 -0
- package/dist/index.js.map +1 -0
- package/dist/operations/attachments.d.ts +34 -0
- package/dist/operations/attachments.d.ts.map +1 -0
- package/dist/operations/attachments.js +89 -0
- package/dist/operations/attachments.js.map +1 -0
- package/dist/operations/board-id.d.ts +9 -0
- package/dist/operations/board-id.d.ts.map +1 -0
- package/dist/operations/board-id.js +86 -0
- package/dist/operations/board-id.js.map +1 -0
- package/dist/operations/boards.d.ts +143 -0
- package/dist/operations/boards.d.ts.map +1 -0
- package/dist/operations/boards.js +391 -0
- package/dist/operations/boards.js.map +1 -0
- package/dist/operations/cards.d.ts +36 -0
- package/dist/operations/cards.d.ts.map +1 -0
- package/dist/operations/cards.js +81 -0
- package/dist/operations/cards.js.map +1 -0
- package/dist/operations/comments.d.ts +28 -0
- package/dist/operations/comments.d.ts.map +1 -0
- package/dist/operations/comments.js +53 -0
- package/dist/operations/comments.js.map +1 -0
- package/dist/operations/labels.d.ts +60 -0
- package/dist/operations/labels.d.ts.map +1 -0
- package/dist/operations/labels.js +146 -0
- package/dist/operations/labels.js.map +1 -0
- package/dist/operations/lists.d.ts +15 -0
- package/dist/operations/lists.d.ts.map +1 -0
- package/dist/operations/lists.js +40 -0
- package/dist/operations/lists.js.map +1 -0
- package/dist/operations/projects.d.ts +24 -0
- package/dist/operations/projects.d.ts.map +1 -0
- package/dist/operations/projects.js +61 -0
- package/dist/operations/projects.js.map +1 -0
- package/dist/operations/tasks.d.ts +30 -0
- package/dist/operations/tasks.d.ts.map +1 -0
- package/dist/operations/tasks.js +111 -0
- package/dist/operations/tasks.js.map +1 -0
- package/dist/schemas/entities.d.ts +389 -0
- package/dist/schemas/entities.d.ts.map +1 -0
- package/dist/schemas/entities.js +202 -0
- package/dist/schemas/entities.js.map +1 -0
- package/dist/schemas/requests.d.ts +539 -0
- package/dist/schemas/requests.d.ts.map +1 -0
- package/dist/schemas/requests.js +187 -0
- package/dist/schemas/requests.js.map +1 -0
- package/dist/schemas/responses.d.ts +2626 -0
- package/dist/schemas/responses.d.ts.map +1 -0
- package/dist/schemas/responses.js +75 -0
- package/dist/schemas/responses.js.map +1 -0
- package/dist/tools/attachments.d.ts +270 -0
- package/dist/tools/attachments.d.ts.map +1 -0
- package/dist/tools/attachments.js +247 -0
- package/dist/tools/attachments.js.map +1 -0
- package/dist/tools/cards.d.ts +401 -0
- package/dist/tools/cards.d.ts.map +1 -0
- package/dist/tools/cards.js +368 -0
- package/dist/tools/cards.js.map +1 -0
- package/dist/tools/comments.d.ts +217 -0
- package/dist/tools/comments.d.ts.map +1 -0
- package/dist/tools/comments.js +208 -0
- package/dist/tools/comments.js.map +1 -0
- package/dist/tools/index.d.ts +1293 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +62 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/labels.d.ts +214 -0
- package/dist/tools/labels.d.ts.map +1 -0
- package/dist/tools/labels.js +302 -0
- package/dist/tools/labels.js.map +1 -0
- package/dist/tools/lists.d.ts +117 -0
- package/dist/tools/lists.d.ts.map +1 -0
- package/dist/tools/lists.js +178 -0
- package/dist/tools/lists.js.map +1 -0
- package/dist/tools/navigation.d.ts +106 -0
- package/dist/tools/navigation.d.ts.map +1 -0
- package/dist/tools/navigation.js +151 -0
- package/dist/tools/navigation.js.map +1 -0
- package/dist/tools/queries.d.ts +380 -0
- package/dist/tools/queries.d.ts.map +1 -0
- package/dist/tools/queries.js +256 -0
- package/dist/tools/queries.js.map +1 -0
- package/dist/tools/tasks.d.ts +223 -0
- package/dist/tools/tasks.d.ts.map +1 -0
- package/dist/tools/tasks.js +169 -0
- package/dist/tools/tasks.js.map +1 -0
- package/docs/planka-2x-gotchas.md +192 -0
- package/docs/tools.md +659 -0
- package/docs/troubleshooting.md +138 -0
- package/package.json +71 -0
- package/project.identity.json +7 -0
- package/scripts/bootstrap-board.mjs +160 -0
- package/scripts/lib/planka-api.mjs +244 -0
- package/scripts/lib/workflow-template.mjs +26 -0
- package/scripts/setup.mjs +236 -0
- package/scripts/setup.sh +48 -0
- package/scripts/sync-identity.mjs +57 -0
- package/server.json +43 -0
- package/tests/smoke/planka-smoke.mjs +920 -0
- package/workflow/README.md +232 -0
- package/workflow/board-template.md +115 -0
- package/workflow/skills/planka-close-card/SKILL.md +81 -0
- package/workflow/skills/planka-orchestrator/SKILL.md +126 -0
- package/workflow/worktrees/README.md +104 -0
- package/workflow/worktrees/wt.conf.example +24 -0
- package/workflow/worktrees/wt.sh +345 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool: planka_create_tasks
|
|
3
|
+
* Add one or more tasks (checklist items) to a card.
|
|
4
|
+
*/
|
|
5
|
+
export declare const createTasksTool: {
|
|
6
|
+
name: string;
|
|
7
|
+
description: string;
|
|
8
|
+
inputSchema: {
|
|
9
|
+
type: "object";
|
|
10
|
+
properties: {
|
|
11
|
+
cardId: {
|
|
12
|
+
type: string;
|
|
13
|
+
description: string;
|
|
14
|
+
};
|
|
15
|
+
tasks: {
|
|
16
|
+
type: string;
|
|
17
|
+
items: {
|
|
18
|
+
type: string;
|
|
19
|
+
};
|
|
20
|
+
minItems: number;
|
|
21
|
+
description: string;
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
required: string[];
|
|
25
|
+
};
|
|
26
|
+
handler: (params: {
|
|
27
|
+
cardId: string;
|
|
28
|
+
tasks: string[];
|
|
29
|
+
}) => Promise<{
|
|
30
|
+
content: {
|
|
31
|
+
type: "text";
|
|
32
|
+
text: string;
|
|
33
|
+
}[];
|
|
34
|
+
isError?: undefined;
|
|
35
|
+
} | {
|
|
36
|
+
content: {
|
|
37
|
+
type: "text";
|
|
38
|
+
text: string;
|
|
39
|
+
}[];
|
|
40
|
+
isError: boolean;
|
|
41
|
+
}>;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Tool: planka_update_task
|
|
45
|
+
* Update a task's name or completion status.
|
|
46
|
+
*/
|
|
47
|
+
export declare const updateTaskTool: {
|
|
48
|
+
name: string;
|
|
49
|
+
description: string;
|
|
50
|
+
inputSchema: {
|
|
51
|
+
type: "object";
|
|
52
|
+
properties: {
|
|
53
|
+
taskId: {
|
|
54
|
+
type: string;
|
|
55
|
+
description: string;
|
|
56
|
+
};
|
|
57
|
+
name: {
|
|
58
|
+
type: string;
|
|
59
|
+
description: string;
|
|
60
|
+
};
|
|
61
|
+
isCompleted: {
|
|
62
|
+
type: string;
|
|
63
|
+
description: string;
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
required: string[];
|
|
67
|
+
};
|
|
68
|
+
handler: (params: {
|
|
69
|
+
taskId: string;
|
|
70
|
+
name?: string;
|
|
71
|
+
isCompleted?: boolean;
|
|
72
|
+
}) => Promise<{
|
|
73
|
+
content: {
|
|
74
|
+
type: "text";
|
|
75
|
+
text: string;
|
|
76
|
+
}[];
|
|
77
|
+
isError?: undefined;
|
|
78
|
+
} | {
|
|
79
|
+
content: {
|
|
80
|
+
type: "text";
|
|
81
|
+
text: string;
|
|
82
|
+
}[];
|
|
83
|
+
isError: boolean;
|
|
84
|
+
}>;
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* Tool: planka_delete_task
|
|
88
|
+
* Delete a task from a card.
|
|
89
|
+
*/
|
|
90
|
+
export declare const deleteTaskTool: {
|
|
91
|
+
name: string;
|
|
92
|
+
description: string;
|
|
93
|
+
inputSchema: {
|
|
94
|
+
type: "object";
|
|
95
|
+
properties: {
|
|
96
|
+
taskId: {
|
|
97
|
+
type: string;
|
|
98
|
+
description: string;
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
required: string[];
|
|
102
|
+
};
|
|
103
|
+
handler: (params: {
|
|
104
|
+
taskId: string;
|
|
105
|
+
}) => Promise<{
|
|
106
|
+
content: {
|
|
107
|
+
type: "text";
|
|
108
|
+
text: string;
|
|
109
|
+
}[];
|
|
110
|
+
isError?: undefined;
|
|
111
|
+
} | {
|
|
112
|
+
content: {
|
|
113
|
+
type: "text";
|
|
114
|
+
text: string;
|
|
115
|
+
}[];
|
|
116
|
+
isError: boolean;
|
|
117
|
+
}>;
|
|
118
|
+
};
|
|
119
|
+
export declare const taskTools: ({
|
|
120
|
+
name: string;
|
|
121
|
+
description: string;
|
|
122
|
+
inputSchema: {
|
|
123
|
+
type: "object";
|
|
124
|
+
properties: {
|
|
125
|
+
cardId: {
|
|
126
|
+
type: string;
|
|
127
|
+
description: string;
|
|
128
|
+
};
|
|
129
|
+
tasks: {
|
|
130
|
+
type: string;
|
|
131
|
+
items: {
|
|
132
|
+
type: string;
|
|
133
|
+
};
|
|
134
|
+
minItems: number;
|
|
135
|
+
description: string;
|
|
136
|
+
};
|
|
137
|
+
};
|
|
138
|
+
required: string[];
|
|
139
|
+
};
|
|
140
|
+
handler: (params: {
|
|
141
|
+
cardId: string;
|
|
142
|
+
tasks: string[];
|
|
143
|
+
}) => Promise<{
|
|
144
|
+
content: {
|
|
145
|
+
type: "text";
|
|
146
|
+
text: string;
|
|
147
|
+
}[];
|
|
148
|
+
isError?: undefined;
|
|
149
|
+
} | {
|
|
150
|
+
content: {
|
|
151
|
+
type: "text";
|
|
152
|
+
text: string;
|
|
153
|
+
}[];
|
|
154
|
+
isError: boolean;
|
|
155
|
+
}>;
|
|
156
|
+
} | {
|
|
157
|
+
name: string;
|
|
158
|
+
description: string;
|
|
159
|
+
inputSchema: {
|
|
160
|
+
type: "object";
|
|
161
|
+
properties: {
|
|
162
|
+
taskId: {
|
|
163
|
+
type: string;
|
|
164
|
+
description: string;
|
|
165
|
+
};
|
|
166
|
+
name: {
|
|
167
|
+
type: string;
|
|
168
|
+
description: string;
|
|
169
|
+
};
|
|
170
|
+
isCompleted: {
|
|
171
|
+
type: string;
|
|
172
|
+
description: string;
|
|
173
|
+
};
|
|
174
|
+
};
|
|
175
|
+
required: string[];
|
|
176
|
+
};
|
|
177
|
+
handler: (params: {
|
|
178
|
+
taskId: string;
|
|
179
|
+
name?: string;
|
|
180
|
+
isCompleted?: boolean;
|
|
181
|
+
}) => Promise<{
|
|
182
|
+
content: {
|
|
183
|
+
type: "text";
|
|
184
|
+
text: string;
|
|
185
|
+
}[];
|
|
186
|
+
isError?: undefined;
|
|
187
|
+
} | {
|
|
188
|
+
content: {
|
|
189
|
+
type: "text";
|
|
190
|
+
text: string;
|
|
191
|
+
}[];
|
|
192
|
+
isError: boolean;
|
|
193
|
+
}>;
|
|
194
|
+
} | {
|
|
195
|
+
name: string;
|
|
196
|
+
description: string;
|
|
197
|
+
inputSchema: {
|
|
198
|
+
type: "object";
|
|
199
|
+
properties: {
|
|
200
|
+
taskId: {
|
|
201
|
+
type: string;
|
|
202
|
+
description: string;
|
|
203
|
+
};
|
|
204
|
+
};
|
|
205
|
+
required: string[];
|
|
206
|
+
};
|
|
207
|
+
handler: (params: {
|
|
208
|
+
taskId: string;
|
|
209
|
+
}) => Promise<{
|
|
210
|
+
content: {
|
|
211
|
+
type: "text";
|
|
212
|
+
text: string;
|
|
213
|
+
}[];
|
|
214
|
+
isError?: undefined;
|
|
215
|
+
} | {
|
|
216
|
+
content: {
|
|
217
|
+
type: "text";
|
|
218
|
+
text: string;
|
|
219
|
+
}[];
|
|
220
|
+
isError: boolean;
|
|
221
|
+
}>;
|
|
222
|
+
})[];
|
|
223
|
+
//# sourceMappingURL=tasks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tasks.d.ts","sourceRoot":"","sources":["../../src/tools/tasks.ts"],"names":[],"mappings":"AAOA;;;GAGG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;sBAmBF;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,CAAA;KAAE;;;;;;;;;;;;;CAqC5D,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;sBAqBD;QACtB,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,WAAW,CAAC,EAAE,OAAO,CAAC;KACvB;;;;;;;;;;;;;CAyCF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;sBAaD;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE;;;;;;;;;;;;;CA6B3C,CAAC;AAEF,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;sBA/JI;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,CAAA;KAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sBAgEnC;QACtB,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,WAAW,CAAC,EAAE,OAAO,CAAC;KACvB;;;;;;;;;;;;;;;;;;;;;;;;;;sBA4DuB;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE;;;;;;;;;;;;;IA+B8B,CAAC"}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task tools for PLANKA MCP server.
|
|
3
|
+
*/
|
|
4
|
+
import { createTasks, updateTask, deleteTask } from "../operations/tasks.js";
|
|
5
|
+
import { PlankaError } from "../errors.js";
|
|
6
|
+
/**
|
|
7
|
+
* Tool: planka_create_tasks
|
|
8
|
+
* Add one or more tasks (checklist items) to a card.
|
|
9
|
+
*/
|
|
10
|
+
export const createTasksTool = {
|
|
11
|
+
name: "planka_create_tasks",
|
|
12
|
+
description: "Add one or more tasks (checklist items) to a card.",
|
|
13
|
+
inputSchema: {
|
|
14
|
+
type: "object",
|
|
15
|
+
properties: {
|
|
16
|
+
cardId: {
|
|
17
|
+
type: "string",
|
|
18
|
+
description: "The card ID",
|
|
19
|
+
},
|
|
20
|
+
tasks: {
|
|
21
|
+
type: "array",
|
|
22
|
+
items: { type: "string" },
|
|
23
|
+
minItems: 1,
|
|
24
|
+
description: "Task names to create",
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
required: ["cardId", "tasks"],
|
|
28
|
+
},
|
|
29
|
+
handler: async (params) => {
|
|
30
|
+
try {
|
|
31
|
+
const tasks = await createTasks({
|
|
32
|
+
cardId: params.cardId,
|
|
33
|
+
tasks: params.tasks.map((name) => ({ name })),
|
|
34
|
+
});
|
|
35
|
+
return {
|
|
36
|
+
content: [
|
|
37
|
+
{
|
|
38
|
+
type: "text",
|
|
39
|
+
text: JSON.stringify({
|
|
40
|
+
success: true,
|
|
41
|
+
tasksCreated: tasks.length,
|
|
42
|
+
tasks: tasks.map((t) => ({
|
|
43
|
+
id: t.id,
|
|
44
|
+
name: t.name,
|
|
45
|
+
isCompleted: t.isCompleted,
|
|
46
|
+
})),
|
|
47
|
+
}, null, 2),
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
if (error instanceof PlankaError) {
|
|
54
|
+
return {
|
|
55
|
+
content: [{ type: "text", text: `Error: ${error.message}` }],
|
|
56
|
+
isError: true,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Tool: planka_update_task
|
|
65
|
+
* Update a task's name or completion status.
|
|
66
|
+
*/
|
|
67
|
+
export const updateTaskTool = {
|
|
68
|
+
name: "planka_update_task",
|
|
69
|
+
description: "Update a task's name or completion status.",
|
|
70
|
+
inputSchema: {
|
|
71
|
+
type: "object",
|
|
72
|
+
properties: {
|
|
73
|
+
taskId: {
|
|
74
|
+
type: "string",
|
|
75
|
+
description: "The task ID",
|
|
76
|
+
},
|
|
77
|
+
name: {
|
|
78
|
+
type: "string",
|
|
79
|
+
description: "New task name",
|
|
80
|
+
},
|
|
81
|
+
isCompleted: {
|
|
82
|
+
type: "boolean",
|
|
83
|
+
description: "Mark as complete/incomplete",
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
required: ["taskId"],
|
|
87
|
+
},
|
|
88
|
+
handler: async (params) => {
|
|
89
|
+
try {
|
|
90
|
+
const { taskId, ...updates } = params;
|
|
91
|
+
// Only include defined fields
|
|
92
|
+
const patch = { taskId };
|
|
93
|
+
if (updates.name !== undefined)
|
|
94
|
+
patch.name = updates.name;
|
|
95
|
+
if (updates.isCompleted !== undefined)
|
|
96
|
+
patch.isCompleted = updates.isCompleted;
|
|
97
|
+
const task = await updateTask(patch);
|
|
98
|
+
return {
|
|
99
|
+
content: [
|
|
100
|
+
{
|
|
101
|
+
type: "text",
|
|
102
|
+
text: JSON.stringify({
|
|
103
|
+
success: true,
|
|
104
|
+
task: {
|
|
105
|
+
id: task.id,
|
|
106
|
+
name: task.name,
|
|
107
|
+
isCompleted: task.isCompleted,
|
|
108
|
+
},
|
|
109
|
+
}, null, 2),
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
if (error instanceof PlankaError) {
|
|
116
|
+
return {
|
|
117
|
+
content: [{ type: "text", text: `Error: ${error.message}` }],
|
|
118
|
+
isError: true,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
throw error;
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
/**
|
|
126
|
+
* Tool: planka_delete_task
|
|
127
|
+
* Delete a task from a card.
|
|
128
|
+
*/
|
|
129
|
+
export const deleteTaskTool = {
|
|
130
|
+
name: "planka_delete_task",
|
|
131
|
+
description: "Delete a task from a card.",
|
|
132
|
+
inputSchema: {
|
|
133
|
+
type: "object",
|
|
134
|
+
properties: {
|
|
135
|
+
taskId: {
|
|
136
|
+
type: "string",
|
|
137
|
+
description: "The task ID to delete",
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
required: ["taskId"],
|
|
141
|
+
},
|
|
142
|
+
handler: async (params) => {
|
|
143
|
+
try {
|
|
144
|
+
await deleteTask({ taskId: params.taskId });
|
|
145
|
+
return {
|
|
146
|
+
content: [
|
|
147
|
+
{
|
|
148
|
+
type: "text",
|
|
149
|
+
text: JSON.stringify({
|
|
150
|
+
success: true,
|
|
151
|
+
message: `Task ${params.taskId} deleted`,
|
|
152
|
+
}, null, 2),
|
|
153
|
+
},
|
|
154
|
+
],
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
if (error instanceof PlankaError) {
|
|
159
|
+
return {
|
|
160
|
+
content: [{ type: "text", text: `Error: ${error.message}` }],
|
|
161
|
+
isError: true,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
throw error;
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
export const taskTools = [createTasksTool, updateTaskTool, deleteTaskTool];
|
|
169
|
+
//# sourceMappingURL=tasks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tasks.js","sourceRoot":"","sources":["../../src/tools/tasks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# Planka 2.x compatibility gotchas
|
|
2
|
+
|
|
3
|
+
The older published package can fail without throwing. That is worse than a loud
|
|
4
|
+
error because an agent may make a workflow decision from false state.
|
|
5
|
+
|
|
6
|
+
This fork fixes five known incompatibilities and verifies the dangerous writes.
|
|
7
|
+
|
|
8
|
+
## 1. Comments appear empty
|
|
9
|
+
|
|
10
|
+
**Old behavior:** read `included.comments` from `GET /api/cards/:cardId`.
|
|
11
|
+
|
|
12
|
+
**Planka 2.x behavior:** comments live at:
|
|
13
|
+
|
|
14
|
+
```text
|
|
15
|
+
GET /api/cards/:cardId/comments
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The old request returns HTTP 200, but the comments array is absent or empty. The MCP
|
|
19
|
+
then reports zero comments without an error. This fork calls the dedicated endpoint
|
|
20
|
+
for both `planka_get_comments` and the comments included by `planka_get_card`.
|
|
21
|
+
|
|
22
|
+
Why it matters: comments often contain the newest decision or closing evidence.
|
|
23
|
+
|
|
24
|
+
Upstream fix proposed in [PR #5](https://github.com/gogogadgetbytes/planka-mcp/pull/5).
|
|
25
|
+
|
|
26
|
+
## 2. `labelIds` is silently discarded
|
|
27
|
+
|
|
28
|
+
**Old schema:** only declared `addLabelIds`.
|
|
29
|
+
|
|
30
|
+
MCP clients commonly send this natural payload:
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"cardId": "<CARD_ID>",
|
|
35
|
+
"labelIds": ["<LABEL_ID>"]
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Unknown properties were discarded during validation. The handler then counted an
|
|
40
|
+
empty request and could return a successful result without touching the card.
|
|
41
|
+
|
|
42
|
+
This fork:
|
|
43
|
+
|
|
44
|
+
- accepts `labelIds` and `addLabelIds`;
|
|
45
|
+
- merges and deduplicates them;
|
|
46
|
+
- rejects an empty label operation;
|
|
47
|
+
- re-reads the card after the operation;
|
|
48
|
+
- returns `added`, `removed`, `alreadyPresent`, `notPresent`, `failed`, and
|
|
49
|
+
`finalLabelIds` from observed state.
|
|
50
|
+
|
|
51
|
+
## 3. Removing a label uses criteria syntax
|
|
52
|
+
|
|
53
|
+
The junction record returned by Planka has its own ID, but Planka 2.x deletion is
|
|
54
|
+
addressed by label criteria:
|
|
55
|
+
|
|
56
|
+
```text
|
|
57
|
+
DELETE /api/cards/:cardId/card-labels/labelId:<labelId>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Deleting by the junction-record ID returns 404 and leaves the label attached. This
|
|
61
|
+
fork uses the criteria URL and then verifies the final card state.
|
|
62
|
+
|
|
63
|
+
## 4. Label colors are an open read contract
|
|
64
|
+
|
|
65
|
+
Planka adds colors over time. A closed Zod enum on responses means one unfamiliar
|
|
66
|
+
color rejects the entire `planka_get_board` response.
|
|
67
|
+
|
|
68
|
+
This fork uses `z.string()` for label colors returned by Planka. Write paths remain
|
|
69
|
+
validated against the currently documented Planka 2.x colors.
|
|
70
|
+
|
|
71
|
+
Upstream fix proposed in [PR #1](https://github.com/gogogadgetbytes/planka-mcp/pull/1).
|
|
72
|
+
|
|
73
|
+
## 5. A long column truncates in silence
|
|
74
|
+
|
|
75
|
+
**Planka 2.x behavior:** `GET /api/lists/:listId/cards` returns at most 50 cards
|
|
76
|
+
per page and **ignores `?limit=`**. There is no `total` in the response and no
|
|
77
|
+
flag saying more exist.
|
|
78
|
+
|
|
79
|
+
A column holding 77 cards answers with 50 of them, HTTP 200, and nothing else.
|
|
80
|
+
Read as-is, that is a board with 27 cards fewer than it has, and an agent will
|
|
81
|
+
happily conclude a card does not exist.
|
|
82
|
+
|
|
83
|
+
The cursor is **composite**. Both parts are required, and they come from the last
|
|
84
|
+
card of the previous page:
|
|
85
|
+
|
|
86
|
+
```text
|
|
87
|
+
GET /api/lists/<LIST_ID>/cards?before[id]=<LAST_CARD_ID>&before[listChangedAt]=<LAST_CARD_listChangedAt>
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
This fork follows that cursor until a page comes back short, capped at 20 pages.
|
|
91
|
+
When it stops early, or when the last card has no `listChangedAt` and no cursor
|
|
92
|
+
can be built, `planka_list_cards` returns `truncated: true` with a note. It says
|
|
93
|
+
it did not see everything rather than pretending it did.
|
|
94
|
+
|
|
95
|
+
Why it matters: an incomplete list looks exactly like a complete one.
|
|
96
|
+
|
|
97
|
+
## Related Planka 2.x traps
|
|
98
|
+
|
|
99
|
+
### Board-level card and label endpoints do not exist
|
|
100
|
+
|
|
101
|
+
These two routes look like they should exist, and they do not:
|
|
102
|
+
|
|
103
|
+
```text
|
|
104
|
+
GET /api/boards/:boardId/cards
|
|
105
|
+
GET /api/boards/:boardId/labels
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Planka does not 404 them. The web server answers with the single-page app's HTML
|
|
109
|
+
and **status 200**, so a client that only checks `response.ok` sees a success and
|
|
110
|
+
then fails while parsing, or worse, treats the result as an empty collection.
|
|
111
|
+
|
|
112
|
+
Cards and labels are reachable per list (`GET /api/lists/:listId/cards`) or in
|
|
113
|
+
the board payload itself (`GET /api/boards/:boardId`, whose `included` carries
|
|
114
|
+
every card, label, and card-label link).
|
|
115
|
+
|
|
116
|
+
### Per-list `search` and `labelIds` really do filter server-side
|
|
117
|
+
|
|
118
|
+
Verified against Planka 2.x on 2026-08-24, `GET /api/lists/:listId/cards`
|
|
119
|
+
accepts:
|
|
120
|
+
|
|
121
|
+
- `?search=<text>` — matches the card **name and description**, case-insensitive;
|
|
122
|
+
- `?labelIds=<labelId>` — restricts to cards carrying that label.
|
|
123
|
+
|
|
124
|
+
Both are per list, not per board. Since enumerating a board's columns, or
|
|
125
|
+
resolving a label name to its ID, needs a `GET /api/boards/:boardId` anyway, and
|
|
126
|
+
that single call already carries every card with its description and label
|
|
127
|
+
links, this fork filters board-level searches locally over that one request.
|
|
128
|
+
Issuing one search per list would be N extra round-trips for data already in
|
|
129
|
+
hand. What stays small in `planka_find_cards` and `planka_board_summary` is the
|
|
130
|
+
**output**, which is what a model pays for.
|
|
131
|
+
|
|
132
|
+
### A projectId where a boardId goes returns a bare 404
|
|
133
|
+
|
|
134
|
+
Project IDs and board IDs are both strings of digits. Nothing in either one says
|
|
135
|
+
which it is. Passing a project ID to a board tool produces
|
|
136
|
+
`GET /api/boards/<projectId>` and a naked `404`, which does not hint that the ID
|
|
137
|
+
belongs to a different kind of entity.
|
|
138
|
+
|
|
139
|
+
This fork diagnoses that 404 before reporting it:
|
|
140
|
+
|
|
141
|
+
- if the ID is a project with exactly one board, it resolves to that board and
|
|
142
|
+
the call proceeds;
|
|
143
|
+
- if it is a project with several boards, the error says so and lists them with
|
|
144
|
+
their IDs and names;
|
|
145
|
+
- if it is a project with no boards, the error says that instead;
|
|
146
|
+
- if it is neither a project nor a board, the error says so and lists the boards
|
|
147
|
+
the account can actually see.
|
|
148
|
+
|
|
149
|
+
### Creating lists requires `type`
|
|
150
|
+
|
|
151
|
+
Planka 2.x requires all three fields:
|
|
152
|
+
|
|
153
|
+
```json
|
|
154
|
+
{
|
|
155
|
+
"name": "Pending",
|
|
156
|
+
"position": 65536,
|
|
157
|
+
"type": "active"
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Valid write types are `active` and `closed`. Board responses can also contain
|
|
162
|
+
internal `archive` and `trash` lists; read schemas must tolerate them.
|
|
163
|
+
|
|
164
|
+
### Creating cards requires a card type
|
|
165
|
+
|
|
166
|
+
The server sends `type: "project"` by default when creating a card. Callers do not
|
|
167
|
+
need to know this Planka-specific requirement.
|
|
168
|
+
|
|
169
|
+
### Authentication redirects can turn into HTML
|
|
170
|
+
|
|
171
|
+
Use the final HTTPS origin. An HTTP URL may redirect the authentication POST, lose
|
|
172
|
+
its body, and return an HTML page. `setup.sh` uses `redirect: manual` and reports this
|
|
173
|
+
case explicitly before writing configuration.
|
|
174
|
+
|
|
175
|
+
## Why the smoke test uses the raw API
|
|
176
|
+
|
|
177
|
+
A valid JSON-RPC result proves only that the MCP handler returned. It does not prove
|
|
178
|
+
Planka persisted the change.
|
|
179
|
+
|
|
180
|
+
`npm run test:smoke` builds the server, starts it over stdio, and drives all 24
|
|
181
|
+
tools through real MCP calls — 64 named checks. Every write is then cross-checked
|
|
182
|
+
against the raw Planka HTTP API: a tool that reports success on something that
|
|
183
|
+
never landed fails here. It creates a uniquely prefixed card, list, labels, tasks,
|
|
184
|
+
comments, and an attachment, exercises the scoped reads against the sizes the raw
|
|
185
|
+
board actually returns, feeds a project ID to a board tool to confirm the 404 gets
|
|
186
|
+
explained, and deletes everything it made in a `finally` — over raw HTTP, so
|
|
187
|
+
cleanup does not depend on the component under test.
|
|
188
|
+
|
|
189
|
+
`PLANKA_SMOKE_FAIL_AFTER=<n>` injects a failure after check `n`, which is how the
|
|
190
|
+
cleanup path itself is verified.
|
|
191
|
+
|
|
192
|
+
This is the regression defense for silent failures.
|