Contents
Connect your Passport to any AI app
One memory for the business, in every assistant your team already uses. A passport is reachable two ways:
- Local (desktop apps that run MCP servers on your machine: Claude Desktop, Claude Code, Cursor, VS Code, Codex, Windsurf, Gemini CLI). They start
open-passport mcpover stdio against a passport folder, or against Principia's live vault. - Hosted (web apps that only take a URL: ChatGPT, Claude.ai). They connect to a host's MCP endpoint,
https://<host>/v1/passports/<id>/mcp, and sign in with OAuth ("Connect your Passport"). Desktop apps can use the hosted URL too.
Every client sees the same six tools (SPEC.md §9.4), filtered by what you allowed:
| Tool | What it does | Cost |
|---|---|---|
passport_context | The company card, plus one department's slot. Models are told to call it first. | ~300 + ~600 tokens |
get_department | Another department's slot | ~600 tokens |
search | Keyword search; returns {results: [{id, title, url}]} (the shape ChatGPT expects) | small |
fetch | One note by id; returns {id, title, text, url, metadata} | ≤ 8000 chars |
remember | One lasting fact on the note it is about | write |
taste | A like, dislike, preference or hard no, with its example | write |
Writes from apps are proposals until you accept them, unless you mark the app trusted. Taste an app inferred always waits for review (SPEC.md §11.5).
1. Get a URL (hosted)#
Skip this for local-only use.
Fly.io (from a clone of this repository):
fly launch --no-deploy --copy-config --name acme-passport
fly volumes create passport_data --size 1
fly secrets set OPEN_PASSPORT_ADMIN_TOKEN=$(openssl rand -hex 24) OPEN_PASSPORT_PUBLIC_URL=https://acme-passport.fly.dev
fly deploy
Render: New → Blueprint → this repository (render.yaml), then set OPEN_PASSPORT_PUBLIC_URL to the service's https URL and redeploy.
Google Cloud Run (no persistent disk; data mirrored to a Cloud Storage bucket): deploy/gcp/deploy.sh prints the whole plan, --apply runs it. Runbook, DNS records and costs: deploy/gcp/README.md.
Anywhere with Docker: docker build -f packages/server/Dockerfile -t open-passport-host . and run it behind an HTTPS proxy with a volume on /data.
| Variable | Required | Meaning |
|---|---|---|
OPEN_PASSPORT_ADMIN_TOKEN | yes (16+ chars) | Creates passports and owner keys. Never give it to an app. |
OPEN_PASSPORT_PUBLIC_URL | yes for ChatGPT / Claude.ai | The https:// origin apps reach. It is the OAuth issuer and every resource URL, so it must not change. |
OPEN_PASSPORT_TRUST_PROXY | no | 1 derives the origin from X-Forwarded-* when no public URL is set. |
OPEN_PASSPORT_ALLOWED_ORIGINS | no | Extra browser origins for the MCP endpoint (ChatGPT, Claude and localhost are built in). |
DATA_DIR | no (/data in the image) | Revisions, grants, oauth.json, audit logs. Put it on a volume. |
OPEN_PASSPORT_GCS_BUCKET | no | Keep DATA_DIR in this Cloud Storage bucket (hosts without a disk): loaded on start, each change uploaded before the answer. Also OPEN_PASSPORT_GCS_PREFIX. |
PORT, HOST | no | 8787, 0.0.0.0 in the image |
HTTPS assumptions: the host speaks plain HTTP and expects a proxy (Fly, Render, Caddy, nginx) to terminate TLS. ChatGPT and Claude.ai refuse non-https servers, and Claude.ai connects from Anthropic's IP range 160.79.104.0/21, so the host must be reachable from the public internet. Run one instance: OAuth codes and sign-in sessions live in memory.
Then upload your passport and make yourself an owner key:
HOST=https://acme-passport.fly.dev; ADMIN=<the admin token>
open-passport pack my-company/ -o acme.passport.zip # Principia: Passport → Export → Open Passport Bundle
curl -X POST "$HOST/v1/passports?id=acme" -H "authorization: Bearer $ADMIN" --data-binary @acme.passport.zip
curl -X POST $HOST/v1/passports/acme/grants -H "authorization: Bearer $ADMIN" \
-d '{"app":"owner","scopes":["owner"]}' # -> {"token":"opk_…"}: your owner key
Principia does the upload for you: Company Settings → Passport folder → Cloud sync (host URL, passport id acme, your owner key) keeps the host in step with your vault, and Use in other apps → Sign In to Host opens the host's pages already signed in. A write:all + read:all key also syncs, but cannot sign you in.
Your MCP URL is https://acme-passport.fly.dev/v1/passports/acme/mcp. The owner's page, https://acme-passport.fly.dev/oauth/connections, lists connected apps (trust, revoke) and the proposals waiting for you.
2. Add it to ChatGPT#
- Open chatgpt.com/plugins (older workspaces: Settings → Apps & Connectors → Advanced → Developer mode), select +, then Add custom MCP server. A workspace admin may have to enable developer mode first.
- Name:
Acme Passport. Server URL:https://acme-passport.fly.dev/v1/passports/acme/mcp. Authentication: OAuth (leave client fields empty: ChatGPT presents its client ID metadata document,https://chatgpt.com/oauth/client.json, or registers itself). - Accept the risk notice and select Create.
- ChatGPT opens the host's sign-in page. Paste your owner key (or click Sign In to Host in Principia first, then you skip this).
- On the consent screen, choose what ChatGPT may use: the card (~N tokens), each department, everything on demand, suggest facts, suggest taste. Leave Trust this app off to review its writes. Select Allow.
- In a chat, enable the connector and ask "What does our company do?" ChatGPT calls
passport_context. Deep research and company knowledge usesearchandfetch; citations link to/p/acme/n/<note>, which opens for you when you are signed in on the host.
ChatGPT asks before running remember or taste (tools without readOnlyHint are write actions). Plan limits change often: OpenAI documents full MCP support (with write actions) for Business, Enterprise and Edu; reports say Plus and Pro get read-only developer-mode connectors. Check the current help center if writes do not appear.
3. Add it to Claude.ai, Claude Desktop (connector) and the Claude mobile apps#
- Pro / Max: Customize → Connectors → + → Add custom connector. Team / Enterprise: an owner adds it in Organization settings → Connectors → Add → Custom → Web, then members connect.
- URL:
https://acme-passport.fly.dev/v1/passports/acme/mcp. Leave OAuth client fields empty (Claude uses its client ID metadata document or registers itself; the callback ishttps://claude.ai/api/mcp/auth_callback). - Connect → sign in on the host → consent → Allow. The connector then works in Claude.ai, Claude Desktop and the mobile apps.
4. Local: Claude Desktop, Cursor, VS Code, Codex, Windsurf, Gemini CLI, Claude Code#
The local server reads the folder on every call, so it is always current. --writes propose (recommended for a vault you edit elsewhere) files writes under <folder>/.open-passport/proposals/ for open-passport proposals <folder> or Principia's Inbox; --writes direct applies them; --writes off is read-only.
With the CLI from this repository (Node ≥ 22.18):
OP="node /path/to/open-passport/packages/js/bin/open-passport.mjs"
Principia users: Company Settings → Use in other apps shows these snippets already filled in with Principia's own runtime (no Node install needed) and your live vault, with one-click buttons for Claude Desktop, Cursor and VS Code.
Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json, Settings → Developer → Edit Config):
{ "mcpServers": { "acme-passport": { "command": "node", "args": ["/path/to/open-passport/packages/js/bin/open-passport.mjs", "mcp", "--dir", "/path/to/acme", "--writes", "propose", "--actor", "claude-desktop"] } } }
Cursor (~/.cursor/mcp.json, or one click: cursor://anysphere.cursor-deeplink/mcp/install?name=acme-passport&config=<base64 of the server entry>):
{ "mcpServers": { "acme-passport": { "command": "node", "args": ["…/open-passport.mjs", "mcp", "--dir", "/path/to/acme", "--writes", "propose", "--actor", "cursor"] } } }
Hosted instead: { "mcpServers": { "acme-passport": { "url": "https://acme-passport.fly.dev/v1/passports/acme/mcp" } } } (Cursor runs the OAuth flow), or add "headers": { "Authorization": "Bearer opk_…" } with a key from POST /grants.
VS Code (user mcp.json, or code --add-mcp '<json>'):
{ "servers": { "acme-passport": { "type": "stdio", "command": "node", "args": ["…/open-passport.mjs", "mcp", "--dir", "/path/to/acme", "--writes", "propose", "--actor", "vscode"] } } }
Hosted: { "servers": { "acme-passport": { "type": "http", "url": "https://acme-passport.fly.dev/v1/passports/acme/mcp" } } }.
Codex (~/.codex/config.toml):
[mcp_servers.acme-passport]
command = "node"
args = ["/path/to/open-passport/packages/js/bin/open-passport.mjs", "mcp", "--dir", "/path/to/acme", "--writes", "propose", "--actor", "codex"]
Hosted: codex mcp add acme-passport --url https://acme-passport.fly.dev/v1/passports/acme/mcp then codex mcp login acme-passport (OAuth), or url = "…" with bearer_token_env_var = "ACME_PASSPORT_TOKEN".
Claude Code: claude mcp add --transport stdio acme-passport -- node …/open-passport.mjs mcp --dir /path/to/acme --writes propose --actor claude-code, or hosted: claude mcp add --transport http acme-passport https://acme-passport.fly.dev/v1/passports/acme/mcp then /mcp to sign in.
Windsurf (~/.codeium/windsurf/mcp_config.json; newer builds: ~/.config/devin/mcp_config.json): the same mcpServers entry as Claude Desktop, or { "serverUrl": "https://…/mcp", "headers": { "Authorization": "Bearer opk_…" } }.
Gemini CLI (~/.gemini/settings.json): { "mcpServers": { "acme-passport": { "command": "node", "args": [ … ] } } }, or hosted { "httpUrl": "https://…/mcp", "headers": { "Authorization": "Bearer opk_…" } }.
Python (no MCP needed):
import open_passport as op
system_prompt += op.fetch_context("https://acme-passport.fly.dev/v1/passports/acme", token, "marketing") # framed L0 + L1
5. Bring in what you already have#
Everything lands as proposals for review; nothing is written silently.
open-passport import chatgpt-export.zip # prints the candidates; nothing is written
open-passport import chatgpt-export.zip --into acme/ --propose # files them; review with:
open-passport proposals acme/ # --accept <id|all> / --reject <id|all>
open-passport import claude-export.zip --into acme/ --propose
open-passport import ~/Notion-export/ --into acme/ --propose # or an Obsidian vault, any markdown folder
open-passport import https://acme.com --into acme/ --propose # homepage + About → a company card draft
pbpaste | open-passport import - --into acme/ --propose # pasted text, a memory dump, a brief
open-passport import chatgpt-export.zip --host https://…/v1/passports/acme --token opk_… # as host proposals
- ChatGPT: Settings → Data controls → Export data. The importer reads
conversations.json: your custom instructions ("About you", "How should ChatGPT respond"), saved memories ChatGPT wrote during chats, and your own messages (never the assistant's). Saved memories that never appeared in a chat are not in the export: copy them from Settings → Personalization → Manage memories and paste them (import -). - Claude: Settings → Privacy → Export data. Reads your messages, project descriptions, project instructions and docs, and your name (role facts go on your person note). Emails are never imported.
- Heuristics are deterministic: first-person statements about the business ("we sell…", "our customers are…", "we decided…", "$29 per seat") and taste ("we never…", "I prefer…", "our tone is…"). Questions, requests to the assistant, hypotheticals, secrets, emails and phone numbers are skipped. Results are deduplicated (a statement seen twice ranks higher) and budgeted (
--max 40). --llm anthropic|openai(withANTHROPIC_API_KEY/OPENAI_API_KEY) adds a model pass over the highest-signal text (instructions, memories, About pages first; at most 24 000 characters sent). Each item must quote the source exactly or it is dropped.
Principia: Company Settings → Use in other apps → Import into Passport does the same from a sheet (ChatGPT / Claude export, folder, website, paste); the candidates appear in the Inbox.
Client compatibility#
| Client | How | Auth | Reads | Writes | Verified |
|---|---|---|---|---|---|
| ChatGPT (web, desktop) | hosted URL (plugins → Add custom MCP server) | OAuth: CIMD, DCR | context, department, search/fetch (deep research, company knowledge) | remember, taste with confirmation; plan-dependent | simulated (tests) |
| Claude.ai, Claude Desktop connector, Claude mobile | hosted URL (Customize → Connectors) | OAuth: CIMD, DCR | all | all | simulated (tests) |
| Claude Desktop (local) | stdio, claude_desktop_config.json | none (local) | all | propose / direct | stdio tests |
| Claude Code | stdio or hosted (--transport http) | none / OAuth / bearer header | all | all | stdio tests |
| Cursor | stdio or hosted; cursor:// deeplink | none / OAuth / bearer header | all | all | stdio tests |
| VS Code (Copilot agent) | stdio or type: http | none / OAuth / bearer header | all | all | stdio tests |
| OpenAI Codex | stdio or hosted (codex mcp login) | none / OAuth / bearer_token_env_var | all | all | stdio tests |
| Windsurf | stdio or serverUrl | none / bearer header | all | all | not tested |
| Gemini CLI | stdio or httpUrl | none / bearer header / OAuth | all | all | not tested |
| Gemini app, Microsoft Copilot | no custom MCP connectors found | n/a | use the Python or REST profile | n/a | n/a |
| Python, scripts, cloud runners | REST GET /v1/passports/{id} | bearer | tiers, search, get | deltas, notes, taste | packages/py tests |
| Any prompt | open-passport render <p> --tier txt / agents-md | n/a | tiers | open-passport-notes block → ingest | CLI tests |
Protocol: the host and the stdio server speak MCP 2026-07-28 (per-request _meta, server/discover, no sessions, Mcp-Method / Mcp-Name headers validated) and the earlier initialize revisions (2025-11-25, 2025-06-18, 2025-03-26), so older and newer clients both work.
What still needs a person with real accounts#
These are covered by simulated clients in packages/server/test/oauth.test.ts, not by the real apps:
- ChatGPT: add the connector on a Business/Enterprise workspace and on Plus/Pro; confirm which of CIMD or DCR it uses against this host, that the redirect is
https://chatgpt.com/connector_platform_oauth_redirect(we advertiseauthorization_response_iss_parameter_supported), thatsearch/fetchwork in deep research and company knowledge, that citations link to/p/<id>/n/…, and thatremember/tasteshow the confirmation. - Claude.ai: add the connector on Pro and on a Team org; confirm CIMD is chosen (we list
noneintoken_endpoint_auth_methods_supported), that refresh after one hour works, and the tools in Claude Desktop and mobile. - Cursor, VS Code, Codex, Claude Code: the hosted URL's OAuth flow with loopback redirects (we accept any port on
127.0.0.1/localhost), and the one-click install links Principia generates. - Behind the real proxy:
OPEN_PASSPORT_PUBLIC_URLmatches what clients see,/.well-known/oauth-protected-resource/v1/passports/<id>/mcpand/.well-known/oauth-authorization-serverare reachable, response times stay under Claude's 10-second discovery limit. - Windsurf and Gemini CLI configs (paths have moved between releases).
Sources (checked 2026-10-21): MCP authorization, Streamable HTTP, versioning, server/discover, tools; OpenAI: custom MCP server, Apps SDK MCP server, Apps SDK auth; Claude connectors, connector auth, custom connectors help, Claude Code MCP; Cursor MCP; VS Code MCP; Codex MCP; Windsurf / Devin MCP; Gemini CLI MCP.