gigahost-mcp
by ecschoye
README.md
# gigahost-mcp
A tiny [MCP](https://modelcontextprotocol.io) server exposing the
[Gigahost](https://gigahost.no) API (`https://api.gigahost.no/api/v0`) to any
MCP-compatible client (Claude Code/Desktop, Cursor, Windsurf, Cline, or your own).
The Gigahost API is uniform REST, so the whole surface (~150 endpoints: DNS
zones/records, `.no` domains, servers, web hosting, BGP, DynDNS) is wrapped by a
single passthrough tool rather than one tool per endpoint. The model drives paths
directly.
## Tool
`gigahost_request(method, path, body=None, query=None, files=None)`
- `method` — `GET` / `POST` / `PUT` / `DELETE`
- `path` — e.g. `/dns/zones`, `/servers/123/reboot`
- `body` — JSON body for `POST`/`PUT`
- `query` — querystring params
- `files` — `{form_field: local_file_path}` for multipart uploads (e.g.
`POST /webhosting/{id}/files/upload`). When set, `body` is sent as multipart form
fields instead of JSON.
Returns `{"status": <http_status>, "data": <json|text>}`, or `{"error": ...}` on a
network/file failure.
This covers the full Gigahost REST surface: all JSON endpoints plus multipart file
uploads. The only bound on what works is the API key's permission scope.
Endpoint reference: <https://gigahost.no/en/api-dokumentasjon>
### Examples
Calls below are shown as the tool arguments the model produces.
List DNS zones:
```json
{ "method": "GET", "path": "/dns/zones" }
```
Add an A record to zone 4040:
```json
{
"method": "POST",
"path": "/dns/zones/4040/records",
"body": { "record_name": "@", "record_type": "A", "record_value": "203.0.113.10", "record_ttl": 3600 }
}
```
Reboot a server:
```json
{ "method": "GET", "path": "/servers/123/reboot" }
```
Upload a file to a web-hosting account (multipart):
```json
{
"method": "POST",
"path": "/webhosting/55/files/upload",
"files": { "file": "/local/path/index.html" }
}
```
## Prerequisites
- **Python 3.11+**
- A **Gigahost API key** (`flux_live_<hex>`), created in the Flux dashboard under
**Konto → API keys**. Scope it to only the permissions you need (e.g. DNS /
domain). It is revocable and shown only once at creation.
- The key is read from the `GIGAHOST_API_KEY` environment variable.
## Install
Pick whichever you already use.
### Option A — uv (zero install)
Dependencies are declared inline (PEP 723), so nothing to install:
```sh
GIGAHOST_API_KEY=flux_live_xxxxxxxx uv run /path/to/server.py
```
### Option B — pip / venv
```sh
python -m venv .venv && . .venv/bin/activate
pip install -r requirements.txt
GIGAHOST_API_KEY=flux_live_xxxxxxxx python server.py
```
### Self-check (offline, no key, no network)
```sh
uv run server.py --selfcheck # or: python server.py --selfcheck
# prints: ok
```
## Configure your MCP client
Most clients read a JSON config with an `mcpServers` map. Add one of the following,
using an **absolute path** to `server.py` and your key in `env`.
With uv:
```json
{
"mcpServers": {
"gigahost": {
"command": "uv",
"args": ["run", "/absolute/path/to/server.py"],
"env": { "GIGAHOST_API_KEY": "flux_live_xxxxxxxx" }
}
}
}
```
With a venv / system Python:
```json
{
"mcpServers": {
"gigahost": {
"command": "python",
"args": ["/absolute/path/to/server.py"],
"env": { "GIGAHOST_API_KEY": "flux_live_xxxxxxxx" }
}
}
}
```
Config file locations vary by client, e.g.:
- **Claude Desktop** — `claude_desktop_config.json` (Settings → Developer → Edit Config)
- **Cursor** — `~/.cursor/mcp.json`
- **Windsurf / Cline / others** — their MCP settings JSON
### Claude Code (CLI shortcut)
```sh
claude mcp add -s user gigahost -e GIGAHOST_API_KEY=flux_live_xxxxxxxx \
-- uv run /absolute/path/to/server.py
```
After configuring, the client should list a `gigahost` server with the
`gigahost_request` tool. Smoke-test by asking it to `GET /dns/zones`.
## Key permissions (scope)
When you create the Flux API key you set an access level **per area**. This bounds
what `gigahost_request` can do, independent of the code:
- **Ingen** — no access, calls return `403`.
- **Les** — read only, `GET` works, writes (`POST`/`PUT`/`DELETE`) return `403`.
- **Les/skriv** — full read + write.
Areas map to API path prefixes:
| Flux area | Path prefix | Usable with an API key? |
|------------------|--------------------------|---------------------------|
| DNS og domener | `/dns/...` | yes |
| Servere | `/servers/...` | yes |
| Webhotell | `/webhosting/...` | yes |
| Deploy | `/deploy/...`, `/reinstall/...` | yes |
| Rack | `/bgp/...` | yes |
| Fakturering | `/my/invoices` | **no, see below** |
| Min konto | `/account/...`, `/my/account` | `/account` yes, `/my/account` no |
Three traps, all found by probing the live API rather than reading the docs:
**`403` is the catch-all.** A bogus path returns the same
`"You do not have permission for this operation."` as a genuine denial, so a 403
proves neither that the path exists nor that a scope is missing.
**A key cannot audit itself.** Every endpoint marked `(admin only)` needs a
session token and always 403s to a key, including all of `/account/apikeys`. So
the only way to know what a key can do is to look in the Flux web UI.
**Scope does not gate everything.** `/account` returns full account data even
for a key scoped to `dns` alone, and `/servers` / `/webhosting` / `/bgp` return
`200` with empty collections when you own none of those. Neither a `200` nor an
empty list tells you a scope is granted. `/my/invoices` 403s under every scope
tried, including after granting `Fakturering`, for reasons that cannot be
resolved from the API. Read invoices in the Flux UI.
Grant the minimum each area needs, not blanket `Les/skriv`. But note that
`DNS og domener` must be `Les/skriv` for `POST /dns/domains/register`, and must
not be pinned to specific zone IDs, since a newly registered domain is by
definition not in that list. See [API.md](API.md) for the full endpoint map.
## Security
- The API key is read from `GIGAHOST_API_KEY` only. It is never written to this repo.
- Putting the key in a client config file (or `~/.claude.json` via `claude mcp add
-e`) stores it in plaintext. Acceptable for a personal scoped key on your own
machine. Revoke and rotate in Flux if it leaks.
- Prefer a narrowly-scoped key over your account password.
## License
MIT
This server cannot be deployed
Maintenance
ActivityStale
ResponsivenessNo issues