@utilix-tech/mcp 0.82.0 → 0.83.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/CHANGELOG.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
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
4
 
5
+ ## 0.83.0
6
+ - Add `extract_markdown_link_references` tool: extract reference-style Markdown links (`[text][ref]`, the `[text][]` shortcut, and `![alt][ref]` image references) and match them against their `[ref]: url` definitions, flagging unresolved references and unused definitions.
7
+ - Add `extract_prompt_template_variables` tool: extract `{{variable}}`-style placeholders from a prompt template, report occurrence counts, flag duplicates, and optionally cross-check against a list of provided variable names.
8
+
5
9
  ## 0.82.0
6
10
  - Add `generate_robots_txt` tool: build a robots.txt file from User-agent groups with Allow/Disallow rules and an optional Crawl-delay, plus a list of Sitemap URLs. The generator-side sibling of `validate_robots_txt`.
7
11
 
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @utilix-tech/mcp
2
2
 
3
- MCP server exposing 258 developer utility tools to Claude, Cursor, VS Code Copilot, and any [Model Context Protocol](https://modelcontextprotocol.io) compatible AI assistant.
3
+ MCP server exposing 260 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. Requires Node.js 18 or later.
6
6
 
@@ -282,7 +282,7 @@ claude mcp add utilix -- npx -y @utilix-tech/mcp
282
282
  | `color_palette` | Generate a color palette from a base color |
283
283
  | `color_shades` | Generate shades or tints from a base color |
284
284
 
285
- ### Code (24 tools)
285
+ ### Code (25 tools)
286
286
  | Tool | Description |
287
287
  |------|-------------|
288
288
  | `format_sql` | Format a SQL query with proper indentation |
@@ -309,6 +309,7 @@ claude mcp add utilix -- npx -y @utilix-tech/mcp
309
309
  | `parse_connection_string` | Parse a database connection URI (postgres, mysql, mongodb, redis, amqp, and similar) into its scheme, credentials, hosts, database, and query parameters |
310
310
  | `validate_fence_language_tags` | Scan a Markdown document's fenced code blocks and flag a missing, unknown, or inconsistently-cased language tag against a curated list of common syntax-highlighter identifiers |
311
311
  | `count_sql_placeholders` | Count positional (`?`, `$1`) and named (`:name`, `@name`) parameter placeholders in a SQL query, flagging mixed styles and numbered-sequence gaps or duplicates |
312
+ | `extract_markdown_link_references` | Extract every reference-style Markdown link ([text][ref], [text][], ![alt][ref]) and match it against its [ref]: url definition, flagging unresolved references and unused definitions |
312
313
 
313
314
  ### Data (16 tools)
314
315
  | Tool | Description |
@@ -344,7 +345,7 @@ claude mcp add utilix -- npx -y @utilix-tech/mcp
344
345
  | `unicode_inspect` | Inspect Unicode code points of a string |
345
346
  | `optimize_svg` | Optimize and minify an SVG string |
346
347
 
347
- ### AI / Agent (29 tools)
348
+ ### AI / Agent (30 tools)
348
349
  | Tool | Description |
349
350
  |------|-------------|
350
351
  | `estimate_tokens` | Estimate token count and cost for an LLM prompt |
@@ -376,6 +377,7 @@ claude mcp add utilix -- npx -y @utilix-tech/mcp
376
377
  | `format_few_shot_examples` | Format input/output example pairs into a numbered, XML-tagged, or delimiter-separated few-shot prompt template |
377
378
  | `lint_tool_schema` | Lint an OpenAI, Anthropic, or MCP tool/function-calling definition for missing descriptions, invalid names, empty enums, and required/properties mismatches |
378
379
  | `calculate_retry_backoff_schedule` | Compute the full sequence of delays an exponential-backoff retry policy would actually produce, given a base delay, multiplier, and max retry count, optionally capped at a max delay and with full/equal jitter ranges |
380
+ | `extract_prompt_template_variables` | Extract every {{variable}}-style placeholder from a prompt template, report occurrence counts, flag duplicates, and optionally cross-check against a list of provided variable names |
379
381
 
380
382
  ### Image (4 tools)
381
383
  | Tool | Description |
package/dist/index.js CHANGED
@@ -13159,5 +13159,117 @@ server.tool(
13159
13159
  return text(JSON.stringify(result, null, 2));
13160
13160
  }
13161
13161
  );
13162
+ var _LINK_REF_DEFINITION_REGEX_MCP = /^ {0,3}\[([^\]]+)\]:\s*(\S+)(?:\s+(?:"([^"]*)"|'([^']*)'|\(([^)]*)\)))?\s*$/;
13163
+ var _LINK_REF_USAGE_REGEX_MCP = /(!?)\[([^[\]\n]*)\]\[([^[\]\n]*)\]/g;
13164
+ function _normalizeLinkLabelMcp(label) {
13165
+ return label.trim().replace(/\s+/g, " ").toLowerCase();
13166
+ }
13167
+ function _extractMarkdownLinkReferencesMcp(markdown) {
13168
+ const trimmed = markdown.trim();
13169
+ if (!trimmed) return { error: "Empty input" };
13170
+ const lines = markdown.replace(/\r\n/g, "\n").split("\n");
13171
+ const definitions = [];
13172
+ const definitionsByLabel = /* @__PURE__ */ new Map();
13173
+ for (const line of lines) {
13174
+ const match = line.match(_LINK_REF_DEFINITION_REGEX_MCP);
13175
+ if (!match) continue;
13176
+ const label = match[1];
13177
+ const normalized = _normalizeLinkLabelMcp(label);
13178
+ if (definitionsByLabel.has(normalized)) continue;
13179
+ const def = { label, url: match[2], title: match[3] ?? match[4] ?? match[5] ?? void 0 };
13180
+ definitionsByLabel.set(normalized, def);
13181
+ definitions.push(def);
13182
+ }
13183
+ const usages = [];
13184
+ for (const m of markdown.matchAll(_LINK_REF_USAGE_REGEX_MCP)) {
13185
+ const image = m[1] === "!";
13186
+ const text2 = m[2];
13187
+ const rawRef = m[3];
13188
+ const label = rawRef.trim() === "" ? text2 : rawRef;
13189
+ usages.push({ text: text2, label, image });
13190
+ }
13191
+ if (definitions.length === 0 && usages.length === 0) {
13192
+ return { error: "No reference-style links or definitions found" };
13193
+ }
13194
+ const usedLabels = /* @__PURE__ */ new Set();
13195
+ const resolved = [];
13196
+ const unresolvedReferences = [];
13197
+ for (const usage of usages) {
13198
+ const normalized = _normalizeLinkLabelMcp(usage.label);
13199
+ const def = definitionsByLabel.get(normalized);
13200
+ if (def) {
13201
+ usedLabels.add(normalized);
13202
+ resolved.push({ ...usage, url: def.url, title: def.title });
13203
+ } else {
13204
+ unresolvedReferences.push(usage);
13205
+ }
13206
+ }
13207
+ const unusedDefinitions = definitions.filter((d) => !usedLabels.has(_normalizeLinkLabelMcp(d.label)));
13208
+ return {
13209
+ referenceCount: usages.length,
13210
+ definitionCount: definitions.length,
13211
+ resolved,
13212
+ unresolvedReferences,
13213
+ unusedDefinitions
13214
+ };
13215
+ }
13216
+ server.tool(
13217
+ "extract_markdown_link_references",
13218
+ 'Extract every reference-style Markdown link ([text][ref], including the shortcut form [text][] and image references ![alt][ref]) alongside its [ref]: url "title" definition, then flag usages with no matching definition and definitions that no usage ever references.',
13219
+ {
13220
+ markdown: z.string().min(1).describe("The Markdown document text")
13221
+ },
13222
+ async ({ markdown }) => {
13223
+ const result = _extractMarkdownLinkReferencesMcp(markdown);
13224
+ if ("error" in result) return errText(result);
13225
+ return text(JSON.stringify(result, null, 2));
13226
+ }
13227
+ );
13228
+ var _PROMPT_TEMPLATE_VARIABLE_REGEX_MCP = /\{\{\s*([a-zA-Z0-9_.-]+)\s*\}\}/g;
13229
+ function _extractPromptTemplateVariablesMcp(template, providedVars) {
13230
+ const trimmed = template.trim();
13231
+ if (!trimmed) return { error: "Empty input" };
13232
+ const order = [];
13233
+ const counts = /* @__PURE__ */ new Map();
13234
+ for (const m of template.matchAll(_PROMPT_TEMPLATE_VARIABLE_REGEX_MCP)) {
13235
+ const name = m[1];
13236
+ if (!counts.has(name)) {
13237
+ counts.set(name, 0);
13238
+ order.push(name);
13239
+ }
13240
+ counts.set(name, (counts.get(name) ?? 0) + 1);
13241
+ }
13242
+ if (order.length === 0) return { error: "No {{variable}} placeholders found" };
13243
+ const variables = order.map((name) => ({ name, count: counts.get(name) ?? 0 }));
13244
+ const duplicates = order.filter((name) => (counts.get(name) ?? 0) > 1);
13245
+ let missing = [];
13246
+ const unused = [];
13247
+ if (providedVars !== void 0) {
13248
+ const providedSet = new Set(providedVars.map((v) => v.trim()).filter(Boolean));
13249
+ const usedSet = new Set(order);
13250
+ missing = order.filter((name) => !providedSet.has(name));
13251
+ const seenProvided = /* @__PURE__ */ new Set();
13252
+ for (const raw of providedVars) {
13253
+ const v = raw.trim();
13254
+ if (!v || usedSet.has(v) || seenProvided.has(v)) continue;
13255
+ seenProvided.add(v);
13256
+ unused.push(v);
13257
+ }
13258
+ }
13259
+ return { variableCount: order.length, variables, duplicates, missing, unused };
13260
+ }
13261
+ server.tool(
13262
+ "extract_prompt_template_variables",
13263
+ "Extract every {{variable}}-style placeholder from a prompt template, reporting each unique variable's occurrence count and flagging ones used more than once. Optionally cross-check against a providedVars list to flag variables the template uses that are missing, and provided names the template never references.",
13264
+ {
13265
+ template: z.string().min(1).describe("The prompt template text containing {{variable}} placeholders"),
13266
+ providedVars: z.array(z.string()).optional().describe("Optional list of variable names already available, to cross-check against the template")
13267
+ },
13268
+ async ({ template, providedVars }) => {
13269
+ const result = _extractPromptTemplateVariablesMcp(template, providedVars);
13270
+ if ("error" in result) return errText(result);
13271
+ return text(JSON.stringify(result, null, 2));
13272
+ }
13273
+ );
13162
13274
  var transport = new StdioServerTransport();
13163
13275
  await server.connect(transport);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@utilix-tech/mcp",
3
- "version": "0.82.0",
3
+ "version": "0.83.0",
4
4
  "description": "MCP server exposing 198 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",