Skip to content

v0.42.2.0 feat: gbrain connect — one-command Claude Code onboarding from a bearer token#1683

Merged
garrytan merged 22 commits into
masterfrom
garrytan/gbrain-thin-client-onboard
Jun 2, 2026
Merged

v0.42.2.0 feat: gbrain connect — one-command Claude Code onboarding from a bearer token#1683
garrytan merged 22 commits into
masterfrom
garrytan/gbrain-thin-client-onboard

Conversation

@garrytan

@garrytan garrytan commented May 31, 2026

Copy link
Copy Markdown
Owner

Summary

One command to wire a coding agent to a remote gbrain from a bearer token (or OAuth), for Claude Code, Codex, and Perplexity Computer.

gbrain connect <mcp-url> (src/commands/connect.ts) — turns a remote MCP URL + credential into a paste-ready setup block, or wires it up directly with --install and smoke-tests the credential before you walk away. Direct HTTP MCP, no local install/proxy. The printed block tells the agent to call get_brain_identity + list_skills.

Per-agent (--agent):

  • claude-code (default): claude mcp add ... -H "Authorization: Bearer <tok>". --install runs it.
  • codex: codex mcp add <name> --url <url> --bearer-token-env-var GBRAIN_REMOTE_TOKEN — Codex reads the token from the env var at runtime, so it never lands in Codex config. --install runs it.
  • perplexity: prints the connector fields for Settings → Connectors (GUI; no --install). Bearer works, but OAuth is the recommended path (it's a cloud service).
  • generic: prints URL + Authorization header (or OAuth fields) for any other MCP client.

OAuth 2.1 client credentials (--oauth, perplexity/generic) — the correct path when the credential lives on a third-party cloud: least-privilege scopes + short-lived rotating access tokens instead of a long-lived full-access bearer. --oauth --register mints a client on the host in one command; --oauth --client-id X --client-secret Y uses an existing one (runs anywhere). Rejected for claude-code/codex (they consume bearer) and with --install.

Raw-bearer MCP probe (src/core/connect-probe.ts) — --install's smoke test does a real initialize + tools/call get_brain_identity over the official SDK with a static Authorization header (not the OAuth-only callRemoteTool), bounded by a Promise.race timeout so a stalled server can't hang it.

gbrain auth create bugfix (src/commands/auth.ts) — the bare form (gbrain auth create <name>, no --takes-holders) silently dropped the name; fixed + extracted parseAuthCreateArgs.

Wiring: cli.ts dispatch (CLI_ONLY + CLI_ONLY_SELF_HELP + handleCliOnly, no-DB). Security: POSIX single-quoted commands (paste-safe), token header-injection validation, link-local/cloud-metadata exfil guard (incl. IPv4-mapped IPv6), token/secret redaction in errors + --json, --install requires --yes non-interactively.

Test Coverage

  • Unit test/connect.test.ts (100 cases): URL normalization, token/secret validation + redaction, all four agents' blocks + JSON, OAuth helpers (issuer derivation, register orchestration, BYO creds), every runConnect guard path (claude/codex --install, oauth rejections, arg-guard). test/auth-create-args.test.ts (6, bare-name regression).
  • E2E test/e2e/connect-bearer.test.ts (7) against a live gbrain serve --http (PGLite):
    • raw-bearer probe round-trips get_brain_identity; wrong token → auth; unreachable → unreachable.
    • real claude CLI claude mcp add via connect --installclaude mcp get confirms registration (sandboxed HOME).
    • real codex CLI codex mcp add --bearer-token-env-varcodex mcp get confirms (token stays out of config; sandboxed CODEX_HOME).
    • full OAuth chain: register client → connect --oauth formats it → OAuth discovery → /token client-credentials mint → get_brain_identity tool call.
    • Real-CLI cases skip gracefully when the binary is absent (e.g. CI). Perplexity's GUI itself can't be automated, so it's print-asserted.
  • bun run verify 29/29; full unit suite green (one pre-existing, unrelated batch-retry-audit local-only test that passes in CI).

Pre-Landing Review

Specialists (testing, maintainability, security, performance): 0 critical; all informational auto-fixed or documented.

Adversarial Review

Claude adversarial + Codex structured (GATE: PASS, no [P1]). Real fixes: probe Promise.race timeout (a stalled connect() could have hung --install), close transport on connect-throw, reject flag-shaped arg values, non-interactive --install requires --yes, POSIX single-quote the command (paste-safety), IPv4-mapped IPv6 metadata guard.

Plan Completion

5/5 in-scope tasks DONE; T6 (--env-token) and T7 (Tier 2 local thin-client) deferred by design (filed in TODOS).

TODOS

Added ## v0.42.2.0 gbrain connect follow-ups (T6 --env-token, T7 Tier 2 bearer thin-client). No existing items completed.

Documentation

  • README.md — Claude Code / Codex / Perplexity MCP bullets (Perplexity leads with OAuth).
  • docs/mcp/CLAUDE_CODE.md — leads with gbrain connect, keeps local stdio path.
  • docs/mcp/CODEX.md (new) — Codex connect + env-var bearer + manual setup.
  • docs/mcp/PERPLEXITY.md — OAuth-first (--oauth --register), serve --bind 0.0.0.0 --public-url ECONNREFUSED footgun, both auth paths.
  • docs/INSTALL.md — §3 now shows the one-command gbrain connect for all three agents + links CODEX.md.
  • CLAUDE.md — Key-files entry for connect.ts + connect-probe.ts (multi-agent + OAuth); auth.ts bare-name fix. llms-full.txt regenerated.
  • CHANGELOG.md — v0.42.2.0 entry covers all three agents + OAuth.

Coverage: gbrain connect has reference (README, CLAUDE.md, per-client docs), how-to (CLAUDE_CODE/CODEX/PERPLEXITY/INSTALL examples), and tutorial (CHANGELOG "To take advantage" walkthrough). No documentation debt.

Documentation Debt

  • ⚠️ Perplexity's GUI connector flow can't be automated/E2E'd (no CLI/API) — print-asserted + manual docs only. Inherent, not fixable.

Test plan

  • bun test test/connect.test.ts 100/100
  • bun test test/e2e/connect-bearer.test.ts 7/7 (real claude + codex + OAuth chain)
  • bun run verify 29/29
  • Manual: codex + perplexity (bearer + OAuth) output; real claude/codex registration against a live server

🤖 Generated with Claude Code


Follow-up (this session): two-funnel onboarding + fixes

Extends the same v0.42.2.0 wave so the "use gbrain with my coding agent" story is complete and documented, not just the remote-connect mechanism.

  • fix — connect LEARN_INSTRUCTION named a non-existent MCP tool (src/commands/connect.ts): the self-orientation block told connected agents capture was a core tool. capture is CLI-only, not exposed over MCP — an agent that followed it hit "unknown tool". Now names put_page (the real MCP write tool). Regression block in test/connect.test.ts.
  • feat — serve --http surfaces skill-publishing status (src/commands/serve-http.ts): when mcp.publish_skills is OFF, connected agents can't call list_skills/get_skill, so the host's skill catalog is invisible. Startup banner now shows a Skills: line; a stderr nudge fires when off with the paste-ready gbrain config set mcp.publish_skills true. Pure skillPublishStatus() helper, unit-tested (test/serve-skills-publish-nudge.test.ts, 6 cases).
  • test — local stdio funnel proven end-to-end (test/e2e/serve-stdio-roundtrip.test.ts, 3 cases): spawns real gbrain serve (stdio) against a freshly init --pglite brain and drives the official MCP SDK client through initializetools/listtools/call (get_brain_identity + search). Pins the advertised core-tool set against what the server actually exposes (asserts capture is NOT advertised). This funnel had zero e2e coverage before.
  • test — batch-retry-audit ENOENT case made hermetic (test/audit/batch-retry-audit.test.ts): the "audit dir does not exist" case read the real ~/.gbrain/audit (no temp override) and flaked (kept:1) on any dev machine with a batch-retry-*.jsonl on disk. Now points at a guaranteed-missing temp subdir, matching the file's own hermetic-header contract. Pre-existing isolation bug; fixed since this branch lands next.
  • docs — the two-funnel walkthrough: new docs/tutorials/connect-coding-agent.md covers Path A (connect Claude Code / Codex to a brain you already run) and Path B (start from nothing: local PGLite + claude/codex mcp add gbrain -- gbrain serve), plus the brain-first protocol to paste into CLAUDE.md/AGENTS.md and the four translatable habits (brain-first lookup, ambient capture, briefing-from-your-brain, whoknows). README gains a "Quick start: Claude Code or Codex" fork separating lightweight retrieval from the full autonomous install; INSTALL.md shows the one-command wire-up at the standalone CLI section; mcp/CLAUDE_CODE.md + mcp/CODEX.md cross-link the tutorial and note the publish_skills + capture-is-CLI-only gotchas; tutorial promoted to Shipped in the index.

Test note

Affected files all green: connect + serve-skills-publish-nudge + batch-retry-audit (120 unit) + the stdio e2e (3) + bun run verify (29/29) + typecheck. The full parallel unit suite was OOM-thrashing on this shared box (a sibling Conductor workspace was running its own suite concurrently); the only real failure surfaced was the batch-retry-audit isolation bug, now fixed and confirmed hermetic.

garrytan and others added 22 commits May 30, 2026 22:17
Extract parseAuthCreateArgs; only exclude the --takes-holders value from the
positional search when the flag is present (rest[takesIdx+1] resolved to rest[0]
when takesIdx === -1, silently dropping the name). Add regression test.
…er token

New connect command prints a paste-ready claude-mcp-add block (or --install wires
it + smoke-tests the token via a raw-bearer get_brain_identity probe). Direct HTTP
MCP, literal-token default, URL normalization, token header-injection guard,
--json redaction, execFileSync (no shell). Wired into CLI_ONLY + CLI_ONLY_SELF_HELP
+ handleCliOnly. 58 unit + 3 PGLite-E2E cases; e2e-test-map updated.
…ADME one-liner

Regenerate llms-full.txt for the README change.
- DRY: single DEFAULT_PROBE_TIMEOUT_MS + shared isAuthErrorMessage predicate
- reuse promptLine (shared stdin lifecycle) for the --install confirm
- harden redactToken with a Bearer <value> scrub (defense in depth)
- +8 tests: orchestrator guard paths, deterministic timeout, invalid --timeout-ms,
  Bearer-redaction
- probe: Promise.race the call against a real timer so a stalled connect()/SSE
  handshake (signal alone doesn't cover it) can't hang --install indefinitely
- probe: close transport even if client.connect() throws
- parseArgs: reject a missing/flag-shaped value (e.g. --token --install)
- block link-local / cloud-metadata hosts (169.254/fe80:/fd00:ec2::254) — keeps
  localhost + RFC1918 LAN brains working
- non-interactive --install now requires --yes
- clearer message when --force removed then add failed
+8 tests covering each
- POSIX single-quote the rendered claude-mcp-add command so a token with shell
  metacharacters ($(), backticks) can't trigger command substitution on paste
- detect IPv4-mapped IPv6 metadata addresses (::ffff:169.254.x.x / ::ffff:a9fe:*)
  so they don't bypass the link-local guard
+3 tests
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…(v0.42.2.0)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…n-client-onboard

# Conflicts:
#	CHANGELOG.md
#	VERSION
#	package.json
#	src/cli.ts
--agent codex emits 'codex mcp add ... --bearer-token-env-var GBRAIN_REMOTE_TOKEN'
(token read from the env var at runtime, never in Codex config; --install runs it).
--agent perplexity prints the URL + token for the Settings → Connectors GUI (no
--install). Generalized the command file: AGENT_SPECS table, buildCodexMcpAddArgv,
cmdString(binary,argv), binary-generic ConnectDeps (hasBinary/runBinary/env),
agent-aware buildConnectBlock/buildJson. +25 tests.
…OG, CLAUDE.md)

Regenerate llms-full.txt for the CLAUDE.md/README edits.
…a live server

Adds claude-code + codex cases to connect-bearer.test.ts that run the real
'claude mcp add' / 'codex mcp add' through 'gbrain connect --install' against a
live 'gbrain serve --http' (sandboxed HOME/CODEX_HOME), then assert via
'claude mcp get' / 'codex mcp get' that the server registered (and codex's token
stays out of config). Skips when the binary is absent. Perplexity is GUI-only so
it's print-asserted. Regen llms for the CLAUDE.md note.
…exity feedback)

PERPLEXITY.md now documents the host-side HTTP setup (gbrain serve --http
--bind 0.0.0.0 --public-url, the v0.34 ECONNREFUSED footgun) and the OAuth 2.1
client_credentials path (gbrain auth register-client) alongside the legacy
bearer token. The 'connect --agent perplexity' output points at the same
bind/public-url requirement + PERPLEXITY.md.
…/generic

OAuth is the correct path for a third-party cloud connector (Perplexity): instead
of a long-lived full-access bearer token, the connector gets Issuer URL + Client
ID + Client Secret and mints short-lived scoped tokens. --oauth --register mints a
least-privilege client on the host (shells gbrain auth register-client); --oauth
--client-id/--client-secret uses an existing one. Rejected for claude-code/codex
(bearer) and with --install. Issuer derived from the mcp-url. New E2E proves the
full chain: register → connect --oauth → OAuth discovery → /token client_credentials
mint → get_brain_identity tool call against a live server. Docs: PERPLEXITY.md leads
with OAuth; README + CLAUDE.md updated; +18 unit cases.
The remote-client onboarding command was documented in README/CLAUDE_CODE/CODEX/
PERPLEXITY but missing from INSTALL.md §3 (the natural 'how do I connect a client'
home). Add the one-command connect how-to (claude-code/codex/perplexity) and the
missing docs/mcp/CODEX.md link.
The self-orientation block told a connected agent that `capture` is an
available MCP tool. It isn't — `capture` is a CLI-only convenience command;
the MCP write tool is `put_page`. An agent that followed the instruction hit
"unknown tool". Drop capture; put_page was already in the list. Adds a
regression block to connect.test.ts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
When mcp.publish_skills is OFF, connected agents can search/write but can't
call list_skills/get_skill, so the host's skill catalog is invisible to them.
The startup banner now shows a Skills: line, and a stderr nudge fires when off
with the paste-ready fix. Pure skillPublishStatus() helper, unit-tested.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Spawns real `gbrain serve` (stdio) against a freshly init --pglite brain and
drives the official MCP SDK client through initialize -> tools/list ->
tools/call (get_brain_identity + search). Pins the advertised core-tool set
against what the server actually exposes (asserts capture is NOT advertised).
This funnel had zero e2e coverage before.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The 'no-op when audit dir does not exist' case called
pruneOldBatchRetryAuditFiles(30) without a GBRAIN_AUDIT_DIR override, so it
read the real ~/.gbrain/audit and flaked (kept:1) on any dev machine with a
batch-retry-*.jsonl on disk. Point it at a guaranteed-missing temp subdir,
matching this file's own hermetic-header contract.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
New tutorial docs/tutorials/connect-coding-agent.md: Path A (connect to an
existing brain) + Path B (start from nothing, local stdio), the brain-first
protocol to paste into CLAUDE.md/AGENTS.md, and the four translatable habits.
README gains a 'Quick start: Claude Code or Codex' fork separating lightweight
retrieval from the full autonomous install. INSTALL.md shows the one-command
wire-up at the standalone CLI section. mcp/CLAUDE_CODE + CODEX cross-link the
tutorial + note publish_skills + capture-is-CLI-only. Tutorial promoted to
Shipped in the tutorials index.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@garrytan garrytan merged commit 7b0d99a into master Jun 2, 2026
21 checks passed
mgunnin added a commit to mgunnin/gbrain that referenced this pull request Jun 3, 2026
* upstream/master:
  v0.42.8.0 feat: content-quality gate on sync — quarantine junk + flag boilerplate (garrytan#1699) (garrytan#1756)
  v0.42.7.0 feat(extract): link/timeline extraction freshness watermark — gbrain extract --stale + doctor lag check (garrytan#1696) (garrytan#1755)
  v0.42.6.0 feat(enrich): gbrain enrich --thin — brain-internal grounded synthesis for stub pages (garrytan#1700) (garrytan#1757)
  v0.42.5.0 fix(minions): RSS watchdog opacity + pooler-reap self-heal + silent lens backlog + cycle lint DB-disconnect (garrytan#1678) (garrytan#1735)
  v0.42.4.0 fix: think --model fails loud — slash-form ids + never persist empty synthesis (garrytan#1698) (garrytan#1736)
  v0.42.3.0 feat(search): autocut — score-discontinuity result-sizing (garrytan#1663 wave 1) (garrytan#1682)
  v0.42.2.0 feat: gbrain connect — one-command Claude Code onboarding from a bearer token (garrytan#1683)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant