safe-key-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@safe-key-mcpUse my github_token to list my repositories"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
safe-key-mcp
An MCP server for safe secret access by AI agents. Secrets are stored encrypted. AI agents see only names and descriptions, never values. All actions are executed server-side. Outputs are scrubbed before being returned.
Context — Why this project exists
Today, AI coding agents (Claude, GPT, Codex, etc.) increasingly need to interact with authenticated services: GitHub APIs, cloud providers, databases, SSH servers, email, etc.
The current solutions are problematic:
Approach | Problem |
Paste tokens in the chat | Token ends up in conversation history, logs, and LLM provider servers |
Environment variables | AI can |
| AI can |
1Password MCP | Proprietary, paid, limited to 1Password users |
The fundamental issue: every existing approach eventually exposes the secret value to the AI as plaintext text — which gets sent to inference servers, stored in logs, and leaked in conversation history.
The real threat model
The AI is not the enemy. The real risks are:
Accidental leaks in logs, debug output, error messages
Prompt injection from a malicious website extracting secrets
Conversation history readable by developers or stored on LLM servers
Verbose tool output that echoes back headers, URLs, or environment variables
We don't need military-grade sandboxing. We need secrets to never appear in the AI's context window.
Related MCP server: Janee
How it works
┌─────────────────────────────────────┐
│ safe-key-mcp server │
│ │
│ vault.enc (AES-256-GCM encrypted) │
│ ┌─────────────────────────────┐ │
│ │ github_token = ghp_xxx │ │
│ │ api_key = sk-xxxxx │ │
│ │ ssh_key_ovh = -----BEGIN… │ │
│ └─────────────────────────────┘ │
│ │
│ MCP Tools: │
│ • list_secrets() → names only │
│ • http_request(secret, url, …) │
│ • shell_exec(secret, template) │
│ • ssh_exec(secret, host, cmd) │
│ │
│ Output sanitizer: │
│ Scrubs all known secret values │
│ from every response before sending │
└──────────────┬──────────────────────┘
│
[names only ↑] [scrubbed output ↓]
│
┌──────────────┴──────────────────────┐
│ AI Agent │
│ "Use github_token to GET /repos…" │
│ │
│ Never sees: ghp_xxx │
│ Never sees: sk-xxxxx │
│ Never sees: -----BEGIN… │
└──────────────────────────────────────┘Encryption
AES-256-GCM authenticated encryption
Master password derived via PBKDF2-HMAC-SHA256 (600,000 iterations)
Each save uses a fresh random salt (16 bytes) and nonce (12 bytes)
Vault file is a single binary blob — not human-readable
Sanitizer
The output sanitizer is a defence-in-depth measure. It replaces any occurrence of a known secret value with [REDACTED] in all tool responses. This catches:
Error messages that echo back a URL containing a token
Verbose curl/HTTP output that includes headers
Shell command output that accidentally prints environment variables
It is not a security boundary against a determined attacker with shell access.
MCP Tools
Tool | What the AI sends | What the AI receives |
| nothing | names + descriptions (no values) |
|
| HTTP response with values scrubbed |
|
| stdout/stderr with values scrubbed |
|
| stdout/stderr with values scrubbed |
http_request auth styles
bearer—Authorization: Bearer <value>basic_user— HTTP Basic Auth (secret as username)basic_password— HTTP Basic Auth (secret as password)header— Custom header (specifyheader_name)query_param— URL query parameter (specifyparam_name)
shell_exec template
The command must contain exactly one {SECRET} placeholder:
git clone https://user:{SECRET}@github.com/org/repo.gitThe server replaces {SECRET} with the actual value, runs the command, and scrubs the output.
Quick start
Prerequisites
Python 3.10+
pip install -e ".[dev]"
Add secrets (CLI)
export SAFE_KEY_MASTER_PASSWORD="your-strong-password"
# Interactive (password prompt, value never shown)
python -m safe_key_mcp add github_token -d "GitHub Personal Access Token" -t "ci,github"
python -m safe_key_mcp add openai_key -d "OpenAI API key" -t "llm"
# List (values never shown)
python -m safe_key_mcp listRun the server
export SAFE_KEY_MASTER_PASSWORD="your-strong-password"
python -m safe_key_mcp serve --host 127.0.0.1 --port 8500Docker
cp .env.example .env
# Edit SAFE_KEY_MASTER_PASSWORD in .env
docker compose up -d
# Server available at http://localhost:8500/sseConnect to your AI agent
Add to your MCP client config (e.g. OpenCode, Claude Desktop):
{
"mcpServers": {
"safe-key": {
"url": "http://localhost:8500/sse"
}
}
}Project structure
src/safe_key_mcp/
├── __init__.py # Package docstring
├── __main__.py # CLI: serve / add / list / delete
├── config.py # Configuration via environment variables
├── vault.py # AES-256-GCM encrypted storage
├── sanitizer.py # Output scrubbing
└── server.py # MCP server with 4 tools
tests/
├── conftest.py
├── test_vault.py # 8 tests — encryption, persistence, auth
├── test_sanitizer.py # 6 tests — scrubbing, edge cases
└── test_server.py # 3 tests — tool integrationEnvironment variables
Variable | Default | Description |
| (required) | Master password for vault encryption |
|
| Path to the encrypted vault file |
|
| SSE server host |
|
| SSE server port |
Status
This project is under active development and has not been tested in production.
Core vault encryption: working (17/17 tests passing)
MCP tools: implemented, not yet tested with real services
Docker deployment: ready, not yet deployed
Sanitizer: functional, basic pattern matching
What's next — evolution toward a Service Gateway
During development, we realized that this "vault + secret names" approach still has a conceptual flaw: the AI knows that secrets exist. It manipulates secret names, chooses auth styles, and builds authenticated requests — it's just one abstraction layer away from the values.
The next evolution of this project will be a Service Gateway where:
The AI doesn't know secrets exist at all
It just calls services:
service_call("github", "GET", "/repos/owner/repo")Auth is resolved entirely server-side by config mapping
The AI's mental model is "use this service", not "use this secret"
This is a fundamentally different approach — closer to how a browser handles cookies than how a developer handles API keys. That work will happen in a separate repository.
License
MIT
This server cannot be deployed
Maintenance
Related MCP Connectors
MCP server connecting AI agents to 100+ apps (Gmail, Slack, Notion, GitHub) via one-click OAuth.
MCP server for secureFlows: token-free URL builders and integration-linting tools for AI agents.
MCP server for building and testing AI agents with multi-model experimentation and insights.
Zero-secret MCP gateway for AI agents: risk-scored, audited calls with human-in-the-loop approval.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceMCP server that lets AI agents call APIs without ever seeing the credentials, using a local encrypted vault and per-secret allowlist policies for HTTP requests and subprocess environment variables.1AGPL 3.0
- AlicenseNot gradedqualityCmaintenanceSecrets management MCP server that injects credentials into API requests for AI agents, enforcing policies and logging all activity without exposing raw keys.5430MIT
- AlicenseNot gradedqualityBmaintenanceEncrypted secrets management MCP server for AI agents, enabling secure storage, retrieval, rotation, and auditing of API keys and credentials with AES-128 encryption.MIT
- FlicenseNot gradedqualityBmaintenanceMCP server for end-to-end encrypted secret storage and retrieval, enabling LLM agents to fetch secrets by name via one-time URLs while keeping plaintext out of model context and logs.1-