@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.
Files changed (126) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/CONTRIBUTING.md +37 -0
  3. package/CREDITS.md +39 -0
  4. package/LICENSE +22 -0
  5. package/README.es.md +213 -0
  6. package/README.md +213 -0
  7. package/dist/client.d.ts +96 -0
  8. package/dist/client.d.ts.map +1 -0
  9. package/dist/client.js +281 -0
  10. package/dist/client.js.map +1 -0
  11. package/dist/errors.d.ts +85 -0
  12. package/dist/errors.d.ts.map +1 -0
  13. package/dist/errors.js +226 -0
  14. package/dist/errors.js.map +1 -0
  15. package/dist/identity.generated.d.ts +3 -0
  16. package/dist/identity.generated.d.ts.map +1 -0
  17. package/dist/identity.generated.js +4 -0
  18. package/dist/identity.generated.js.map +1 -0
  19. package/dist/index.d.ts +3 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +93 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/operations/attachments.d.ts +34 -0
  24. package/dist/operations/attachments.d.ts.map +1 -0
  25. package/dist/operations/attachments.js +89 -0
  26. package/dist/operations/attachments.js.map +1 -0
  27. package/dist/operations/board-id.d.ts +9 -0
  28. package/dist/operations/board-id.d.ts.map +1 -0
  29. package/dist/operations/board-id.js +86 -0
  30. package/dist/operations/board-id.js.map +1 -0
  31. package/dist/operations/boards.d.ts +143 -0
  32. package/dist/operations/boards.d.ts.map +1 -0
  33. package/dist/operations/boards.js +391 -0
  34. package/dist/operations/boards.js.map +1 -0
  35. package/dist/operations/cards.d.ts +36 -0
  36. package/dist/operations/cards.d.ts.map +1 -0
  37. package/dist/operations/cards.js +81 -0
  38. package/dist/operations/cards.js.map +1 -0
  39. package/dist/operations/comments.d.ts +28 -0
  40. package/dist/operations/comments.d.ts.map +1 -0
  41. package/dist/operations/comments.js +53 -0
  42. package/dist/operations/comments.js.map +1 -0
  43. package/dist/operations/labels.d.ts +60 -0
  44. package/dist/operations/labels.d.ts.map +1 -0
  45. package/dist/operations/labels.js +146 -0
  46. package/dist/operations/labels.js.map +1 -0
  47. package/dist/operations/lists.d.ts +15 -0
  48. package/dist/operations/lists.d.ts.map +1 -0
  49. package/dist/operations/lists.js +40 -0
  50. package/dist/operations/lists.js.map +1 -0
  51. package/dist/operations/projects.d.ts +24 -0
  52. package/dist/operations/projects.d.ts.map +1 -0
  53. package/dist/operations/projects.js +61 -0
  54. package/dist/operations/projects.js.map +1 -0
  55. package/dist/operations/tasks.d.ts +30 -0
  56. package/dist/operations/tasks.d.ts.map +1 -0
  57. package/dist/operations/tasks.js +111 -0
  58. package/dist/operations/tasks.js.map +1 -0
  59. package/dist/schemas/entities.d.ts +389 -0
  60. package/dist/schemas/entities.d.ts.map +1 -0
  61. package/dist/schemas/entities.js +202 -0
  62. package/dist/schemas/entities.js.map +1 -0
  63. package/dist/schemas/requests.d.ts +539 -0
  64. package/dist/schemas/requests.d.ts.map +1 -0
  65. package/dist/schemas/requests.js +187 -0
  66. package/dist/schemas/requests.js.map +1 -0
  67. package/dist/schemas/responses.d.ts +2626 -0
  68. package/dist/schemas/responses.d.ts.map +1 -0
  69. package/dist/schemas/responses.js +75 -0
  70. package/dist/schemas/responses.js.map +1 -0
  71. package/dist/tools/attachments.d.ts +270 -0
  72. package/dist/tools/attachments.d.ts.map +1 -0
  73. package/dist/tools/attachments.js +247 -0
  74. package/dist/tools/attachments.js.map +1 -0
  75. package/dist/tools/cards.d.ts +401 -0
  76. package/dist/tools/cards.d.ts.map +1 -0
  77. package/dist/tools/cards.js +368 -0
  78. package/dist/tools/cards.js.map +1 -0
  79. package/dist/tools/comments.d.ts +217 -0
  80. package/dist/tools/comments.d.ts.map +1 -0
  81. package/dist/tools/comments.js +208 -0
  82. package/dist/tools/comments.js.map +1 -0
  83. package/dist/tools/index.d.ts +1293 -0
  84. package/dist/tools/index.d.ts.map +1 -0
  85. package/dist/tools/index.js +62 -0
  86. package/dist/tools/index.js.map +1 -0
  87. package/dist/tools/labels.d.ts +214 -0
  88. package/dist/tools/labels.d.ts.map +1 -0
  89. package/dist/tools/labels.js +302 -0
  90. package/dist/tools/labels.js.map +1 -0
  91. package/dist/tools/lists.d.ts +117 -0
  92. package/dist/tools/lists.d.ts.map +1 -0
  93. package/dist/tools/lists.js +178 -0
  94. package/dist/tools/lists.js.map +1 -0
  95. package/dist/tools/navigation.d.ts +106 -0
  96. package/dist/tools/navigation.d.ts.map +1 -0
  97. package/dist/tools/navigation.js +151 -0
  98. package/dist/tools/navigation.js.map +1 -0
  99. package/dist/tools/queries.d.ts +380 -0
  100. package/dist/tools/queries.d.ts.map +1 -0
  101. package/dist/tools/queries.js +256 -0
  102. package/dist/tools/queries.js.map +1 -0
  103. package/dist/tools/tasks.d.ts +223 -0
  104. package/dist/tools/tasks.d.ts.map +1 -0
  105. package/dist/tools/tasks.js +169 -0
  106. package/dist/tools/tasks.js.map +1 -0
  107. package/docs/planka-2x-gotchas.md +192 -0
  108. package/docs/tools.md +659 -0
  109. package/docs/troubleshooting.md +138 -0
  110. package/package.json +71 -0
  111. package/project.identity.json +7 -0
  112. package/scripts/bootstrap-board.mjs +160 -0
  113. package/scripts/lib/planka-api.mjs +244 -0
  114. package/scripts/lib/workflow-template.mjs +26 -0
  115. package/scripts/setup.mjs +236 -0
  116. package/scripts/setup.sh +48 -0
  117. package/scripts/sync-identity.mjs +57 -0
  118. package/server.json +43 -0
  119. package/tests/smoke/planka-smoke.mjs +920 -0
  120. package/workflow/README.md +232 -0
  121. package/workflow/board-template.md +115 -0
  122. package/workflow/skills/planka-close-card/SKILL.md +81 -0
  123. package/workflow/skills/planka-orchestrator/SKILL.md +126 -0
  124. package/workflow/worktrees/README.md +104 -0
  125. package/workflow/worktrees/wt.conf.example +24 -0
  126. 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.