Publish the user's own gbrain over MCP so other devices, desktop apps and cloud agents can reach it. `gbrain mcp expose` installs and signs in Tailscale, publis
复制下面这句话,粘贴给 Claude Code、Codex、Cursor 等 AI 编程工具,它会读取安装说明并在你确认后完成安装。
请阅读 https://ai.atlankj.com/install/asset/gh-remote-mcp-0a3e404fd197 ,按照其中的说明把「remote-mcp」安装到你(当前 AI 工具)中。执行前先告诉我将运行的命令和写入的位置,等我确认。
查看 AI 将读取的安装说明正在读取 GitHub 原文…
内容来自 GitHub 原始文件,由原作者维护。在 GitHub 查看
The brain already runs on the user's computer. This skill makes it reachable over MCP from their other devices and from the agents they use elsewhere, without moving the data and without putting a database on the public internet. Tailscale is the default transport; publishing is one command, access is one scoped grant per client.
This skill guarantees:
gbrain mcp expose publishes the local server
with tailscale serve (HTTPS on the tailnet, nothing public). ngrok and
cloud hosts stay documented alternatives, never the first suggestion.--funnel, which makes the same *.ts.net name publicly reachable. Say
so before running it; the endpoint is then protected by gbrain's OAuth /
bearer auth and scoped grants, not by the network.tailscale up, writing a serve/funnel config and creating a user service
are host-state changes. Show the operator the printed plan and get a yes
before passing --yes.gbrain mcp expose never opens the database. On a PGLite
brain the running server owns the single-writer lock, so every later
provisioning step goes through the server's authenticated admin API
(--admin-token-file), never a second process on the database.admin_token_file (normally ~/.gbrain/serve/admin-token);
machine and confidential OAuth credentials land in a private
--credentials-out file. Public PKCE has no secret. Quote redacted
receipts; deliver a requested owner login link privately and unopened.GBRAIN_ADMIN_BOOTSTRAP_TOKEN; that file authorizes
gbrain mcp admin and mcp grant against that server. An ordinary MCP
token, client secret, or OAuth admin scope does not. With --no-service,
use the independently running server's actual configured owner credential;
creating an expose token file does not configure that process.memory-writer for ordinary
memory use or memory-reader for read-only access. Broader access requires
the user's request. Never operator / full / admin to make a convenience
check pass.gbrain mcp verify accepts a
private machine handoff and checks the access that grant permits. Native
OAuth is verified through the native client's authenticated connection.
A generated file or server probe does not prove that client loaded its tools.| Client | Shape | Command |
|---|---|---|
| Your own devices: Claude Desktop, Claude Code / Codex / opencode on another laptop, phone apps joined to the tailnet | tailnet-only HTTPS (default) | gbrain mcp expose --dry-run (preview; the run itself is Phase 2, after consent) |
| Cloud agents running in a vendor's cloud: Grok Bot, Muse, ChatGPT connector, Claude.ai / Cowork, Perplexity Computer | public HTTPS on the same *.ts.net name | gbrain mcp expose --funnel --dry-run (preview; Phase 2 after consent) |
Cloud agent whose runtime you can join to your tailnet (userspace tailscaled, ephemeral auth key) | tailnet-only | Advanced, unverified, not automated — see the remote MCP guide |
| Local agents on the same machine (Claude Code, Codex, opencode) | loopback — no Tailscale needed | Postgres: gbrain bootstrap harness --yes --port 3131. PGLite: mint the token BEFORE the service runs (gbrain auth create local-agents --scopes read,write — before gbrain mcp expose, or while the service is briefly stopped) and pass gbrain bootstrap harness --yes --port 3131 --token <value>; OR use the scoped path gbrain mcp grant <name> --harness <id> --profile memory-writer --source default --url http://127.0.0.1:3131/mcp --admin-token-file ~/.gbrain/serve/admin-token --credentials-out /private/<name>.json then gbrain connect http://127.0.0.1:3131/mcp --harness <id> --credentials-file /private/<name>.json --install (MCP wiring only, no per-turn hooks) |
| Thin client only (this machine has no brain) | — | Stop: run this skill on the brain host |
Grok Bot and Muse users with no always-on machine keep the in-agent local install described in setup as the alternative.
Network reach and authentication are separate choices. A vendor name or tailnet/Funnel mode does not select the credential flow: inspect the intended client's native OAuth settings or supported private-handoff adapter in Phase 3.
gbrain engine status --json
gbrain mcp expose --status --json
thin_client: true → the brain lives elsewhere. Stop and say the command
runs on the brain host.effective_engine: "pglite" → note the single-writer rule (below) before
continuing; "postgres" → concurrent local commands stay fine.--status reports status: "not_exposed" (exit 2 with --json) →
Phase 2. status: "exposed" → skip to Phase 3 with the printed
mcp_url. status: "pending" → the certificate or service is still coming
up; re-run --status in a minute before changing anything.
reason: "leftovers_without_receipt" (exit 1) → an interrupted run left
the wrapper, the unit / service or a :443 handler behind; run the exact
gbrain mcp expose --remove --yes … line from next_actions (it carries
--force only when a handler stands alone), then Phase 2.serve --http behind ngrok / a reverse proxy, or a brain hosted elsewhere) →
do NOT run gbrain mcp expose. Go straight to hosted access: choose the
client's connection method and register it against that endpoint (Phase 3
with its URL and owner authority), then configure the client (Phase 4).--funnel). Do not ask questions the user already answered.Preview first, then run with consent:
gbrain mcp expose --dry-run # prints the plan; changes nothing
gbrain mcp expose --yes # your own devices (tailnet only)
gbrain mcp expose --funnel --yes # a cloud agent must reach it
Options: --port N (default 3131), --surface verbs|starter|full (default
full, mirrors serve --http), --enable-dcr, --no-tailscale (publish
nothing; just install the service), --no-service (publish only; the
operator runs serve themselves), --no-install (fail instead of
installing Tailscale), --force (take over a serve handler that already
points at another local port), --json.
What the command does, in order: plan (refuses up front when anything —
HTTP or not — already listens on the port and --no-service is absent, and
when --no-tailscale is passed over a receipt that records a live tailnet
mapping) → consent → find or install the tailscale binary → sign in if
needed (Linux: sudo tailscale set --operator=$USER, then sudo tailscale up
— sudo only in front of a system-installed binary; the operator preference
is persistent and --remove does not revoke it; macOS: tailscale up) →
read the machine's tailnet DNS name and pre-check that HTTPS certificates are
enabled (and, with --funnel, that the node has the Funnel capability) →
tailscale serve / funnel → ensure the admin token →
write the wrapper and a launchd agent / systemd user unit → poll local and
tailnet /health → write the receipt ~/.gbrain/serve/expose.json. With
--json every step is a named check (plan, consent, tailscale.binary,
tailscale.login, tailscale.identity, tailscale.publish, admin_token,
service, verify.local, verify.tailnet, receipt); the health checks
are verify.local and verify.tailnet, not verify.
Relay every prompt the command surfaces:
| Output | What you do |
|---|---|
Tailscale login URL printed by tailscale up | Give the user the URL, wait for them to sign in; on exit 2 (tailscale_login_pending) re-run the exact command the output prints |
declined (exit 2, "Nothing changed.") | The operator said no at the prompt. Nothing was installed or published; pass --yes only after they confirm the printed plan |
tailscale_https_not_enabled — pre-check, exit 2, nothing published (CertDomains empty) | Enable MagicDNS + HTTPS Certificates at https://login.tailscale.com/admin/dns, then re-run the printed command. The same reason with exit 1 means the tailscale serve call itself refused for the same cause — same fix |
tailscale_funnel_not_enabled — pre-check, exit 2, nothing published (the node lacks the Funnel capability) | Enable the funnel node attribute in the tailnet policy (https://login.tailscale.com/admin/acls; see https://tailscale.com/kb/1223/funnel), then re-run. Exit 1 with this reason is the post-funnel classified form — same fix |
tailscale_needs_operator | Run sudo tailscale set --operator=$USER, then re-run |
tailscale_unknown (exit 1) — tailscale serve --bg did not finish within 60s | The CLI is waiting on the operator (an enablement step the pre-checks could not see). Have them run the printed tailscale serve --bg <port> by hand to see its prompt, then re-run |
tailscale_needs_login | Linux: sudo tailscale set --operator=$USER then sudo tailscale up; macOS: tailscale up or open the Tailscale app and sign in; then re-run |
tailscale_login_manual (exit 2) — "tailscale at is not a system install, so gbrain will not run it with sudo" | The tailscale on PATH is user-writable (e.g. ~/.local/bin); gbrain never runs it as root. The printed command names that path — sudo <path> set --operator=$USER && sudo <path> up (a bare sudo tailscale would not find a binary outside sudo's secure_path). Running it is the operator choosing to trust that binary: say so, let them run it themselves, then re-run the printed command |
Never work around a classified error by editing Tailscale state by hand; apply the printed fix and re-run.
Use the successful expose receipt's mcp_url and admin_token_file; substitute
them for the example URL/default path below, including when GBRAIN_HOME is
customized. Run owner commands on the host or in a separately authorized admin
harness with that server's protected credential. An ordinary connecting client
hands these steps to that administrator; its OAuth scopes cannot grant owner
authority. Follow mcp-access and
MCP administration for owner actions and recovery.
To open the dashboard:
gbrain mcp admin login-link --url https://your-machine.your-tailnet.ts.net/mcp \
--admin-token-file ~/.gbrain/serve/admin-token --json
Deliver the single-use link privately without fetching it. The plain admin URL
does not authenticate. For pending native consent, add --oauth-request ID
with the exact opaque ID from the client's browser request; the owner then
reviews consent separately. Expired requests or a restart require restarting
authorization in the native client.
| Intended connection | Register | Deliver |
|---|---|---|
| Native public PKCE | mcp admin register, method none, exact client callback | Native OAuth metadata; no client secret |
| Native confidential PKCE | mcp admin register, actual client_secret_post or client_secret_basic, exact callback | Private OAuthClientSetup via --credentials-out |
| Machine credentials / managed bearer configuration | mcp grant --harness ID | Private machine handoff for gbrain connect |
For native OAuth, obtain the actual redirect URI and authentication method from
the intended harness. CLIENT_REDIRECT_URI below must be replaced with that
exact URI; it is not a suggested callback:
gbrain mcp admin register native-example --redirect-uri 'CLIENT_REDIRECT_URI' \
--token-endpoint-auth-method none --profile memory-writer --source default \
--url https://your-machine.your-tailnet.ts.net/mcp \
--admin-token-file ~/.gbrain/serve/admin-token --dry-run --json
Review the preview, then repeat without --dry-run. For confidential PKCE, use
the client's actual POST/Basic method and add --credentials-out PRIVATE_FILE
before creation. Native registration creates authorization-code and refresh
grants, not a machine token. Retrieve instructions using its returned ID:
gbrain mcp admin setup CLIENT_ID --harness generic --flow authorization-code \
--url https://your-machine.your-tailnet.ts.net/mcp \
--admin-token-file ~/.gbrain/serve/admin-token --json
Use the actual supported adapter ID when known; generic gives manual native
settings instructions. The native client initiates OAuth and owns its verifier.
An OAuthClientSetup is not a machine handoff for gbrain connect or
gbrain mcp verify. Keep DCR off unless the owner explicitly chooses it; manual
owner registration works without --enable-dcr.
For a client using a private machine handoff, preview through the same running server's admin API:
gbrain mcp grant agent-example --harness grok-bot --profile memory-writer \
--source default \
--url https://your-machine.your-tailnet.ts.net/mcp \
--admin-token-file ~/.gbrain/serve/admin-token \
--credentials-out /private/agent-example.json --dry-run --json
--harness is the real adapter id (gbrain mcp adapters): grok-bot,
muse, claude-desktop, codex, claude-code, opencode, ...--dry-run to create the client.memory-reader, memory-writer, coding-agent, operator,
delegating-agent, full) and delegation limits are defined in
hosted harness access;
default to memory-writer, or memory-reader when only reading is requested.| Client | Install |
|---|---|
| Any native OAuth client, including a supported Desktop/cloud connector | Enter the native registration's actual endpoint, client ID, callback, and method in its supported settings; confidential clients also receive the private OAuth secret. Start authorization in that client and obtain owner consent. Follow its docs/mcp/ guide when manual client configuration is unavailable; do not substitute machine credentials. |
| Grok Bot using the private machine path | Inside the Bot: gbrain connect https://your-machine.your-tailnet.ts.net/mcp --harness grok-bot --credentials-file /private/agent-example.json --install --root /workspace/gbrain; enable the generated instruction as a native skill (a visible, separate step) |
| Muse using the private machine path | Same gbrain connect … --harness muse … --install --root <verified durable root>; establish the durable user-files location first — never /tmp, never an invented path |
| Claude Code / Codex / opencode using managed private configuration on another machine | gbrain connect https://your-machine.your-tailnet.ts.net/mcp --harness codex --credentials-file /private/agent-example.json --install; select the actual adapter |
| Local agents on the brain host | Postgres: gbrain bootstrap harness --yes --port 3131. PGLite: bootstrap harness refuses under the live serve unless --token is passed — mint BEFORE the service runs (gbrain auth create local-agents --scopes read,write, run before gbrain mcp expose or while the service is briefly stopped) and pass gbrain bootstrap harness --yes --port 3131 --token <value>; or skip hooks and use the scoped gbrain mcp grant … --url http://127.0.0.1:3131/mcp --admin-token-file ~/.gbrain/serve/admin-token --credentials-out /private/<name>.json + gbrain connect http://127.0.0.1:3131/mcp --harness <id> --credentials-file /private/<name>.json --install path (MCP wiring only) |
| ChatGPT / Perplexity / other cloud OAuth clients | Requires explicit --funnel when the runtime is outside the tailnet; follow the native OAuth path and per-client page under docs/mcp/ with the published URL |
Tailnet-only endpoints are reachable only from devices signed in to the same
tailnet — if a device cannot resolve your-machine.your-tailnet.ts.net,
install Tailscale there and sign in; do not switch to --funnel for that.
For the private machine path, from the client's environment:
gbrain mcp verify --client CLIENT_ID --harness grok-bot \
--url https://your-machine.your-tailnet.ts.net/mcp \
--credentials-file /private/agent-example.json --json
server_status: "passed" proves the permitted server checks; writable grants
also exercise randomized write/readback and cleanup. Native activation remains
separate; exit 2 (partial) is not completed native verification.
For native OAuth, observe gbrain://capabilities and an allowed call through
that harness's authenticated MCP connection. Do not pass an OAuth setup export
to the private-handoff verifier. For either path, observe calls in the intended
harness and inspect the advertised schemas before a memory test. With an
authorized writable grant, use only a harmless synthetic fact
with provenance and explicit visibility: "world" within its source grant;
retain its returned ID, verify recall in a new conversation, correct it, and
withdraw it. Private facts cannot be recalled or withdrawn through these remote
operations; never change real private content to pass a test. With read-only
access, observe an authenticated read without adding write authority. Report
registration, setup delivery, server checks, native calls, and cleanup separately.
Host-side check at any time: gbrain mcp expose --status (without a receipt
it reports leftovers as leftovers_without_receipt, exit 1, with the exact
cleanup command). Remove the exposure and host service:
gbrain mcp expose --remove (stops and removes the service — also when the
receipt says it was skipped but the unit exists — clears only gbrain's
serve/funnel handler with --https=443 --set-path=/ off, keeps Tailscale
installed and signed in — the Linux operator preference too — and keeps the
admin token unless --force; without a receipt — an interrupted publish — it
recovers from what is on disk: the wrapper, the unit and the :443 handler
for --port, default 3131, leaving the token; the handler is turned off only
when the wrapper, unit or service corroborates it — a handler standing alone
is reported and left until --force). A handler that survives the off is
never reported as removed: exit 1 handler_not_removed, receipt + wrapper
kept for the re-run. An unreadable tailscale serve status stops both paths
with exit 1 tailscale_serve_status_unreadable (the service is already
removed; receipt and/or wrapper kept) — fix Tailscale, re-run the printed
command. Declining the prompt exits 2 with "Nothing changed."; a
--no-service re-run keeps an existing service.
Removing exposure does not revoke client registrations or erase brain data. Use the owner lifecycle commands in MCP administration to invalidate tokens, revoke, or delete a selected client after reviewing its consequences and revision.
While the exposed server runs, it holds the PGLite lock. Host-side commands
that open the database (gbrain doctor, gbrain mcp grant WITHOUT an owner
file or protected GBRAIN_ADMIN_BOOTSTRAP_TOKEN, gbrain bootstrap harness
without --token) FAIL FAST
with live_serve — they do not wait. Administer through the running server
instead. gbrain sync and gbrain sweep --once are the exceptions: they
delegate into the live serve automatically. Always provision through
--admin-token-file; if the user needs concurrent local commands, route to
postgres-adopt rather than stopping the server.
gbrain serve --http --bind 0.0.0.0 for this shape. Tailscale Serve
terminates TLS and forwards to loopback; the default bind is correct.--funnel silently. Say that it makes the endpoint publicly
reachable and why this client needs it.tailscale serve reset, tailscale logout, or uninstall
Tailscale to clean up — gbrain mcp expose --remove touches only gbrain's
handler.operator/full/admin because a health check failed.admin scope as owner authority, or install a native
OAuth setup file as a private machine handoff.mcp verify passed; native
activation and new-conversation recall are observed in the client.Report the host state and one line per client:
MCP URL https://your-machine.your-tailnet.ts.net/mcp (reach: tailnet only | public via Funnel)
Service launchd com.gbrain.serve, running (log: ~/.gbrain/serve/serve.log)
Engine pglite — provision via --admin-token-file
Client agent-example (grok-bot, memory-writer): granted, installed, verify passed, native activation pending
Pending <exact next command, or "nothing">
Quote gbrain mcp expose --status and gbrain mcp verify output as-is (both
are redacted). Name every unverified step explicitly; a passing server check
never stands in for observed recall inside the client.
tailscale_receipt_present (exit 1, plan fails) — "This brain is already published on your tailnet at " | --no-tailscale was passed while a live Serve/Funnel mapping is on record. Run gbrain mcp expose --remove --yes first, or drop --no-tailscale |
tailscale_daemon_not_running (exit 2) | macOS open -a Tailscale; Linux sudo systemctl enable --now tailscaled; then re-run |
foreign_serve_config — "tailscale serve already proxies :443 to …" (exit 1) | Show the user what the existing handler proxies (tailscale serve status); only --force on their explicit yes. A handler owned by another terminal's foreground tailscale serve session cannot be taken over — it must be stopped in that terminal |
foreign_listener — "Something already answers on 127.0.0.1: and no expose receipt claims it" (exit 1, refused BEFORE anything is published; ANY listener counts — an accepted TCP connect from a non-HTTP service as much as a 404) | Stop the other process, pick another --port, or pass --no-service to publish it as-is. If it is a gbrain server left by an interrupted run, gbrain mcp expose --remove --yes first (receipt-less recovery) |
no_brain_config (exit 1, plan fails) — "No brain is configured on this host (gbrain init first), so a service would only crash-loop" | Run gbrain init on this host first, then re-run; or pass --no-service to only publish a server the operator starts themselves. Never install the service around a missing brain |
pglite_lock warn — "a live process holds this PGLite brain (pid N, …)" | Tell the user which process holds the lock; the service cannot start until it exits. Stop it (or route to postgres-adopt for concurrent use), then gbrain mcp expose --status |
"could not read tailscale serve status" — tailscale.publish fails (publish: exit 1 tailscale_<kind>, nothing published; --status: exit 1; --remove: exit 1 tailscale_serve_status_unreadable — with a receipt, receipt + wrapper kept; without one, the service is still removed but the wrapper is kept as the corroboration the re-run needs) | The command fails closed instead of guessing. Run tailscale serve status --json, apply the classified fix (operator / daemon / login), then re-run the same command |
handler_not_removed (--remove, exit 1) — "the tailscale handler for port is still present" / "could not be confirmed gone" | The off ran but gbrain's handler survived (or the re-read failed). The service is already gone; the receipt and wrapper were kept on purpose. Run tailscale serve status, fix what it reports, then gbrain mcp expose --remove --yes again — never serve reset |
verify.tailnet: warn — "this host cannot resolve " (exit 0) | MagicDNS is off on the brain host itself; tailscale set --accept-dns=true, then gbrain mcp expose --status. Other devices may already reach the server; do not treat it as a certificate wait |
service: manual (no supervisor: cloud sandbox, container) | Relay the printed foreground and nohup … & commands; this is a documented outcome, not a failure |
verify.local: warn (exit 2, local_health_timeout) | The handler is published, the service installed and the receipt written — this exit 2 is NOT "nothing published"; --status / --remove find them. /health did not answer within the wait: check ~/.gbrain/serve/serve.err, then gbrain mcp expose --status |
verify.tailnet: pending (exit 2, tailnet_health_pending; --status reports it with exit 1) | Same: handler, service and receipt are already in place. First certificate issuance can take a minute; gbrain mcp expose --status later |
confirmation_required (exit 2) | Non-TTY run without --yes. Nothing changed; show the printed plan, get the operator's yes, then re-run with --yes |
tailscale_no_dns_name (exit 1) — the node has no MagicDNS name | Enable MagicDNS + HTTPS Certificates at https://login.tailscale.com/admin/dns, then re-run; nothing was published |
tailscale_missing (exit 1, --no-install) / tailscale_unsupported_platform / tailscale_install_failed (exit 1) | Tailscale is not installed and was not (or could not be) installed. Relay the printed install command or https://tailscale.com/download, have the user sign in, then re-run |
tailscale_publish_unconfirmed (exit 1) — serve --bg exited 0 but the re-read shows no handler (or could not be read) | Run tailscale serve status; gbrain mcp expose --remove --yes clears a handler for the port without a receipt (--force when no wrapper or service of gbrain's exists yet), then re-run |
service_install_failed (exit 1) | The handler IS published and the receipt written with service.state: stopped. Read the error and ~/.gbrain/serve/serve.err, fix, re-run; or gbrain mcp expose --remove --yes |
status_unhealthy (--status, exit 1) | A named check failed (tailscale.publish, service, verify.local); apply that row, then gbrain mcp expose --yes repairs |
recovered_without_receipt (--remove, exit 0) | The receipt-less recovery finished: report what was removed and what was left (the admin token stays) |