Client setup
Connect ChatGPT, Claude Code, claude.ai, Claude Desktop, Cursor, VS Code, Codex, Gemini CLI, Zed, Windsurf, Goose, OpenCode and other MCP clients to the hosted MachineMail MCP server, by signing in with OAuth or with an API key.
Every client connects to the same endpoint:
There are two ways to authenticate:
- Sign in with OAuth. Give the client only the URL. On first use it opens your browser on MachineMail’s Connect an app page. Choose what it can reach (the whole organization, one pod or one inbox) and approve. The client stores the tokens and renews them; nothing secret goes in a config file. See OAuth sign-in for how it works.
- Send an API key. Set the header
Authorization: Bearer mm_live_...with a key from the dashboard’s API keys page. Use this where no browser is available (CI, servers, headless agents). For an agent that should only touch one inbox, create a key scoped to that inbox: the MCP tools then see nothing else.
Supported clients
Every client below is tested against MachineMail by replaying its own sign-in sequence: discovery, client identification, authorization, token exchange, initialize and tools/list, refresh and sign-out.
| Client | How it identifies itself | Token endpoint auth | Setup |
|---|---|---|---|
| ChatGPT (developer mode, apps, connectors) | Client metadata document chatgpt.com/oauth/client.json | none (PKCE) | ChatGPT |
| ChatGPT, when the app builder picks DCR | Dynamic registration | client_secret_post or client_secret_basic | ChatGPT |
| claude.ai, Claude Desktop, mobile, Cowork | Client metadata document claude.ai/oauth/mcp-oauth-client-metadata | none (PKCE) | claude.ai and Claude Desktop |
| Claude Code | Client metadata document claude.ai/oauth/claude-code-client-metadata | none, or a secret with --client-secret | Claude Code |
| VS Code (GitHub Copilot) | Client metadata document vscode.dev/oauth/client-metadata.json, else dynamic registration | none | VS Code |
| Cursor | Dynamic registration | none, or a static client ID and secret | Cursor |
| Codex CLI | Client metadata document chatgpt.com/oauth/codex/client.json | none (PKCE) | Codex |
| Gemini CLI | Dynamic registration | none (PKCE) | Gemini CLI |
| Zed | Client metadata document zed.dev/oauth/client-metadata.json | none (PKCE) | Zed |
| Windsurf | Dynamic registration | none (PKCE) | Windsurf |
| Goose | Client metadata document goose-docs.ai/oauth/client-metadata.json | none (PKCE) | Goose |
| JetBrains IDEs | Dynamic registration (Air), or mcp-remote, or an API key | none (PKCE) | JetBrains IDEs |
OpenCode, MCP Inspector, mcp-remote | Dynamic registration | none (PKCE) | OpenCode, Other clients |
| OpenAI Responses API, Claude Messages API | No sign-in: you pass a credential | n/a | Model APIs |
Each section starts with OAuth. Where it shows an API key, it reads the key from an environment variable, MACHINEMAIL_API_KEY in the examples, so it stays out of files you commit:
Note
Apps you connect with OAuth are listed under Settings → Connected apps in the dashboard, where you can disconnect them. Don’t put auth.md access tokens in client configs: they expire after an hour and clients don’t renew them.
Claude Code
Add the server with the CLI, without a header:
Then run /mcp in a Claude Code session, select machinemail and choose Authenticate. Your browser opens on the Connect page. After you approve, Claude Code stores the tokens and refreshes them when they expire. Claude Code identifies itself with a client metadata document, so the Connect page shows it as Claude Code from claude.ai.
--scope user makes the server available in every project. Leave it out to add it to the current project only, or use --scope project to write it to the project’s .mcp.json for your team. With OAuth, each person signs in with their own account, so the shared file holds no secret:
To use an API key instead, for example in CI, add the header:
In a shared .mcp.json, reference the variable instead of the value ("headers": { "Authorization": "Bearer ${MACHINEMAIL_API_KEY}" }), and each person’s environment supplies their own key.
Check it with claude mcp list, or /mcp inside a session.
ChatGPT
In ChatGPT, turn on developer mode (Settings → Apps → Advanced settings), then create an app (connector) for MachineMail:
- Name it
MachineMailand enterhttps://mcp.machinemail.dev/mcpas the MCP server URL. - Choose OAuth as the authentication. Leave the client ID and secret empty: ChatGPT identifies itself with its client metadata document.
- Create it, then connect. A MachineMail tab opens; approve there.
ChatGPT calls MachineMail from OpenAI’s servers, signs in as a public client with PKCE, and renews its tokens on its own. If you build an app that registers with Dynamic Client Registration instead, MachineMail issues it a client secret when it asks for client_secret_post or client_secret_basic.
claude.ai and Claude Desktop
Add MachineMail as a custom connector:
- Open Settings → Connectors and choose Add custom connector.
- Name it
MachineMailand enterhttps://mcp.machinemail.dev/mcpas the URL. Leave the advanced OAuth settings empty. - Choose Add, then Connect. A MachineMail tab opens; approve there.
The connector is available in claude.ai, Claude Desktop and the mobile apps. On Team and Enterprise plans, an owner adds the connector for the organization, and each member then connects it with their own MachineMail account.
To use an API key in Claude Desktop instead, start the server through the mcp-remote bridge, which needs Node.js. Open Settings → Developer → Edit Config and add:
The header value goes through env because some platforms split arguments on spaces. Restart Claude Desktop after saving. Without --header, mcp-remote signs in with OAuth instead and opens your browser on first start.
The file lives at ~/Library/Application Support/Claude/claude_desktop_config.json on macOS and %APPDATA%\Claude\claude_desktop_config.json on Windows.
Cursor
Add the server to ~/.cursor/mcp.json for every project, or .cursor/mcp.json in one project:
Under Settings → MCP the server shows that it needs a login. Click it, approve in the browser, and the tools appear.
For an API key, add a headers entry. Cursor reads ${env:...} from the environment it was started in, so export the variable in your shell profile:
VS Code
Add the server to .vscode/mcp.json in a workspace, or run MCP: Open User Configuration for every workspace:
Start the server from the Start button above it in the file. VS Code asks whether MachineMail may authenticate; allow it and approve in the browser. Then use the tools in Copilot’s agent mode.
For an API key, prompt for it once with an input. VS Code stores it securely, so it never sits in the file:
OpenCode
Add a remote server to opencode.json (global: ~/.config/opencode/opencode.json):
OpenCode notices the 401 and signs in with OAuth. To start the sign-in yourself, run opencode mcp auth machinemail.
For an API key, set "oauth": false so OpenCode doesn’t try to sign in, and add the header:
Check it with opencode mcp list.
Codex
Add the server to ~/.codex/config.toml (or .codex/config.toml in a trusted project). bearer_token_env_var names the environment variable that holds an API key; Codex sends it as Authorization: Bearer ...:
Codex versions with OAuth support for MCP servers can sign in instead. Leave out bearer_token_env_var and run codex mcp login machinemail. Codex identifies itself with its client metadata document and listens on a loopback port for the response.
Check it with codex mcp list, or /mcp inside a session.
Gemini CLI
Add the server to ~/.gemini/settings.json (or .gemini/settings.json in a project). httpUrl selects Streamable HTTP:
Gemini CLI notices the 401 and signs in with OAuth on first use; to start it yourself, run /mcp auth machinemail in a session. For an API key, add "headers": { "Authorization": "Bearer $MACHINEMAIL_API_KEY" } instead.
Zed
Add a remote context server in Zed’s settings.json:
Zed signs in with OAuth when the server answers 401, identifying itself with its client metadata document. For an API key, add "headers": { "Authorization": "Bearer mm_live_..." }.
Windsurf
Add the server to ~/.codeium/windsurf/mcp_config.json:
Windsurf starts the OAuth sign-in when you add the server. For an API key, add "headers": { "Authorization": "Bearer ${env:MACHINEMAIL_API_KEY}" }.
Goose
Run goose configure, choose Add Extension → Remote Extension (Streamable HTTP), name it machinemail and enter https://mcp.machinemail.dev/mcp. Goose signs in with OAuth on first use, identifying itself with its client metadata document.
JetBrains IDEs
JetBrains AI Assistant connects to remote MCP servers with static headers only, so use an API key there, or run the server through mcp-remote (stdio), which signs in with OAuth:
JetBrains Air and the GitHub Copilot plugin sign in with OAuth directly: give them the URL only.
OpenAI Responses API and Claude Messages API
The model APIs call MCP servers for you but don’t run a sign-in: pass a credential with each request. Use an API key (an inbox-scoped key keeps the model to one inbox). An OAuth access token from your own app works too, but it lasts one hour and the API won’t refresh it.
Other clients
Any client that supports remote MCP servers over Streamable HTTP works. Give it:
| Setting | Value |
|---|---|
| Transport | Streamable HTTP (sometimes labelled http or streamable-http) |
| URL | https://mcp.machinemail.dev/mcp |
| Auth | OAuth (automatic in clients that support MCP authorization), or the header below |
| Header | Authorization: Bearer mm_live_..., when the client cannot sign in or has no browser |
Notes for clients that differ:
- Only stdio supported? Run the server through the
mcp-remotebridge:npx -y mcp-remote https://mcp.machinemail.dev/mcp, which signs in with OAuth. To use an API key, add--header "Authorization:${MACHINEMAIL_AUTH}"as in the Claude Desktop example. - Configured with
https://api.machinemail.dev/mcporhttps://machinemail.dev/mcp? Those legacy URLs are the same server and keep working (https://machinemail.dev/mcptokens included). New setups should usehttps://mcp.machinemail.dev/mcp. If OAuth sign-in fails onhttps://api.machinemail.dev/mcp, switch to the new URL: see Legacy endpoint. - Only the older HTTP+SSE transport supported? The server does not offer it. Use
mcp-remote, which speaks Streamable HTTP to MachineMail and stdio to your client. - MCP Inspector: enter the URL and use Quick OAuth Flow; it registers itself and signs in from the browser.
- Writing your own client? OAuth sign-in lists the discovery documents, endpoints and rules. Use PKCE with
S256and sendresource=https://mcp.machinemail.dev/mcp. Use a client metadata document URL asclient_id, or register athttps://machinemail.dev/oauth2/register(ask forclient_secret_basicorclient_secret_postif your app keeps a secret on a server).
Check the connection
Ask the model to call list_inboxes. With a fresh organization it answers No inboxes yet. Use create_inbox. Then try a round trip:
Create an inbox with a one-hour TTL, tell me its address, and wait for a message to arrive.
Send mail to the address from anywhere and the model gets it back from wait_for_message, with its trust level.
If the client reports a 401:
-
With OAuth: the app may have been disconnected under Settings → Connected apps, or its refresh token expired after 30 days unused. Sign in again from the client.
-
With an API key: the key is missing, mistyped or revoked. Test it with curl:
200means the key is good and the problem is in the client config.