UNPKG

@digitalsamba/embedded-api-mcp-server

Version:

Digital Samba Embedded API MCP Server - Model Context Protocol server for Digital Samba's Embedded API

1,282 lines (1,281 loc) 66.3 kB
/** * Digital Samba MCP Server - Library Management Tools * * This module implements tools for managing content libraries within Digital Samba. * It provides MCP tools for creating, updating, and managing libraries, folders, and files. * * Tools provided: * - create-library: Create a new content library * - update-library: Update library details * - delete-library: Delete a library * - create-library-folder: Create a folder in a library * - update-library-folder: Update folder details * - delete-library-folder: Delete a folder * - create-library-file: Get upload URL for a new file * - update-library-file: Update file details * - delete-library-file: Delete a file * - get-file-links: Get viewing/thumbnail links for a file * * @module tools/library-management * @author Digital Samba Team * @version 1.0.0 */ // External dependencies // import { z } from "zod"; // Removed: unused // MCP SDK imports // import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; // TODO: Direct MCP server integration import { ErrorCode, McpError } from "@modelcontextprotocol/sdk/types.js"; import logger from "../../logger.js"; import { handleListLibraries, handleGetLibraryDetails, handleGetLibraryHierarchy, handleListLibraryFolders, handleGetLibraryFolderDetails, handleListLibraryFiles, handleGetLibraryFileDetails, } from "./reader-handlers.js"; /** * Register library management tools with the MCP SDK * * @returns {ToolDefinition[]} Array of tool definitions */ export function registerLibraryTools() { return [ // Library CRUD { name: "create-library", description: '[Content Library] Create a new content library for storing files and documents. Use when users say: "create library", "make content library", "new file storage", "create document library", "set up content repository". Requires externalId. Returns library ID for file uploads.', inputSchema: { type: "object", properties: { name: { type: "string", description: "Name of the library", }, externalId: { type: "string", description: "External identifier for the library", }, }, required: ["externalId"], }, }, { name: "update-library", description: '[Content Library] Update library name or external ID. Use when users say: "rename library", "update library", "change library name", "modify library details", "edit library settings". Requires libraryId. Can update name and external identifier.', inputSchema: { type: "object", properties: { libraryId: { type: "string", description: "The ID of the library to update", }, name: { type: "string", description: "Updated name of the library", }, externalId: { type: "string", description: "Updated external identifier", }, }, required: ["libraryId"], }, }, { name: "delete-library", description: '[Content Library] Permanently delete a content library and all its contents. Use when users say: "delete library", "remove content library", "delete file storage", "remove library". Requires libraryId. This deletes ALL files and folders within!', inputSchema: { type: "object", properties: { libraryId: { type: "string", description: "The ID of the library to delete", }, }, required: ["libraryId"], }, }, // Folder Management { name: "create-library-folder", description: '[Content Library] Create a folder for organizing files. Use when users say: "create folder", "make directory", "add folder", "create subfolder", "organize files in folders". Requires libraryId. Optional parentId for nested folders. Returns folder ID.', inputSchema: { type: "object", properties: { libraryId: { type: "string", description: "The ID of the library", }, name: { type: "string", description: "Name of the folder", }, parentId: { type: "string", description: "Parent folder ID (for nested folders)", }, }, required: ["libraryId"], }, }, { name: "update-library-folder", description: '[Content Library] Update folder name or move to different parent. Use when users say: "rename folder", "update folder", "change folder name", "move folder", "reorganize folders". Requires libraryId and folderId. Can change name or parent folder.', inputSchema: { type: "object", properties: { libraryId: { type: "string", description: "The ID of the library", }, folderId: { type: "string", description: "The ID of the folder to update", }, name: { type: "string", description: "Updated name of the folder", }, parentId: { type: "string", description: "Updated parent folder ID", }, }, required: ["libraryId", "folderId"], }, }, { name: "delete-library-folder", description: '[Content Library] Delete a folder and optionally its contents. Use when users say: "delete folder", "remove directory", "delete folder and files", "remove subfolder". Requires libraryId and folderId. May delete contained files depending on settings.', inputSchema: { type: "object", properties: { libraryId: { type: "string", description: "The ID of the library", }, folderId: { type: "string", description: "The ID of the folder to delete", }, }, required: ["libraryId", "folderId"], }, }, // File Management { name: "create-library-file", description: '[Content Library] Create file entry and get upload URL. Use when users say: "upload file", "add document", "upload to library", "add file", "store document". Requires libraryId and name. Returns upload URL for actual file transfer. Optional folderId.', inputSchema: { type: "object", properties: { libraryId: { type: "string", description: "The ID of the library", }, name: { type: "string", description: "Name of the file", }, folderId: { type: "string", description: "Folder ID to place the file in", }, }, required: ["libraryId", "name"], }, }, { name: "update-library-file", description: '[Content Library] Update file name or move to different folder. Use when users say: "rename file", "update file", "change file name", "move file to folder", "reorganize files". Requires libraryId and fileId. Can change name or folder location.', inputSchema: { type: "object", properties: { libraryId: { type: "string", description: "The ID of the library", }, fileId: { type: "string", description: "The ID of the file to update", }, name: { type: "string", description: "Updated name of the file", }, folderId: { type: "string", description: "Updated folder ID", }, }, required: ["libraryId", "fileId"], }, }, { name: "delete-library-file", description: '[Content Library] Permanently delete a file from library. Use when users say: "delete file", "remove document", "delete upload", "remove file from library". Requires libraryId and fileId. This action cannot be undone.', inputSchema: { type: "object", properties: { libraryId: { type: "string", description: "The ID of the library", }, fileId: { type: "string", description: "The ID of the file to delete", }, }, required: ["libraryId", "fileId"], }, }, { name: "get-file-links", description: '[Content Library] Get viewing and thumbnail URLs for a file. Use when users say: "get file link", "share file", "view file", "get download link", "access file URL". Requires libraryId and fileId. Returns URLs for viewing and thumbnails.', inputSchema: { type: "object", properties: { libraryId: { type: "string", description: "The ID of the library", }, fileId: { type: "string", description: "The ID of the file", }, }, required: ["libraryId", "fileId"], }, }, // Webapp and Whiteboard Creation { name: "create-webapp", description: '[Content Library] Create a webapp/web application entry in library. Use when users say: "create webapp", "add web app", "create web application", "add webapp to library". Requires libraryId and name. Optional folderId. For embedding web content.', inputSchema: { type: "object", properties: { libraryId: { type: "string", description: "The ID of the library", }, name: { type: "string", description: "Name of the webapp", }, folderId: { type: "string", description: "Folder ID to place the webapp in", }, }, required: ["libraryId", "name"], }, }, { name: "create-whiteboard", description: '[Content Library] Create a collaborative whiteboard in library. Use when users say: "create whiteboard", "add whiteboard", "create drawing board", "make collaborative board". Requires libraryId and name. Optional folderId. For visual collaboration.', inputSchema: { type: "object", properties: { libraryId: { type: "string", description: "The ID of the library", }, name: { type: "string", description: "Name of the whiteboard", }, folderId: { type: "string", description: "Folder ID to place the whiteboard in", }, }, required: ["libraryId", "name"], }, }, // Bulk and Move Operations { name: "move-library-file", description: '[Content Library] Move a file to a different folder. Use when users say: "move file", "relocate file", "move to folder", "reorganize file", "change file location". Requires libraryId and fileId. Moves file within same library only.', inputSchema: { type: "object", properties: { libraryId: { type: "string", description: "The ID of the library", }, fileId: { type: "string", description: "The ID of the file to move", }, targetFolderId: { type: "string", description: "The ID of the target folder (null for root)", }, }, required: ["libraryId", "fileId"], }, }, { name: "move-library-folder", description: '[Content Library] Move a folder to a different parent location. Use when users say: "move folder", "relocate directory", "reorganize folders", "change folder parent", "nest folder". Requires libraryId and folderId. Moves entire folder tree.', inputSchema: { type: "object", properties: { libraryId: { type: "string", description: "The ID of the library", }, folderId: { type: "string", description: "The ID of the folder to move", }, targetParentId: { type: "string", description: "The ID of the target parent folder (null for root)", }, }, required: ["libraryId", "folderId"], }, }, { name: "bulk-delete-library-files", description: '[Content Library] Delete multiple files in one operation. Use when users say: "delete multiple files", "bulk delete", "remove several files", "mass delete files", "delete file batch". Requires libraryId and fileIds array. Efficient for cleanup tasks.', inputSchema: { type: "object", properties: { libraryId: { type: "string", description: "The ID of the library", }, fileIds: { type: "array", description: "Array of file IDs to delete", items: { type: "string", }, }, }, required: ["libraryId", "fileIds"], }, }, { name: "bulk-upload-library-files", description: '[Content Library] Get upload URLs for multiple files in batch. Use when users say: "upload multiple files", "bulk upload", "batch upload", "upload many files", "mass file upload". Requires libraryId and files array with names, sizes, and MIME types.', inputSchema: { type: "object", properties: { libraryId: { type: "string", description: "The ID of the library", }, files: { type: "array", description: "Array of file information", items: { type: "object", properties: { name: { type: "string", description: "File name", }, size: { type: "number", description: "File size in bytes", }, mimeType: { type: "string", description: "MIME type of the file", }, folderId: { type: "string", description: "Target folder ID", }, }, required: ["name", "size", "mimeType"], }, }, }, required: ["libraryId", "files"], }, }, { name: "copy-library-content", description: '[Content Library] Copy files or folders within/between libraries. Use when users say: "copy file", "duplicate folder", "copy to another library", "clone content", "duplicate files". Requires source/target library IDs, content type and ID. Can rename during copy.', inputSchema: { type: "object", properties: { sourceLibraryId: { type: "string", description: "The ID of the source library", }, targetLibraryId: { type: "string", description: "The ID of the target library (same as source if copying within library)", }, contentType: { type: "string", enum: ["file", "folder"], description: "Type of content to copy", }, contentId: { type: "string", description: "The ID of the file or folder to copy", }, targetFolderId: { type: "string", description: "The ID of the target folder in the destination library", }, newName: { type: "string", description: "Optional new name for the copied content", }, }, required: [ "sourceLibraryId", "targetLibraryId", "contentType", "contentId", ], }, }, // Reader tools for content resources (hybrid approach for Claude Desktop compatibility) { name: "list-libraries", description: '[Content Library - TOOL] List all content libraries in your account. Use when users say: "show libraries", "list libraries", "get all libraries", "view content libraries", "show file storage", "find library", "search for library". This TOOL provides the same data as digitalsamba://libraries resource. Returns array of library objects with names, IDs, and file counts. Can search by name.', inputSchema: { type: "object", properties: { limit: { type: "number", description: "Maximum number of libraries to return (default: 100)", }, offset: { type: "number", description: "Number of libraries to skip for pagination", }, searchName: { type: "string", description: "Search for libraries by name or external ID (searches all libraries)", }, }, }, }, { name: "search-libraries", description: '[Content Library - TOOL] Search for libraries by name. Use when users say: "find library named", "search for library", "locate library", "where is library", "find Conal\'s library". Searches through ALL libraries in the account to find matches by name or external ID.', inputSchema: { type: "object", properties: { searchTerm: { type: "string", description: "Name or external ID to search for (required)", }, }, required: ["searchTerm"], }, }, { name: "verify-library-id", description: '[Content Library - TOOL] Verify if a library ID exists by attempting to retrieve it. Use when users say: "check if library ID exists", "verify library ID", "test library ID", "validate library ID". Useful for checking if a specific library ID is valid.', inputSchema: { type: "object", properties: { libraryId: { type: "string", description: "Library ID to verify (required)", }, }, required: ["libraryId"], }, }, { name: "get-library-details", description: '[Content Library - TOOL] Get detailed information about a specific library. Use when users say: "show library details", "get library info", "library information", "describe library", "library metadata". Requires libraryId. This TOOL provides the same data as digitalsamba://libraries/{id} resource. Returns complete library information and statistics.', inputSchema: { type: "object", properties: { libraryId: { type: "string", description: "Library ID (required)", }, }, required: ["libraryId"], }, }, { name: "get-library-hierarchy", description: '[Content Library - TOOL] Get complete folder and file hierarchy of a library. Use when users say: "show library structure", "get folder tree", "library hierarchy", "folder organization", "library tree view". Requires libraryId. This TOOL provides the same data as digitalsamba://libraries/{id}/hierarchy resource. Returns nested structure showing all folders and their relationships.', inputSchema: { type: "object", properties: { libraryId: { type: "string", description: "Library ID (required)", }, }, required: ["libraryId"], }, }, { name: "list-library-folders", description: '[Content Library - TOOL] List all folders in a library. Use when users say: "list folders", "show library folders", "get folders", "view directories", "folder list". Requires libraryId. This TOOL provides the same data as digitalsamba://libraries/{id}/folders resource. Returns flat list of all folders with names, IDs, and parent relationships.', inputSchema: { type: "object", properties: { libraryId: { type: "string", description: "Library ID (required)", }, limit: { type: "number", description: "Maximum number of folders to return", }, offset: { type: "number", description: "Number of folders to skip for pagination", }, }, required: ["libraryId"], }, }, { name: "get-library-folder-details", description: '[Content Library - TOOL] Get details of a specific folder in a library. Use when users say: "show folder details", "get folder info", "folder contents", "folder information", "describe folder". Requires libraryId and folderId. This TOOL provides the same data as digitalsamba://libraries/{id}/folders/{folderId} resource. Returns folder information and contained files.', inputSchema: { type: "object", properties: { libraryId: { type: "string", description: "Library ID (required)", }, folderId: { type: "string", description: "Folder ID (required)", }, }, required: ["libraryId", "folderId"], }, }, { name: "list-library-files", description: '[Content Library - TOOL] List all files in a library. Use when users say: "list files", "show library files", "get files", "view documents", "file list". Requires libraryId. This TOOL provides the same data as digitalsamba://libraries/{id}/files resource. Returns array of file objects with names, sizes, types, and folder locations.', inputSchema: { type: "object", properties: { libraryId: { type: "string", description: "Library ID (required)", }, limit: { type: "number", description: "Maximum number of files to return", }, offset: { type: "number", description: "Number of files to skip for pagination", }, }, required: ["libraryId"], }, }, { name: "get-library-file-details", description: '[Content Library - TOOL] Get detailed information about a specific file. Use when users say: "show file details", "get file info", "file information", "describe file", "file metadata". Requires libraryId and fileId. This TOOL provides the same data as digitalsamba://libraries/{id}/files/{fileId} resource. Returns complete file information including size, type, upload date, and access URLs.', inputSchema: { type: "object", properties: { libraryId: { type: "string", description: "Library ID (required)", }, fileId: { type: "string", description: "File ID (required)", }, }, required: ["libraryId", "fileId"], }, }, ]; } /** * Execute a library management tool * * @param {string} toolName - Name of the tool to execute * @param {any} params - Tool parameters * @param {DigitalSambaApiClient} apiClient - API client instance * @returns {Promise<any>} Tool execution result */ export async function executeLibraryTool(toolName, params, apiClient) { switch (toolName) { // Library CRUD case "create-library": return handleCreateLibrary(params, apiClient); case "update-library": return handleUpdateLibrary(params, apiClient); case "delete-library": return handleDeleteLibrary(params, apiClient); // Folder Management case "create-library-folder": return handleCreateLibraryFolder(params, apiClient); case "update-library-folder": return handleUpdateLibraryFolder(params, apiClient); case "delete-library-folder": return handleDeleteLibraryFolder(params, apiClient); // File Management case "create-library-file": return handleCreateLibraryFile(params, apiClient); case "update-library-file": return handleUpdateLibraryFile(params, apiClient); case "delete-library-file": return handleDeleteLibraryFile(params, apiClient); case "get-file-links": return handleGetFileLinks(params, apiClient); // Webapp and Whiteboard Creation case "create-webapp": return handleCreateWebapp(params, apiClient); case "create-whiteboard": return handleCreateWhiteboard(params, apiClient); // Bulk and Move Operations case "move-library-file": return handleMoveLibraryFile(params, apiClient); case "move-library-folder": return handleMoveLibraryFolder(params, apiClient); case "bulk-delete-library-files": return handleBulkDeleteLibraryFiles(params, apiClient); case "bulk-upload-library-files": return handleBulkUploadLibraryFiles(params, apiClient); case "copy-library-content": return handleCopyLibraryContent(params, apiClient); // Reader tools for content resources (hybrid approach) case "list-libraries": return handleListLibraries(params, apiClient); case "search-libraries": return handleListLibraries({ searchName: params.searchTerm }, apiClient); case "verify-library-id": return handleGetLibraryDetails(params, apiClient); case "get-library-details": return handleGetLibraryDetails(params, apiClient); case "get-library-hierarchy": return handleGetLibraryHierarchy(params, apiClient); case "list-library-folders": return handleListLibraryFolders(params, apiClient); case "get-library-folder-details": return handleGetLibraryFolderDetails(params, apiClient); case "list-library-files": return handleListLibraryFiles(params, apiClient); case "get-library-file-details": return handleGetLibraryFileDetails(params, apiClient); default: throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${toolName}`); } } /** * Handle create library */ async function handleCreateLibrary(params, apiClient) { const { name, externalId } = params; if (!externalId || externalId.trim() === "") { return { content: [ { type: "text", text: "External ID is required to create a library.", }, ], isError: true, }; } logger.info("Creating library", { name, externalId }); try { const result = await apiClient.createLibrary({ name, external_id: externalId, }); return { content: [ { type: "text", text: `Successfully created library "${result.name || externalId}" with ID: ${result.id}`, }, ], }; } catch (error) { logger.error("Error creating library", { error: error instanceof Error ? error.message : String(error), }); return { content: [ { type: "text", text: `Error creating library: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } } /** * Handle update library */ async function handleUpdateLibrary(params, apiClient) { const { libraryId, name, externalId } = params; if (!libraryId || libraryId.trim() === "") { return { content: [ { type: "text", text: "Library ID is required to update a library.", }, ], isError: true, }; } const updates = {}; if (name !== undefined) updates.name = name; if (externalId !== undefined) updates.external_id = externalId; if (Object.keys(updates).length === 0) { return { content: [ { type: "text", text: "No updates provided for the library.", }, ], isError: true, }; } logger.info("Updating library", { libraryId, updates }); try { const result = await apiClient.updateLibrary(libraryId, updates); void result; // Result is used for side effects, response details not needed return { content: [ { type: "text", text: `Successfully updated library ${libraryId}. Updated fields: ${Object.keys(updates).join(", ")}`, }, ], }; } catch (error) { logger.error("Error updating library", { libraryId, error: error instanceof Error ? error.message : String(error), }); const errorMessage = error instanceof Error ? error.message : String(error); let displayMessage = `Error updating library: ${errorMessage}`; if (errorMessage.includes("404") || errorMessage.includes("not found")) { displayMessage = `Library with ID ${libraryId} not found`; } return { content: [ { type: "text", text: displayMessage, }, ], isError: true, }; } } /** * Handle delete library */ async function handleDeleteLibrary(params, apiClient) { const { libraryId } = params; if (!libraryId || libraryId.trim() === "") { return { content: [ { type: "text", text: "Library ID is required to delete a library.", }, ], isError: true, }; } logger.info("Deleting library", { libraryId }); try { await apiClient.deleteLibrary(libraryId); return { content: [ { type: "text", text: `Successfully deleted library ${libraryId}`, }, ], }; } catch (error) { logger.error("Error deleting library", { libraryId, error: error instanceof Error ? error.message : String(error), }); const errorMessage = error instanceof Error ? error.message : String(error); let displayMessage = `Error deleting library: ${errorMessage}`; if (errorMessage.includes("404") || errorMessage.includes("not found")) { displayMessage = `Library with ID ${libraryId} not found`; } return { content: [ { type: "text", text: displayMessage, }, ], isError: true, }; } } /** * Handle create library folder */ async function handleCreateLibraryFolder(params, apiClient) { const { libraryId, name, parentId } = params; if (!libraryId || libraryId.trim() === "") { return { content: [ { type: "text", text: "Library ID is required to create a folder.", }, ], isError: true, }; } logger.info("Creating library folder", { libraryId, name, parentId }); try { const folderData = {}; if (name !== undefined) folderData.name = name; if (parentId !== undefined) folderData.parent_id = parentId; const result = await apiClient.createLibraryFolder(libraryId, folderData); return { content: [ { type: "text", text: `Successfully created folder in library ${libraryId}. Folder ID: ${result.id}`, }, ], }; } catch (error) { logger.error("Error creating library folder", { libraryId, error: error instanceof Error ? error.message : String(error), }); const errorMessage = error instanceof Error ? error.message : String(error); let displayMessage = `Error creating folder: ${errorMessage}`; if (errorMessage.includes("404") || errorMessage.includes("Library not found")) { displayMessage = `Library with ID ${libraryId} not found`; } return { content: [ { type: "text", text: displayMessage, }, ], isError: true, }; } } /** * Handle update library folder */ async function handleUpdateLibraryFolder(params, apiClient) { const { libraryId, folderId, name, parentId } = params; if (!libraryId || libraryId.trim() === "") { return { content: [ { type: "text", text: "Library ID is required to update a folder.", }, ], isError: true, }; } if (!folderId || folderId.trim() === "") { return { content: [ { type: "text", text: "Folder ID is required to update a folder.", }, ], isError: true, }; } const updates = {}; if (name !== undefined) updates.name = name; if (parentId !== undefined) updates.parent_id = parentId; if (Object.keys(updates).length === 0) { return { content: [ { type: "text", text: "No updates provided for the folder.", }, ], isError: true, }; } logger.info("Updating library folder", { libraryId, folderId, updates }); try { await apiClient.updateLibraryFolder(libraryId, folderId, updates); return { content: [ { type: "text", text: `Successfully updated folder ${folderId}. Updated fields: ${Object.keys(updates).join(", ")}`, }, ], }; } catch (error) { logger.error("Error updating library folder", { libraryId, folderId, error: error instanceof Error ? error.message : String(error), }); const errorMessage = error instanceof Error ? error.message : String(error); let displayMessage = `Error updating folder: ${errorMessage}`; if (errorMessage.includes("404") || errorMessage.includes("not found")) { displayMessage = `Folder with ID ${folderId} not found in library ${libraryId}`; } return { content: [ { type: "text", text: displayMessage, }, ], isError: true, }; } } /** * Handle delete library folder */ async function handleDeleteLibraryFolder(params, apiClient) { const { libraryId, folderId } = params; if (!libraryId || libraryId.trim() === "") { return { content: [ { type: "text", text: "Library ID is required to delete a folder.", }, ], isError: true, }; } if (!folderId || folderId.trim() === "") { return { content: [ { type: "text", text: "Folder ID is required to delete a folder.", }, ], isError: true, }; } logger.info("Deleting library folder", { libraryId, folderId }); try { await apiClient.deleteLibraryFolder(libraryId, folderId); return { content: [ { type: "text", text: `Successfully deleted folder ${folderId} from library ${libraryId}`, }, ], }; } catch (error) { logger.error("Error deleting library folder", { libraryId, folderId, error: error instanceof Error ? error.message : String(error), }); const errorMessage = error instanceof Error ? error.message : String(error); let displayMessage = `Error deleting folder: ${errorMessage}`; if (errorMessage.includes("404") || errorMessage.includes("not found")) { displayMessage = `Folder with ID ${folderId} not found in library ${libraryId}`; } return { content: [ { type: "text", text: displayMessage, }, ], isError: true, }; } } /** * Handle create library file */ async function handleCreateLibraryFile(params, apiClient) { const { libraryId, name, folderId } = params; if (!libraryId || libraryId.trim() === "") { return { content: [ { type: "text", text: "Library ID is required to create a file.", }, ], isError: true, }; } if (!name || name.trim() === "") { return { content: [ { type: "text", text: "File name is required.", }, ], isError: true, }; } logger.info("Creating library file", { libraryId, name, folderId }); try { const fileData = { name }; if (folderId !== undefined) fileData.folder_id = folderId; const result = await apiClient.createLibraryFile(libraryId, fileData); return { content: [ { type: "text", text: `Successfully created file "${name}" in library ${libraryId}.\n` + `File ID: ${result.file_id}\n` + `Upload URL: ${result.external_storage_url}\n` + `Upload Token: ${result.token}\n` + `Token expires at: ${new Date(result.expiration_timestamp * 1000).toISOString()}`, }, ], }; } catch (error) { logger.error("Error creating library file", { libraryId, name, error: error instanceof Error ? error.message : String(error), }); const errorMessage = error instanceof Error ? error.message : String(error); let displayMessage = `Error creating file: ${errorMessage}`; if (errorMessage.includes("404") || errorMessage.includes("Library not found")) { displayMessage = `Library with ID ${libraryId} not found`; } return { content: [ { type: "text", text: displayMessage, }, ], isError: true, }; } } /** * Handle update library file */ async function handleUpdateLibraryFile(params, apiClient) { const { libraryId, fileId, name, folderId } = params; if (!libraryId || libraryId.trim() === "") { return { content: [ { type: "text", text: "Library ID is required to update a file.", }, ], isError: true, }; } if (!fileId || fileId.trim() === "") { return { content: [ { type: "text", text: "File ID is required to update a file.", }, ], isError: true, }; } const updates = {}; if (name !== undefined) updates.name = name; if (folderId !== undefined) updates.folder_id = folderId; if (Object.keys(updates).length === 0) { return { content: [ { type: "text", text: "No updates provided for the file.", }, ], isError: true, }; } logger.info("Updating library file", { libraryId, fileId, updates }); try { await apiClient.updateLibraryFile(libraryId, fileId, updates); return { content: [ { type: "text", text: `Successfully updated file ${fileId}. Updated fields: ${Object.keys(updates).join(", ")}`, }, ], }; } catch (error) { logger.error("Error updating library file", { libraryId, fileId, error: error instanceof Error ? error.message : String(error), }); const errorMessage = error instanceof Error ? error.message : String(error); let displayMessage = `Error updating file: ${errorMessage}`; if (errorMessage.includes("404") || errorMessage.includes("not found")) { displayMessage = `File with ID ${fileId} not found in library ${libraryId}`; } return { content: [ { type: "text", text: displayMessage, }, ], isError: true, }; } } /** * Handle delete library file */ async function handleDeleteLibraryFile(params, apiClient) { const { libraryId, fileId } = params; if (!libraryId || libraryId.trim() === "") { return { content: [ { type: "text", text: "Library ID is required to delete a file.", }, ], isError: true, }; } if (!fileId || fileId.trim() === "") { return { content: [ { type: "text", text: "File ID is required to delete a file.", }, ], isError: true, }; } logger.info("Deleting library file", { libraryId, fileId }); try { await apiClient.deleteLibraryFile(libraryId, fileId); return { content: [ { type: "text", text: `Successfully deleted file ${fileId} from library ${libraryId}`, }, ], }; } catch (error) { logger.error("Error deleting library file", { libraryId, fileId, error: error instanceof Error ? error.message : String(error), }); const errorMessage = error instanceof Error ? error.message : String(error); let displayMessage = `Error deleting file: ${errorMessage}`; if (errorMessage.includes("404") || errorMessage.includes("not found")) { displayMessage = `File with ID ${fileId} not found in library ${libraryId}`; } return { content: [ { type: "text", text: displayMessage, }, ], isError: true, }; } } /** * Handle get file links */ async function handleGetFileLinks(params, apiClient) { const { libraryId, fileId } = params; if (!libraryId || libraryId.trim() === "") { return { content: [ { type: "text", text: "Library ID is required to get file links.", }, ], isError: true, }; } if (!fileId || fileId.trim() === "") { return { content: [ { type: "text", text: "File ID is required to get file links.", }, ], isError: true, }; } logger.info("Getting file links", { libraryId, fileId }); try { const result = await apiClient.getFileLinks(libraryId, fileId); let message = `File links for ${fileId}:\n`; if (result.pages && result.pages.length > 0) { result.pages.forEach((page, index) => { message += `\nPage ${index + 1}:\n`; message += ` View URL: ${page.url}\n`; message += ` Thumbnail URL: ${page.thumbnail_url}`; }); } else { message += "No pages available for this file."; } return { content: [ { type: "text", text: message, }, ], }; } catch (error) { logger.error("Error getting file links", { libraryId, fileId,