Try Bifrost Enterprise free for 14 days. Request access

Claude Code Internal MCP Servers: Connect Without Exposing Credentials

Claude Code Internal MCP Servers: Connect Without Exposing Credentials

TL;DR

  • Putting MCP server credentials in Claude Code's settings.json, .mcp.json, or environment files spreads secrets to every developer machine with no rotation path and no audit trail.
  • Bifrost stores upstream MCP credentials on the gateway and gives each developer a scoped virtual key, so Claude Code never holds a service token.
  • Bifrost supports six MCP authentication types, including static headers, OAuth 2.0, per-user OAuth, per-user headers, and enterprise Token Exchange.
  • Header values can reference env.VAR or, in Bifrost Enterprise, vault.<path> secrets in AWS Secrets Manager, GCP Secret Manager, or HashiCorp Vault.

Connecting Claude Code to internal MCP servers creates a credential exposure problem, because each server needs an API key, bearer token, or OAuth grant and the default place to put it is the developer's machine. The default approach, placing API keys and service tokens directly in settings.json or environment variables, means credentials travel with developer workstations, get committed to dotfiles repos, and proliferate across every machine that runs the agent. Bifrost, the open-source AI gateway built in Go by Maxim AI, solves this by sitting between Claude Code and every internal MCP server: credentials stay on the gateway, and Claude Code receives only a scoped virtual key.

Why Credential Exposure Is a Real Risk for MCP Deployments

Credential exposure is a primary MCP security risk for Claude Code because every locally configured server multiplies the number of machines holding production secrets. A single compromised laptop then exposes every internal system its MCP servers can reach, and nobody can tell which developer or agent session used a shared token.

Claude Code connects to MCP servers to read files, query databases, call internal APIs, and execute business logic. Each of those connections needs authentication. The straightforward path is to embed credentials in the developer's local config.

That approach has three structural problems:

  • Blast radius: a single leaked developer workstation exposes every service the developer was authorized to use
  • No rotation path: credentials baked into config files are rarely rotated because the rotation requires updating every developer's machine
  • No audit trail: when a shared static token is used, you cannot determine which developer (or which AI agent invocation) executed a given tool call

The servers themselves add risk. A 2025 empirical study of 1,899 open-source MCP servers found general vulnerabilities in 7.2% of them and MCP-specific tool poisoning in 5.5%, so a developer who wires a real service token into an unvetted server hands that token to code nobody has reviewed. The attack surface grows with every new MCP server added to a developer's config.

The correct architecture is to centralize credential storage on a gateway, issue developers short-lived scoped keys, and enforce access control per consumer. Bifrost, deployed as an MCP gateway, is designed to do exactly that. The practical guide to using an MCP gateway with Claude Code covers the broader setup.

How Bifrost Handles MCP Authentication Centrally

Bifrost handles MCP authentication centrally by storing every upstream credential on the gateway, refreshing OAuth tokens, and enforcing access control per virtual key. Claude Code authenticates only to Bifrost, and Bifrost authenticates to each internal MCP server on the developer's behalf, so no upstream service credential is ever written to a developer machine. The developer's settings.json contains only a Bifrost virtual key, which carries no inherent permissions to any backend system.

Six Authentication Modes for Upstream MCP Servers

Bifrost supports six MCP authentication types for connecting to external MCP servers:

Auth type How it works Best fit for Claude Code
None No upstream authentication Local STDIO tools and public servers
Headers Static API keys, bearer tokens, or custom headers stored in Bifrost and injected at tool execution time Internal APIs with one service token
OAuth 2.0 Admin-level shared OAuth token with automatic refresh, PKCE, and dynamic client registration A shared third-party service the whole team uses
Per-User OAuth Each developer completes a consent flow; Bifrost stores tokens per identity Personal integrations such as GitHub or Notion
Per-User Headers Each developer submits their own header values through a Bifrost-hosted form Per-user API keys or tenant tokens
Token Exchange (Enterprise) Each caller's identity-provider token is exchanged per caller and never persisted Internal servers that trust your identity provider

For most internal API use cases, headers auth is the right starting point. The credential is configured once on the gateway and never leaves it. This is the body of a POST /api/mcp/client request, or an entry under mcp.client_configs in config.json:

{
  "name": "internal_crm",
  "connection_type": "http",
  "connection_string": "https://crm-mcp.internal.example.com/mcp",
  "auth_type": "headers",
  "headers": {
    "Authorization": "Bearer your-service-token",
    "X-Tenant-ID": "production"
  },
  "tools_to_execute": ["get_customer", "list_accounts"]
}

The Authorization and X-Tenant-ID values are stored in Bifrost. Claude Code, configured to point at Bifrost, never sees them.

For sensitive values, Bifrost supports environment variable references in the connection string and header values using the env.VARIABLE_NAME syntax, so even the gateway config file does not contain plaintext credentials. Header values are also encrypted at rest when BIFROST_ENCRYPTION_KEY is set.

Referencing Secrets Without Storing Them in Config

Environment variable references keep credentials out of the same MCP client configuration:

{
  "name": "data_warehouse",
  "connection_type": "http",
  "connection_string": "env.DW_MCP_URL",
  "auth_type": "headers",
  "headers": {
    "Authorization": "env.DW_SERVICE_TOKEN"
  },
  "tools_to_execute": ["*"]
}

Bifrost validates at startup that env.DW_MCP_URL and env.DW_SERVICE_TOKEN are set, resolves them when the client connects, and redacts them from API responses and the management UI.

For teams that want a secrets manager as the source of truth, Bifrost Enterprise secret management connects AWS Secrets Manager, GCP Secret Manager, or HashiCorp Vault. Any field that accepts env.VAR, including MCP auth headers, then also accepts a vault.<path> reference such as "Authorization": "vault.prod/mcp/crm#token", which Bifrost resolves at runtime. Bifrost reads from the vault; Claude Code never touches it.

Scoping Access with Virtual Keys

Virtual keys scope access by giving each developer, team, or agent a Bifrost-issued key that carries an explicit MCP allow-list, a budget, and rate limits. The key itself grants nothing on any backend system; it only defines which tools that consumer can reach through the gateway.

The second half of the credential isolation problem is ensuring that different developers, teams, or agent invocations access only the tools they are authorized to use.

Bifrost's virtual keys are the primary governance entity. Each virtual key can be configured with:

  • An explicit list of MCP servers it can access
  • Tool-level filtering within each server
  • Spend budgets and rate limits
  • An optional expiry, from 30 minutes to a custom date, for short-lived credentials

A developer running Claude Code connects to Bifrost using their virtual key. The gateway evaluates what that key is permitted to access and presents Claude Code with only those tools.

curl -X POST http://your-bifrost-instance:8080/api/governance/virtual-keys \
  -H "Content-Type: application/json" \
  -d '{
    "name": "dev-alice",
    "mcp_configs": [
      { "mcp_client_name": "internal_crm", "tools_to_execute": ["get_customer", "list_accounts"] },
      { "mcp_client_name": "knowledge_base", "tools_to_execute": ["*"] }
    ],
    "budgets": [{ "max_limit": 10, "reset_duration": "1d" }],
    "rate_limit": { "request_max_limit": 60, "request_reset_duration": "1m" }
  }'

Alice's Claude Code session can call internal_crm and knowledge_base tools. It cannot call data_warehouse tools even if that server is registered on the same Bifrost instance. The restriction is enforced at the gateway, not in Alice's client config. MCP tool filtering per virtual key gives you tool-level granularity within each server, and budgets and rate limits reset on the schedule you set.

For enterprise teams provisioning access at scale, access profiles let you define reusable permission templates that automatically allocate virtual keys with the correct MCP server lists, budgets, and rate limits when a new user is provisioned.

Connecting Claude Code to the Bifrost MCP Gateway

Claude Code connects to Bifrost in two places: the ANTHROPIC_BASE_URL and ANTHROPIC_AUTH_TOKEN environment variables route model traffic through the gateway, and a single MCP server entry pointed at Bifrost's /mcp endpoint replaces every per-server MCP config on the developer machine.

Once your upstream MCP servers are registered in Bifrost with their credentials, Claude Code connects to a single Bifrost endpoint. All tool discovery, execution, and authentication flow through that endpoint.

Claude Code's settings.json configuration is minimal, following the Bifrost Claude Code integration:

{
  "env": {
    "ANTHROPIC_BASE_URL": "http://your-bifrost-instance:8080/anthropic",
    "ANTHROPIC_AUTH_TOKEN": "sk-bf-dev-alice-xxxx"
  }
}

The ANTHROPIC_AUTH_TOKEN is Alice's virtual key. Claude Code sends it as an Authorization: Bearer header, and no Anthropic account login is required. The virtual key authenticates Alice to Bifrost; Bifrost handles authentication to every upstream MCP server on her behalf.

When Claude Code is configured to use Bifrost as an MCP gateway endpoint, it can also be pointed at Bifrost's /mcp endpoint directly, which exposes all registered tools through a single aggregated MCP server. The quickest way to add it is the claude mcp add command:

claude mcp add --transport http bifrost http://your-bifrost-instance:8080/mcp \
  --header "Authorization: Bearer sk-bf-dev-alice-xxxx" \
  --scope user

The same entry can be written directly into .mcp.json or ~/.claude.json:

{
  "mcpServers": {
    "bifrost": {
      "type": "http",
      "url": "http://your-bifrost-instance:8080/mcp",
      "headers": {
        "Authorization": "Bearer sk-bf-dev-alice-xxxx"
      }
    }
  }
}

Claude Code connects once, discovers all tools Alice is authorized to use, and has no visibility into which upstream servers back each tool or what credentials those servers require. Anthropic's Claude Code MCP documentation covers the --scope options and config file locations, and the guide on connecting Claude Code to multiple MCP servers through one gateway walks through a larger tool catalog.

Forwarding Per-Request Context Without Exposing Service Credentials

Some internal APIs require per-request context that varies by caller: a user token for data-plane authorization, a tenant ID for multi-tenant services, or a trace ID for observability. Bifrost's allowed_extra_headers field handles this without requiring developers to know the upstream credentials. Header forwarding is available in Bifrost v1.5.0-prerelease1 and above.

You configure which headers a given MCP client will accept from callers in its client configuration:

{
  "name": "internal_crm",
  "connection_type": "http",
  "connection_string": "https://crm-mcp.internal.example.com/mcp",
  "auth_type": "headers",
  "headers": {
    "Authorization": "Bearer service-level-token"
  },
  "allowed_extra_headers": ["x-user-token", "x-tenant-id"],
  "tools_to_execute": ["*"]
}

The static Authorization header (the service-level credential) is managed by Bifrost and injected on every tool call. The x-user-token and x-tenant-id values are forwarded only when the caller provides them, and only if they appear in that client's allowlist. Each client enforces its own allowlist at execution time, so a header allowed for the CRM server is not automatically forwarded to the data warehouse server.

The developer's Claude Code session passes these headers in the inference request, for example through the ANTHROPIC_CUSTOM_HEADERS environment variable. The service-level credential is never visible to the developer; only the developer's own per-request values, such as their user token, live on their machine. Because forwarded values come from the caller, the upstream server should treat them as untrusted input.

Per-User OAuth for Personal Integrations

When internal tools require developers to authenticate with their own identity (for audit trail purposes, or because the backing API enforces per-user authorization), Bifrost's per-user OAuth flow handles the consent and token storage without requiring developers to manage tokens themselves.

On the first tool call to a per-user OAuth server, Bifrost returns an mcp_auth_required payload containing an authorize_url, and the tool is not executed. The developer opens that URL, completes the OAuth consent flow with the upstream provider, and Bifrost stores the resulting token against their identity. Subsequent tool calls execute normally. Token refresh is automatic, and each stored credential appears on the MCP sessions page, where it can be revoked per identity.

Per-user OAuth means developers authenticate with their own credentials once, through a governed flow, and Bifrost enforces that each developer can only act within the scope they were granted by the upstream system.

Audit Trails and Observability

With credentials centralized on the gateway, every tool call passes through a single point where it can be logged with full context. Bifrost request logs record MCP tool executions with their arguments and results, and selected request headers can be captured as metadata on every LLM and MCP log entry for tracing and attribution.

For regulated environments, Bifrost Enterprise audit logs record administrative activity, such as changes to MCP clients and virtual keys, in HMAC-signed entries with configurable retention, giving compliance reviews for SOC 2, GDPR, HIPAA, or ISO 27001 a verifiable record of access changes. Log exports offload request and response payloads to S3 or GCS object storage.

Secrets detection guardrails can scan prompts and completions for API keys, tokens, and credentials before they reach upstream systems, providing a second layer of protection against accidental credential leakage in agent outputs.

Enterprise Deployment: Keeping Everything Inside the Network Boundary

Bifrost Enterprise keeps Claude Code's tool traffic and every MCP credential inside your network by running the gateway in your own VPC or on-premises. Developers reach an internal Bifrost endpoint, and Bifrost reaches internal MCP servers over private DNS.

For teams with strict network segmentation requirements, Bifrost supports in-VPC deployment so the gateway runs inside your private cloud infrastructure. Claude Code connects to a Bifrost instance that has no public internet exposure. Internal MCP servers are reachable from Bifrost by private DNS, and no credentials or tool traffic cross the public internet.

The Bifrost Enterprise page covers deployment options including air-gapped configurations and on-premises infrastructure for teams that cannot use cloud-hosted gateways. Teams in regulated industries can also review Claude Code in VPC deployments with audit logs.

Credential Direct MCP config in Claude Code Claude Code through Bifrost
Upstream service token On every developer machine Stored on the gateway, env. or vault. referenced
Per-user OAuth token Managed by each client Stored per identity, revocable from MCP sessions
What the developer holds Every upstream secret One scoped, optionally expiring virtual key
Rotation Update every machine Update once on the gateway

Centralize Credentials Once, Govern Access Everywhere

The correct model for Claude Code and internal MCP servers is not to distribute credentials to every developer's machine. It is to register each internal server once on Bifrost, configure the right authentication type, and issue developers virtual keys that carry exactly the access they need.

The Bifrost MCP gateway resource page covers the full set of authentication modes, virtual key configuration, and tool filtering options. For the day-to-day workflow, see how to add and govern MCP servers in Claude Code and how to connect Claude Code to an MCP gateway. For teams running multiple Claude Code users against a shared set of internal tools, the governance and audit capabilities in Bifrost Enterprise remove the operational overhead of per-machine credential management.

Frequently Asked Questions

How do I add an MCP server to Claude Code?

Run claude mcp add with the server name, transport, and URL, for example claude mcp add --transport http bifrost http://your-bifrost-instance:8080/mcp. Add --header for authentication and --scope to choose local, project, or user storage. Pointing that single entry at Bifrost gives Claude Code every tool the virtual key allows.

Where does Claude Code store MCP server configuration?

Claude Code stores MCP servers in .mcp.json at the project root for project scope, or in ~/.claude.json for user and local scope. Environment variables such as ANTHROPIC_BASE_URL live in settings.json under ~/.claude/ or .claude/ in the project. A checked-in .mcp.json is shared with everyone who clones the repository, which is why it should contain only a Bifrost URL and a reference to a virtual key rather than upstream secrets.

How does MCP authentication work through a gateway?

Through a gateway, MCP authentication happens in two hops. Claude Code authenticates to Bifrost with a virtual key, and Bifrost authenticates to each upstream MCP server with the credential configured for it, whether static headers, OAuth 2.0, per-user OAuth, per-user headers, or Token Exchange. The upstream credential never reaches the client.

Can Claude Code use environment variables for MCP credentials?

Claude Code can, but environment variables on a developer machine are still credentials on that machine. With Bifrost, the env.VAR references live in the gateway's configuration instead, and Bifrost validates them at startup and redacts them from API responses and the UI. Developers only need ANTHROPIC_BASE_URL and a virtual key.

Do developers need an Anthropic API key when Claude Code runs through Bifrost?

No. When ANTHROPIC_AUTH_TOKEN is set to a Bifrost virtual key, Claude Code sends it as a bearer token and no Anthropic account login is required. Bifrost holds the provider keys and applies the virtual key's budgets, rate limits, and MCP allow-list to every request.

How do I revoke a developer's access to internal MCP tools?

Disable or expire the developer's virtual key, or remove the MCP client from its allow-list; expired or inactive keys are rejected with a 403 at tool execution time. For per-user OAuth or per-user headers, revoke the individual credential from the MCP sessions page. No change is needed on the developer's machine.

To see how Bifrost can centralize MCP authentication for your Claude Code deployments, book a demo with the Bifrost team.