UNPKG

@lautarobarba/bookstack-mcp-server

Version:

MCP Server for BookStack API - Allows AI models to interact with BookStack wiki

153 lines (152 loc) 4.97 kB
#!/usr/bin/env node import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js"; import { BookStackClient } from "./lib/bookstack-client.js"; import { createContentTools, handleContentTool, } from "./tools/content-tools.js"; import { createSearchAndUserTools, handleSearchAndUserTool, } from "./tools/search-user-tools.js"; // Configuration from environment variables const config = { baseUrl: process.env.BOOKSTACK_BASE_URL || "http://localhost", tokenId: process.env.BOOKSTACK_TOKEN_ID || "", token: process.env.BOOKSTACK_TOKEN || "", }; // Validate configuration if (!config.baseUrl || !config.tokenId || !config.token) { console.error("Missing required configuration. Please set:"); console.error("- BOOKSTACK_BASE_URL: Base URL of your BookStack instance"); console.error("- BOOKSTACK_TOKEN_ID: API Token ID"); console.error("- BOOKSTACK_TOKEN: API Token Secret"); process.exit(1); } // Initialize BookStack client const bookStackClient = new BookStackClient(config); // Initialize MCP server const server = new Server({ name: "bookstack-mcp-server", version: "1.0.0", description: "MCP server for BookStack API integration, enabling AI models to generate and edit wiki content", }, { capabilities: { tools: {}, }, }); // Combine all tools const allTools = [ ...createContentTools(bookStackClient), ...createSearchAndUserTools(bookStackClient), ]; // List tools handler server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: allTools, }; }); // Call tool handler server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { let result; // Content tools (books, chapters, pages, shelves) const contentToolNames = [ "list_books", "get_book", "create_book", "update_book", "delete_book", "export_book", "list_chapters", "get_chapter", "create_chapter", "update_chapter", "delete_chapter", "export_chapter", "list_pages", "get_page", "create_page", "update_page", "delete_page", "export_page", "list_shelves", "get_shelf", "create_shelf", "update_shelf", "delete_shelf", ]; // Search and user tools const searchUserToolNames = [ "search_all", "list_users", "get_user", "create_user", "update_user", "delete_user", "list_roles", "get_role", "create_role", "update_role", "delete_role", "list_attachments", "get_attachment", "delete_attachment", "list_images", "get_image", "update_image", "delete_image", ]; if (contentToolNames.includes(name)) { result = await handleContentTool(name, args, bookStackClient); } else if (searchUserToolNames.includes(name)) { result = await handleSearchAndUserTool(name, args, bookStackClient); } else { throw new Error(`Unknown tool: ${name}`); } return { content: [ { type: "text", text: result, }, ], }; } catch (error) { const errorMessage = error instanceof Error ? error.message : "An unknown error occurred"; return { content: [ { type: "text", text: `Error executing tool "${name}": ${errorMessage}`, }, ], isError: true, }; } }); // Enhanced error handling process.on("uncaughtException", (error) => { console.error("Uncaught Exception:", error); process.exit(1); }); process.on("unhandledRejection", (reason, promise) => { console.error("Unhandled Rejection at:", promise, "reason:", reason); process.exit(1); }); // Start the server async function main() { const transport = new StdioServerTransport(); await server.connect(transport); // Keep the process alive console.error("BookStack MCP Server started successfully"); console.error(`Connected to BookStack at: ${config.baseUrl}`); console.error("Available tools:"); allTools.forEach((tool) => { console.error(` - ${tool.name}: ${tool.description}`); }); } main().catch((error) => { console.error("Failed to start server:", error); process.exit(1); });