UNPKG

@edicarlos.lds/businessmap-mcp

Version:

Model Context Protocol server for BusinessMap (Kanbanize) integration

210 lines 9.64 kB
import { createBoardSchema, createLaneSchema, getBoardSchema, getCurrentBoardStructureSchema, getLaneSchema, listBoardsSchema, searchBoardSchema, } from '../../schemas/index.js'; import { createErrorResponse, createSuccessResponse } from './base-tool.js'; export class BoardToolHandler { registerTools(server, client, readOnlyMode) { this.registerListBoards(server, client); this.registerSearchBoard(server, client); this.registerGetColumns(server, client); this.registerGetLanes(server, client); this.registerGetLane(server, client); this.registerGetCurrentBoardStructure(server, client); if (!readOnlyMode) { this.registerCreateBoard(server, client); this.registerCreateLane(server, client); } } registerListBoards(server, client) { server.registerTool('list_boards', { title: 'List Boards', description: 'Get a list of boards with optional filters', inputSchema: listBoardsSchema.shape, }, async (params) => { try { const boards = await client.getBoards(params); return createSuccessResponse(boards); } catch (error) { return createErrorResponse(error, 'fetching boards'); } }); } registerSearchBoard(server, client) { server.registerTool('search_board', { title: 'Search Board', description: 'Search for a board by ID or name, with intelligent fallback to list all boards if direct search fails', inputSchema: searchBoardSchema.shape, }, async ({ board_id, board_name, workspace_id }) => { try { if (board_id) { return await this.searchBoardById(client, board_id, workspace_id); } if (board_name) { return await this.searchBoardByName(client, board_name, workspace_id); } // If neither ID nor name provided, list all boards return await this.getAllBoards(client, workspace_id); } catch (error) { return createErrorResponse(error, 'searching for board'); } }); } async searchBoardById(client, boardId, workspaceId) { try { const [board, structure] = await Promise.all([ client.getBoard(boardId), client.getBoardStructure(boardId), ]); return createSuccessResponse({ ...board, structure }, 'Board found directly:'); } catch (directError) { console.warn(`Direct board lookup failed for ID ${boardId}:`, directError instanceof Error ? directError.message : 'Unknown error'); return await this.searchBoardByIdFallback(client, boardId, workspaceId); } } async searchBoardByIdFallback(client, boardId, workspaceId) { const boards = await client.getBoards(workspaceId ? { workspace_id: workspaceId } : undefined); const foundBoard = boards.find((b) => b.board_id === boardId); if (!foundBoard) { return createErrorResponse(new Error(`Board with ID ${boardId} not found. Available boards:\n${JSON.stringify(this.formatBoardsList(boards), null, 2)}`), 'searching for board'); } return await this.getBoardWithStructure(client, foundBoard, 'Board found via list search:'); } async searchBoardByName(client, boardName, workspaceId) { const boards = await client.getBoards(workspaceId ? { workspace_id: workspaceId } : undefined); const foundBoards = boards.filter((b) => b.name.toLowerCase().includes(boardName.toLowerCase())); if (foundBoards.length === 0) { return createErrorResponse(new Error(`No boards found matching name "${boardName}". Available boards:\n${JSON.stringify(this.formatBoardsList(boards), null, 2)}`), 'searching for board by name'); } if (foundBoards.length === 1) { const foundBoard = foundBoards[0]; if (!foundBoard.board_id) { return createErrorResponse(new Error('Board missing board_id'), 'board validation'); } return await this.getBoardWithStructure(client, foundBoard, 'Board found by name:'); } return createSuccessResponse(this.formatBoardsList(foundBoards), `Multiple boards found matching "${boardName}":`); } async getAllBoards(client, workspaceId) { const boards = await client.getBoards(workspaceId ? { workspace_id: workspaceId } : undefined); return createSuccessResponse(this.formatBoardsList(boards), 'All available boards:'); } async getBoardWithStructure(client, board, successMessage) { try { const structure = await client.getBoardStructure(board.board_id); return createSuccessResponse({ ...board, structure }, successMessage); } catch (structureError) { console.warn(`Structure lookup failed for board ID ${board.board_id}:`, structureError instanceof Error ? structureError.message : 'Unknown error'); return createSuccessResponse(board, `Board found but structure unavailable. Structure error: ${structureError instanceof Error ? structureError.message : 'Unknown error'}`); } } formatBoardsList(boards) { return boards.map((b) => ({ board_id: b.board_id, name: b.name, workspace_id: b.workspace_id, })); } registerGetColumns(server, client) { server.registerTool('get_columns', { title: 'Get Board Columns', description: 'Get all columns for a board (válido na API oficial)', inputSchema: getBoardSchema.shape, }, async ({ board_id }) => { try { const columns = await client.getColumns(board_id); return createSuccessResponse(columns); } catch (error) { return createErrorResponse(error, 'fetching board columns'); } }); } registerGetLanes(server, client) { server.registerTool('get_lanes', { title: 'Get Board Lanes', description: 'Get all lanes/swimlanes for a board (válido na API oficial)', inputSchema: getBoardSchema.shape, }, async ({ board_id }) => { try { const lanes = await client.getLanes(board_id); return createSuccessResponse(lanes); } catch (error) { return createErrorResponse(error, 'fetching board lanes'); } }); } registerGetLane(server, client) { server.registerTool('get_lane', { title: 'Get Lane Details', description: 'Get details of a specific lane/swimlane (válido na API oficial)', inputSchema: getLaneSchema.shape, }, async ({ lane_id }) => { try { const lane = await client.getLane(lane_id); return createSuccessResponse(lane); } catch (error) { return createErrorResponse(error, 'fetching lane details'); } }); } registerCreateBoard(server, client) { server.registerTool('create_board', { title: 'Create Board', description: 'Create a new board in a workspace', inputSchema: createBoardSchema.shape, }, async ({ name, workspace_id, description }) => { try { const board = await client.createBoard({ name, workspace_id, description, }); return createSuccessResponse(board, 'Board created successfully:'); } catch (error) { return createErrorResponse(error, 'creating board'); } }); } registerCreateLane(server, client) { server.registerTool('create_lane', { title: 'Create Lane', description: 'Create a new lane/swimlane in a board (válido na API oficial)', inputSchema: createLaneSchema.shape, }, async ({ workflow_id, name, description, color, position }) => { try { const lane = await client.createLane({ workflow_id, name, description: description || null, color, position, }); return createSuccessResponse(lane, 'Lane created successfully:'); } catch (error) { return createErrorResponse(error, 'creating lane'); } }); } registerGetCurrentBoardStructure(server, client) { server.registerTool('get_current_board_structure', { title: 'Get Current Board Structure', description: 'Get the complete current structure of a board including workflows, columns, lanes, and configurations', inputSchema: getCurrentBoardStructureSchema.shape, }, async ({ board_id }) => { try { const structure = await client.getCurrentBoardStructure(board_id); return createSuccessResponse(structure, 'Board structure retrieved successfully:'); } catch (error) { return createErrorResponse(error, 'getting current board structure'); } }); } } //# sourceMappingURL=board-tools.js.map