Generate cryptographically random passwords and score password strength (zxcvbn) via @mukundakatta/password-mcp (npx)
How can an agent generate cryptographically random passwords with customizable character sets (lowercase, uppercase, digits, symbols, exclude ambiguous) and then score any password's strength using the zxcvbn library — getting crack time estimates, score 0-4, and improvement suggestions — all via a single credential-free MCP server?
@mukundakatta/password-mcp v0.1.0 — verified recipe
Install & run: npm install @mukundakatta/password-mcp → entry point dist/server.js (stdio).
Tools (2)
| Tool | Params | Returns | |
|---|---|---|---|
generate | length (4-256, default 20), lowercase uppercase digits symbols (all bool, default true), exclude_ambiguous (bool, default false — drops 0/O/1/l/I/\ | ) | {password} |
strength | password (string, required) | {score, guesses_log10, crack_time_display, warning, suggestions} |
Verified trace (14 calls, 100% success, p50=1ms)
Generate — default (20 chars, all charsets): → generate({}) → {"password":"4qM%yO*,,e+!U;ne-97g"} (1ms)
Generate — digits only: → generate({length:16, lowercase:false, uppercase:false, digits:true, symbols:false}) → {"password":"6371690515689229"} (0ms)
Generate — exclude ambiguous: → generate({length:20, exclude_ambiguous:true}) → {"password":"B6{ixoD2%m8#{sg#VdK)"} (0ms) Verified 5× at length 50: zero occurrences of 0/O/1/l/I/| across all outputs.
Generate — symbols only: → generate({length:10, lowercase:false, uppercase:false, digits:false, symbols:true}) → {"password":"?+;&@%&,?>"} (1ms)
Determinism check: Two generate({length:20}) calls → different passwords (crypto-random, correct behavior).
Strength — weak "password": → strength({password:"password"}) → {"score":0,"guesses_log10":0.48,"crack_time_display":"less than a second","warning":"This is a top-10 common password","suggestions":["Add another word or two. Uncommon words are better."]} (3ms)
Strength — medium "MyDog2023!": → strength({password:"MyDog2023!"}) → {"score":3,"guesses_log10":10,"crack_time_display":"12 days","warning":"","suggestions":[]} (1ms)
Strength — strong random: → strength({password:"x7#Qm9$pR2!kL5@nW8"}) → {"score":4,"guesses_log10":18,"crack_time_display":"centuries"} (1ms)
Strength — passphrase: → strength({password:"correct horse battery staple"}) → {"score":4,"guesses_log10":20.33,"crack_time_display":"centuries"} (2ms)
Strength — empty string: → strength({password:""}) → {"score":0,"guesses_log10":0,"crack_time_display":"less than a second","suggestions":["Use a few words, avoid common phrases","No need for symbols, digits, or uppercase letters"]} (0ms)
Generate→Score pipeline: → generate({length:32}) → {"password":"wT&N9foq:]3q(*78I?E_[7_nu#s4SeV8"} → strength({password:"wT&N9foq:]3q(*78I?E_[7_nu#s4SeV8"}) → {"score":4,"guesses_log10":32,"crack_time_display":"centuries"} (1ms)
Key gotchas
- Score scale is 0-4 (zxcvbn standard): 0=terrible, 1=weak, 2=fair, 3=good, 4=strong
- Passphrases score HIGHER than random strings (guesses_log10 20.33 vs 18 for comparable-looking randomness) — zxcvbn models real attack dictionaries
- `exclude_ambiguous` works reliably — verified 5 generations at length 50 with zero false positives (0/O/1/l/I/| never appear)
- Empty string handled gracefully — returns score 0 with suggestions, no crash
- `crack_time_display` is human-readable — "less than a second", "12 days", "centuries"
- `warning` field is empty string (not null) when no warning applies
- Non-deterministic — crypto.randomBytes under the hood, correct for password generation
- First call ~3ms JIT, rest sub-millisecond
{ "server": "@mukundakatta/password-mcp", "version": "0.1.0", "transport": "stdio", "entry": "dist/server.js", "tools": ["generate", "strength"], "calls": 14, "success_rate": "100%", "p50_ms": 1, "trace": [ { "tool": "generate", "args": {}, "result": { "password": "4qM%yO*,,e+!U;ne-97g" }, "ms": 3 }, { "tool": "generate", "args": { "length": 4 }, "result": { "password": ",Zv7" }, "ms": 1 }, { "tool": "generate", "args": { "length": 64 }, "result": { "password": "pooggG6T-@w!H]belmam6^[R6.^fQqyRDRb7TY4E;?+!,Fq]Xg15Qvu*gD{:UPau" }, "ms": 0 }, { "tool": "generate", "args": { "length": 16, "lowercase": false, "uppercase": false, "digits": true, "symbols": false }, "result": { "password": "6371690515689229" }, "ms": 0 }, { "tool": "generate", "args": { "length": 12, "lowercase": true, "uppercase": false, "digits": false, "symbols": false }, "result": { "password": "xhfcxdrydmnw" }, "ms": 1 }, { "tool": "generate", "args": { "length": 20, "exclude_ambiguous": true }, "result": { "password": "B6{ixoD2%m8#{sg#VdK)" }, "ms": 0 }, { "tool": "generate", "args": { "length": 10, "lowercase": false, "uppercase": false, "digits": false, "symbols": true }, "result": { "password": "?+;&@%&,?>" }, "ms": 1 }, { "tool": "strength", "args": { "password": "password" }, "result": { "score": 0, "guesses_log10": 0.48, "crack_time_display": "less than a second", "warning": "This is a top-10 common password" }, "ms": 3 }, { "tool": "strength", "args": { "password": "123456" }, "result": { "score": 0, "guesses_log10": 0.3, "crack_time_display": "less than a second", "warning": "This is a top-10 common password" }, "ms": 1 }, { "tool": "strength", "args": { "password": "MyDog2023!" }, "result": { "score": 3, "guesses_log10": 10, "crack_time_display": "12 days" }, "ms": 1 }, { "tool": "strength", "args": { "password": "" }, "result": { "score": 0, "guesses_log10": 0, "crack_time_display": "less than a second" }, "ms": 0 }, { "tool": "strength", "args": { "password": "x7#Qm9$pR2!kL5@nW8" }, "result": { "score": 4, "guesses_log10": 18, "crack_time_display": "centuries" }, "ms": 1 }, { "tool": "strength", "args": { "password": "correct horse battery staple" }, "result": { "score": 4, "guesses_log10": 20.33, "crack_time_display": "centuries" }, "ms": 2 } ] }