@utilix-tech/mcp 0.1.0 → 0.3.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 +35 -8
- package/dist/index.js +410 -7
- package/package.json +2 -2
- package/smithery.yaml +20 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @utilix-tech/mcp
|
|
2
2
|
|
|
3
|
-
MCP server exposing
|
|
3
|
+
MCP server exposing 70 developer utility tools to Claude, Cursor, VS Code Copilot, and any [Model Context Protocol](https://modelcontextprotocol.io) compatible AI assistant.
|
|
4
4
|
|
|
5
5
|
No API key required. All tools run locally.
|
|
6
6
|
|
|
@@ -91,14 +91,16 @@ Add to `.vscode/mcp.json`:
|
|
|
91
91
|
| `bcrypt_hash` | Hash a password using bcrypt |
|
|
92
92
|
| `bcrypt_verify` | Verify a password against a bcrypt hash |
|
|
93
93
|
|
|
94
|
-
### Generators (
|
|
94
|
+
### Generators (5 tools)
|
|
95
95
|
| Tool | Description |
|
|
96
96
|
|------|-------------|
|
|
97
97
|
| `generate_uuid` | Generate UUID v4 or v7 |
|
|
98
98
|
| `generate_ulid` | Generate a ULID |
|
|
99
99
|
| `generate_password` | Generate a secure random password |
|
|
100
|
+
| `generate_qr` | Generate a QR code as an SVG string |
|
|
101
|
+
| `generate_random_data` | Generate random mock data: names, emails, UUIDs, IPs, etc. |
|
|
100
102
|
|
|
101
|
-
### Text (
|
|
103
|
+
### Text (9 tools)
|
|
102
104
|
| Tool | Description |
|
|
103
105
|
|------|-------------|
|
|
104
106
|
| `convert_case` | Convert text case: camelCase, snake_case, PascalCase, etc. |
|
|
@@ -107,8 +109,11 @@ Add to `.vscode/mcp.json`:
|
|
|
107
109
|
| `escape_string` | Escape characters for JSON, JavaScript, HTML, URL, or CSV |
|
|
108
110
|
| `unescape_string` | Unescape JSON, JavaScript, HTML, URL, or CSV strings |
|
|
109
111
|
| `lorem_ipsum` | Generate Lorem Ipsum placeholder text |
|
|
112
|
+
| `text_diff` | Diff two text blocks line-by-line |
|
|
113
|
+
| `html_to_markdown` | Convert HTML to Markdown |
|
|
114
|
+
| `line_operations` | Sort, deduplicate, reverse, shuffle, trim, or number lines |
|
|
110
115
|
|
|
111
|
-
### Time (
|
|
116
|
+
### Time (5 tools)
|
|
112
117
|
| Tool | Description |
|
|
113
118
|
|------|-------------|
|
|
114
119
|
| `unix_timestamp` | Convert a Unix timestamp to human-readable date |
|
|
@@ -123,31 +128,53 @@ Add to `.vscode/mcp.json`:
|
|
|
123
128
|
| `convert_bytes` | Convert between B, KB, MB, GB, TB, KiB, MiB, GiB, TiB |
|
|
124
129
|
| `px_to_units` | Convert pixels to rem, em, pt, and vw |
|
|
125
130
|
|
|
126
|
-
### API / Network (
|
|
131
|
+
### API / Network (4 tools)
|
|
127
132
|
| Tool | Description |
|
|
128
133
|
|------|-------------|
|
|
129
134
|
| `decode_jwt` | Decode a JWT — header, payload, and expiry |
|
|
130
135
|
| `build_curl` | Build a cURL command from method, URL, headers, and body |
|
|
131
136
|
| `curl_to_code` | Convert a cURL command to fetch, axios, Python, Go, PHP, or Ruby |
|
|
137
|
+
| `cors_headers` | Generate CORS response headers for a configuration |
|
|
132
138
|
|
|
133
|
-
### Color (
|
|
139
|
+
### Color (4 tools)
|
|
134
140
|
| Tool | Description |
|
|
135
141
|
|------|-------------|
|
|
136
142
|
| `convert_color` | Convert a color between HEX, RGB, HSL, and HSV |
|
|
143
|
+
| `check_contrast` | Check WCAG contrast ratio between two colors |
|
|
144
|
+
| `color_palette` | Generate a color palette from a base color |
|
|
145
|
+
| `color_shades` | Generate shades or tints from a base color |
|
|
137
146
|
|
|
138
|
-
### Code (
|
|
147
|
+
### Code (7 tools)
|
|
139
148
|
| Tool | Description |
|
|
140
149
|
|------|-------------|
|
|
141
150
|
| `format_sql` | Format a SQL query with proper indentation |
|
|
142
151
|
| `format_html` | Format and indent HTML markup |
|
|
143
152
|
| `test_regex` | Test a regular expression and return all matches |
|
|
153
|
+
| `minify_js` | Minify JavaScript by removing whitespace and comments |
|
|
154
|
+
| `format_graphql` | Format or minify a GraphQL query or schema |
|
|
155
|
+
| `parse_env` | Parse a .env file and return all key-value pairs |
|
|
156
|
+
| `parse_docker_image` | Parse a Docker image reference into its components |
|
|
144
157
|
|
|
145
|
-
### Data (
|
|
158
|
+
### Data (4 tools)
|
|
146
159
|
| Tool | Description |
|
|
147
160
|
|------|-------------|
|
|
148
161
|
| `validate_yaml` | Validate YAML syntax |
|
|
149
162
|
| `toml_to_json` | Convert TOML to JSON |
|
|
150
163
|
| `xml_to_json` | Convert XML to JSON |
|
|
164
|
+
| `parse_csv` | Parse a CSV string and return the data as JSON |
|
|
165
|
+
|
|
166
|
+
### CSS (2 tools)
|
|
167
|
+
| Tool | Description |
|
|
168
|
+
|------|-------------|
|
|
169
|
+
| `minify_css` | Minify CSS by removing whitespace and comments |
|
|
170
|
+
| `css_gradient` | Generate a CSS gradient from color stops |
|
|
171
|
+
|
|
172
|
+
### Misc (3 tools)
|
|
173
|
+
| Tool | Description |
|
|
174
|
+
|------|-------------|
|
|
175
|
+
| `number_to_words` | Convert a number to English words, Roman numerals, or ordinal |
|
|
176
|
+
| `unicode_inspect` | Inspect Unicode code points of a string |
|
|
177
|
+
| `optimize_svg` | Optimize and minify an SVG string |
|
|
151
178
|
|
|
152
179
|
## Requirements
|
|
153
180
|
|
package/dist/index.js
CHANGED
|
@@ -7,7 +7,6 @@ import { z } from "zod";
|
|
|
7
7
|
import {
|
|
8
8
|
parseJson,
|
|
9
9
|
minifyJson,
|
|
10
|
-
diffJson,
|
|
11
10
|
jsonToCsv,
|
|
12
11
|
csvToJson,
|
|
13
12
|
evaluateJsonPath,
|
|
@@ -38,7 +37,9 @@ import {
|
|
|
38
37
|
import {
|
|
39
38
|
generateUuid,
|
|
40
39
|
generatePassword,
|
|
41
|
-
generateUlid
|
|
40
|
+
generateUlid,
|
|
41
|
+
generateData,
|
|
42
|
+
generateQrSvg
|
|
42
43
|
} from "@utilix-tech/sdk/generators";
|
|
43
44
|
import {
|
|
44
45
|
countWords,
|
|
@@ -46,7 +47,10 @@ import {
|
|
|
46
47
|
slugify,
|
|
47
48
|
generate as loremGenerate,
|
|
48
49
|
escapeString,
|
|
49
|
-
unescapeString
|
|
50
|
+
unescapeString,
|
|
51
|
+
diffLines,
|
|
52
|
+
htmlToMarkdown,
|
|
53
|
+
applyOps
|
|
50
54
|
} from "@utilix-tech/sdk/text";
|
|
51
55
|
import {
|
|
52
56
|
fromUnix,
|
|
@@ -60,11 +64,16 @@ import {
|
|
|
60
64
|
decodeJwt,
|
|
61
65
|
buildCurl,
|
|
62
66
|
parseCurlCommand,
|
|
63
|
-
convertToCode
|
|
67
|
+
convertToCode,
|
|
68
|
+
generateCors,
|
|
69
|
+
DEFAULT_CORS_CONFIG
|
|
64
70
|
} 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";
|
|
71
|
+
import { parseColor, checkContrast, generatePalette, generateShades } from "@utilix-tech/sdk/color";
|
|
72
|
+
import { formatSql, formatHtml, testRegex, minifyJs, formatGql, parseDockerImage, parseEnv as parseEnvFile } from "@utilix-tech/sdk/code";
|
|
73
|
+
import { validateYaml, tomlToJson, xmlToJson, parseCsv } from "@utilix-tech/sdk/data";
|
|
74
|
+
import { generateGradient, minifyCss } from "@utilix-tech/sdk/css";
|
|
75
|
+
import { optimizeSvg, charToCodePoint, numberToWords } from "@utilix-tech/sdk/misc";
|
|
76
|
+
import { estimateTokens, estimateCost, detectPii, redactPii, detectSecrets, detectPromptInjection, repairJson, trimToTokens, chunkText, extractUrls, sanitizeHtml, flattenJson, unflattenJson, mergeJson, extractJson, deduplicateLines, extractKeywords, validateJsonSchema, diffJson, extractTables, extractEntities, compressHtml, compressMarkdown, compressJson, rerankChunks, scoreRelevance, expandQuery, summarizeForLlm } from "@utilix-tech/sdk/ai_agent";
|
|
68
77
|
var CRON_DAY_MAP = {
|
|
69
78
|
SUN: "0",
|
|
70
79
|
MON: "1",
|
|
@@ -739,5 +748,399 @@ server.tool(
|
|
|
739
748
|
return text(result);
|
|
740
749
|
}
|
|
741
750
|
);
|
|
751
|
+
server.tool(
|
|
752
|
+
"text_diff",
|
|
753
|
+
"Diff two text blocks line-by-line and show added, removed, and unchanged lines.",
|
|
754
|
+
{
|
|
755
|
+
left: z.string().describe("Original text"),
|
|
756
|
+
right: z.string().describe("New/modified text")
|
|
757
|
+
},
|
|
758
|
+
async ({ left, right }) => {
|
|
759
|
+
const r = diffLines(left, right);
|
|
760
|
+
const lines = r.lines.map((l) => {
|
|
761
|
+
const prefix = l.type === "added" ? "+ " : l.type === "removed" ? "- " : " ";
|
|
762
|
+
return `${prefix}${l.text}`;
|
|
763
|
+
});
|
|
764
|
+
return text(`${r.addedCount} added, ${r.removedCount} removed, ${r.unchangedCount} unchanged
|
|
765
|
+
|
|
766
|
+
${lines.join("\n")}`);
|
|
767
|
+
}
|
|
768
|
+
);
|
|
769
|
+
server.tool(
|
|
770
|
+
"html_to_markdown",
|
|
771
|
+
"Convert HTML to Markdown.",
|
|
772
|
+
{ input: z.string().describe("HTML string to convert") },
|
|
773
|
+
async ({ input }) => text(htmlToMarkdown(input))
|
|
774
|
+
);
|
|
775
|
+
server.tool(
|
|
776
|
+
"line_operations",
|
|
777
|
+
"Sort, deduplicate, reverse, shuffle, trim, or number lines of text.",
|
|
778
|
+
{
|
|
779
|
+
input: z.string().describe("Multi-line text"),
|
|
780
|
+
operation: z.enum(["sort-asc", "sort-desc", "sort-natural", "reverse", "dedupe", "dedupe-ci", "shuffle", "trim", "remove-empty", "number-lines"]).describe("Operation to apply")
|
|
781
|
+
},
|
|
782
|
+
async ({ input, operation }) => text(applyOps(input, [operation]))
|
|
783
|
+
);
|
|
784
|
+
server.tool(
|
|
785
|
+
"number_to_words",
|
|
786
|
+
"Convert a number to its English words representation, Roman numerals, or ordinal form.",
|
|
787
|
+
{
|
|
788
|
+
number: z.number().describe("Number to convert"),
|
|
789
|
+
format: z.enum(["words", "roman", "ordinal"]).default("words").describe("Output format")
|
|
790
|
+
},
|
|
791
|
+
async ({ number: n, format }) => {
|
|
792
|
+
if (format === "roman") {
|
|
793
|
+
const { numberToRoman } = await import("@utilix-tech/sdk/misc");
|
|
794
|
+
const r = numberToRoman(n);
|
|
795
|
+
return typeof r === "string" ? text(r) : text(`Error: ${r}`);
|
|
796
|
+
}
|
|
797
|
+
if (format === "ordinal") {
|
|
798
|
+
const { numberToOrdinal } = await import("@utilix-tech/sdk/misc");
|
|
799
|
+
return text(numberToOrdinal(n));
|
|
800
|
+
}
|
|
801
|
+
return text(numberToWords(n));
|
|
802
|
+
}
|
|
803
|
+
);
|
|
804
|
+
server.tool(
|
|
805
|
+
"unicode_inspect",
|
|
806
|
+
"Inspect the Unicode code point(s) of a character or string.",
|
|
807
|
+
{ input: z.string().min(1).describe("Character or string to inspect") },
|
|
808
|
+
async ({ input }) => {
|
|
809
|
+
const chars = [...input].slice(0, 50);
|
|
810
|
+
const lines = chars.map((c) => {
|
|
811
|
+
const cp = charToCodePoint(c);
|
|
812
|
+
const hex = cp.toString(16).toUpperCase().padStart(4, "0");
|
|
813
|
+
return `U+${hex} ${JSON.stringify(c)} (decimal ${cp})`;
|
|
814
|
+
});
|
|
815
|
+
return text(lines.join("\n"));
|
|
816
|
+
}
|
|
817
|
+
);
|
|
818
|
+
server.tool(
|
|
819
|
+
"minify_css",
|
|
820
|
+
"Minify CSS by removing whitespace and comments.",
|
|
821
|
+
{ input: z.string().describe("CSS to minify") },
|
|
822
|
+
async ({ input }) => {
|
|
823
|
+
const r = minifyCss(input);
|
|
824
|
+
return text(`${r.output}
|
|
825
|
+
|
|
826
|
+
(${r.originalSize}B \u2192 ${r.outputSize}B, saved ${r.savingsPercent}%)`);
|
|
827
|
+
}
|
|
828
|
+
);
|
|
829
|
+
server.tool(
|
|
830
|
+
"css_gradient",
|
|
831
|
+
"Generate a CSS gradient from stops.",
|
|
832
|
+
{
|
|
833
|
+
type: z.enum(["linear", "radial", "conic"]).default("linear").describe("Gradient type"),
|
|
834
|
+
angle: z.number().default(90).describe("Angle in degrees (linear only)"),
|
|
835
|
+
stops: z.array(z.object({
|
|
836
|
+
color: z.string().describe("CSS color value"),
|
|
837
|
+
position: z.number().min(0).max(100).describe("Position 0\u2013100")
|
|
838
|
+
})).min(2).describe("Color stops")
|
|
839
|
+
},
|
|
840
|
+
async ({ type, angle, stops }) => {
|
|
841
|
+
const css = generateGradient({ type, angle, stops });
|
|
842
|
+
return text(css);
|
|
843
|
+
}
|
|
844
|
+
);
|
|
845
|
+
server.tool(
|
|
846
|
+
"check_contrast",
|
|
847
|
+
"Check WCAG contrast ratio between two colors.",
|
|
848
|
+
{
|
|
849
|
+
foreground: z.string().describe("Foreground color (HEX, RGB, or named)"),
|
|
850
|
+
background: z.string().describe("Background color (HEX, RGB, or named)")
|
|
851
|
+
},
|
|
852
|
+
async ({ foreground, background }) => {
|
|
853
|
+
const r = checkContrast(foreground, background);
|
|
854
|
+
return text(
|
|
855
|
+
`Ratio: ${r.ratioString}
|
|
856
|
+
Level: ${r.level}
|
|
857
|
+
AA normal: ${r.aa.normal ? "\u2713" : "\u2717"} AA large: ${r.aa.large ? "\u2713" : "\u2717"} AA UI: ${r.aa.ui ? "\u2713" : "\u2717"}
|
|
858
|
+
AAA normal: ${r.aaa.normal ? "\u2713" : "\u2717"} AAA large: ${r.aaa.large ? "\u2713" : "\u2717"}`
|
|
859
|
+
);
|
|
860
|
+
}
|
|
861
|
+
);
|
|
862
|
+
server.tool(
|
|
863
|
+
"color_palette",
|
|
864
|
+
"Generate a color palette (analogous, complementary, triadic, etc.) from a base color.",
|
|
865
|
+
{
|
|
866
|
+
color: z.string().describe("Base color in HEX format"),
|
|
867
|
+
type: z.enum(["analogous", "complementary", "triadic", "tetradic", "split-complementary"]).default("analogous")
|
|
868
|
+
},
|
|
869
|
+
async ({ color, type }) => {
|
|
870
|
+
const r = generatePalette(color, type);
|
|
871
|
+
const colors = r.colors;
|
|
872
|
+
return text(colors.map((c) => `${c.hex} ${c.label}`).join("\n"));
|
|
873
|
+
}
|
|
874
|
+
);
|
|
875
|
+
server.tool(
|
|
876
|
+
"color_shades",
|
|
877
|
+
"Generate a scale of shades (darker) or tints (lighter) from a base color.",
|
|
878
|
+
{
|
|
879
|
+
color: z.string().describe("Base color in HEX format"),
|
|
880
|
+
count: z.number().int().min(2).max(20).default(5).describe("Number of shades to generate")
|
|
881
|
+
},
|
|
882
|
+
async ({ color, count }) => {
|
|
883
|
+
const shades = generateShades(color, count);
|
|
884
|
+
return text(shades.join("\n"));
|
|
885
|
+
}
|
|
886
|
+
);
|
|
887
|
+
server.tool(
|
|
888
|
+
"cors_headers",
|
|
889
|
+
"Generate CORS response headers for a given configuration.",
|
|
890
|
+
{
|
|
891
|
+
origins: z.array(z.string()).default(["*"]).describe("Allowed origins"),
|
|
892
|
+
methods: z.array(z.string()).default(["GET", "POST", "PUT", "DELETE", "OPTIONS"]).describe("Allowed HTTP methods"),
|
|
893
|
+
headers: z.array(z.string()).default(["Content-Type", "Authorization"]).describe("Allowed request headers"),
|
|
894
|
+
credentials: z.boolean().default(false).describe("Allow credentials"),
|
|
895
|
+
max_age: z.number().int().default(86400).describe("Preflight cache duration in seconds")
|
|
896
|
+
},
|
|
897
|
+
async ({ origins, methods, headers: hdrs, credentials, max_age }) => {
|
|
898
|
+
const r = generateCors({
|
|
899
|
+
...DEFAULT_CORS_CONFIG,
|
|
900
|
+
allowedOrigins: origins,
|
|
901
|
+
allowedMethods: methods,
|
|
902
|
+
allowedHeaders: hdrs,
|
|
903
|
+
allowCredentials: credentials,
|
|
904
|
+
maxAge: max_age
|
|
905
|
+
});
|
|
906
|
+
const headerLines = Object.entries(r.headers).map(([k, v]) => `${k}: ${v}`).join("\n");
|
|
907
|
+
return text(`Headers:
|
|
908
|
+
${headerLines}
|
|
909
|
+
|
|
910
|
+
Express:
|
|
911
|
+
${r.expressCode}`);
|
|
912
|
+
}
|
|
913
|
+
);
|
|
914
|
+
server.tool(
|
|
915
|
+
"minify_js",
|
|
916
|
+
"Minify JavaScript by removing whitespace and comments.",
|
|
917
|
+
{ input: z.string().describe("JavaScript code to minify") },
|
|
918
|
+
async ({ input }) => {
|
|
919
|
+
const r = minifyJs(input);
|
|
920
|
+
return text(`${r.minified}
|
|
921
|
+
|
|
922
|
+
(${r.originalSize}B \u2192 ${r.minifiedSize}B, saved ${r.savedBytes}B)`);
|
|
923
|
+
}
|
|
924
|
+
);
|
|
925
|
+
server.tool(
|
|
926
|
+
"format_graphql",
|
|
927
|
+
"Format or minify a GraphQL query or schema.",
|
|
928
|
+
{
|
|
929
|
+
input: z.string().describe("GraphQL query or schema string"),
|
|
930
|
+
minify: z.boolean().default(false).describe("Minify instead of format")
|
|
931
|
+
},
|
|
932
|
+
async ({ input, minify }) => {
|
|
933
|
+
if (minify) {
|
|
934
|
+
const { minifyGql } = await import("@utilix-tech/sdk/code");
|
|
935
|
+
const r2 = minifyGql(input);
|
|
936
|
+
if (!r2.valid) return text("Error: invalid GraphQL");
|
|
937
|
+
return text(r2.output);
|
|
938
|
+
}
|
|
939
|
+
const r = formatGql(input);
|
|
940
|
+
if (!r.valid) return text("Error: invalid GraphQL");
|
|
941
|
+
return text(r.output);
|
|
942
|
+
}
|
|
943
|
+
);
|
|
944
|
+
server.tool(
|
|
945
|
+
"parse_env",
|
|
946
|
+
"Parse a .env file and return all key-value pairs with validation.",
|
|
947
|
+
{ input: z.string().describe(".env file contents") },
|
|
948
|
+
async ({ input }) => {
|
|
949
|
+
const r = parseEnvFile(input);
|
|
950
|
+
if (r.errors.length) {
|
|
951
|
+
return text(`Errors:
|
|
952
|
+
${r.errors.map((e) => ` Line ${e.line}: ${e.message}`).join("\n")}
|
|
953
|
+
|
|
954
|
+
Parsed ${r.entries.length} entries.`);
|
|
955
|
+
}
|
|
956
|
+
const lines = r.entries.map((e) => `${e.key}=${e.value}`);
|
|
957
|
+
return text(`${lines.join("\n")}
|
|
958
|
+
|
|
959
|
+
(${r.entries.length} entries, ${r.comments.length} comments)`);
|
|
960
|
+
}
|
|
961
|
+
);
|
|
962
|
+
server.tool(
|
|
963
|
+
"parse_docker_image",
|
|
964
|
+
"Parse a Docker image reference into registry, namespace, repository, tag, and digest.",
|
|
965
|
+
{ input: z.string().describe('Docker image reference e.g. "nginx:1.21-alpine"') },
|
|
966
|
+
async ({ input }) => {
|
|
967
|
+
const r = parseDockerImage(input);
|
|
968
|
+
return text(
|
|
969
|
+
`Registry: ${r.registry}
|
|
970
|
+
Namespace: ${r.namespace}
|
|
971
|
+
Repository: ${r.repository}
|
|
972
|
+
Tag: ${r.tag || "latest"}
|
|
973
|
+
Digest: ${r.digest || "none"}
|
|
974
|
+
Official: ${r.isOfficial}`
|
|
975
|
+
);
|
|
976
|
+
}
|
|
977
|
+
);
|
|
978
|
+
server.tool(
|
|
979
|
+
"generate_qr",
|
|
980
|
+
"Generate a QR code as an SVG string.",
|
|
981
|
+
{
|
|
982
|
+
input: z.string().describe("Text or URL to encode"),
|
|
983
|
+
size: z.number().int().min(100).max(1e3).default(300).describe("SVG size in px")
|
|
984
|
+
},
|
|
985
|
+
async ({ input, size }) => {
|
|
986
|
+
const svg = await generateQrSvg(input, { size });
|
|
987
|
+
return text(svg);
|
|
988
|
+
}
|
|
989
|
+
);
|
|
990
|
+
server.tool(
|
|
991
|
+
"generate_random_data",
|
|
992
|
+
"Generate random mock data: names, emails, UUIDs, phone numbers, dates, colors, IPs, etc.",
|
|
993
|
+
{
|
|
994
|
+
type: z.enum(["name", "email", "uuid", "phone", "date", "number", "boolean", "color", "url", "ipv4", "sentence", "paragraph"]).describe("Data type to generate"),
|
|
995
|
+
count: z.number().int().min(1).max(50).default(5).describe("Number of items")
|
|
996
|
+
},
|
|
997
|
+
async ({ type, count }) => {
|
|
998
|
+
const items = generateData({ type, count });
|
|
999
|
+
const valid = items.filter(Boolean);
|
|
1000
|
+
if (!valid.length) return text("Error: could not generate data for this type");
|
|
1001
|
+
return text(valid.join("\n"));
|
|
1002
|
+
}
|
|
1003
|
+
);
|
|
1004
|
+
server.tool(
|
|
1005
|
+
"parse_csv",
|
|
1006
|
+
"Parse a CSV string and return the data as JSON.",
|
|
1007
|
+
{
|
|
1008
|
+
input: z.string().describe("CSV content"),
|
|
1009
|
+
delimiter: z.string().default(",").describe("Field delimiter")
|
|
1010
|
+
},
|
|
1011
|
+
async ({ input, delimiter }) => {
|
|
1012
|
+
const r = parseCsv(input, { delimiter });
|
|
1013
|
+
return text(JSON.stringify({ headers: r.headers, rows: r.rows, totalRows: r.totalRows }, null, 2));
|
|
1014
|
+
}
|
|
1015
|
+
);
|
|
1016
|
+
server.tool(
|
|
1017
|
+
"optimize_svg",
|
|
1018
|
+
"Optimize and minify an SVG string.",
|
|
1019
|
+
{ input: z.string().describe("SVG content to optimize") },
|
|
1020
|
+
async ({ input }) => {
|
|
1021
|
+
const result = optimizeSvg(input);
|
|
1022
|
+
const original = new TextEncoder().encode(input).length;
|
|
1023
|
+
const optimized = new TextEncoder().encode(result).length;
|
|
1024
|
+
const saved = Math.round((1 - optimized / original) * 100);
|
|
1025
|
+
return text(`${result}
|
|
1026
|
+
|
|
1027
|
+
(${original}B \u2192 ${optimized}B, saved ${saved}%)`);
|
|
1028
|
+
}
|
|
1029
|
+
);
|
|
1030
|
+
server.tool(
|
|
1031
|
+
"estimate_tokens",
|
|
1032
|
+
"Estimate the token count and API cost for a text string across popular LLMs (GPT-4o, Claude, Gemini, etc.).",
|
|
1033
|
+
{
|
|
1034
|
+
text: z.string().describe("Text to estimate tokens for"),
|
|
1035
|
+
model: z.string().optional().describe("Model slug (e.g. gpt-4o, claude-sonnet-4) \u2014 omit for all models"),
|
|
1036
|
+
output_tokens: z.number().int().min(0).optional().describe("Expected output tokens (defaults to same as input)")
|
|
1037
|
+
},
|
|
1038
|
+
async ({ text: text2, model, output_tokens }) => {
|
|
1039
|
+
const est = estimateTokens(text2);
|
|
1040
|
+
const result = estimateCost(est.tokens, output_tokens, model);
|
|
1041
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1042
|
+
}
|
|
1043
|
+
);
|
|
1044
|
+
server.tool(
|
|
1045
|
+
"detect_pii",
|
|
1046
|
+
"Detect personally identifiable information (emails, phones, SSNs, credit cards, IPs, IBANs, crypto wallets) in text.",
|
|
1047
|
+
{
|
|
1048
|
+
text: z.string().describe("Text to scan for PII"),
|
|
1049
|
+
redact: z.boolean().default(false).describe("If true, return redacted text instead of findings list"),
|
|
1050
|
+
replacement: z.string().default("[REDACTED]").describe("Replacement string when redacting")
|
|
1051
|
+
},
|
|
1052
|
+
async ({ text: text2, redact, replacement }) => {
|
|
1053
|
+
const result = redact ? redactPii(text2, replacement) : detectPii(text2);
|
|
1054
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1055
|
+
}
|
|
1056
|
+
);
|
|
1057
|
+
server.tool(
|
|
1058
|
+
"detect_secrets",
|
|
1059
|
+
"Scan text for leaked API keys, tokens, and credentials (OpenAI, Anthropic, AWS, GitHub, Stripe, etc.).",
|
|
1060
|
+
{ text: z.string().describe("Text or code to scan for secrets") },
|
|
1061
|
+
async ({ text: text2 }) => {
|
|
1062
|
+
const result = detectSecrets(text2);
|
|
1063
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1064
|
+
}
|
|
1065
|
+
);
|
|
1066
|
+
server.tool(
|
|
1067
|
+
"detect_prompt_injection",
|
|
1068
|
+
"Detect prompt injection and jailbreak attempts in user-provided text before passing to an LLM.",
|
|
1069
|
+
{ text: z.string().describe("User-provided text to check for injection attempts") },
|
|
1070
|
+
async ({ text: text2 }) => {
|
|
1071
|
+
const result = detectPromptInjection(text2);
|
|
1072
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1073
|
+
}
|
|
1074
|
+
);
|
|
1075
|
+
server.tool(
|
|
1076
|
+
"repair_json",
|
|
1077
|
+
"Fix malformed JSON: unquoted keys, single quotes, trailing commas, JS comments, undefined/NaN values, unclosed brackets.",
|
|
1078
|
+
{ input: z.string().describe("Malformed JSON string to repair") },
|
|
1079
|
+
async ({ input }) => {
|
|
1080
|
+
const result = repairJson(input);
|
|
1081
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1082
|
+
}
|
|
1083
|
+
);
|
|
1084
|
+
server.tool("trim_to_tokens", "Truncate text to a token budget (end/start/middle strategy).", { text: z.string(), max_tokens: z.number().int().min(1), strategy: z.enum(["end", "start", "middle"]).default("end") }, async ({ text: text2, max_tokens, strategy }) => {
|
|
1085
|
+
return { content: [{ type: "text", text: JSON.stringify(trimToTokens(text2, max_tokens, strategy), null, 2) }] };
|
|
1086
|
+
});
|
|
1087
|
+
server.tool("chunk_text", "Split text into overlapping chunks for RAG.", { text: z.string(), chunk_size: z.number().int().min(10).default(200), overlap: z.number().int().min(0).default(20), strategy: z.enum(["paragraph", "sentence", "fixed"]).default("paragraph") }, async ({ text: text2, chunk_size, overlap, strategy }) => {
|
|
1088
|
+
return { content: [{ type: "text", text: JSON.stringify(chunkText(text2, chunk_size, overlap, strategy), null, 2) }] };
|
|
1089
|
+
});
|
|
1090
|
+
server.tool("extract_urls", "Extract all URLs from text or HTML.", { text: z.string() }, async ({ text: text2 }) => {
|
|
1091
|
+
return { content: [{ type: "text", text: JSON.stringify(extractUrls(text2), null, 2) }] };
|
|
1092
|
+
});
|
|
1093
|
+
server.tool("sanitize_html", "Strip HTML tags, scripts, and event handlers for LLM ingestion.", { html: z.string(), keep_links: z.boolean().default(false) }, async ({ html, keep_links }) => {
|
|
1094
|
+
return { content: [{ type: "text", text: JSON.stringify(sanitizeHtml(html, keep_links), null, 2) }] };
|
|
1095
|
+
});
|
|
1096
|
+
server.tool("flatten_json", "Flatten nested JSON to dot-notation keys.", { json: z.unknown(), separator: z.string().default("."), unflatten: z.boolean().default(false) }, async ({ json, separator, unflatten }) => {
|
|
1097
|
+
const result = unflatten ? unflattenJson(json, separator) : flattenJson(json, separator);
|
|
1098
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1099
|
+
});
|
|
1100
|
+
server.tool("merge_json", "Deep merge two JSON objects.", { base: z.unknown(), override: z.unknown(), strategy: z.enum(["deep", "shallow", "replace-arrays"]).default("deep") }, async ({ base, override, strategy }) => {
|
|
1101
|
+
return { content: [{ type: "text", text: JSON.stringify(mergeJson(base, override, strategy), null, 2) }] };
|
|
1102
|
+
});
|
|
1103
|
+
server.tool("extract_json", "Extract JSON blocks from mixed LLM output text.", { text: z.string() }, async ({ text: text2 }) => {
|
|
1104
|
+
return { content: [{ type: "text", text: JSON.stringify(extractJson(text2), null, 2) }] };
|
|
1105
|
+
});
|
|
1106
|
+
server.tool("deduplicate_lines", "Remove duplicate lines from text.", { text: z.string(), strategy: z.enum(["exact", "case-insensitive", "trimmed", "normalized"]).default("exact") }, async ({ text: text2, strategy }) => {
|
|
1107
|
+
return { content: [{ type: "text", text: JSON.stringify(deduplicateLines(text2, strategy), null, 2) }] };
|
|
1108
|
+
});
|
|
1109
|
+
server.tool("extract_keywords", "Extract top keywords using TF-IDF scoring.", { text: z.string(), max_keywords: z.number().int().min(1).max(100).default(20), min_word_len: z.number().int().min(2).default(3) }, async ({ text: text2, max_keywords, min_word_len }) => {
|
|
1110
|
+
return { content: [{ type: "text", text: JSON.stringify(extractKeywords(text2, max_keywords, min_word_len), null, 2) }] };
|
|
1111
|
+
});
|
|
1112
|
+
server.tool("validate_json_schema", "Validate JSON data against a JSON Schema (Draft-07).", { data: z.unknown(), schema: z.record(z.unknown()) }, async ({ data, schema }) => {
|
|
1113
|
+
return { content: [{ type: "text", text: JSON.stringify(validateJsonSchema(data, schema), null, 2) }] };
|
|
1114
|
+
});
|
|
1115
|
+
server.tool("diff_json", "Diff two JSON objects \u2014 returns added, removed, changed, and unchanged paths.", { a: z.unknown(), b: z.unknown() }, async ({ a, b }) => {
|
|
1116
|
+
return { content: [{ type: "text", text: JSON.stringify(diffJson(a, b), null, 2) }] };
|
|
1117
|
+
});
|
|
1118
|
+
server.tool("extract_tables", "Extract HTML tables into JSON arrays with headers and rows.", { html: z.string() }, async ({ html }) => {
|
|
1119
|
+
return { content: [{ type: "text", text: JSON.stringify(extractTables(html), null, 2) }] };
|
|
1120
|
+
});
|
|
1121
|
+
server.tool("extract_entities", "Extract emails, phones, dates, IPs, currencies, hashtags from text.", { text: z.string() }, async ({ text: text2 }) => {
|
|
1122
|
+
return { content: [{ type: "text", text: JSON.stringify(extractEntities(text2), null, 2) }] };
|
|
1123
|
+
});
|
|
1124
|
+
server.tool("compress_html", "Strip scripts, styles, classes, and whitespace from HTML to minimize tokens.", { html: z.string(), remove_comments: z.boolean().default(true), remove_scripts: z.boolean().default(true), remove_styles: z.boolean().default(true), collapse_whitespace: z.boolean().default(true) }, async ({ html, remove_comments, remove_scripts, remove_styles, collapse_whitespace }) => {
|
|
1125
|
+
return { content: [{ type: "text", text: JSON.stringify(compressHtml(html, { removeComments: remove_comments, removeScripts: remove_scripts, removeStyles: remove_styles, collapseWhitespace: collapse_whitespace }), null, 2) }] };
|
|
1126
|
+
});
|
|
1127
|
+
server.tool("compress_markdown", "Normalize and compress markdown to reduce token count.", { markdown: z.string(), collapse_blank_lines: z.boolean().default(true), remove_comments: z.boolean().default(true), strip_frontmatter: z.boolean().default(false) }, async ({ markdown, collapse_blank_lines, remove_comments, strip_frontmatter }) => {
|
|
1128
|
+
return { content: [{ type: "text", text: JSON.stringify(compressMarkdown(markdown, { collapseBlankLines: collapse_blank_lines, removeComments: remove_comments, stripFrontmatter: strip_frontmatter }), null, 2) }] };
|
|
1129
|
+
});
|
|
1130
|
+
server.tool("compress_json", "Remove nulls, empty arrays, and empty objects from JSON to reduce tokens.", { json: z.unknown(), remove_nulls: z.boolean().default(true), remove_empty_arrays: z.boolean().default(true), remove_empty_objects: z.boolean().default(true), sort_keys: z.boolean().default(false) }, async ({ json, remove_nulls, remove_empty_arrays, remove_empty_objects, sort_keys }) => {
|
|
1131
|
+
return { content: [{ type: "text", text: JSON.stringify(compressJson(json, { removeNulls: remove_nulls, removeEmptyArrays: remove_empty_arrays, removeEmptyObjects: remove_empty_objects, sortKeys: sort_keys }), null, 2) }] };
|
|
1132
|
+
});
|
|
1133
|
+
server.tool("rerank_chunks", "Score and rerank text chunks by TF-IDF relevance to a query.", { query: z.string(), chunks: z.array(z.string()), top_k: z.number().int().min(1).optional() }, async ({ query, chunks, top_k }) => {
|
|
1134
|
+
return { content: [{ type: "text", text: JSON.stringify(rerankChunks(query, chunks, top_k), null, 2) }] };
|
|
1135
|
+
});
|
|
1136
|
+
server.tool("score_relevance", "Score a passage relevance to a query (0-1 score with grade).", { query: z.string(), passage: z.string() }, async ({ query, passage }) => {
|
|
1137
|
+
return { content: [{ type: "text", text: JSON.stringify(scoreRelevance(query, passage), null, 2) }] };
|
|
1138
|
+
});
|
|
1139
|
+
server.tool("expand_query", "Expand a search query with synonyms and stemmed variants for better retrieval.", { query: z.string(), max_synonyms: z.number().int().min(1).max(5).default(2) }, async ({ query, max_synonyms }) => {
|
|
1140
|
+
return { content: [{ type: "text", text: JSON.stringify(expandQuery(query, max_synonyms), null, 2) }] };
|
|
1141
|
+
});
|
|
1142
|
+
server.tool("summarize_for_llm", "Extractive summarization \u2014 pick key sentences to fit a token budget.", { text: z.string(), max_tokens: z.number().int().min(10).default(200), strategy: z.enum(["extractive", "first", "last"]).default("extractive") }, async ({ text: text2, max_tokens, strategy }) => {
|
|
1143
|
+
return { content: [{ type: "text", text: JSON.stringify(summarizeForLlm(text2, max_tokens, strategy), null, 2) }] };
|
|
1144
|
+
});
|
|
742
1145
|
var transport = new StdioServerTransport();
|
|
743
1146
|
await server.connect(transport);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@utilix-tech/mcp",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "MCP server exposing
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "MCP server exposing 70 Utilix developer tools to Claude, Cursor, VS Code Copilot, and any MCP-compatible AI assistant.",
|
|
5
5
|
"author": "Utilix <hello@utilix.tech>",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"homepage": "https://utilix.tech/docs",
|
package/smithery.yaml
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
name: utilix
|
|
2
|
-
description:
|
|
2
|
+
description: 70 developer utility tools — JSON, hashing, encoding, JWT, UUID, cron, color, SQL, regex, CSS, QR codes and more. All run locally, no API key required.
|
|
3
3
|
homepage: https://utilix.tech
|
|
4
4
|
license: MIT
|
|
5
5
|
|
|
@@ -61,3 +61,22 @@ tools:
|
|
|
61
61
|
- validate_yaml
|
|
62
62
|
- toml_to_json
|
|
63
63
|
- xml_to_json
|
|
64
|
+
- text_diff
|
|
65
|
+
- html_to_markdown
|
|
66
|
+
- line_operations
|
|
67
|
+
- number_to_words
|
|
68
|
+
- unicode_inspect
|
|
69
|
+
- minify_css
|
|
70
|
+
- css_gradient
|
|
71
|
+
- check_contrast
|
|
72
|
+
- color_palette
|
|
73
|
+
- color_shades
|
|
74
|
+
- cors_headers
|
|
75
|
+
- minify_js
|
|
76
|
+
- format_graphql
|
|
77
|
+
- parse_env
|
|
78
|
+
- parse_docker_image
|
|
79
|
+
- generate_qr
|
|
80
|
+
- generate_random_data
|
|
81
|
+
- parse_csv
|
|
82
|
+
- optimize_svg
|