Publish permanent static sites via MCP/REST. 24 tools. No account or claim link.
Call this server's tools from your browser. Nothing to install.
Optional. 'Bearer deed_tok_…' token returned by your first publish_site call. Tools also accept it as a 'token' argument.
Requests go through mcptry because browsers can't call most MCP servers directly. Header values are sent to the server for that request only and are never stored or logged.
Tool list from our last check. Connect live to refresh it.
Attaches a hostname to a slug owned by the token and returns the DNS records to create: a CNAME for a subdomain, an A record or an ALIAS record for an apex (one of those, not both), and every TXT record. Inputs: slug, domain, and token. Status starts at pending_dns until DNS and the certificate are ready. Free allows 1 hostname, Hobby 3, Pro 20. deed.page names are rejected.
Slug you own.
Hostname to attach, such as docs.example.com or example.com. No scheme or path.
Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer.
The server marks this tool as destructive. Running it may delete or overwrite data on the server or in any account you connected with your headers.
Attaches a hostname to a slug owned by the token and returns the DNS records to create: a CNAME for a subdomain, an A record or an ALIAS record for an apex (one of those, not both), and every TXT record. Inputs: slug, domain, and token. Status starts at pending_dns until DNS and the certificate are ready. Free allows 1 hostname, Hobby 3, Pro 20. deed.page names are rejected.
| Argument | Type | Description |
|---|---|---|
| slug* | string | Slug you own. |
| token | string | Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer. |
| domain* | string | Hostname to attach, such as docs.example.com or example.com. No scheme or path. |
{
"type": "object",
"required": [
"slug",
"domain"
],
"properties": {
"slug": {
"type": "string",
"description": "Slug you own."
},
"token": {
"type": "string",
"description": "Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer."
},
"domain": {
"type": "string",
"description": "Hostname to attach, such as docs.example.com or example.com. No scheme or path."
}
}
}Registers an http or https URL for the events site.published, site.version_restored, site.deleted, site.taken_down, domain.active, and report.received. Inputs: url, optional events, and token. Returns the webhook id and an HMAC secret once. Free allows 1 webhook, Hobby 5, Pro 20. Private, internal, and localhost targets are rejected. Delivery is best-effort, with up to 3 attempts. API documentation: https://deed.page/docs.
| Argument | Type | Description |
|---|---|---|
| url* | string | Public http or https URL on port 80 or 443. |
| token | string | Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer. |
| events | array | Events to send. Omit for all of them. |
{
"type": "object",
"required": [
"url"
],
"properties": {
"url": {
"type": "string",
"description": "Public http or https URL on port 80 or 443."
},
"token": {
"type": "string",
"description": "Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer."
},
"events": {
"type": "array",
"items": {
"enum": [
"site.published",
"site.version_restored",
"site.deleted",
"site.taken_down",
"domain.active",
"report.received"
],
"type": "string"
},
"description": "Events to send. Omit for all of them."
}
}
}Permanently removes a slug owned by the token and detaches its custom hostnames. The slug becomes available again. Inputs: slug and token.
| Argument | Type | Description |
|---|---|---|
| slug* | string | Slug you own. |
| token | string | Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer. |
{
"type": "object",
"required": [
"slug"
],
"properties": {
"slug": {
"type": "string",
"description": "Slug you own."
},
"token": {
"type": "string",
"description": "Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer."
}
}
}Deletes one webhook. Inputs: id (whk_...) and token.
| Argument | Type | Description |
|---|---|---|
| id* | string | Webhook id (whk_...). |
| token | string | Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer. |
{
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"description": "Webhook id (whk_...)."
},
"token": {
"type": "string",
"description": "Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer."
}
}
}Returns the verification status of a custom domain and refreshes verification, DNS config, and certificate state. Inputs: slug, domain, and token. status is pending_dns, verifying, issuing_cert, active, or error. dns_records are the records to create. An error includes the reason.
| Argument | Type | Description |
|---|---|---|
| slug* | string | Slug the hostname is attached to. |
| token | string | Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer. |
| domain* | string | Hostname to check. |
{
"type": "object",
"required": [
"slug",
"domain"
],
"properties": {
"slug": {
"type": "string",
"description": "Slug the hostname is attached to."
},
"token": {
"type": "string",
"description": "Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer."
},
"domain": {
"type": "string",
"description": "Hostname to check."
}
}
}Checks uploaded hashes, screens the new bytes, and commits one version under the slug lock. Inputs: session (upl_...), optional expected_version, via, heard_from, and token. A stale expected_version returns version_conflict and writes nothing. A bad hash returns hash_mismatch. An expired session returns session_expired.
| Argument | Type | Description |
|---|---|---|
| via | string | Optional label stored with the publish. At most 80 characters. An IP address, a URL, or an email address is not stored. A non-empty value replaces the X-Via header. |
| token | string | Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer. |
| session* | string | Upload session id (upl_...). |
| heard_from | string | Optional. Where you or your user found deed.page, such as a skill repo, a directory, a blog post, X, or a friend. Free text, at most 200 characters. An email address, IP address, or phone number is stored as [redacted]. A URL or hostname is kept. Omit it and signup continues. |
| expected_version | string |
{
"type": "object",
"required": [
"session"
],
"properties": {
"via": {
"type": "string",
"description": "Optional label stored with the publish. At most 80 characters. An IP address, a URL, or an email address is not stored. A non-empty value replaces the X-Via header."
},
"token": {
"type": "string",
"description": "Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer."
},
"session": {
"type": "string",
"description": "Upload session id (upl_...)."
},
"heard_from": {
"type": "string",
"description": "Optional. Where you or your user found deed.page, such as a skill repo, a directory, a blog post, X, or a friend. Free text, at most 200 characters. An email address, IP address, or phone number is stored as [redacted]. A URL or hostname is kept. Omit it and signup continues."
},
"expected_version": {
"type": "string"
}
}
}Returns the canonical manifest for a site version: paths, sha256, sizes, version, slug, and created_at, plus an Ed25519 signature. signature is null when the server has no signing key. Inputs: slug, and optional version. When version is omitted, the result is the live site. Public keys are published at /.well-known/deed-signing-keys.json. No token is required.
| Argument | Type | Description |
|---|---|---|
| slug* | string | |
| version | string | Optional retained version, such as "v2". Omit for the live version. |
{
"type": "object",
"required": [
"slug"
],
"properties": {
"slug": {
"type": "string"
},
"version": {
"type": "string",
"description": "Optional retained version, such as \"v2\". Omit for the live version."
}
}
}Returns whether a slug is live, plus its URL, version, size, and timestamps. Input: slug. No token is required.
| Argument | Type | Description |
|---|---|---|
| slug* | string | Slug to look up. |
{
"type": "object",
"required": [
"slug"
],
"properties": {
"slug": {
"type": "string",
"description": "Slug to look up."
}
}
}Returns a Stripe Checkout URL for the Hobby plan ($3/mo) or the Pro plan ($15/mo), tied to the token. Inputs: plan (hobby or pro), token, optional how_heard (at most 200 characters, stored on the owner), and optional upgrade_trigger (custom_domain_limit when this follows a custom-domain plan limit, otherwise manual). The call does not charge; payment happens on the returned Checkout page.
| Argument | Type | Description |
|---|---|---|
| plan* | string | Plan to buy. |
| token | string | Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer. |
| how_heard | string | Optional note, at most 200 characters. Stored on the owner. |
| upgrade_trigger | string | custom_domain_limit when the domain limit sent you here. Otherwise manual. |
{
"type": "object",
"required": [
"plan"
],
"properties": {
"plan": {
"enum": [
"hobby",
"pro"
],
"type": "string",
"description": "Plan to buy."
},
"token": {
"type": "string",
"description": "Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer."
},
"how_heard": {
"type": "string",
"description": "Optional note, at most 200 characters. Stored on the owner."
},
"upgrade_trigger": {
"enum": [
"custom_domain_limit",
"manual"
],
"type": "string",
"description": "custom_domain_limit when the domain limit sent you here. Otherwise manual."
}
}
}Returns custom hostnames on one slug, or on every site the token owns when slug is omitted. Inputs: token, and optional slug. Status is the last checked value.
| Argument | Type | Description |
|---|---|---|
| slug | string | Optional slug you own. Omit to list every hostname on this token. |
| token | string | Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer. |
{
"type": "object",
"properties": {
"slug": {
"type": "string",
"description": "Optional slug you own. Omit to list every hostname on this token."
},
"token": {
"type": "string",
"description": "Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer."
}
}
}Returns every live site owned by the token, with URLs and versions. Input: token, or an Authorization bearer header.
| Argument | Type | Description |
|---|---|---|
| token | string | Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer. |
{
"type": "object",
"properties": {
"token": {
"type": "string",
"description": "Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer."
}
}
}Returns every retained version of a slug owned by the token, newest first. Inputs: slug and token. Each entry includes version, created_at, file_count, bytes, kind (publish, merge, or restore), restored_from, and a public permalink. Free keeps 10 versions, Hobby 50, Pro unlimited.
| Argument | Type | Description |
|---|---|---|
| slug* | string | Slug you own. |
| token | string | Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer. |
{
"type": "object",
"required": [
"slug"
],
"properties": {
"slug": {
"type": "string",
"description": "Slug you own."
},
"token": {
"type": "string",
"description": "Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer."
}
}
}Returns the webhook URLs registered for the token. Secrets are not included. Input: token.
| Argument | Type | Description |
|---|---|---|
| token | string | Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer. |
{
"type": "object",
"properties": {
"token": {
"type": "string",
"description": "Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer."
}
}
}Accepts a file manifest and returns which hashes are already stored, a direct upload URL for each missing hash, and a session id. Inputs: files as {path, sha256, size, content_type?}, and optional slug, spa, merge, ttl_seconds, expected_version, via, heard_from, and token. Applies when the files are larger than about 4 MB, or when each sha256 is already known. Does not publish by itself. Per-site size limits are unchanged.
| Argument | Type | Description |
|---|---|---|
| spa | boolean | |
| via | string | Optional label stored with the publish. At most 80 characters. An IP address, a URL, or an email address is not stored. A non-empty value replaces the X-Via header. |
| slug | string | Exact slug, or omit for a random one. |
| files* | array | Manifest entries. sha256 is 64 lowercase hex characters. size is the byte length. |
| merge | boolean | Overlay these paths on the live site. |
| token | string | Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer. |
| heard_from | string | Optional. Where you or your user found deed.page, such as a skill repo, a directory, a blog post, X, or a friend. Free text, at most 200 characters. An email address, IP address, or phone number is stored as [redacted]. A URL or hostname is kept. Omit it and signup continues. |
Publishes files to a live permanent URL at a slug. Inputs: files (required), and optional slug, spa, ttl_seconds, merge, expected_version, via, heard_from, token, listed, category, tags, title, and description. Paths are stored exactly. Returns url, path_url, slug, version, listing, and token on the first anonymous publish. On deed.page, url and path_url are both https://{slug}.deed.page/. A lost token has no recovery unless an optional recovery email was attached. When expected_version does not match the live version, the result is version_conflict and nothing is written. A racing publish of the same slug returns version_conflict even without expected_version. Malware, executables, and credential-harvesting pages return content_rejected and are not stored. .bin is blocked by file type (it often has no MZ or ELF header). A raw octet-stream body is screened as one file. listed defaults to false, so the slug stays out of the public gallery unless listed is true. Password-protected sites cannot be listed.
| Argument | Type | Description |
|---|---|---|
| spa | boolean | Serve index.html for unknown paths (single-page apps). |
| via | string | Optional label stored with the publish. At most 80 characters. An IP address, a URL, or an email address is not stored. A non-empty value replaces the X-Via header. |
| slug | string | Exact slug matching ^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ (lowercase letters, digits, hyphens, 1-63, no leading or trailing hyphen). Invalid slugs return slug_invalid and are not rewritten. Reserved names (hello, my-page, www, api, docs, admin, mcp, blog, app, status, and other docs examples) return slug_reserved on a new claim. A token that already owns one can still update it. Omit for a random slug. |
Removes a hostname so requests to that host no longer serve the site. Inputs: domain, optional slug, and token. When slug is set, the hostname must already be attached to that slug.
| Argument | Type | Description |
|---|---|---|
| slug | string | Optional slug. When set, the hostname must be attached to that slug. |
| token | string | Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer. |
| domain* | string | Hostname to remove. |
{
"type": "object",
"required": [
"domain"
],
"properties": {
"slug": {
"type": "string",
"description": "Optional slug. When set, the hostname must be attached to that slug."
},
"token": {
"type": "string",
"description": "Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer."
},
"domain": {
"type": "string",
"description": "Hostname to remove."
}
}
}Creates a new version whose files equal a retained version, without uploading those files again. Inputs: slug, version, optional expected_version, and token. When expected_version does not match the live version, the result is version_conflict and nothing is written. The previous permalink keeps serving until retention drops it.
| Argument | Type | Description |
|---|---|---|
| slug* | string | Slug you own. |
| token | string | Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer. |
| version* | string | Version to copy forward, such as "v2". |
| expected_version | string | Live version this restore is based on, such as "v4". A mismatch returns version_conflict. |
{
"type": "object",
"required": [
"slug",
"version"
],
"properties": {
"slug": {
"type": "string",
"description": "Slug you own."
},
"token": {
"type": "string",
"description": "Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer."
},
"version": {
"type": "string",
"description": "Version to copy forward, such as \"v2\"."
},
"expected_version": {
"type": "string",
"description": "Live version this restore is based on, such as \"v4\". A mismatch returns version_conflict."
}
}
}Revokes owner tokens. Inputs: token, and optional scope. Scope "current" (the default) revokes the token that was sent. Scope "all" revokes every token for that principal, including any overlap window. A later request with a revoked token returns 401 token_revoked, which is distinct from 403 suspended. Sites stay online. Without a recovery email, a revoked token cannot update them.
| Argument | Type | Description |
|---|---|---|
| scope | string | current revokes only this token. all revokes every token for the principal. Default current. |
| token | string | Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer. |
{
"type": "object",
"properties": {
"scope": {
"enum": [
"current",
"all"
],
"type": "string",
"description": "current revokes only this token. all revokes every token for the principal. Default current."
},
"token": {
"type": "string",
"description": "Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer."
}
}
}Issues a new token for the same principal. Every site carries over. Inputs: token, and optional overlap_seconds from 0 to 300. The previous token stops working immediately when overlap_seconds is omitted or 0. During a non-zero overlap window both tokens work. After that window the previous token returns 401 token_revoked. The new token is returned once.
| Argument | Type | Description |
|---|---|---|
| token | string | Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer. |
| overlap_seconds | integer | Seconds the previous token keeps working so an in-flight deploy can finish. Omit or 0 to invalidate it immediately. Maximum 300. |
{
"type": "object",
"properties": {
"token": {
"type": "string",
"description": "Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer."
},
"overlap_seconds": {
"type": "integer",
"maximum": 300,
"minimum": 0,
"description": "Seconds the previous token keeps working so an in-flight deploy can finish. Omit or 0 to invalidate it immediately. Maximum 300."
}
}
}Sets or clears the public gallery listing for a slug. Inputs: slug, listed, and when listed is true a category, title, description, and optional tags (at most 5), plus token. Returns listed, status, category, tags, title, and description. listed false removes the slug from the gallery immediately. Password-protected sites cannot be listed. A new version of the site runs the gallery checks again. Text is checked on deed.page, and also with the OpenAI moderation API when OPENAI_API_KEY is set.
| Argument | Type | Description |
|---|---|---|
| slug* | string | Slug you own. |
| tags | array | Up to 5 tags. Each tag is lowercase letters, digits, and hyphens, at most 32 characters. |
| title | string | Gallery title, at most 80 characters. Required when listed is true. |
| token | string | Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer. |
| listed* | boolean | Opt into the public gallery. Default false. false removes the slug immediately. Password-protected sites cannot be listed. |
| category | string | Gallery category. Required when listed is true. |
| description | string | Gallery description, at most 300 characters. Required when listed is true. |
Changes the access setting stored for a slug on deed.page. Inputs: slug, password, and token. password is 8 to 200 characters, or null to clear the password. The call writes a scrypt hash into the site record on deed.page, or removes that hash, and contacts no outside system. Setting a password is available on Hobby and Pro; Free returns plan_required. Clearing is allowed on every plan.
| Argument | Type | Description |
|---|---|---|
| slug* | string | Slug you own. |
| token | string | Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer. |
| password* | string | null | New password, or null to clear the password and make the site public. |
{
"type": "object",
"required": [
"slug",
"password"
],
"properties": {
"slug": {
"type": "string",
"description": "Slug you own."
},
"token": {
"type": "string",
"description": "Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer."
},
"password": {
"type": [
"string",
"null"
],
"description": "New password, or null to clear the password and make the site public."
}
}
}Returns owner-only cookieless counts for a slug: views per UTC day, top paths, top referrer hosts, and AI crawler counts by user-agent family. Inputs: slug, optional from and to as YYYY-MM-DD (default the last 7 days, maximum 90), and token. The accuracy field describes under-counting. No raw IP or full user agent is stored.
| Argument | Type | Description |
|---|---|---|
| to | string | Last UTC day, YYYY-MM-DD. Default is today. |
| from | string | First UTC day, YYYY-MM-DD. Default is 6 days before to. |
| slug* | string | Slug you own. |
| token | string | Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer. |
{
"type": "object",
"required": [
"slug"
],
"properties": {
"to": {
"type": "string",
"description": "Last UTC day, YYYY-MM-DD. Default is today."
},
"from": {
"type": "string",
"description": "First UTC day, YYYY-MM-DD. Default is 6 days before to."
},
"slug": {
"type": "string",
"description": "Slug you own."
},
"token": {
"type": "string",
"description": "Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer."
}
}
}Replaces the files of a slug owned by the token, or, when merge is true, adds and overwrites only the paths sent. Inputs: slug, files, and optional spa, merge, expected_version, via, token, listed, category, tags, title, and description. Returns url, path_url, slug, listing, and a new version. When expected_version does not match the live version, the result is version_conflict and nothing is written. Two racing updates return version_conflict even without expected_version. listed defaults to false. Password-protected sites cannot be listed. A new version of a listed slug runs the gallery checks again.
| Argument | Type | Description |
|---|---|---|
| spa | boolean | Serve index.html for unknown paths. |
| via | string | Optional label stored with the publish. At most 80 characters. An IP address, a URL, or an email address is not stored. A non-empty value replaces the X-Via header. |
| slug* | string | Slug you own. Must match ^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$. Not rewritten. Reserved slugs are rejected. |
| tags | array | Up to 5 tags. Each tag is lowercase letters, digits, and hyphens, at most 32 characters. |
| files* | object | Path map, or an array of {path, content} or {path, base64}. Paths are stored exactly as given (a single top-level directory is not removed). Text as a string, binary as {"base64":"..."}. Example: {"index.html":"<!doctype html><h1>Hi</h1>","img/logo.png":{"base64":"iVBOR..."}}. Any other array is rejected. |
Returns the plan, limits, the number of sites the token owns, and the number of custom hostnames attached. Input: token.
| Argument | Type | Description |
|---|---|---|
| token | string | Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer. |
{
"type": "object",
"properties": {
"token": {
"type": "string",
"description": "Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer."
}
}
}| ttl_seconds | integer |
| expected_version | string | Live version this upload must match, such as "v3". |
{
"type": "object",
"required": [
"files"
],
"properties": {
"spa": {
"type": "boolean"
},
"via": {
"type": "string",
"description": "Optional label stored with the publish. At most 80 characters. An IP address, a URL, or an email address is not stored. A non-empty value replaces the X-Via header."
},
"slug": {
"type": "string",
"description": "Exact slug, or omit for a random one."
},
"files": {
"type": "array",
"items": {
"type": "object",
"required": [
"path",
"sha256",
"size"
],
"properties": {
"path": {
"type": "string"
},
"size": {
"type": "integer"
},
"sha256": {
"type": "string"
},
"content_type": {
"type": "string"
}
}
},
"description": "Manifest entries. sha256 is 64 lowercase hex characters. size is the byte length."
},
"merge": {
"type": "boolean",
"description": "Overlay these paths on the live site."
},
"token": {
"type": "string",
"description": "Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer."
},
"heard_from": {
"type": "string",
"description": "Optional. Where you or your user found deed.page, such as a skill repo, a directory, a blog post, X, or a friend. Free text, at most 200 characters. An email address, IP address, or phone number is stored as [redacted]. A URL or hostname is kept. Omit it and signup continues."
},
"ttl_seconds": {
"type": "integer"
},
"expected_version": {
"type": "string",
"description": "Live version this upload must match, such as \"v3\"."
}
}
}| tags |
| array |
| Up to 5 tags. Each tag is lowercase letters, digits, and hyphens, at most 32 characters. |
| files* | object | Path map, or an array of {path, content} or {path, base64}. Paths are stored exactly as given (a single top-level directory is not removed). Text as a string, binary as {"base64":"..."}. Example: {"index.html":"<!doctype html><h1>Hi</h1>","img/logo.png":{"base64":"iVBOR..."}}. Any other array is rejected. |
| merge | boolean | Add or overwrite only these exact paths on the existing site. Paths omitted from this call are left unchanged. Nothing is moved to the site root. |
| title | string | Gallery title, at most 80 characters. Required when listed is true. |
| token | string | Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer. |
| listed | boolean | Opt into the public gallery. Default false. false removes the slug immediately. Password-protected sites cannot be listed. |
| category | string | Gallery category. Required when listed is true. |
| heard_from | string | Optional. Where you or your user found deed.page, such as a skill repo, a directory, a blog post, X, or a friend. Free text, at most 200 characters. An email address, IP address, or phone number is stored as [redacted]. A URL or hostname is kept. Omit it and signup continues. |
| description | string | Gallery description, at most 300 characters. Required when listed is true. |
| ttl_seconds | integer | Opt-in expiry in seconds. Omit for a permanent site. |
| expected_version | string | Live version this write is based on, such as "v3". If the slug is no longer at that version, the result is version_conflict and nothing is written. A racing publish of the same slug returns version_conflict even when this is omitted. |
{
"type": "object",
"required": [
"files"
],
"properties": {
"spa": {
"type": "boolean",
"description": "Serve index.html for unknown paths (single-page apps)."
},
"via": {
"type": "string",
"description": "Optional label stored with the publish. At most 80 characters. An IP address, a URL, or an email address is not stored. A non-empty value replaces the X-Via header."
},
"slug": {
"type": "string",
"description": "Exact slug matching ^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$ (lowercase letters, digits, hyphens, 1-63, no leading or trailing hyphen). Invalid slugs return slug_invalid and are not rewritten. Reserved names (hello, my-page, www, api, docs, admin, mcp, blog, app, status, and other docs examples) return slug_reserved on a new claim. A token that already owns one can still update it. Omit for a random slug."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"maxItems": 5,
"description": "Up to 5 tags. Each tag is lowercase letters, digits, and hyphens, at most 32 characters."
},
"files": {
"type": "object",
"description": "Path map, or an array of {path, content} or {path, base64}. Paths are stored exactly as given (a single top-level directory is not removed). Text as a string, binary as {\"base64\":\"...\"}. Example: {\"index.html\":\"<!doctype html><h1>Hi</h1>\",\"img/logo.png\":{\"base64\":\"iVBOR...\"}}. Any other array is rejected.",
"additionalProperties": {
"anyOf": [
{
"type": "string"
},
{
"type": "object",
"properties": {
"base64": {
"type": "string"
},
"content": {
"type": "string"
}
}
}
]
}
},
"merge": {
"type": "boolean",
"description": "Add or overwrite only these exact paths on the existing site. Paths omitted from this call are left unchanged. Nothing is moved to the site root."
},
"title": {
"type": "string",
"maxLength": 80,
"description": "Gallery title, at most 80 characters. Required when listed is true."
},
"token": {
"type": "string",
"description": "Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer."
},
"listed": {
"type": "boolean",
"description": "Opt into the public gallery. Default false. false removes the slug immediately. Password-protected sites cannot be listed."
},
"category": {
"enum": [
"Apps & tools",
"Games",
"Business",
"Portfolio",
"Docs & guides",
"Education",
"Events",
"Other"
],
"type": "string",
"description": "Gallery category. Required when listed is true."
},
"heard_from": {
"type": "string",
"description": "Optional. Where you or your user found deed.page, such as a skill repo, a directory, a blog post, X, or a friend. Free text, at most 200 characters. An email address, IP address, or phone number is stored as [redacted]. A URL or hostname is kept. Omit it and signup continues."
},
"description": {
"type": "string",
"maxLength": 300,
"description": "Gallery description, at most 300 characters. Required when listed is true."
},
"ttl_seconds": {
"type": "integer",
"description": "Opt-in expiry in seconds. Omit for a permanent site."
},
"expected_version": {
"type": "string",
"description": "Live version this write is based on, such as \"v3\". If the slug is no longer at that version, the result is version_conflict and nothing is written. A racing publish of the same slug returns version_conflict even when this is omitted."
}
}
}{
"type": "object",
"required": [
"slug",
"listed"
],
"properties": {
"slug": {
"type": "string",
"description": "Slug you own."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"maxItems": 5,
"description": "Up to 5 tags. Each tag is lowercase letters, digits, and hyphens, at most 32 characters."
},
"title": {
"type": "string",
"maxLength": 80,
"description": "Gallery title, at most 80 characters. Required when listed is true."
},
"token": {
"type": "string",
"description": "Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer."
},
"listed": {
"type": "boolean",
"description": "Opt into the public gallery. Default false. false removes the slug immediately. Password-protected sites cannot be listed."
},
"category": {
"enum": [
"Apps & tools",
"Games",
"Business",
"Portfolio",
"Docs & guides",
"Education",
"Events",
"Other"
],
"type": "string",
"description": "Gallery category. Required when listed is true."
},
"description": {
"type": "string",
"maxLength": 300,
"description": "Gallery description, at most 300 characters. Required when listed is true."
}
}
}| merge | boolean | Keep every path you do not send, and add or overwrite only these exact paths. |
| title | string | Gallery title, at most 80 characters. Required when listed is true. |
| token | string | Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer. |
| listed | boolean | Opt into the public gallery. Default false. false removes the slug immediately. Password-protected sites cannot be listed. |
| category | string | Gallery category. Required when listed is true. |
| description | string | Gallery description, at most 300 characters. Required when listed is true. |
| expected_version | string | Live version this update is based on, such as "v3". If the site has moved on, the call returns version_conflict with no write. Omit it and a concurrent update still returns version_conflict rather than dropping files. |
{
"type": "object",
"required": [
"slug",
"files"
],
"properties": {
"spa": {
"type": "boolean",
"description": "Serve index.html for unknown paths."
},
"via": {
"type": "string",
"description": "Optional label stored with the publish. At most 80 characters. An IP address, a URL, or an email address is not stored. A non-empty value replaces the X-Via header."
},
"slug": {
"type": "string",
"description": "Slug you own. Must match ^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$. Not rewritten. Reserved slugs are rejected."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"maxItems": 5,
"description": "Up to 5 tags. Each tag is lowercase letters, digits, and hyphens, at most 32 characters."
},
"files": {
"type": "object",
"description": "Path map, or an array of {path, content} or {path, base64}. Paths are stored exactly as given (a single top-level directory is not removed). Text as a string, binary as {\"base64\":\"...\"}. Example: {\"index.html\":\"<!doctype html><h1>Hi</h1>\",\"img/logo.png\":{\"base64\":\"iVBOR...\"}}. Any other array is rejected.",
"additionalProperties": {
"anyOf": [
{
"type": "string"
},
{
"type": "object",
"properties": {
"base64": {
"type": "string"
},
"content": {
"type": "string"
}
}
}
]
}
},
"merge": {
"type": "boolean",
"description": "Keep every path you do not send, and add or overwrite only these exact paths."
},
"title": {
"type": "string",
"maxLength": 80,
"description": "Gallery title, at most 80 characters. Required when listed is true."
},
"token": {
"type": "string",
"description": "Bearer token returned by your first publish (deed_tok_...). Optional if the MCP client sends Authorization: Bearer."
},
"listed": {
"type": "boolean",
"description": "Opt into the public gallery. Default false. false removes the slug immediately. Password-protected sites cannot be listed."
},
"category": {
"enum": [
"Apps & tools",
"Games",
"Business",
"Portfolio",
"Docs & guides",
"Education",
"Events",
"Other"
],
"type": "string",
"description": "Gallery category. Required when listed is true."
},
"description": {
"type": "string",
"maxLength": 300,
"description": "Gallery description, at most 300 characters. Required when listed is true."
},
"expected_version": {
"type": "string",
"description": "Live version this update is based on, such as \"v3\". If the site has moved on, the call returns version_conflict with no write. Omit it and a concurrent update still returns version_conflict rather than dropping files."
}
}
}