Skip to main content
Glama
thinhost-mcp

thinhost-mcp

Official
by thinhost

thinhost-mcp

The thin.host MCP server: publish static sites (Docusaurus, Astro, Vite, Next export, hand-written HTML), provision custom domains, and send transactional email — from Claude Code, Claude Desktop, or any MCP client.

Prefer nothing to install? The same tools are hosted at https://thin.host/mcp (OAuth or a bearer API key): claude mcp add --transport http thin-host https://thin.host/mcp.

Claude Code plugin

This repo is also a Claude Code plugin marketplace. The thin-host plugin connects the hosted server (OAuth sign-in) and adds the skills in skills/:

/plugin marketplace add thinhost/thinhost-mcp
/plugin install thin-host@thinhost

Setup for Cursor, VS Code, Windsurf, Codex and ChatGPT: https://thin.host/docs/quickstart-mcp

Related MCP server: seite

Install (local server, reads your build folder directly)

Create an API key in the thin.host dashboard (Settings → Create API key), then:

claude mcp add thin-host \
  -e THINHOST_API_URL=https://thin.host/v1 \
  -e THIN_HOST_API_KEY=th_live_your_key \
  -- uvx thinhost-mcp

Claude Desktop (claude_desktop_config.json):

{{ "mcpServers": {{ "thin-host": {{ "command": "uvx", "args": ["thinhost-mcp"],
  "env": {{ "THINHOST_API_URL": "https://thin.host/v1", "THIN_HOST_API_KEY": "th_live_your_key" }} }} }} }}

Tools

Tool

What it does

publish_website

Publish an HTML file, folder, ZIP or static build folder as a live site (big builds batched automatically)

update_website

Push changes; sync=true prunes files the new build no longer ships

publish_website_from_url / update_website_from_url

Same, from a public .zip/.html URL

list_websites

List the account's hosted sites

provision_domain / check_domain_status / update_origin / release_domain / list_domains

Custom domains with a hosted claim + DNS flow

send_email

Transactional email (allowlist + verified sending domain required)

Docusaurus users: see the recipe and the drop-in Claude skill in skills/thin-host-docusaurus.

Development

This repo is exported from the thin.host application repo (scripts/export_mcp_package.py there) so the package can never drift from the deployed server; edit it there, re-export, and push. Releases are built by GitHub Actions and published to PyPI with trusted publishing when a v* tag is pushed.

Available Tools

11 tools
check_domain_statusA

Check the current provisioning status of a domain.

Call this to poll whether a domain is ready. Typical flow:

  1. provision_domain → returns pending_payment

  2. User visits claim_url and pays → status becomes pending_dns

  3. User configures DNS → status becomes pending_ssl

  4. SSL auto-provisions → status becomes active, ready=true

Args: provision_id: The provision_id returned by provision_domain

Returns: status: Current status enum (pending_payment, pending_dns, pending_ssl, active, failed, released) dns_status: Whether DNS is resolving (resolved, unresolved) ssl_status: SSL certificate state (pending, active, failed) payment_status: Whether the user has paid (paid, unpaid, unknown) ready: Boolean — true only when domain is fully active with SSL failure_reason: Present only if status is "failed", explains what went wrong

Inputs: provision_id (required).

list_domainsA

List all domains provisioned through your platform account.

Use this to build dashboards or check the state of all domains.

Args: status_filter: Optional filter by status (pending_payment, pending_dns, pending_ssl, active, failed, released) limit: Maximum number of domains to return (default 50, max 100)

Returns: domains: Array of domain objects with id, hostname, status, ready fields total: Total number of matching domains has_more: Whether more results exist beyond this page

Inputs: limit, status_filter.

list_websitesB

List the account's hosted websites with their live URLs, newest first.

Args: limit: Max sites to return (default 50, max 100).

Inputs: limit.

provision_domainA

Provision a new custom domain for an AI-generated site or app.

Call this when a user wants their deployed site accessible at a custom domain. This creates the domain mapping and returns a claim_url where the user completes payment ($8/mo) and DNS setup. The domain is NOT immediately active — the user must visit the claim_url first.

Args: domain: The custom domain to provision (e.g., "mysite.com", "portfolio.dev") origin_url: The current URL where the site is hosted (e.g., "https://myapp.vercel.app") user_email: The end user's email address for billing and notifications idempotency_key: Optional key to prevent duplicate provisions on retry

Returns: provision_id: Unique ID to track this domain (use with check_domain_status) status: Current provisioning status enum (pending_payment, pending_dns, pending_ssl, active, failed, released) dns_instructions: Structured DNS record the user needs to create, plus a human_readable summary claim_url: URL to send the user to for payment and DNS setup walkthrough expected_completion_seconds: Estimated time for full provisioning after payment

Inputs: domain (required), origin_url (required), user_email (required), idempotency_key.

publish_websiteA

Publish a local HTML file, directory, ZIP, or static build folder as a live site on thin.host.

Use this right after generating a site so the user gets a live, shareable URL instead of loose files they would have to upload somewhere themselves. Works for hand-written sites and for generator output — point it at a Docusaurus build/, an Astro dist/, a Vite dist/, or a Next out/ folder. HTML files become pages (index.html -> /, about.html -> /about, docs/intro/index.html -> /docs/intro, 404.html -> the site's own not-found page); css/js/images/fonts become assets served relative to the site root. Large builds are uploaded in several requests automatically.

Docusaurus: the response reports the build's base_url. A build made with baseUrl '/' only renders correctly once a custom domain is mapped to the site; to preview at the returned thin.host URL, build with baseUrl '/s//' (pass that slug here). Any mismatch comes back as a warning — surface it to the user.

The account's API key must be created from the thin.host dashboard (Settings → Create API key) so it is linked to a user account — error_code "account_not_linked" means it isn't; surface that guidance rather than retrying.

Args: source_path: Absolute path to an .html file, a site/build directory, or a .zip. title: Human-readable site title (shown in the dashboard and emails). slug: Optional URL slug (site lives at /s//). Auto-generated if omitted. "slug_taken" means it's in use — pick another or omit.

Returns: url: The live site URL — share this with the user. id, slug, title, page_count, asset_count, pages, assets: What was published. framework, base_url: Detected generator (e.g. "docusaurus") and its baseUrl. warning: Present when the build's baseUrl won't resolve at the returned url. skipped_files: Local files ignored because their type isn't supported.

Inputs: title (required), source_path (required), slug.

publish_website_from_urlA

Publish a site from a URL — a .zip of the site/build folder, or a single HTML page.

The hosted thin.host MCP endpoint cannot read your disk, so this is its publish path: zip the build folder (cd build && zip -r ../site.zip .), put the zip somewhere publicly reachable (a release asset, object storage, a slim.to link), and pass that URL. Everything else — page/asset mapping, batching, Docusaurus baseUrl detection and the warning it may produce — matches publish_website. Private, loopback and cloud-metadata addresses are refused.

Args: source_url: Public http(s) URL of a .zip archive or an .html file (≤150MB). title: Human-readable site title. slug: Optional URL slug (site lives at /s//).

Returns: Same shape as publish_website.

Inputs: title (required), source_url (required), slug.

release_domainA

Release a provisioned domain, removing it from thin.host.

This tears down the domain mapping, SSL certificate, and cancels billing. The domain becomes available for re-provisioning. This action is not reversible.

Args: provision_id: The provision_id of the domain to release

Returns: status: "released" released_at: Timestamp of the release

Inputs: provision_id (required).

send_emailA

Send a single transactional notification email (e.g. a sign-in link, receipt, or alert).

PRECONDITION — this only works for accounts thin.host has approved for email AND when the 'from' address uses a verified sending domain. There is no self-serve:

  • Unapproved accounts get error_code "email_not_enabled".

  • A 'from' address on an unverified/unapproved domain gets "invalid_from_domain". In both cases the returned suggested_next_tool_call explains how to get approved or which sending domain to use. Do NOT retry blindly — surface that guidance to the user.

Omit 'from_address' to send from the account's default verified sender (recommended). Provide at least one of 'text' or 'html'. This is transactional only — do not use it for marketing or bulk sends.

Args: to: Recipient email address subject: Email subject line text: Plain-text body (provide this and/or html) html: HTML body (provide this and/or text) from_address: Optional sender; must be on a verified sending domain. Omit for the default. reply_to: Optional Reply-To address

Returns: id: The provider message ID for the accepted send status: "sent" when the message was accepted for delivery to: The recipient the message was sent to from_address: The sender the message was sent from

On failure returns the standard agent error shape (error_code, error_message, agent_should_retry, suggested_next_tool_call). Common error_codes: email_not_enabled, invalid_from_domain, recipient_suppressed (prior bounce/complaint — do not retry), rate_limited (retryable after the window resets), invalid_to, missing_subject, empty_body.

Inputs: to (required), subject (required), html, text, +2 more.

update_originA

Change the origin/target URL for an existing domain.

Use this when the user redeploys their site to a new URL and wants the custom domain to point to the new location.

Args: provision_id: The provision_id of the domain to update new_origin_url: The new URL to route traffic to (e.g., "https://new-deploy.vercel.app")

Returns: status: Current domain status updated_at: Timestamp of the update

Inputs: provision_id (required), new_origin_url (required).

update_websiteA

Push updated pages/assets to an existing hosted website (upsert by path).

Files present locally replace the same paths on the site. With sync=false (default) site files not in this upload are left untouched — right for editing a few pages by hand. With sync=true the upload is treated as the complete site: after it lands, every page and asset the site has that this upload does not is deleted. Always use sync=true when redeploying a generator build folder (Docusaurus, Astro, Vite, Next export) — each build renames its hashed chunks, and without sync the old ones pile up forever. Dashboard-authored redirect pages are kept regardless and listed in kept_redirects.

Use publish_website for a brand-new site.

Args: website: The site's id, slug, or live URL (e.g. "https://thin.host/s/my-site/"). source_path: Absolute path to an .html file, a site/build directory, or a .zip. title: Optional new site title. sync: Treat the upload as the whole site and remove everything else.

Returns: url plus updated counts, and with sync=true the removed_pages / removed_assets. framework, base_url, warning as in publish_website.

Inputs: website (required), source_path (required), sync, title.

update_website_from_urlA

Redeploy an existing site from a URL (.zip of the site, or a single HTML page).

The URL form of update_website, for the hosted endpoint or any agent that has the build as a downloadable archive. sync defaults to true here because a zipped build is the whole site: after upload, files the archive does not contain are deleted (dashboard-authored redirect pages are kept). Pass sync=false to upsert only.

Args: website: The site's id, slug, or live URL. source_url: Public http(s) URL of a .zip archive or an .html file (≤150MB). title: Optional new site title. sync: Treat the archive as the whole site (default true).

Returns: Same shape as update_website.

Inputs: website (required), source_url (required), sync, title.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A4.1/5.0

    Scored across 11 tools

    Disambiguation4/5

    Tools target mostly distinct resources and actions: website publishing/listing/updating, domain provisioning/status/update/release/listing, and email. The only real overlap is between publish_website/update_website and their _from_url variants; descriptions clarify the source-path vs URL distinction, but an agent could still hesitate between them.

    Naming Consistency5/5

    All tool names use consistent snake_case and follow a predictable verb_noun or verb_noun_suffix pattern. The _from_url variants also use a consistent suffix convention.

    Tool Count5/5

    11 tools is well-scoped for a hosting, domain, and transactional email platform. Each tool covers a distinct operation without excessive surface area.

    Completeness3/5

    Domain lifecycle is complete: provision, check status, update origin, release, and list. Website lifecycle covers publish, update, and list, but lacks an obvious delete/unpublish or get-detail operation, which is a notable gap for a hosting service.

    Maintenance

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers