download_file
Download file content from an Anaplan model. Text files are returned inline; set saveToDownloads=true for binary files to preserve exact bytes.
Instructions
Download file content from a model. Text files are returned inline; for binary files, set saveToDownloads=true to preserve the exact bytes.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| workspaceId | Yes | Anaplan workspace ID or name | |
| modelId | Yes | Anaplan model ID or name | |
| fileId | Yes | Anaplan file ID or name (from show_files) | |
| saveToDownloads | No | If true, save the file to ~/Downloads without decoding | |
| fileName | No | Optional local file name when saveToDownloads is true |
Implementation Reference
- src/tools/bulk.ts:210-216 (registration)Registration of the 'download_file' tool via server.tool() with its schema and handler callback.
server.tool("download_file", "Download file content from a model. Text files are returned inline; for binary files, set saveToDownloads=true to preserve the exact bytes.", { workspaceId: z.string().describe("Anaplan workspace ID or name"), modelId: z.string().describe("Anaplan model ID or name"), fileId: z.string().describe("Anaplan file ID or name (from show_files)"), saveToDownloads: z.boolean().optional().describe("If true, save the file to ~/Downloads without decoding"), fileName: z.string().optional().describe("Optional local file name when saveToDownloads is true"), }, async ({ workspaceId, modelId, fileId, saveToDownloads, fileName }) => { - src/tools/bulk.ts:210-215 (schema)Input schema for download_file: workspaceId, modelId, fileId (required), saveToDownloads and fileName (optional).
server.tool("download_file", "Download file content from a model. Text files are returned inline; for binary files, set saveToDownloads=true to preserve the exact bytes.", { workspaceId: z.string().describe("Anaplan workspace ID or name"), modelId: z.string().describe("Anaplan model ID or name"), fileId: z.string().describe("Anaplan file ID or name (from show_files)"), saveToDownloads: z.boolean().optional().describe("If true, save the file to ~/Downloads without decoding"), fileName: z.string().optional().describe("Optional local file name when saveToDownloads is true"), - src/tools/bulk.ts:216-257 (handler)Handler implementation: resolves IDs, calls apis.files.download(), attempts UTF-8 decode; if saveToDownloads, writes buffer to ~/Downloads; otherwise returns inline text or instructs to use saveToDownloads for binary.
}, async ({ workspaceId, modelId, fileId, saveToDownloads, fileName }) => { const wId = await resolver.resolveWorkspace(workspaceId); const mId = await resolver.resolveModel(wId, modelId); const fId = await resolver.resolveFile(wId, mId, fileId); const content = await apis.files.download(wId, mId, fId); const text = tryDecodeUtf8(content); if (saveToDownloads) { const requestedName = fileName?.trim(); const defaultName = sanitizeFileName(fileId) || `file-${fId}`; const resolvedName = sanitizeFileName(requestedName && requestedName.length > 0 ? requestedName : defaultName) || defaultName; const outputPath = join(homedir(), "Downloads", resolvedName); await writeFile(outputPath, content); return { content: [{ type: "text", text: text === null ? `File saved to ${outputPath} (${content.length} bytes). Inline preview is unavailable for binary content.` : `File saved to ${outputPath}\n\nPreview:\n${buildTextPreview(text)}`, }], }; } if (text === null) { return { content: [{ type: "text", text: "This file contains binary data and cannot be returned inline safely. Set `saveToDownloads` to `true` to save it locally.", }], }; } if (text.length > MAX_INLINE_TEXT_CHARS) { return { content: [{ type: "text", text: buildTextPreview(text), }], }; } return { content: [{ type: "text", text }] }; }); - src/tools/bulk.ts:75-82 (helper)Helper function tryDecodeUtf8: checks for null bytes and attempts UTF-8 decoding to determine if content is text or binary.
function tryDecodeUtf8(buffer: Buffer): string | null { if (buffer.includes(0)) return null; try { return new TextDecoder("utf-8", { fatal: true }).decode(buffer); } catch { return null; } } - src/tools/bulk.ts:39-44 (helper)Helper function sanitizeFileName: strips invalid filesystem characters from file names.
function sanitizeFileName(value: string): string { return value .replace(/[<>:"/\\|?*\x00-\x1f]/g, "_") .replace(/\s+/g, " ") .trim(); } - src/tools/bulk.ts:84-88 (helper)Helper function buildTextPreview: truncates text to MAX_INLINE_TEXT_CHARS (50000) for inline display.
function buildTextPreview(text: string): string { return text.length > MAX_INLINE_TEXT_CHARS ? text.slice(0, MAX_INLINE_TEXT_CHARS) + `\n\n[Truncated - showing first ${MAX_INLINE_TEXT_CHARS} of ${text.length} characters]` : text; } - src/api/files.ts:26-39 (helper)FilesApi.download(): fetches chunk list then concatenates raw bytes from each chunk into a Buffer.
async download(workspaceId: string, modelId: string, fileId: string): Promise<Buffer> { const res = await this.client.get<any>( `/workspaces/${workspaceId}/models/${modelId}/files/${fileId}/chunks` ); const chunks: Array<{ id: string; name: string }> = res.chunks ?? []; const parts: Buffer[] = []; for (const chunk of chunks) { const data = await this.client.getRawBytes( `/workspaces/${workspaceId}/models/${modelId}/files/${fileId}/chunks/${chunk.id}` ); parts.push(data); } return Buffer.concat(parts); }