Skip to content
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 mcp over 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:

ToolWhat it doesCost
passport_contextThe company card, plus one department's slot. Models are told to call it first.~300 + ~600 tokens
get_departmentAnother department's slot~600 tokens
searchKeyword search; returns {results: [{id, title, url}]} (the shape ChatGPT expects)small
fetchOne note by id; returns {id, title, text, url, metadata}≤ 8000 chars
rememberOne lasting fact on the note it is aboutwrite
tasteA like, dislike, preference or hard no, with its examplewrite

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.

VariableRequiredMeaning
OPEN_PASSPORT_ADMIN_TOKENyes (16+ chars)Creates passports and owner keys. Never give it to an app.
OPEN_PASSPORT_PUBLIC_URLyes for ChatGPT / Claude.aiThe https:// origin apps reach. It is the OAuth issuer and every resource URL, so it must not change.
OPEN_PASSPORT_TRUST_PROXYno1 derives the origin from X-Forwarded-* when no public URL is set.
OPEN_PASSPORT_ALLOWED_ORIGINSnoExtra browser origins for the MCP endpoint (ChatGPT, Claude and localhost are built in).
DATA_DIRno (/data in the image)Revisions, grants, oauth.json, audit logs. Put it on a volume.
OPEN_PASSPORT_GCS_BUCKETnoKeep 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, HOSTno8787, 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#

  1. 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.
  2. 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).
  3. Accept the risk notice and select Create.
  4. 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).
  5. 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.
  6. In a chat, enable the connector and ask "What does our company do?" ChatGPT calls passport_context. Deep research and company knowledge use search and fetch; 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#

  1. Pro / Max: Customize → Connectors → + → Add custom connector. Team / Enterprise: an owner adds it in Organization settings → Connectors → Add → Custom → Web, then members connect.
  2. 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 is https://claude.ai/api/mcp/auth_callback).
  3. 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 (with ANTHROPIC_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#

ClientHowAuthReadsWritesVerified
ChatGPT (web, desktop)hosted URL (plugins → Add custom MCP server)OAuth: CIMD, DCRcontext, department, search/fetch (deep research, company knowledge)remember, taste with confirmation; plan-dependentsimulated (tests)
Claude.ai, Claude Desktop connector, Claude mobilehosted URL (Customize → Connectors)OAuth: CIMD, DCRallallsimulated (tests)
Claude Desktop (local)stdio, claude_desktop_config.jsonnone (local)allpropose / directstdio tests
Claude Codestdio or hosted (--transport http)none / OAuth / bearer headerallallstdio tests
Cursorstdio or hosted; cursor:// deeplinknone / OAuth / bearer headerallallstdio tests
VS Code (Copilot agent)stdio or type: httpnone / OAuth / bearer headerallallstdio tests
OpenAI Codexstdio or hosted (codex mcp login)none / OAuth / bearer_token_env_varallallstdio tests
Windsurfstdio or serverUrlnone / bearer headerallallnot tested
Gemini CLIstdio or httpUrlnone / bearer header / OAuthallallnot tested
Gemini app, Microsoft Copilotno custom MCP connectors foundn/ause the Python or REST profilen/an/a
Python, scripts, cloud runnersREST GET /v1/passports/{id}bearertiers, search, getdeltas, notes, tastepackages/py tests
Any promptopen-passport render <p> --tier txt / agents-mdn/atiersopen-passport-notes block → ingestCLI 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:

  1. 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 advertise authorization_response_iss_parameter_supported), that search / fetch work in deep research and company knowledge, that citations link to /p/<id>/n/…, and that remember / taste show the confirmation.
  2. Claude.ai: add the connector on Pro and on a Team org; confirm CIMD is chosen (we list none in token_endpoint_auth_methods_supported), that refresh after one hour works, and the tools in Claude Desktop and mobile.
  3. 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.
  4. Behind the real proxy: OPEN_PASSPORT_PUBLIC_URL matches what clients see, /.well-known/oauth-protected-resource/v1/passports/<id>/mcp and /.well-known/oauth-authorization-server are reachable, response times stay under Claude's 10-second discovery limit.
  5. 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.

Ask an assistant about Open Passport.