Securely inject MCP API credentials from system keyring into Claude Code at session start
MCP Keyring Injector
Session-scoped credential management for Claude Code MCP servers. API keys are automatically injected from your OS keyring at startup and removed at exit - credentials exist in config files only while Claude Code runs.
The Problem
MCP servers in Claude Code require API keys in their environment configuration (~/.claude.json). Storing API keys in config files is a security risk:
- Keys are in plaintext on disk
- Easy to accidentally commit to git
- Shared across all projects
- Hard to rotate without editing configs
The Solution
This plugin solves the problem by:
- Storing API keys in your system keyring (encrypted)
- Dynamically injecting them into
~/.claude.jsonwhen Claude Code starts (SessionStart hook) - Automatically removing them when the session ends (SessionEnd hook)
- Working cross-platform (Linux, macOS, Windows)
Keys exist in config files only while Claude Code is running - true session-scoped security!
Features
- Secure: Keys stored encrypted in system keyring
- Cross-platform: Works on Linux (GNOME Keyring), macOS (Keychain), Windows (Credential Manager)
- Automatic: Runs on every Claude Code session start
- Flexible: Configure multiple MCP services from one file
- Minimal overhead: Negligible performance impact on session startup
Installation
1. Install the plugin
# Via Claude Code plugin system
/plugin marketplace add astrogilda/claude-plugins
/plugin install mcp-keyring-injector
# Or manually
git clone https://github.com/astrogilda/mcp-keyring-injector.git ~/Documents/mcp-keyring-injector
2. Install Python dependencies
uv pip install keyring
3. Store your first API key
Choose your platform:
Linux (GNOME Keyring):
python3 -c "import keyring; keyring.set_password('github', 'api-key', 'YOUR_API_KEY')"
macOS (Keychain):
security add-generic-password -s github -a api-key -w YOUR_API_KEY
Windows (PowerShell):
cmdkey /generic:github /user:api-key /pass:YOUR_API_KEY
4. Configure MCP credentials
Create ~/.claude/config/mcp-credentials.json:
{
"github": {
"env_var": "GITHUB_TOKEN",
"service": "github",
"account": "api-key",
"label": "GitHub API Token",
"mcp_server": "github-mcp"
}
}
See examples/mcp-credentials.json for more examples.
5. Verify it works
Restart Claude Code. You should see:
MCP credentials - Injected: GitHub API Token
Check your GitHub MCP tools are now available!
Upgrading from v1.0.0
If you're upgrading from v1.0.0, here's what changed in v1.1.0:
What's New
- SessionEnd cleanup hook: Credentials are now automatically removed when Claude Code exits
- Session-scoped security: Keys only exist in
~/.claude.jsonwhile Claude is running - Enhanced status messages: Better feedback on cleanup operations
Migration Steps
-
Update the plugin (automatic if using plugin marketplace):
/plugin update mcp-keyring-injector -
One-time cleanup - Remove old credentials from config (they won't auto-cleanup):
# Backup first cp ~/.claude.json ~/.claude.json.backup # Remove credentials manually from ~/.claude.json env sections # Or let them be removed on next session start -
No config changes needed - Your
mcp-credentials.jsonformat remains the same
Breaking Changes
- None! This is a fully backwards-compatible security enhancement
What Happens After Upgrade
- Next session: Credentials injected at start (as before)
- New behavior: Credentials automatically removed at session end
- Keys always remain safe in your system keyring
Configuration
Config File Format
~/.claude/config/mcp-credentials.json:
{
"service-name": {
"env_var": "ENVIRONMENT_VARIABLE_NAME",
"service": "keyring-service-name",
"account": "keyring-account-name",
"label": "Human Readable Label",
"mcp_server": "mcp-server-name-in-claude.json"
}
}
Fields:
env_var: Environment variable the MCP server expects (e.g.,GITHUB_TOKEN)service: Keyring service name (how you stored the key)account: Keyring account/username (how you stored the key)label: Display name in status messagesmcp_server: MCP server name from~/.claude.json(defaults to service-name if omitted)
Adding Multiple Services
{
"github": {
"env_var": "GITHUB_TOKEN",
"service": "github",
"account": "api-key",
"label": "GitHub API Token",
"mcp_server": "github-mcp"
},
"openai": {
"env_var": "OPENAI_API_KEY",
"service": "openai",
"account": "api-key",
"label": "OpenAI API Key",
"mcp_server": "openai-mcp"
}
}
How It Works
Claude Code Session Start
|
SessionStart hook triggered
|
Read ~/.claude/config/mcp-credentials.json
|
For each service:
|-- Retrieve API key from system keyring
|-- Inject into ~/.claude.json MCP server env
+-- Report success/failure
|
Claude Code continues startup
|
MCP servers start with injected credentials
|
[... Claude Code session runs ...]
|
Claude Code Session End
|
SessionEnd hook triggered
|
Read ~/.claude/config/mcp-credentials.json
|
For each service:
|-- Remove API key from ~/.claude.json MCP server env
+-- Report cleanup status
|
Keys removed from config
|
Keys remain only in system keyring
Security Considerations
What This Protects Against
- [YES] API keys in plaintext config files
- [YES] Accidental git commits of secrets
- [YES] Unauthorized file access (keyring requires user authentication)
- [YES] Session-scoped security (v1.1.0+): Keys automatically removed when Claude Code exits
What This Doesn't Protect Against
- [NO] Malicious code with your user permissions (can access keyring)
- [NO] Physical access to unlocked machine
- [NO] Memory dumps while Claude Code is running
Best Practices
- Use unique API keys per machine
- Rotate keys regularly
- Use least-privilege API keys (read-only when possible)
- Don't share
~/.claude/config/mcp-credentials.json(it's not secret, but lists your services)
Troubleshooting
"MCP credentials - Failed: X (not in keyring)"
The key isn't stored in your system keyring.
Solution:
python3 -c "import keyring; keyring.set_password('service', 'account', 'YOUR_KEY')"
"MCP credentials - Failed: X (MCP server 'Y' not found)"
The MCP server name doesn't match what's in ~/.claude.json.
Solution: Check your MCP server name:
jq '.mcpServers | keys' ~/.claude.json
Update mcp_server field in config to match.
"ERROR: keyring library not installed"
Python keyring library is missing.
Solution:
uv pip install keyring
Hook runs but tools still unavailable
Check MCP server logs:
ls ~/.cache/claude-cli-nodejs/*/mcp-logs-*/
cat ~/.cache/claude-cli-nodejs/*/mcp-logs-github-mcp/*.txt
Look for errors about missing API keys.
Development
Project Structure
mcp-keyring-injector/
.claude-plugin/
+-- plugin.json # Plugin metadata
hooks/
+-- hooks.json # Hook configuration
+-- inject-credentials.py # SessionStart hook
+-- remove-credentials.py # SessionEnd hook (v1.1.0+)
examples/
+-- mcp-credentials.json # Example config
docs/
+-- SECURITY.md # Security considerations
CHANGELOG.md # Version history
README.md
LICENSE
Testing Locally
# Test the hook directly
echo '{}' | ~/Documents/claude-mcp-credentials/hooks/inject-credentials.py
# Check if keys were injected
jq '.mcpServers."github-mcp".env' ~/.claude.json
Contributing
Contributions welcome! Please:
- Fork the repo
- Create a feature branch
- Add tests for new functionality
- Ensure code passes black + ruff
- Submit PR with clear description
License
MIT License - see LICENSE file for details.
Credits
Created by @astrogilda
Uses the excellent keyring library by Jason R. Coombs.