Introduction

Some MCP Servers require you to put API keys directly in mcp.json. However, hardcoding credentials in a config file comes with real risks.

  • Accidental Git commits: A missing .gitignore entry or a careless git add can push your API key to a repository
  • Exposure to AI agents: AI agents like Claude Code have access to the filesystem. If mcp.json is read, your API key ends up in the AI's context
  • File leaks: If your machine is lost, stolen, or accessed without authorization, credentials stored in plain text are immediately compromised

In this article, I'll walk through two approaches to keep API keys out of mcp.json, using Backlog MCP Server as an example.

Option 1: Keychain (Mac only)

This approach stores the API key in macOS Keychain and retrieves it dynamically when the MCP server starts.

1. Generate a Backlog API key

Generate an API key from the Backlog settings page.

None

2. Save it to Keychain

Use the security add-generic-password command to register the API key in Keychain.

security add-generic-password -a "$USER" -s "BACKLOG_API_KEY" -w

You'll be prompted to enter the password interactively.

password data for new item:
retype password for new item:

The value passed to -s (BACKLOG_API_KEY) is the service name in Keychain. Choose something descriptive — you'll reference it later with find-generic-password.

Note: By placing -w at the end, the password is never passed as a command-line argument, which prevents it from appearing in your shell history.

3. Configure mcp.json

The official setup example looks like this. (This article uses npx.)

{
  "mcpServers": {
    "backlog": {
      "command": "npx",
      "args": ["backlog-mcp-server"],
      "env": {
        "BACKLOG_DOMAIN": "your-domain.backlog.com",
        "BACKLOG_API_KEY": "your-api-key"
      }
    }
  }
}

Here's the Keychain-based version.

{
  "mcpServers": {
    "backlog": {
      "command": "bash",
      "args": [
        "-c",
        "BACKLOG_API_KEY=$(security find-generic-password -a \"$USER\" -s \"BACKLOG_API_KEY\" -w) exec npx backlog-mcp-server"
      ],
      "env": {
        "BACKLOG_DOMAIN": "your-backlog-domain"
      }
    }
  }
}

The command is changed to bash, and inside args, the API key is fetched from Keychain before launching npx backlog-mcp-server. security find-generic-password -w prints the stored value to stdout, which is then assigned to the BACKLOG_API_KEY environment variable.

The args becomes a bit harder to read, but the API key never appears in mcp.json.

Note: BACKLOG_API_KEY is only set within this bash -c subprocess. When exec npx backlog-mcp-server replaces the bash process, the environment variables are inherited — so the MCP server receives the key correctly. The variable does not propagate to your shell session, so running echo $BACKLOG_API_KEY in your terminal will return nothing.

With this setup, the API key is never stored in plain text in any file, significantly reducing the risk of exposure to AI agents or file leaks.

Option 2: 1Password CLI (cross-platform)

If you use 1Password, the 1Password CLI is another option — and unlike Keychain, it works on Windows too.

Here's what the configuration looks like with 1Password CLI.

{
  "mcpServers": {
    "backlog": {
      "command": "op",
      "args": [
        "run",
        "--env-file=/dev/null",
        "--",
        "npx",
        "backlog-mcp-server"
      ],
      "env": {
        "BACKLOG_DOMAIN": "your-domain.backlog.com",
        "BACKLOG_API_KEY": "op://your-vault/your-item/your-field"
      }
    }
  }
}

Here's what each setting does.

  • "command": "op" — Launches the 1Password CLI as the main process
  • "run" — The op run command. It replaces any op:// references in env with the actual secret values before launching the child process
  • "--env-file=/dev/null" — By default, op run looks for a .env file. This disables that behavior so only the env block in mcp.json is used
  • "--" — A separator indicating that everything after this is the child command, not an op option
  • "npx", "backlog-mcp-server" — The actual MCP server command to run
  • BACKLOG_API_KEY: "op://your-vault/your-item/your-field" — A secret reference pointing to a specific field in your 1Password vault. op run detects this and substitutes the real value before passing it to npx

The API key is never stored in plain text — it's fetched securely from 1Password at runtime.

Conclusion

Hardcoding API keys in mcp.json creates risks you might not notice until it's too late — accidental Git commits, AI agents reading your credentials, or a leaked config file.

Here's a summary of the two approaches covered in this article.

  • Option 1: Keychain — Mac only. No extra tools required, easy to set up
  • Option 2: 1Password CLI — Cross-platform. The smoothest option if you're already using 1Password

When setting up an MCP Server, take a moment to think about how you're managing credentials — not just whether the server works.

Thank you for reading this far!