Documentation
MCP server
Blobify exposes a Model Context Protocol server for AI hosts that support Streamable HTTP. The server provides typed tools for discovery, content authoring, assets, publishing, schemas, generated clients, locales, archives, and webhooks.
Endpoint and transport
POST https://api.blobify.io/v1/mcp
Authorization: Bearer YOUR_TOKEN
Content-Type: application/jsonThe endpoint is stateless. Each request creates a fresh MCP server and returns plain JSON. It does not use SSE or resumable sessions.
The same URL serves every organization. A manual API key grants one organization. An OAuth token can grant one or several organizations. Call listOrgs at the start of a session, and pass orgId on targeted calls when the token covers more than one organization.
Choose OAuth or an API key
Claude and ChatGPT can connect through Blobify's OAuth 2.1 flow. Add https://api.blobify.io/v1/mcp as a custom connector, sign in to Blobify, choose the organizations to share, and approve access. The connector receives a scoped token without asking you to paste a long-lived API key.
Use a manual API key for automation hosts that accept a bearer header, including Codex, Cursor, Zed, scripts, and local MCP bridges. Create the key in Settings > Developer > API Keys, assign the least privileged role, and restrict it to the required spaces.
OAuth-issued access follows the user's current organization membership. Manual keys use the role and spaces assigned when the key is created. Both can be revoked from Blobify.
Connect with a bearer header
For a client that accepts remote Streamable HTTP directly, configure this URL and header:
{
"url": "https://api.blobify.io/v1/mcp",
"headers": {
"Authorization": "Bearer YOUR_API_KEY"
}
}Claude Desktop installations that need a stdio bridge can use mcp-remote:
{
"mcpServers": {
"blobify": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://api.blobify.io/v1/mcp",
"--header",
"Authorization: Bearer YOUR_API_KEY"
]
}
}
}Do not expose a manual API key in browser JavaScript. MCP clients send requests from their native application or hosted connector infrastructure.
Scoped API keys over MCP
A scoped key carries capability grants instead of a role, and MCP enforces exactly the same scope as the REST API. Each tool maps to the one grant it needs, so a key with content:read and content:write can find, read, and edit drafts but cannot publish. A tool whose REST equivalent carries no grant at all is denied to scoped keys, which covers schema writes, taxonomy writes, routing publish, rebuilds, and every webhook tool. On top of the grant check, the key's allowed spaces are checked whenever a call names a space, its allowed models whenever a call names a model, and its allowed locales on publishing, on setLocaleStatus, and against the translatable fields a write touches.
Two discovery tools are always allowed for any authenticated key, listOrgs and listLocales, because every session is told to call listOrgs first and a scoped key has to be able to find the organization it is scoped to. Both return only metadata the key already carries. For a scoped key, listOrgs reports its grants plus any model and locale restrictions instead of a role.
Unscoped role keys and OAuth tokens keep role-based behavior. See Security and access and Automation API.
Tool catalog
The server currently exposes 69 tools. The names below are the exact tool names an AI host will see.
Discovery and reads
| Tool | Purpose |
|---|---|
listOrgs | List granted organizations with the live role, or grants for a scoped key, and space scope |
getReadContext | Read bucket, locale, space, model, block, and path-template context |
listModels | List model IDs and names |
getModelSchema | Read one full model schema |
listBlocks | List block IDs and names |
getBlockSchema | Read one full block schema |
getWriteContext | Get stored field shapes and a model-specific write skeleton |
listFieldTypes | List supported field types and capabilities |
listLocales | List configured locales and the default locale |
findContent | List or search summary-backed content |
getContent | Read one draft or published content document |
findAssets | Search asset-catalog entries |
getContent with state: "draft" returns the full document plus its etag, which is the version token setLocaleStatus and other conditional writes require. state: "published" returns the public shape, with timestamps and no actor IDs, and no etag.
Drafts, imports, and assets
| Tool | Purpose |
|---|---|
createDraft | Create a draft |
saveDraft | Replace a draft's fields |
patchFields | Apply RFC 6902 operations to the fields object |
appendBlock | Append a block and generate its instance ID |
setRichtext | Convert markdown to the rich-text AST and save the field |
setLocaleStatus | Set or clear one locale's translation marker on a draft |
bulkImport | Save up to 100 inline items with per-item results |
requestImportUpload | Create a presigned upload for an NDJSON import payload |
bulkImportFromUpload | Import a previously uploaded NDJSON payload |
requestAssetUpload | Create one presigned asset upload |
confirmAssetUpload | Confirm one uploaded asset and update the catalog |
requestAssetUploads | Create up to 100 presigned asset uploads |
confirmAssetUploads | Confirm up to 100 uploaded assets in one batch |
patchAssetMetadata | Update alt text, title, and description for up to 100 assets |
Asset bytes go directly from the host to the presigned bucket URL. Upload the original file, then confirm it. Blobify stores originals and leaves resizing and cropping to delivery helpers.
setLocaleStatus writes editorial metadata, not content: it never blocks a save or a publish, changes no timestamps, creates no version, and emits no webhook. It does move the draft's etag, so expectedEtag is required. Pass the etag from getContent, or reuse the one the previous setLocaleStatus returned. See Localization workflow.
Uploading and referencing assets
An asset field stores a reference, never a URL:
{
"coverImage": { "type": "asset", "assetId": "mrcrushtyods56" }
}A translatable asset field wraps the whole reference per locale, as { "en": { "type": "asset", "assetId": "..." } }.
Adding a new file takes three steps:
requestAssetUploadwithfilename,contentType, andsizereturnsassetId, a presigneduploadUrlvalid for 15 minutes, and theheadersthe PUT must send. Optionalfolder,name, and localizedaltare set here.- The host PUTs the raw bytes to
uploadUrlwith those headers. The bytes never pass through Blobify's API. confirmAssetUploadfinalizes the asset and recordswidth,height,durationfor video and audio, andposterfor video.
An unconfirmed asset is absent from the asset catalog, so write the assetId into a content field only after the confirm call. For migrations, requestAssetUploads and confirmAssetUploads do the same in batches of up to 100.
To reuse a file that is already in the space, skip the upload entirely: findAssets filters by category, folder, and extension, matches a query against filename and alt text, and returns asset IDs you can reference directly.
Reads work the other way around. getContent and findContent return the stored reference verbatim, with no URL, dimensions, or alt text. The generated client resolves each reference against the public asset catalog into url, width, height, duration, alt, title, description, contentType, filename, and a resolved video poster. Do not expect resolved media from MCP reads, and do not build bucket URLs from an asset ID by hand. getWriteContext returns the same guidance in its assets section. See Field types.
Validation and lifecycle
| Tool | Purpose |
|---|---|
validateContent | Validate candidate fields before a write |
canPublish | Check a saved draft against publish-time requirements and uniqueness |
publish | Publish selected locales |
bulkPublish | Publish a batch with per-item results |
unpublish | Unpublish selected locales or the whole entry |
deleteContent | Archive a live entry, or permanently delete an already archived entry |
restoreContent | Restore an archived entry |
listArchived | Search archived entries for a model |
Deletion is archive-first. Use restoreContent to recover the first delete. Permanent deletion is the second delete of an archived entry.
Scheduling and releases
| Tool | Purpose |
|---|---|
schedulePublish | Schedule a future publish or unpublish for one entry |
cancelScheduledPublish | Cancel the pending schedule for one action |
listScheduledPublishes | List a space's schedule records with action, status, and result |
createRelease | Create a named group of documents that publish together |
listReleases | List releases with status, schedule, and membership |
getRelease | Read one release record with per-item publish results |
addToRelease | Add documents, copying each current draft into the release |
removeFromRelease | Remove a document and delete its working copy |
getReleaseCopy | Read a member's working copy |
saveReleaseCopy | Replace a working copy's fields |
publishRelease | Publish every member now, with per-item results |
scheduleRelease | Schedule the release to publish at a future minute |
unscheduleRelease | Return a scheduled release to open |
discardRelease | Discard an open release, or delete a terminal one |
Schedules use minute precision in UTC, and one entry can hold one pending schedule per action. A document belongs to at most one open release at a time. See Publishing and releases.
Schemas, locales, and code generation
| Tool | Purpose |
|---|---|
upsertModel | Create or replace a model schema |
deleteModel | Delete an unused model schema |
upsertBlock | Create or replace a block schema |
deleteBlock | Delete an unused block schema |
importSchemas | Validate and apply a model and block bundle |
rebuildModelIndexes | Queue summary, field-index, or list-index rebuilds for a model |
addLocale | Add a locale and optionally make it the default |
publishRouting | Publish the organization's draft routing configuration |
generateClient | Generate the current TypeScript types, helpers, and bucket client |
Schema writes and rebuilds require developer or admin. Destructive schema operations and locale changes can require admin. Regenerate the client after any model or block schema change.
Taxonomies
| Tool | Purpose |
|---|---|
listTaxonomies | List schemes with label, versions, concept count, and depth |
getTaxonomy | Read one authoring scheme plus the version token for writes |
upsertTaxonomy | Create or replace a whole scheme |
upsertConcept | Add or edit one concept |
deleteConcept | Tombstone one concept and prune it from related lists |
publishTaxonomy | Write the scheme's public projection |
Reads are open to organization members. Every mutation requires developer or admin, and every mutation stays private until publishTaxonomy runs. Replacing an existing scheme needs the version token from getTaxonomy, while upsertConcept and deleteConcept read the scheme themselves and write under the version they read, so a concurrent edit fails instead of being overwritten.
Tagging content is not a taxonomy tool. Write the taxonomy field with patchFields, saveDraft, or bulkImport as an array of { scheme, id } objects, and filter with findContent by concept ID, optionally with includeDescendants. See Taxonomies.
Webhooks
| Tool | Purpose |
|---|---|
getWebhookSpec | Return headers, signature rules, payload shape, events, retry policy, and verifier example |
listWebhooks | List webhook configurations with masked secrets |
createWebhook | Create a webhook and return its secret once |
updateWebhook | Change a webhook URL, events, or enabled state |
testWebhook | Send a synchronous test delivery |
deleteWebhook | Remove a webhook configuration |
Call getWebhookSpec before implementing a receiver. Blobify includes resolved urls and revalidationTags in lifecycle payloads, retries a failed delivery once, and stores only the latest delivery result. It does not maintain a retry queue.
A safe authoring workflow
- Call
listOrgs, thengetReadContext. - Read the relevant model or block schema.
- Find and read the target content before changing it.
- Prefer
patchFieldsfor a small edit andsaveDraftfor an intentional full replacement. - Call
canPublishbefore publishing. - Publish only when the user's request clearly includes publishing.
For a block append, the tool input separates the block type from its field values:
{
"model": "page",
"id": "page_1",
"blocksField": "sections",
"block": {
"type": "hero-block",
"fields": {
"heading": { "en": "Welcome" }
}
}
}appendBlock supplies the block instance ID. Before calling it, read both the model and block schemas so the chosen block is allowed and its required fields are present.
Reads for applications
MCP read tools are useful while an AI host is authoring content. Production websites should use generateClient and read published JSON directly from the bucket. The generated client follows summary manifests, content prefixes, asset resolution, references, and list-index v2 pages.
Troubleshooting
A client tries to parse SSE
Choose Streamable HTTP and expect an application/json response. Blobify does not return an SSE stream.
resources/list returns method not found
Blobify exposes MCP tools, not MCP resources. Use tools/list.
A call returns 401
The token is missing, malformed, expired, or revoked.
A call returns 403
The current role does not permit that tool, a scoped key lacks the grant that tool needs, the target space, model, or locale is outside the token's scope, or an OAuth user's live membership no longer grants access.
The host cannot fetch a presigned or bucket URL
Use findContent, getContent, and findAssets for normal authoring reads. Asset and import uploads still require the host to perform an HTTP PUT to the presigned URL.