gunio-mcp
README.md
# gunio-mcp
A **local-only, Docker-packaged MCP server** (and a thin read CLI) for [gun.io](https://gun.io), authenticated with **your own browser session** — the same host-side cookie-broker + wrapper pattern as [claudesync](https://github.com/InfiniteRoomLabs/claudesync). It lets an MCP client (Claude Code, etc.) read your gun.io freelancer data — interviews, applications, jobs, profile — and perform a small set of **gated** writes.
> **Unofficial. No affiliation with gun.io.** This talks to gun.io's private, undocumented API using your logged-in session. It is a personal automation tool; use it at your own risk and within gun.io's Terms of Service. No gun.io code is included in this repo.
## How it works
```
Firefox session ──(host-side broker: rookie-cli)──▶ GUNIO_COOKIE ──▶ docker run ──▶ FastMCP server ──▶ app.gun.io/api/v2
```
- A **cookie broker** (`scripts/lib/harvest-cookie.sh`) reads your gun.io session (`sessionid` + `csrftoken`) from the browser, host-side. Nothing leaves your machine.
- A thin **wrapper** passes that cookie into a container via the `GUNIO_COOKIE` env var and `docker run`s it per invocation.
- Inside the container, a **Python/FastMCP** server ([`gunio_mcp`](src/gunio_mcp)) calls gun.io's `/api/v2` through a safety-guarded client ([`gunio_broker`](src/gunio_broker)).
## Security model
- **Reads need only the cookies.** Writes echo the `csrftoken` as `X-CSRFToken` (Django CSRF), and are **double-gated**: a call previews unless `confirm=true` **and** the server env has `GUNIO_MCP_WRITE_SCOPE` set.
- **Hard denylist in the client**, enforced regardless of tool: `auth/logout` (a GET that kills your session), the entire `/staff/` tree, `DELETE` on a freelancer (account deletion), `complete-screening`, and telemetry writes.
- **Cookies are never logged** or placed in exceptions.
- **Local only.** No data is sent anywhere except gun.io itself.
## Install (once Docker image + installer land)
```bash
# bootstrap the broker + wrappers, pull the image
scripts/gunio-setup.sh install
# register the MCP server with Claude Code
claude mcp add --scope user gunio ~/.local/share/gunio/gunio-mcp-wrapper.sh
```
To enable writes, set `GUNIO_MCP_WRITE_SCOPE=writes` in the server env (see the wrapper). Without it, write tools only ever return a dry-run preview.
## Remote mode (--serve)
By default `gunio-mcp` speaks MCP over stdio for a local client. With `--serve` it
instead runs FastMCP's streamable HTTP transport as a long-lived service:
```bash
gunio-mcp --serve # http://127.0.0.1:8000/mcp
gunio-mcp --serve --host 0.0.0.0 --port 9000
```
Configuration (flags win over env vars):
| Flag | Env var | Default | Meaning |
|---|---|---|---|
| `--host` | `GUNIO_MCP_HOST` | `127.0.0.1` | bind host |
| `--port` | `GUNIO_MCP_PORT` | `8000` | bind port |
| n/a | `GUNIO_MCP_AUTH_TOKEN` | unset | static bearer token required from clients |
| n/a | `GUNIO_MCP_INSECURE` | unset | `1` allows a non-loopback bind without a token |
Naming convention: `GUNIO_MCP_*` vars configure the server process itself;
`GUNIO_*` vars (today just `GUNIO_COOKIE`) configure the gun.io account/session.
### Endpoint security
`GUNIO_COOKIE` authenticates the server TO gun.io; nothing inherent authenticates
clients TO this server. Anyone who can reach the HTTP endpoint can act as your
gun.io account, including gated writes if the write scope is set. Therefore:
- Set `GUNIO_MCP_AUTH_TOKEN` to require `Authorization: Bearer <token>` on every
request (compared in constant time; never logged).
- Binding a non-loopback host without that token **refuses to start**, unless you
explicitly set `GUNIO_MCP_INSECURE=1` because the network layer (e.g. Tailscale
or an authenticating proxy) already provides access control.
- Do NOT set `GUNIO_MCP_WRITE_SCOPE` on a remote deployment unless
`GUNIO_MCP_AUTH_TOKEN` is configured.
Bearer auth applies only to serve mode; the stdio default ignores these vars.
### Docker
```bash
docker run -e GUNIO_COOKIE=... -e GUNIO_MCP_AUTH_TOKEN=... -p 8000:8000 \
gunio-mcp:local --serve --host 0.0.0.0
```
Inside a container, binding `0.0.0.0` with the token set is the expected pattern.
### Operational caveat: the cookie is a deploy-time secret
In remote mode there is no host-side broker refreshing the session: the
`GUNIO_COOKIE` you deploy with is it. When gun.io expires that session, reads
start failing until the deployment is updated with a fresh cookie. The
`auth_status` tool (or `gunio auth status` on the CLI) is how you detect this: it
performs one lightweight authenticated read and reports
```json
{"mode": "cookie", "authenticated": false, "checked_at": "...", "error": "gun.io returned HTTP 401"}
```
without ever raising raw errors or leaking cookie material. Phase 2 will add
credential-based login; this release is transport + visibility only.
## Develop
```bash
uv sync --extra dev
uv run --extra dev pytest -q # full suite
uv run gunio me # thin CLI (needs GUNIO_COOKIE or a logged-in browser)
uv run gunio-mcp # run the MCP server over stdio
```
`GUNIO_COOKIE` is a `sessionid=...; csrftoken=...` string; the broker produces it, or set it by hand for local dev.
## Layout
| Path | Role |
|---|---|
| `src/gunio_broker/client.py` | Safety-guarded HTTP client for `/api/v2` |
| `src/gunio_mcp/tools.py` | Pure tool helpers (reads + gated writes), unit-tested |
| `src/gunio_mcp/server.py` | FastMCP wrappers + `main()` (stdio) |
| `src/gunio_cli/main.py` | Thin read-only CLI |
| `scripts/` | Cookie broker, wrappers, installer (lifted from claudesync) |
## License
MIT.
TDQS
B3.2/5.0
Scored across 24 tools
Disambiguation4/5
Most tools target distinct resources (user, profile, interviews, applications, etc.), but 'list_interviews' and 'list_mission_interviews' could cause confusion; descriptions help clarify.
Naming Consistency4/5
Predominantly uses verb_noun pattern (list_, get_, update_, etc.), with minor deviations like 'get_me' and 'job_stats' being slightly off-pattern.
Tool Count4/5
24 tools is above the typical range but still reasonable for a comprehensive freelance platform client; each tool serves a distinct purpose without being excessive.
Completeness4/5
Covers core freelancer workflows (profile, applications, interviews, invites, hires, job search), but lacks tools for messaging or payment tracking which might be relevant.
Maintenance
ActivitySlowing
ResponsivenessNo issues