Makes familiar reachable over HTTPS as a remote MCP server so it can be wired up as a custom connector in chat.mistral.ai (or any remote MCP client). This is purely a transport-layer addition — the EVE-SSO/ESI multi-character auth in auth.py is unchanged.
stdio stays the default and is completely untouched: familiar / python -m familiar run exactly as before.
Changes
familiar serve — new subcommand that runs the FastMCP streamable-HTTP transport. Binds 127.0.0.1:8080/mcp by default (a TLS reverse proxy is expected to terminate HTTPS in front of it). Host/port/path are env-configurable via FAMILIAR_HTTP_HOST / FAMILIAR_HTTP_PORT / FAMILIAR_HTTP_PATH.
Bearer auth — the expected token is read from FAMILIAR_HTTP_TOKEN. HTTP mode refuses to start without it (no open server). Requests without a valid Authorization: Bearer <token> get 401 + WWW-Authenticate: Bearer; a valid token passes the MCP handshake. Uses FastMCP's built-in StaticTokenVerifier. stdio gets no token check (local transport).
Writes off by default over HTTP — a network-reachable, LLM-driven endpoint shouldn't send mail or set waypoints by accident, so over HTTP writes default to disabled unless FAMILIAR_ENABLE_WRITES is explicitly truthy. Only the default resolution changes; the writes.guard choke point and per-call confirm=true requirement are untouched. Over stdio the default is unchanged (writes on).
Headless login — familiar login --no-browser prints the EVE SSO authorize URL, opens no browser, and reads the pasted callback URL (or bare code) from stdin, for provisioning characters on a headless server. The interactive localhost-callback login stays the default. Docs also cover the alternative: log in on the desktop and copy the encrypted store (token.enc + key) to the server.
fastmcp pin bumped to >=3.0.0 for the streamable-HTTP + verifier API; uv.lock refreshed.
Tests
New tests/test_serve.py (follows the existing conftest style, no async-test plugin):
401 without a token / with a wrong token (incl. the WWW-Authenticate header), 200 with a valid token — driven through the ASGI app in-process.
Start refusal when FAMILIAR_HTTP_TOKEN is unset or blank.
FAMILIAR_HTTP_* config defaults and overrides.
Write default in HTTP context (disabled by default, env still wins, stdio stays enabled).
Headless-login callback parsing and the manual_login exchange flow.
uv run ruff format . && uv run ruff check . && uv run pytest is green, coverage 91% (>= 80%). Also smoke-tested end-to-end: FAMILIAR_HTTP_TOKEN=… familiar serve binds the port, returns 401 without a token and 200 with the right bearer; stdio still boots unchanged.
Docs
README.md and doc/getting-started.md get a "Serve over HTTPS (remote MCP)" section (env vars, an example nginx location block with proxy_buffering off; and a long read timeout, headless-login/copy-the-store options, and the Mistral connector steps). doc/architecture.md describes the stdio/HTTP transport split and the HTTP write default. CLAUDE.md gets the new commands and env vars.
## What
Makes familiar reachable over HTTPS as a remote MCP server so it can be wired up as a custom connector in chat.mistral.ai (or any remote MCP client). This is purely a transport-layer addition — the EVE-SSO/ESI multi-character auth in `auth.py` is unchanged.
**stdio stays the default and is completely untouched**: `familiar` / `python -m familiar` run exactly as before.
## Changes
- **`familiar serve`** — new subcommand that runs the FastMCP **streamable-HTTP** transport. Binds `127.0.0.1:8080/mcp` by default (a TLS reverse proxy is expected to terminate HTTPS in front of it). Host/port/path are env-configurable via `FAMILIAR_HTTP_HOST` / `FAMILIAR_HTTP_PORT` / `FAMILIAR_HTTP_PATH`.
- **Bearer auth** — the expected token is read from `FAMILIAR_HTTP_TOKEN`. HTTP mode **refuses to start without it** (no open server). Requests without a valid `Authorization: Bearer <token>` get `401` + `WWW-Authenticate: Bearer`; a valid token passes the MCP handshake. Uses FastMCP's built-in `StaticTokenVerifier`. stdio gets no token check (local transport).
- **Writes off by default over HTTP** — a network-reachable, LLM-driven endpoint shouldn't send mail or set waypoints by accident, so over HTTP writes default to disabled unless `FAMILIAR_ENABLE_WRITES` is explicitly truthy. Only the default resolution changes; the `writes.guard` choke point and per-call `confirm=true` requirement are untouched. Over stdio the default is unchanged (writes on).
- **Headless login** — `familiar login --no-browser` prints the EVE SSO authorize URL, opens no browser, and reads the pasted callback URL (or bare code) from stdin, for provisioning characters on a headless server. The interactive localhost-callback login stays the default. Docs also cover the alternative: log in on the desktop and copy the encrypted store (`token.enc` + `key`) to the server.
- **fastmcp pin** bumped to `>=3.0.0` for the streamable-HTTP + verifier API; `uv.lock` refreshed.
## Tests
New `tests/test_serve.py` (follows the existing conftest style, no async-test plugin):
- 401 without a token / with a wrong token (incl. the `WWW-Authenticate` header), 200 with a valid token — driven through the ASGI app in-process.
- Start refusal when `FAMILIAR_HTTP_TOKEN` is unset or blank.
- `FAMILIAR_HTTP_*` config defaults and overrides.
- Write default in HTTP context (disabled by default, env still wins, stdio stays enabled).
- Headless-login callback parsing and the `manual_login` exchange flow.
`uv run ruff format . && uv run ruff check . && uv run pytest` is green, coverage 91% (>= 80%). Also smoke-tested end-to-end: `FAMILIAR_HTTP_TOKEN=… familiar serve` binds the port, returns 401 without a token and 200 with the right bearer; stdio still boots unchanged.
## Docs
`README.md` and `doc/getting-started.md` get a "Serve over HTTPS (remote MCP)" section (env vars, an example nginx location block with `proxy_buffering off;` and a long read timeout, headless-login/copy-the-store options, and the Mistral connector steps). `doc/architecture.md` describes the stdio/HTTP transport split and the HTTP write default. `CLAUDE.md` gets the new commands and env vars.
Add a `familiar serve` subcommand that runs the FastMCP streamable-HTTP
transport for use as a remote MCP server (e.g. a Mistral custom connector),
while stdio stays the default and is completely unchanged.
- config: FAMILIAR_HTTP_HOST/PORT/PATH (default 127.0.0.1:8080/mcp) and a
live http_token() reader for FAMILIAR_HTTP_TOKEN.
- server: configure_http()/serve_http() attach a StaticTokenVerifier so a
missing/wrong bearer token gets 401 + WWW-Authenticate: Bearer and a valid
one passes the handshake. Refuses to start (SystemExit) without a token.
- writes: in HTTP mode the write default flips to disabled unless
FAMILIAR_ENABLE_WRITES is explicitly truthy; guard() logic is untouched.
- auth: manual_login() + parse_callback() power `login --no-browser`, printing
the SSO URL and reading the pasted callback from stdin (no local browser).
- bump fastmcp pin to >=3.0.0 for the streamable-HTTP + verifier API.
- 401 without/with a bad token (incl. WWW-Authenticate: Bearer), 200 with a
valid one, driven through the ASGI app in-process.
- configure_http refuses to start when the token is unset or blank.
- FAMILIAR_HTTP_* config defaults and overrides; http_token() strip/empty.
- HTTP write default disabled by default, env still wins, stdio stays enabled.
- parse_callback (URL / bare code / empty) and the manual_login exchange flow.
- README + getting-started: a "Serve over HTTPS (remote MCP)" section with the
`familiar serve` command, the FAMILIAR_HTTP_* env vars, the write-off-by-
default note, an nginx location block (proxy_buffering off, long read
timeout), the headless-login / copy-the-store options, and the steps to add
it as a Mistral custom connector.
- architecture: describe the stdio/HTTP transport split, the bearer-auth gate,
and the HTTP write default.
- CLAUDE.md: add the new commands and env vars to Commands + Architecture.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
What
Makes familiar reachable over HTTPS as a remote MCP server so it can be wired up as a custom connector in chat.mistral.ai (or any remote MCP client). This is purely a transport-layer addition — the EVE-SSO/ESI multi-character auth in
auth.pyis unchanged.stdio stays the default and is completely untouched:
familiar/python -m familiarrun exactly as before.Changes
familiar serve— new subcommand that runs the FastMCP streamable-HTTP transport. Binds127.0.0.1:8080/mcpby default (a TLS reverse proxy is expected to terminate HTTPS in front of it). Host/port/path are env-configurable viaFAMILIAR_HTTP_HOST/FAMILIAR_HTTP_PORT/FAMILIAR_HTTP_PATH.FAMILIAR_HTTP_TOKEN. HTTP mode refuses to start without it (no open server). Requests without a validAuthorization: Bearer <token>get401+WWW-Authenticate: Bearer; a valid token passes the MCP handshake. Uses FastMCP's built-inStaticTokenVerifier. stdio gets no token check (local transport).FAMILIAR_ENABLE_WRITESis explicitly truthy. Only the default resolution changes; thewrites.guardchoke point and per-callconfirm=truerequirement are untouched. Over stdio the default is unchanged (writes on).familiar login --no-browserprints the EVE SSO authorize URL, opens no browser, and reads the pasted callback URL (or bare code) from stdin, for provisioning characters on a headless server. The interactive localhost-callback login stays the default. Docs also cover the alternative: log in on the desktop and copy the encrypted store (token.enc+key) to the server.>=3.0.0for the streamable-HTTP + verifier API;uv.lockrefreshed.Tests
New
tests/test_serve.py(follows the existing conftest style, no async-test plugin):WWW-Authenticateheader), 200 with a valid token — driven through the ASGI app in-process.FAMILIAR_HTTP_TOKENis unset or blank.FAMILIAR_HTTP_*config defaults and overrides.manual_loginexchange flow.uv run ruff format . && uv run ruff check . && uv run pytestis green, coverage 91% (>= 80%). Also smoke-tested end-to-end:FAMILIAR_HTTP_TOKEN=… familiar servebinds the port, returns 401 without a token and 200 with the right bearer; stdio still boots unchanged.Docs
README.mdanddoc/getting-started.mdget a "Serve over HTTPS (remote MCP)" section (env vars, an example nginx location block withproxy_buffering off;and a long read timeout, headless-login/copy-the-store options, and the Mistral connector steps).doc/architecture.mddescribes the stdio/HTTP transport split and the HTTP write default.CLAUDE.mdgets the new commands and env vars.