MCP server - Spacefast Docs

MCP server

Connect to the Spacefast MCP server over hosted HTTP or local stdio, and use its five tools, execute sandbox, and approval model.

After this page you can connect any MCP client to Spacefast, read every tool’s input schema, write execute programs against the whole Spacefast API, and know exactly when a call will stop and ask you for approval.

Two runtimes, one tool set

The same server ships in two runtimes. Both register the same five tools. The difference is the filesystem.

Hosted Local
Transport Streamable HTTP stdio
Endpoint or command https://mcp.spacefast.com sf mcp, or npx -y spacefast mcp
Filesystem None. publish requires inline files Reads your workspace. publish takes a path or publishes the current workspace
Auth OAuth, per client Your CLI login, or SPACEFAST_TOKEN
Where it runs Spacefast Your machine

The hosted endpoint also answers on /mcp and /mcp/. Only the mcp. hostname serves MCP; the path /mcp on the API host does not.

There is a third lane, sf mcp proxy, which speaks stdio to your editor and HTTPS to the hosted endpoint using your CLI login.

Authentication

Hosted

The hosted server is an OAuth 2.0 protected resource. It is never anonymous. A request with no token gets a 401 and a WWW-Authenticate challenge that points at the resource metadata document, and your client walks the rest from there. Bearer and DPoP are both accepted; a DPoP-shaped request gets a DPoP challenge back.

By default a client asks for these scopes.

Scope What it grants
teams:read Read the teams the account belongs to
teams:create Create teams
spaces:read Read spaces, versions, and build status
spaces:write Create and update spaces and their settings
spaces:rename Rename space slugs and managed addresses
publish:write Publish new immutable versions
domains:read Read domain inventory and DNS diagnostics
domains:write Attach, verify, and mutate domains and DNS records
offline_access Refresh access without another sign-in

Each tool declares the scopes it needs, and clients can read them from tools/list before calling.

Tool Required scopes
publish publish:write
operation_status spaces:read
show_space The scopes required by the selected view
execute, resume_execution The called API operation checks the credential’s scopes

Your client never receives a Spacefast API token. Each authenticated request mints an internal delegation token that lives five minutes.

Hosted requests are rate limited to 600 per minute per credential and 300 per minute per IP.

Local

The local server takes its credential from the process, never from client config. sf mcp resolves your CLI login and hands it to the in-process server. Standalone runs read SPACEFAST_TOKEN.

Variable Effect
SPACEFAST_TOKEN Bearer token for API calls
SPACEFAST_API_URL API base URL
SPACEFAST_MCP_WORKSPACE_ROOT Pins the one directory publish may read from

The tools

Tool What it does Key inputs Returns
publish Uploads files and creates an immutable version path or files, spaceId, teamId, access Publish receipt and status
operation_status Checks a publish that is still running operationId Status, progress, diagnostics, done
show_space Opens a read-only Space view request.view, request.input The selected view and its data
execute Runs a JavaScript program against the API catalog code Program result or a pause for approval
resume_execution Continues a paused program executionId and the requested decision fields The continued program’s result

Every result is capped at 24,000 characters across the whole structured value.

publish

publish is synchronous by default. It waits for the version to finalize and returns the receipt.

Field Type What it does
path string Local runtime only. One local directory, file, or archive. Omit path and files to publish the current workspace
files array of 1 to 1000 Inline files. Required on hosted. Each is { path, content, encoding } with encoding one of utf8 or base64, default utf8
spaceId string starting spc_ Update this Space. Omit to create one, or on local to use the Space linked to this workspace
teamId string Destination team id or slug for a new Space. Send one after a team_required error
access private, team, or public Initial root access for a Space this publish creates. Invalid with spaceId
createNew boolean, default false Local runtime only. Forces a separate Space. Invalid with spaceId
slug string, 2 to 55 Slug for a new Space
title string, 1 to 255 Title for a new Space
description string, up to 2000 Changelog for this version. It does not change the Space description
requestId string, 16 to 200 Idempotency handle. Reuse the same value only when retrying a call that timed out
{
  "path": "dist",
  "description": "Fix the pricing table"
}

The result carries status of publishing or published, liveUrl, immutableUrl, and a receipt with the Space, the version, and any diagnostics. When status is publishing, result.job.poll.operationId is the handle for operation_status.

operation_status

Field Type What it does
operationId string, required Copy publish.result.job.poll.operationId
{ "operationId": "opr_7f2b8c1d4e5a6b7c" }

Poll about every two seconds. Stop when result.done is true. Report success only when result.status is succeeded.

show_space

Choose a view, then provide the input that view requires. Detailed views require a claimed Space.

View What it shows
deployments Deployment history and build status
domains Domains, DNS records, and TLS status
analytics Traffic over the selected window
source_files Repository files and folders
source_changes Pending source workspace changes
source_history Repository commits
source_comparison Differences between source commits
deployment_comparison Differences between published versions
build_logs Logs from one build
{
  "request": {
    "view": "deployments",
    "input": { "space": "docs.example.com" }
  }
}

execute

Field Type What it does
code string, required One complete JavaScript program

resume_execution

The input depends on where the approval decision is made.

Field Type What it does
executionId string, required The id from the paused execute result
{ "executionId": "exe_9c3f1b7a20d4" }

The execute sandbox

execute runs your program in QuickJS inside a worker. The whole Spacefast OpenAPI catalog is mounted as a lazy tools proxy.

Limit Value
Runtime 30 seconds
Memory 64 MiB
Stack 1 MiB
Network No fetch. Only tools.*

Find the Space, then publish to it

execute is not the publish path. Use it to resolve which Space you want, then call publish with the id.

const page = await tools.search({ query: "list spaces", limit: 10 });
const match = page.items.find((item) => item.path.endsWith(".spaces.listSpaces"));
if (!match) return { error: "No matching list-spaces operation." };

const shape = await tools.describe.tool({ path: match.path });
if (shape.error) return { error: shape.error };

const result = await tools[match.path]({});
if (!result.ok) return { error: result.error };

const spaces = result.data?.data ?? result.data;
return spaces.map((space) => ({ id: space.id, slug: space.slug }));

Then, with the id in hand:

{ "spaceId": "spc_4a91c0de2f3b", "path": "dist" }

Add a domain, then watch it

One program creates the domain and binds it to the Space. Use show_space with the domains view to inspect DNS and TLS afterward.

const found = await tools.search({ query: "create domain for space", limit: 10 });
const create = found.items.find((item) => item.path.endsWith(".domains.createDomain"));
if (!create) return { error: "No matching create-domain operation." };

const shape = await tools.describe.tool({ path: create.path });
if (shape.error) return { error: shape.error };

const added = await tools[create.path]({
  body: { name: "docs.example.com", teamId: "tm_2b7d40a9" },
});
if (!added.ok) return { error: added.error };
const domain = added.data?.data ?? added.data;

const page = await tools.search({ query: "assign domain to space", limit: 10 });
const bind = page.items.find((item) => item.path.endsWith(".domains.updateDomain"));
if (!bind) return { error: "No matching update-domain operation." };

const bound = await tools[bind.path]({
  path: { domainId: domain.id },
  body: { spaceId: "spc_4a91c0de2f3b", primary: true },
});
return bound.ok ? { domainId: domain.id, name: domain.name } : { error: bound.error };

Then watch the DNS and TLS state with a read tool until it settles.

{ "space": "spc_4a91c0de2f3b" }

Approvals

A write inside an execute program can pause before its HTTP request goes out.

The program pauses

execute returns status: "user_approval_required" with an executionId, an approvalUrl, and a resumePrompt.

You review it in the browser

The approvalUrl opens the approvals page in the dashboard.

The agent resumes

The agent calls resume_execution with the same executionId. That call blocks until your decision lands, then the saved program continues from where it stopped.

Local file access

Only the local runtime reads files, and it reads them from exactly one directory.

The workspace root is resolved in this order.

  1. SPACEFAST_MCP_WORKSPACE_ROOT, or the --workspace-root flag on sf mcp.
  2. The first file: root your MCP client advertises.
  3. The directory the server process started in.

Setting the environment variable turns off root discovery. The variable wins and your client’s advertised root is ignored.

Some files never upload, at any depth. .env, .git, node_modules, .ssh, and other sensitive files are disallowed.