list_files_on_integration
List files from cloud or storage gateway integrations with pagination support. Use a cursor to retrieve subsequent pages when more results are available.
Instructions
List files on any integration (cloud or MASV Storage Gateway). Supports pagination — if more results are available, a cursor is returned; pass it back in the next call to get the next page.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| integrationId | Yes | ID of the cloud or storage gateway integration | |
| path | No | Directory path/prefix to list files from | |
| cursor | No | Pagination cursor returned from a previous call. Pass this to retrieve the next page of results. |
Implementation Reference
- src/api/integrations.ts:237-346 (handler)The main handler function that lists files on a cloud or Storage Gateway integration. It fetches integration details, determines if it's a Storage Gateway (offset-based pagination) or cloud integration (last_file_path cursor pagination), fetches files via MASV API, and formats the response with file names, sizes, and pagination cursors.
async function listFilesOnIntegration({ integrationId, path, cursor, }: ListFilesOnIntegrationParams) { const integration = await getIntegration(integrationId); const STORAGE_GATEWAY_PROVIDERS = new Set([ "storage_gateway", "jellyfish_sg", "synology_sg", "qnap_sg", "amazon_efs_sg", "opendrives_sg", "desktop_sg", ]); const isStorageGateway = STORAGE_GATEWAY_PROVIDERS.has(integration.provider); const headers = { "content-type": "application/json", "x-api-key": MASV_API_KEY, }; let files: any[]; let hasMore = false; let nextCursor: string | undefined; if (isStorageGateway) { // Storage gateway uses offset-based pagination let offset = 0; if (cursor) { const decoded = decodeCursor(cursor); if (decoded.type !== "storage_gateway") throw new Error("Invalid cursor for this integration type"); offset = decoded.offset; } const url = new URL( `${MASV_BASE_URL}/v1/cloud_connections/providers/storage_gateway/${integrationId}/files`, ); if (path) url.searchParams.append("path", path); url.searchParams.append("offset", String(offset)); // Request 1 more file to check if there are more files than we return url.searchParams.append("count", String(PAGE_SIZE + 1)); const r = await fetch(url.toString(), { headers }); const data = await r.json(); const allFiles = data.files || []; hasMore = allFiles.length > PAGE_SIZE || data.more_data === true; files = allFiles.slice(0, PAGE_SIZE); if (hasMore) { nextCursor = encodeCursor({ type: "storage_gateway", offset: offset + PAGE_SIZE }); } } else { // Cloud integrations use last_file_path cursor pagination let last_file_path: string | undefined; if (cursor) { const decoded = decodeCursor(cursor); if (decoded.type !== "cloud") throw new Error("Invalid cursor for this integration type"); last_file_path = decoded.last_file_path; } const url = new URL( `${MASV_BASE_URL}/v1/cloud_connections/${integrationId}/files`, ); if (path) url.searchParams.append("prefix", path); if (last_file_path) url.searchParams.append("prev_key", last_file_path); // Request 1 more file to check if there are more files than we return url.searchParams.append("count", String(PAGE_SIZE + 1)); const r = await fetch(url.toString(), { headers }); const data = await r.json(); const allFiles = Array.isArray(data) ? data : []; hasMore = allFiles.length > PAGE_SIZE; files = allFiles.slice(0, PAGE_SIZE); if (hasMore) { const lastFile = files[files.length - 1]; const lastPath = lastFile?.id || ""; nextCursor = encodeCursor({ type: "cloud", last_file_path: lastPath }); } } // Format response const formattedLines: string[] = []; for (const file of files) { const isDirectory = file.kind === "directory"; const name = file.name || ""; const size = file.size; const id = file.id || ""; if (isDirectory) { formattedLines.push(`[DIR] ${name}/${id ? ` (ID: ${id})` : ""}`); } else { const sizeStr = size !== undefined && size !== null ? formatFileSize(size) : "unknown"; formattedLines.push(`[FILE] ${name} (${sizeStr})${id ? ` (ID: ${id})` : ""}`); } } if (hasMore && nextCursor) { formattedLines.push( `\nMore results available. Call this tool again with cursor: ${nextCursor}`, ); } return formattedLines.join("\n"); } - src/api/integrations.ts:229-233 (schema)Zod schema for the list_files_on_integration tool: defines integrationId (required), path (optional directory prefix), and cursor (optional pagination cursor).
const ListFilesOnIntegrationSchema = z.object({ integrationId: z.string().describe("ID of the cloud or storage gateway integration"), path: z.string().optional().describe("Directory path/prefix to list files from"), cursor: z.string().optional().describe("Pagination cursor returned from a previous call. Pass this to retrieve the next page of results."), }); - src/index.ts:280-296 (registration)Registers the 'list_files_on_integration' tool with the MCP server, linking the schema (ListFilesOnIntegrationSchema.shape) and the handler (listFilesOnIntegration function).
server.registerTool( "list_files_on_integration", { description: "List files on any integration (cloud or MASV Storage Gateway). Supports pagination — if more results are available, a cursor is returned; pass it back in the next call to get the next page.", inputSchema: ListFilesOnIntegrationSchema.shape, }, async (args) => { try { const data = await listFilesOnIntegration(args); return mcpOk(data); } catch (error) { return mcpError(error); } }, ); - src/api/integrations.ts:351-367 (helper)Helper function formatFileSize converts bytes to human-readable sizes (B, KiB, MiB, GiB, TiB). Used by listFilesOnIntegration to display file sizes in the formatted output.
function formatFileSize(bytes: number): string { if (bytes === 0) return "0 B"; const units = ["B", "KiB", "MiB", "GiB", "TiB"]; const bytesPerUnit = 1024; const maxUnitIndex = units.length - 1; let unitIndex = 0; let size = bytes; while (size >= bytesPerUnit && unitIndex !== maxUnitIndex) { unitIndex++; size = size / bytesPerUnit; } return `${size.toFixed(2)} ${units[unitIndex]}`; } - src/api/integrations.ts:214-226 (helper)Helper types and functions for cursor-based pagination: CloudCursor (type + last_file_path), StorageGatewayCursor (type + offset), encodeCursor, decodeCursor, and PAGE_SIZE constant (50).
// Cursor encodes pagination state for both integration types type CloudCursor = { type: "cloud"; last_file_path: string }; type StorageGatewayCursor = { type: "storage_gateway"; offset: number }; type PaginationCursor = CloudCursor | StorageGatewayCursor; function encodeCursor(cursor: PaginationCursor): string { return Buffer.from(JSON.stringify(cursor)).toString("base64"); } function decodeCursor(cursor: string): PaginationCursor { return JSON.parse(Buffer.from(cursor, "base64").toString("utf8")); }