MCP is everywhere right now. Every AI coding tool — Claude Code, Cursor, Windsurf, Copilot — speaks the Model Context Protocol. Agents can query databases, create pull requests, deploy code. All through standardized tool calls. It's a plugin system for AI, and it's moving fast.
But here's what nobody's talking about: agents need secrets to do most of that. Database credentials. API tokens. Deployment keys. And the way most MCP servers handle those secrets? They don't. They dump them in plaintext config files and call it a day.
We think that's a problem worth solving.
Why secrets and MCP don't mix well by default
The typical MCP setup looks like this. You configure a server in your config file — claude_desktop_config.json, .cursor/mcp.json, or .claude/settings.json — and pass credentials as arguments or environment variables:
{
"mcpServers": {
"database": {
"command": "npx",
"args": [
"@modelcontextprotocol/server-postgres",
"postgresql://admin:s3cretP@ss@db.example.com/prod"
]
}
}
}
That connection string — password included — sits in a JSON file on disk. The agent reads the config. The MCP server process inherits it. If anything logs the connection for debugging, the password is in the conversation.
It gets worse. When an agent calls an MCP tool, the parameters live in the conversation context. An agent constructing an API request might include an Authorization header in the tool call. That header — raw token and all — gets logged, stored in history, and potentially sent to the AI provider's servers.
MCP itself doesn't solve this. The protocol defines how tools are discovered and called. It says nothing about how secrets should flow into those tools. That's left to the implementation. And most implementations just hardcode credentials in config files.
What we built: an MCP server for NoxKey
We hit this problem building our own agent workflows. We had secrets in the macOS Keychain, protected by Touch ID. We had the CLI with encrypted handoff. But when agents used MCP tools that needed credentials, everything bypassed all of that and landed in plaintext config files.
So we built an MCP server for NoxKey. It exposes secrets management as MCP tools — listing, peeking, getting, setting, authorizing — with the same security model as the CLI. Raw values never appear in the conversation.
Configuration
Add the NoxKey MCP server to your tool's config. For Claude Code, that's .claude/settings.json:
{
"mcpServers": {
"noxkey": {
"command": "noxkey",
"args": ["mcp"]
}
}
}
That's it. No credentials in the config. No tokens, no passwords, no connection strings. The MCP server talks to the NoxKey menu bar app over a local Unix socket, which handles Keychain access and Touch ID. This file is safe to commit.
For Cursor, same structure in .cursor/mcp.json. For Claude Desktop, claude_desktop_config.json. The server binary is the same noxkey CLI you already have installed.
The tools
The MCP server exposes tools that mirror the CLI — with the same agent-aware restrictions.
List secrets
The agent discovers what secrets exist without seeing any values:
// Agent calls: noxkey_list with prefix "noboxdev/gitpulse"
// Returns:
[
{ "name": "noboxdev/gitpulse/DATABASE_URL", "type": "password" },
{ "name": "noboxdev/gitpulse/OAUTH_CLIENT_SECRET", "type": "api_key" },
{ "name": "noboxdev/gitpulse/SESSION_SECRET", "type": "secret" }
]
Names and metadata only. The agent figures out which secrets are available — useful when deciding what to load for a deployment — without any values entering the conversation.
Peek at secrets
Sometimes the agent needs to verify it has the right key. Peek shows the first 8 characters:
// Agent calls: noxkey_peek with account "noboxdev/gitpulse/DATABASE_URL"
// Returns:
{ "peek": "postgres" }
Enough to confirm "yes, that's the Postgres connection string" without exposing the password, host, or database name. Safe for verification. Useless for exfiltration.
Get a secret
This is where encrypted handoff comes in. When the agent calls noxkey_get, it doesn't receive the secret value. It gets a file path:
// Agent calls: noxkey_get with account "noboxdev/gitpulse/DATABASE_URL"
// Returns:
{ "path": "/tmp/noxkey_a8f3c1.sh" }
// Agent then runs in bash:
source /tmp/noxkey_a8f3c1.sh
// $DATABASE_URL is now in the shell environment
// The script deleted itself immediately after sourcing
The raw value never appears in the MCP response. Never enters the conversation context. The agent gets a path to a self-deleting script that loads the value into the environment. Subprocesses — npm run deploy, curl, database CLIs — can access $DATABASE_URL. The agent itself only ever saw a file path.
Touch ID is required. The NoxKey menu bar app prompts for your fingerprint before releasing anything. You approve each access physically.
Batch get
Need all secrets for a project at once? One tool call, one Touch ID prompt:
// Agent calls: noxkey_batch_get with prefix "noboxdev/gitpulse"
// Returns a single script path
// Source it to load all secrets under that prefix at once:
source /tmp/noxkey_batch_d4e2f1.sh
// $DATABASE_URL, $OAUTH_CLIENT_SECRET, $SESSION_SECRET — all loaded
Useful for deployments where the agent needs five or six credentials. One fingerprint instead of six.
Set a secret
Agents can store new secrets too. When an API returns a token, the agent saves it directly to the Keychain:
// Agent calls: noxkey_set with:
// account: "noboxdev/gitpulse/NEW_API_TOKEN"
// value: "tok_abc123..."
// type: "api_key"
// desc: "Generated by Cloudflare API"
The value goes straight into the Keychain. It doesn't persist in the conversation — the tool call happens, the MCP server stores it, done. Touch ID is required unless the prefix is session-unlocked.
Authorize: scoped tokens for unattended work
This is the tool that makes long-running agent workflows practical. The agent requests a pre-authorized token scoped to a specific prefix:
// Agent calls: noxkey_authorize with:
// name: "deploy-agent"
// prefix: "noboxdev/gitpulse"
// timeout: 3600 // 1 hour
// You get a Touch ID prompt:
// "deploy-agent wants access to noboxdev/gitpulse/* for 1 hour"
// Approve with your fingerprint
// For the next hour, the agent can get secrets
// under noboxdev/gitpulse/ without additional Touch ID prompts
The token is scoped — it can only access secrets under the prefix you approved. It's time-limited: 1 hour, 24 hours, up to 72 hours max. Revoke it at any time:
// Agent calls: noxkey_revoke with name "deploy-agent"
// Token is immediately invalidated
You can also list active tokens:
// Agent calls: noxkey_tokens
// Returns:
[
{ "name": "deploy-agent", "prefix": "noboxdev/gitpulse", "remaining": "47m" }
]
This solves the "I need to deploy but I don't want to touch my fingerprint sensor every 30 seconds" problem. Approve once, scoped narrowly, with a clear expiration.
How auto-registration works
When you add the NoxKey MCP server to your config, tool discovery happens automatically. The agent starts a session, the MCP server announces its tools, and everything's available immediately. No API keys to register. No OAuth flow. No webhook.
Authentication is physical — Touch ID on your Mac — not token-based. There's nothing to configure in the MCP config because there's no credential. The NoxKey menu bar app is already running, already connected to the Keychain, already listening on its Unix socket. The MCP server just bridges tool calls to that existing infrastructure.
Most MCP servers need something in the config: a connection string, a bearer token, an API key. NoxKey's needs nothing because authentication happens out of band — through your fingerprint on the hardware sensor.
Using secrets with other MCP servers
The real payoff isn't just managing secrets through MCP. It's using NoxKey to feed credentials to other MCP servers — without hardcoding anything.
Instead of putting a database password in your Postgres MCP server config:
// DON'T do this — credential in plaintext config
{
"mcpServers": {
"database": {
"command": "npx",
"args": ["@modelcontextprotocol/server-postgres",
"postgresql://admin:s3cretP@ss@db.example.com/prod"]
}
}
}
Load the credential from NoxKey first, then start the server with the environment variable:
// In the agent's workflow:
// 1. Load the credential via NoxKey MCP tool
source /tmp/noxkey_abc123.sh // $DATABASE_URL is now set
// 2. The Postgres MCP server reads from the environment
// Configure it to use $DATABASE_URL instead of a hardcoded string
The config file stays clean. The credential flows through the Keychain, through encrypted handoff, into the environment — never through the conversation, never into a file on disk.
What MCP can't protect against
We want to be straight about the limitations. MCP is a transport layer. It defines how tools are called and how results are returned. It can't prevent every way a secret might leak.
Tool results live in the conversation. If an MCP tool returns a value that contains a secret — say, a database query that includes a connection string — that value is in the agent's context. NoxKey's MCP tools never return raw secret values. Third-party MCP servers might. You're trusting each server to handle sensitive data responsibly.
Agents can still run shell commands. An agent with bash access can run echo $DATABASE_URL after the handoff. The value is in the environment — the agent just has to ask for it. Encrypted handoff prevents exposure during delivery. The DLP guard catches exposure after. Neither is a hard barrier.
MCP config files can be read. If you do put credentials in an MCP config file, the agent can read that file. It's in the project directory. The credential is a string in a JSON file. Same problem as .env files, different format.
Scoped tokens are still bearer tokens. An authorized NoxKey token grants access to all secrets under a prefix for a time window. If the agent's session is compromised during that window, the attacker gets the same access. Scope and time limits contain the blast radius. They don't eliminate it.
The honest take: NoxKey's MCP server makes the default path safe. Secrets don't sit in config files. Raw values don't flow through tool calls. Touch ID gates every access. But a sufficiently motivated attacker — or a careless prompt — can still surface a secret after it's in the environment. We make accidental exposure impossible and deliberate exposure hard. Not an impenetrable vault.
Frequently asked questions
Does the NoxKey MCP server work with Claude Code, Cursor, and other tools?
It works with anything that supports the Model Context Protocol. Claude Code, Cursor, Windsurf, Claude Desktop — any MCP-compatible client. The config format varies slightly between tools, but the server is the same noxkey mcp command everywhere. Tools are discovered automatically when the server starts.
Do I need the NoxKey menu bar app running?
Yes. The MCP server is a thin bridge between tool calls and the NoxKey menu bar app. The app handles Keychain access, Touch ID, encryption, and session management. If it's not running, tool calls return an error. It lives in the menu bar — always there once installed.
Can I restrict which secrets an agent can access through MCP?
Yes, two ways. First, noxkey_authorize creates scoped tokens — the agent only gets access to secrets under the prefix you approve. Second, process-tree detection automatically identifies agent callers and blocks dangerous flags (--raw, --copy, bulk export). You can also mark individual secrets as strict with noxkey strict, which forces Touch ID for every access regardless of sessions or tokens.
What happens if an MCP tool call fails or times out?
The server returns a structured error. Touch ID timeout? "Authentication timed out." App not running? "NoxKey server not reachable." Secret doesn't exist? "Key not found." All errors are descriptive. The agent can report the issue and suggest next steps — no secret data leaks through error messages.
How is this different from putting secrets in MCP server environment variables?
When you set secrets as environment variables in your MCP config, they're in a plaintext JSON file. The agent can read it. The values sit on disk forever. No access logging, no expiration, no approval step. With NoxKey's MCP server, secrets live in the Keychain (hardware-encrypted), every access requires Touch ID or a scoped token, raw values never appear in tool responses, and the config file contains zero credentials. The difference: "secret in a file anyone can read" vs. "secret behind a fingerprint sensor with an audit trail."
noxkey_get returns file paths (not values), noxkey_authorize creates scoped time-limited tokens for unattended work, and the whole thing requires zero credentials in your config. Same encrypted handoff and process-tree detection as the CLI, exposed as MCP tools. Set it up in one line and get started with NoxKey.