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

http
code
POST https://api.blobify.io/v1/mcp
Authorization: Bearer YOUR_TOKEN
Content-Type: application/json

The 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:

json
code
{
  "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:

json
code
{
  "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

ToolPurpose
listOrgsList granted organizations with the live role, or grants for a scoped key, and space scope
getReadContextRead bucket, locale, space, model, block, and path-template context
listModelsList model IDs and names
getModelSchemaRead one full model schema
listBlocksList block IDs and names
getBlockSchemaRead one full block schema
getWriteContextGet stored field shapes and a model-specific write skeleton
listFieldTypesList supported field types and capabilities
listLocalesList configured locales and the default locale
findContentList or search summary-backed content
getContentRead one draft or published content document
findAssetsSearch 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

ToolPurpose
createDraftCreate a draft
saveDraftReplace a draft's fields
patchFieldsApply RFC 6902 operations to the fields object
appendBlockAppend a block and generate its instance ID
setRichtextConvert markdown to the rich-text AST and save the field
setLocaleStatusSet or clear one locale's translation marker on a draft
bulkImportSave up to 100 inline items with per-item results
requestImportUploadCreate a presigned upload for an NDJSON import payload
bulkImportFromUploadImport a previously uploaded NDJSON payload
requestAssetUploadCreate one presigned asset upload
confirmAssetUploadConfirm one uploaded asset and update the catalog
requestAssetUploadsCreate up to 100 presigned asset uploads
confirmAssetUploadsConfirm up to 100 uploaded assets in one batch
patchAssetMetadataUpdate 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:

json
code
{
  "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:

  1. requestAssetUpload with filename, contentType, and size returns assetId, a presigned uploadUrl valid for 15 minutes, and the headers the PUT must send. Optional folder, name, and localized alt are set here.
  2. The host PUTs the raw bytes to uploadUrl with those headers. The bytes never pass through Blobify's API.
  3. confirmAssetUpload finalizes the asset and records width, height, duration for video and audio, and poster for 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

ToolPurpose
validateContentValidate candidate fields before a write
canPublishCheck a saved draft against publish-time requirements and uniqueness
publishPublish selected locales
bulkPublishPublish a batch with per-item results
unpublishUnpublish selected locales or the whole entry
deleteContentArchive a live entry, or permanently delete an already archived entry
restoreContentRestore an archived entry
listArchivedSearch 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

ToolPurpose
schedulePublishSchedule a future publish or unpublish for one entry
cancelScheduledPublishCancel the pending schedule for one action
listScheduledPublishesList a space's schedule records with action, status, and result
createReleaseCreate a named group of documents that publish together
listReleasesList releases with status, schedule, and membership
getReleaseRead one release record with per-item publish results
addToReleaseAdd documents, copying each current draft into the release
removeFromReleaseRemove a document and delete its working copy
getReleaseCopyRead a member's working copy
saveReleaseCopyReplace a working copy's fields
publishReleasePublish every member now, with per-item results
scheduleReleaseSchedule the release to publish at a future minute
unscheduleReleaseReturn a scheduled release to open
discardReleaseDiscard 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

ToolPurpose
upsertModelCreate or replace a model schema
deleteModelDelete an unused model schema
upsertBlockCreate or replace a block schema
deleteBlockDelete an unused block schema
importSchemasValidate and apply a model and block bundle
rebuildModelIndexesQueue summary, field-index, or list-index rebuilds for a model
addLocaleAdd a locale and optionally make it the default
publishRoutingPublish the organization's draft routing configuration
generateClientGenerate 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

ToolPurpose
listTaxonomiesList schemes with label, versions, concept count, and depth
getTaxonomyRead one authoring scheme plus the version token for writes
upsertTaxonomyCreate or replace a whole scheme
upsertConceptAdd or edit one concept
deleteConceptTombstone one concept and prune it from related lists
publishTaxonomyWrite 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

ToolPurpose
getWebhookSpecReturn headers, signature rules, payload shape, events, retry policy, and verifier example
listWebhooksList webhook configurations with masked secrets
createWebhookCreate a webhook and return its secret once
updateWebhookChange a webhook URL, events, or enabled state
testWebhookSend a synchronous test delivery
deleteWebhookRemove 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

  1. Call listOrgs, then getReadContext.
  2. Read the relevant model or block schema.
  3. Find and read the target content before changing it.
  4. Prefer patchFields for a small edit and saveDraft for an intentional full replacement.
  5. Call canPublish before publishing.
  6. 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:

json
code
{
  "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.