Project · gcp-mcp-standalone

Grok Build Client

type componentstatus activemcp · grok · client · oauth

Summary

Grok Build (xAI's coding TUI on the Mac) is a second MCP client of the broker, alongside the Claude apps. It connects as its own broker app-key (my_company:grok) with a separate token store and a narrower allow-list than app1, so a compromise of Grok's key cannot reach the Claude client's connectors — and vice versa. Added 2026-07-11; hardened + documented 2026-07-12.

Key points

  • Its own key, its own allow-list. my_company:grok has scopes [proxy, status] (no admin) and allowed_connectors: [notion_api, slack, github] only — it cannot reach gcp/tailscale/gitlab/etc. Separate app-key ⇒ separate encrypted token store, isolated from app1.
  • How it authenticates. Grok's ~/.grok/config.toml points three MCP servers at https://bobsmcp.uk/proxy/{notion_api,slack,github}/ with headers X-Broker-Key: ${GROK_BROKER_KEY} + X-App-Id: my_company:grok. The key is the per-app broker key (br_…, not the admin key), stored in ~/.grok/broker.env (mode 600) and referenced via env var — never inline, never committed to git (verified: the key is in zero tracked files).
  • Read-only GitHub (per-app OAuth scope override). A new broker capability: an app's connector config may set scopes to override the connector default for that app only. grok.github requests [read:user, read:org] while app1.github keeps the full repo default. So Grok's GitHub token cannot write anything. (See the caveat below — this is metadata-only today; a read-only fine-grained-PAT path for private-repo contents is designed but not yet built.)
  • Network path. Either the Cloudflare Tunnel hostname (bobsmcp.uk) or a Tailscale SSH local-forward to 127.0.0.1:8002. The broker admin key is never placed in Grok's config.

The per-app scope override (how it works)

AppConnectorCredentials.scopes (optional) overrides ConnectorMeta.scopes for a single app. The override wins in both the OAuth authorize request (what scope is asked for) and the recorded connection (what the token is allowed to do); empty = connector default. This is the security-load-bearing path — the requested scope bounds the resulting token — and it is covered by unit tests (tests/test_broker.py). Committed to the fork (d28436f) after being found live but uncommitted on the VM (a latent deploy hazard, now removed).

Caveats & next steps

  • GitHub [read:user, read:org] grants no repo access at all. Classic GitHub OAuth has no "read private repos only" scope — repo is the only key to private repos and it is read+write. Dropping it makes Grok's GitHub metadata-only (org/user info), not "read your code." To let Grok read private code without write, the connector needs a per-app fine-grained PAT (contents:read, metadata:read) — a static_token path that bypasses OAuth for that app. Designed, not yet built.
  • Connections not yet established. Grok holds a valid key but has not run the OAuth connect flow for its connectors, so its tools return nothing until each is connected under my_company:grok.
  • Slack is unconfigured for everyone — the slack connector is enabled but no Slack app credentials exist yet (SLACK_CLIENT_ID/SECRET empty), so it won't work until a Slack app is created and its secret stored in Secret Manager.

Sources

Compiled from wiki/projects/gcp-mcp-standalone/grok-build-client.md · git is the source of truth