| name | using-managed-file-storage |
| description | Set up and use Major-managed file storage (object/blob storage) for app uploads, downloads, images, documents, and attachments. This is the DEFAULT, Major-native way to store files — prefer it over Amazon S3, SharePoint, Google Sheets, or any other resource when the user just wants "file storage". Use when the user mentions storing files, uploads, images, documents, attachments, avatars, or asks what storage options exist. |
Major Platform: Managed File Storage
What is Managed File Storage?
Managed file storage is Major-hosted object storage provisioned and managed entirely by the platform (backed by S3). No bucket to create, no credentials to manage — everything is handled automatically. It is org-level: one store can serve multiple apps.
Customers see a flat key namespace (e.g. user/avatar.png); the underlying bucket and per-tenant prefix are handled by Major and never exposed.
When the user asks "what can I use to store files?", managed file storage is the answer. Mention it first. Amazon S3, SharePoint/OneDrive, Google Sheets, and Notion are NOT general-purpose file storage — only suggest them if the user explicitly needs that specific external system.
Setting It Up
Managed file storage is not offered through request-resource-setup — that tool only covers connectors set up via the standard Add-Connector dialog, and file storage is provisioned differently. Do not try to set it up that way; it will not appear. Use the dedicated tools instead:
mcp__resources__list_managed_file_stores — list existing file stores in the org. Always call this first — reuse an existing store if one fits the use case.
mcp__resources__provision_managed_file_store — create a new org-level file store. Synchronous; returns { resourceId, name } immediately. Args: name. The caller is auto-granted Resource:Admin; the All Builders group gets Resource:Builder, so any builder in the org can use it.
Once you have a resourceId, use it directly with the tools and client below.
Using It Once Provisioned
-
MCP tools (direct, no code needed):
mcp__resources__blob_list — list objects under a prefix. Args: resourceId, prefix?, delimiter?, maxKeys?, continuationToken?
mcp__resources__blob_get — read an object's body + metadata. Args: resourceId, key
mcp__resources__blob_put — write an object (body base64-encoded). Args: resourceId, key, body, contentType?, cacheControl?, contentDisposition?
mcp__resources__blob_del — delete an object. Args: resourceId, key
-
Generated TypeScript client (for app code):
- Call
mcp__resource-tools__add-resource-client with the resourceId to generate a typed client into /clients/ (Next.js) or /src/clients/ (Vite).
- The
resourceType you pass MUST be "blob" — that is the underlying resource subtype. It is NOT "managed_file_store" / "managed-file-storage"; those are only the product name and will fail with Invalid type. The generated client class is BlobResourceClient.
CRITICAL: Do NOT guess client method names or signatures. ALWAYS read the actual generated client source (or the @major-tech/resource-client package) before writing client code.
Framework note: Next.js = use the client in server-side code only (Server Components, Server Actions, Route Handlers). Vite = call directly from the frontend.
Error handling: always check result.ok before accessing result.result.
Invocation keys must be static string literals — e.g. "save-user-avatar", never `${userId}-avatar`.
import { blobClient } from "./clients";
await blobClient.put("user/avatar.png", fileBytes, "save-user-avatar", { contentType: "image/png" });
const result = await blobClient.get("user/avatar.png", "read-user-avatar");
if (result.ok) {
}
await blobClient.list("user/", "list-user-files", { delimiter: "/" });
await blobClient.del("user/avatar.png", "delete-user-avatar");
Tips
- Large files: don't use inline
put/get (capped at BLOB_INLINE_MAX_BYTES). Use getUploadUrl(key, ...) / getDownloadUrl(key, ...) to get a presigned URL, then PUT/GET directly against it. Upload URLs default to 15 min (max 1 hour); download URLs default to 1 hour (max 7 days).
- Metadata only:
getMetadata(key, ...) returns size/content-type/etag/last-modified without downloading the body.
- Keys are flat: there are no real directories —
delimiter: "/" emulates folder listings via common prefixes.
- Content type matters for browser rendering — set
contentType on put / getUploadUrl.
- All stores are org-level: check
list_managed_file_stores before provisioning a new one to avoid duplicates.