mcp-devutils 1.3.0 → 1.4.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.
Files changed (3) hide show
  1. package/README.md +43 -216
  2. package/index.js +275 -1
  3. package/package.json +16 -3
package/README.md CHANGED
@@ -1,70 +1,8 @@
1
1
  # mcp-devutils
2
2
 
3
- An MCP (Model Context Protocol) server with developer utilities. Use it directly with Claude Desktop, Cursor, or any MCP-compatible client.
3
+ MCP server with **30 developer utilities** for Claude Desktop, Cursor, and any MCP-compatible AI assistant.
4
4
 
5
- ## Tools
6
-
7
- | Tool | Description |
8
- |------|-------------|
9
- | `uuid` | Generate UUID v4 (up to 10 at once) |
10
- | `hash` | Hash text with md5, sha1, or sha256 |
11
- | `base64` | Encode or decode base64 |
12
- | `timestamp` | Convert between Unix timestamps and ISO 8601 |
13
- | `jwt_decode` | Decode JWT token payload (no verification) |
14
- | `random_string` | Generate random strings/passwords |
15
- | `url_encode` | URL encode or decode strings |
16
- | `json_format` | Pretty-print or minify JSON |
17
- | `regex_test` | Test regex patterns against strings |
18
- | `cron_explain` | Explain cron expressions in plain English + next 5 runs |
19
- | `hmac` | Generate HMAC signatures (SHA-256, SHA-512, etc.) |
20
- | `color_convert` | Convert colors between hex, RGB, and HSL |
21
- | `semver_compare` | Compare two semantic versions |
22
- | `http_status` | Look up HTTP status code meaning and usage |
23
- | `slug` | Generate URL-safe slugs from text |
24
- | `escape_html` | Escape or unescape HTML entities |
25
- | `chmod_calc` | Convert between numeric and symbolic Unix permissions |
26
- | `diff` | Compare two texts and show differences line by line |
27
- | `number_base` | Convert numbers between decimal, hex, octal, and binary |
28
- | `lorem_ipsum` | Generate placeholder lorem ipsum text |
29
- | `word_count` | Count characters, words, lines, and bytes in text |
30
- | `cidr` | Parse CIDR notation — network, broadcast, host range |
31
- | `case_convert` | Convert between camelCase, snake_case, PascalCase, kebab-case, CONSTANT_CASE, Title Case |
32
- | `markdown_toc` | Generate table of contents from markdown headings |
33
-
34
- ## Installation
35
-
36
- ```bash
37
- npm install -g mcp-devutils
38
- ```
39
-
40
- Or use directly with `npx`:
41
-
42
- ```json
43
- {
44
- "mcpServers": {
45
- "devutils": {
46
- "command": "npx",
47
- "args": ["-y", "mcp-devutils"]
48
- }
49
- }
50
- }
51
- ```
52
-
53
- ## Usage with Claude Desktop
54
-
55
- Add to your `claude_desktop_config.json`:
56
-
57
- ```json
58
- {
59
- "mcpServers": {
60
- "devutils": {
61
- "command": "mcp-devutils"
62
- }
63
- }
64
- }
65
- ```
66
-
67
- Or with npx (no install needed):
5
+ ## Install
68
6
 
69
7
  ```json
70
8
  {
@@ -77,162 +15,51 @@ Or with npx (no install needed):
77
15
  }
78
16
  ```
79
17
 
80
- ## Usage with Cursor
18
+ ## Tools (30)
81
19
 
82
- Add to your Cursor MCP settings:
83
-
84
- ```json
85
- {
86
- "mcpServers": {
87
- "devutils": {
88
- "command": "npx",
89
- "args": ["-y", "mcp-devutils"]
90
- }
91
- }
92
- }
93
- ```
94
-
95
- ## Tool Examples
96
-
97
- ### uuid
98
- Generate one or more UUIDs:
99
- - `count`: Number of UUIDs to generate (1–10, default: 1)
100
-
101
- ### hash
102
- Hash text with a chosen algorithm:
103
- - `text`: Text to hash (required)
104
- - `algorithm`: `md5`, `sha1`, or `sha256` (default: sha256)
105
-
106
- ### base64
107
- Encode or decode base64:
108
- - `text`: Input text (required)
109
- - `action`: `encode` or `decode` (default: encode)
110
-
111
- ### timestamp
112
- Convert timestamps:
113
- - `value`: Unix timestamp or ISO date string (leave empty for current time)
114
-
115
- ### jwt_decode
116
- Decode a JWT without verifying the signature:
117
- - `token`: JWT string (required)
118
- - Returns header, payload, expiry info
119
-
120
- ### random_string
121
- Generate random strings:
122
- - `length`: String length (default: 16, max: 256)
123
- - `charset`: `alphanumeric`, `alpha`, `numeric`, `hex`, `password`, or `url-safe`
124
-
125
- ### url_encode
126
- Encode or decode URLs:
127
- - `text`: Input text (required)
128
- - `action`: `encode` or `decode` (default: encode)
129
-
130
- ### json_format
131
- Format or minify JSON:
132
- - `json`: JSON string (required)
133
- - `action`: `format` or `minify` (default: format)
134
- - `indent`: Spaces for indentation (default: 2)
135
-
136
- ### regex_test
137
- Test regex patterns:
138
- - `pattern`: Regex pattern (required)
139
- - `text`: String to test against (required)
140
- - `flags`: Regex flags like `g`, `i`, `gi` (optional)
141
-
142
- ### cron_explain
143
- Explain a cron expression in plain English and show the next 5 scheduled runs:
144
- - `expression`: 5-field cron expression (required), e.g. `*/15 9-17 * * 1-5`
145
-
146
- ### hmac
147
- Generate an HMAC signature:
148
- - `message`: Message to sign (required)
149
- - `key`: Secret key (required)
150
- - `algorithm`: `sha256`, `sha512`, `sha1`, or `md5` (default: sha256)
151
- - `encoding`: `hex` or `base64` (default: hex)
152
-
153
- ### color_convert
154
- Convert colors between formats:
155
- - `color`: Color string (required) — accepts `#ff5733`, `rgb(255,87,51)`, or `hsl(11,100%,60%)`
156
- - Returns all three formats
157
-
158
- ### semver_compare
159
- Compare two semantic versions:
160
- - `version1`: First version (required), e.g. `1.2.3`
161
- - `version2`: Second version (required), e.g. `2.0.0`
162
- - Returns comparison result and parsed components
163
-
164
- ### http_status
165
- Look up HTTP status codes:
166
- - `code`: HTTP status code (required), e.g. `404`, `502`
167
- - Returns status name, category, and description
168
-
169
- ### slug
170
- Generate URL-safe slugs:
171
- - `text`: Text to slugify (required)
172
- - `separator`: Word separator (default: `-`)
173
-
174
- ### escape_html
175
- Escape or unescape HTML entities:
176
- - `text`: Input text (required)
177
- - `action`: `escape` or `unescape` (default: escape)
178
-
179
- ### chmod_calc
180
- Convert Unix file permissions:
181
- - `permission`: Numeric (e.g. `755`) or symbolic (e.g. `rwxr-xr-x`) (required)
182
- - Returns both formats plus owner/group/other breakdown
183
-
184
- ### diff
185
- Compare two texts:
186
- - `text1`: Original text (required)
187
- - `text2`: Modified text (required)
188
- - Returns line-by-line diff with added/removed/unchanged summary
189
-
190
- ### number_base
191
- Convert numbers between bases:
192
- - `value`: Number string (required) — prefix `0x` for hex, `0o` for octal, `0b` for binary, or plain decimal
193
- - Returns decimal, hex, octal, and binary representations
194
-
195
- ### lorem_ipsum
196
- Generate placeholder text:
197
- - `count`: Number of units (default: 1, max: 20)
198
- - `unit`: `paragraphs`, `sentences`, or `words` (default: paragraphs)
199
-
200
- ### word_count
201
- Analyze text:
202
- - `text`: Input text (required)
203
- - Returns character count, word count, line count, and byte size
204
-
205
- ### cidr
206
- Parse CIDR notation:
207
- - `notation`: CIDR string (required), e.g. `192.168.1.0/24`
208
- - Returns network, netmask, broadcast, host range, total hosts
209
-
210
- ### case_convert
211
- Convert between naming conventions:
212
- - `text`: Input text (required), e.g. `myVariableName` or `my-variable-name`
213
- - `to`: Target case (required) — `camel`, `snake`, `pascal`, `kebab`, `constant`, or `title`
214
- - Returns converted text plus all format variants
20
+ | Tool | Description |
21
+ |------|-------------|
22
+ | `uuid` | Generate UUID v4 (batch support) |
23
+ | `hash` | Hash text (md5, sha1, sha256) |
24
+ | `base64` | Encode/decode base64 |
25
+ | `timestamp` | Convert Unix ↔ ISO 8601 dates |
26
+ | `jwt_decode` | Decode JWT tokens (header + payload) |
27
+ | `random_string` | Generate random strings/passwords |
28
+ | `url_encode` | URL encode/decode |
29
+ | `json_format` | Pretty-print or minify JSON |
30
+ | `regex_test` | Test regex patterns with match details |
31
+ | `cron_explain` | Explain cron expressions + next run times |
32
+ | `hmac` | Generate HMAC signatures |
33
+ | `color_convert` | Convert hex ↔ RGB ↔ HSL colors |
34
+ | `semver_compare` | Compare semantic versions |
35
+ | `http_status` | Look up HTTP status code meanings |
36
+ | `slug` | Generate URL-safe slugs |
37
+ | `escape_html` | Escape/unescape HTML entities |
38
+ | `chmod_calc` | Convert numeric ↔ symbolic permissions |
39
+ | `diff` | Compare two text strings |
40
+ | `number_base` | Convert decimal/hex/octal/binary |
41
+ | `lorem_ipsum` | Generate placeholder text |
42
+ | `word_count` | Count chars, words, lines, bytes |
43
+ | `cidr` | Parse CIDR notation (network, broadcast, hosts) |
44
+ | `case_convert` | Convert camelCase/snake_case/PascalCase/kebab-case |
45
+ | `markdown_toc` | Generate table of contents from markdown |
46
+ | `env_parse` | Parse and validate .env files |
47
+ | `ip_info` | Analyze IP addresses (type, class, private/public) |
48
+ | `password_strength` | Analyze password entropy and strength |
49
+ | `data_size` | Convert between bytes/KB/MB/GB/TB (SI + IEC) |
50
+ | `string_escape` | Escape strings for JSON/CSV/regex/SQL/shell |
51
+
52
+ ## Zero dependencies
53
+
54
+ Only requires `@modelcontextprotocol/sdk`. All tools use Node.js built-ins.
215
55
 
216
- ### markdown_toc
217
- Generate a table of contents:
218
- - `markdown`: Markdown text (required)
219
- - `max_depth`: Maximum heading level to include (default: 3)
220
- - Returns formatted TOC with anchor links
56
+ ## Support
221
57
 
222
- ## See Also
58
+ If this tool saves you time, consider supporting development:
223
59
 
224
- - [mcp-apitools](https://www.npmjs.com/package/mcp-apitools) — 8 API & web dev utilities: HTTP status codes, MIME types, JWT creation, mock data, CORS headers, cookie parsing
225
- - [mcp-texttools](https://www.npmjs.com/package/mcp-texttools) — 10 text transformation tools: case convert, slugify, word count, lorem ipsum, regex replace, markdown strip
226
- - [mcp-mathtools](https://www.npmjs.com/package/mcp-mathtools) — 12 math & statistics tools: arithmetic, statistics, unit conversion, financial calculations, matrices
227
- - [mcp-datetime](https://www.npmjs.com/package/mcp-datetime) — 10 date & time tools: timezone conversion, date math, cron explanation, business days
228
- - [mcp-quick-calc](https://www.npmjs.com/package/mcp-quick-calc) — 5 calculator tools: currency conversion, percentages, compound interest, unit conversion, loan payments
229
- - **[mcp-all-tools](https://www.npmjs.com/package/mcp-all-tools)** — All 54+ tools in a single MCP server
60
+ - [Buy me a coffee](https://buymeacoffee.com/gl89tu25lp)
61
+ - [Tip via Stripe ($3)](https://buy.stripe.com/dRm8wP8R295Z9VyeN59Zm00)
230
62
 
231
63
  ## License
232
64
 
233
- MIT — [Hong Teoh](https://github.com/hlteoh37)
234
-
235
- ## Support
236
-
237
- If this tool saves you time, consider buying me a coffee:
238
- [buymeacoffee.com/gl89tu25lp](https://buymeacoffee.com/gl89tu25lp)
65
+ MIT
package/index.js CHANGED
@@ -5,7 +5,7 @@ import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprot
5
5
  import crypto from "crypto";
6
6
 
7
7
  const server = new Server(
8
- { name: "mcp-devutils", version: "1.3.0" },
8
+ { name: "mcp-devutils", version: "1.4.0" },
9
9
  { capabilities: { tools: {} } }
10
10
  );
11
11
 
@@ -335,6 +335,76 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
335
335
  },
336
336
  required: ["markdown"]
337
337
  }
338
+ },
339
+ {
340
+ name: "env_parse",
341
+ description: "Parse and validate .env file contents — shows keys, detects issues like missing values, duplicate keys, or invalid lines",
342
+ inputSchema: {
343
+ type: "object",
344
+ properties: {
345
+ content: { type: "string", description: ".env file content to parse and validate" }
346
+ },
347
+ required: ["content"]
348
+ }
349
+ },
350
+ {
351
+ name: "ip_info",
352
+ description: "Parse and analyze an IP address — shows type (IPv4/IPv6), class, whether private/public/loopback/link-local",
353
+ inputSchema: {
354
+ type: "object",
355
+ properties: {
356
+ ip: { type: "string", description: "IP address to analyze (e.g. '192.168.1.1' or '::1')" }
357
+ },
358
+ required: ["ip"]
359
+ }
360
+ },
361
+ {
362
+ name: "password_strength",
363
+ description: "Analyze password strength — calculates entropy, checks length, character diversity, and common patterns",
364
+ inputSchema: {
365
+ type: "object",
366
+ properties: {
367
+ password: { type: "string", description: "Password to analyze" }
368
+ },
369
+ required: ["password"]
370
+ }
371
+ },
372
+ {
373
+ name: "data_size",
374
+ description: "Convert between data size units (bytes, KB, MB, GB, TB, PB) with both decimal (SI) and binary (IEC) representations",
375
+ inputSchema: {
376
+ type: "object",
377
+ properties: {
378
+ value: { type: "number", description: "Numeric value to convert" },
379
+ unit: {
380
+ type: "string",
381
+ enum: ["B", "KB", "MB", "GB", "TB", "PB", "KiB", "MiB", "GiB", "TiB", "PiB"],
382
+ description: "Source unit (default: B for bytes)"
383
+ }
384
+ },
385
+ required: ["value"]
386
+ }
387
+ },
388
+ {
389
+ name: "string_escape",
390
+ description: "Escape or unescape strings for various contexts: JSON, CSV, regex, SQL, or shell",
391
+ inputSchema: {
392
+ type: "object",
393
+ properties: {
394
+ text: { type: "string", description: "Text to escape or unescape" },
395
+ format: {
396
+ type: "string",
397
+ enum: ["json", "csv", "regex", "sql", "shell"],
398
+ description: "Target format to escape for"
399
+ },
400
+ action: {
401
+ type: "string",
402
+ enum: ["escape", "unescape"],
403
+ description: "Action: escape or unescape (default: escape)"
404
+ }
405
+ },
406
+ required: ["text", "format"]
407
+ }
338
408
  }
339
409
  ]
340
410
  };
@@ -1032,6 +1102,210 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1032
1102
  return { content: [{ type: "text", text: toc.join("\n") }] };
1033
1103
  }
1034
1104
 
1105
+ case "env_parse": {
1106
+ const { content } = args;
1107
+ const lines = content.split("\n");
1108
+ const keys = [];
1109
+ const issues = [];
1110
+ const seen = new Set();
1111
+ lines.forEach((line, i) => {
1112
+ const num = i + 1;
1113
+ const trimmed = line.trim();
1114
+ if (!trimmed || trimmed.startsWith("#")) return;
1115
+ const eqIdx = trimmed.indexOf("=");
1116
+ if (eqIdx === -1) {
1117
+ issues.push(`Line ${num}: Invalid format (no '=' found): ${trimmed}`);
1118
+ return;
1119
+ }
1120
+ const key = trimmed.substring(0, eqIdx).trim();
1121
+ const val = trimmed.substring(eqIdx + 1).trim();
1122
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
1123
+ issues.push(`Line ${num}: Invalid key name '${key}'`);
1124
+ }
1125
+ if (seen.has(key)) {
1126
+ issues.push(`Line ${num}: Duplicate key '${key}'`);
1127
+ }
1128
+ seen.add(key);
1129
+ if (!val) {
1130
+ issues.push(`Line ${num}: Empty value for '${key}'`);
1131
+ }
1132
+ keys.push({ key, value: val.length > 50 ? val.substring(0, 50) + "..." : val, line: num });
1133
+ });
1134
+ const output = [`Parsed ${keys.length} key(s) from ${lines.length} line(s)`];
1135
+ if (keys.length > 0) {
1136
+ output.push("\nKeys:");
1137
+ keys.forEach(k => output.push(` ${k.key} = ${k.value} (line ${k.line})`));
1138
+ }
1139
+ if (issues.length > 0) {
1140
+ output.push(`\n⚠ ${issues.length} issue(s):`);
1141
+ issues.forEach(i => output.push(` ${i}`));
1142
+ } else {
1143
+ output.push("\n✓ No issues found");
1144
+ }
1145
+ return { content: [{ type: "text", text: output.join("\n") }] };
1146
+ }
1147
+
1148
+ case "ip_info": {
1149
+ const { ip } = args;
1150
+ const trimmed = ip.trim();
1151
+ const output = [];
1152
+ if (trimmed.includes(":")) {
1153
+ output.push(`IP: ${trimmed}`);
1154
+ output.push(`Version: IPv6`);
1155
+ if (trimmed === "::1") output.push("Type: Loopback");
1156
+ else if (trimmed.startsWith("fe80:")) output.push("Type: Link-local");
1157
+ else if (trimmed.startsWith("fc") || trimmed.startsWith("fd")) output.push("Type: Unique local (private)");
1158
+ else if (trimmed.startsWith("ff")) output.push("Type: Multicast");
1159
+ else if (trimmed === "::") output.push("Type: Unspecified");
1160
+ else output.push("Type: Global unicast (public)");
1161
+ } else {
1162
+ const parts = trimmed.split(".");
1163
+ if (parts.length !== 4 || parts.some(p => isNaN(p) || +p < 0 || +p > 255)) {
1164
+ throw new Error(`Invalid IPv4 address: ${trimmed}`);
1165
+ }
1166
+ const octets = parts.map(Number);
1167
+ output.push(`IP: ${trimmed}`);
1168
+ output.push(`Version: IPv4`);
1169
+ output.push(`Binary: ${octets.map(o => o.toString(2).padStart(8, "0")).join(".")}`);
1170
+ if (octets[0] === 127) output.push("Type: Loopback");
1171
+ else if (octets[0] === 10) output.push("Type: Private (10.0.0.0/8, Class A)");
1172
+ else if (octets[0] === 172 && octets[1] >= 16 && octets[1] <= 31) output.push("Type: Private (172.16.0.0/12, Class B)");
1173
+ else if (octets[0] === 192 && octets[1] === 168) output.push("Type: Private (192.168.0.0/16, Class C)");
1174
+ else if (octets[0] === 169 && octets[1] === 254) output.push("Type: Link-local (APIPA)");
1175
+ else if (octets[0] >= 224 && octets[0] <= 239) output.push("Type: Multicast");
1176
+ else if (octets[0] >= 240) output.push("Type: Reserved");
1177
+ else output.push("Type: Public");
1178
+ if (octets[0] < 128) output.push("Class: A");
1179
+ else if (octets[0] < 192) output.push("Class: B");
1180
+ else if (octets[0] < 224) output.push("Class: C");
1181
+ else if (octets[0] < 240) output.push("Class: D (Multicast)");
1182
+ else output.push("Class: E (Reserved)");
1183
+ }
1184
+ return { content: [{ type: "text", text: output.join("\n") }] };
1185
+ }
1186
+
1187
+ case "password_strength": {
1188
+ const { password } = args;
1189
+ const len = password.length;
1190
+ let charsetSize = 0;
1191
+ if (/[a-z]/.test(password)) charsetSize += 26;
1192
+ if (/[A-Z]/.test(password)) charsetSize += 26;
1193
+ if (/[0-9]/.test(password)) charsetSize += 10;
1194
+ if (/[^a-zA-Z0-9]/.test(password)) charsetSize += 32;
1195
+ const entropy = Math.round(len * Math.log2(charsetSize || 1) * 100) / 100;
1196
+ const issues = [];
1197
+ if (len < 8) issues.push("Too short (< 8 characters)");
1198
+ if (!/[A-Z]/.test(password)) issues.push("No uppercase letters");
1199
+ if (!/[a-z]/.test(password)) issues.push("No lowercase letters");
1200
+ if (!/[0-9]/.test(password)) issues.push("No digits");
1201
+ if (!/[^a-zA-Z0-9]/.test(password)) issues.push("No special characters");
1202
+ if (/(.)\1{2,}/.test(password)) issues.push("Contains repeated characters (3+)");
1203
+ if (/^(123|abc|qwerty|password|admin|letmein)/i.test(password)) issues.push("Starts with common pattern");
1204
+ let strength;
1205
+ if (entropy < 28) strength = "Very Weak";
1206
+ else if (entropy < 36) strength = "Weak";
1207
+ else if (entropy < 60) strength = "Moderate";
1208
+ else if (entropy < 80) strength = "Strong";
1209
+ else strength = "Very Strong";
1210
+ const output = [
1211
+ `Password length: ${len}`,
1212
+ `Charset size: ${charsetSize}`,
1213
+ `Entropy: ${entropy} bits`,
1214
+ `Strength: ${strength}`,
1215
+ ];
1216
+ if (issues.length > 0) {
1217
+ output.push(`\nIssues (${issues.length}):`);
1218
+ issues.forEach(i => output.push(` - ${i}`));
1219
+ } else {
1220
+ output.push("\n✓ No issues detected");
1221
+ }
1222
+ return { content: [{ type: "text", text: output.join("\n") }] };
1223
+ }
1224
+
1225
+ case "data_size": {
1226
+ const { value, unit = "B" } = args;
1227
+ const units = {
1228
+ B: 1, KB: 1e3, MB: 1e6, GB: 1e9, TB: 1e12, PB: 1e15,
1229
+ KiB: 1024, MiB: 1048576, GiB: 1073741824, TiB: 1099511627776, PiB: 1125899906842624
1230
+ };
1231
+ if (!units[unit]) throw new Error(`Unknown unit: ${unit}. Use: ${Object.keys(units).join(", ")}`);
1232
+ const bytes = value * units[unit];
1233
+ const fmt = (n) => n < 0.01 ? n.toExponential(2) : (n % 1 === 0 ? n.toString() : n.toFixed(2));
1234
+ const output = [
1235
+ `Input: ${value} ${unit} = ${fmt(bytes)} bytes`,
1236
+ "",
1237
+ "Decimal (SI):",
1238
+ ` ${fmt(bytes)} B`,
1239
+ ` ${fmt(bytes / 1e3)} KB`,
1240
+ ` ${fmt(bytes / 1e6)} MB`,
1241
+ ` ${fmt(bytes / 1e9)} GB`,
1242
+ ` ${fmt(bytes / 1e12)} TB`,
1243
+ ` ${fmt(bytes / 1e15)} PB`,
1244
+ "",
1245
+ "Binary (IEC):",
1246
+ ` ${fmt(bytes)} B`,
1247
+ ` ${fmt(bytes / 1024)} KiB`,
1248
+ ` ${fmt(bytes / 1048576)} MiB`,
1249
+ ` ${fmt(bytes / 1073741824)} GiB`,
1250
+ ` ${fmt(bytes / 1099511627776)} TiB`,
1251
+ ` ${fmt(bytes / 1125899906842624)} PiB`,
1252
+ ];
1253
+ return { content: [{ type: "text", text: output.join("\n") }] };
1254
+ }
1255
+
1256
+ case "string_escape": {
1257
+ const { text, format, action = "escape" } = args;
1258
+ let result;
1259
+ if (action === "escape") {
1260
+ switch (format) {
1261
+ case "json":
1262
+ result = JSON.stringify(text).slice(1, -1);
1263
+ break;
1264
+ case "csv":
1265
+ result = text.includes(",") || text.includes('"') || text.includes("\n")
1266
+ ? '"' + text.replace(/"/g, '""') + '"'
1267
+ : text;
1268
+ break;
1269
+ case "regex":
1270
+ result = text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1271
+ break;
1272
+ case "sql":
1273
+ result = text.replace(/'/g, "''");
1274
+ break;
1275
+ case "shell":
1276
+ result = "'" + text.replace(/'/g, "'\\''") + "'";
1277
+ break;
1278
+ default:
1279
+ throw new Error(`Unknown format: ${format}`);
1280
+ }
1281
+ } else {
1282
+ switch (format) {
1283
+ case "json":
1284
+ result = JSON.parse(`"${text}"`);
1285
+ break;
1286
+ case "csv":
1287
+ result = text.startsWith('"') && text.endsWith('"')
1288
+ ? text.slice(1, -1).replace(/""/g, '"')
1289
+ : text;
1290
+ break;
1291
+ case "regex":
1292
+ result = text.replace(/\\([.*+?^${}()|[\]\\])/g, "$1");
1293
+ break;
1294
+ case "sql":
1295
+ result = text.replace(/''/g, "'");
1296
+ break;
1297
+ case "shell":
1298
+ result = text.startsWith("'") && text.endsWith("'")
1299
+ ? text.slice(1, -1).replace(/'\\''/g, "'")
1300
+ : text;
1301
+ break;
1302
+ default:
1303
+ throw new Error(`Unknown format: ${format}`);
1304
+ }
1305
+ }
1306
+ return { content: [{ type: "text", text: result }] };
1307
+ }
1308
+
1035
1309
  default:
1036
1310
  throw new Error(`Unknown tool: ${name}`);
1037
1311
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mcp-devutils",
3
- "version": "1.3.0",
4
- "description": "MCP server with 25 developer utilities - UUID, hash, HMAC, base64, timestamps, JWT decode, random strings, URL encode/decode, JSON format, regex test, cron explain, color convert, semver compare, HTTP status codes, slugify, HTML escape, chmod calculator, text diff, number base converter, lorem ipsum, word count, CIDR calculator, case converter, markdown TOC",
3
+ "version": "1.4.0",
4
+ "description": "MCP server with 30 developer utilities - UUID, hash, HMAC, base64, timestamps, JWT decode, random strings, URL encode/decode, JSON format, regex test, cron explain, color convert, semver compare, HTTP status codes, slugify, HTML escape, chmod calculator, text diff, number base converter, lorem ipsum, word count, CIDR calculator, case converter, markdown TOC, env parser, IP info, password strength, data size converter, string escape",
5
5
  "type": "module",
6
6
  "main": "index.js",
7
7
  "bin": {
@@ -36,7 +36,12 @@
36
36
  "word-count",
37
37
  "cidr",
38
38
  "case-convert",
39
- "markdown-toc"
39
+ "markdown-toc",
40
+ "env-parser",
41
+ "ip-address",
42
+ "password-strength",
43
+ "data-size",
44
+ "string-escape"
40
45
  ],
41
46
  "author": "Hong Teoh",
42
47
  "license": "MIT",
@@ -46,5 +51,13 @@
46
51
  },
47
52
  "dependencies": {
48
53
  "@modelcontextprotocol/sdk": "^1.0.0"
54
+ },
55
+ "repository": {
56
+ "type": "git",
57
+ "url": "https://github.com/hlteoh37/mcp-devutils.git"
58
+ },
59
+ "homepage": "https://github.com/hlteoh37/mcp-devutils#readme",
60
+ "bugs": {
61
+ "url": "https://github.com/hlteoh37/mcp-devutils/issues"
49
62
  }
50
63
  }