Per-harness setup
Codex (Headless via bearer header)
codex mcp add charactr --url https://mcp.character.app/mcp?profile=full
codex mcp login charactr --scopes email
Or paste into ~/.codex/config.toml:
[mcp_servers.charactr]
url = "https://mcp.character.app/mcp?profile=full"
tool_timeout_sec = 120
# headless: bearer_token_env_var = "CHARACTR_TOKEN"
Sign-in: codex mcp add charactr --url <url> writes the config and immediately opens a browser, blocking on a localhost callback. Its default OAuth request asks for `openid profile email phone offline_access`; the platform now publishes an ES256 signing key, so `openid` is expected to work, but a consented `openid` exchange has not yet been witnessed — if `codex mcp add` fails at the token exchange, sign in with the scoped `codex mcp login charactr --scopes email` line instead.
A bearer token is a short-lived (~1h) Supabase access token.
Claude Code (No-browser OAuth path)
claude mcp add --transport http -s user charactr https://mcp.character.app/mcp?profile=full
Or paste into ~/.claude.json under "mcpServers":
{
"mcpServers": {
"charactr": {
"type": "http",
"url": "https://mcp.character.app/mcp?profile=full"
}
}
}
Sign-in: claude mcp login charactr — opens a browser. On a machine with no browser, claude mcp login charactr --no-browser prints the authorization URL and reads the pasted redirect back.
A typeless { "url" } block is a configuration error in Claude Code — the "type": "http" field is required.
Claude Desktop (Needs one browser sign-in)
Claude app → Settings → Connectors → Add custom connector → name it charactr → URL: https://mcp.character.app/mcp?profile=full
Sign-in: OAuth consent inside the app — the config file has no remote-URL field.
Same on claude.ai: Settings → Connectors → Add custom connector. A config-file MCP registration only covers local stdio servers here.
Cursor (Headless via bearer header)
{
"mcpServers": {
"charactr": {
"url": "https://mcp.character.app/mcp?profile=full"
}
}
}
One-click install
Goes in ~/.cursor/mcp.json
Sign-in: OAuth on first use — the client registers itself (DCR), no sign-up step.
Headless: add "headers": { "Authorization": "Bearer $CHARACTR_TOKEN" } to the same entry.
Devin CLI (Headless via bearer header)
devin mcp add charactr -s user --transport http --scopes email https://mcp.character.app/mcp?profile=full
devin mcp login charactr --scopes email
Or paste into ~/.config/devin/mcp_config.json:
{
"mcpServers": {
"charactr": {
"url": "https://mcp.character.app/mcp?profile=full",
"transport": "http"
}
}
}
Sign-in: devin mcp add writes the config and immediately opens the browser sign-in — its OAuth callback listens on the fixed port 8765, so the browser must run on the same machine.
Windsurf's Devin Local agent (the default for new tabs) reads this same Devin config — no separate Windsurf setup needed there. Headless/CI: "headers": { "Authorization": "Bearer $CHARACTR_TOKEN" } on the same entry, or .devin/mcp_config.local.json for one project.
Gemini CLI (Headless via bearer header)
{
"mcpServers": {
"charactr": {
"httpUrl": "https://mcp.character.app/mcp?profile=full"
}
}
}
Goes in ~/.gemini/settings.json
Sign-in: Browser OAuth on first use — no browserless OAuth flow is documented.
The remote key is "httpUrl", not "url". Headless: "headers": { "Authorization": "Bearer $CHARACTR_TOKEN" } on the same entry. A workspace .gemini/settings.json overrides the user file.
VS Code (Headless via bearer header)
{
"servers": {
"charactr": {
"type": "http",
"url": "https://mcp.character.app/mcp?profile=full"
}
}
}
One-click install
Goes in User mcp.json — Command Palette → "MCP: Open User Configuration"
Sign-in: Browser OAuth on first connect.
The root key is "servers", not "mcpServers". Also works: code --add-mcp '{"name":"charactr","type":"http","url":"https://mcp.character.app/mcp?profile=full"}'. Agent Host never forwards ${input:…} variables — use a literal "headers" bearer there.
Windsurf (Headless via bearer header)
{
"mcpServers": {
"charactr": {
"serverUrl": "https://mcp.character.app/mcp?profile=full"
}
}
}
Goes in ~/.codeium/windsurf/mcp_config.json
Sign-in: OAuth on first use.
This file configures the legacy Cascade agent only. New Windsurf tabs run the Devin Local agent, which reads the Devin CLI config — use the Devin CLI entry above for them.
Zed (Needs one browser sign-in)
{
"context_servers": {
"charactr": {
"url": "https://mcp.character.app/mcp?profile=full"
}
}
}
Goes in ~/.config/zed/settings.json under "context_servers"
Sign-in: OAuth on first use.
No verified static-header path for remote servers — plan on one browser sign-in.
ChatGPT (Needs one browser sign-in)
Settings → Connectors → Developer Mode → New app → URL: https://mcp.character.app/mcp?profile=full → auth: OAuth
Sign-in: OAuth only — there is no bearer-header field, so a browser sign-in is mandatory.
Needs ChatGPT developer mode. Refresh tokens keep the connection alive past access-token expiry.
Any other MCP client (Headless via bearer header)
{
"mcpServers": {
"charactr": {
"type": "http",
"url": "https://mcp.character.app/mcp?profile=full"
}
}
}
Sign-in: The client runs its own OAuth prompt and consent screen.
Any client that speaks streamable HTTP. If it accepts a static "Authorization: Bearer …" header, a fresh Supabase access token serves headless for about an hour.
Headless, honestly
OAuth 2.1 is the only door — Character App does not issue a long-lived API key yet. Clients that take a static Authorization bearer header can run headless with a fresh Supabase access token, but that token lives about an hour: fine for CI smoke checks, not a durable install. Claude Code has a true no-browser OAuth path (claude mcp login charactr --no-browser); every other harness needs one browser sign-in on the same machine the callback listens on.
Request only the advertised scope email. Codex's default OAuth request asks for openid profile email phone offline_access; the platform now publishes an ES256 signing key, so openid is expected to work, but a consented openid exchange has not yet been witnessed — if codex mcp add fails at the token exchange, sign in with codex mcp login charactr --scopes email. Where a client offers no scope override and no bearer-header option, it cannot connect headless today.