🐛 Bug Report
Describe the bug
The MCP tools add_card_comment and update_card_comment (packages/mcp/src/tools/card.ts) send the comment text in the request body under the key content, but the REST endpoints POST /cards/{cardPublicId}/comments and PUT /cards/{cardPublicId}/comments/{commentPublicId} expect it under the key comment. Every comment create/update through the MCP server therefore fails input validation with BAD_REQUEST — it is impossible to write comments via MCP at all.
A second, related problem makes it look like reading comments is also broken: the get_card tool description promises "full details of a card including comments", but the card-detail response (cardDetailSchema) has no top-level comments array. Comments only surface nested inside activities[] entries of type card.updated.comment.added (at activity.comment.comment), and since tRPC-OpenAPI strips fields not in the output schema, there is no way to get a plain comments list. Agents looking for a comments field in the get_card response come up empty and conclude comments cannot be read.
To Reproduce
Steps to reproduce the behavior:
- Configure the Kan MCP server against a Kan instance.
- Ask the agent (or call the tool directly) to add a comment to a card:
add_card_comment with { cardPublicId, content: "Hello" }.
- See the API reject the request with
400 BAD_REQUEST (comment is required but missing — the body key sent is content).
- Call
get_card on a card that has comments (added via the web UI) and observe there is no comments field in the response; the comments are only found nested in activities[].comment.
Expected behavior
add_card_comment and update_card_comment should succeed — the MCP layer should send the body field the API expects (comment). The get_card / get_card_activities tool descriptions should tell the agent where comment text actually lives in the response.
Root cause
The MCP tools send content:
// packages/mcp/src/tools/card.ts
server.tool(
"add_card_comment",
"Add a comment to a card",
{
cardPublicId: z.string().describe("The card's public ID"),
content: z.string().describe("Comment text"),
},
async ({ cardPublicId, content }) => {
const data = await kanRequest("POST", `/cards/${cardPublicId}/comments`, { content }); // <-- wrong key
...
},
);
But the REST API expects comment — packages/api/src/routers/card.ts, addComment mutation:
.input(
z.object({
cardPublicId: z.string().min(12),
comment: z.string().min(1), // <-- required, so `{ content: ... }` always fails validation
}),
)
The same mismatch exists in updateComment (input: comment: z.string().min(1)) vs. the MCP update_card_comment tool (sends { content }). delete_card_comment is unaffected as it only uses path parameters.
For the read path, cardDetailSchema (packages/api/src/schemas/card.ts) contains labels, attachments, checklists, list, members and activities — but no comments array; comment text is only reachable via activities[].comment on card.updated.comment.added entries.
Proposed fix
Map the MCP tool parameter to the field name the API expects, in both tools:
async ({ cardPublicId, content }) => {
const data = await kanRequest("POST", `/cards/${cardPublicId}/comments`, {
comment: content,
});
...
},
And update the tool descriptions so agents can find comments in the response, e.g. for get_card:
"… Note: the response has no top-level comments array — comments are nested in the activities array as entries of type card.updated.comment.added, with the text at activity.comment.comment."
(Optionally, adding a proper comments array to cardDetailSchema — or a dedicated GET /cards/{cardPublicId}/comments endpoint — would be the cleaner long-term fix for the read path.)
Screenshots
N/A.
Environment (please complete the following information):
- OS: macOS (not relevant — field-name mismatch in the MCP request body, not platform-specific)
- Browser: N/A (MCP server over stdio)
- Node version: N/A — reproducible on any supported version (
>=18); not a factor
- App version/commit:
main @ 9ec4bedd9ad0 (packages/mcp/src/tools/card.ts, Kan REST API v1)
Additional context
The write-path bug is a two-line fix in packages/mcp/src/tools/card.ts ({ content } → { comment: content } in add_card_comment and update_card_comment). The read-path confusion is purely a tool-description gap — the data is present, just nested under activities where agents don't expect it.
🐛 Bug Report
Describe the bug
The MCP tools
add_card_commentandupdate_card_comment(packages/mcp/src/tools/card.ts) send the comment text in the request body under the keycontent, but the REST endpointsPOST /cards/{cardPublicId}/commentsandPUT /cards/{cardPublicId}/comments/{commentPublicId}expect it under the keycomment. Every comment create/update through the MCP server therefore fails input validation withBAD_REQUEST— it is impossible to write comments via MCP at all.A second, related problem makes it look like reading comments is also broken: the
get_cardtool description promises "full details of a card including comments", but the card-detail response (cardDetailSchema) has no top-levelcommentsarray. Comments only surface nested insideactivities[]entries of typecard.updated.comment.added(atactivity.comment.comment), and since tRPC-OpenAPI strips fields not in the output schema, there is no way to get a plain comments list. Agents looking for acommentsfield in theget_cardresponse come up empty and conclude comments cannot be read.To Reproduce
Steps to reproduce the behavior:
add_card_commentwith{ cardPublicId, content: "Hello" }.400 BAD_REQUEST(commentis required but missing — the body key sent iscontent).get_cardon a card that has comments (added via the web UI) and observe there is nocommentsfield in the response; the comments are only found nested inactivities[].comment.Expected behavior
add_card_commentandupdate_card_commentshould succeed — the MCP layer should send the body field the API expects (comment). Theget_card/get_card_activitiestool descriptions should tell the agent where comment text actually lives in the response.Root cause
The MCP tools send
content:But the REST API expects
comment—packages/api/src/routers/card.ts,addCommentmutation:The same mismatch exists in
updateComment(input:comment: z.string().min(1)) vs. the MCPupdate_card_commenttool (sends{ content }).delete_card_commentis unaffected as it only uses path parameters.For the read path,
cardDetailSchema(packages/api/src/schemas/card.ts) containslabels,attachments,checklists,list,membersandactivities— but nocommentsarray; comment text is only reachable viaactivities[].commentoncard.updated.comment.addedentries.Proposed fix
Map the MCP tool parameter to the field name the API expects, in both tools:
And update the tool descriptions so agents can find comments in the response, e.g. for
get_card:(Optionally, adding a proper
commentsarray tocardDetailSchema— or a dedicatedGET /cards/{cardPublicId}/commentsendpoint — would be the cleaner long-term fix for the read path.)Screenshots
N/A.
Environment (please complete the following information):
>=18); not a factormain@9ec4bedd9ad0(packages/mcp/src/tools/card.ts, Kan REST API v1)Additional context
The write-path bug is a two-line fix in
packages/mcp/src/tools/card.ts({ content }→{ comment: content }inadd_card_commentandupdate_card_comment). The read-path confusion is purely a tool-description gap — the data is present, just nested underactivitieswhere agents don't expect it.