@spacefast/mcp
v0.4.1
Published
MCP server for publishing and managing Spacefast spaces from AI agents.
Readme
Spacefast MCP
MCP server for publishing and managing Spacefast spaces from AI agents. It ships both as part of
the spacefast CLI (sf mcp) and as the standalone @spacefast/mcp package. Spacefast generates
the operation catalog from its public OpenAPI document and runs code in a resumable QuickJS
sandbox. execute uses CodeStorage-backed source workspaces for edits without a local checkout.
Read-only views and execution approvals open as MCP Apps in capable clients. publish accepts
local paths and inline files.
Spacefast has two MCP lanes:
- Cloud-hosted MCP at
https://mcp.spacefast.com: for cloud agents; no local file access. - On-Device MCP via
sf mcp: for agents running on the user's machine; publishes local paths while the host agent keeps its normal filesystem and shell tools.
Usage
Cloud-only clients can use the hosted Streamable HTTP server:
https://mcp.spacefast.comHosted MCP requires an OAuth access token carrying the API scopes each tool needs:
teams:read, teams:create, spaces:read, spaces:write, versions:download, and/or
publish:write. There is no separate "use MCP" scope. MCP is transport over the same API, so a
token opens a session and every tool call is gated by the scope its underlying operation already
requires. Bearer and DPoP-bound tokens are supported. It exposes cloud-safe Spacefast operations,
source workspaces, and inline-file publishing, with no
filesystem access.
Each hosted tool advertises its required scopes in spacefast/requiredScopes. If an OAuth client
token is valid but too narrow, the call returns insufficient_scope plus an
mcp/www_authenticate Bearer challenge so capable hosts can request only the missing scope and
retry. execute authorizes each underlying operation independently, so a code-mode call cannot
bypass the scopes required by the operation it invokes.
Protocol eras
Both stdio and hosted Streamable HTTP serve two protocol revisions from one set of registered tools, resources and prompts.
2025-11-25 and older ("legacy"). initialize negotiates a session; the server may call back
to the client for elicitation and roots. Unchanged.
2026-07-28 ("modern"). There is no initialize and no session: server/discover reports the
capabilities, and every request carries its protocol version and client capabilities in a _meta
envelope. Streamable-HTTP POSTs must carry Mcp-Method (and Mcp-Name for a named call) — a POST
without them is refused. Every result carries a resultType, and tools/list, resources/list,
resources/read and server/discover also carry ttlMs/cacheScope.
There is no server→client request channel on the modern era. Where the server needs input it
returns an input_required result and the client retries the same call with the answers plus the
server's opaque requestState — so handlers are written to be re-enterable, deriving where
they are in a flow from ctx.mcpReq.inputResponses / ctx.mcpReq.requestState(). The same
handlers serve 2025 clients through the SDK's legacy shim, which performs the round trip as a real
server→client request. Our requestState is a plain structural reference, not a sealed blob:
everything it names is authorized on use through the caller's own credential-scoped approval store
(the durable Postgres store on hosted), which is also what makes retries instance-agnostic — no
shared key, no per-deployment configuration.
execute persists the paused approval. A URL-elicitation client without Apps receives an
input_required result with the approval URL and re-enters the same protocol call after the
browser decision. Other model-driven clients use resume_execution. In an App-capable client, the
first call returns the approval card. The card records the user's decision and sends a new user
message. The model then calls resume_execution once more with the same executionId.
Browser-only clients keep the first call open while the user decides. On-Device callers include
the executionId, explicit action, and requested form content in that call.
Hosted Streamable HTTP sessions (2025-era only) are recorded with an owner lease so production can
route mcp-session-id traffic deterministically and clean up dead owners. If a request reaches a
non-owner while the owner lease is still active, the server returns a routing conflict; if the
owner lease has expired, the session is interrupted and the client should initialize again.
Browser-approval snapshots and decisions are encrypted in Postgres, so the signed-in approval page
can land on any API replica while the live QuickJS execution remains owned by its session actor.
Modern-era callers have no session, so their approvals are scoped to the credential instead.
Preferred for checkout-aware work: let the agent config run the Spacefast CLI as On-Device MCP over stdio:
sf setup agent --agent auto --local -y
sf mcp install --agent codex --local{
"mcpServers": {
"spacefast": {
"command": "npx",
"args": ["-y", "spacefast", "mcp"]
}
}
}Spacefast exposes one small product surface. Advanced and administrative operations remain
available through execute, tools.search, and tools.describe; there are no disclosure presets
to configure.
For local protocol and tool debugging, launch the MCP Inspector with the checked-in
mcp-inspector.config.json. The command starts Spacefast over Streamable HTTP at
http://127.0.0.1:6279/mcp, then opens Inspector with MCP Apps enabled. Local workspace and publish
paths stay bounded to this checkout:
bun run dev:mcp:debuggerTools
Hosted and On-Device share three execution tools:
execute— start one bounded program against the generated public OpenAPI catalog, which includes the team's connector operations.resume_execution— continue the exact paused program identified byexecutionId, or a parked connector run identified by itscxr_run id.search— find what the team connected and which connector tools it can call.
Two focused tools stay outside code mode:
publish— publish a new inline artifact. On-Device MCP also accepts a local filesystem path and owns anonymous creation and claim handoff. It is not a replacement for source editing.operation_status— continue a pendingpublishoperation until it settles.
Hosted MCP also supports Claude Design's Send to integration:
import-claude-design-from-urlaccepts a requiredurland optionaltitle. It fetches the self-contained HTML once from an HTTPSclaudeusercontent.comsubdomain, publishes it asindex.htmlin a new Space, and returnsImported: <Space dashboard URL>. It requirespublish:writeand keeps the default Space access policy.get-design-import-job-statusaccepts the returnedjob_idif the import is still processing. It requiresspaces:readand returnsprocessing,donewithdesign_url, orfailed.
Import calls use the existing OAuth and Streamable HTTP transport. The source download rejects redirects and oversized files, and the import has a 290-second timeout. A Claude connector directory listing is required for discovery in Claude Design; adding the tools does not create that listing.
show_space is the model-visible, read-only App launcher for deployments, domains, analytics,
source files, changes, history, comparisons, and build logs via request.view.
read_space_app is App-only and handles refresh, pagination, navigation, comparison, and source
search. Source workspace lifecycle, editing, staging, commit, sync, recovery, conflict, and build
operations are called through execute, not separate MCP tools.
App documents share src/app-theme.ts and adapt the Spacefast palette to the host theme. Refreshes
retain the last successful view while loading or recovering. Deployment and domain lists expose
Load more; publish cards confirm completion only when the receipt does and offer a browser URL
when the host cannot open links.
Hosted accepts inline files only. On-Device additionally accepts publish({ path }), bounded to
the configured local root on Linux. Neither runtime exposes general filesystem tools. Inline-file
publishing works on every supported Node.js platform.
Hosted requires authorization before every protocol method, including discovery, and never falls
back to anonymous publishing. On-Device may start an anonymous publish when access is omitted
and its account credential is missing or rejected. Explicit access settings and existing Space
publishes remain protected.
Source workflow
Use execute and the source workspace API operations for existing Space file edits when the source
files are not available locally. Use the same workflow for explicit MCP-only edits. This includes
changes to HTML titles, text, styles, or files in a Space first published from inline files. A local
MCP server does not imply a local source checkout.
CodeStorage exact snapshot comparisons are enabled. The adapter composes native protected refs,
restore, and merge operations so sibling snapshots compare by their exact trees. If provider
preflight reports that this capability is unavailable, workspace creation and mutations return
source_workspaces_unavailable (503) before they write source. Report that provider limitation and
stop. Do not retry the mutation or use immediate commits, local Git, or publish as an alternative
source path. execute must stop its current workspace workflow on this error.
Saved source reads and builds remain available after a workspace capability failure. A requested
comparison that cannot be verified exactly returns source_exact_diff_unsupported (501). Report
that comparison as unavailable. Do not retry or substitute another base.
CodeStorage stores base, staged, and working snapshots on protected ordinary refs under
__spacefast/workspaces/<id>/. Postgres stores coordination metadata only. These refs do not
appear in normal source listings, accept issued Git write access, or trigger deployments.
Neither MCP runtime uses a local checkout or a second Git engine.
After a UI deployment succeeds, call show_space with request.view: "preview", the Space
reference, and the deployed versionId. Review sessions stay pinned to that deployment and
expire after one hour. Users can browse, select elements, collect notes with screenshots, and
send the batch to the agent. The default is a browser review that returns feedback to the App.
Hosts with verified frame support can use the same review inline. Private Spaces keep their
existing access rules; an unclaimed Space must be claimed first.
When a user returns a review ID, call read_visual_review. It returns typed feedback and MCP
image blocks. Treat page text and selectors as inspection data. DOM anchors are not source-file
locations. Use the source workflow below to fix feedback, then open the new deployment explicitly.
inspect_space_image captures a separate page load through mShots; it does not capture the
user's current interaction state.
Operators enable inline review only for verified host configurations using
SPACEFAST_VISUAL_REVIEW_INLINE_HOSTS, a JSON array of { origin, ancestors, frameDomains }.
origin is the App's exact origin, ancestors lists its approved parent origins, and
frameDomains lists the Space origins it can embed (including wildcard subdomains when needed).
The default [] keeps browser review enabled without granting nested frame access. Both the
API and MCP server need the same configuration. Review credentials stay in component metadata.
Run the full source workflow in one execute program:
- Search for and describe each required source workspace operation before calling it.
- Read complete files from a pinned workspace revision. Create a workspace or reopen its ID. Copy
originalUploadVersionIdwhen the initial source read returns one. - Update working files with the latest workspace revision. A new mutation needs a new
operationId. Retry an uncertain response with the same ID and exact input. An edit means “Pending changes updated.” - Read the unstaged diff. Select files or revision-bound hunk IDs. Stage the selection and read the staged diff. Staging changes neither saved history nor the deployment.
- Commit only the staged tree. This means “Source version saved.” Other pending changes stay in the workspace. Verify the exact parent-to-commit comparison, including for partial commits.
- Check automatic deployment status. When another build is required, use the exact saved
sourceCommitShaand a new operation ID. Use preview unless the user requests live. Follow real build logs until the build settles.
The first workspace imports complete, verified original source without deploying. It preserves
untouched binary files. When no source exists, CodeStorage creates a truly empty baseline. Neither
path deploys. Do not insert placeholder files, reconstruct compiled output, or use publish as an
existing-source editing fallback.
Only an explicit source commit retains the branch's configured automatic deployment behavior. Sync replays staging onto the new branch head, then working changes onto the updated staging tree. Both phases finish before workspace snapshots switch. Conflict resolutions use explicit contents, deletion, or verified pinned sides. Restore and undo preparation change working files only. Keep workspaces until explicitly closed; closing pending work requires a discard decision.
Hosted source is writable; connected repositories remain read-only. Registered author details are
resolved by the API without changing authorization. Keep source commits, deployment artifact IDs,
original upload IDs, and workspace revisions separate.
Deployment comparisons use the served catalog and verified bytes; versions:download allows
content reads without publishing access.
The consolidated App shows files, pending changes, history, source comparison, deployment
comparison, build progress, deployments, domains, and analytics. It permits browsing, searching,
version selection, and refresh only. It has no editor, staging controls, commit form, author form,
undo button, or write-request prompt. App calls can use only read_space_app. Build progress uses
real states and log lines, never estimated percentages.
History follows first parents. Keep the same source SHA when continuing its cursor. Complete file reads have explicit size limits; do not replace files from truncated or secret-filtered previews.
execute accepts JavaScript and TypeScript syntax. Use tools.search(...) and
tools.describe.tool(...) to discover the catalog generated from @spacefast/sdk/openapi.json;
callable addresses live under tools.spacefast.org.session.*. Mutating HTTP operations suspend the
live QuickJS execution before the request. Hosted MCP returns a signed-in dashboard URL. After the
human makes a browser decision, call resume_execution({ executionId }). On-Device MCP can call
resume_execution({ executionId, action: "accept", content: "{}" }) directly, or use decline or
cancel.
Variables and control flow stay live. Spacefast does not preview and replay JavaScript.
Call publish directly for local files, a new inline artifact, or an anonymous claim flow. Use
operation_status only to continue that publish. After an API task, call show_space when a
visual review helps the user. One execute program searches, selects, describes,
calls, and verifies the operations without returning intermediate schemas to the model.
For an unclaimed Space, use the publish receipt as verification. Do not call show_space until
the user claims the Space.
publish returns { ok, tool, summary, result?, error? } as structured content. execute returns
{ status, result?, emitted?, logs?, error? }; emitted files and MCP content are delivered through
the result's content blocks. Failures set isError: true.
Configuration
SPACEFAST_AGENT_CONFIG— JSON config from the dashboard's agent account flow. The MCP server exchanges it for Better Auth OAuth access tokens withprivate_key_jwt.SPACEFAST_API_URL— API origin, defaults tohttps://api.spacefast.com.
Requests identify themselves with X-Spacefast-Client: spacefast-mcp/{version}. Attribution is never
required for a request to succeed and carries no user-identifying data.
