Client setup
Every MCP client starts or reaches the same server. What changes from one client to the next is the file it reads, the key it keeps servers under, how it passes environment variables and headers, and whether it can ask the user a question in the middle of a call. This page has the entry for each client in each of the three ways to run arcane-mcp. Getting started takes you from an Arcane API key to the first prompt; every setting named here is in Configuration.
Three ways to run it
Section titled “Three ways to run it”| Way | What the client does | Where the Arcane credential lives |
|---|---|---|
| Binary over stdio | Starts arcane-mcp as a child process and speaks MCP over its standard input and output | ARCANE_URL and ARCANE_API_KEY in the entry’s environment (or a .env file in the server’s working directory) |
| Docker over stdio | Starts docker run -i --rm … ghcr.io/jmrplens/arcane-mcp:latest as that child process | The same variables, forwarded into the container with -e |
| Remote Streamable HTTP | Sends every request to a server started with --http (or ARCANE_MCP_HTTP_ADDR), at /mcp | Authorization: Bearer <ARCANE_MCP_HTTP_TOKEN> and, per user, X-Arcane-API-Key: <Arcane key> on every request; on a server in apikey mode only Authorization: Bearer <Arcane key> |
Before you pick an entry:
-
The binary comes from any channel on Installation (the release binary, Homebrew, winget, npm, PyPI, NuGet,
go install), or from a build as in Getting started. Give clients its absolute path: desktop applications do not inherit your shell’sPATH. Withnpx,uvxordnx, the command and arguments are on that channel’s page and the environment below stays the same. -
The Docker image is
ghcr.io/jmrplens/arcane-mcp, published forlinux/amd64andlinux/arm64by the release workflow on every version tag (X.Y.Z,X.Yandlatest; see Docker); pin a version tag where you can. It serves stdio unlessARCANE_MCP_HTTP_ADDRis set. In a stdio entry:- keep
-i(the protocol travels over standard input) and never add-tor-d; -e NAMEwith no value forwards that variable from the environment the client startsdockerwith, so the entry’senvblock carries the values and the arguments only carry the names. Every extra setting (ARCANE_MCP_TOOL_SURFACE,ARCANE_MCP_READ_ONLY…) needs both its own-e NAMEand a value inenv;ARCANE_URLmust be reachable from inside the container.localhostthere is the container itself: use the host’s LAN name,host.docker.internal(Docker Desktop), or--network host(Linux);- the container does not see a
.envfile in your working directory.
- keep
-
An HTTP entry needs a server already running in HTTP mode, for example:
Terminal window export ARCANE_MCP_HTTP_TOKEN=$(openssl rand -hex 32)docker run -d --name arcane-mcp -p 127.0.0.1:8080:8080 \-e ARCANE_URL=https://arcane.example.com \-e ARCANE_MCP_HTTP_ADDR=:8080 \-e ARCANE_MCP_HTTP_ALLOW_PLAINTEXT=true \-e ARCANE_MCP_HTTP_TOKEN \ghcr.io/jmrplens/arcane-mcp:latestor
./arcane-mcp --http 127.0.0.1:8080with the same variables. The examples below usehttps://arcane-mcp.example.com/mcp, a TLS-terminating reverse proxy in front of that listener; the server itself speaks plain HTTP, whichARCANE_MCP_HTTP_ALLOW_PLAINTEXT=truedeclares: inside the container it binds every interface, the port is published on the host’s loopback only, and without the variable it logs a warning at startup (see Plain HTTP and the hardened mode). SetARCANE_MCP_HTTP_ALLOWED_HOSTS=arcane-mcp.example.comwhen the proxy forwards that public name: on a loopback listener the server otherwise answers only loopbackHostvalues. Each user sends their own Arcane key inX-Arcane-API-Keyand acts with its permissions; without the header the server falls back to its ownARCANE_API_KEY(or username and password) and refuses the request when it has none. A session stays bound to the key it was opened with, so the client must send the same headers on every request, which every client below does. HTTP mode covers the limits, allowed hosts and the startup warnings. -
A server in
apikeymode (ARCANE_MCP_HTTP_AUTH=apikey, see HTTP authentication) needs one secret per user: put the user’s Arcane API key where the entries below put the token (Authorization: Bearer <Arcane key>) and leave outX-Arcane-API-Key. That suits clients that can only setAuthorization. A connector whose headers an organisation shares (see Claude Desktop below) then shares that key’s Arcane permissions with everyone. -
Keep secrets off command lines.
ARCANE_MCP_HTTP_TOKENandARCANE_API_KEYdeliberately have no flag. Where a client can prompt for a value or expand an environment variable, its entry below does that; elsewhere the value sits in the client’s configuration file, so protect that file.
Which client supports what
Section titled “Which client supports what”| Client | Configuration | stdio | Streamable HTTP | Elicitation | Notes |
|---|---|---|---|---|---|
| Claude Desktop | claude_desktop_config.json, mcpServers | Yes | Custom connector only (public HTTPS) | Not documented | Remote connectors are reached from Anthropic’s cloud, not from your computer |
| Claude Code | claude mcp add, or .mcp.json with mcpServers | Yes | Yes | Yes | Expands ${VAR} in .mcp.json |
| VS Code (GitHub Copilot) | .vscode/mcp.json with servers, or .mcp.json with mcpServers | Yes | Yes | Yes (since 1.102) | At most 128 tools per chat request |
| Cursor | .cursor/mcp.json or ~/.cursor/mcp.json, mcpServers | Yes | Yes | Yes | Expands ${env:NAME} |
| Windsurf (Devin Desktop) | mcp_config.json, mcpServers | Yes | Yes | Not documented | Renamed Devin Desktop in 2026; see the section for the file location |
| Zed | settings.json, context_servers | Yes | Yes | No | Zed documents tools and prompts only |
| Cline | cline_mcp_settings.json, mcpServers | Yes | Yes ("type": "streamableHttp") | Not documented | Without type, a remote entry is treated as legacy SSE |
| Continue | config.yaml or .continue/mcpServers/*.yaml, mcpServers list | Yes | Yes (type: streamable-http) | Not documented | Secrets via ${{ secrets.NAME }} |
| LM Studio | mcp.json, mcpServers (Cursor’s notation) | Yes | Yes | Not documented | |
| Kiro | .kiro/settings/mcp.json or ~/.kiro/settings/mcp.json, mcpServers | Yes | Yes | Yes | ${VAR} only after you approve the variable |
| Gemini CLI | ~/.gemini/settings.json or .gemini/settings.json, mcpServers | Yes | Yes (httpUrl) | Not documented | Strips secret-looking variables it does not see in env |
| OpenAI Codex CLI | ~/.codex/config.toml, [mcp_servers.<name>] | Yes | Yes | Yes | TOML, not JSON |
| JetBrains AI Assistant | Settings dialog, JSON with mcpServers | Yes | URL only, no headers documented | Not documented | Reach a token-protected server through mcp-remote |
| Goose | ~/.config/goose/config.yaml, extensions | Yes | Yes (type: streamable_http) | Yes (form mode) |
The client facts on this page were checked against each client’s own documentation in October 2026; clients change quickly, so when an entry stops working, compare it with the client’s current documentation first.
What elicitation changes
Section titled “What elicitation changes”Destructive actions (deletes, prunes, kills, restores, redeploys…) never run without approval. On a client that supports MCP elicitation, a destructive call without confirm: true makes the server ask the user directly, in the client’s own interface; on the others the call is refused with a message telling the model to ask the user and repeat it with confirm: true. “Not documented” above means the client’s documentation does not say either way: arcane-mcp reads the capability the client declares when it connects, so nothing needs configuring, and a client without it gets the refusal. See Confirmation of destructive actions; ARCANE_MCP_ELICITATION=off turns the question off for every client.
Which surface for which client
Section titled “Which surface for which client”ARCANE_MCP_TOOL_SURFACE (or --tool-surface) decides how many tools the client sees; every surface reaches all 461 actions. See Tool surfaces.
dynamic(the default, 2 tools, about 400 tokens) suits every client and is the one to keep on clients that cap the number of tools or have a small context window: VS Code accepts at most 128 tools per chat request, and local models in LM Studio or Goose pay for every tool definition in their context.meta(52 tools, about 16 k tokens) fits under those caps and lets the model read every action name up front.individual(461 tools, about 111 k tokens) exceeds VS Code’s limit and is heavy for most clients; use it with exclusions (ARCANE_MCP_EXCLUDE_TOOLS) when you want per-tool approvals or allow-lists in the client.
Several clients also filter tools on their side (Codex enabled_tools, Gemini CLI includeTools, Goose available_tools, Kiro and Windsurf per-tool toggles); ARCANE_MCP_EXCLUDE_TOOLS does the same on the server, for every client at once.
Safety switches in an entry
Section titled “Safety switches in an entry”Add these to the entry’s env block (and, for the Docker form, a matching -e NAME argument), or as flags in args for the binary:
| Setting | Flag | Effect |
|---|---|---|
ARCANE_MCP_READ_ONLY=true | --read-only | Only read-only actions exist; nothing can change Arcane |
ARCANE_MCP_SAFE_MODE=true | --safe-mode | Mutating calls return a preview of the exact request instead of running |
ARCANE_MCP_EXCLUDE_TOOLS=swarm_node,image.prune | --exclude-tools | Hides domains, actions or tool names |
ARCANE_MCP_TOOL_SURFACE=meta | --tool-surface meta | Picks the surface |
In HTTP mode these are set where the server runs, not in the client entry, and apply to every user of that deployment. Safety explains each one.
Claude Desktop
Section titled “Claude Desktop”The Claude Desktop extension installs the server from one .mcpb file with a settings form, no JSON to edit. The entries below are for running it without the extension.
Open Settings → Developer → Edit Config, which opens (and creates) claude_desktop_config.json: ~/Library/Application Support/Claude/ on macOS, %APPDATA%\Claude\ on Windows, ~/.config/Claude/ on Linux. Quit Claude Desktop completely and start it again after editing; it loads servers at startup only.
{ "mcpServers": { "arcane": { "command": "/path/to/arcane-mcp", "env": { "ARCANE_URL": "https://arcane.example.com", "ARCANE_API_KEY": "arc_..." } } }}Claude Desktop does not inherit your shell’s PATH; if docker is not found, give its absolute path (which docker).
{ "mcpServers": { "arcane": { "command": "docker", "args": [ "run", "-i", "--rm", "-e", "ARCANE_URL", "-e", "ARCANE_API_KEY", "ghcr.io/jmrplens/arcane-mcp:latest" ], "env": { "ARCANE_URL": "https://arcane.example.com", "ARCANE_API_KEY": "arc_..." } } }}claude_desktop_config.json starts local processes only. A remote server is added as a custom connector (Customize → Connectors → + Add → Add custom connector): enter a name and https://arcane-mcp.example.com/mcp, choose No sign in under Authentication, and under Request headers add Authorization with Bearer <ARCANE_MCP_HTTP_TOKEN> and X-Arcane-API-Key with your Arcane key.
Claude reaches custom connectors from Anthropic’s cloud, not from your computer, so the URL must be public HTTPS; a loopback or private address cannot work. On Team and Enterprise plans a connector added by an owner shares its request headers with every member, so an X-Arcane-API-Key there gives everyone that key’s Arcane permissions. For a server only your network reaches, use mcp-remote as a stdio entry instead.
Claude Code
Section titled “Claude Code”Claude Code keeps a server in ~/.claude.json for the current project by default; --scope user makes it available in every project and --scope project writes it to .mcp.json in the repository. In .mcp.json it expands ${VAR} and ${VAR:-default} in command, args, env, url and headers, which keeps keys out of a shared file.
claude mcp add arcane \ --env ARCANE_URL=https://arcane.example.com \ --env ARCANE_API_KEY=arc_... \ -- /path/to/arcane-mcp--env puts the key in your shell history. To avoid that, write the entry in .mcp.json and export ARCANE_API_KEY in the shell you start claude from:
{ "mcpServers": { "arcane": { "command": "/path/to/arcane-mcp", "env": { "ARCANE_URL": "https://arcane.example.com", "ARCANE_API_KEY": "${ARCANE_API_KEY}" } } }}{ "mcpServers": { "arcane": { "command": "docker", "args": [ "run", "-i", "--rm", "-e", "ARCANE_URL", "-e", "ARCANE_API_KEY", "ghcr.io/jmrplens/arcane-mcp:latest" ], "env": { "ARCANE_URL": "https://arcane.example.com", "ARCANE_API_KEY": "${ARCANE_API_KEY}" } } }}{ "mcpServers": { "arcane": { "type": "http", "url": "https://arcane-mcp.example.com/mcp", "headers": { "Authorization": "Bearer ${ARCANE_MCP_HTTP_TOKEN}", "X-Arcane-API-Key": "${ARCANE_API_KEY}" } } }}claude mcp add --transport http arcane https://arcane-mcp.example.com/mcp --header "Authorization: Bearer …" --header "X-Arcane-API-Key: …" writes the same entry, with the values on the command line.
VS Code (GitHub Copilot)
Section titled “VS Code (GitHub Copilot)”VS Code reads .vscode/mcp.json in the workspace, or the mcp.json of your user profile (MCP: Open User Configuration), and keeps servers under servers. That format supports inputs: an input with "password": true prompts for the key once and stores it, so it never lands in the file. VS Code also reads a portable format, .mcp.json at the workspace root or ~/.copilot/mcp-config.json, with servers under mcpServers, which other Copilot tools share but which does not support inputs.
{ "inputs": [ { "type": "promptString", "id": "arcane-api-key", "description": "Arcane API key", "password": true } ], "servers": { "arcane": { "type": "stdio", "command": "/path/to/arcane-mcp", "env": { "ARCANE_URL": "https://arcane.example.com", "ARCANE_API_KEY": "${input:arcane-api-key}" } } }}{ "inputs": [ { "type": "promptString", "id": "arcane-api-key", "description": "Arcane API key", "password": true } ], "servers": { "arcane": { "type": "stdio", "command": "docker", "args": [ "run", "-i", "--rm", "-e", "ARCANE_URL", "-e", "ARCANE_API_KEY", "ghcr.io/jmrplens/arcane-mcp:latest" ], "env": { "ARCANE_URL": "https://arcane.example.com", "ARCANE_API_KEY": "${input:arcane-api-key}" } } }}{ "inputs": [ { "type": "promptString", "id": "arcane-mcp-token", "description": "arcane-mcp HTTP token", "password": true }, { "type": "promptString", "id": "arcane-api-key", "description": "Arcane API key", "password": true } ], "servers": { "arcane": { "type": "http", "url": "https://arcane-mcp.example.com/mcp", "headers": { "Authorization": "Bearer ${input:arcane-mcp-token}", "X-Arcane-API-Key": "${input:arcane-api-key}" } } }}A chat request carries at most 128 tools, counting every server and built-in tool, so keep dynamic or meta here (see Which surface for which client). Copilot sessions run by the Agent Host do not receive servers that need an ${input:…} prompt; for those, use the portable .mcp.json format.
Cursor
Section titled “Cursor”Cursor reads .cursor/mcp.json in the project, or ~/.cursor/mcp.json for every project. It expands ${env:NAME} (and ${userHome}, ${workspaceFolder}) in command, args, env, url and headers, so the entries below read the secrets from the environment Cursor starts from.
{ "mcpServers": { "arcane": { "command": "/path/to/arcane-mcp", "env": { "ARCANE_URL": "https://arcane.example.com", "ARCANE_API_KEY": "${env:ARCANE_API_KEY}" } } }}{ "mcpServers": { "arcane": { "command": "docker", "args": [ "run", "-i", "--rm", "-e", "ARCANE_URL", "-e", "ARCANE_API_KEY", "ghcr.io/jmrplens/arcane-mcp:latest" ], "env": { "ARCANE_URL": "https://arcane.example.com", "ARCANE_API_KEY": "${env:ARCANE_API_KEY}" } } }}{ "mcpServers": { "arcane": { "url": "https://arcane-mcp.example.com/mcp", "headers": { "Authorization": "Bearer ${env:ARCANE_MCP_HTTP_TOKEN}", "X-Arcane-API-Key": "${env:ARCANE_API_KEY}" } } }}Windsurf (Devin Desktop)
Section titled “Windsurf (Devin Desktop)”Windsurf was renamed Devin Desktop in June 2026. Its Cascade agent’s Open MCP config file action opens ~/.config/devin/mcp_config.json (%APPDATA%\devin\mcp_config.json on Windows), the same user file the Devin Local agent and the Devin CLI read; builds from before the rename read ~/.codeium/windsurf/mcp_config.json. Use the file that action opens on your installation. Servers sit under mcpServers. A remote server takes url (Cascade also accepts serverUrl, the key older Windsurf builds documented). Press the refresh button in the MCP panel after editing.
{ "mcpServers": { "arcane": { "command": "/path/to/arcane-mcp", "env": { "ARCANE_URL": "https://arcane.example.com", "ARCANE_API_KEY": "arc_..." } } }}{ "mcpServers": { "arcane": { "command": "docker", "args": [ "run", "-i", "--rm", "-e", "ARCANE_URL", "-e", "ARCANE_API_KEY", "ghcr.io/jmrplens/arcane-mcp:latest" ], "env": { "ARCANE_URL": "https://arcane.example.com", "ARCANE_API_KEY": "arc_..." } } }}{ "mcpServers": { "arcane": { "url": "https://arcane-mcp.example.com/mcp", "headers": { "Authorization": "Bearer ${env:ARCANE_MCP_HTTP_TOKEN}", "X-Arcane-API-Key": "${env:ARCANE_API_KEY}" } } }}Cascade expands ${env:NAME} in command, args, env, serverUrl, url and headers. The Devin CLI documentation describes that expansion only for its OAuth fields; if a header arrives unexpanded there, write the value, preferably in the gitignored .devin/mcp_config.local.json.
On Teams and Enterprise plans an admin may have to enable MCP, and once a single server is allowlisted every other one is blocked.
Zed keeps servers under context_servers in its settings.json (zed: open settings file; ~/.config/zed/settings.json on macOS and Linux), or in .zed/settings.json in a project. It does not document environment-variable expansion in these entries, so the values are written in the file.
{ "context_servers": { "arcane": { "command": "/path/to/arcane-mcp", "args": [], "env": { "ARCANE_URL": "https://arcane.example.com", "ARCANE_API_KEY": "arc_..." } } }}{ "context_servers": { "arcane": { "command": "docker", "args": [ "run", "-i", "--rm", "-e", "ARCANE_URL", "-e", "ARCANE_API_KEY", "ghcr.io/jmrplens/arcane-mcp:latest" ], "env": { "ARCANE_URL": "https://arcane.example.com", "ARCANE_API_KEY": "arc_..." } } }}{ "context_servers": { "arcane": { "url": "https://arcane-mcp.example.com/mcp", "headers": { "Authorization": "Bearer change-me", "X-Arcane-API-Key": "arc_..." } } }}Keep the Authorization header: a remote entry without one makes Zed start the MCP OAuth flow, which arcane-mcp does not offer, so run the server with ARCANE_MCP_HTTP_TOKEN for Zed.
Zed documents support for MCP tools and prompts, not elicitation, so destructive calls need confirm: true there.
In the Cline panel, select the MCP Servers icon, open the Configure tab and select Configure MCP Servers; that opens cline_mcp_settings.json, wherever your installation keeps it. Cline adds its own disabled and autoApprove keys; leave autoApprove empty or limited to read-only tools.
{ "mcpServers": { "arcane": { "command": "/path/to/arcane-mcp", "env": { "ARCANE_URL": "https://arcane.example.com", "ARCANE_API_KEY": "arc_..." }, "disabled": false, "autoApprove": [] } }}{ "mcpServers": { "arcane": { "command": "docker", "args": [ "run", "-i", "--rm", "-e", "ARCANE_URL", "-e", "ARCANE_API_KEY", "ghcr.io/jmrplens/arcane-mcp:latest" ], "env": { "ARCANE_URL": "https://arcane.example.com", "ARCANE_API_KEY": "arc_..." }, "disabled": false, "autoApprove": [] } }}Set "type": "streamableHttp" explicitly: without it Cline treats a remote entry as the legacy SSE transport, which arcane-mcp does not serve.
{ "mcpServers": { "arcane": { "type": "streamableHttp", "url": "https://arcane-mcp.example.com/mcp", "headers": { "Authorization": "Bearer change-me", "X-Arcane-API-Key": "arc_..." }, "disabled": false, "autoApprove": [] } }}Continue
Section titled “Continue”Continue reads ~/.continue/config.yaml; a workspace can add servers as block files in .continue/mcpServers/, which need the name, version and schema header shown here. ${{ secrets.NAME }} is resolved by Continue itself, so the file never holds the key. MCP tools are available in agent mode only; reload the Continue window after editing.
name: Arcaneversion: 0.0.1schema: v1mcpServers: - name: arcane command: /path/to/arcane-mcp env: ARCANE_URL: https://arcane.example.com ARCANE_API_KEY: ${{ secrets.ARCANE_API_KEY }}name: Arcaneversion: 0.0.1schema: v1mcpServers: - name: arcane command: docker args: - run - -i - --rm - -e - ARCANE_URL - -e - ARCANE_API_KEY - ghcr.io/jmrplens/arcane-mcp:latest env: ARCANE_URL: https://arcane.example.com ARCANE_API_KEY: ${{ secrets.ARCANE_API_KEY }}name: Arcaneversion: 0.0.1schema: v1mcpServers: - name: arcane type: streamable-http url: https://arcane-mcp.example.com/mcp requestOptions: headers: Authorization: Bearer ${{ secrets.ARCANE_MCP_HTTP_TOKEN }} X-Arcane-API-Key: ${{ secrets.ARCANE_API_KEY }}LM Studio
Section titled “LM Studio”LM Studio follows Cursor’s mcp.json notation. Open the file from the Program tab of the right-hand sidebar with Install → Edit mcp.json. Its documentation does not describe environment-variable expansion, so the values are written in the file. LM Studio asks before each tool call unless you allow a tool permanently.
{ "mcpServers": { "arcane": { "command": "/path/to/arcane-mcp", "env": { "ARCANE_URL": "https://arcane.example.com", "ARCANE_API_KEY": "arc_..." } } }}{ "mcpServers": { "arcane": { "command": "docker", "args": [ "run", "-i", "--rm", "-e", "ARCANE_URL", "-e", "ARCANE_API_KEY", "ghcr.io/jmrplens/arcane-mcp:latest" ], "env": { "ARCANE_URL": "https://arcane.example.com", "ARCANE_API_KEY": "arc_..." } } }}{ "mcpServers": { "arcane": { "url": "https://arcane-mcp.example.com/mcp", "headers": { "Authorization": "Bearer change-me", "X-Arcane-API-Key": "arc_..." } } }}Local models have small context windows; keep the dynamic surface here.
Kiro reads .kiro/settings/mcp.json in the workspace, or ~/.kiro/settings/mcp.json for every workspace; with both, workspace entries win. It expands ${VARIABLE_NAME}, but the IDE only does so for variables you have approved in the warning it shows when an entry first uses them. Users have reported that the Kiro CLI leaves ${…} unexpanded in some versions; if the server answers 401, write the values instead. Changes apply when you save.
{ "mcpServers": { "arcane": { "command": "/path/to/arcane-mcp", "args": [], "env": { "ARCANE_URL": "https://arcane.example.com", "ARCANE_API_KEY": "${ARCANE_API_KEY}" }, "disabled": false } }}{ "mcpServers": { "arcane": { "command": "docker", "args": [ "run", "-i", "--rm", "-e", "ARCANE_URL", "-e", "ARCANE_API_KEY", "ghcr.io/jmrplens/arcane-mcp:latest" ], "env": { "ARCANE_URL": "https://arcane.example.com", "ARCANE_API_KEY": "${ARCANE_API_KEY}" }, "disabled": false } }}{ "mcpServers": { "arcane": { "url": "https://arcane-mcp.example.com/mcp", "headers": { "Authorization": "Bearer ${ARCANE_MCP_HTTP_TOKEN}", "X-Arcane-API-Key": "${ARCANE_API_KEY}" }, "disabled": false } }}Gemini CLI
Section titled “Gemini CLI”Gemini CLI reads ~/.gemini/settings.json, or .gemini/settings.json in a project, and expands $VAR and ${VAR} in its string values. When it starts a stdio server it removes from the inherited environment every variable whose name looks secret (*KEY*, *TOKEN*, *PASSWORD*, *AUTH*…), so ARCANE_API_KEY reaches the server only if the entry’s env names it, as below.
{ "mcpServers": { "arcane": { "command": "/path/to/arcane-mcp", "env": { "ARCANE_URL": "https://arcane.example.com", "ARCANE_API_KEY": "$ARCANE_API_KEY" } } }}{ "mcpServers": { "arcane": { "command": "docker", "args": [ "run", "-i", "--rm", "-e", "ARCANE_URL", "-e", "ARCANE_API_KEY", "ghcr.io/jmrplens/arcane-mcp:latest" ], "env": { "ARCANE_URL": "https://arcane.example.com", "ARCANE_API_KEY": "$ARCANE_API_KEY" } } }}httpUrl is the Streamable HTTP endpoint; a bare url would be read as legacy SSE.
{ "mcpServers": { "arcane": { "httpUrl": "https://arcane-mcp.example.com/mcp", "headers": { "Authorization": "Bearer $ARCANE_MCP_HTTP_TOKEN", "X-Arcane-API-Key": "$ARCANE_API_KEY" } } }}Leave the entry’s trust option unset: it skips Gemini CLI’s own confirmation before every tool call.
OpenAI Codex CLI
Section titled “OpenAI Codex CLI”Codex reads ~/.codex/config.toml, or .codex/config.toml in a trusted project; the ChatGPT desktop app and the Codex IDE extension share the same file. Each server is a [mcp_servers.<name>] table.
[mcp_servers.arcane]command = "/path/to/arcane-mcp"env_vars = ["ARCANE_API_KEY"]tool_timeout_sec = 1800
[mcp_servers.arcane.env]ARCANE_URL = "https://arcane.example.com"env_vars forwards ARCANE_API_KEY from the environment Codex runs in, so the file never holds it; put it in the env table instead if you prefer.
[mcp_servers.arcane]command = "docker"args = ["run", "-i", "--rm", "-e", "ARCANE_URL", "-e", "ARCANE_API_KEY", "ghcr.io/jmrplens/arcane-mcp:latest"]env_vars = ["ARCANE_API_KEY"]tool_timeout_sec = 1800
[mcp_servers.arcane.env]ARCANE_URL = "https://arcane.example.com"[mcp_servers.arcane]url = "https://arcane-mcp.example.com/mcp"bearer_token_env_var = "ARCANE_MCP_HTTP_TOKEN"env_http_headers = { "X-Arcane-API-Key" = "ARCANE_API_KEY" }tool_timeout_sec = 1800bearer_token_env_var and env_http_headers name environment variables; Codex reads their values when it connects and sends Authorization: Bearer … and X-Arcane-API-Key: ….
Codex gives a tool call 60 seconds unless tool_timeout_sec says otherwise, which cuts off image pulls, builds and redeploys; the entries above raise it to match the server’s own ARCANE_MCP_ACTION_TIMEOUT default of 30 minutes. Codex supports elicitation; with a granular approval_policy, keep mcp_elicitations = true, or Codex rejects the server’s confirmation question and the destructive call is refused.
JetBrains AI Assistant
Section titled “JetBrains AI Assistant”In IntelliJ IDEA, GoLand, PyCharm and the other JetBrains IDEs, open Settings → Tools → AI Assistant → Model Context Protocol (MCP), select Add, choose the transport and paste the JSON. Choose whether the server is available globally or for the current project, then select OK and Apply. Import from Claude copies the servers already in Claude Desktop’s configuration.
JetBrains documents an stdio entry as command and args only, plus a Working directory field. arcane-mcp reads a .env file in its working directory, so put the credentials there rather than in the JSON:
ARCANE_URL=https://arcane.example.comARCANE_API_KEY=arc_...Set Working directory to the folder that holds that .env file (/path/to/arcane-config), and paste:
{ "mcpServers": { "arcane": { "command": "/path/to/arcane-mcp", "args": [] } }}The container does not see the working directory’s .env, but Docker can read the same file with --env-file:
{ "mcpServers": { "arcane": { "command": "docker", "args": [ "run", "-i", "--rm", "--env-file", "/path/to/arcane-config/.env", "ghcr.io/jmrplens/arcane-mcp:latest" ] } }}JetBrains documents a Streamable HTTP entry as a url alone, with no headers. To reach a server protected by ARCANE_MCP_HTTP_TOKEN, add mcp-remote as an stdio server instead; it carries the headers. The plain entry works only against a server you run without a token on loopback or a trusted network, with its own ARCANE_API_KEY:
{ "mcpServers": { "arcane": { "url": "http://127.0.0.1:8080/mcp" } }}Goose keeps MCP servers as extensions under extensions in ~/.config/goose/config.yaml (%APPDATA%\Block\goose\config\config.yaml on Windows). goose configure → Add Extension writes the same entry interactively, and can store secrets in the system keyring (env_keys) instead of the file. Goose supports elicitation in form mode.
extensions: arcane: name: arcane type: stdio enabled: true cmd: /path/to/arcane-mcp args: [] envs: ARCANE_URL: https://arcane.example.com ARCANE_API_KEY: arc_... timeout: 1800extensions: arcane: name: arcane type: stdio enabled: true cmd: docker args: [run, -i, --rm, -e, ARCANE_URL, -e, ARCANE_API_KEY, "ghcr.io/jmrplens/arcane-mcp:latest"] envs: ARCANE_URL: https://arcane.example.com ARCANE_API_KEY: arc_... timeout: 1800A remote extension takes uri, not url, and the type is spelled streamable_http:
extensions: arcane: name: arcane type: streamable_http enabled: true uri: https://arcane-mcp.example.com/mcp headers: Authorization: Bearer change-me X-Arcane-API-Key: arc_... timeout: 1800timeout is how long Goose waits for one tool call, in seconds; the entries above raise the usual 300 so that long image pulls and redeploys can finish.
mcp-remote (a stdio bridge)
Section titled “mcp-remote (a stdio bridge)”mcp-remote is a small Node.js program a client starts as an stdio server and that forwards everything to a remote one. It is the way to reach an HTTP deployment from a client that cannot send headers (JetBrains), or that only connects to public URLs (Claude Desktop’s connectors). It expands ${NAME} in its arguments from its own environment, so the secrets sit in the entry’s env block:
{ "mcpServers": { "arcane": { "command": "npx", "args": [ "-y", "mcp-remote", "https://arcane-mcp.example.com/mcp", "--header", "Authorization:${ARCANE_AUTH}", "--header", "X-Arcane-API-Key:${ARCANE_API_KEY}" ], "env": { "ARCANE_AUTH": "Bearer change-me", "ARCANE_API_KEY": "arc_..." } } }}Write each header with no space after the colon and keep the space inside the variable: some clients do not quote arguments that contain spaces when they start npx. mcp-remote refuses plain http:// URLs other than localhost unless you add --allow-http.
When something does not work
Section titled “When something does not work”- The server does not start. Run the entry’s command in a terminal with the same variables. A missing
ARCANE_URL, or in stdio mode a missingARCANE_API_KEY(or username and password), is reported and the process exits. - Every tool call fails with a
401from Arcane. Arcane rejected the key: in stdio mode checkARCANE_API_KEY; over HTTP, the key in the client’sX-Arcane-API-Keyheader (or the server’s ownARCANE_API_KEYwhen the client sends none), or withARCANE_MCP_HTTP_AUTH=apikeythe key the client sends as itsAuthorization: Bearertoken. The error’s hint names the credential that was sent:ARCANE_API_KEY(or the login) when it was the server’s own, theX-Arcane-API-Keyheader when it was the client’s, and the client’s Bearer token withARCANE_MCP_HTTP_AUTH=apikey. - The HTTP connection itself is refused with
401. A401withWWW-Authenticate: Bearerbefore any MCP traffic means theAuthorizationheader is missing or does not matchARCANE_MCP_HTTP_TOKEN; withARCANE_MCP_HTTP_AUTH=apikey, that the Bearer is missing or Arcane does not accept it as an API key. - The Docker form hangs or exits at once. Check that
-iis there, that no-tor-dcrept in, and thatARCANE_URLresolves from inside the container. - The client shows no tools or too many. See Which surface for which client.
- Destructive calls keep coming back asking for confirmation. That is the refusal for clients without elicitation; the model should ask you and repeat the call with
confirm: true. See Safety. - Anything else. Troubleshooting quotes the errors you are likely to meet, from startup to HTTP refusals, with their cause and fix.