Deploy and share sites instantly from Claude Code.
Connect spacesheep to any MCP-compatible client. The server URL is always https://mcp.spacesheep.dev/mcp.
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).
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.
https://mcp.spacesheep.dev/.well-known/oauth-authorization-server.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.
| Tool | Description |
|---|---|
deploy | Deploy 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_spaces | List all spaces you own or have access to. |
get_space | Get metadata and access list. Accepts UUID or URL. |
read_space | Read deployed file contents. Accepts UUID or URL. Optional path for a specific file. |
share_space | Grant access by email. Owner only. |
delete_space | Delete a space and all files. Owner only. |
list_comments | Read all comments on a space — threaded, with anchors, reactions, and resolve state. |
add_comment | Comment or reply. Posts under the Claude bot identity. Optional anchor_id pins it to an element. |
react_to_comment | Toggle an emoji reaction on a comment. |
resolve_comment | Resolve or reopen a comment thread. |
create_org | Create a new organization. Caller becomes owner. Org dashboard at <slug>.spacesheep.dev. |
set_secret | Set a personal or org secret. Optionally restrict allowed URLs. Available in workers via env.secrets.get(name). |
list_secrets | List secret names (never values). Personal or org scope. |
delete_secret | Delete a personal or org secret. |
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"] }
})
Every space has one of three visibility tiers. By default a space is private. Set access.visibility on deploy:
access: { visibility: "public" } — anyone with the link, no sign-in (static spaces only)access: { visibility: "members" } — invited members only (in an org, org members); members can inviteaccess: { visibility: "private" } — you plus people you invite (the default)access: { emails: ["a@b.com"] } — invite specific people (combine with any tier)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.
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:
env.D1 — a D1 database binding for SQL queriesschema.sql — auto-executed on deploy for migrationsfetch, crypto, Request/Response, WebSocket supportfetch("api/data") not fetch("/api/data")). Your Worker's paths are relative to the space URL.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.
// 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/"]
})
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 },
// ...
});
}
};
env directly. JSON.stringify(env) and Object.keys(env) won't reveal them. Access only via env.secrets.get(name).allowed_urls, outbound fetch() calls are inspected. Requests containing the secret sent to non-allowed URLs are blocked (403).[REDACTED] before reaching the browser.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).
Paste a spacesheep URL and ask Claude to read it:
read_space({ uuid: "https://spacesheep.dev/@misha/quarterly-report" })
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.
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.
/sheep skillSpacesheep 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:
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.
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.
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.
Add @TheSpaceSheepBot to a Telegram group, then bind that group to whichever account it should act on:
/bind <org-slug> — act on an organization you belong to. For example /bind sheep./bind — act on your own personal account./unbind — clear the binding.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.
<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).| Command | Where | What it does |
|---|---|---|
/start | DM | Link your account (or /start <code> with a code from Settings) |
/bind <org-slug> | Group | Bind the group to an org (omit the slug to bind to your personal account) |
/unbind | Group | Clear the group’s binding |
/summary | DM & group | Recap the recent conversation |
/unlink | DM | Disconnect and revoke the bot’s API key |
/help | DM & group | Show the command list |