@utilix-tech/mcp 0.1.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 ADDED
@@ -0,0 +1,160 @@
1
+ # @utilix-tech/mcp
2
+
3
+ MCP server exposing 51 developer utility tools to Claude, Cursor, VS Code Copilot, and any [Model Context Protocol](https://modelcontextprotocol.io) compatible AI assistant.
4
+
5
+ No API key required. All tools run locally.
6
+
7
+ ## Installation
8
+
9
+ ### Claude Desktop
10
+
11
+ Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
12
+
13
+ ```json
14
+ {
15
+ "mcpServers": {
16
+ "utilix": {
17
+ "command": "npx",
18
+ "args": ["-y", "@utilix-tech/mcp"]
19
+ }
20
+ }
21
+ }
22
+ ```
23
+
24
+ ### Cursor
25
+
26
+ Add to `.cursor/mcp.json` in your project, or `~/.cursor/mcp.json` globally:
27
+
28
+ ```json
29
+ {
30
+ "mcpServers": {
31
+ "utilix": {
32
+ "command": "npx",
33
+ "args": ["-y", "@utilix-tech/mcp"]
34
+ }
35
+ }
36
+ }
37
+ ```
38
+
39
+ ### VS Code Copilot
40
+
41
+ Add to `.vscode/mcp.json`:
42
+
43
+ ```json
44
+ {
45
+ "servers": {
46
+ "utilix": {
47
+ "type": "stdio",
48
+ "command": "npx",
49
+ "args": ["-y", "@utilix-tech/mcp"]
50
+ }
51
+ }
52
+ }
53
+ ```
54
+
55
+ ## Tools
56
+
57
+ ### JSON (13 tools)
58
+ | Tool | Description |
59
+ |------|-------------|
60
+ | `format_json` | Pretty-print and validate a JSON string |
61
+ | `minify_json` | Remove all whitespace from JSON |
62
+ | `diff_json` | Compare two JSON objects and show changes |
63
+ | `json_to_csv` | Convert a JSON array of objects to CSV |
64
+ | `csv_to_json` | Convert CSV text to a JSON array |
65
+ | `jsonpath` | Evaluate a JSONPath expression |
66
+ | `validate_json` | Validate JSON against a JSON Schema |
67
+ | `yaml_to_json` | Convert YAML to JSON |
68
+ | `json_to_yaml` | Convert JSON to YAML |
69
+ | `json_to_typescript` | Generate TypeScript interfaces from JSON |
70
+ | `json_to_go` | Generate Go structs from JSON |
71
+ | `json_to_python` | Generate Python dataclasses from JSON |
72
+ | `json_to_zod` | Generate a Zod schema from JSON |
73
+
74
+ ### Encoding (8 tools)
75
+ | Tool | Description |
76
+ |------|-------------|
77
+ | `base64_encode` | Encode a string to Base64 |
78
+ | `base64_decode` | Decode a Base64 string |
79
+ | `url_encode` | Percent-encode a URL or component |
80
+ | `url_decode` | Decode a percent-encoded string |
81
+ | `html_entities_encode` | Encode HTML entities |
82
+ | `html_entities_decode` | Decode HTML entities |
83
+ | `base32_encode` | Encode a string to Base32 |
84
+ | `base32_decode` | Decode a Base32 string |
85
+
86
+ ### Hashing (4 tools)
87
+ | Tool | Description |
88
+ |------|-------------|
89
+ | `hash` | Hash a string with MD5, SHA-1, SHA-256, SHA-384, or SHA-512 |
90
+ | `hash_all` | Hash a string with all algorithms at once |
91
+ | `bcrypt_hash` | Hash a password using bcrypt |
92
+ | `bcrypt_verify` | Verify a password against a bcrypt hash |
93
+
94
+ ### Generators (3 tools)
95
+ | Tool | Description |
96
+ |------|-------------|
97
+ | `generate_uuid` | Generate UUID v4 or v7 |
98
+ | `generate_ulid` | Generate a ULID |
99
+ | `generate_password` | Generate a secure random password |
100
+
101
+ ### Text (8 tools)
102
+ | Tool | Description |
103
+ |------|-------------|
104
+ | `convert_case` | Convert text case: camelCase, snake_case, PascalCase, etc. |
105
+ | `slugify` | Convert text to a URL-friendly slug |
106
+ | `word_count` | Count words, characters, sentences, and reading time |
107
+ | `escape_string` | Escape characters for JSON, JavaScript, HTML, URL, or CSV |
108
+ | `unescape_string` | Unescape JSON, JavaScript, HTML, URL, or CSV strings |
109
+ | `lorem_ipsum` | Generate Lorem Ipsum placeholder text |
110
+
111
+ ### Time (4 tools)
112
+ | Tool | Description |
113
+ |------|-------------|
114
+ | `unix_timestamp` | Convert a Unix timestamp to human-readable date |
115
+ | `parse_cron` | Parse a cron expression into a readable description |
116
+ | `cron_next_runs` | Show the next N scheduled run times for a cron expression |
117
+ | `date_diff` | Calculate the difference between two dates |
118
+ | `convert_timezone` | Convert a date/time between timezones |
119
+
120
+ ### Units (2 tools)
121
+ | Tool | Description |
122
+ |------|-------------|
123
+ | `convert_bytes` | Convert between B, KB, MB, GB, TB, KiB, MiB, GiB, TiB |
124
+ | `px_to_units` | Convert pixels to rem, em, pt, and vw |
125
+
126
+ ### API / Network (3 tools)
127
+ | Tool | Description |
128
+ |------|-------------|
129
+ | `decode_jwt` | Decode a JWT — header, payload, and expiry |
130
+ | `build_curl` | Build a cURL command from method, URL, headers, and body |
131
+ | `curl_to_code` | Convert a cURL command to fetch, axios, Python, Go, PHP, or Ruby |
132
+
133
+ ### Color (1 tool)
134
+ | Tool | Description |
135
+ |------|-------------|
136
+ | `convert_color` | Convert a color between HEX, RGB, HSL, and HSV |
137
+
138
+ ### Code (3 tools)
139
+ | Tool | Description |
140
+ |------|-------------|
141
+ | `format_sql` | Format a SQL query with proper indentation |
142
+ | `format_html` | Format and indent HTML markup |
143
+ | `test_regex` | Test a regular expression and return all matches |
144
+
145
+ ### Data (3 tools)
146
+ | Tool | Description |
147
+ |------|-------------|
148
+ | `validate_yaml` | Validate YAML syntax |
149
+ | `toml_to_json` | Convert TOML to JSON |
150
+ | `xml_to_json` | Convert XML to JSON |
151
+
152
+ ## Requirements
153
+
154
+ - Node.js 18+
155
+
156
+ ## Links
157
+
158
+ - [Utilix web app](https://utilix.tech) — use these tools in the browser
159
+ - [Node.js SDK](https://www.npmjs.com/package/@utilix-tech/sdk) — use in your Node.js projects
160
+ - [Python SDK](https://pypi.org/project/utilix-sdk/) — use in your Python projects
package/dist/index.js ADDED
@@ -0,0 +1,743 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
+ import { z } from "zod";
7
+ import {
8
+ parseJson,
9
+ minifyJson,
10
+ diffJson,
11
+ jsonToCsv,
12
+ csvToJson,
13
+ evaluateJsonPath,
14
+ validateJsonString,
15
+ yamlToJson,
16
+ jsonToYaml,
17
+ jsonToTypescript,
18
+ jsonToGo,
19
+ jsonToPython,
20
+ jsonToZod
21
+ } from "@utilix-tech/sdk/json";
22
+ import {
23
+ encodeBase64,
24
+ decodeBase64,
25
+ encodeUrl,
26
+ decodeUrl,
27
+ encodeHtmlEntities,
28
+ decodeHtmlEntities,
29
+ base32Encode,
30
+ base32Decode
31
+ } from "@utilix-tech/sdk/encoding";
32
+ import {
33
+ hashOne,
34
+ hashAll,
35
+ hashPassword,
36
+ verifyPassword
37
+ } from "@utilix-tech/sdk/hashing";
38
+ import {
39
+ generateUuid,
40
+ generatePassword,
41
+ generateUlid
42
+ } from "@utilix-tech/sdk/generators";
43
+ import {
44
+ countWords,
45
+ convertCase,
46
+ slugify,
47
+ generate as loremGenerate,
48
+ escapeString,
49
+ unescapeString
50
+ } from "@utilix-tech/sdk/text";
51
+ import {
52
+ fromUnix,
53
+ parseCron,
54
+ getNextRuns,
55
+ diffDates,
56
+ convertTime
57
+ } from "@utilix-tech/sdk/time";
58
+ import { convertBytes, pxToAll } from "@utilix-tech/sdk/units";
59
+ import {
60
+ decodeJwt,
61
+ buildCurl,
62
+ parseCurlCommand,
63
+ convertToCode
64
+ } from "@utilix-tech/sdk/api";
65
+ import { parseColor } from "@utilix-tech/sdk/color";
66
+ import { formatSql, formatHtml, testRegex } from "@utilix-tech/sdk/code";
67
+ import { validateYaml, tomlToJson, xmlToJson } from "@utilix-tech/sdk/data";
68
+ var CRON_DAY_MAP = {
69
+ SUN: "0",
70
+ MON: "1",
71
+ TUE: "2",
72
+ WED: "3",
73
+ THU: "4",
74
+ FRI: "5",
75
+ SAT: "6"
76
+ };
77
+ var CRON_MON_MAP = {
78
+ JAN: "1",
79
+ FEB: "2",
80
+ MAR: "3",
81
+ APR: "4",
82
+ MAY: "5",
83
+ JUN: "6",
84
+ JUL: "7",
85
+ AUG: "8",
86
+ SEP: "9",
87
+ OCT: "10",
88
+ NOV: "11",
89
+ DEC: "12"
90
+ };
91
+ function normalizeCron(expr) {
92
+ return expr.toUpperCase().replace(/\b(SUN|MON|TUE|WED|THU|FRI|SAT)\b/g, (d) => CRON_DAY_MAP[d]).replace(/\b(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)\b/g, (m) => CRON_MON_MAP[m]);
93
+ }
94
+ function text(t) {
95
+ return { content: [{ type: "text", text: t }] };
96
+ }
97
+ function errText(e) {
98
+ const msg = e instanceof Error ? e.message : typeof e === "object" && e !== null && "error" in e ? String(e.error) : String(e);
99
+ return { content: [{ type: "text", text: `Error: ${msg}` }], isError: true };
100
+ }
101
+ function toText(result) {
102
+ if (typeof result === "string") return result;
103
+ return JSON.stringify(result, null, 2);
104
+ }
105
+ function checkErr(result) {
106
+ return typeof result === "object" && result !== null && "error" in result;
107
+ }
108
+ var server = new McpServer({
109
+ name: "utilix",
110
+ version: "0.1.0"
111
+ });
112
+ server.tool(
113
+ "format_json",
114
+ "Parse, validate and pretty-print a JSON string. Returns formatted JSON or an error message.",
115
+ { input: z.string().describe("JSON string to format"), indent: z.number().int().min(1).max(8).default(2).describe("Indentation spaces (default 2)") },
116
+ async ({ input, indent }) => {
117
+ const result = parseJson(input, indent);
118
+ if (result.status === "error") return text(`Invalid JSON: ${result.error}`);
119
+ return text(result.output);
120
+ }
121
+ );
122
+ server.tool(
123
+ "minify_json",
124
+ "Minify a JSON string by removing all whitespace.",
125
+ { input: z.string().describe("JSON string to minify") },
126
+ async ({ input }) => {
127
+ const result = minifyJson(input);
128
+ if (checkErr(result)) return errText(result);
129
+ return text(result);
130
+ }
131
+ );
132
+ server.tool(
133
+ "diff_json",
134
+ "Compare two JSON strings and return a structured diff showing added, removed, and changed keys.",
135
+ {
136
+ left: z.string().describe("Original JSON"),
137
+ right: z.string().describe("New/modified JSON")
138
+ },
139
+ async ({ left, right }) => {
140
+ const result = diffJson(left, right);
141
+ if (checkErr(result)) return errText(result);
142
+ return text(toText(result));
143
+ }
144
+ );
145
+ server.tool(
146
+ "json_to_csv",
147
+ "Convert a JSON array of objects to CSV format.",
148
+ { input: z.string().describe("JSON array of objects") },
149
+ async ({ input }) => {
150
+ const result = jsonToCsv(input);
151
+ if (checkErr(result)) return errText(result);
152
+ return text(result);
153
+ }
154
+ );
155
+ server.tool(
156
+ "csv_to_json",
157
+ "Convert CSV text to a JSON array of objects.",
158
+ {
159
+ input: z.string().describe("CSV text"),
160
+ delimiter: z.string().default(",").describe("Field delimiter (default comma)")
161
+ },
162
+ async ({ input, delimiter }) => {
163
+ const result = csvToJson(input, { delimiter });
164
+ if (checkErr(result)) return errText(result);
165
+ return text(toText(result));
166
+ }
167
+ );
168
+ server.tool(
169
+ "jsonpath",
170
+ "Evaluate a JSONPath expression against a JSON string and return matching values.",
171
+ {
172
+ input: z.string().describe("JSON string"),
173
+ path: z.string().describe("JSONPath expression e.g. $.store.book[*].author")
174
+ },
175
+ async ({ input, path }) => {
176
+ const result = evaluateJsonPath(input, path);
177
+ if (!result.valid) return text(`Error: ${result.error ?? "Invalid JSONPath"}`);
178
+ return text(JSON.stringify(result.results, null, 2));
179
+ }
180
+ );
181
+ server.tool(
182
+ "validate_json",
183
+ "Validate a JSON string against a JSON Schema (Draft-7). Returns pass/fail with error details.",
184
+ {
185
+ data: z.string().describe("JSON data to validate"),
186
+ schema: z.string().describe("JSON Schema string")
187
+ },
188
+ async ({ data, schema }) => {
189
+ let parsedSchema;
190
+ try {
191
+ parsedSchema = JSON.parse(schema);
192
+ } catch {
193
+ return errText("Invalid schema JSON");
194
+ }
195
+ const result = validateJsonString(data, parsedSchema);
196
+ if (checkErr(result)) return errText(result);
197
+ return text(toText(result));
198
+ }
199
+ );
200
+ server.tool(
201
+ "yaml_to_json",
202
+ "Convert a YAML string to JSON.",
203
+ { input: z.string().describe("YAML string") },
204
+ async ({ input }) => {
205
+ const result = yamlToJson(input);
206
+ if (checkErr(result)) return errText(result);
207
+ return text(result);
208
+ }
209
+ );
210
+ server.tool(
211
+ "json_to_yaml",
212
+ "Convert a JSON string to YAML.",
213
+ { input: z.string().describe("JSON string") },
214
+ async ({ input }) => {
215
+ const result = jsonToYaml(input);
216
+ if (checkErr(result)) return errText(result);
217
+ return text(result);
218
+ }
219
+ );
220
+ server.tool(
221
+ "json_to_typescript",
222
+ "Generate TypeScript interfaces from a JSON object.",
223
+ {
224
+ input: z.string().describe("JSON string"),
225
+ name: z.string().default("Root").describe("Root interface name")
226
+ },
227
+ async ({ input, name }) => {
228
+ const result = jsonToTypescript(input, name);
229
+ if (checkErr(result)) return errText(result);
230
+ return text(result);
231
+ }
232
+ );
233
+ server.tool(
234
+ "json_to_go",
235
+ "Generate Go struct definitions from a JSON object.",
236
+ {
237
+ input: z.string().describe("JSON string"),
238
+ name: z.string().default("Root").describe("Root struct name")
239
+ },
240
+ async ({ input, name }) => {
241
+ const result = jsonToGo(input, name);
242
+ if (checkErr(result)) return errText(result);
243
+ return text(result);
244
+ }
245
+ );
246
+ server.tool(
247
+ "json_to_python",
248
+ "Generate Python TypedDict, dataclass, or Pydantic models from a JSON object.",
249
+ {
250
+ input: z.string().describe("JSON string"),
251
+ style: z.enum(["typeddict", "dataclass", "pydantic"]).default("pydantic").describe("Output style")
252
+ },
253
+ async ({ input, style }) => {
254
+ const result = jsonToPython(input, style);
255
+ if (checkErr(result)) return errText(result);
256
+ return text(result);
257
+ }
258
+ );
259
+ server.tool(
260
+ "json_to_zod",
261
+ "Generate a Zod schema from a JSON object.",
262
+ { input: z.string().describe("JSON string") },
263
+ async ({ input }) => {
264
+ const result = jsonToZod(input);
265
+ if (checkErr(result)) return errText(result);
266
+ return text(result);
267
+ }
268
+ );
269
+ server.tool(
270
+ "base64_encode",
271
+ "Encode a string to Base64.",
272
+ { input: z.string().describe("String to encode") },
273
+ async ({ input }) => {
274
+ const result = encodeBase64(input);
275
+ return text(result.output);
276
+ }
277
+ );
278
+ server.tool(
279
+ "base64_decode",
280
+ "Decode a Base64 string.",
281
+ { input: z.string().describe("Base64 string to decode") },
282
+ async ({ input }) => {
283
+ const result = decodeBase64(input);
284
+ if (!result.ok) return text(`Error: invalid Base64`);
285
+ return text(result.output);
286
+ }
287
+ );
288
+ server.tool(
289
+ "url_encode",
290
+ "Percent-encode a URL or URL component.",
291
+ {
292
+ input: z.string().describe("String to encode"),
293
+ mode: z.enum(["component", "full", "form"]).default("component").describe("Encoding mode")
294
+ },
295
+ async ({ input, mode }) => {
296
+ const result = encodeUrl(input, mode);
297
+ return text(result.output);
298
+ }
299
+ );
300
+ server.tool(
301
+ "url_decode",
302
+ "Decode a percent-encoded URL or URL component.",
303
+ { input: z.string().describe("Encoded string to decode") },
304
+ async ({ input }) => {
305
+ const result = decodeUrl(input);
306
+ return text(result.output);
307
+ }
308
+ );
309
+ server.tool(
310
+ "html_entities_encode",
311
+ "Encode special characters as HTML entities (e.g. < \u2192 &lt;).",
312
+ { input: z.string().describe("HTML string to encode") },
313
+ async ({ input }) => text(encodeHtmlEntities(input))
314
+ );
315
+ server.tool(
316
+ "html_entities_decode",
317
+ "Decode HTML entities back to plain text (e.g. &lt; \u2192 <).",
318
+ { input: z.string().describe("String with HTML entities to decode") },
319
+ async ({ input }) => text(decodeHtmlEntities(input))
320
+ );
321
+ server.tool(
322
+ "base32_encode",
323
+ "Encode a string to Base32.",
324
+ {
325
+ input: z.string().describe("String to encode"),
326
+ alphabet: z.enum(["standard", "extendedHex", "crockford", "zbase32"]).default("standard")
327
+ },
328
+ async ({ input, alphabet }) => text(base32Encode(input, alphabet))
329
+ );
330
+ server.tool(
331
+ "base32_decode",
332
+ "Decode a Base32 string.",
333
+ {
334
+ input: z.string().describe("Base32 string to decode"),
335
+ alphabet: z.enum(["standard", "extendedHex", "crockford", "zbase32"]).default("standard")
336
+ },
337
+ async ({ input, alphabet }) => {
338
+ const result = base32Decode(input, alphabet);
339
+ if (checkErr(result)) return errText(result);
340
+ return text(result);
341
+ }
342
+ );
343
+ server.tool(
344
+ "hash",
345
+ "Hash a string using MD5, SHA-1, SHA-256, SHA-384, or SHA-512.",
346
+ {
347
+ input: z.string().describe("String to hash"),
348
+ algorithm: z.enum(["MD5", "SHA-1", "SHA-256", "SHA-384", "SHA-512"]).default("SHA-256")
349
+ },
350
+ async ({ input, algorithm }) => {
351
+ const result = await hashOne(algorithm, input);
352
+ return text(result);
353
+ }
354
+ );
355
+ server.tool(
356
+ "hash_all",
357
+ "Hash a string with all algorithms (MD5, SHA-1, SHA-256, SHA-384, SHA-512) at once.",
358
+ { input: z.string().describe("String to hash") },
359
+ async ({ input }) => {
360
+ const results = await hashAll(input);
361
+ const lines = results.map((r) => `${r.algorithm.padEnd(8)}: ${r.hex}`).join("\n");
362
+ return text(lines);
363
+ }
364
+ );
365
+ server.tool(
366
+ "bcrypt_hash",
367
+ "Hash a password using bcrypt.",
368
+ {
369
+ password: z.string().describe("Password to hash"),
370
+ rounds: z.number().int().min(8).max(14).default(10).describe("Salt rounds (8\u201314, default 10)")
371
+ },
372
+ async ({ password, rounds }) => {
373
+ const hash = await hashPassword(password, rounds);
374
+ return text(hash);
375
+ }
376
+ );
377
+ server.tool(
378
+ "bcrypt_verify",
379
+ "Verify a password against a bcrypt hash.",
380
+ {
381
+ password: z.string().describe("Plain-text password"),
382
+ hash: z.string().describe("bcrypt hash to verify against")
383
+ },
384
+ async ({ password, hash }) => {
385
+ const valid = await verifyPassword(password, hash);
386
+ return text(valid ? "Valid \u2014 password matches hash." : "Invalid \u2014 password does not match hash.");
387
+ }
388
+ );
389
+ server.tool(
390
+ "generate_uuid",
391
+ "Generate one or more UUIDs (v4 random or v7 time-ordered).",
392
+ {
393
+ version: z.enum(["v4", "v7"]).default("v4").describe("UUID version"),
394
+ count: z.number().int().min(1).max(100).default(1).describe("How many to generate")
395
+ },
396
+ async ({ version, count }) => {
397
+ const ver = version === "v4" ? "v4" : "v7";
398
+ if (count === 1) {
399
+ const result = generateUuid(ver);
400
+ return text(result.value);
401
+ }
402
+ const uuids = [];
403
+ for (let i = 0; i < count; i++) uuids.push(generateUuid(ver).value);
404
+ return text(uuids.join("\n"));
405
+ }
406
+ );
407
+ server.tool(
408
+ "generate_ulid",
409
+ "Generate a ULID (Universally Unique Lexicographically Sortable Identifier).",
410
+ { count: z.number().int().min(1).max(100).default(1) },
411
+ async ({ count }) => {
412
+ const ids = [];
413
+ for (let i = 0; i < count; i++) ids.push(generateUlid());
414
+ return text(ids.join("\n"));
415
+ }
416
+ );
417
+ server.tool(
418
+ "generate_password",
419
+ "Generate a cryptographically secure random password.",
420
+ {
421
+ length: z.number().int().min(8).max(128).default(16).describe("Password length"),
422
+ uppercase: z.boolean().default(true),
423
+ lowercase: z.boolean().default(true),
424
+ numbers: z.boolean().default(true),
425
+ symbols: z.boolean().default(true)
426
+ },
427
+ async ({ length, uppercase, lowercase, numbers, symbols }) => {
428
+ const result = generatePassword({ length, uppercase, lowercase, numbers, symbols });
429
+ return text(result);
430
+ }
431
+ );
432
+ server.tool(
433
+ "convert_case",
434
+ "Convert a string to a different case format: camelCase, snake_case, kebab-case, PascalCase, SCREAMING_SNAKE, Title Case, etc.",
435
+ {
436
+ input: z.string().describe("String to convert"),
437
+ format: z.enum([
438
+ "camelCase",
439
+ "PascalCase",
440
+ "snake_case",
441
+ "kebab-case",
442
+ "SCREAMING_SNAKE",
443
+ "Title Case",
444
+ "lowercase",
445
+ "UPPERCASE",
446
+ "dot.case",
447
+ "path/case"
448
+ ]).describe("Target case format")
449
+ },
450
+ async ({ input, format }) => text(convertCase(input, format))
451
+ );
452
+ server.tool(
453
+ "slugify",
454
+ "Convert text to a URL-friendly slug.",
455
+ {
456
+ input: z.string().describe("Text to slugify"),
457
+ separator: z.enum(["-", "_"]).default("-")
458
+ },
459
+ async ({ input, separator }) => text(slugify(input, separator))
460
+ );
461
+ server.tool(
462
+ "word_count",
463
+ "Count words, characters, sentences, and estimate reading time for a block of text.",
464
+ { input: z.string().describe("Text to analyze") },
465
+ async ({ input }) => {
466
+ const result = countWords(input);
467
+ return text(
468
+ `Words: ${result.words}
469
+ Characters: ${result.chars} (${result.charsNoSpaces} without spaces)
470
+ Lines: ${result.lines}
471
+ Sentences: ${result.sentences}
472
+ Reading: ~${result.readingTimeSec}s (~${Math.ceil(result.readingTimeSec / 60)} min)`
473
+ );
474
+ }
475
+ );
476
+ server.tool(
477
+ "escape_string",
478
+ "Escape special characters in a string for use in JSON, JavaScript, HTML, URL, or CSV.",
479
+ {
480
+ input: z.string().describe("String to escape"),
481
+ mode: z.enum(["json", "javascript", "html", "url", "csv"]).describe("Escape target format")
482
+ },
483
+ async ({ input, mode }) => text(escapeString(input, mode))
484
+ );
485
+ server.tool(
486
+ "unescape_string",
487
+ "Unescape special characters in a JSON, JavaScript, HTML, URL-encoded, or CSV string.",
488
+ {
489
+ input: z.string().describe("String to unescape"),
490
+ mode: z.enum(["json", "javascript", "html", "url", "csv"]).describe("Format to unescape from")
491
+ },
492
+ async ({ input, mode }) => text(unescapeString(input, mode))
493
+ );
494
+ server.tool(
495
+ "lorem_ipsum",
496
+ "Generate Lorem Ipsum placeholder text.",
497
+ {
498
+ unit: z.enum(["words", "sentences", "paragraphs"]).default("sentences"),
499
+ count: z.number().int().min(1).max(50).default(3)
500
+ },
501
+ async ({ unit, count }) => text(loremGenerate(unit, count))
502
+ );
503
+ server.tool(
504
+ "unix_timestamp",
505
+ "Convert a Unix timestamp (seconds or milliseconds) to a human-readable date.",
506
+ { timestamp: z.number().describe("Unix timestamp in seconds or milliseconds") },
507
+ async ({ timestamp }) => {
508
+ const result = fromUnix(timestamp);
509
+ return text(
510
+ `UTC: ${result.utc}
511
+ ISO: ${result.iso}
512
+ Local: ${result.local}
513
+ Unix: ${result.unix} (${result.ms ? "ms" : "s"})`
514
+ );
515
+ }
516
+ );
517
+ server.tool(
518
+ "parse_cron",
519
+ "Parse a cron expression and return a human-readable description.",
520
+ { expression: z.string().describe('Cron expression e.g. "0 9 * * MON-FRI"') },
521
+ async ({ expression }) => {
522
+ const result = parseCron(normalizeCron(expression));
523
+ if (!result.valid) return text(`Invalid cron: ${result.error}`);
524
+ return text(result.summary ?? "Valid cron expression");
525
+ }
526
+ );
527
+ server.tool(
528
+ "cron_next_runs",
529
+ "Show the next N scheduled run times for a cron expression.",
530
+ {
531
+ expression: z.string().describe("Cron expression"),
532
+ count: z.number().int().min(1).max(20).default(5)
533
+ },
534
+ async ({ expression, count }) => {
535
+ const result = getNextRuns(normalizeCron(expression), count);
536
+ if (checkErr(result)) return errText(result);
537
+ const runs = result;
538
+ return text(runs.map((d, i) => `${i + 1}. ${d.toISOString()}`).join("\n"));
539
+ }
540
+ );
541
+ server.tool(
542
+ "date_diff",
543
+ "Calculate the difference between two dates.",
544
+ {
545
+ from: z.string().describe("Start date (ISO 8601 or natural language)"),
546
+ to: z.string().describe("End date (ISO 8601 or natural language)")
547
+ },
548
+ async ({ from, to }) => {
549
+ const result = diffDates(from, to);
550
+ if (checkErr(result)) return errText(result);
551
+ const r = result;
552
+ return text(
553
+ `${r.years}y ${r.months}mo ${r.days}d ${r.hours}h
554
+ Total days: ${r.totalDays}`
555
+ );
556
+ }
557
+ );
558
+ server.tool(
559
+ "convert_timezone",
560
+ "Convert a date/time from one timezone to another.",
561
+ {
562
+ datetime: z.string().describe("Date-time string (ISO 8601)"),
563
+ from_tz: z.string().describe('Source IANA timezone e.g. "UTC"'),
564
+ to_tz: z.string().describe('Target IANA timezone e.g. "America/New_York"')
565
+ },
566
+ async ({ datetime, from_tz, to_tz }) => {
567
+ const result = convertTime(datetime, from_tz, to_tz);
568
+ if (checkErr(result)) return errText(result);
569
+ const r = result;
570
+ return text(r.result ?? toText(result));
571
+ }
572
+ );
573
+ server.tool(
574
+ "convert_bytes",
575
+ "Convert a byte value between B, KB, MB, GB, TB, KiB, MiB, GiB, TiB.",
576
+ {
577
+ value: z.number().describe("Numeric value to convert"),
578
+ from: z.enum(["B", "KB", "MB", "GB", "TB", "PB", "KiB", "MiB", "GiB", "TiB"]).describe("Source unit")
579
+ },
580
+ async ({ value, from }) => {
581
+ const result = convertBytes(value, from);
582
+ const lines = Object.entries(result).map(([unit, r]) => `${unit.padEnd(5)}: ${r.formatted}`).join("\n");
583
+ return text(lines);
584
+ }
585
+ );
586
+ server.tool(
587
+ "px_to_units",
588
+ "Convert a px value to rem, em, pt, and vw units.",
589
+ {
590
+ px: z.number().describe("Pixel value to convert"),
591
+ base_font_size: z.number().default(16).describe("Base font size in px (default 16)")
592
+ },
593
+ async ({ px, base_font_size }) => {
594
+ const result = pxToAll(px, base_font_size);
595
+ const lines = Object.entries(result).map(([unit, val]) => `${unit.padEnd(6)}: ${val}`).join("\n");
596
+ return text(lines);
597
+ }
598
+ );
599
+ server.tool(
600
+ "decode_jwt",
601
+ "Decode a JWT token and display header, payload, and expiry info. Does not verify signature.",
602
+ { token: z.string().describe("JWT token (3 dot-separated base64 segments)") },
603
+ async ({ token }) => {
604
+ const result = decodeJwt(token);
605
+ if (!result.ok) return text(`Error: ${result.error}`);
606
+ const lines = [];
607
+ lines.push("\u2500\u2500 Header \u2500\u2500");
608
+ lines.push(JSON.stringify(result.header, null, 2));
609
+ lines.push("\u2500\u2500 Payload \u2500\u2500");
610
+ lines.push(JSON.stringify(result.payload, null, 2));
611
+ if (result.expiresAt) {
612
+ const expired = result.expiresAt < /* @__PURE__ */ new Date();
613
+ lines.push(`\u2500\u2500 Expiry \u2500\u2500
614
+ ${result.expiresAt.toISOString()} (${expired ? "EXPIRED" : "valid"})`);
615
+ }
616
+ lines.push(`Algorithm: ${result.algorithm}`);
617
+ return text(lines.join("\n"));
618
+ }
619
+ );
620
+ server.tool(
621
+ "build_curl",
622
+ "Build a cURL command from URL, method, headers, and body.",
623
+ {
624
+ url: z.string().url().describe("Request URL"),
625
+ method: z.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]).default("GET"),
626
+ headers: z.record(z.string()).optional().describe("Key/value headers object"),
627
+ body: z.string().optional().describe("Request body string")
628
+ },
629
+ async ({ url, method, headers, body }) => {
630
+ const result = buildCurl({ url, method, headers: headers ? Object.entries(headers).map(([k, v]) => ({ key: k, value: v })) : [], body: body ?? "", bodyType: body ? "raw" : "none", auth: { type: "none" } });
631
+ return text(result);
632
+ }
633
+ );
634
+ server.tool(
635
+ "curl_to_code",
636
+ "Convert a cURL command to code: fetch, axios, python-requests, go-http, php-curl, or ruby-net-http.",
637
+ {
638
+ curl: z.string().describe("Full cURL command string"),
639
+ language: z.enum(["fetch", "axios", "python-requests", "go-http", "php-curl", "ruby-net-http"]).describe("Target language/library")
640
+ },
641
+ async ({ curl, language }) => {
642
+ const parsed = parseCurlCommand(curl);
643
+ if (checkErr(parsed)) return errText(parsed);
644
+ const result = convertToCode(parsed, language);
645
+ if (!result) return text("Error: could not convert cURL to code");
646
+ return text(result);
647
+ }
648
+ );
649
+ server.tool(
650
+ "convert_color",
651
+ "Parse a color (HEX, RGB, HSL, HSV, or named CSS color) and return all format conversions.",
652
+ { input: z.string().describe('Color value e.g. "#ff6b35", "rgb(255,107,53)", "hsl(18,100%,60%)"') },
653
+ async ({ input }) => {
654
+ const result = parseColor(input);
655
+ if (!result.valid) return text(`Error: cannot parse color "${input}"`);
656
+ const c = result;
657
+ return text(
658
+ `HEX: ${c.hex}
659
+ RGB: rgb(${c.rgb.r}, ${c.rgb.g}, ${c.rgb.b})
660
+ HSL: hsl(${c.hsl.h}, ${c.hsl.s}%, ${c.hsl.l}%)
661
+ HSV: hsv(${c.hsv.h}, ${c.hsv.s}%, ${c.hsv.v}%)`
662
+ );
663
+ }
664
+ );
665
+ server.tool(
666
+ "format_sql",
667
+ "Format a SQL query with proper indentation and keyword uppercasing.",
668
+ {
669
+ input: z.string().describe("SQL query to format"),
670
+ uppercase: z.boolean().default(true).describe("Uppercase SQL keywords")
671
+ },
672
+ async ({ input, uppercase }) => {
673
+ const result = formatSql(input, { uppercase });
674
+ return text(result);
675
+ }
676
+ );
677
+ server.tool(
678
+ "format_html",
679
+ "Format and indent HTML markup.",
680
+ {
681
+ input: z.string().describe("HTML string to format"),
682
+ indent: z.number().int().min(1).max(8).default(2)
683
+ },
684
+ async ({ input, indent }) => {
685
+ const result = formatHtml(input, indent);
686
+ if (checkErr(result)) return errText(result);
687
+ return text(result.output ?? toText(result));
688
+ }
689
+ );
690
+ server.tool(
691
+ "test_regex",
692
+ "Test a regular expression against an input string and return all matches with capture groups.",
693
+ {
694
+ pattern: z.string().describe("Regex pattern (without slashes)"),
695
+ flags: z.string().default("g").describe('Regex flags: g, i, m, s, u (default "g")'),
696
+ input: z.string().describe("String to test the pattern against")
697
+ },
698
+ async ({ pattern, flags, input }) => {
699
+ const result = testRegex(pattern, flags.split(""), input);
700
+ if (checkErr(result)) return errText(result);
701
+ const r = result;
702
+ if (r.matchCount === 0) return text("No matches found.");
703
+ const lines = [`${r.matchCount} match${r.matchCount > 1 ? "es" : ""}:`];
704
+ r.matches.forEach((m, i) => {
705
+ lines.push(` ${i + 1}. "${m.fullMatch}" at index ${m.index}${m.groups.length ? ` (groups: ${m.groups.join(", ")})` : ""}`);
706
+ });
707
+ return text(lines.join("\n"));
708
+ }
709
+ );
710
+ server.tool(
711
+ "validate_yaml",
712
+ "Validate YAML syntax and return stats or error details.",
713
+ { input: z.string().describe("YAML string to validate") },
714
+ async ({ input }) => {
715
+ const result = validateYaml(input);
716
+ if (!result.valid) return text(`Invalid YAML`);
717
+ return text(`Valid YAML
718
+ Documents: ${result.documentCount}
719
+ Keys: ${result.keyCount}`);
720
+ }
721
+ );
722
+ server.tool(
723
+ "toml_to_json",
724
+ "Convert a TOML string to JSON.",
725
+ { input: z.string().describe("TOML string") },
726
+ async ({ input }) => {
727
+ const result = tomlToJson(input);
728
+ if (checkErr(result)) return errText(result);
729
+ return text(result);
730
+ }
731
+ );
732
+ server.tool(
733
+ "xml_to_json",
734
+ "Convert an XML document to JSON.",
735
+ { input: z.string().describe("XML string") },
736
+ async ({ input }) => {
737
+ const result = xmlToJson(input);
738
+ if (checkErr(result)) return errText(result);
739
+ return text(result);
740
+ }
741
+ );
742
+ var transport = new StdioServerTransport();
743
+ await server.connect(transport);
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@utilix-tech/mcp",
3
+ "version": "0.1.0",
4
+ "description": "MCP server exposing 107 Utilix developer tools to Claude, Cursor, VS Code Copilot, and any MCP-compatible AI assistant.",
5
+ "author": "Utilix <hello@utilix.tech>",
6
+ "license": "MIT",
7
+ "homepage": "https://utilix.tech/docs",
8
+ "keywords": [
9
+ "mcp",
10
+ "model-context-protocol",
11
+ "developer-tools",
12
+ "utilities",
13
+ "claude",
14
+ "cursor",
15
+ "copilot",
16
+ "json",
17
+ "encoding",
18
+ "hashing",
19
+ "utilix"
20
+ ],
21
+ "engines": {
22
+ "node": ">=18"
23
+ },
24
+ "type": "module",
25
+ "main": "./dist/index.js",
26
+ "bin": {
27
+ "utilix-mcp": "./dist/index.js"
28
+ },
29
+ "files": [
30
+ "dist",
31
+ "smithery.yaml",
32
+ "README.md"
33
+ ],
34
+ "scripts": {
35
+ "build": "tsup",
36
+ "dev": "tsup --watch",
37
+ "start": "node dist/index.js",
38
+ "prepublishOnly": "npm run build"
39
+ },
40
+ "dependencies": {
41
+ "@modelcontextprotocol/sdk": "^1.12.0",
42
+ "@utilix-tech/sdk": "^0.1.4",
43
+ "zod": "^3.23.0"
44
+ },
45
+ "devDependencies": {
46
+ "@types/node": "^20.0.0",
47
+ "tsup": "^8.0.0",
48
+ "typescript": "^5.4.0"
49
+ }
50
+ }
package/smithery.yaml ADDED
@@ -0,0 +1,63 @@
1
+ name: utilix
2
+ description: 107 developer utility tools — JSON, hashing, encoding, JWT, UUID, cron, color, SQL, regex and more. All run locally, no API key required.
3
+ homepage: https://utilix.tech
4
+ license: MIT
5
+
6
+ startCommand:
7
+ type: stdio
8
+ command: npx
9
+ args:
10
+ - "@utilix-tech/mcp"
11
+
12
+ tools:
13
+ - format_json
14
+ - minify_json
15
+ - diff_json
16
+ - json_to_csv
17
+ - csv_to_json
18
+ - jsonpath
19
+ - validate_json
20
+ - yaml_to_json
21
+ - json_to_yaml
22
+ - json_to_typescript
23
+ - json_to_go
24
+ - json_to_python
25
+ - json_to_zod
26
+ - base64_encode
27
+ - base64_decode
28
+ - url_encode
29
+ - url_decode
30
+ - html_entities_encode
31
+ - html_entities_decode
32
+ - base32_encode
33
+ - base32_decode
34
+ - hash
35
+ - hash_all
36
+ - bcrypt_hash
37
+ - bcrypt_verify
38
+ - generate_uuid
39
+ - generate_ulid
40
+ - generate_password
41
+ - convert_case
42
+ - slugify
43
+ - word_count
44
+ - escape_string
45
+ - unescape_string
46
+ - lorem_ipsum
47
+ - unix_timestamp
48
+ - parse_cron
49
+ - cron_next_runs
50
+ - date_diff
51
+ - convert_timezone
52
+ - convert_bytes
53
+ - px_to_units
54
+ - decode_jwt
55
+ - build_curl
56
+ - curl_to_code
57
+ - convert_color
58
+ - format_sql
59
+ - format_html
60
+ - test_regex
61
+ - validate_yaml
62
+ - toml_to_json
63
+ - xml_to_json