mcp-devutils 1.2.0 → 1.3.1

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 +4 -196
  2. package/index.js +245 -1
  3. package/package.json +17 -3
package/README.md CHANGED
@@ -1,200 +1,8 @@
1
- # mcp-devutils
2
1
 
3
- An MCP (Model Context Protocol) server with developer utilities. Use it directly with Claude Desktop, Cursor, or any MCP-compatible client.
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
-
28
- ## Installation
29
-
30
- ```bash
31
- npm install -g mcp-devutils
32
- ```
33
-
34
- Or use directly with `npx`:
35
-
36
- ```json
37
- {
38
- "mcpServers": {
39
- "devutils": {
40
- "command": "npx",
41
- "args": ["-y", "mcp-devutils"]
42
- }
43
- }
44
- }
45
- ```
46
-
47
- ## Usage with Claude Desktop
48
-
49
- Add to your `claude_desktop_config.json`:
50
-
51
- ```json
52
- {
53
- "mcpServers": {
54
- "devutils": {
55
- "command": "mcp-devutils"
56
- }
57
- }
58
- }
59
- ```
60
-
61
- Or with npx (no install needed):
62
-
63
- ```json
64
- {
65
- "mcpServers": {
66
- "devutils": {
67
- "command": "npx",
68
- "args": ["-y", "mcp-devutils"]
69
- }
70
- }
71
- }
72
- ```
73
-
74
- ## Usage with Cursor
75
-
76
- Add to your Cursor MCP settings:
77
-
78
- ```json
79
- {
80
- "mcpServers": {
81
- "devutils": {
82
- "command": "npx",
83
- "args": ["-y", "mcp-devutils"]
84
- }
85
- }
86
- }
87
- ```
88
-
89
- ## Tool Examples
90
-
91
- ### uuid
92
- Generate one or more UUIDs:
93
- - `count`: Number of UUIDs to generate (1–10, default: 1)
94
-
95
- ### hash
96
- Hash text with a chosen algorithm:
97
- - `text`: Text to hash (required)
98
- - `algorithm`: `md5`, `sha1`, or `sha256` (default: sha256)
99
-
100
- ### base64
101
- Encode or decode base64:
102
- - `text`: Input text (required)
103
- - `action`: `encode` or `decode` (default: encode)
104
-
105
- ### timestamp
106
- Convert timestamps:
107
- - `value`: Unix timestamp or ISO date string (leave empty for current time)
108
-
109
- ### jwt_decode
110
- Decode a JWT without verifying the signature:
111
- - `token`: JWT string (required)
112
- - Returns header, payload, expiry info
113
-
114
- ### random_string
115
- Generate random strings:
116
- - `length`: String length (default: 16, max: 256)
117
- - `charset`: `alphanumeric`, `alpha`, `numeric`, `hex`, `password`, or `url-safe`
118
-
119
- ### url_encode
120
- Encode or decode URLs:
121
- - `text`: Input text (required)
122
- - `action`: `encode` or `decode` (default: encode)
123
-
124
- ### json_format
125
- Format or minify JSON:
126
- - `json`: JSON string (required)
127
- - `action`: `format` or `minify` (default: format)
128
- - `indent`: Spaces for indentation (default: 2)
129
-
130
- ### regex_test
131
- Test regex patterns:
132
- - `pattern`: Regex pattern (required)
133
- - `text`: String to test against (required)
134
- - `flags`: Regex flags like `g`, `i`, `gi` (optional)
135
-
136
- ### cron_explain
137
- Explain a cron expression in plain English and show the next 5 scheduled runs:
138
- - `expression`: 5-field cron expression (required), e.g. `*/15 9-17 * * 1-5`
139
-
140
- ### hmac
141
- Generate an HMAC signature:
142
- - `message`: Message to sign (required)
143
- - `key`: Secret key (required)
144
- - `algorithm`: `sha256`, `sha512`, `sha1`, or `md5` (default: sha256)
145
- - `encoding`: `hex` or `base64` (default: hex)
146
-
147
- ### color_convert
148
- Convert colors between formats:
149
- - `color`: Color string (required) — accepts `#ff5733`, `rgb(255,87,51)`, or `hsl(11,100%,60%)`
150
- - Returns all three formats
151
-
152
- ### semver_compare
153
- Compare two semantic versions:
154
- - `version1`: First version (required), e.g. `1.2.3`
155
- - `version2`: Second version (required), e.g. `2.0.0`
156
- - Returns comparison result and parsed components
157
-
158
- ### http_status
159
- Look up HTTP status codes:
160
- - `code`: HTTP status code (required), e.g. `404`, `502`
161
- - Returns status name, category, and description
162
-
163
- ### slug
164
- Generate URL-safe slugs:
165
- - `text`: Text to slugify (required)
166
- - `separator`: Word separator (default: `-`)
167
-
168
- ### escape_html
169
- Escape or unescape HTML entities:
170
- - `text`: Input text (required)
171
- - `action`: `escape` or `unescape` (default: escape)
172
-
173
- ### chmod_calc
174
- Convert Unix file permissions:
175
- - `permission`: Numeric (e.g. `755`) or symbolic (e.g. `rwxr-xr-x`) (required)
176
- - Returns both formats plus owner/group/other breakdown
177
-
178
- ### diff
179
- Compare two texts:
180
- - `text1`: Original text (required)
181
- - `text2`: Modified text (required)
182
- - Returns line-by-line diff with added/removed/unchanged summary
183
-
184
- ## See Also
185
-
186
- - [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
187
- - [mcp-texttools](https://www.npmjs.com/package/mcp-texttools) — 10 text transformation tools: case convert, slugify, word count, lorem ipsum, regex replace, markdown strip
188
- - [mcp-mathtools](https://www.npmjs.com/package/mcp-mathtools) — 12 math & statistics tools: arithmetic, statistics, unit conversion, financial calculations, matrices
189
- - [mcp-datetime](https://www.npmjs.com/package/mcp-datetime) — 10 date & time tools: timezone conversion, date math, cron explanation, business days
190
- - [mcp-quick-calc](https://www.npmjs.com/package/mcp-quick-calc) — 5 calculator tools: currency conversion, percentages, compound interest, unit conversion, loan payments
191
- - **[mcp-all-tools](https://www.npmjs.com/package/mcp-all-tools)** — All 54+ tools in a single MCP server
192
-
193
- ## License
194
-
195
- MIT — [Hong Teoh](https://github.com/hlteoh37)
196
2
 
197
3
  ## Support
198
4
 
199
- If this tool saves you time, consider buying me a coffee:
200
- [buymeacoffee.com/gl89tu25lp](https://buymeacoffee.com/gl89tu25lp)
5
+ If this tool saves you time, consider supporting development:
6
+
7
+ - [Buy me a coffee](https://buymeacoffee.com/gl89tu25lp)
8
+ - [Tip via Stripe ($3)](https://buy.stripe.com/dRm8wP8R295Z9VyeN59Zm00)
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.2.0" },
8
+ { name: "mcp-devutils", version: "1.3.0" },
9
9
  { capabilities: { tools: {} } }
10
10
  );
11
11
 
@@ -259,6 +259,82 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
259
259
  },
260
260
  required: ["text1", "text2"]
261
261
  }
262
+ },
263
+ {
264
+ name: "number_base",
265
+ description: "Convert numbers between decimal, hexadecimal, octal, and binary",
266
+ inputSchema: {
267
+ type: "object",
268
+ properties: {
269
+ value: { type: "string", description: "Number to convert (prefix with 0x for hex, 0o for octal, 0b for binary, or plain decimal)" }
270
+ },
271
+ required: ["value"]
272
+ }
273
+ },
274
+ {
275
+ name: "lorem_ipsum",
276
+ description: "Generate placeholder lorem ipsum text",
277
+ inputSchema: {
278
+ type: "object",
279
+ properties: {
280
+ count: { type: "number", description: "Number of units to generate (default: 1)" },
281
+ unit: {
282
+ type: "string",
283
+ enum: ["paragraphs", "sentences", "words"],
284
+ description: "Unit type (default: paragraphs)"
285
+ }
286
+ }
287
+ }
288
+ },
289
+ {
290
+ name: "word_count",
291
+ description: "Count characters, words, lines, and bytes in text",
292
+ inputSchema: {
293
+ type: "object",
294
+ properties: {
295
+ text: { type: "string", description: "Text to analyze" }
296
+ },
297
+ required: ["text"]
298
+ }
299
+ },
300
+ {
301
+ name: "cidr",
302
+ description: "Parse CIDR notation and show network address, broadcast, host range, and number of hosts",
303
+ inputSchema: {
304
+ type: "object",
305
+ properties: {
306
+ notation: { type: "string", description: "CIDR notation (e.g. '192.168.1.0/24')" }
307
+ },
308
+ required: ["notation"]
309
+ }
310
+ },
311
+ {
312
+ name: "case_convert",
313
+ description: "Convert text between camelCase, snake_case, PascalCase, kebab-case, CONSTANT_CASE, and Title Case",
314
+ inputSchema: {
315
+ type: "object",
316
+ properties: {
317
+ text: { type: "string", description: "Text to convert (e.g. 'myVariableName' or 'my-variable-name')" },
318
+ to: {
319
+ type: "string",
320
+ enum: ["camel", "snake", "pascal", "kebab", "constant", "title"],
321
+ description: "Target case format"
322
+ }
323
+ },
324
+ required: ["text", "to"]
325
+ }
326
+ },
327
+ {
328
+ name: "markdown_toc",
329
+ description: "Generate a table of contents from markdown headings",
330
+ inputSchema: {
331
+ type: "object",
332
+ properties: {
333
+ markdown: { type: "string", description: "Markdown text to extract headings from" },
334
+ max_depth: { type: "number", description: "Maximum heading depth to include (default: 3)" }
335
+ },
336
+ required: ["markdown"]
337
+ }
262
338
  }
263
339
  ]
264
340
  };
@@ -788,6 +864,174 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
788
864
  return { content: [{ type: "text", text: output.join("\n") + summary }] };
789
865
  }
790
866
 
867
+ case "number_base": {
868
+ const { value } = args;
869
+ let num;
870
+ const v = value.trim();
871
+ if (v.startsWith("0x") || v.startsWith("0X")) num = parseInt(v, 16);
872
+ else if (v.startsWith("0o") || v.startsWith("0O")) num = parseInt(v.slice(2), 8);
873
+ else if (v.startsWith("0b") || v.startsWith("0B")) num = parseInt(v.slice(2), 2);
874
+ else num = parseInt(v, 10);
875
+ if (isNaN(num)) throw new Error(`Invalid number: ${value}`);
876
+ const output = [
877
+ `Decimal: ${num}`,
878
+ `Hexadecimal: 0x${num.toString(16).toUpperCase()}`,
879
+ `Octal: 0o${num.toString(8)}`,
880
+ `Binary: 0b${num.toString(2)}`
881
+ ];
882
+ return { content: [{ type: "text", text: output.join("\n") }] };
883
+ }
884
+
885
+ case "lorem_ipsum": {
886
+ const { count = 1, unit = "paragraphs" } = args || {};
887
+ const sentences = [
888
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
889
+ "Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
890
+ "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.",
891
+ "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore.",
892
+ "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia.",
893
+ "Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit.",
894
+ "Neque porro quisquam est qui dolorem ipsum quia dolor sit amet.",
895
+ "Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit.",
896
+ "Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse.",
897
+ "At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis."
898
+ ];
899
+ const n = Math.min(Math.max(1, count), 20);
900
+ let result;
901
+ if (unit === "words") {
902
+ const allWords = sentences.join(" ").split(/\s+/);
903
+ const words = [];
904
+ for (let i = 0; i < n; i++) words.push(allWords[i % allWords.length]);
905
+ result = words.join(" ");
906
+ } else if (unit === "sentences") {
907
+ const out = [];
908
+ for (let i = 0; i < n; i++) out.push(sentences[i % sentences.length]);
909
+ result = out.join(" ");
910
+ } else {
911
+ const paras = [];
912
+ for (let i = 0; i < n; i++) {
913
+ const start = (i * 3) % sentences.length;
914
+ const para = [];
915
+ for (let j = 0; j < 5; j++) para.push(sentences[(start + j) % sentences.length]);
916
+ paras.push(para.join(" "));
917
+ }
918
+ result = paras.join("\n\n");
919
+ }
920
+ return { content: [{ type: "text", text: result }] };
921
+ }
922
+
923
+ case "word_count": {
924
+ const { text } = args;
925
+ const chars = text.length;
926
+ const charsNoSpaces = text.replace(/\s/g, "").length;
927
+ const words = text.trim() === "" ? 0 : text.trim().split(/\s+/).length;
928
+ const lines = text.split("\n").length;
929
+ const bytes = Buffer.byteLength(text, "utf8");
930
+ const output = [
931
+ `Characters: ${chars}`,
932
+ `Characters (no spaces): ${charsNoSpaces}`,
933
+ `Words: ${words}`,
934
+ `Lines: ${lines}`,
935
+ `Bytes (UTF-8): ${bytes}`
936
+ ];
937
+ return { content: [{ type: "text", text: output.join("\n") }] };
938
+ }
939
+
940
+ case "cidr": {
941
+ const { notation } = args;
942
+ const match = notation.match(/^(\d+)\.(\d+)\.(\d+)\.(\d+)\/(\d+)$/);
943
+ if (!match) throw new Error("Invalid CIDR notation. Use format: 192.168.1.0/24");
944
+ const octets = [parseInt(match[1]), parseInt(match[2]), parseInt(match[3]), parseInt(match[4])];
945
+ const prefix = parseInt(match[5]);
946
+ if (octets.some(o => o < 0 || o > 255) || prefix < 0 || prefix > 32) {
947
+ throw new Error("Invalid IP address or prefix length");
948
+ }
949
+ const ip = ((octets[0] << 24) | (octets[1] << 16) | (octets[2] << 8) | octets[3]) >>> 0;
950
+ const mask = prefix === 0 ? 0 : (~0 << (32 - prefix)) >>> 0;
951
+ const network = (ip & mask) >>> 0;
952
+ const broadcast = (network | ~mask) >>> 0;
953
+ const firstHost = prefix >= 31 ? network : (network + 1) >>> 0;
954
+ const lastHost = prefix >= 31 ? broadcast : (broadcast - 1) >>> 0;
955
+ const hostCount = prefix >= 31 ? (prefix === 32 ? 1 : 2) : Math.pow(2, 32 - prefix) - 2;
956
+ const toIP = (n) => `${(n >>> 24) & 255}.${(n >>> 16) & 255}.${(n >>> 8) & 255}.${n & 255}`;
957
+ const output = [
958
+ `CIDR: ${notation}`,
959
+ `Network: ${toIP(network)}`,
960
+ `Netmask: ${toIP(mask)}`,
961
+ `Broadcast: ${toIP(broadcast)}`,
962
+ `First host: ${toIP(firstHost)}`,
963
+ `Last host: ${toIP(lastHost)}`,
964
+ `Total hosts: ${hostCount}`,
965
+ `Prefix length: /${prefix}`
966
+ ];
967
+ return { content: [{ type: "text", text: output.join("\n") }] };
968
+ }
969
+
970
+ case "case_convert": {
971
+ const { text, to } = args;
972
+ // Split input into words regardless of input format
973
+ const words = text
974
+ .replace(/([a-z])([A-Z])/g, "$1 $2") // camelCase splits
975
+ .replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2") // ABCDef -> ABC Def
976
+ .replace(/[-_]/g, " ")
977
+ .split(/\s+/)
978
+ .filter(w => w.length > 0)
979
+ .map(w => w.toLowerCase());
980
+ let result;
981
+ switch (to) {
982
+ case "camel":
983
+ result = words[0] + words.slice(1).map(w => w[0].toUpperCase() + w.slice(1)).join("");
984
+ break;
985
+ case "pascal":
986
+ result = words.map(w => w[0].toUpperCase() + w.slice(1)).join("");
987
+ break;
988
+ case "snake":
989
+ result = words.join("_");
990
+ break;
991
+ case "kebab":
992
+ result = words.join("-");
993
+ break;
994
+ case "constant":
995
+ result = words.map(w => w.toUpperCase()).join("_");
996
+ break;
997
+ case "title":
998
+ result = words.map(w => w[0].toUpperCase() + w.slice(1)).join(" ");
999
+ break;
1000
+ default:
1001
+ throw new Error(`Unknown case: ${to}`);
1002
+ }
1003
+ const all = {
1004
+ camelCase: words[0] + words.slice(1).map(w => w[0].toUpperCase() + w.slice(1)).join(""),
1005
+ PascalCase: words.map(w => w[0].toUpperCase() + w.slice(1)).join(""),
1006
+ snake_case: words.join("_"),
1007
+ "kebab-case": words.join("-"),
1008
+ CONSTANT_CASE: words.map(w => w.toUpperCase()).join("_"),
1009
+ "Title Case": words.map(w => w[0].toUpperCase() + w.slice(1)).join(" ")
1010
+ };
1011
+ const output = [`Result: ${result}`, "", "All formats:"];
1012
+ for (const [k, v] of Object.entries(all)) output.push(` ${k}: ${v}`);
1013
+ return { content: [{ type: "text", text: output.join("\n") }] };
1014
+ }
1015
+
1016
+ case "markdown_toc": {
1017
+ const { markdown, max_depth = 3 } = args;
1018
+ const lines = markdown.split("\n");
1019
+ const toc = [];
1020
+ for (const line of lines) {
1021
+ const match = line.match(/^(#{1,6})\s+(.+)$/);
1022
+ if (match) {
1023
+ const level = match[1].length;
1024
+ if (level > max_depth) continue;
1025
+ const text = match[2].replace(/[*_`\[\]]/g, "").trim();
1026
+ const anchor = text.toLowerCase().replace(/[^\w\s-]/g, "").replace(/\s+/g, "-");
1027
+ const indent = " ".repeat(level - 1);
1028
+ toc.push(`${indent}- [${text}](#${anchor})`);
1029
+ }
1030
+ }
1031
+ if (toc.length === 0) return { content: [{ type: "text", text: "No headings found." }] };
1032
+ return { content: [{ type: "text", text: toc.join("\n") }] };
1033
+ }
1034
+
791
1035
  default:
792
1036
  throw new Error(`Unknown tool: ${name}`);
793
1037
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mcp-devutils",
3
- "version": "1.2.0",
4
- "description": "MCP server with 19 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",
3
+ "version": "1.3.1",
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",
5
5
  "type": "module",
6
6
  "main": "index.js",
7
7
  "bin": {
@@ -30,7 +30,13 @@
30
30
  "html-escape",
31
31
  "chmod",
32
32
  "diff",
33
- "permissions"
33
+ "permissions",
34
+ "number-base",
35
+ "lorem-ipsum",
36
+ "word-count",
37
+ "cidr",
38
+ "case-convert",
39
+ "markdown-toc"
34
40
  ],
35
41
  "author": "Hong Teoh",
36
42
  "license": "MIT",
@@ -40,5 +46,13 @@
40
46
  },
41
47
  "dependencies": {
42
48
  "@modelcontextprotocol/sdk": "^1.0.0"
49
+ },
50
+ "repository": {
51
+ "type": "git",
52
+ "url": "https://github.com/hlteoh37/mcp-devutils.git"
53
+ },
54
+ "homepage": "https://github.com/hlteoh37/mcp-devutils#readme",
55
+ "bugs": {
56
+ "url": "https://github.com/hlteoh37/mcp-devutils/issues"
43
57
  }
44
58
  }