Upload Blob
upload_blobUpload files or blobs to AFFiNE workspace storage by providing workspace ID and base64-encoded content.
Instructions
Upload a file or blob to workspace storage.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| workspaceId | Yes | Workspace ID | |
| content | Yes | Base64 encoded content or text | |
| filename | No | Filename | |
| contentType | No | MIME type |
Implementation Reference
- src/tools/blobStorage.ts:26-78 (handler)The uploadBlobHandler function that executes the upload_blob tool logic. It accepts workspaceId, content (base64 or text), filename, and contentType, then performs a multipart/form-data GraphQL mutation upload (setBlob) to the endpoint.
const uploadBlobHandler = async ({ workspaceId, content, filename, contentType }: { workspaceId: string; content: string; filename?: string; contentType?: string }) => { try { const endpoint = gql.endpoint; const headers = gql.headers; const cookie = gql.cookie; const payload = decodeBlobContent(content); const safeFilename = filename || `blob-${Date.now()}.bin`; const mime = contentType || "application/octet-stream"; const form = new FormData(); form.append("operations", JSON.stringify({ query: `mutation SetBlob($workspaceId: String!, $blob: Upload!) { setBlob(workspaceId: $workspaceId, blob: $blob) }`, variables: { workspaceId, blob: null } })); form.append("map", JSON.stringify({ "0": ["variables.blob"] })); form.append("0", payload, { filename: safeFilename, contentType: mime }); const response = await fetch(endpoint, { method: "POST", headers: { ...headers, Cookie: cookie, ...form.getHeaders(), }, body: form as any, }); const result = await response.json() as any; if (result.errors?.length) { throw new Error(result.errors[0].message); } const blobKey = result.data?.setBlob; if (!blobKey) { throw new Error("Upload succeeded but no blob key was returned."); } return text({ id: blobKey, key: blobKey, workspaceId, filename: safeFilename, contentType: mime, size: payload.length, uploadedAt: new Date().toISOString() }); } catch (error: any) { return text({ error: error.message }); } }; - src/tools/blobStorage.ts:80-89 (schema)Registration of upload_blob tool with input schema: workspaceId (string), content (string/base64), filename (optional string), contentType (optional string).
"upload_blob", { title: "Upload Blob", description: "Upload a file or blob to workspace storage.", inputSchema: { workspaceId: z.string().describe("Workspace ID"), content: z.string().describe("Base64 encoded content or text"), filename: z.string().optional().describe("Filename"), contentType: z.string().optional().describe("MIME type") } - src/tools/blobStorage.ts:79-92 (registration)The server.registerTool('upload_blob', ...) call that registers the tool with the MCP server.
server.registerTool( "upload_blob", { title: "Upload Blob", description: "Upload a file or blob to workspace storage.", inputSchema: { workspaceId: z.string().describe("Workspace ID"), content: z.string().describe("Base64 encoded content or text"), filename: z.string().optional().describe("Filename"), contentType: z.string().optional().describe("MIME type") } }, uploadBlobHandler as any ); - src/tools/blobStorage.ts:8-22 (helper)Helper function decodeBlobContent that tries to decode the content as base64, falling back to UTF-8 text.
function decodeBlobContent(content: string): Buffer { const normalized = content.trim().replace(/\s+/g, ""); const base64Like = normalized.length > 0 && normalized.length % 4 === 0 && /^[A-Za-z0-9+/=]+$/.test(normalized); if (base64Like) { try { const decoded = Buffer.from(normalized, "base64"); if (decoded.length > 0) { return decoded; } } catch { // Fallback to UTF-8 text below. } } return Buffer.from(content, "utf8"); } - src/toolSurface.ts:175-176 (registration)Permission/group configuration mapping upload_blob to ['blobs', 'blobs.write', 'write'] scopes.
upload_blob: ["blobs", "blobs.write", "write"], };