Skip to main content
Glama
hostsmith

Hostsmith MCP Server

by hostsmith

Server Configuration

Describes the environment variables required to run the server.

NameRequiredDescriptionDefault

No arguments

Capabilities

Features and capabilities supported by this server

CapabilityDetails
tools
{
  "listChanged": true
}

Tools

Functions exposed to the LLM to take actions

NameDescription
list_sitesA

List Hostsmith sites in the user's account. Returns each site's siteId, subdomain, domain, and current status - feed siteId into get_site, deploy_files, deploy_create_upload, or delete_site. This is the source of truth for "does the user already have a site at FQDN X" - call it before any create/deploy/delete to resolve the user's site reference. By default queries all data partitions and merges the results; pass partition: "us" or "eu" to limit the query.

list_domainsA

List domains the user can host sites under. Returns shared hosting domains (e.g. hostsmith.link, available to everyone) and custom domains owned by the user's organization. Use this to pick a domain value before calling create_site. By default queries all partitions and merges; pass partition or shared to narrow.

get_accountA

Get the user's account: organization details (orgId, orgName), the calling user's home partition under user.homePartition, current subscription plan with its limits (max sites, max domains, storage, bandwidth), and current usage counts. Use to check how much headroom the user has before creating new sites or to confirm plan-tier features. Usage is summed across all partitions.

get_siteA

Get full details of a specific Hostsmith site by ID, including its public URL (https://<subdomain>.<domain>), current deployment status, and configuration. Use after list_sites to inspect a single site, or after deploy_files / deploy_finalize to confirm the site is live and grab the URL to share with the user. Defaults to the user's home partition; pass partition explicitly when the site lives in a different one (visible in list_sites output).

create_siteA

Create a new Hostsmith site and return its siteId, full URL, and configuration. Use when the user wants to publish or host new content and no suitable site already exists. After creation, deploy content with deploy_files (small inline text) or deploy_create_upload + deploy_finalize (binaries / files > ~1 MB, uploaded directly to S3). The site-resolution and confirmation flow is described in the global server instructions; the rules below are specific to this tool's parameters.

domain MUST be one of the domains returned by list_domains for this user - never invent or assume one. The selected domain must be in active status; if it isn't, surface the problem to the user instead of attempting creation. partition passed to this tool MUST match the partition of the selected domain.

Subdomain selection must respect the domain's capabilities from list_domains. To serve the bare apex, pass subdomain: "www" - only valid when the domain has enableApexDomain: true (typically custom domains the user owns). For any other subdomain, the domain must have enableSubdomains: true; shared hosting domains (e.g. *.hostsmith.link) and most custom domains have enableApexDomain: false, so a non-apex subdomain is required there. If the chosen domain doesn't support the kind of site the user asked for (apex vs subdomain), surface the conflict rather than silently picking something else.

delete_siteA

Permanently delete a Hostsmith site and all of its deployed files. Destructive - only call after explicit user confirmation. The site URL becomes unreachable immediately and the content cannot be recovered. The user must pass confirm: true for the deletion to proceed; otherwise the call returns an error explaining the safeguard.

deploy_filesA

Publish in-memory file contents to a Hostsmith site without writing to disk. Use when you have just generated content (an HTML page, a report, JSON data) and the user wants it live. Returns the deployment version and status; call get_site afterwards if you need the public URL to share. The site must already exist - call create_site first if you do not have a siteId. Deploying to a site that already has content overwrites it - confirm overwrite with the user first.

Anti-pattern: do not use this tool to ship binaries (images, PDFs, video, fonts, zips) by base64-embedding or data-URI inlining them into HTML/CSS/JSON. Binaries belong on deploy_create_upload. If that path is blocked by sandbox/network, escalate to the user (ask them to enable egress, or offer manual upload of the presigned URL) - never reach for this tool as a workaround. Inlining bloats pages, breaks browser caching, and reships the bytes on every deploy.

deploy_create_uploadA

Start a direct-to-S3 upload for binary or large files. Use this instead of deploy_files for binaries (PDF, image, video, zip) or any file > ~1 MB. The MCP server has no access to the user's filesystem and deploy_files ships content inline through Lambda (capped at ~6 MB JSON-RPC payloads); this tool returns presigned S3 PUT URLs so the file bytes flow directly from your environment to S3, never through the MCP server.

Bundle into a zip first when: the upload contains more than 3 files OR any file is larger than ~1 MB. The fileWorker auto-extracts a single-zip upload after promotion, so subdirectories are preserved end-to-end and you avoid one PUT round-trip per file. Skip zipping only for the trivial single-small-file case (e.g. one HTML).

Bash bundle-and-deploy template (the agent should adapt fileNames and the cleanup prompt): TMP=$(mktemp -d) zip -r "$TMP/site.zip" index.html styles.css img/ # add every file/dir to deploy SIZE=$(stat -c%s "$TMP/site.zip" 2>/dev/null || stat -f%z "$TMP/site.zip") # 1. call deploy_create_upload with { siteId, files: [{ fileName: "site.zip", fileSize: $SIZE }] } # 2. PUT $TMP/site.zip to the returned URL(s) per the protocol below, capturing ETag # 3. call deploy_finalize with { siteId, versionId, completions: [...] } # 4. ASK THE USER: "Deploy succeeded. Remove temp folder $TMP? [y/N]" # Only run rm -rf "$TMP" after explicit confirmation; otherwise leave it for them to inspect.

Three-step protocol:

  1. Call this tool with { siteId, files: [{ fileName, fileSize }] }. Receive { versionId, files: { [fileName]: { uploadId, key, partUploadUrls: [{ part, url }], partSize, expiresAt } } }.

  2. For each file, slice the bytes into chunks of partSize and PUT each chunk to its partUploadUrls[i].url. Capture the ETag response header from every PUT - you will need it for finalize.

    Single-part (small file, one URL): curl -D - -X PUT --data-binary @file.pdf "$URL", then grep the response headers for ETag.

    Multi-part with dd (no temp files; reads each chunk in place): count=$(jq ".files["large.zip"].partUploadUrls | length" envelope.json) for i in $(seq 0 $((count-1))); do url=$(jq -r ".files["large.zip"].partUploadUrls[$i].url" envelope.json) etag=$(dd if=large.zip bs=5M skip=$i count=1 status=none
    | curl -sS -D - -X PUT --data-binary @- "$url"
    | awk -F': ' 'tolower($1)=="etag"{print $2}' | tr -d '\r') echo "{ "PartNumber": $((i+1)), "ETag": $etag }" >> parts.json done

    Multi-part in Python - prefer this over dd for files > ~50 MB (parallel PUTs, no temp files, cleaner error handling): import json, requests from concurrent.futures import ThreadPoolExecutor env = json.load(open("envelope.json")) info = env["files"]["large.zip"] part_size = info["partSize"] def upload_part(p): with open("large.zip", "rb") as f: # own handle per thread f.seek((p["part"] - 1) * part_size) r = requests.put(p["url"], data=f.read(part_size)) r.raise_for_status() return {"PartNumber": p["part"], "ETag": r.headers["ETag"]} with ThreadPoolExecutor(max_workers=5) as ex: # cap concurrency at 5 parts = list(ex.map(upload_part, info["partUploadUrls"]))

  3. Call deploy_finalize with { siteId, versionId, completions: [{ uploadId, key, parts: [{ ETag, PartNumber }] }] } for every multi-part file. Single-part uploads (uploadId is empty in the start response) need no completion entry.

The site must already exist - call create_site first if you do not have a siteId. Deploying overwrites existing content; confirm overwrite with the user first.

Anti-patterns - never do these to bypass a blocked or unavailable upload path:

  • Do NOT base64-embed, data-URI, or otherwise inline binary content (images, PDFs, video, fonts, zips) into HTML/CSS/JSON or any other deployed file. Inlining bloats the page, breaks browser caching, reships the bytes on every redeploy, and hides the real environment problem from the user.

  • Do NOT fall back to deploy_files for a binary that was meant for deploy_create_upload - the payload exceeds deploy_files' cap and the workaround is itself an anti-pattern.

  • Do NOT silently downscale, recompress, or otherwise mutate the user's file to fit a smaller path - ask first. When the upload path is unavailable, surface it to the user using the escalation order below.

Recovery escalation order when a PUT fails with a network/DNS error (connection refused, name resolution failure, timeout, sandbox-denied, proxy 403/407) OR your host environment has no HTTP-PUT capability at all (no bash/curl, no Python requests, no fetch). Do NOT retry blindly - a sandbox denial does not resolve on its own.

  1. Ask the user to enable egress (preferred fix). This is the cheapest option and unblocks all future uploads. Surface the exact sandbox configuration below and wait for the user to confirm before retrying. Most users can flip this setting in seconds; do not skip to step 2 without asking.

  2. If the user declines, cannot change the sandbox, or has no HTTP-PUT capability in-environment, present the failing presigned URL(s) verbatim and ask them to upload the file themselves (curl one-liner, browser, or any HTTP client they have). Provide a copy-pasteable command. Call deploy_finalize only after the user confirms each PUT returned 200 with an ETag (they will need to share the ETags back for multi-part uploads).

  3. Never route around the failure by base64-inlining the file into another deploy, recompressing, or falling back to deploy_files. See anti-patterns above.

Egress allowlist instructions for step 1:

  1. Read the failing URL's hostname. It will be either us.api.hostsmith.net, eu.api.hostsmith.net, or an S3 endpoint (copy verbatim from the failing URL).

  2. Infer the sandbox and give the user the exact configuration snippet:

    • Cursor (signal: cwd contains .cursor/, or CURSOR_* env vars) - edit ~/.cursor/sandbox.json (user scope) or <repo>/.cursor/sandbox.json (repo scope):

      { "networkPolicy": { "allow": ["us.api.hostsmith.net", "eu.api.hostsmith.net"] } }

      Reload Cursor.

    • Claude Code (signal: CLAUDE_CODE_* env vars, or ~/.claude/ settings present) - edit ~/.claude/settings.json:

      { "sandbox": { "enabled": true, "network": { "allowedDomains": ["us.api.hostsmith.net", "eu.api.hostsmith.net"] } } }

      If deniedDomains lists a matching host, remove it first - deny takes precedence.

    • Codex CLI (signal: ~/.codex/ settings, CODEX_* env vars) - edit ~/.codex/config.toml:

      [sandbox_workspace_write]
      network_access = true

      Codex has no per-host allowlist at this layer - this enables network for workspace-write mode globally. Risk is bounded by the upload token in the URL.

    • Unknown sandbox - tell the user: allow outbound HTTPS (port 443) to us.api.hostsmith.net and eu.api.hostsmith.net (and the failing S3 host if the URL points at S3) in whatever firewall/proxy they control. If a corporate HTTP proxy is in play, ensure CONNECT to those hostnames is permitted, or set NO_PROXY for direct routing.

  3. After the user confirms the change, retry the failed PUT. Tokens in the URL (ut=... for partition-host URLs, X-Amz-Signature for S3 URLs) remain valid for 1 hour from issuance, so re-running the same URL within that window works without re-calling deploy_create_upload.

deploy_finalizeA

Commit a deploy started with deploy_create_upload. Pass the versionId from the start response and a completions array containing the agent-collected ETags for each multi-part file (single-part uploads - those whose start response had an empty uploadId - do not need a completion entry). Returns the live site URL on success. The site must belong to the authenticated user; bearer-token auth is re-validated server-side, so holding presigned URLs alone does not let an unrelated caller finalize.

Prompts

Interactive templates invoked by user choice

NameDescription

No prompts

Resources

Contextual data attached and managed by the client

NameDescription

No resources

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/hostsmith/mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server