spacesheep

Documentation

Deploy and share sites instantly from Claude Code.

Setup

Connect spacesheep to any MCP-compatible client. The server URL is always https://mcp.spacesheep.dev/mcp.

Claude Code / Cursor / Windsurf (API key)

Add to your MCP config (.mcp.json or ~/.claude/claude_desktop_config.json):

{
  "mcpServers": {
    "spacesheep": {
      "type": "url",
      "url": "https://mcp.spacesheep.dev/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_API_KEY"
      }
    }
  }
}

Get your API key from the dashboard (claim a username first).

ChatGPT / Claude.ai / other OAuth clients

Clients that support MCP with OAuth connect automatically — just add the server URL:

https://mcp.spacesheep.dev/mcp

The client discovers the OAuth endpoints, redirects you to log in via Cloudflare Access (Google or email OTP), and you authorize the connection. No API key needed — one is minted automatically.

OAuth uses the standard Authorization Code flow with PKCE. The authorization server metadata is at https://mcp.spacesheep.dev/.well-known/oauth-authorization-server.

CLI login

If you installed the /sheep skill, run:

npx spacesheep-skill

It starts a device flow — approve in browser, and the CLI gets an API key automatically.

MCP Tools

ToolDescription
deployDeploy files as a static site or Worker app. Pass files[], optional title, uuid (to update), and access: { visibility, emails[] } (visibility: public / members / private). Include worker.js for dynamic mode.
list_spacesList all spaces you own or have access to.
get_spaceGet metadata and access list. Accepts UUID or URL.
read_spaceRead deployed file contents. Accepts UUID or URL. Optional path for a specific file.
share_spaceGrant access by email. Owner only.
delete_spaceDelete a space and all files. Owner only.
list_commentsRead all comments on a space — threaded, with anchors, reactions, and resolve state.
add_commentComment or reply. Posts under the Claude bot identity. Optional anchor_id pins it to an element.
react_to_commentToggle an emoji reaction on a comment.
resolve_commentResolve or reopen a comment thread.
create_orgCreate a new organization. Caller becomes owner. Org dashboard at <slug>.spacesheep.dev.
set_secretSet a personal or org secret. Optionally restrict allowed URLs. Available in workers via env.secrets.get(name).
list_secretsList secret names (never values). Personal or org scope.
delete_secretDelete a personal or org secret.

Deploy a site

In Claude Code, just ask to deploy:

"Deploy this as a spacesheep site"
"Upload index.html and style.css to spacesheep"

Or use the tool directly:

deploy({
  files: [
    { path: "index.html", content: "<h1>Hello</h1>" },
    { path: "style.css", content: "body { color: red; }" }
  ],
  title: "My Site",
  access: { visibility: "members", emails: ["friend@example.com"] }
})

Access control

Every space has one of three visibility tiers. By default a space is private. Set access.visibility on deploy:

Use the share_space tool to add emails or change the tier after deploy. Visibility can be set on create or update (pass uuid to update). A worker space can't be open — it needs a session.

Worker mode (dynamic apps)

To deploy a dynamic app with server-side logic and database access, include a worker.js file. Spacesheep detects it automatically and deploys a live Cloudflare Worker.

// worker.js — ES module format
export default {
  async fetch(request, env) {
    const url = new URL(request.url);

    if (url.pathname === "api/items") {
      await env.D1.exec("CREATE TABLE IF NOT EXISTS items (id INTEGER PRIMARY KEY, name TEXT)");
      if (request.method === "POST") {
        const { name } = await request.json();
        await env.D1.prepare("INSERT INTO items (name) VALUES (?)").bind(name).run();
        return Response.json({ ok: true });
      }
      const rows = await env.D1.prepare("SELECT * FROM items").all();
      return Response.json(rows.results);
    }

    return new Response("<h1>My App</h1>", {
      headers: { "Content-Type": "text/html" }
    });
  }
};

Database migrations: Include a schema.sql file — it runs against D1 on every deploy. Write idempotent SQL:

-- schema.sql
CREATE TABLE IF NOT EXISTS items (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  name TEXT NOT NULL,
  created_at TEXT NOT NULL DEFAULT (datetime('now'))
);

What your Worker gets:

Important: Use relative URLs in frontend code (fetch("api/data") not fetch("/api/data")). Your Worker's paths are relative to the space URL.

Secrets

Workers can use API keys and credentials without exposing them to the frontend. Secrets are set via MCP tools and accessed in worker code through a safe API.

Setting secrets

// Personal secret — available to all your worker spaces
set_secret({ name: "OPENAI_KEY", value: "sk-..." })

// Org secret — shared across all spaces in the org
set_secret({ name: "OPENAI_KEY", value: "sk-...", scope: "org", org: "my-team" })

// Restrict where the secret can be sent
set_secret({
  name: "OPENAI_KEY",
  value: "sk-...",
  allowed_urls: ["https://api.openai.com/"]
})

Using secrets in worker code

export default {
  async fetch(request, env) {
    const key = await env.secrets.get("OPENAI_KEY");
    const allNames = await env.secrets.list();

    // Use in API calls — safe, stays server-side
    const resp = await fetch("https://api.openai.com/v1/chat/completions", {
      headers: { "Authorization": "Bearer " + key },
      // ...
    });
  }
};

Safety layers

Sharing

Spaces are private by default. Share with specific people:

share_space({
  uuid: "abc-123...",
  emails: ["friend@example.com"]
})

Viewers authenticate via Cloudflare Access (Google login or email OTP).

Reading content

Paste a spacesheep URL and ask Claude to read it:

read_space({ uuid: "https://spacesheep.dev/@misha/quarterly-report" })

URLs

Spaces live under their owner's username: spacesheep.dev/@username/<title-slug>. The slug is generated from the space title; renaming a space updates the slug and old links redirect permanently. Claim your username from the dashboard.

Legacy UUID links (/space/<uuid>) keep working — they redirect to the pretty URL once the owner has a username. All MCP tools accept either form.

Comments & feedback

Every space has a real-time comment sidebar. Viewers can:

Commented elements show a 💬 count badge in the document and quoted passages get a subtle highlight, so open feedback is visible in place. Clicking a badge jumps to the thread. Resolving a thread clears its markers. Everything syncs live across viewers over WebSockets.

Close the loop with Claude: the MCP tools read and write the same comments. Claude replies post under a distinct bot identity — shown as Claude with an AI chip, attributed to the API key owner.

list_comments({ uuid })          // read feedback
add_comment({ uuid, body, parent_id?, anchor_id? })
react_to_comment({ uuid, comment_id, emoji })
resolve_comment({ uuid, comment_id })

Documents generated with the /sheep skill include data-ss-id anchors on every content block, so comments survive redeploys as long as those IDs stay stable.

The /sheep skill

Spacesheep includes a Claude Code skill called /sheep that generates publication-quality documents following perception science best practices — optimized typography, information hierarchy, data visualization, and perception-optimized light theme.

Usage in Claude Code:

/sheep quarterly metrics report from this data...
/sheep architecture overview of the payment system
/sheep comparison of React vs Svelte for our use case

The skill builds a self-contained HTML document and deploys it to spacesheep automatically. Documents follow principles from Tufte, perceptual psychology, and modern web typography:

Installing the skill

One-line install:

curl -fsSL https://spacesheep.app/install.sh | bash

Or via npm:

npx spacesheep-skill

Install globally (available in all projects):

curl -fsSL https://spacesheep.app/install.sh | bash -s -- -g
npx spacesheep-skill -g

The installer downloads the skill and optionally configures the MCP server.

Telegram bot

Spacesheep has one Telegram bot — @TheSpaceSheepBot — that acts as an agent across your spaces and orgs. DM it questions or actions (“list my spaces”, “deploy this”, “share that with…”, “remind me to…”); it runs the same MCP tools and uses your account’s LLM key. There’s nothing to install and no per-org bot to create — the same bot switches accounts depending on where you use it.

1. Link your account

Open a direct message with @TheSpaceSheepBot and send:

/start

Tap the link it replies with to connect your Spacesheep account — one tap, done. (Alternatively, grab a one-time code from Settings → Telegram and send /start <code>.) Once linked, anything you DM the bot acts on your personal account.

2. Use it in a group / for an org

Add @TheSpaceSheepBot to a Telegram group, then bind that group to whichever account it should act on:

After you bind to an org, everything the bot does in that group runs on the org — its spaces, its secrets and LLM key, its permissions — not your personal account. Mention @TheSpaceSheepBot or reply to one of its messages to ask it something.

Heads up: the <org-slug> is the org’s slug (the <slug> in its <slug>.spacesheep.dev URL), not its display name. You must be a member of the org to bind to it. For /summary and group awareness the bot needs to read every message — a group admin disables privacy mode once in @BotFather (/setprivacy → Disable).

Commands

CommandWhereWhat it does
/startDMLink your account (or /start <code> with a code from Settings)
/bind <org-slug>GroupBind the group to an org (omit the slug to bind to your personal account)
/unbindGroupClear the group’s binding
/summaryDM & groupRecap the recent conversation
/unlinkDMDisconnect and revoke the bot’s API key
/helpDM & groupShow the command list

Architecture

User content is served on a separate origin (spacesheep.app) so it cannot access spacesheep.dev cookies, storage, or DOM. The iframe sandbox attribute provides defense in depth.

Security