Skip to content

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.

Before you commit an MCP config
  • 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 headerOAuth
What you configureAuthorization: Bearer <key>Only the URL; the client handles sign-in
Who creates the credentialYou, in the provider's dashboardThe client, after you sign in
Good forMetered services, CI, scripts, shared team setupsServers that act inside your account elsewhere
RevocationDelete the key in the dashboardRevoke the app grant
Where it can leakConfig files, shell history, screenshotsRarely; 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 local scope or --scope user, not project.

  • 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}" in mcp.json. Note the env: 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}" in args and set AUTH_HEADER in the env block. The key then sits in your local claude_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:

  1. Commit references, not values. Shared files use ${VAR}, ${env:VAR} or ${input:id}. Add a .env.example with the variable name and an empty value if your team uses one.
  2. 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.
  3. Check before pushing. Search the diff for Bearer followed 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.

  1. Create the new key in the dashboard.
  2. 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.
  3. Restart the clients and check that the server still shows as connected.
  4. 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 after Bearer),
  • 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

  1. Authorization (MCP specification 2025-06-18) (Documentation)
  2. Connect Claude Code to tools via MCP (Claude Code docs) (Documentation)
  3. Model Context Protocol (Cursor docs) (Documentation)
  4. MCP configuration reference (VS Code docs) (Documentation)