# LookerHost — MCP server and API

Canonical page: https://lookerhost.com/docs/mcp
Machine-readable catalog: https://api.lookerhost.com/.well-known/mcp.json

## What LookerHost is

LookerHost is a cloud hosting platform: Cloud VPS, Web Hosting with a control
panel, an App Platform that builds and runs web apps straight from a Git
repository, managed databases and managed WordPress.

**LookerHost is not a Git hosting service.** It is not GitHub, Gitea or Forgejo.
Your code stays where it is; LookerHost clones it, builds it and serves it. Never
add a Git remote pointing at `lookerhost.com` — there is nothing to push to.

Everything a customer can do in the dashboard is also available over a REST API,
and that API is wrapped by an **MCP server** so an AI agent can provision and
operate infrastructure directly, with no manual steps.

## Quick start

### 1. Issue an API key

Sign in at [lookerhost.com/dashboard](https://lookerhost.com/dashboard) →
**Settings** → **API keys** → **Create key**. Pick a name and the scopes the
integration needs.

The secret (`lh_live_…`) is shown **once**, at creation. If it is lost, revoke the
key and issue a new one — it cannot be recovered.

The account must be linked to a company. Operator (admin) accounts do not issue
customer keys; they issue **platform keys** from **Integrations**, which span the
whole platform instead of one customer.

### 2. Add the server to your agent

Create a file named **`.mcp.json`** — with a leading dot — at the **root of your
project**:

```json
{
  "mcpServers": {
    "lookerhost": {
      "command": "npx",
      "args": ["-y", "lookerhost-mcp-server@latest"],
      "env": {
        "LOOKERHOST_API_URL": "https://api.lookerhost.com",
        "LOOKERHOST_API_KEY": "lh_live_..."
      }
    }
  }
}
```

> **The most common failure by far:** naming the file `mcp.json` instead of
> `.mcp.json`. No MCP client reads a file without the leading dot, so the agent
> starts with no LookerHost tools at all and reports that it has never heard of
> LookerHost. Check the filename first.

With the Claude Code CLI the same thing can be done in one command:

```bash
claude mcp add lookerhost --env LOOKERHOST_API_URL=https://api.lookerhost.com --env LOOKERHOST_API_KEY=lh_live_... -- npx -y lookerhost-mcp-server@latest
```

For Claude Desktop, the same `mcpServers` block goes into
`claude_desktop_config.json`.

**OpenAI Codex** (CLI and the ChatGPT desktop app) reads `~/.codex/config.toml`:

```toml
[mcp_servers.lookerhost]
command = "npx"
args = ["-y", "lookerhost-mcp-server@latest"]

[mcp_servers.lookerhost.env]
LOOKERHOST_API_URL = "https://api.lookerhost.com"
LOOKERHOST_API_KEY = "lh_live_..."
```

**pnpm** users can replace `npx -y` with `pnpm dlx` in any of the blocks above
(`"command": "pnpm", "args": ["dlx", "lookerhost-mcp-server@latest"]`), or
install it once with `pnpm add -g lookerhost-mcp-server` and use
`"command": "lookerhost-mcp-server"` with no args. Gemini CLI reads the same
`mcpServers` JSON from `~/.gemini/settings.json`.

The npm package is
[`lookerhost-mcp-server`](https://www.npmjs.com/package/lookerhost-mcp-server).
Pinning `@latest` matters: `npx` will otherwise reuse whatever version it cached
earlier, which is why an agent may insist the server is out of date.

### 3. Check that it works

Restart the agent and ask it to list your services. It should call
`lookerhost_list_services`. If it answers that it has no such tool: the config
file name or location is wrong. If it answers `invalid-token`: the key is wrong,
revoked or expired.

## Credential and scopes

The MCP server authenticates with the API key as a bearer token
(`Authorization: Bearer lh_live_…`). A key is bound to **one** customer account
and to a set of scopes, so the agent only ever sees and operates that account's
services. Anything the key does not allow is rejected with `insufficient-scope`,
and the response names the missing scope under `need`.

| Scope | Grants |
|---|---|
| `vps:read` | Read anything in the account: services, detail, live metrics and disk usage, catalog and plans, activity, deploy history, backups, invoices, app logs. **Every GET of the customer portal requires it** — a key without `vps:read` cannot read at all |
| `vps:write` | Create VPS and Web Hosting; install SSH public keys into a VPS |
| `vps:power` | Start, stop, restart any service |
| `apps:write` | Deploy and redeploy apps, create databases and WordPress sites, manage repository deploy keys |
| `service:delete` | Permanently delete a service — irreversible |
| `agent:run` | Run a coding agent inside the account's own VPS over SSH, approve or deny its actions, continue the conversation, abort. In `auto` mode the agent changes the server without review — the most powerful scope after `service:delete`; off by default when creating a key |

## Tools

Forty-two tools, and the REST endpoint each one calls.

| Tool | Scope | Endpoint | What it does |
|---|---|---|---|
| `lookerhost_list_services` | `vps:read` | `GET /api/services` | Every service in the account: VPS, web hosting, apps, databases, WordPress |
| `lookerhost_get_service` | `vps:read` | `GET /api/services/:id` | Full detail of one service |
| `lookerhost_get_metrics` | `vps:read` | `GET /api/services/:id/metrics` | Live CPU, RAM, network and disk of a VPS (VPS only) |
| `lookerhost_list_plans` | `vps:read` | `GET /api/services/plans` | Catalog: VPS plans, OS images, database kinds, WordPress variants, plus the account's quota and usage |
| `lookerhost_get_activity` | `vps:read` | `GET /api/services/activity` | Audit log, per service or account-wide |
| `lookerhost_get_deploy_history` | `vps:read` | `GET /api/services/:id/paas/deploy-history` | Deploys of an app, newest first, with build logs of failed attempts |
| `lookerhost_repo_access` | `vps:read` | `GET /api/services/:id/repository-access` | Which credential clones the app's repo: `public`, `github-app`, `deploy-key` or `none` |
| `lookerhost_create_vps` | `vps:write` | `POST /api/services` | Create a VPS |
| `lookerhost_create_webhosting` | `vps:write` | `POST /api/services/webhosting` | Create Web Hosting (CloudPanel preinstalled) |
| `lookerhost_add_ssh_key` | `vps:write` | `POST /api/services/:id/vps/ssh-keys` | Install an SSH public key into a VPS |
| `lookerhost_list_ssh_keys` | `vps:read` | `GET /api/services/:id/vps/ssh-keys` | List a VPS user's authorized keys |
| `lookerhost_generate_ssh_key` | `vps:write` | `POST /api/services/:id/vps/ssh-keys/generate` | Generate a key pair, install it and return the private key once |
| `lookerhost_remove_ssh_key` | `vps:write` | `DELETE /api/services/:id/vps/ssh-keys` | Remove a key (`confirm: true`) |
| `lookerhost_set_vps_password` | `vps:write` | `POST /api/services/:id/vps/password` | Set a user password, optionally allow SSH passwords (`confirm: true`) |
| `lookerhost_list_port_forwards` | `vps:read` | `GET /api/services/:id/portforwards` | List the public ports mapped to a VPS |
| `lookerhost_open_port` | `vps:write` | `POST /api/services/:id/portforwards` | Open a public port to a VPS port (`confirm: true`; pending approval unless trusted) |
| `lookerhost_close_port` | `vps:write` | `DELETE /api/services/:id/portforwards/:pfId` | Release a port-forward (`confirm: true`) |
| `lookerhost_deploy_app` | `apps:write` | `POST /api/services/apps` | Create and deploy an app from a Git repository |
| `lookerhost_redeploy_app` | `apps:write` | `POST /api/services/:id/paas/deploy` | Redeploy an existing app (or restart a catalog AI app). Returns `deployId` to poll |
| `lookerhost_get_deploy` | `vps:read` | `GET /api/services/:id/paas/deploys/:deployId` | How one deployment ended (`latest` accepted). On failure it carries `errorCode`, a one-line reason and the fix |
| `lookerhost_rollback_app` | `apps:write` | `POST /api/services/:id/paas/rollback` | Return to a previous version. Edge apps only (static sites and workers), restoring the stored artifact; container apps answer `rollback-not-supported` |
| `lookerhost_repo_deploy_key` | `apps:write` | `POST /api/services/:id/repository-access/deploy-key` | Generate a read-only deploy key for a private repo |
| `lookerhost_create_database` | `apps:write` | `POST /api/services/databases` | Managed PostgreSQL, MySQL, MongoDB or Redis |
| `lookerhost_create_redis` | `apps:write` | `POST /api/services/redis` | Dedicated Redis instance in Docker: own endpoint, ACL username/password and IP allow-list. Born CLOSED: no allowed IPs, no public endpoint |
| `lookerhost_get_redis` | `vps:read` | `GET /api/services/:id/redis` | Configuration, endpoint, allow-list and live container state. Never returns the password |
| `lookerhost_redis_connection` | `apps:write` | `GET /api/services/:id/redis/connection` | Username, password and full `redis://` URI. Only way to recover the password after creation; every call is audited |
| `lookerhost_redis_allowed_ips` | `apps:write` | `PUT /api/services/:id/redis/allowed-ips` | Replace the list of IPs/CIDRs allowed to connect. Widening needs `confirm: true`; an empty list closes the endpoint |
| `lookerhost_update_redis` | `apps:write` | `PATCH /api/services/:id/redis` | Version, ACL username, memory cap, eviction policy or persistence. Recreates the container; data survives |
| `lookerhost_redis_password` | `apps:write` | `POST /api/services/:id/redis/password` | Rotate the ACL user's password (`confirm: true`): cuts every connection using the old one |
| `lookerhost_create_wordpress` | `apps:write` | `POST /api/services/wordpress` | Managed WordPress site, database included |
| `lookerhost_create_aiapp` | `apps:write` | `POST /api/services/aiapps` | One-click AI app: n8n, Hermes Agent, OpenClaw or Steel Browser |
| `lookerhost_app_volumes` | `apps:write` (list: `vps:read`) | `GET`/`POST`/`DELETE /api/services/:id/paas/volumes` | List, add or remove a PaaS app's persistent volumes — without one, every redeploy loses anything written to disk. Removing needs `confirm: true` |
| `lookerhost_agent_chat` | `apps:write` | `POST /api/services/:id/aiapp/api` | Chat with the account's Hermes Agent through its OpenAI-compatible API, reached over LookerHost's internal network (the agent's API is never exposed to the internet directly). `api_key` is the app's own `API_SERVER_KEY` |
| `lookerhost_aiapp_api` | `apps:write` (`GET`: `vps:read`) | `POST /api/services/:id/aiapp/api` | Allow-listed call to an AI app's API — Hermes runs/jobs, Steel Browser sessions and scraping |
| `lookerhost_browser_session` | `apps:write` | `POST /api/services/:id/aiapp/api` | Create, list, get or release a session on the account's Steel Browser app — returns a CDP endpoint for Playwright/Puppeteer |
| `lookerhost_browser_scrape` | `apps:write` | `POST /api/services/:id/aiapp/api` | Render and extract a web page (markdown, HTML or readability) with the account's own Steel Browser, no session bookkeeping needed |
| `lookerhost_power_service` | `vps:power` | `POST /api/services/:id/power` | Start, stop or restart any service |
| `lookerhost_delete_service` | `service:delete` | `DELETE /api/services/:id` | Delete a service permanently — also requires `confirm: true` |
| `lookerhost_run_agent` | `agent:run` | `POST /api/agent/jobs` | Start an autonomous coding/ops agent inside one VPS from a natural-language task (like Claude Code logged into the server). `approval_mode`: `write-approval` (default), `auto` (needs `confirm: true`), `manual` |
| `lookerhost_agent_job` | `agent:run` | `GET /api/agent/jobs/:id` | Status, transcript tail, pending approvals and final result of a job |
| `lookerhost_list_agent_jobs` | `agent:run` | `GET /api/agent/jobs` | Jobs of the account, optionally per VPS |
| `lookerhost_agent_approve` | `agent:run` | `POST /api/agent/jobs/:id/approval` | Allow (`confirm: true`) or deny one command or file write the agent asked for |
| `lookerhost_agent_message` | `agent:run` | `POST /api/agent/jobs/:id/message` | Follow-up instruction or files for a finished job; the agent resumes with full context |
| `lookerhost_agent_abort` | `agent:run` | `POST /api/agent/jobs/:id/abort` | Stop a running job (`confirm: true`) |

Provisioning is **asynchronous**. Creating a VPS returns it with status
`provisioning`; poll `lookerhost_get_service` until it reads `running`, typically
one to three minutes. The first app of an account also provisions its Docker
host, so that first deploy is noticeably slower.

## Build packs

`lookerhost_deploy_app` takes a build pack:

- **`nixpacks`** — auto-detects Astro, Next.js, React, Vue, Svelte, Node.js,
  Python, Go, Rust and PHP. Use it for anything that runs a server; pass the port
  it listens on.
- **`static`** — builds and serves static output such as Astro's `dist/` or a
  Vite build.
- **`dockerfile`** — builds from the repository's `Dockerfile`.
- **`dockercompose`** — builds from the repository's compose file.
- **`opennextjs-cloudflare`** — a Next.js project using `@opennextjs/cloudflare`,
  built in an isolated VM and served as a Cloudflare Worker instead of getting its
  own VM.

Nothing LookerHost-specific has to be committed to the repository.

Public repositories need only their URL. For a private repository, call
`lookerhost_repo_deploy_key` to get a read-only public key and add it on GitHub
under **Settings → Deploy keys**, leaving *Allow write access* unchecked. The
private half stays encrypted on the platform and is never shown.
`lookerhost_repo_access` reports which credential is in play — a build that fails
with `repository-credential-missing` means the repository is private and no
credential covers it.

## Using the REST API without the MCP server

The MCP server is a convenience wrapper; the same key works directly.

```bash
curl -s https://api.lookerhost.com/api/services \
  -H "Authorization: Bearer lh_live_..."
```

```bash
curl -s https://api.lookerhost.com/api/services/apps \
  -H "Authorization: Bearer lh_live_..." \
  -H "Content-Type: application/json" \
  -d '{"label":"my-site","repoUrl":"https://github.com/acme/my-site","branch":"main","buildPack":"static","static":true}'
```

## Errors

Every failure returns JSON with an `error` code. The ones worth handling:

| Code | Meaning |
|---|---|
| `no-token` | No key was sent — check `LOOKERHOST_API_KEY` |
| `invalid-token` | Key is invalid, revoked or expired |
| `insufficient-scope` | The key lacks a scope; `need` names it. Re-issue with that scope |
| `no-customer` | The key's user is not linked to a company |
| `account-not-active` | Account pending or suspended: read-only until staff activates it |
| `quota-exceeded` | Account hit its service quota |
| `no-docker-host` | No PaaS Docker host yet; deploying an app provisions one |
| `protected` | Adopted VM, protected from self-service deletion |
| `rate-limited` | Too many requests; wait a few minutes |
| `timeout` | The call timed out but the operation may still be running — check with `lookerhost_get_service` before retrying |

## Machine-readable endpoints

For agents that need to discover all of this on their own:

- `https://api.lookerhost.com/` — JSON index of the API
- `https://api.lookerhost.com/.well-known/mcp.json` — the MCP catalog: tools,
  scopes, endpoints, errors, ready-to-paste client config
- `https://api.lookerhost.com/llms.txt` — the same map in plain text
- `https://lookerhost.com/llms.txt` — product-level summary of the platform
- `https://lookerhost.com/docs/mcp.md` — this page as raw markdown

The manifest at `/.well-known/mcp.json` is the source of truth for the tool list.

## Support

Email [support@lookerhost.com](mailto:support@lookerhost.com), or open an issue
at [github.com/Cloudcity-Colombia/lookerhost](https://github.com/Cloudcity-Colombia/lookerhost/issues).
