businessmap-mcp
Version:
MCP server for Businessmap Kanbanize, exposing tools for managing business entities like boards, cards, and columns, facilitating LLM interaction.
155 lines (154 loc) • 7.99 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.CardCommentsToolsController = void 0;
const zod_1 = require("zod");
const ApiService_1 = require("../../services/ApiService");
const apiResponseHandler_1 = require("../../utils/apiResponseHandler");
const env_1 = require("../../utils/env");
class CardCommentsToolsController {
server;
constructor(server) {
this.server = server;
this.registerTools();
}
registerTools() {
this.registerGetCardCommentsToolhandler();
this.registerGetCardCommentToolhandler();
if (!env_1.env.BUSINESSMAP_READ_ONLY) {
this.registerAddCardCommentToolhandler();
this.registerAddFormattedCardCommentToolhandler();
this.registerUpdateCardCommentToolhandler();
this.registerDeleteCardCommentToolhandler();
}
}
registerGetCardCommentsToolhandler() {
this.server.tool("get-card-comments", "Get a card's comments", {
cardId: zod_1.z.string().describe("A card id"),
}, async ({ cardId }) => {
const response = await ApiService_1.apiServices.getCardComments(cardId);
return (0, apiResponseHandler_1.handleApiResponse)(response);
});
}
registerAddCardCommentToolhandler() {
this.server.tool("add-card-comment", "Add a comment to a card", {
cardId: zod_1.z.string().describe("A card id"),
comment: zod_1.z.string().describe("Comment to be added"),
}, async ({ cardId, comment }) => {
const response = await ApiService_1.apiServices.addCardComment(cardId, comment);
return (0, apiResponseHandler_1.handleApiResponse)(response);
});
}
registerGetCardCommentToolhandler() {
this.server.tool("get-card-comment", "Get the details of a comment for a card", {
cardId: zod_1.z.string().describe("A card id"),
commentId: zod_1.z.string().describe("A comment id"),
}, async ({ cardId, commentId }) => {
const response = await ApiService_1.apiServices.getCardComment(cardId, commentId);
return (0, apiResponseHandler_1.handleApiResponse)(response);
});
}
registerUpdateCardCommentToolhandler() {
this.server.tool("update-card-comment", "Update the details of a comment for a card", {
cardId: zod_1.z.string().describe("A card id"),
commentId: zod_1.z.string().describe("A comment id"),
comment: zod_1.z.string().describe("A comment a updated"),
}, async ({ cardId, commentId, comment }) => {
const response = await ApiService_1.apiServices.updateCardComment(cardId, commentId, comment);
return (0, apiResponseHandler_1.handleApiResponse)(response);
});
}
registerDeleteCardCommentToolhandler() {
this.server.tool("delete-card-comment", "Delete a comment for a card", {
cardId: zod_1.z.string().describe("A card id"),
commentId: zod_1.z.string().describe("A comment id"),
}, async ({ cardId, commentId }) => {
const response = await ApiService_1.apiServices.deleteCardComment(cardId, commentId);
return (0, apiResponseHandler_1.handleApiResponse)(response);
});
}
/**
* IMPORTANT - API Limitation:
* The comment creation API (addCardComment) only accepts plain text (type: plain),
* it does not accept HTML. The update API (updateCardComment) accepts complete HTML with inline styles.
*
* This tool accepts HTML directly and internally:
* 1. Extracts plain text from HTML to create the comment
* 2. Waits for the returned comment_id
* 3. Immediately updates with the original HTML
*
* For the user, it appears as a single HTML creation operation.
*/
registerAddFormattedCardCommentToolhandler() {
this.server.tool("add-formatted-card-comment", "Add a formatted comment to a card with HTML rich formatting. Accepts HTML content directly. Internally creates a plain text comment first, then immediately updates it with the provided HTML formatting.", {
cardId: zod_1.z.string().describe("A card id"),
htmlContent: zod_1.z.string().describe("Comment content in HTML format (e.g., <h3>Title</h3><p>Paragraph</p>)"),
}, async ({ cardId, htmlContent }) => {
// Step 1: Extract plain text from HTML for creation
// Remove HTML tags to get plain text, preserving text content
const plainText = htmlContent
.replace(/<[^>]*>/g, '') // Remove HTML tags
.replace(/ /g, ' ') // Replace with space
.replace(/&/g, '&') // Replace & with &
.replace(/</g, '<') // Replace < with <
.replace(/>/g, '>') // Replace > with >
.replace(/"/g, '"') // Replace " with "
.replace(/'/g, "'") // Replace ' with '
.replace(/\s+/g, ' ') // Normalize whitespace
.trim();
// If plain text is empty after extraction, use a placeholder
const textForCreation = plainText || 'Comment';
// Step 2: Create comment with plain text (API limitation)
// This is done internally, user doesn't need to know
const createResponse = await ApiService_1.apiServices.addCardComment(cardId, textForCreation);
if (createResponse.error) {
return (0, apiResponseHandler_1.handleApiResponse)(createResponse);
}
if (!createResponse.data) {
return (0, apiResponseHandler_1.handleApiResponse)({
error: {
code: "NO_DATA",
message: "Failed to create comment - no data returned",
reference: "Check API response"
}
});
}
// Step 3: Extract comment_id from response
// The response structure is: { data: CardCommentResponsePost }
// CardCommentResponsePost has: { data: DataResponsePost }
// DataResponsePost has: { comment_id: number }
let commentId;
// Try primary path: createResponse.data.data.comment_id
if (createResponse.data?.data?.comment_id) {
commentId = createResponse.data.data.comment_id.toString();
}
// Try alternative path in case API structure is different
else if (createResponse.data?.comment_id) {
commentId = createResponse.data.comment_id.toString();
}
if (!commentId) {
return (0, apiResponseHandler_1.handleApiResponse)({
error: {
code: "NO_COMMENT_ID",
message: "Failed to get comment_id from creation response",
reference: `Check API response structure. Response: ${JSON.stringify(createResponse.data)}`
}
});
}
// Step 4: Update comment immediately with HTML formatting
// This happens internally, user sees it as a single operation
const updateResponse = await ApiService_1.apiServices.updateCardComment(cardId, commentId, htmlContent);
if (updateResponse.error) {
return (0, apiResponseHandler_1.handleApiResponse)(updateResponse);
}
// Return success as if it was a single operation
// User doesn't need to know about the two-step process
return (0, apiResponseHandler_1.handleApiResponse)({
data: {
comment_id: commentId,
...updateResponse.data
}
});
});
}
}
exports.CardCommentsToolsController = CardCommentsToolsController;