Skip to main content

Host and share HTML pages from LLMs and coding agents in one line.

Project description

HTMLShip

Host and share HTML pages from LLMs and coding agents in one line.

HTMLShip has three surfaces:

  • a public Python library and CLI (htmlship)
  • a FastAPI service for creating, updating, deleting, and viewing HTML pastes
  • a stdio MCP server (htmlship-mcp) for agent clients
pip install htmlship
import htmlship

paste = htmlship.publish("<h1>Hello</h1>", title="Demo", expires_in=3600)
print(paste.url)
print(paste.owner_key)  # save this to update or delete the paste later
curl -X POST https://api.htmlship.com/api/v1/pastes \
  -H "Content-Type: application/json" \
  -d '{"html":"<h1>Hello</h1>","title":"Demo"}'

See htmlship-implementation-spec.md for the product spec and DEPLOY.md for the production runbook.

Python Library

The module-level helpers use https://api.htmlship.com by default. Override with HTMLSHIP_API_URL or htmlship.configure(base_url=...).

import htmlship

paste = htmlship.publish(
    "<h1>Hello</h1>",
    title="Demo",
    password="optional-password",
    expires_in=86400,
)

fresh = htmlship.get(paste.slug)
updated = htmlship.update(paste.slug, "<h1>Updated</h1>", owner_key=paste.owner_key)
htmlship.delete(updated.slug, owner_key=paste.owner_key)

owner_key is returned only when a paste is created. It is required for updates and deletes, and the API does not return it again from metadata calls.

CLI

htmlship publish report.html
cat report.html | htmlship publish -
htmlship publish --file report.html --title "Q4 Report" --expires-in 3600

htmlship get <slug>
htmlship update <slug> updated.html
htmlship delete <slug>
htmlship list-mine

The CLI stores owner keys in ~/.htmlship/keys.json so update, delete, and list-mine can work with pastes you created locally. Set HTMLSHIP_KEYS_DIR to use another key-store directory, and set HTMLSHIP_API_URL to point the CLI at a local or staging API.

API

Base URL: https://api.htmlship.com.

Method Path Description
GET /health Health check with service version.
GET /version Service version.
POST /api/v1/pastes Create a paste.
GET /api/v1/pastes/{slug} Fetch paste metadata.
PATCH /api/v1/pastes/{slug} Replace HTML or title. Requires X-Owner-Key.
DELETE /api/v1/pastes/{slug} Soft-delete a paste. Requires X-Owner-Key.
POST /api/v1/pastes/{slug}/version Create a new paste linked to an existing parent slug.

Create payload:

{
  "html": "<h1>Hello</h1>",
  "title": "Optional title",
  "password": "optional password",
  "expires_in": 3600,
  "parent_slug": "optional-parent",
  "sandbox_mode": "strict"
}

Notes:

  • html is stored and served verbatim. Scripts are blocked by the view CSP, not by sanitizing the body.
  • Payloads are limited to 10 MiB by default.
  • expires_in is in seconds and must be between 60 seconds and 365 days.
  • sandbox_mode accepts strict or relaxed; the current view headers use the strict CSP.
  • Password-protected views set a signed, HTTP-only session cookie after the correct password is submitted.

Rendering

Rendered HTML is served from view.htmlship.com/{slug} with strict security headers:

  • Content-Security-Policy: default-src 'none'
  • images from data: and https:
  • inline styles plus HTTPS stylesheets
  • HTTPS/data fonts and HTTPS media
  • X-Content-Type-Options: nosniff
  • Referrer-Policy: no-referrer
  • restrictive Permissions-Policy

The app routes by Host header:

  • htmlship.com serves the landing page
  • api.htmlship.com serves the API and landing assets
  • view.htmlship.com/{slug} serves sandboxed HTML

For local development without DNS, append ?_host=view.htmlship.com (or your configured view host) to spoof the host header, for example:

curl "http://localhost:8000/<slug>?_host=view.htmlship.com"

MCP Server

htmlship-mcp is a stdio MCP server exposing three tools:

  • publish_html
  • fetch_html
  • update_html

All HTTP traffic goes through the public Python client. Configure the endpoint with HTMLSHIP_API_URL.

Claude Desktop

Edit ~/Library/Application Support/Claude/claude_desktop_config.json on macOS or %APPDATA%\Claude\claude_desktop_config.json on Windows:

{
  "mcpServers": {
    "htmlship": {
      "command": "htmlship-mcp",
      "env": {
        "HTMLSHIP_API_URL": "https://api.htmlship.com"
      }
    }
  }
}

Restart Claude Desktop. Then ask: publish this HTML: <h1>test</h1>.

Claude Code

Edit ~/.claude.json or use claude mcp add:

{
  "mcpServers": {
    "htmlship": {
      "type": "stdio",
      "command": "htmlship-mcp",
      "env": {
        "HTMLSHIP_API_URL": "https://api.htmlship.com"
      }
    }
  }
}

Local Development

Requirements:

  • Python 3.12+
  • uv
  • Docker
# 1. Install dependencies (creates .venv)
uv sync --extra server --extra cli --extra mcp --extra dev

# 2. Start Postgres on localhost:5433
docker compose up -d postgres

# 3. Copy env and edit if needed
cp .env.example .env

# 4. Run migrations
uv run alembic upgrade head

# 5. Start the server
uv run uvicorn htmlship_server.main:app --reload

# 6. Health check
curl http://localhost:8000/health

Run tests and linting:

uv run pytest
uv run ruff check .

The test suite uses SQLite and a temporary local blob store. Local development uses Postgres metadata plus ./tmp/blobs/ for HTML blobs unless SPACES_BUCKET is configured.

Configuration

The server reads .env via Pydantic settings.

Variable Default Purpose
DATABASE_URL postgresql+asyncpg://htmlship:htmlship@localhost:5433/htmlship Async SQLAlchemy database URL.
PUBLIC_BASE_DOMAIN htmlship.com Base domain used to derive host routing.
API_BASE_URL https://api.htmlship.com Public API URL setting.
VIEW_BASE_URL https://view.htmlship.com Public view URL used in paste responses.
LANDING_BASE_URL https://htmlship.com Public landing URL.
SPACES_BUCKET empty If empty, use local blob storage; otherwise use DigitalOcean Spaces/S3.
SPACES_REGION nyc3 Spaces/S3 region.
SPACES_ENDPOINT_URL https://nyc3.digitaloceanspaces.com Spaces/S3 endpoint.
SPACES_ACCESS_KEY / SPACES_SECRET_KEY empty Spaces/S3 credentials.
SECRET_KEY development placeholder Signs password-view session cookies. Use a strong value in production.
ENVIRONMENT development Enables API docs outside production and secure cookies in production.
LOG_LEVEL info Application log level.
MAX_PAYLOAD_BYTES 10485760 Server-side HTML size limit.
DEFAULT_EXPIRES_IN_SECONDS empty Optional default TTL for new pastes.

Architecture

One FastAPI process hosts the landing page, JSON API, and view renderer. HostRoutingMiddleware classifies requests by host and prevents API routes from being served on the view host.

Postgres stores paste metadata, owner-key/password hashes, expiry, view counts, and parent-version links. HTML bodies are stored as blobs, either in LocalBlobStore for development/tests or DigitalOcean Spaces in production.

Project Layout

src/htmlship/        Public Python library + CLI
src/htmlship_server/ FastAPI app, API routers, storage, database models
src/htmlship_mcp/    MCP server (stdio)
web/                 Static landing page
tests/               API, client, CLI, MCP, landing, and view tests
alembic/             Database migrations
deploy/              Production configs (nginx, systemd)
scripts/             Deploy and smoke-test scripts

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

htmlship-0.1.1.tar.gz (21.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

htmlship-0.1.1-py3-none-any.whl (27.6 kB view details)

Uploaded Python 3

File details

Details for the file htmlship-0.1.1.tar.gz.

File metadata

  • Download URL: htmlship-0.1.1.tar.gz
  • Upload date:
  • Size: 21.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for htmlship-0.1.1.tar.gz
Algorithm Hash digest
SHA256 f4c0c48d84dd6cf067ddd60a1e69d8c5156caf4bfd8b86b91f5c8e3816979f07
MD5 f5b6acc49dc8c15ed5e1bce60d914a6f
BLAKE2b-256 de28015924d50a84e2bf55ca74203d057229c7bee52a5fe64709e92a631d43d9

See more details on using hashes here.

File details

Details for the file htmlship-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: htmlship-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 27.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for htmlship-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 884f80f3fbe1b085524737da5d68ffad91182604d6879bd96ef974b07e0844d2
MD5 db24ed5e2c1d8005655691c745672d41
BLAKE2b-256 cc9e3029990a669dec1aa11a3fd39d194be37b836750952850d6c2f17cfecc3f

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page