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.
- package/README.md +43 -216
- package/index.js +275 -1
- package/package.json +16 -3
package/README.md
CHANGED
|
@@ -1,70 +1,8 @@
|
|
|
1
1
|
# mcp-devutils
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
MCP server with **30 developer utilities** for Claude Desktop, Cursor, and any MCP-compatible AI assistant.
|
|
4
4
|
|
|
5
|
-
##
|
|
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
|
-
##
|
|
18
|
+
## Tools (30)
|
|
81
19
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
Generate
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
-
|
|
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
|
-
|
|
58
|
+
If this tool saves you time, consider supporting development:
|
|
223
59
|
|
|
224
|
-
- [
|
|
225
|
-
- [
|
|
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
|
|
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.
|
|
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.
|
|
4
|
-
"description": "MCP server with
|
|
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
|
}
|