@grec0/memory-bank-mcp 0.0.2
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/.memoryignore.example +76 -0
- package/README.md +425 -0
- package/dist/common/chunker.js +407 -0
- package/dist/common/embeddingService.js +302 -0
- package/dist/common/errors.js +71 -0
- package/dist/common/fileScanner.js +261 -0
- package/dist/common/indexManager.js +332 -0
- package/dist/common/setup.js +49 -0
- package/dist/common/types.js +115 -0
- package/dist/common/utils.js +215 -0
- package/dist/common/vectorStore.js +332 -0
- package/dist/common/version.js +2 -0
- package/dist/index.js +274 -0
- package/dist/operations/boardMemberships.js +186 -0
- package/dist/operations/boards.js +268 -0
- package/dist/operations/cards.js +426 -0
- package/dist/operations/comments.js +249 -0
- package/dist/operations/labels.js +258 -0
- package/dist/operations/lists.js +157 -0
- package/dist/operations/projects.js +102 -0
- package/dist/operations/tasks.js +238 -0
- package/dist/tools/analyzeCoverage.js +316 -0
- package/dist/tools/board-summary.js +151 -0
- package/dist/tools/card-details.js +106 -0
- package/dist/tools/create-card-with-tasks.js +81 -0
- package/dist/tools/getStats.js +59 -0
- package/dist/tools/index.js +12 -0
- package/dist/tools/indexCode.js +53 -0
- package/dist/tools/readFile.js +69 -0
- package/dist/tools/searchMemory.js +60 -0
- package/dist/tools/workflow-actions.js +145 -0
- package/dist/tools/writeFile.js +66 -0
- package/package.json +58 -0
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Comment operations for the MCP Kanban server
|
|
3
|
+
*
|
|
4
|
+
* This module provides functions for interacting with comments in the Planka Kanban board,
|
|
5
|
+
* including creating, retrieving, updating, and deleting comments on cards.
|
|
6
|
+
*/
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import { plankaRequest } from "../common/utils.js";
|
|
9
|
+
// Schema definitions
|
|
10
|
+
/**
|
|
11
|
+
* Schema for creating a new comment
|
|
12
|
+
* @property {string} cardId - The ID of the card to create the comment on
|
|
13
|
+
* @property {string} text - The text content of the comment
|
|
14
|
+
*/
|
|
15
|
+
export const CreateCommentSchema = z.object({
|
|
16
|
+
cardId: z.string().describe("Card ID"),
|
|
17
|
+
text: z.string().describe("Comment text"),
|
|
18
|
+
});
|
|
19
|
+
/**
|
|
20
|
+
* Schema for retrieving comments from a card
|
|
21
|
+
* @property {string} cardId - The ID of the card to get comments from
|
|
22
|
+
*/
|
|
23
|
+
export const GetCommentsSchema = z.object({
|
|
24
|
+
cardId: z.string().describe("Card ID"),
|
|
25
|
+
});
|
|
26
|
+
/**
|
|
27
|
+
* Schema for retrieving a specific comment
|
|
28
|
+
* @property {string} id - The ID of the comment to retrieve
|
|
29
|
+
*/
|
|
30
|
+
export const GetCommentSchema = z.object({
|
|
31
|
+
id: z.string().describe("Comment ID"),
|
|
32
|
+
});
|
|
33
|
+
/**
|
|
34
|
+
* Schema for updating a comment
|
|
35
|
+
* @property {string} id - The ID of the comment to update
|
|
36
|
+
* @property {string} text - The new text content for the comment
|
|
37
|
+
*/
|
|
38
|
+
export const UpdateCommentSchema = z.object({
|
|
39
|
+
id: z.string().describe("Comment ID"),
|
|
40
|
+
text: z.string().describe("Comment text"),
|
|
41
|
+
});
|
|
42
|
+
/**
|
|
43
|
+
* Schema for deleting a comment
|
|
44
|
+
* @property {string} id - The ID of the comment to delete
|
|
45
|
+
*/
|
|
46
|
+
export const DeleteCommentSchema = z.object({
|
|
47
|
+
id: z.string().describe("Comment ID"),
|
|
48
|
+
});
|
|
49
|
+
// Comment action schema
|
|
50
|
+
const CommentActionSchema = z.object({
|
|
51
|
+
id: z.string(),
|
|
52
|
+
type: z.literal("commentCard"),
|
|
53
|
+
data: z.object({
|
|
54
|
+
text: z.string(),
|
|
55
|
+
}),
|
|
56
|
+
cardId: z.string(),
|
|
57
|
+
userId: z.string(),
|
|
58
|
+
createdAt: z.string(),
|
|
59
|
+
updatedAt: z.string().nullable(),
|
|
60
|
+
});
|
|
61
|
+
// Response schemas
|
|
62
|
+
const CommentActionsResponseSchema = z.object({
|
|
63
|
+
items: z.array(CommentActionSchema),
|
|
64
|
+
included: z.record(z.any()).optional(),
|
|
65
|
+
});
|
|
66
|
+
const CommentActionResponseSchema = z.object({
|
|
67
|
+
item: CommentActionSchema,
|
|
68
|
+
included: z.record(z.any()).optional(),
|
|
69
|
+
});
|
|
70
|
+
// Function implementations
|
|
71
|
+
/**
|
|
72
|
+
* Creates a new comment on a card
|
|
73
|
+
*
|
|
74
|
+
* @param {CreateCommentOptions} options - Options for creating the comment
|
|
75
|
+
* @param {string} options.cardId - The ID of the card to create the comment on
|
|
76
|
+
* @param {string} options.text - The text content of the comment
|
|
77
|
+
* @returns {Promise<object>} The created comment
|
|
78
|
+
* @throws {Error} If the comment creation fails
|
|
79
|
+
*/
|
|
80
|
+
export async function createComment(options) {
|
|
81
|
+
try {
|
|
82
|
+
const response = await plankaRequest(`/api/cards/${options.cardId}/comment-actions`, {
|
|
83
|
+
method: "POST",
|
|
84
|
+
body: {
|
|
85
|
+
text: options.text,
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
const parsedResponse = CommentActionResponseSchema.parse(response);
|
|
89
|
+
return parsedResponse.item;
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
throw new Error(`Failed to create comment: ${error instanceof Error ? error.message : String(error)}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Retrieves all comments for a specific card
|
|
97
|
+
*
|
|
98
|
+
* @param {string} cardId - The ID of the card to get comments for
|
|
99
|
+
* @returns {Promise<Array<object>>} Array of comments on the card
|
|
100
|
+
* @throws {Error} If retrieving comments fails
|
|
101
|
+
*/
|
|
102
|
+
export async function getComments(cardId) {
|
|
103
|
+
try {
|
|
104
|
+
const response = await plankaRequest(`/api/cards/${cardId}/actions`);
|
|
105
|
+
try {
|
|
106
|
+
// Try to parse as a CommentsResponseSchema first
|
|
107
|
+
const parsedResponse = CommentActionsResponseSchema.parse(response);
|
|
108
|
+
// Filter only comment actions
|
|
109
|
+
if (parsedResponse.items && Array.isArray(parsedResponse.items)) {
|
|
110
|
+
return parsedResponse.items.filter((item) => item.type === "commentCard");
|
|
111
|
+
}
|
|
112
|
+
return parsedResponse.items;
|
|
113
|
+
}
|
|
114
|
+
catch (parseError) {
|
|
115
|
+
// If that fails, try to parse as an array directly
|
|
116
|
+
if (Array.isArray(response)) {
|
|
117
|
+
const items = z.array(CommentActionSchema).parse(response);
|
|
118
|
+
// Filter only comment actions
|
|
119
|
+
return items.filter((item) => item.type === "commentCard");
|
|
120
|
+
}
|
|
121
|
+
// If we get here, we couldn't parse the response in any expected format
|
|
122
|
+
throw new Error(`Could not parse comments response: ${JSON.stringify(response)}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
// If all else fails, return an empty array
|
|
127
|
+
return [];
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Retrieves a specific comment by ID
|
|
132
|
+
*
|
|
133
|
+
* @param {string} id - The ID of the comment to retrieve
|
|
134
|
+
* @returns {Promise<object>} The requested comment
|
|
135
|
+
* @throws {Error} If retrieving the comment fails
|
|
136
|
+
*/
|
|
137
|
+
export async function getComment(id) {
|
|
138
|
+
try {
|
|
139
|
+
// Get all projects which includes boards
|
|
140
|
+
const projectsResponse = await plankaRequest(`/api/projects`);
|
|
141
|
+
if (!projectsResponse ||
|
|
142
|
+
typeof projectsResponse !== "object" ||
|
|
143
|
+
!("included" in projectsResponse) ||
|
|
144
|
+
!projectsResponse.included ||
|
|
145
|
+
typeof projectsResponse.included !== "object") {
|
|
146
|
+
throw new Error("Failed to get projects");
|
|
147
|
+
}
|
|
148
|
+
const included = projectsResponse.included;
|
|
149
|
+
// Get all boards
|
|
150
|
+
if (!("boards" in included) || !Array.isArray(included.boards)) {
|
|
151
|
+
throw new Error("No boards found");
|
|
152
|
+
}
|
|
153
|
+
const boards = included.boards;
|
|
154
|
+
// Check each board for cards
|
|
155
|
+
for (const board of boards) {
|
|
156
|
+
if (typeof board !== "object" || board === null || !("id" in board)) {
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
const boardId = board.id;
|
|
160
|
+
// Get the board details which includes cards
|
|
161
|
+
const boardResponse = await plankaRequest(`/api/boards/${boardId}`);
|
|
162
|
+
if (!boardResponse ||
|
|
163
|
+
typeof boardResponse !== "object" ||
|
|
164
|
+
!("included" in boardResponse) ||
|
|
165
|
+
!boardResponse.included ||
|
|
166
|
+
typeof boardResponse.included !== "object") {
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
const boardIncluded = boardResponse.included;
|
|
170
|
+
if (!("cards" in boardIncluded) ||
|
|
171
|
+
!Array.isArray(boardIncluded.cards)) {
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
const cards = boardIncluded.cards;
|
|
175
|
+
// Check each card for the comment
|
|
176
|
+
for (const card of cards) {
|
|
177
|
+
if (typeof card !== "object" || card === null || !("id" in card)) {
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
const cardId = card.id;
|
|
181
|
+
// Get the card actions
|
|
182
|
+
const actionsResponse = await plankaRequest(`/api/cards/${cardId}/actions`);
|
|
183
|
+
if (!actionsResponse ||
|
|
184
|
+
typeof actionsResponse !== "object" ||
|
|
185
|
+
!("items" in actionsResponse) ||
|
|
186
|
+
!Array.isArray(actionsResponse.items)) {
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
const actions = actionsResponse.items;
|
|
190
|
+
// Find the comment with the matching ID
|
|
191
|
+
const comment = actions.find((action) => typeof action === "object" &&
|
|
192
|
+
action !== null &&
|
|
193
|
+
"id" in action &&
|
|
194
|
+
action.id === id &&
|
|
195
|
+
"type" in action &&
|
|
196
|
+
action.type === "commentCard");
|
|
197
|
+
if (comment) {
|
|
198
|
+
return comment;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
throw new Error(`Comment not found: ${id}`);
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
throw new Error(`Failed to get comment: ${error instanceof Error ? error.message : String(error)}`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Updates a comment's text content
|
|
210
|
+
*
|
|
211
|
+
* @param {string} id - The ID of the comment to update
|
|
212
|
+
* @param {Partial<Omit<CreateCommentOptions, "cardId">>} options - The properties to update
|
|
213
|
+
* @param {string} options.text - The new text content for the comment
|
|
214
|
+
* @returns {Promise<object>} The updated comment
|
|
215
|
+
* @throws {Error} If updating the comment fails
|
|
216
|
+
*/
|
|
217
|
+
export async function updateComment(id, options) {
|
|
218
|
+
try {
|
|
219
|
+
const response = await plankaRequest(`/api/comment-actions/${id}`, {
|
|
220
|
+
method: "PATCH",
|
|
221
|
+
body: {
|
|
222
|
+
text: options.text,
|
|
223
|
+
},
|
|
224
|
+
});
|
|
225
|
+
const parsedResponse = CommentActionResponseSchema.parse(response);
|
|
226
|
+
return parsedResponse.item;
|
|
227
|
+
}
|
|
228
|
+
catch (error) {
|
|
229
|
+
throw new Error(`Failed to update comment: ${error instanceof Error ? error.message : String(error)}`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Deletes a comment by ID
|
|
234
|
+
*
|
|
235
|
+
* @param {string} id - The ID of the comment to delete
|
|
236
|
+
* @returns {Promise<{success: boolean}>} Success indicator
|
|
237
|
+
* @throws {Error} If deleting the comment fails
|
|
238
|
+
*/
|
|
239
|
+
export async function deleteComment(id) {
|
|
240
|
+
try {
|
|
241
|
+
await plankaRequest(`/api/comment-actions/${id}`, {
|
|
242
|
+
method: "DELETE",
|
|
243
|
+
});
|
|
244
|
+
return { success: true };
|
|
245
|
+
}
|
|
246
|
+
catch (error) {
|
|
247
|
+
throw new Error(`Failed to delete comment: ${error instanceof Error ? error.message : String(error)}`);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Label operations for the MCP Kanban server
|
|
3
|
+
*
|
|
4
|
+
* This module provides functions for interacting with labels in the Planka Kanban board,
|
|
5
|
+
* including creating, retrieving, updating, and deleting labels, as well as
|
|
6
|
+
* adding and removing labels from cards.
|
|
7
|
+
*/
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
import { plankaRequest } from "../common/utils.js";
|
|
10
|
+
import { PlankaLabelSchema } from "../common/types.js";
|
|
11
|
+
/**
|
|
12
|
+
* Valid color options for labels in Planka
|
|
13
|
+
*/
|
|
14
|
+
export const VALID_LABEL_COLORS = [
|
|
15
|
+
"berry-red",
|
|
16
|
+
"pumpkin-orange",
|
|
17
|
+
"lagoon-blue",
|
|
18
|
+
"pink-tulip",
|
|
19
|
+
"light-mud",
|
|
20
|
+
"orange-peel",
|
|
21
|
+
"bright-moss",
|
|
22
|
+
"antique-blue",
|
|
23
|
+
"dark-granite",
|
|
24
|
+
"lagune-blue",
|
|
25
|
+
"sunny-grass",
|
|
26
|
+
"morning-sky",
|
|
27
|
+
"light-orange",
|
|
28
|
+
"midnight-blue",
|
|
29
|
+
"tank-green",
|
|
30
|
+
"gun-metal",
|
|
31
|
+
"wet-moss",
|
|
32
|
+
"red-burgundy",
|
|
33
|
+
"light-concrete",
|
|
34
|
+
"apricot-red",
|
|
35
|
+
"desert-sand",
|
|
36
|
+
"navy-blue",
|
|
37
|
+
"egg-yellow",
|
|
38
|
+
"coral-green",
|
|
39
|
+
"light-cocoa",
|
|
40
|
+
];
|
|
41
|
+
/**
|
|
42
|
+
* Schema for creating a new label
|
|
43
|
+
* @property {string} boardId - The ID of the board to create the label in
|
|
44
|
+
* @property {string} name - The name of the label
|
|
45
|
+
* @property {string} color - The color of the label (must be one of the valid colors)
|
|
46
|
+
* @property {number} [position] - The position of the label in the board (default: 65535)
|
|
47
|
+
*/
|
|
48
|
+
export const CreateLabelSchema = z.object({
|
|
49
|
+
boardId: z.string().describe("Board ID"),
|
|
50
|
+
name: z.string().describe("Label name"),
|
|
51
|
+
color: z.enum(VALID_LABEL_COLORS).describe("Label color"),
|
|
52
|
+
position: z.number().optional().describe("Label position (default: 65535)"),
|
|
53
|
+
});
|
|
54
|
+
/**
|
|
55
|
+
* Schema for retrieving labels from a board
|
|
56
|
+
* @property {string} boardId - The ID of the board to get labels from
|
|
57
|
+
*/
|
|
58
|
+
export const GetLabelsSchema = z.object({
|
|
59
|
+
boardId: z.string().describe("Board ID"),
|
|
60
|
+
});
|
|
61
|
+
export const GetLabelSchema = z.object({
|
|
62
|
+
id: z.string().describe("Label ID"),
|
|
63
|
+
});
|
|
64
|
+
/**
|
|
65
|
+
* Schema for updating a label
|
|
66
|
+
* @property {string} id - The ID of the label to update
|
|
67
|
+
* @property {string} [name] - The new name for the label
|
|
68
|
+
* @property {string} [color] - The new color for the label
|
|
69
|
+
* @property {number} [position] - The new position for the label
|
|
70
|
+
*/
|
|
71
|
+
export const UpdateLabelSchema = z.object({
|
|
72
|
+
id: z.string().describe("Label ID"),
|
|
73
|
+
name: z.string().optional().describe("Label name"),
|
|
74
|
+
color: z.enum(VALID_LABEL_COLORS).optional().describe("Label color"),
|
|
75
|
+
position: z.number().optional().describe("Label position"),
|
|
76
|
+
});
|
|
77
|
+
/**
|
|
78
|
+
* Schema for deleting a label
|
|
79
|
+
* @property {string} id - The ID of the label to delete
|
|
80
|
+
*/
|
|
81
|
+
export const DeleteLabelSchema = z.object({
|
|
82
|
+
id: z.string().describe("Label ID"),
|
|
83
|
+
});
|
|
84
|
+
/**
|
|
85
|
+
* Schema for adding a label to a card
|
|
86
|
+
* @property {string} cardId - The ID of the card to add the label to
|
|
87
|
+
* @property {string} labelId - The ID of the label to add to the card
|
|
88
|
+
*/
|
|
89
|
+
export const AddLabelToCardSchema = z.object({
|
|
90
|
+
cardId: z.string().describe("Card ID"),
|
|
91
|
+
labelId: z.string().describe("Label ID"),
|
|
92
|
+
});
|
|
93
|
+
/**
|
|
94
|
+
* Schema for removing a label from a card
|
|
95
|
+
* @property {string} cardId - The ID of the card to remove the label from
|
|
96
|
+
* @property {string} labelId - The ID of the label to remove from the card
|
|
97
|
+
*/
|
|
98
|
+
export const RemoveLabelFromCardSchema = z.object({
|
|
99
|
+
cardId: z.string().describe("Card ID"),
|
|
100
|
+
labelId: z.string().describe("Label ID"),
|
|
101
|
+
});
|
|
102
|
+
// Response schemas
|
|
103
|
+
const LabelsResponseSchema = z.object({
|
|
104
|
+
items: z.array(PlankaLabelSchema),
|
|
105
|
+
included: z.record(z.any()).optional(),
|
|
106
|
+
});
|
|
107
|
+
const LabelResponseSchema = z.object({
|
|
108
|
+
item: PlankaLabelSchema,
|
|
109
|
+
included: z.record(z.any()).optional(),
|
|
110
|
+
});
|
|
111
|
+
const CardLabelResponseSchema = z.object({
|
|
112
|
+
item: z.object({
|
|
113
|
+
id: z.string(),
|
|
114
|
+
cardId: z.string(),
|
|
115
|
+
labelId: z.string(),
|
|
116
|
+
createdAt: z.string(),
|
|
117
|
+
updatedAt: z.string().nullable(),
|
|
118
|
+
}),
|
|
119
|
+
included: z.record(z.any()).optional(),
|
|
120
|
+
});
|
|
121
|
+
// Function implementations
|
|
122
|
+
/**
|
|
123
|
+
* Creates a new label in a board
|
|
124
|
+
*
|
|
125
|
+
* @param {CreateLabelOptions} options - Options for creating the label
|
|
126
|
+
* @param {string} options.boardId - The ID of the board to create the label in
|
|
127
|
+
* @param {string} options.name - The name of the label
|
|
128
|
+
* @param {string} options.color - The color of the label
|
|
129
|
+
* @param {number} [options.position] - The position of the label in the board (default: 65535)
|
|
130
|
+
* @returns {Promise<object>} The created label
|
|
131
|
+
* @throws {Error} If the label creation fails
|
|
132
|
+
*/
|
|
133
|
+
export async function createLabel(options) {
|
|
134
|
+
try {
|
|
135
|
+
const response = await plankaRequest(`/api/boards/${options.boardId}/labels`, {
|
|
136
|
+
method: "POST",
|
|
137
|
+
body: {
|
|
138
|
+
name: options.name,
|
|
139
|
+
color: options.color,
|
|
140
|
+
position: options.position,
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
const parsedResponse = LabelResponseSchema.parse(response);
|
|
144
|
+
return parsedResponse.item;
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
throw new Error(`Failed to create label: ${error instanceof Error ? error.message : String(error)}`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Retrieves all labels for a specific board
|
|
152
|
+
*
|
|
153
|
+
* @param {string} boardId - The ID of the board to get labels from
|
|
154
|
+
* @returns {Promise<Array<object>>} Array of labels in the board
|
|
155
|
+
*/
|
|
156
|
+
export async function getLabels(boardId) {
|
|
157
|
+
try {
|
|
158
|
+
// Get the board which includes labels in the response
|
|
159
|
+
const response = await plankaRequest(`/api/boards/${boardId}`);
|
|
160
|
+
// Check if the response has the expected structure
|
|
161
|
+
if (response &&
|
|
162
|
+
typeof response === "object" &&
|
|
163
|
+
"included" in response &&
|
|
164
|
+
response.included &&
|
|
165
|
+
typeof response.included === "object" &&
|
|
166
|
+
"labels" in response.included) {
|
|
167
|
+
// Get the labels from the included property
|
|
168
|
+
const labels = response.included.labels;
|
|
169
|
+
if (Array.isArray(labels)) {
|
|
170
|
+
return labels;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
// If we can't find labels in the expected format, return an empty array
|
|
174
|
+
return [];
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
// If all else fails, return an empty array
|
|
178
|
+
return [];
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Updates a label's properties
|
|
183
|
+
*
|
|
184
|
+
* @param {string} id - The ID of the label to update
|
|
185
|
+
* @param {Partial<Omit<CreateLabelOptions, "boardId">>} options - The properties to update
|
|
186
|
+
* @returns {Promise<object>} The updated label
|
|
187
|
+
*/
|
|
188
|
+
export async function updateLabel(id, options) {
|
|
189
|
+
try {
|
|
190
|
+
const response = await plankaRequest(`/api/labels/${id}`, {
|
|
191
|
+
method: "PATCH",
|
|
192
|
+
body: options,
|
|
193
|
+
});
|
|
194
|
+
const parsedResponse = LabelResponseSchema.parse(response);
|
|
195
|
+
return parsedResponse.item;
|
|
196
|
+
}
|
|
197
|
+
catch (error) {
|
|
198
|
+
throw new Error(`Failed to update label: ${error instanceof Error ? error.message : String(error)}`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Deletes a label by ID
|
|
203
|
+
*
|
|
204
|
+
* @param {string} id - The ID of the label to delete
|
|
205
|
+
* @returns {Promise<{success: boolean}>} Success indicator
|
|
206
|
+
*/
|
|
207
|
+
export async function deleteLabel(id) {
|
|
208
|
+
try {
|
|
209
|
+
await plankaRequest(`/api/labels/${id}`, {
|
|
210
|
+
method: "DELETE",
|
|
211
|
+
});
|
|
212
|
+
return { success: true };
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
throw new Error(`Failed to delete label: ${error instanceof Error ? error.message : String(error)}`);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Adds a label to a card
|
|
220
|
+
*
|
|
221
|
+
* @param {string} cardId - The ID of the card to add the label to
|
|
222
|
+
* @param {string} labelId - The ID of the label to add to the card
|
|
223
|
+
* @returns {Promise<object>} The created card-label relationship
|
|
224
|
+
*/
|
|
225
|
+
export async function addLabelToCard(cardId, labelId) {
|
|
226
|
+
try {
|
|
227
|
+
// The correct endpoint is /api/cards/{cardId}/labels with labelId in the body
|
|
228
|
+
await plankaRequest(`/api/cards/${cardId}/labels`, {
|
|
229
|
+
method: "POST",
|
|
230
|
+
body: {
|
|
231
|
+
labelId,
|
|
232
|
+
},
|
|
233
|
+
});
|
|
234
|
+
return { success: true };
|
|
235
|
+
}
|
|
236
|
+
catch (error) {
|
|
237
|
+
throw new Error(`Failed to add label to card: ${error instanceof Error ? error.message : String(error)}`);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Removes a label from a card
|
|
242
|
+
*
|
|
243
|
+
* @param {string} cardId - The ID of the card to remove the label from
|
|
244
|
+
* @param {string} labelId - The ID of the label to remove from the card
|
|
245
|
+
* @returns {Promise<{success: boolean}>} Success indicator
|
|
246
|
+
*/
|
|
247
|
+
export async function removeLabelFromCard(cardId, labelId) {
|
|
248
|
+
try {
|
|
249
|
+
// The correct endpoint is /api/cards/{cardId}/labels/{labelId}
|
|
250
|
+
await plankaRequest(`/api/cards/${cardId}/labels/${labelId}`, {
|
|
251
|
+
method: "DELETE",
|
|
252
|
+
});
|
|
253
|
+
return { success: true };
|
|
254
|
+
}
|
|
255
|
+
catch (error) {
|
|
256
|
+
throw new Error(`Failed to remove label from card: ${error instanceof Error ? error.message : String(error)}`);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview List operations for the MCP Kanban server
|
|
3
|
+
*
|
|
4
|
+
* This module provides functions for interacting with lists in the Planka Kanban board,
|
|
5
|
+
* including creating, retrieving, updating, and deleting lists.
|
|
6
|
+
*/
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import { plankaRequest } from "../common/utils.js";
|
|
9
|
+
import { PlankaListSchema } from "../common/types.js";
|
|
10
|
+
// Schema definitions
|
|
11
|
+
/**
|
|
12
|
+
* Schema for creating a new list
|
|
13
|
+
* @property {string} boardId - The ID of the board to create the list in
|
|
14
|
+
* @property {string} name - The name of the list
|
|
15
|
+
* @property {number} [position] - The position of the list in the board (default: 65535)
|
|
16
|
+
*/
|
|
17
|
+
export const CreateListSchema = z.object({
|
|
18
|
+
boardId: z.string().describe("Board ID"),
|
|
19
|
+
name: z.string().describe("List name"),
|
|
20
|
+
position: z.number().optional().describe("List position (default: 65535)"),
|
|
21
|
+
});
|
|
22
|
+
/**
|
|
23
|
+
* Schema for retrieving lists from a board
|
|
24
|
+
* @property {string} boardId - The ID of the board to get lists from
|
|
25
|
+
*/
|
|
26
|
+
export const GetListsSchema = z.object({
|
|
27
|
+
boardId: z.string().describe("Board ID"),
|
|
28
|
+
});
|
|
29
|
+
/**
|
|
30
|
+
* Schema for updating a list
|
|
31
|
+
* @property {string} id - The ID of the list to update
|
|
32
|
+
* @property {string} [name] - The new name for the list
|
|
33
|
+
* @property {number} [position] - The new position for the list
|
|
34
|
+
*/
|
|
35
|
+
export const UpdateListSchema = z.object({
|
|
36
|
+
id: z.string().describe("List ID"),
|
|
37
|
+
name: z.string().optional().describe("List name"),
|
|
38
|
+
position: z.number().optional().describe("List position"),
|
|
39
|
+
});
|
|
40
|
+
/**
|
|
41
|
+
* Schema for deleting a list
|
|
42
|
+
* @property {string} id - The ID of the list to delete
|
|
43
|
+
*/
|
|
44
|
+
export const DeleteListSchema = z.object({
|
|
45
|
+
id: z.string().describe("List ID"),
|
|
46
|
+
});
|
|
47
|
+
// Response schemas
|
|
48
|
+
const ListsResponseSchema = z.object({
|
|
49
|
+
items: z.array(PlankaListSchema),
|
|
50
|
+
included: z.record(z.any()).optional(),
|
|
51
|
+
});
|
|
52
|
+
const ListResponseSchema = z.object({
|
|
53
|
+
item: PlankaListSchema,
|
|
54
|
+
included: z.record(z.any()).optional(),
|
|
55
|
+
});
|
|
56
|
+
// Function implementations
|
|
57
|
+
/**
|
|
58
|
+
* Creates a new list in a board
|
|
59
|
+
*
|
|
60
|
+
* @param {CreateListOptions} options - Options for creating the list
|
|
61
|
+
* @param {string} options.boardId - The ID of the board to create the list in
|
|
62
|
+
* @param {string} options.name - The name of the list
|
|
63
|
+
* @param {number} [options.position] - The position of the list in the board (default: 65535)
|
|
64
|
+
* @returns {Promise<object>} The created list
|
|
65
|
+
* @throws {Error} If the list creation fails
|
|
66
|
+
*/
|
|
67
|
+
export async function createList(options) {
|
|
68
|
+
try {
|
|
69
|
+
const response = await plankaRequest(`/api/boards/${options.boardId}/lists`, {
|
|
70
|
+
method: "POST",
|
|
71
|
+
body: {
|
|
72
|
+
name: options.name,
|
|
73
|
+
position: options.position,
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
const parsedResponse = ListResponseSchema.parse(response);
|
|
77
|
+
return parsedResponse.item;
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
throw new Error(`Failed to create list: ${error instanceof Error ? error.message : String(error)}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Retrieves all lists for a specific board
|
|
85
|
+
*
|
|
86
|
+
* @param {string} boardId - The ID of the board to get lists from
|
|
87
|
+
* @returns {Promise<Array<object>>} Array of lists in the board
|
|
88
|
+
*/
|
|
89
|
+
export async function getLists(boardId) {
|
|
90
|
+
try {
|
|
91
|
+
// Get the board which includes lists in the response
|
|
92
|
+
const response = await plankaRequest(`/api/boards/${boardId}`);
|
|
93
|
+
// Check if the response has the expected structure
|
|
94
|
+
if (response &&
|
|
95
|
+
typeof response === "object" &&
|
|
96
|
+
"included" in response &&
|
|
97
|
+
response.included &&
|
|
98
|
+
typeof response.included === "object" &&
|
|
99
|
+
"lists" in response.included) {
|
|
100
|
+
// Get the lists from the included property
|
|
101
|
+
const lists = response.included.lists;
|
|
102
|
+
if (Array.isArray(lists)) {
|
|
103
|
+
return lists;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// If we can't find lists in the expected format, return an empty array
|
|
107
|
+
return [];
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
// If all else fails, return an empty array
|
|
111
|
+
return [];
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Retrieves a specific list by ID
|
|
116
|
+
*
|
|
117
|
+
* @param {string} id - The ID of the list to retrieve
|
|
118
|
+
* @returns {Promise<object|null>} The requested list or null if not found
|
|
119
|
+
*/
|
|
120
|
+
export async function getList(id) {
|
|
121
|
+
try {
|
|
122
|
+
const response = await plankaRequest(`/api/lists/${id}`);
|
|
123
|
+
const parsedResponse = ListResponseSchema.parse(response);
|
|
124
|
+
return parsedResponse.item;
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
console.error(`Error getting list with ID ${id}:`, error);
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Updates a list's properties
|
|
133
|
+
*
|
|
134
|
+
* @param {string} id - The ID of the list to update
|
|
135
|
+
* @param {Partial<Omit<CreateListOptions, "boardId">>} options - The properties to update
|
|
136
|
+
* @returns {Promise<object>} The updated list
|
|
137
|
+
*/
|
|
138
|
+
export async function updateList(id, options) {
|
|
139
|
+
const response = await plankaRequest(`/api/lists/${id}`, {
|
|
140
|
+
method: "PATCH",
|
|
141
|
+
body: options,
|
|
142
|
+
});
|
|
143
|
+
const parsedResponse = ListResponseSchema.parse(response);
|
|
144
|
+
return parsedResponse.item;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Deletes a list by ID
|
|
148
|
+
*
|
|
149
|
+
* @param {string} id - The ID of the list to delete
|
|
150
|
+
* @returns {Promise<{success: boolean}>} Success indicator
|
|
151
|
+
*/
|
|
152
|
+
export async function deleteList(id) {
|
|
153
|
+
await plankaRequest(`/api/lists/${id}`, {
|
|
154
|
+
method: "DELETE",
|
|
155
|
+
});
|
|
156
|
+
return { success: true };
|
|
157
|
+
}
|