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:
htmlis 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_inis in seconds and must be between 60 seconds and 365 days.sandbox_modeacceptsstrictorrelaxed; 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:andhttps: - inline styles plus HTTPS stylesheets
- HTTPS/data fonts and HTTPS media
X-Content-Type-Options: nosniffReferrer-Policy: no-referrer- restrictive
Permissions-Policy
The app routes by Host header:
htmlship.comserves the landing pageapi.htmlship.comserves the API and landing assetsview.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_htmlfetch_htmlupdate_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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f4c0c48d84dd6cf067ddd60a1e69d8c5156caf4bfd8b86b91f5c8e3816979f07
|
|
| MD5 |
f5b6acc49dc8c15ed5e1bce60d914a6f
|
|
| BLAKE2b-256 |
de28015924d50a84e2bf55ca74203d057229c7bee52a5fe64709e92a631d43d9
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
884f80f3fbe1b085524737da5d68ffad91182604d6879bd96ef974b07e0844d2
|
|
| MD5 |
db24ed5e2c1d8005655691c745672d41
|
|
| BLAKE2b-256 |
cc9e3029990a669dec1aa11a3fd39d194be37b836750952850d6c2f17cfecc3f
|