@utilix-tech/mcp 0.3.1 → 0.4.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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,19 @@
1
+ # Changelog
2
+
3
+ Full release notes for all Utilix surfaces (browser tools, REST API, SDKs, MCP server) live at [utilix.tech/changelog](https://utilix.tech/changelog). This file tracks just this package.
4
+
5
+ ## 0.4.1
6
+ - Bumped the `@utilix-tech/sdk` dependency from `^0.2.0` to `^0.3.0` to match the SDK version actually in use, closing a gap where a future tool addition could have silently pinned to a stale SDK release.
7
+ - Corrected repository/bug tracker links.
8
+
9
+ ## 0.4.0
10
+ - Added `detect_passive_voice` and `read_image_info`, bringing the total to 97 tools.
11
+
12
+ ## 0.3.1
13
+ - Fixed a duplicate tool registration (`diff_json` was registered twice, silently shadowing the richer variant). Renamed the shadowed tool to `diff_json_detailed`.
14
+
15
+ ## 0.3.0
16
+ - Expanded to 95+ tools.
17
+
18
+ ## 0.1.0
19
+ - Initial release.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Utilix
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # @utilix-tech/mcp
2
2
 
3
- MCP server exposing 95 developer utility tools to Claude, Cursor, VS Code Copilot, and any [Model Context Protocol](https://modelcontextprotocol.io) compatible AI assistant.
3
+ MCP server exposing 97 developer utility tools to Claude, Cursor, VS Code Copilot, and any [Model Context Protocol](https://modelcontextprotocol.io) compatible AI assistant.
4
4
 
5
- No API key required. All tools run locally.
5
+ No API key required. All tools run locally. Requires Node.js 18 or later.
6
6
 
7
7
  ## Installation
8
8
 
@@ -121,7 +121,7 @@ claude mcp add utilix -- npx -y @utilix-tech/mcp
121
121
  | `generate_qr` | Generate a QR code as an SVG string |
122
122
  | `generate_random_data` | Generate random mock data: names, emails, UUIDs, IPs, etc. |
123
123
 
124
- ### Text (9 tools)
124
+ ### Text (10 tools)
125
125
  | Tool | Description |
126
126
  |------|-------------|
127
127
  | `convert_case` | Convert text case: camelCase, snake_case, PascalCase, etc. |
@@ -133,6 +133,7 @@ claude mcp add utilix -- npx -y @utilix-tech/mcp
133
133
  | `text_diff` | Diff two text blocks line-by-line |
134
134
  | `html_to_markdown` | Convert HTML to Markdown |
135
135
  | `line_operations` | Sort, deduplicate, reverse, shuffle, trim, or number lines |
136
+ | `detect_passive_voice` | Flag sentences written in passive voice, e.g. "was written", "were approved by the team" |
136
137
 
137
138
  ### Time (5 tools)
138
139
  | Tool | Description |
@@ -152,7 +153,7 @@ claude mcp add utilix -- npx -y @utilix-tech/mcp
152
153
  ### API / Network (4 tools)
153
154
  | Tool | Description |
154
155
  |------|-------------|
155
- | `decode_jwt` | Decode a JWT header, payload, and expiry |
156
+ | `decode_jwt` | Decode a JWT: header, payload, and expiry |
156
157
  | `build_curl` | Build a cURL command from method, URL, headers, and body |
157
158
  | `curl_to_code` | Convert a cURL command to fetch, axios, Python, Go, PHP, or Ruby |
158
159
  | `cors_headers` | Generate CORS response headers for a configuration |
@@ -226,14 +227,23 @@ claude mcp add utilix -- npx -y @utilix-tech/mcp
226
227
  | `compress_json` | Compress JSON for LLM context budgets |
227
228
  | `diff_json_detailed` | Diff two parsed JSON values, returning every path with its op and unchanged paths |
228
229
 
230
+ ### Image (1 tool)
231
+ | Tool | Description |
232
+ |------|-------------|
233
+ | `read_image_info` | Read format, dimensions, bit depth, and alpha channel from image bytes: no decoding required. Supports PNG, JPEG, GIF, WebP, BMP |
234
+
229
235
  ## Requirements
230
236
 
231
237
  - Node.js 18+
232
238
 
239
+ ## Publishing (maintainers)
240
+
241
+ CI publishes automatically via npm Trusted Publishing (OIDC) on every push to `main` that bumps `version`. Requires npm **>=11.5.1**: older versions (e.g. the npm bundled with Node 20) sign provenance but skip the OIDC auth exchange, so `npm publish` fails with a 404 as if unauthenticated.
242
+
233
243
  ## Links
234
244
 
235
- - [Utilix web app](https://utilix.tech) use these tools in the browser
236
- - [API Playground](https://utilix.tech/api) try 10 endpoints live, no signup required
237
- - [REST API docs](https://utilix.tech/docs#rest-api) 100+ endpoints at `api.utilix.tech/v1` (1,000 req/day free)
238
- - [Node.js SDK](https://www.npmjs.com/package/@utilix-tech/sdk) use in your Node.js projects
239
- - [Python SDK](https://pypi.org/project/utilix-sdk/) use in your Python projects
245
+ - [Utilix web app](https://utilix.tech): use these tools in the browser
246
+ - [API Playground](https://utilix.tech/api): try 10 endpoints live, no signup required
247
+ - [REST API docs](https://utilix.tech/docs#rest-api): 100+ endpoints at `api.utilix.tech/v1` (1,000 req/day free)
248
+ - [Node.js SDK](https://www.npmjs.com/package/@utilix-tech/sdk): use in your Node.js projects
249
+ - [Python SDK](https://pypi.org/project/utilix-sdk/): use in your Python projects
package/SECURITY.md ADDED
@@ -0,0 +1,16 @@
1
+ # Security Policy
2
+
3
+ ## Reporting a Vulnerability
4
+
5
+ If you discover a security vulnerability in this package, please report it privately rather than opening a public issue.
6
+
7
+ Email **hello@utilix.tech** with the subject line "Security report" and include:
8
+ - A description of the vulnerability and its potential impact
9
+ - Steps to reproduce, if possible
10
+ - Any suggested fix
11
+
12
+ We aim to acknowledge reports within 3 business days. Please give us a reasonable window to release a fix before disclosing publicly.
13
+
14
+ ## Supported Versions
15
+
16
+ Only the latest published version of `@utilix-tech/mcp` receives security fixes. We recommend always running the latest release (the package is invoked via `npx`, which always fetches the latest version by default).
package/dist/index.js CHANGED
@@ -393,7 +393,7 @@ server.tool(
393
393
  },
394
394
  async ({ password, hash }) => {
395
395
  const valid = await verifyPassword(password, hash);
396
- return text(valid ? "Valid \u2014 password matches hash." : "Invalid \u2014 password does not match hash.");
396
+ return text(valid ? "Valid: password matches hash." : "Invalid: password does not match hash.");
397
397
  }
398
398
  );
399
399
  server.tool(
@@ -1033,7 +1033,7 @@ server.tool(
1033
1033
  "Estimate the token count and API cost for a text string across popular LLMs (GPT-4o, Claude, Gemini, etc.).",
1034
1034
  {
1035
1035
  text: z.string().describe("Text to estimate tokens for"),
1036
- model: z.string().optional().describe("Model slug (e.g. gpt-4o, claude-sonnet-4) \u2014 omit for all models"),
1036
+ model: z.string().optional().describe("Model slug (e.g. gpt-4o, claude-sonnet-4): omit for all models"),
1037
1037
  output_tokens: z.number().int().min(0).optional().describe("Expected output tokens (defaults to same as input)")
1038
1038
  },
1039
1039
  async ({ text: text2, model, output_tokens }) => {
@@ -1113,7 +1113,7 @@ server.tool("extract_keywords", "Extract top keywords using TF-IDF scoring.", {
1113
1113
  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 }) => {
1114
1114
  return { content: [{ type: "text", text: JSON.stringify(validateJsonSchema(data, schema), null, 2) }] };
1115
1115
  });
1116
- server.tool("diff_json_detailed", "Diff two JSON objects (parsed values, not strings) \u2014 returns a flat list of every path with its op (added/removed/changed/unchanged), plus counts.", { a: z.unknown(), b: z.unknown() }, async ({ a, b }) => {
1116
+ server.tool("diff_json_detailed", "Diff two JSON objects (parsed values, not strings): returns a flat list of every path with its op (added/removed/changed/unchanged), plus counts.", { a: z.unknown(), b: z.unknown() }, async ({ a, b }) => {
1117
1117
  return { content: [{ type: "text", text: JSON.stringify(diffJsonDetailed(a, b), null, 2) }] };
1118
1118
  });
1119
1119
  server.tool("extract_tables", "Extract HTML tables into JSON arrays with headers and rows.", { html: z.string() }, async ({ html }) => {
@@ -1140,8 +1140,263 @@ server.tool("score_relevance", "Score a passage relevance to a query (0-1 score
1140
1140
  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 }) => {
1141
1141
  return { content: [{ type: "text", text: JSON.stringify(expandQuery(query, max_synonyms), null, 2) }] };
1142
1142
  });
1143
- 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
+ server.tool("summarize_for_llm", "Extractive summarization: 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 }) => {
1144
1144
  return { content: [{ type: "text", text: JSON.stringify(summarizeForLlm(text2, max_tokens, strategy), null, 2) }] };
1145
1145
  });
1146
+ var _BE_VERBS = /* @__PURE__ */ new Set(["am", "is", "are", "was", "were", "be", "been", "being"]);
1147
+ var _IRREGULAR_PARTICIPLES = /* @__PURE__ */ new Set([
1148
+ "awoken",
1149
+ "been",
1150
+ "become",
1151
+ "begun",
1152
+ "bent",
1153
+ "bet",
1154
+ "bitten",
1155
+ "bled",
1156
+ "blown",
1157
+ "born",
1158
+ "bought",
1159
+ "bound",
1160
+ "bred",
1161
+ "broken",
1162
+ "brought",
1163
+ "built",
1164
+ "burnt",
1165
+ "burst",
1166
+ "caught",
1167
+ "chosen",
1168
+ "clung",
1169
+ "come",
1170
+ "cost",
1171
+ "crept",
1172
+ "cut",
1173
+ "dealt",
1174
+ "dived",
1175
+ "done",
1176
+ "drawn",
1177
+ "dreamt",
1178
+ "driven",
1179
+ "drunk",
1180
+ "dug",
1181
+ "eaten",
1182
+ "fallen",
1183
+ "fed",
1184
+ "felt",
1185
+ "fit",
1186
+ "fled",
1187
+ "flown",
1188
+ "forbidden",
1189
+ "forgiven",
1190
+ "forgotten",
1191
+ "fought",
1192
+ "found",
1193
+ "frozen",
1194
+ "given",
1195
+ "gone",
1196
+ "gotten",
1197
+ "grown",
1198
+ "hidden",
1199
+ "held",
1200
+ "hung",
1201
+ "hurt",
1202
+ "kept",
1203
+ "knelt",
1204
+ "knit",
1205
+ "known",
1206
+ "laid",
1207
+ "lain",
1208
+ "led",
1209
+ "left",
1210
+ "lent",
1211
+ "let",
1212
+ "lit",
1213
+ "lost",
1214
+ "made",
1215
+ "meant",
1216
+ "met",
1217
+ "mistaken",
1218
+ "paid",
1219
+ "proven",
1220
+ "put",
1221
+ "quit",
1222
+ "read",
1223
+ "rid",
1224
+ "ridden",
1225
+ "risen",
1226
+ "run",
1227
+ "said",
1228
+ "sat",
1229
+ "sawn",
1230
+ "seen",
1231
+ "sent",
1232
+ "set",
1233
+ "sewn",
1234
+ "shaken",
1235
+ "shed",
1236
+ "shone",
1237
+ "shot",
1238
+ "shown",
1239
+ "shrunk",
1240
+ "shut",
1241
+ "sold",
1242
+ "sought",
1243
+ "sown",
1244
+ "spent",
1245
+ "spoken",
1246
+ "spread",
1247
+ "sprung",
1248
+ "stolen",
1249
+ "stood",
1250
+ "stuck",
1251
+ "stung",
1252
+ "struck",
1253
+ "strung",
1254
+ "sung",
1255
+ "sunk",
1256
+ "swept",
1257
+ "sworn",
1258
+ "swum",
1259
+ "swung",
1260
+ "taken",
1261
+ "taught",
1262
+ "thought",
1263
+ "thrown",
1264
+ "told",
1265
+ "torn",
1266
+ "understood",
1267
+ "upset",
1268
+ "woken",
1269
+ "won",
1270
+ "worn",
1271
+ "wound",
1272
+ "woven",
1273
+ "written"
1274
+ ]);
1275
+ function _isPastParticiple(word) {
1276
+ const w = word.toLowerCase();
1277
+ if (_IRREGULAR_PARTICIPLES.has(w)) return true;
1278
+ return w.length > 3 && /^[a-z]+ed$/.test(w);
1279
+ }
1280
+ function _splitIntoSentences(text2) {
1281
+ const matches = text2.match(/[^.!?]+[.!?]*/g);
1282
+ if (!matches) return [];
1283
+ return matches.map((s) => s.trim()).filter((s) => s.length > 0);
1284
+ }
1285
+ function _detectPassiveVoice(text2) {
1286
+ const sentences = _splitIntoSentences(text2);
1287
+ const analyzed = sentences.map((sentence, index) => {
1288
+ const words = sentence.match(/[a-zA-Z']+/g) ?? [];
1289
+ const matches = [];
1290
+ for (let i = 0; i < words.length; i++) {
1291
+ if (!_BE_VERBS.has(words[i].toLowerCase())) continue;
1292
+ let j = i + 1;
1293
+ let adverbCount = 0;
1294
+ while (j < words.length && adverbCount < 2 && /ly$/i.test(words[j]) && !_isPastParticiple(words[j])) {
1295
+ j++;
1296
+ adverbCount++;
1297
+ }
1298
+ if (j < words.length && _isPastParticiple(words[j])) {
1299
+ const hasAgent = words.slice(j + 1, j + 5).some((w) => w.toLowerCase() === "by");
1300
+ matches.push({ phrase: words.slice(i, j + 1).join(" "), hasAgent });
1301
+ i = j;
1302
+ }
1303
+ }
1304
+ return { index, text: sentence, isPassive: matches.length > 0, matches };
1305
+ });
1306
+ const totalSentences = analyzed.length;
1307
+ const passiveSentences = analyzed.filter((s) => s.isPassive).length;
1308
+ const passivePercent = totalSentences > 0 ? Math.round(passiveSentences / totalSentences * 1e3) / 10 : 0;
1309
+ return { sentences: analyzed, totalSentences, passiveSentences, passivePercent };
1310
+ }
1311
+ var _PNG_COLOR_TYPES = { 0: "grayscale", 2: "rgb", 3: "palette", 4: "grayscale+alpha", 6: "rgba" };
1312
+ var _JPEG_SOF_MARKERS = /* @__PURE__ */ new Set([192, 193, 194, 195, 197, 198, 199, 201, 202, 203, 205, 206, 207]);
1313
+ function _readImageInfo(bytes) {
1314
+ if (bytes.length < 10) return { error: "File too small to determine format" };
1315
+ if (bytes[0] === 137 && bytes[1] === 80 && bytes[2] === 78 && bytes[3] === 71 && bytes[4] === 13 && bytes[5] === 10 && bytes[6] === 26 && bytes[7] === 10) {
1316
+ if (bytes.length < 33) return { error: "Truncated PNG file" };
1317
+ const width = (bytes[16] << 24 | bytes[17] << 16 | bytes[18] << 8 | bytes[19]) >>> 0;
1318
+ const height = (bytes[20] << 24 | bytes[21] << 16 | bytes[22] << 8 | bytes[23]) >>> 0;
1319
+ const bitDepth = bytes[24];
1320
+ const colorTypeNum = bytes[25];
1321
+ const colorType = _PNG_COLOR_TYPES[colorTypeNum] ?? "unknown";
1322
+ const hasAlpha = colorTypeNum === 4 || colorTypeNum === 6;
1323
+ return { format: "png", width, height, bitDepth, colorType, hasAlpha };
1324
+ }
1325
+ if (bytes[0] === 71 && bytes[1] === 73 && bytes[2] === 70) {
1326
+ if (bytes.length < 10) return { error: "Truncated GIF file" };
1327
+ const width = bytes[6] | bytes[7] << 8;
1328
+ const height = bytes[8] | bytes[9] << 8;
1329
+ return { format: "gif", width, height };
1330
+ }
1331
+ if (bytes[0] === 66 && bytes[1] === 77) {
1332
+ if (bytes.length < 30) return { error: "Truncated BMP file" };
1333
+ const width = bytes[18] | bytes[19] << 8 | bytes[20] << 16 | bytes[21] << 24;
1334
+ const heightRaw = bytes[22] | bytes[23] << 8 | bytes[24] << 16 | bytes[25] << 24;
1335
+ const bitDepth = bytes[28] | bytes[29] << 8;
1336
+ return { format: "bmp", width: Math.abs(width), height: Math.abs(heightRaw), bitDepth };
1337
+ }
1338
+ if (bytes[0] === 82 && bytes[1] === 73 && bytes[2] === 70 && bytes[3] === 70 && bytes[8] === 87 && bytes[9] === 69 && bytes[10] === 66 && bytes[11] === 80) {
1339
+ const fourCC = String.fromCharCode(bytes[12], bytes[13], bytes[14], bytes[15]);
1340
+ if (fourCC === "VP8 ") {
1341
+ if (bytes.length < 30) return { error: "Truncated WebP file" };
1342
+ const width = (bytes[26] | bytes[27] << 8) & 16383;
1343
+ const height = (bytes[28] | bytes[29] << 8) & 16383;
1344
+ return { format: "webp", width, height };
1345
+ }
1346
+ if (fourCC === "VP8L") {
1347
+ if (bytes.length < 25) return { error: "Truncated WebP file" };
1348
+ const bits = bytes[21] | bytes[22] << 8 | bytes[23] << 16 | bytes[24] << 24;
1349
+ const width = (bits & 16383) + 1;
1350
+ const height = (bits >>> 14 & 16383) + 1;
1351
+ const hasAlpha = !!(bits >>> 28 & 1);
1352
+ return { format: "webp", width, height, hasAlpha };
1353
+ }
1354
+ if (fourCC === "VP8X") {
1355
+ if (bytes.length < 30) return { error: "Truncated WebP file" };
1356
+ const flags = bytes[20];
1357
+ const hasAlpha = !!(flags & 16);
1358
+ const width = (bytes[24] | bytes[25] << 8 | bytes[26] << 16) + 1;
1359
+ const height = (bytes[27] | bytes[28] << 8 | bytes[29] << 16) + 1;
1360
+ return { format: "webp", width, height, hasAlpha };
1361
+ }
1362
+ return { error: "Unrecognized WebP chunk format" };
1363
+ }
1364
+ if (bytes[0] === 255 && bytes[1] === 216) {
1365
+ let offset = 2;
1366
+ while (offset < bytes.length - 1) {
1367
+ if (bytes[offset] !== 255) {
1368
+ offset++;
1369
+ continue;
1370
+ }
1371
+ const marker = bytes[offset + 1];
1372
+ if (marker === 216 || marker === 1 || marker >= 208 && marker <= 215) {
1373
+ offset += 2;
1374
+ continue;
1375
+ }
1376
+ if (marker === 217) break;
1377
+ if (offset + 3 >= bytes.length) break;
1378
+ const segmentLength = bytes[offset + 2] << 8 | bytes[offset + 3];
1379
+ if (_JPEG_SOF_MARKERS.has(marker)) {
1380
+ if (offset + 9 >= bytes.length) return { error: "Truncated JPEG SOF segment" };
1381
+ const bitDepth = bytes[offset + 4];
1382
+ const height = bytes[offset + 5] << 8 | bytes[offset + 6];
1383
+ const width = bytes[offset + 7] << 8 | bytes[offset + 8];
1384
+ const components = bytes[offset + 9];
1385
+ const colorType = components === 1 ? "grayscale" : components === 3 ? "rgb" : components === 4 ? "cmyk" : "unknown";
1386
+ return { format: "jpeg", width, height, bitDepth, colorType };
1387
+ }
1388
+ offset += 2 + segmentLength;
1389
+ }
1390
+ return { error: "No SOF marker found in JPEG (file may be corrupt)" };
1391
+ }
1392
+ return { error: "Unrecognized image format. Supported formats: PNG, JPEG, GIF, WebP, BMP" };
1393
+ }
1394
+ server.tool("detect_passive_voice", 'Heuristically flag sentences containing passive-voice constructions, e.g. "was written", "were approved by the team".', { text: z.string() }, async ({ text: text2 }) => {
1395
+ return { content: [{ type: "text", text: JSON.stringify(_detectPassiveVoice(text2), null, 2) }] };
1396
+ });
1397
+ server.tool("read_image_info", "Read image format, dimensions, and color info directly from file bytes: no decoding required. Supports PNG, JPEG, GIF, WebP, BMP.", { image_base64: z.string().describe("Base64-encoded image bytes, optionally prefixed with a data: URL scheme") }, async ({ image_base64 }) => {
1398
+ const bytes = new Uint8Array(Buffer.from(image_base64.replace(/^data:.*;base64,/, ""), "base64"));
1399
+ return { content: [{ type: "text", text: JSON.stringify(_readImageInfo(bytes), null, 2) }] };
1400
+ });
1146
1401
  var transport = new StdioServerTransport();
1147
1402
  await server.connect(transport);
package/package.json CHANGED
@@ -1,10 +1,17 @@
1
1
  {
2
2
  "name": "@utilix-tech/mcp",
3
- "version": "0.3.1",
4
- "description": "MCP server exposing 95 Utilix developer tools to Claude, Cursor, VS Code Copilot, and any MCP-compatible AI assistant.",
3
+ "version": "0.4.1",
4
+ "description": "MCP server exposing 97 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
- "homepage": "https://utilix.tech/docs",
7
+ "homepage": "https://utilix.tech/mcp",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/utilix-tech/utilix-mcp.git"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/utilix-tech/utilix-mcp/issues"
14
+ },
8
15
  "keywords": [
9
16
  "mcp",
10
17
  "model-context-protocol",
@@ -16,7 +23,10 @@
16
23
  "json",
17
24
  "encoding",
18
25
  "hashing",
19
- "utilix"
26
+ "utilix",
27
+ "ai-agent",
28
+ "llm-tools",
29
+ "stdio"
20
30
  ],
21
31
  "engines": {
22
32
  "node": ">=18"
@@ -29,17 +39,21 @@
29
39
  "files": [
30
40
  "dist",
31
41
  "smithery.yaml",
32
- "README.md"
42
+ "README.md",
43
+ "LICENSE",
44
+ "CHANGELOG.md",
45
+ "SECURITY.md"
33
46
  ],
34
47
  "scripts": {
35
48
  "build": "tsup && chmod +x dist/index.js",
36
49
  "dev": "tsup --watch",
37
50
  "start": "node dist/index.js",
51
+ "test": "node scripts/check-duplicate-tools.mjs",
38
52
  "prepublishOnly": "npm run build"
39
53
  },
40
54
  "dependencies": {
41
55
  "@modelcontextprotocol/sdk": "^1.12.0",
42
- "@utilix-tech/sdk": "^0.1.4",
56
+ "@utilix-tech/sdk": "^0.3.0",
43
57
  "zod": "^3.23.0"
44
58
  },
45
59
  "devDependencies": {
package/smithery.yaml CHANGED
@@ -1,5 +1,5 @@
1
1
  name: utilix
2
- description: 70 developer utility tools for Claude, Cursor, VS Code, Windsurf, and any MCP-compatible AI assistant. Covers JSON, hashing, encoding, JWT, UUID, cron, color, SQL, regex, CSS, QR codes, and more. All tools run locally no API key required.
2
+ description: 70 developer utility tools for Claude, Cursor, VS Code, Windsurf, and any MCP-compatible AI assistant. Covers JSON, hashing, encoding, JWT, UUID, cron, color, SQL, regex, CSS, QR codes, and more. All tools run locally: no API key required.
3
3
  homepage: https://utilix.tech/mcp
4
4
  license: MIT
5
5
  repository: https://github.com/utilix-tech/utilix-mcp