Skip to content
machinemail

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:

text
URL:  https://mcp.machinemail.dev/mcp

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.

ClientHow it identifies itselfToken endpoint authSetup
ChatGPT (developer mode, apps, connectors)Client metadata document chatgpt.com/oauth/client.jsonnone (PKCE)ChatGPT
ChatGPT, when the app builder picks DCRDynamic registrationclient_secret_post or client_secret_basicChatGPT
claude.ai, Claude Desktop, mobile, CoworkClient metadata document claude.ai/oauth/mcp-oauth-client-metadatanone (PKCE)claude.ai and Claude Desktop
Claude CodeClient metadata document claude.ai/oauth/claude-code-client-metadatanone, or a secret with --client-secretClaude Code
VS Code (GitHub Copilot)Client metadata document vscode.dev/oauth/client-metadata.json, else dynamic registrationnoneVS Code
CursorDynamic registrationnone, or a static client ID and secretCursor
Codex CLIClient metadata document chatgpt.com/oauth/codex/client.jsonnone (PKCE)Codex
Gemini CLIDynamic registrationnone (PKCE)Gemini CLI
ZedClient metadata document zed.dev/oauth/client-metadata.jsonnone (PKCE)Zed
WindsurfDynamic registrationnone (PKCE)Windsurf
GooseClient metadata document goose-docs.ai/oauth/client-metadata.jsonnone (PKCE)Goose
JetBrains IDEsDynamic registration (Air), or mcp-remote, or an API keynone (PKCE)JetBrains IDEs
OpenCode, MCP Inspector, mcp-remoteDynamic registrationnone (PKCE)OpenCode, Other clients
OpenAI Responses API, Claude Messages APINo sign-in: you pass a credentialn/aModel 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:

shell
export MACHINEMAIL_API_KEY=mm_live_...

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:

shell
claude mcp add --transport http --scope user machinemail https://mcp.machinemail.dev/mcp

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:

.mcp.json
{
  "mcpServers": {
    "machinemail": {
      "type": "http",
      "url": "https://mcp.machinemail.dev/mcp"
    }
  }
}

To use an API key instead, for example in CI, add the header:

shell
claude mcp add --transport http --scope user machinemail https://mcp.machinemail.dev/mcp \
  --header "Authorization: Bearer $MACHINEMAIL_API_KEY"

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:

  1. Name it MachineMail and enter https://mcp.machinemail.dev/mcp as the MCP server URL.
  2. Choose OAuth as the authentication. Leave the client ID and secret empty: ChatGPT identifies itself with its client metadata document.
  3. 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:

  1. Open Settings → Connectors and choose Add custom connector.
  2. Name it MachineMail and enter https://mcp.machinemail.dev/mcp as the URL. Leave the advanced OAuth settings empty.
  3. 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:

claude_desktop_config.json
{
  "mcpServers": {
    "machinemail": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://mcp.machinemail.dev/mcp", "--header", "Authorization:${MACHINEMAIL_AUTH}"],
      "env": {
        "MACHINEMAIL_AUTH": "Bearer mm_live_..."
      }
    }
  }
}

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:

~/.cursor/mcp.json
{
  "mcpServers": {
    "machinemail": {
      "url": "https://mcp.machinemail.dev/mcp"
    }
  }
}

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:

~/.cursor/mcp.json
{
  "mcpServers": {
    "machinemail": {
      "url": "https://mcp.machinemail.dev/mcp",
      "headers": {
        "Authorization": "Bearer ${env:MACHINEMAIL_API_KEY}"
      }
    }
  }
}

VS Code

Add the server to .vscode/mcp.json in a workspace, or run MCP: Open User Configuration for every workspace:

.vscode/mcp.json
{
  "servers": {
    "machinemail": {
      "type": "http",
      "url": "https://mcp.machinemail.dev/mcp"
    }
  }
}

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:

.vscode/mcp.json
{
  "inputs": [
    {
      "type": "promptString",
      "id": "machinemail-api-key",
      "description": "MachineMail API key",
      "password": true
    }
  ],
  "servers": {
    "machinemail": {
      "type": "http",
      "url": "https://mcp.machinemail.dev/mcp",
      "headers": {
        "Authorization": "Bearer ${input:machinemail-api-key}"
      }
    }
  }
}

OpenCode

Add a remote server to opencode.json (global: ~/.config/opencode/opencode.json):

opencode.json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "machinemail": {
      "type": "remote",
      "url": "https://mcp.machinemail.dev/mcp",
      "enabled": true
    }
  }
}

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:

opencode.json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "machinemail": {
      "type": "remote",
      "url": "https://mcp.machinemail.dev/mcp",
      "enabled": true,
      "oauth": false,
      "headers": {
        "Authorization": "Bearer {env:MACHINEMAIL_API_KEY}"
      }
    }
  }
}

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/config.toml
[mcp_servers.machinemail]
url = "https://mcp.machinemail.dev/mcp"
bearer_token_env_var = "MACHINEMAIL_API_KEY"

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/settings.json
{
  "mcpServers": {
    "machinemail": {
      "httpUrl": "https://mcp.machinemail.dev/mcp"
    }
  }
}

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:

settings.json
{
  "context_servers": {
    "machinemail": {
      "url": "https://mcp.machinemail.dev/mcp"
    }
  }
}

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:

mcp_config.json
{
  "mcpServers": {
    "machinemail": {
      "serverUrl": "https://mcp.machinemail.dev/mcp"
    }
  }
}

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:

AI Assistant → Model Context Protocol
{
  "mcpServers": {
    "machinemail": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://mcp.machinemail.dev/mcp"]
    }
  }
}

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.

OpenAI Responses API
curl https://api.openai.com/v1/responses \
  -H "Authorization: Bearer $OPENAI_API_KEY" -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-5",
    "input": "List my MachineMail inboxes.",
    "tools": [{
      "type": "mcp",
      "server_label": "machinemail",
      "server_url": "https://mcp.machinemail.dev/mcp",
      "authorization": "'"$MACHINEMAIL_API_KEY"'",
      "require_approval": "never"
    }]
  }'
Claude Messages API (MCP connector)
curl https://api.anthropic.com/v1/messages \
  -H "x-api-key: $ANTHROPIC_API_KEY" -H "anthropic-version: 2023-06-01" \
  -H "anthropic-beta: mcp-client-2025-11-20" -H "content-type: application/json" \
  -d '{
    "model": "claude-opus-5-5",
    "max_tokens": 4096,
    "messages": [{"role": "user", "content": "List my MachineMail inboxes."}],
    "mcp_servers": [{
      "type": "url",
      "url": "https://mcp.machinemail.dev/mcp",
      "name": "machinemail",
      "authorization_token": "'"$MACHINEMAIL_API_KEY"'"
    }],
    "tools": [{"type": "mcp_toolset", "mcp_server_name": "machinemail"}]
  }'

Other clients

Any client that supports remote MCP servers over Streamable HTTP works. Give it:

SettingValue
TransportStreamable HTTP (sometimes labelled http or streamable-http)
URLhttps://mcp.machinemail.dev/mcp
AuthOAuth (automatic in clients that support MCP authorization), or the header below
HeaderAuthorization: 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-remote bridge: 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/mcp or https://machinemail.dev/mcp? Those legacy URLs are the same server and keep working (https://machinemail.dev/mcp tokens included). New setups should use https://mcp.machinemail.dev/mcp. If OAuth sign-in fails on https://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 S256 and send resource=https://mcp.machinemail.dev/mcp. Use a client metadata document URL as client_id, or register at https://machinemail.dev/oauth2/register (ask for client_secret_basic or client_secret_post if 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:

    shell
    curl -s -o /dev/null -w "%{http_code}\n" https://api.machinemail.dev/v0/inboxes -H "Authorization: Bearer $MACHINEMAIL_API_KEY"

    200 means the key is good and the problem is in the client config.