Guide · Auth and keys
MCP server authentication: bearer keys, OAuth, safe configs
MCP server authentication from the client side: bearer headers vs OAuth, how each client reads a key from the environment, and how to rotate keys safely.
Guide
This article was drafted with AI assistance from the sources listed below and edited and published by Teneo Labs. Disclosure: this site is run by Teneo Labs, the company behind Live Web Data API, which this article mentions.
- The file contains a variable or input reference, never the key itself.
- The variable is set where the client starts, not only in one terminal tab.
- Each person or machine has its own key.
- Personal keys live in user scope; shared entries live in project scope.
- git log -p shows no key in the file's history.
- You know how to revoke a key from the provider's dashboard.
A remote MCP server needs to know who is calling. The MCP specification covers authorization for HTTP transports and leaves stdio servers to read credentials from the environment. In practice, remote servers use one of two patterns: a static key sent as a bearer header, or an OAuth flow where the client opens a browser for you to sign in. This guide covers both from the client side, then the part that causes most real incidents: keeping the key out of files you commit.
Bearer header or OAuth
| Bearer header | OAuth | |
|---|---|---|
| What you configure | Authorization: Bearer <key> | Only the URL; the client handles sign-in |
| Who creates the credential | You, in the provider's dashboard | The client, after you sign in |
| Good for | Metered services, CI, scripts, shared team setups | Servers that act inside your account elsewhere |
| Revocation | Delete the key in the dashboard | Revoke the app grant |
| Where it can leak | Config files, shell history, screenshots | Rarely; tokens live in the client's store |
A fetch server reads public pages; it does not act inside your accounts. What it needs to know is which account to charge and rate-limit, so a bearer key is the common choice. If a server supports OAuth and your client does too, the client will usually prompt you on first connect, and there is nothing to put in the file.
Bearer headers, client by client
Every client lets you send a header. The difference is how each one keeps the key out of the file:
-
Claude Code, command line. Your shell expands the variable before Claude Code sees it:
claude mcp add --transport http live-web-data https://fetchmcp.net/mcp \ --header "Authorization: Bearer $LIVE_WEB_DATA_API_KEY"The expanded key is stored in your Claude Code config, so use the default
localscope or--scope user, notproject. -
Claude Code,
.mcp.json. For a committed project file, write"Authorization": "Bearer ${LIVE_WEB_DATA_API_KEY}". Claude Code expands${VAR}(and${VAR:-default}) when it loads the file, so each developer supplies their own key. -
Cursor. Use
"Authorization": "Bearer ${env:LIVE_WEB_DATA_API_KEY}"inmcp.json. Note theenv:prefix. The variable must exist in the environment Cursor was launched from. -
VS Code. Declare an input with
"type": "promptString"and"password": true, then reference it as${input:live-web-data-key}. VS Code asks once and stores the value securely; the file holds only the reference. -
Claude Desktop with
mcp-remote. Pass the header as"Authorization:${AUTH_HEADER}"inargsand setAUTH_HEADERin theenvblock. The key then sits in your localclaude_desktop_config.json, which is not in a repository, but treat that file as a secret too.
Full config files for each client are in the setup guide.
Keeping keys out of git
A key in a committed file stays in the repository's history after you delete the line. Three habits prevent that:
- Commit references, not values. Shared files use
${VAR},${env:VAR}or${input:id}. Add a.env.examplewith the variable name and an empty value if your team uses one. - Split by scope. Shared server entries (name, URL, transport) go in the project file. Anything personal goes in user scope or the client's secure input store.
- Check before pushing. Search the diff for
Bearerfollowed by anything that is not a variable. A secret scanner in CI catches what review misses.
If a key does reach a commit, assume it is exposed. Revoke it first, then clean up.
Rotating a key without breaking configs
Rotation is painless when the provider allows more than one active key. Live Web Data API, for example, allows up to five active keys per account.
- Create the new key in the dashboard.
- Update the variable or input everywhere it is used: your shell profile, CI secrets, each developer's environment. VS Code users clear the stored input so it prompts again.
- Restart the clients and check that the server still shows as connected.
- Watch usage on the old key drop to zero, then revoke it.
If the provider allows only one key, plan a short window where everyone updates at once.
When authentication fails
An auth failure looks like a connection failure in most clients. The server rejects the request before any tool runs, so the server list shows failed or the tools never appear. Typical causes:
- the header name or format is wrong (
Authorization: Bearer <key>, one space afterBearer), - the variable was not expanded and the server received the literal
${...}text, - the key was revoked or the account is suspended.
A 401 means the key is missing or wrong; a 403 means the key is valid but the account may not use the server. The debugging runbook shows how to send the same header with MCP Inspector and see the server's answer directly.
A worked example
Live Web Data API, made by Teneo Labs, authenticates its MCP server with Authorization: Bearer <key>, the same key as its REST API. A missing or invalid key returns 401 and a suspended account 403, before any tool runs. Keys are created in the dashboard once your plan is active. See the MCP docs for the current configs.
Frequently asked questions
Should an MCP server use OAuth or an API key?
OAuth suits servers that act on a user's account in another service, because the user signs in and can revoke access. A static API key in a bearer header suits servers that bill or rate-limit an account, such as a fetch server. Use whichever the server documents.
How do I pass an API key to an MCP server in Claude Code?
Add --header "Authorization: Bearer $YOUR_VAR" to claude mcp add, so your shell expands the variable. In a shared .mcp.json, write Bearer ${YOUR_VAR} and Claude Code expands it when it loads the file.
What should I do if I committed an MCP API key?
Revoke it in the provider's dashboard first, then create a new one. Removing the line in a later commit does not remove it from git history, and anyone with a clone still has it.
Sources
- Authorization (MCP specification 2025-06-18) (Documentation)
- Connect Claude Code to tools via MCP (Claude Code docs) (Documentation)
- Model Context Protocol (Cursor docs) (Documentation)
- MCP configuration reference (VS Code docs) (Documentation)