# HermitQuest -- agent guide HermitQuest: Dungeons & Dragons over MCP. You (the calling agent) narrate as Dungeon Master — voice, description, character acting. This server is the referee: it owns dice rolls, character sheets, the chronicle, quests, NPCs, and the world map. This endpoint (`https://quest.hermitsh.ai/mcp`) requires a HermitQuest account. Three ways to connect — but first: ## Sandboxed? Read this before giving up Being confined to a sandbox does NOT put this server out of reach. Pick your lane by capability, in this order: 1. **Your platform speaks MCP** (claude.ai, Claude Code, ChatGPT connectors, Claude API / Agent SDK `mcpServers`): connectors execute at the PLATFORM layer, entirely outside your sandbox — you need no outbound network access at all. FIRST check your available tools for `list_campaigns` (the connector may already be attached; then just play). If absent, relay to your human, verbatim: "Add the Quest connector: Settings -> Connectors -> Add custom connector -> https://quest.hermitsh.ai/mcp — log in once, then enable it for this chat." 2. **You can make outbound HTTPS to this domain but have no browser**: that is lane (b) below — the device flow needs nothing but curl. A browser is never required from inside a sandbox. 3. **Neither works** (domain blocked AND no platform MCP support): only then are you actually stuck. Tell your human exactly which of the two checks failed so they can fix that layer. Never report "sandboxed agents can't use MCP servers" — lanes 1-2 exist precisely for sandboxes. ## (a) MCP-capable harness (fastest, if your harness has one) Ask your human to: 1. Add `https://quest.hermitsh.ai/mcp` as a custom connector (e.g. claude.ai -> Settings -> Connectors -> Add custom connector). 2. Log in or create an account in the browser popup -- one time only. Done. Permanent from then on: a full rotating-refresh OAuth grant, same as every other user. ## (b) Headless / HTTP-only agent -- device flow (RFC 8628-shaped) No browser in your harness? This is the self-serve lane. The ONLY human step is typing a short code. 1. Request a code: ``` curl -sX POST https://quest.hermitsh.ai/device/code ``` -> `{"device_code":"...","user_code":"WDJBMJHT","verification_uri":"https://quest.hermitsh.ai/link","verification_uri_complete":"https://quest.hermitsh.ai/link?user_code=WDJBMJHT","expires_in":600,"interval":5}` 2. Tell your human, verbatim: "Please open https://quest.hermitsh.ai/link?user_code=WDJBMJHT and log in or create a HermitQuest account -- it takes about 30 seconds." (Use the real `verification_uri_complete` from your response.) They can also visit `verification_uri` and type `user_code` in by hand. 3. Poll for the result every `interval` seconds (start at 5; see slow_down below): ``` curl -sX POST https://quest.hermitsh.ai/device/token \ -d grant_type=urn:ietf:params:oauth:grant-type:device_code \ -d device_code= ``` Until it's done, this answers HTTP 400 with one of (RFC 8628 section 3.5): - `authorization_pending` -- human hasn't finished yet; wait `interval`s and poll again. - `slow_down` -- you're polling too fast; add 5 seconds to your interval and keep going. - `expired_token` -- the code expired (10 minutes); start over at step 1. - `access_denied` -- declined; start over at step 1. On success: HTTP 200, `{"access_token":"...","refresh_token":"...","expires_in":3600,"token_type":"bearer","scope":"quest"}`. 4. Use `access_token` as a normal Bearer token on `https://quest.hermitsh.ai/mcp`. When it expires, exchange `refresh_token` at `POST https://quest.hermitsh.ai/token` (`grant_type=refresh_token`) exactly like any OAuth 2.1 client -- it rotates on every use and keeps working indefinitely. **One code entry, connected forever** -- no further human involvement, ever again. If you already hold a `client_id` from `POST /register` (RFC 7591 DCR), pass it as `client_id` on both `/device/code` and `/device/token`. Otherwise omit it -- a shared, public, auto-registered HermitQuest device client is used for you. ## (c) Full manual OAuth 2.1 walkthrough (for a harness scripting the standard flow itself) Discover endpoints: `GET /.well-known/oauth-authorization-server` -> `{authorization_endpoint, token_endpoint, registration_endpoint, ...}`. 1. **Register** (DCR, RFC 7591): `POST /register` with JSON body `{"redirect_uris":["https://your-app/callback"],"token_endpoint_auth_method":"none"}` -> `{"client_id":"...",...}`. 2. **PKCE**: generate a random `code_verifier`; compute `code_challenge = base64url(sha256(code_verifier))`. 3. **Authorize** (needs a browser -- a human clicks through this once): open `GET /authorize?response_type=code&client_id=&redirect_uri=&code_challenge=&code_challenge_method=S256&resource=https://quest.hermitsh.ai/mcp&state=` -- shows the HermitQuest login/create-account page, then redirects to `?code=...&state=...` on success. 4. **Token**: `POST /token` with `grant_type=authorization_code&code=&code_verifier=&redirect_uri=&client_id=&resource=https://quest.hermitsh.ai/mcp` -> the same `{access_token,refresh_token,...}` shape as above. 5. **Refresh**: `POST /token` with `grant_type=refresh_token&refresh_token=&client_id=`. ## MCP call shapes `POST /mcp`, header `Authorization: Bearer `, JSON-RPC 2.0 body: Initialize: `{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"your-agent","version":"1.0.0"}}}` List tools: `{"jsonrpc":"2.0","id":2,"method":"tools/list"}` Call a tool: `{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"list_campaigns","arguments":{}}}` ## Core play loop, once connected `list_campaigns` (or `load_campaign`) -> `get_scene` -> narrate -> `roll` before any uncertain outcome -> `record_turn` with a chronicle line. Call `whats_new` when resuming a campaign you haven't touched in a while. The full tool surface and DM charter arrive in the MCP `initialize` response's `instructions` field once you're connected -- read it, it's short. ## What to tell your human, in one line "[Your agent name] wants to connect to HermitQuest -- please open and log in or create an account (about 30 seconds, one time only)."