@lautarobarba/bookstack-mcp-server
Version:
MCP Server for BookStack API - Allows AI models to interact with BookStack wiki
471 lines (470 loc) • 18.5 kB
JavaScript
import { formatApiResponse, handleApiError, parseInteger, PaginationSchema, } from "../lib/validation.js";
export function createSearchAndUserTools(client) {
return [
// ========== SEARCH ==========
{
name: "search_all",
description: "Search across all content types (books, chapters, pages, shelves) in BookStack",
inputSchema: {
type: "object",
properties: {
query: {
type: "string",
description: "Search query string (required)",
},
page: {
type: "number",
description: "Page number for pagination",
},
count: {
type: "number",
description: "Number of items per page (default 20, max 500)",
},
},
required: ["query"],
},
},
// ========== USERS ==========
{
name: "list_users",
description: "Get a listing of users in the system",
inputSchema: {
type: "object",
properties: {
page: { type: "number", description: "Page number for pagination" },
count: { type: "number", description: "Number of items per page" },
sort: { type: "string", description: "Sort parameter" },
},
},
},
{
name: "get_user",
description: "Get details of a specific user",
inputSchema: {
type: "object",
properties: {
id: { type: "number", description: "User ID" },
},
required: ["id"],
},
},
{
name: "create_user",
description: "Create a new user account",
inputSchema: {
type: "object",
properties: {
name: {
type: "string",
description: "Full name of the user (required)",
},
email: {
type: "string",
description: "Email address (required, must be unique)",
},
password: {
type: "string",
description: "Password (required, min 8 characters)",
},
roles: {
type: "array",
description: "Array of role IDs to assign to the user",
items: { type: "number" },
},
language: {
type: "string",
description: "User interface language code",
},
external_auth_id: {
type: "string",
description: "External authentication ID",
},
send_invite: {
type: "boolean",
description: "Send invitation email to user",
},
},
required: ["name", "email", "password"],
},
},
{
name: "update_user",
description: "Update an existing user account",
inputSchema: {
type: "object",
properties: {
id: { type: "number", description: "User ID" },
name: { type: "string", description: "Full name of the user" },
email: {
type: "string",
description: "Email address (must be unique)",
},
password: {
type: "string",
description: "New password (min 8 characters)",
},
roles: {
type: "array",
description: "Array of role IDs to assign to the user (replaces existing)",
items: { type: "number" },
},
language: {
type: "string",
description: "User interface language code",
},
external_auth_id: {
type: "string",
description: "External authentication ID",
},
},
required: ["id"],
},
},
{
name: "delete_user",
description: "Delete a user account (requires admin permissions)",
inputSchema: {
type: "object",
properties: {
id: { type: "number", description: "User ID" },
migrate_ownership_id: {
type: "number",
description: "ID of user to transfer ownership of content to",
},
},
required: ["id"],
},
},
// ========== ROLES ==========
{
name: "list_roles",
description: "Get a listing of roles in the system",
inputSchema: {
type: "object",
properties: {
page: { type: "number", description: "Page number for pagination" },
count: { type: "number", description: "Number of items per page" },
sort: { type: "string", description: "Sort parameter" },
},
},
},
{
name: "get_role",
description: "Get details of a specific role including permissions",
inputSchema: {
type: "object",
properties: {
id: { type: "number", description: "Role ID" },
},
required: ["id"],
},
},
{
name: "create_role",
description: "Create a new role",
inputSchema: {
type: "object",
properties: {
display_name: {
type: "string",
description: "Display name for the role (required)",
},
description: { type: "string", description: "Role description" },
external_auth_id: {
type: "string",
description: "External authentication ID",
},
permissions: {
type: "array",
description: "Array of permission names to assign to the role",
items: { type: "string" },
},
},
required: ["display_name"],
},
},
{
name: "update_role",
description: "Update an existing role",
inputSchema: {
type: "object",
properties: {
id: { type: "number", description: "Role ID" },
display_name: {
type: "string",
description: "Display name for the role",
},
description: { type: "string", description: "Role description" },
external_auth_id: {
type: "string",
description: "External authentication ID",
},
permissions: {
type: "array",
description: "Array of permission names to assign to the role (replaces existing)",
items: { type: "string" },
},
},
required: ["id"],
},
},
{
name: "delete_role",
description: "Delete a role (users with this role will lose it)",
inputSchema: {
type: "object",
properties: {
id: { type: "number", description: "Role ID" },
migrate_ownership_id: {
type: "number",
description: "ID of role to transfer ownership of content to",
},
},
required: ["id"],
},
},
// ========== ATTACHMENTS & IMAGES ==========
{
name: "list_attachments",
description: "Get a listing of attachments",
inputSchema: {
type: "object",
properties: {
page: { type: "number", description: "Page number for pagination" },
count: { type: "number", description: "Number of items per page" },
sort: { type: "string", description: "Sort parameter" },
},
},
},
{
name: "get_attachment",
description: "Get details of a specific attachment",
inputSchema: {
type: "object",
properties: {
id: { type: "number", description: "Attachment ID" },
},
required: ["id"],
},
},
{
name: "delete_attachment",
description: "Delete an attachment",
inputSchema: {
type: "object",
properties: {
id: { type: "number", description: "Attachment ID" },
},
required: ["id"],
},
},
{
name: "list_images",
description: "Get a listing of images in the gallery",
inputSchema: {
type: "object",
properties: {
page: { type: "number", description: "Page number for pagination" },
count: { type: "number", description: "Number of items per page" },
sort: { type: "string", description: "Sort parameter" },
filter_type: {
type: "string",
enum: ["gallery", "drawio"],
description: "Filter images by type",
},
},
},
},
{
name: "get_image",
description: "Get details of a specific image",
inputSchema: {
type: "object",
properties: {
id: { type: "number", description: "Image ID" },
},
required: ["id"],
},
},
{
name: "update_image",
description: "Update image details",
inputSchema: {
type: "object",
properties: {
id: { type: "number", description: "Image ID" },
name: { type: "string", description: "Image name" },
},
required: ["id"],
},
},
{
name: "delete_image",
description: "Delete an image from the gallery",
inputSchema: {
type: "object",
properties: {
id: { type: "number", description: "Image ID" },
},
required: ["id"],
},
},
];
}
export async function handleSearchAndUserTool(name, args, client) {
try {
switch (name) {
// ========== SEARCH ==========
case "search_all": {
const { query, page, count } = args;
if (!query || typeof query !== "string") {
throw new Error("Search query is required and must be a string");
}
const params = {
query,
page: page ? parseInteger(page) : undefined,
count: count ? parseInteger(count) : undefined,
};
const result = await client.search(query, {
page: page ? parseInteger(page) : undefined,
count: count ? parseInteger(count) : undefined,
});
return formatApiResponse(result.data, result.total);
}
// ========== USERS ==========
case "list_users": {
const params = PaginationSchema.parse(args);
const result = await client.getUsers(params);
return formatApiResponse(result.data, result.total);
}
case "get_user": {
const id = parseInteger(args.id);
const result = await client.getUser(id);
return formatApiResponse(result);
}
case "create_user": {
const { name, email, password, roles, language, external_auth_id, send_invite, } = args;
if (!name || !email || !password) {
throw new Error("name, email, and password are required");
}
if (password.length < 8) {
throw new Error("Password must be at least 8 characters long");
}
const data = {
name,
email,
password,
roles: roles || [],
language,
external_auth_id,
send_invite,
};
const result = await client.createUser(data);
return formatApiResponse(result);
}
case "update_user": {
const { id, migrate_ownership_id, ...updateData } = args;
const userId = parseInteger(id);
const result = await client.updateUser(userId, updateData);
return formatApiResponse(result);
}
case "delete_user": {
const { id, migrate_ownership_id } = args;
const userId = parseInteger(id);
const migrateId = migrate_ownership_id
? parseInteger(migrate_ownership_id)
: undefined;
await client.deleteUser(userId, migrateId);
return `User ${userId} deleted successfully`;
}
// ========== ROLES ==========
case "list_roles": {
const params = PaginationSchema.parse(args);
const result = await client.getRoles(params);
return formatApiResponse(result.data, result.total);
}
case "get_role": {
const id = parseInteger(args.id);
const result = await client.getRole(id);
return formatApiResponse(result);
}
case "create_role": {
const { display_name, description, external_auth_id, permissions } = args;
if (!display_name) {
throw new Error("display_name is required");
}
const data = {
display_name,
description,
external_auth_id,
permissions: permissions || [],
};
const result = await client.createRole(data);
return formatApiResponse(result);
}
case "update_role": {
const { id, ...updateData } = args;
const roleId = parseInteger(id);
const result = await client.updateRole(roleId, updateData);
return formatApiResponse(result);
}
case "delete_role": {
const { id, migrate_ownership_id } = args;
const roleId = parseInteger(id);
const migrateId = migrate_ownership_id
? parseInteger(migrate_ownership_id)
: undefined;
await client.deleteRole(roleId);
return `Role ${roleId} deleted successfully`;
}
// ========== ATTACHMENTS ==========
case "list_attachments": {
const params = PaginationSchema.parse(args);
const result = await client.getAttachments(params);
return formatApiResponse(result.data, result.total);
}
case "get_attachment": {
const id = parseInteger(args.id);
const result = await client.getAttachment(id);
return formatApiResponse(result);
}
case "delete_attachment": {
const id = parseInteger(args.id);
await client.deleteAttachment(id);
return `Attachment ${id} deleted successfully`;
}
// ========== IMAGES ==========
case "list_images": {
const { filter_type, ...paginationArgs } = args;
const params = PaginationSchema.parse(paginationArgs);
const allParams = {
...params,
filter_type,
};
const result = await client.getImageGallery(params);
return formatApiResponse(result.data, result.total);
}
case "get_image": {
const id = parseInteger(args.id);
const result = await client.getImage(id);
return formatApiResponse(result);
}
case "update_image": {
const { id, name } = args;
const imageId = parseInteger(id);
const result = await client.updateImage(imageId, { name });
return formatApiResponse(result);
}
case "delete_image": {
const id = parseInteger(args.id);
await client.deleteImage(id);
return `Image ${id} deleted successfully`;
}
default:
throw new Error(`Unknown search/user tool: ${name}`);
}
}
catch (error) {
return handleApiError(error);
}
}