# MCP server to publish a website and get a public URL

`https://chorus.host/mcp` is a remote MCP server. Add it to Claude Code, Codex, Cursor or claude.ai and your agent can publish HTML, a file or a whole static site with one `publish_site` call. It gets back a live `https://<name>.chorus.host` link and a claim link. No account, no install, no API key.

## At a glance

| | |
|---|---|
| Endpoint | `https://chorus.host/mcp` |
| Transport | Streamable HTTP, stateless |
| Auth | None. Optional `Authorization: Bearer chk_...` publishes into your account |
| Tools | `publish_site`, `get_site`, `get_docs` |
| Per call | 4 MB of files without a key, 10 MB with one; up to 1,000 files |
| Sites without an account | Live for 24 hours; a claim link keeps them |
| Publishes per hour | 5 per IP address without a key, 60 per account with one |
| Official MCP Registry name | `host.chorus/mcp` |
| Price | Free |

## Add it to your client

Checked 1 October 2026. The Claude Code and Codex commands were run against this server that day; the Cursor and VS Code entries follow each client's documented config format and were not run by us.

### Claude Code

```bash
claude mcp add --transport http chorus https://chorus.host/mcp
```

`claude mcp list` should show `chorus: https://chorus.host/mcp (HTTP) - ✔ Connected`.

### Codex CLI

```bash
codex mcp add chorus --url https://chorus.host/mcp
```

This writes an `[mcp_servers.chorus]` entry with the URL to `~/.codex/config.toml`. Codex's default `workspace-write` sandbox has no network, so approve the network request when Codex asks, or see [the Codex section of the Claude Code and Codex guide](/guides/publish-html-from-claude-code#codex-turn-on-network-access-first).

### opencode

In `opencode.json`:

```json
{ "mcp": { "chorus": { "type": "remote", "url": "https://chorus.host/mcp" } } }
```

### Cursor

In `~/.cursor/mcp.json` (all projects) or `.cursor/mcp.json` (one project):

```json
{ "mcpServers": { "chorus": { "url": "https://chorus.host/mcp" } } }
```

### VS Code

In `.vscode/mcp.json`:

```json
{ "servers": { "chorus": { "type": "http", "url": "https://chorus.host/mcp" } } }
```

### claude.ai and Claude Desktop

Customize, then Connectors, then **+** and **Add custom connector**. Name it chorus.host and paste `https://chorus.host/mcp` as the URL. There's no OAuth step. The Free plan allows one custom connector.

### ChatGPT

ChatGPT can only use MCP tools that write (publishing is a write) in Business and Enterprise or Edu workspaces, on the web, through developer mode (Settings, Apps, Advanced settings). On Pro, developer mode connectors can read and fetch but not write, according to OpenAI's help center on 1 October 2026. The article doesn't mention Plus, so expect the same there unless you test otherwise. On Pro or Plus, download the HTML ChatGPT made and drop it on [chorus.host/html-to-url](/html-to-url) instead.

A note on shared addresses: claude.ai and ChatGPT call MCP servers from their own servers, so their requests share a small number of IP addresses. Publishing without a key is limited to 5 per hour per IP address, and that budget can run out on a shared address. If you see a rate-limit error there, add your API key as an `Authorization: Bearer chk_...` header (60 an hour per account).

## What your agent gets

This reference is generated from the tool definitions the server registers, so it matches what `tools/list` returns.

### `publish_site`: Publish website

Publish a website or any file (PDF, image, HTML, CSS/JS) to a live public HTTPS URL at <slug>.chorus.host in one call. Files keep their content type, so PDFs and images open in the browser.

| Argument | Type | Required | What it is |
|---|---|---|---|
| `claim_token` | string | no | The claim_token (ctk_...) returned when the site was first published. Required to update an anonymous site. |
| `expires_at` | string | no | Optional. When the site should be deleted: RFC 3339 (2026-10-08T00:00:00Z) or a duration from now (90m, 2h, 7d). |
| `files` | array of object | yes | Every file of the site. Each call makes a new version with exactly these files; earlier versions are kept. |
| `password` | object | no | Optional. Put the site behind a browser login prompt (HTTP Basic Auth). |
| `slug` | string | no | Optional. Subdomain to publish to (3-63 lowercase letters, digits and hyphens). |
| `title` | string | no | Optional human-readable title saved with the site. |

Returns: `anonymous`, `claim_token`, `claim_url`, `expires_at`, `file_count`, `file_url`, `next_steps`, `page_url`, `password_protected`, `slug`, `tell_the_user`, `total_bytes`, `updated`, `url`, `version_id`, `warnings`.

Annotations: not destructive (earlier versions are kept).

### `get_site`: Check site status

Check a chorus.host site: whether it is live, its URL, whether it has a password, and when it expires. With the site's claim_token (or an API key that owns the site) it also returns the file list, total size, live version and claim link.

| Argument | Type | Required | What it is |
|---|---|---|---|
| `claim_token` | string | no | Optional claim_token (ctk_...) of the site. Unlocks the file list and other details. |
| `slug` | string | yes | The site's slug, i.e. the subdomain in <slug>.chorus.host. |

Returns: `can_manage`, `claim_url`, `claimed`, `created_at`, `current_version_id`, `expires_at`, `file_count`, `files`, `files_truncated`, `password_protected`, `slug`, `status`, `summary`, `title`, `total_bytes`, `updated_at`, `url`.

Annotations: read-only.

### `get_docs`: Read chorus.host docs

Return the full chorus.host documentation as Markdown (the same text as https://chorus.host/skill.md): the REST API, the beacon CLI, accounts and API keys, claiming sites, password protection, and Workers, which run server-side JavaScript at <slug>.worker.chorus.host for APIs and webhook receivers (GitHub, Stripe), including Hono apps, with no Cloudflare account needed. Read it before building a backend, when the user asks about accounts or limits, or when a task needs something publish_site can't do.

Annotations: read-only.

## Try it without a client

MCP is JSON over HTTP, so curl works. This publishes one page and prints the live URL:

```bash
jq -n --rawfile html index.html \
  '{jsonrpc:"2.0",id:1,method:"tools/call",params:{name:"publish_site",arguments:{files:[{path:"index.html",content:$html}]}}}' \
  | curl -sS https://chorus.host/mcp -H 'Content-Type: application/json' \
      -H 'Accept: application/json, text/event-stream' -d @- \
  | jq -r .result.structuredContent.url
```

The full result has `url`, `page_url`, `file_url`, `claim_url`, `claim_token`, `expires_at` and a `tell_the_user` message. If you only want curl, the plain HTTP endpoint is shorter: `curl -sS https://chorus.host/v1/publish -F file=@index.html`.

## Keep it, update it, lock it

- **Keep it.** Give the person the `claim_url`. They open it, enter their email and the 6-digit code they get, and the site is theirs with no expiry.
- **Same URL, new content.** Call `publish_site` again with the same `slug` and the `claim_token` from the first call. Each call makes a new version live with exactly the files you send; earlier versions are kept and can be rolled back over the REST API.
- **Password.** Pass `password: {username, password}` and visitors get the browser's login prompt. It works without an account. See [password-protect an HTML page](/guides/password-protect-html-page).
- **Expiry.** Pass `expires_at` (`2h`, `7d` or an RFC 3339 time). Without a key it can only be sooner than 24 hours. See [temporary website hosting](/guides/temporary-website-hosting).
- **Publish into your account.** Send your API key as a header. Claude Code: `claude mcp add --transport http chorus https://chorus.host/mcp --header "Authorization: Bearer chk_..."`. Codex: `codex mcp add chorus --url https://chorus.host/mcp --bearer-token-env-var CHORUS_API_KEY`.
- **More than 4 MB.** Use [publish.py or the beacon CLI](/guides/host-static-site-no-signup#pick-a-way-to-publish), which upload straight to storage (250 MB per file without an account).
- **A backend.** APIs and webhook receivers run as [chorus.host Workers](/workers). They are deployed over the REST API or the CLI with a free account, not through an MCP tool today; `get_docs` returns the instructions.

## Which MCP server should you use?

Checked 1 October 2026 from each server's `tools/list` and public docs. Blank cells mean we couldn't confirm the answer, not that the feature is missing.

| | chorus.host | ShipStatic | Just Publish |
|---|---|---|---|
| Publish with only the URL configured | Yes | Yes | The deploy tool asks for an email address |
| Lifetime without an account | 24 hours | 3 days | |
| Password from the tool | Yes | Yes | |
| Sign in from cloud clients | API key header | OAuth | |
| Custom domains | No | Yes | |
| Change one file without resending the site | No, full replace | | Yes (`update_site_file`) |
| Pick the subdomain | Yes | | |
| Server-side code | Workers, over REST or CLI | | |

Where they're better: ShipStatic keeps anonymous sites for 3 days instead of 24 hours, signs in over OAuth and supports custom domains. Just Publish can edit one file of a live site without sending the rest. [here.now](/vs/here-now) has no public MCP server; its agent card says MCP isn't supported.

## Want to host your own MCP server?

This page is about an MCP server that publishes websites. If you want to run your own MCP server on the web, Cloudflare's guide to remote MCP servers on Workers is the place to start.

## Questions

### Is there an MCP server that publishes a website and returns a public URL?

Yes. Add `https://chorus.host/mcp` to your MCP client. Its `publish_site` tool takes the files and returns a live `https://<name>.chorus.host` URL, plus a claim link so a person can keep the site.

### Do I need an account or an API key?

No. Without one, sites stay up for 24 hours and come with a claim link. With an API key in the `Authorization` header, sites go into your account and don't expire.

### Does it work in ChatGPT?

Publishing needs a ChatGPT Business, Enterprise or Edu workspace with developer mode, on the web. On Pro, developer mode connectors are read and fetch only (OpenAI's help center doesn't cover Plus), so on Pro or Plus drag the HTML file onto chorus.host/html-to-url instead.

### How long do sites stay up?

24 hours without an account, unless someone opens the claim link and signs in. Sites in an account stay up until you delete them, or until an `expires_at` you set.

### How big can the site be?

4 MB of files per call without a key and 10 MB with one, up to 1,000 files. Bigger sites go through publish.py, the beacon CLI or the REST API: 250 MB per file without an account, 5 GB with one.

### Can the agent update the same URL later?

Yes. Call `publish_site` again with the same `slug` and the `claim_token` it got the first time (or with your API key for sites in your account). The URL stays the same and the old version is kept.
