MCP for Gemini app
  • TypeScript 55.5%
  • Nix 44.5%
Find a file
2026-10-14 21:59:01 -04:00
nix feat: implement bard remote OAuth 2.1 MCP server 2026-10-14 20:20:03 -04:00
src feat(mcp): tell Gemini the tools run on the user's computer 2026-10-14 21:38:36 -04:00
tests feat(auth): include RFC 9207 iss in authorization responses 2026-10-14 21:17:18 -04:00
.gitignore feat: implement bard remote OAuth 2.1 MCP server 2026-10-14 20:20:03 -04:00
.prettierrc feat: implement bard remote OAuth 2.1 MCP server 2026-10-14 20:20:03 -04:00
bun.lock refactor(auth): switch to sliding refresh tokens and clean up job/rate limits 2026-10-14 20:32:33 -04:00
bun.nix feat: implement bard remote OAuth 2.1 MCP server 2026-10-14 20:20:03 -04:00
bunfig.toml feat: implement bard remote OAuth 2.1 MCP server 2026-10-14 20:20:03 -04:00
flake.lock feat: implement bard remote OAuth 2.1 MCP server 2026-10-14 20:20:03 -04:00
flake.nix feat: implement bard remote OAuth 2.1 MCP server 2026-10-14 20:20:03 -04:00
LICENSE feat: implement bard remote OAuth 2.1 MCP server 2026-10-14 20:20:03 -04:00
package.json refactor(auth): switch to sliding refresh tokens and clean up job/rate limits 2026-10-14 20:32:33 -04:00
README.md docs: document Gemini linking flow and custom instructions 2026-10-14 21:59:01 -04:00
THIRD_PARTY_NOTICES.md feat: implement bard remote OAuth 2.1 MCP server 2026-10-14 20:20:03 -04:00
tsconfig.json feat: implement bard remote OAuth 2.1 MCP server 2026-10-14 20:20:03 -04:00

bard

bard connects the official Gemini web and mobile apps (Connected Apps → Custom apps) to your own machine over MCP. Once linked, Gemini can read files, edit code, and run commands on your host from your browser or phone.

Tools run directly on the host as the service user — there is no container or sandbox. Access is gated by a built-in, single-owner OAuth 2.1 server whose approval page stays dead-bolted until you arm a single-use, 10-minute link code from the server shell.

Tools

bard exposes OMP's core workspace tools plus durable background Fish jobs. read, glob, and grep are annotated read-only; everything else is marked destructive so Gemini asks before changing files or running commands.

Tools Mode Description
read, glob, grep Read-only Read files, match paths, and search contents without confirmation prompts
write, edit Confirmation required Create files or apply surgical edits
bash Confirmation required Run a foreground Fish command and return its output
process, jobs Confirmation required Start, inspect, and stop long-running background Fish jobs

Setup

1. Run the server

Put bard behind a TLS reverse proxy (Caddy, nginx, Cloudflare Tunnel…) and pass its public origin:

HOST=127.0.0.1 PORT=3000 ORIGIN=https://bard.example.com BARD_WORKSPACE=$HOME nix run .#bard

Caddy example:

bard.example.com {
	reverse_proxy 127.0.0.1:3000
}
Variable Default Meaning
HOST 127.0.0.1 Listen address
PORT 3000 Listen port
ORIGIN from X-Forwarded-Proto/-Host Public https:// origin used in OAuth metadata; set explicitly in production
BARD_WORKSPACE current directory Tool working directory
BARD_STATE_DIR $XDG_STATE_HOME/bard (~/.local/state) Private (0700) state: auth.sqlite, workspace/jobs.json
BARD_WORKSPACE_TOOLS all eight Comma-separated allowlist of read,write,edit,glob,grep,bash,process,jobs

For production, run bard under systemd. Background jobs start in their own session, so if bard dies uncleanly (SIGKILL, OOM), only cgroup teardown stops them — systemd's default KillMode=control-group handles this automatically, and bard marks those jobs interrupted on restart. A dropped HTTP connection aborts its foreground bash command, so keep proxy read timeouts generous and use process for long work.

  1. On the server, arm a link window (using the same BARD_STATE_DIR as the server):
    nix run .#bard-cli -- link
    
    This prints the Client ID, Client secret, and a one-time link code valid for 10 minutes.
  2. Open gemini.google.com → Settings → Connected Apps → + Custom (Create custom app).
  3. Enter MCP Server URL https://bard.example.com/mcp, expand Additional settings, paste the Client ID and Client secret, and click Next. (bard does not support dynamic client registration, so credentials are required.)
  4. Under Connect this MCP server, tick the risk acknowledgement and click Connect.
  5. On Google's Link your Google Account and [Custom] … account prompt, click Agree and continue.
  6. In bard's approval popup, enter the 10-minute link code and click Approve. Google redirects back and exchanges the code at /token.
  7. Under Save your custom app, verify bard's tools are listed and click Connect. bard now appears under Custom apps on both web and mobile.

To re-link later, run bard-cli link again for a fresh code; the Client ID and secret stay the same unless you run rotate-client.

Troubleshooting

  • "Account linking is required to use this custom app" — Google never redeemed the authorization code (bard-cli status will show zero active tokens). Browser extensions that inject into pages (password managers, autofill) can break the popup redirect; disable them or set their site access to "On click" for gemini.google.com, then link again.
  • Gemini answers from its own sandbox (for example, showing a gvisor kernel) instead of calling bard — either prefix the prompt with @Bard or add the standing instructions in step 3.

3. Tell Gemini about bard

bard's MCP initialize response and tool descriptions already identify your host (hostname, OS, user, working directory), but Gemini still decides per prompt whether to invoke a connected app. Giving it standing context prevents it from falling back to its own cloud sandbox.

Paste this into Gemini under Settings → Personal Intelligence → Instructions for Gemini, filling in your host details:

I have a custom connected app called Bard. It is my own MCP server running on my computer
(host "<hostname>", <OS>, user "<user>"). Bard is always available to you and is how you reach
my real machine.

- Anything about "my PC", "my computer", my system, files, projects, configs, running processes,
  logs, or "run/check/fix X": use Bard's tools. Never answer those from your own code sandbox.
  Its environment is not my machine.
- Bard tools: read, glob, grep (inspect files), write and edit (change files), bash (run a
  command and return its output), process and jobs (start and manage long-running background
  Fish commands).
- Check first, then answer: run the command or read the file instead of guessing or telling me
  to run it myself.
- Use process for anything that may take more than a minute (builds, downloads, rebuilds), then
  check on it with jobs.
- Before changing files or running anything destructive, briefly say what you'll do. I'll
  confirm in the approval prompt.
- Act like my personal assistant with hands on my machine: do the task end to end, then report
  what you ran and what you found.

Managing access (bard-cli)

Command Effect
bard-cli link Print Client ID/Secret and arm a 10-minute link code (5 wrong tries burn it)
bard-cli status Show Client ID, pinned redirect URI, link window, and active token counts
bard-cli revoke Invalidate every token, auth code, and link code immediately
bard-cli rotate-client Generate a new Client ID/Secret, unpin the redirect URI, and revoke everything
bard-cli unpin-redirect Allow the next approved link to pin a new callback URL

How it works & security model

OAuth 2.1 flow

bard runs a self-hosted OAuth 2.1 authorization server (authorization code + PKCE S256 with a static confidential client).

sequenceDiagram
    participant You
    participant Gemini as Gemini (Google)
    participant Bard as bard
    You->>Bard: bard-cli link (prints Client ID, Secret, link code)
    You->>Gemini: MCP URL + Client ID + Client secret, Next
    Gemini->>You: popup → https://host/authorize
    You->>Bard: enter link code, Approve
    Bard-->>Gemini: 302 to Google callback with auth code
    Gemini->>Bard: POST /token (client credentials + PKCE verifier)
    Bard-->>Gemini: access token (1 h) + refresh token (30 d, sliding)
  • Persistent linking: Google stores the tokens and refreshes them silently. Each refresh extends the refresh token's 30-day expiry instead of rotating it, keeping web and mobile connected until you run bard-cli revoke.
  • Callback pinning: The first approved link pins Google's callback URL; subsequent authorizations must match it unless cleared with bard-cli unpin-redirect.
  • Credential storage: Access tokens, refresh tokens, auth codes, and link codes are stored only as SHA-256 hashes. The client secret is stored in the 0600 SQLite database (auth.sqlite) so bard-cli link can print it.
  • Endpoints: /.well-known/oauth-protected-resource[/mcp], /.well-known/oauth-authorization-server (and /openid-configuration), /authorize, /token, /mcp. Dynamic client registration is intentionally omitted.

Host execution & state

  • Environment scrubbing: At startup, bard strips the process environment (including the C environ inherited by the native shell) down to a minimal allowlist (PATH, HOME, locale, TLS…) so env does not leak service credentials into model context. This is hygiene, not isolation: tools run as your user and can read anything your user can, including /proc/self/environ and bard's state directory.
  • Transport & job persistence: /mcp uses stateless Streamable HTTP with JSON responses, while shell sessions and background jobs live in the long-running server process across requests. Job metadata persists in workspace/jobs.json and retains the newest 100 finished jobs.

Development

nix develop
bun install
bun run check && bun test
bun2nix -o bun.nix   # after dependency changes