mcp-devutils 1.6.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -6
- package/index.js +148 -5
- package/package.json +10 -3
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# mcp-devutils
|
|
2
2
|
|
|
3
|
-
MCP server with **
|
|
3
|
+
MCP server with **44 developer utilities** for Claude Desktop, Cursor, and any MCP-compatible AI assistant.
|
|
4
|
+
|
|
5
|
+
**15 tools free** — unlock all 44 with a Pro license key.
|
|
4
6
|
|
|
5
7
|
## Install
|
|
6
8
|
|
|
@@ -15,7 +17,27 @@ MCP server with **39 developer utilities** for Claude Desktop, Cursor, and any M
|
|
|
15
17
|
}
|
|
16
18
|
```
|
|
17
19
|
|
|
18
|
-
##
|
|
20
|
+
## Unlock Pro (29 extra tools)
|
|
21
|
+
|
|
22
|
+
1. Support development at [buymeacoffee.com/gl89tu25lp](https://buymeacoffee.com/gl89tu25lp)
|
|
23
|
+
2. Get your license key in the confirmation
|
|
24
|
+
3. Add it to your MCP config:
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"mcpServers": {
|
|
29
|
+
"devutils": {
|
|
30
|
+
"command": "npx",
|
|
31
|
+
"args": ["-y", "mcp-devutils"],
|
|
32
|
+
"env": {
|
|
33
|
+
"MCP_DEVUTILS_KEY": "your-key-here"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Free Tools (15)
|
|
19
41
|
|
|
20
42
|
| Tool | Description |
|
|
21
43
|
|------|-------------|
|
|
@@ -31,10 +53,15 @@ MCP server with **39 developer utilities** for Claude Desktop, Cursor, and any M
|
|
|
31
53
|
| `cron_explain` | Explain cron expressions + next run times |
|
|
32
54
|
| `hmac` | Generate HMAC signatures |
|
|
33
55
|
| `color_convert` | Convert hex ↔ RGB ↔ HSL colors |
|
|
34
|
-
| `semver_compare` | Compare semantic versions |
|
|
35
56
|
| `http_status` | Look up HTTP status code meanings |
|
|
36
57
|
| `slug` | Generate URL-safe slugs |
|
|
37
58
|
| `escape_html` | Escape/unescape HTML entities |
|
|
59
|
+
|
|
60
|
+
## Pro Tools (29)
|
|
61
|
+
|
|
62
|
+
| Tool | Description |
|
|
63
|
+
|------|-------------|
|
|
64
|
+
| `semver_compare` | Compare semantic versions |
|
|
38
65
|
| `chmod_calc` | Convert numeric ↔ symbolic permissions |
|
|
39
66
|
| `diff` | Compare two text strings |
|
|
40
67
|
| `number_base` | Convert decimal/hex/octal/binary |
|
|
@@ -58,6 +85,11 @@ MCP server with **39 developer utilities** for Claude Desktop, Cursor, and any M
|
|
|
58
85
|
| `sql_format` | Format SQL queries with proper indentation |
|
|
59
86
|
| `json_query` | Extract values from JSON using dot-notation paths |
|
|
60
87
|
| `epoch_convert` | Convert epoch timestamps across multiple timezones |
|
|
88
|
+
| `aes_encrypt` | AES-256-CBC encrypt text with any key |
|
|
89
|
+
| `aes_decrypt` | Decrypt AES-256-CBC encrypted text |
|
|
90
|
+
| `rsa_keygen` | Generate RSA key pairs (1024/2048/4096-bit) |
|
|
91
|
+
| `scrypt_hash` | Hash passwords with scrypt (RFC 7914) |
|
|
92
|
+
| `regex_replace` | Find & replace with regex + capture groups |
|
|
61
93
|
|
|
62
94
|
## Zero dependencies
|
|
63
95
|
|
|
@@ -65,9 +97,7 @@ Only requires `@modelcontextprotocol/sdk`. All tools use Node.js built-ins.
|
|
|
65
97
|
|
|
66
98
|
## Support
|
|
67
99
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
- [Buy me a coffee](https://buymeacoffee.com/gl89tu25lp)
|
|
100
|
+
- [Buy me a coffee](https://buymeacoffee.com/gl89tu25lp) — unlocks Pro tools!
|
|
71
101
|
- [Tip via Stripe ($3)](https://buy.stripe.com/dRm8wP8R295Z9VyeN59Zm00)
|
|
72
102
|
|
|
73
103
|
## License
|
package/index.js
CHANGED
|
@@ -4,14 +4,33 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
4
4
|
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
5
5
|
import crypto from "crypto";
|
|
6
6
|
|
|
7
|
+
// --- Freemium gating ---
|
|
8
|
+
const PRO_KEY = process.env.MCP_DEVUTILS_KEY || "";
|
|
9
|
+
const isProUnlocked = PRO_KEY.length >= 16;
|
|
10
|
+
|
|
11
|
+
const FREE_TOOLS = new Set([
|
|
12
|
+
"uuid", "hash", "base64", "timestamp", "jwt_decode",
|
|
13
|
+
"random_string", "url_encode", "json_format", "regex_test",
|
|
14
|
+
"cron_explain", "hmac", "color_convert", "http_status",
|
|
15
|
+
"slug", "escape_html"
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
const UPGRADE_MSG = `🔒 This is a PRO tool. Upgrade to unlock 29 additional developer tools!
|
|
19
|
+
|
|
20
|
+
Get your license key: https://buymeacoffee.com/gl89tu25lp
|
|
21
|
+
|
|
22
|
+
After purchase, set your key:
|
|
23
|
+
export MCP_DEVUTILS_KEY="your-key-here"
|
|
24
|
+
|
|
25
|
+
Then restart your MCP client. All 44 tools will be unlocked.`;
|
|
26
|
+
|
|
7
27
|
const server = new Server(
|
|
8
|
-
{ name: "mcp-devutils", version: "
|
|
28
|
+
{ name: "mcp-devutils", version: "2.0.0" },
|
|
9
29
|
{ capabilities: { tools: {} } }
|
|
10
30
|
);
|
|
11
31
|
|
|
12
32
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
13
|
-
|
|
14
|
-
tools: [
|
|
33
|
+
const allTools = [
|
|
15
34
|
{
|
|
16
35
|
name: "uuid",
|
|
17
36
|
description: "Generate a UUID v4",
|
|
@@ -524,14 +543,90 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
524
543
|
timezone: { type: "string", description: "Additional IANA timezone to show (e.g. 'Asia/Tokyo')" }
|
|
525
544
|
}
|
|
526
545
|
}
|
|
546
|
+
},
|
|
547
|
+
{
|
|
548
|
+
name: "aes_encrypt",
|
|
549
|
+
description: "Encrypt text using AES-256-CBC. Returns hex-encoded IV + ciphertext. Use a strong key (will be hashed to 256 bits internally).",
|
|
550
|
+
inputSchema: {
|
|
551
|
+
type: "object",
|
|
552
|
+
properties: {
|
|
553
|
+
text: { type: "string", description: "Plaintext to encrypt" },
|
|
554
|
+
key: { type: "string", description: "Encryption key (any string — will be SHA-256 hashed to derive 256-bit key)" }
|
|
555
|
+
},
|
|
556
|
+
required: ["text", "key"]
|
|
557
|
+
}
|
|
558
|
+
},
|
|
559
|
+
{
|
|
560
|
+
name: "aes_decrypt",
|
|
561
|
+
description: "Decrypt AES-256-CBC encrypted text. Expects hex-encoded input from aes_encrypt.",
|
|
562
|
+
inputSchema: {
|
|
563
|
+
type: "object",
|
|
564
|
+
properties: {
|
|
565
|
+
encrypted: { type: "string", description: "Hex-encoded string (IV + ciphertext) from aes_encrypt" },
|
|
566
|
+
key: { type: "string", description: "Same key used for encryption" }
|
|
567
|
+
},
|
|
568
|
+
required: ["encrypted", "key"]
|
|
569
|
+
}
|
|
570
|
+
},
|
|
571
|
+
{
|
|
572
|
+
name: "rsa_keygen",
|
|
573
|
+
description: "Generate an RSA key pair (PEM format). Useful for testing, dev environments, and learning.",
|
|
574
|
+
inputSchema: {
|
|
575
|
+
type: "object",
|
|
576
|
+
properties: {
|
|
577
|
+
bits: { type: "number", description: "Key size in bits: 1024, 2048, or 4096 (default: 2048)" }
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
},
|
|
581
|
+
{
|
|
582
|
+
name: "scrypt_hash",
|
|
583
|
+
description: "Hash a password using Node.js scrypt (RFC 7914). Returns hex-encoded salt + hash for secure password storage.",
|
|
584
|
+
inputSchema: {
|
|
585
|
+
type: "object",
|
|
586
|
+
properties: {
|
|
587
|
+
password: { type: "string", description: "Password to hash" },
|
|
588
|
+
salt: { type: "string", description: "Optional salt (hex). If omitted, a random 16-byte salt is generated." }
|
|
589
|
+
},
|
|
590
|
+
required: ["password"]
|
|
591
|
+
}
|
|
592
|
+
},
|
|
593
|
+
{
|
|
594
|
+
name: "regex_replace",
|
|
595
|
+
description: "Find and replace text using a regular expression. Supports capture groups ($1, $2, etc.) in the replacement string.",
|
|
596
|
+
inputSchema: {
|
|
597
|
+
type: "object",
|
|
598
|
+
properties: {
|
|
599
|
+
text: { type: "string", description: "Input text" },
|
|
600
|
+
pattern: { type: "string", description: "Regular expression pattern" },
|
|
601
|
+
replacement: { type: "string", description: "Replacement string (use $1, $2 for capture groups)" },
|
|
602
|
+
flags: { type: "string", description: "Regex flags (default: 'g'). Common: 'gi' for global case-insensitive." }
|
|
603
|
+
},
|
|
604
|
+
required: ["text", "pattern", "replacement"]
|
|
605
|
+
}
|
|
527
606
|
}
|
|
528
|
-
|
|
529
|
-
|
|
607
|
+
];
|
|
608
|
+
|
|
609
|
+
// Label pro tools when not unlocked
|
|
610
|
+
const tools = allTools.map(tool => {
|
|
611
|
+
if (!FREE_TOOLS.has(tool.name) && !isProUnlocked) {
|
|
612
|
+
return { ...tool, description: `[PRO] ${tool.description}` };
|
|
613
|
+
}
|
|
614
|
+
return tool;
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
return { tools };
|
|
530
618
|
});
|
|
531
619
|
|
|
532
620
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
533
621
|
const { name, arguments: args } = request.params;
|
|
534
622
|
|
|
623
|
+
// Gate pro tools
|
|
624
|
+
if (!FREE_TOOLS.has(name) && !isProUnlocked) {
|
|
625
|
+
return {
|
|
626
|
+
content: [{ type: "text", text: UPGRADE_MSG }]
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
|
|
535
630
|
try {
|
|
536
631
|
switch (name) {
|
|
537
632
|
case "uuid": {
|
|
@@ -1618,6 +1713,54 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1618
1713
|
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
1619
1714
|
}
|
|
1620
1715
|
|
|
1716
|
+
case "aes_encrypt": {
|
|
1717
|
+
const keyHash = crypto.createHash("sha256").update(args.key).digest();
|
|
1718
|
+
const iv = crypto.randomBytes(16);
|
|
1719
|
+
const cipher = crypto.createCipheriv("aes-256-cbc", keyHash, iv);
|
|
1720
|
+
let encrypted = cipher.update(args.text, "utf8", "hex");
|
|
1721
|
+
encrypted += cipher.final("hex");
|
|
1722
|
+
const result = iv.toString("hex") + encrypted;
|
|
1723
|
+
return { content: [{ type: "text", text: `Encrypted (hex): ${result}\n\nIV (first 32 hex chars): ${iv.toString("hex")}\nCiphertext: ${encrypted}\nTotal length: ${result.length} hex chars` }] };
|
|
1724
|
+
}
|
|
1725
|
+
|
|
1726
|
+
case "aes_decrypt": {
|
|
1727
|
+
const keyHash = crypto.createHash("sha256").update(args.key).digest();
|
|
1728
|
+
const encHex = args.encrypted;
|
|
1729
|
+
if (encHex.length < 34) throw new Error("Encrypted string too short — must contain 32-char IV + ciphertext");
|
|
1730
|
+
const iv = Buffer.from(encHex.slice(0, 32), "hex");
|
|
1731
|
+
const ciphertext = encHex.slice(32);
|
|
1732
|
+
const decipher = crypto.createDecipheriv("aes-256-cbc", keyHash, iv);
|
|
1733
|
+
let decrypted = decipher.update(ciphertext, "hex", "utf8");
|
|
1734
|
+
decrypted += decipher.final("utf8");
|
|
1735
|
+
return { content: [{ type: "text", text: decrypted }] };
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1738
|
+
case "rsa_keygen": {
|
|
1739
|
+
const bits = [1024, 2048, 4096].includes(args?.bits) ? args.bits : 2048;
|
|
1740
|
+
const { publicKey, privateKey } = crypto.generateKeyPairSync("rsa", {
|
|
1741
|
+
modulusLength: bits,
|
|
1742
|
+
publicKeyEncoding: { type: "spki", format: "pem" },
|
|
1743
|
+
privateKeyEncoding: { type: "pkcs8", format: "pem" }
|
|
1744
|
+
});
|
|
1745
|
+
return { content: [{ type: "text", text: `=== RSA ${bits}-bit Key Pair ===\n\n--- Public Key ---\n${publicKey}\n--- Private Key ---\n${privateKey}\n⚠️ This is for dev/testing. Never share private keys.` }] };
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1748
|
+
case "scrypt_hash": {
|
|
1749
|
+
const salt = args.salt ? Buffer.from(args.salt, "hex") : crypto.randomBytes(16);
|
|
1750
|
+
const derived = crypto.scryptSync(args.password, salt, 64);
|
|
1751
|
+
const saltHex = salt.toString("hex");
|
|
1752
|
+
const hashHex = derived.toString("hex");
|
|
1753
|
+
return { content: [{ type: "text", text: `Salt (hex): ${saltHex}\nHash (hex): ${hashHex}\nCombined: ${saltHex}:${hashHex}\n\nTo verify, use the same salt with scrypt_hash.` }] };
|
|
1754
|
+
}
|
|
1755
|
+
|
|
1756
|
+
case "regex_replace": {
|
|
1757
|
+
const flags = args.flags || "g";
|
|
1758
|
+
const regex = new RegExp(args.pattern, flags);
|
|
1759
|
+
const result = args.text.replace(regex, args.replacement);
|
|
1760
|
+
const matchCount = (args.text.match(regex) || []).length;
|
|
1761
|
+
return { content: [{ type: "text", text: `Matches found: ${matchCount}\n\n--- Result ---\n${result}` }] };
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1621
1764
|
default:
|
|
1622
1765
|
throw new Error(`Unknown tool: ${name}`);
|
|
1623
1766
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-devutils",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "MCP server with
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "MCP server with 44 developer utilities (15 free + 29 pro). Free: UUID, hash, HMAC, base64, timestamps, JWT decode, random strings, URL encode/decode, JSON format, regex test, cron explain, color convert, HTTP status, slugify, HTML escape. Pro: nanoid, hex encode, JWT create, JSON diff/query, CSV/JSON convert, regex replace, semver compare, chmod calc, text diff, number base, lorem ipsum, word/byte count, CIDR calc, case convert, markdown TOC, env parser, IP info, password strength, data size, string escape, char info, SQL format, epoch convert, AES encrypt/decrypt, RSA keygen, scrypt hash",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"bin": {
|
|
@@ -52,7 +52,14 @@
|
|
|
52
52
|
"sql-format",
|
|
53
53
|
"json-query",
|
|
54
54
|
"epoch-convert",
|
|
55
|
-
"timezone"
|
|
55
|
+
"timezone",
|
|
56
|
+
"aes",
|
|
57
|
+
"encryption",
|
|
58
|
+
"rsa",
|
|
59
|
+
"keygen",
|
|
60
|
+
"scrypt",
|
|
61
|
+
"password-hash",
|
|
62
|
+
"regex-replace"
|
|
56
63
|
],
|
|
57
64
|
"author": "Hong Teoh",
|
|
58
65
|
"license": "MIT",
|