mcp-scan rule
Exposed secret detected
exposed-secretcriticalWhat mcp-scan saw
The secret scanner ran 50-plus provider regexes over every env value, the server url, and every string argument, after first trying decodeURIComponent so percent-escaped keys still match. Values that are exactly an env reference (${VAR}, $VAR, %VAR%) are skipped before the patterns run. Prefix-less formats (Pinecone, Together, Cloudflare, Heroku, Railway) only fire when the env key name itself matches a keyContext regex.
Why it fires
A literal key in a client config is a key in a file that gets synced, backed up, and screenshotted, and MCP client configs are not treated like secrets by the tools that touch them. The scanner stops at the first matching pattern per value and marks the finding fixable with 99 percent confidence, so mcp-scan fix can rewrite the value to ${KEY} in place.
When this is a false positive
- Stripe Publishable Key (pk_live_/pk_test_) is in the pattern list at CRITICAL. That key is designed to ship in browser bundles and is not a secret.
- Twilio Account SID (AC plus 32 hex) is an account identifier, not a credential, and is safe in config.
- The keyContext-gated patterns match on shape alone once the key name qualifies. An env var named RAILWAY_PROJECT_ID or HEROKU_APP_UUID holds a 36-char id or a UUID and is reported as a token.
- The Database URL with Credentials pattern matches scheme://user:pass@host, and it still matches when the password segment is an env placeholder, for example postgres://app:${PGPASS}@db.internal.
How to fix it
- Run mcp-scan fix. For this rule it rewrites the offending env value to ${KEY} in the config, leaving the key name intact.
- Export the real value in your shell profile or your secret manager under that same key name, so the server still resolves it at launch.
- Rotate the exposed credential at the provider. It has been sitting in a plaintext file, and rotation is the only step that actually closes the exposure.
- If the value is a publishable key, an account SID, or a project id, leave it and add 'exposed-secret' to suppressRules in .mcp-scan.json only if you have checked every other hit first.
- Confirm the config file itself is not tracked in git, and add it to .gitignore if it is.