@utilix-tech/mcp 0.91.0 → 0.92.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.92.0
6
+ - Add `build_clear_site_data_header`/`parse_clear_site_data_header` tools: build a `Clear-Site-Data` response header from directives (cache, cookies, storage, executionContexts, or `*` for all of them), flagging redundant directives alongside the wildcard, or parse an existing header back into its directives.
7
+ - Add `summarize_csv_column_stats` tool: parse CSV/TSV data and compute per-column null/unique counts plus min/max/mean/median/standard deviation for numeric columns, or the most common value for categorical ones.
8
+
5
9
  ## 0.91.0
6
10
  - Add `find_yaml_duplicate_keys` tool: report every mapping key that collides with an earlier key in the same YAML mapping scope, with the line and column of each repeated occurrence.
7
11
  - Add `detect_negative_amortization` tool: check whether a proposed loan payment covers the interest accruing each period and, if not, project the shortfall and the resulting balance growth, optionally flagging the month a balance cap is crossed.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @utilix-tech/mcp
2
2
 
3
- MCP server exposing 274 developer utility tools to Claude, Cursor, VS Code Copilot, and any [Model Context Protocol](https://modelcontextprotocol.io) compatible AI assistant.
3
+ MCP server exposing 277 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
 
@@ -224,7 +224,7 @@ claude mcp add utilix -- npx -y @utilix-tech/mcp
224
224
  | `calculate_sales_commission` | Compute a sales commission from either a flat percent rate applied to the whole sale amount, or a tiered (marginal) rate structure where each tier applies only to the slice of sales within it, plus an optional base salary added to the total payout |
225
225
  | `intersect_semver_ranges` | Determine whether two semver ranges overlap and, if so, the intersecting range, supporting the same caret/tilde/comparison-operator/exact-version syntax as the semver tool |
226
226
 
227
- ### API / Network (51 tools)
227
+ ### API / Network (53 tools)
228
228
  | Tool | Description |
229
229
  |------|-------------|
230
230
  | `decode_jwt` | Decode a JWT: header, payload, and expiry |
@@ -281,6 +281,8 @@ claude mcp add utilix -- npx -y @utilix-tech/mcp
281
281
  | `calculate_cookie_size_budget` | Parse a Cookie header's name=value pairs and compute each cookie's and the total header's byte size against RFC 6265's 4096-byte per-cookie guarantee and a configurable budget |
282
282
  | `verify_webhook_signature` | Recompute an HMAC over a raw webhook payload and compare it to a provided signature, accepting a `sha256=` prefix style and matching against hex or base64 encoding |
283
283
  | `score_security_headers` | Score a pasted block of raw HTTP response headers against commonly recommended security headers (CSP, HSTS, X-Content-Type-Options, X-Frame-Options, Referrer-Policy, Permissions-Policy, Cross-Origin-*), with an overall 0-100 score and A-F letter grade |
284
+ | `build_clear_site_data_header` | Build a Clear-Site-Data response header from directives (cache, cookies, storage, executionContexts, or * for all of them), flagging redundant directives alongside the wildcard |
285
+ | `parse_clear_site_data_header` | Parse an existing Clear-Site-Data header value into its individual directives |
284
286
 
285
287
  ### Color (4 tools)
286
288
  | Tool | Description |
@@ -322,7 +324,7 @@ claude mcp add utilix -- npx -y @utilix-tech/mcp
322
324
  | `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 |
323
325
  | `build_character_class` | Assemble a [...] regex character class from named building blocks (lowercase, uppercase, digits, whitespace), custom ranges, and literal custom characters, with optional negation |
324
326
 
325
- ### Data (20 tools)
327
+ ### Data (21 tools)
326
328
  | Tool | Description |
327
329
  |------|-------------|
328
330
  | `validate_yaml` | Validate YAML syntax |
@@ -345,6 +347,7 @@ claude mcp add utilix -- npx -y @utilix-tech/mcp
345
347
  | `align_markdown_tables` | Reformat every GitHub-Flavored-Markdown pipe table in a document so each column's pipes line up visually, preserving left/center/right alignment markers |
346
348
  | `validate_markdown_heading_hierarchy` | Scan a Markdown document's ATX headings and flag heading-structure issues: skipped levels, multiple H1s, empty headings, duplicate headings, and a document that doesn't start at H1 |
347
349
  | `check_markdown_internal_links` | Scan a Markdown document's ATX headings, compute the GitHub-style anchor slug for each, and check every [text](#slug) internal link against them, flagging links that point at a heading that doesn't exist |
350
+ | `summarize_csv_column_stats` | Parse CSV/TSV data and compute per-column summary statistics: null/unique counts for every column, plus min/max/mean/median/standard deviation for numeric columns or the most common value for categorical ones |
348
351
 
349
352
  ### CSS (2 tools)
350
353
  | Tool | Description |
package/dist/index.js CHANGED
@@ -14560,5 +14560,177 @@ server.tool(
14560
14560
  return text(JSON.stringify(result, null, 2));
14561
14561
  }
14562
14562
  );
14563
+ var _CLEAR_SITE_DATA_DIRECTIVES_MCP = /* @__PURE__ */ new Set(["cache", "cookies", "storage", "executionContexts", "*"]);
14564
+ function _buildClearSiteDataMcp(directives) {
14565
+ if (!Array.isArray(directives) || directives.length === 0) {
14566
+ return { error: "directives must be a non-empty array" };
14567
+ }
14568
+ const seen = /* @__PURE__ */ new Set();
14569
+ const normalized = [];
14570
+ for (const raw of directives) {
14571
+ if (!_CLEAR_SITE_DATA_DIRECTIVES_MCP.has(raw)) {
14572
+ return { error: `Unrecognized directive "${raw}". Valid directives: cache, cookies, storage, executionContexts, *` };
14573
+ }
14574
+ if (seen.has(raw)) continue;
14575
+ seen.add(raw);
14576
+ normalized.push(raw);
14577
+ }
14578
+ const warnings = [];
14579
+ if (normalized.includes("*") && normalized.length > 1) {
14580
+ warnings.push('"*" already clears cache, cookies, storage, and executionContexts (plus any future data types the spec adds), so listing other directives alongside it is redundant.');
14581
+ }
14582
+ const header = normalized.map((d) => `"${d}"`).join(", ");
14583
+ return {
14584
+ header,
14585
+ headerName: "Clear-Site-Data",
14586
+ snippet: `Clear-Site-Data: ${header}`,
14587
+ directives: normalized,
14588
+ warnings
14589
+ };
14590
+ }
14591
+ function _parseClearSiteDataMcp(raw) {
14592
+ if (!raw || raw.trim() === "") return { error: "Input is empty" };
14593
+ const withoutLabel = raw.trim().replace(/^clear-site-data:\s*/i, "");
14594
+ const tokens = withoutLabel.split(",").map((t) => t.trim()).filter((t) => t !== "");
14595
+ if (tokens.length === 0) return { error: "No directives found" };
14596
+ const directives = [];
14597
+ for (const token of tokens) {
14598
+ const match = token.match(/^"(.*)"$/);
14599
+ const value = match ? match[1] : token;
14600
+ if (!_CLEAR_SITE_DATA_DIRECTIVES_MCP.has(value)) {
14601
+ return { error: `Unrecognized directive "${token}"; each directive should be a quoted string like "cookies"` };
14602
+ }
14603
+ directives.push(value);
14604
+ }
14605
+ return _buildClearSiteDataMcp(directives);
14606
+ }
14607
+ server.tool(
14608
+ "build_clear_site_data_header",
14609
+ "Build a Clear-Site-Data response-header value from a list of directives (cache, cookies, storage, executionContexts, or * for all of them), flagging redundant directives alongside the wildcard.",
14610
+ {
14611
+ directives: z.array(z.enum(["cache", "cookies", "storage", "executionContexts", "*"])).min(1).describe("Directives to clear")
14612
+ },
14613
+ async ({ directives }) => {
14614
+ const result = _buildClearSiteDataMcp(directives);
14615
+ if ("error" in result) return errText(result);
14616
+ return text(JSON.stringify(result, null, 2));
14617
+ }
14618
+ );
14619
+ server.tool(
14620
+ "parse_clear_site_data_header",
14621
+ 'Parse an existing Clear-Site-Data header value (with or without a leading "Clear-Site-Data:" label, quoted or unquoted) into its individual directives.',
14622
+ {
14623
+ header: z.string().min(1).describe("The Clear-Site-Data header value to parse")
14624
+ },
14625
+ async ({ header }) => {
14626
+ const result = _parseClearSiteDataMcp(header);
14627
+ if ("error" in result) return errText(result);
14628
+ return text(JSON.stringify(result, null, 2));
14629
+ }
14630
+ );
14631
+ function _parseCsvForStatsMcp(input, delimiter, hasHeader) {
14632
+ if (!input || input.trim() === "") return { error: "Input is empty" };
14633
+ const lines = input.replace(/\r\n/g, "\n").split("\n").filter((l) => l.trim() !== "");
14634
+ if (lines.length === 0) return { error: "No data found" };
14635
+ function parseRow(line) {
14636
+ const fields = [];
14637
+ let cur = "";
14638
+ let inQuote = false;
14639
+ for (let i = 0; i < line.length; i++) {
14640
+ if (line[i] === '"') {
14641
+ if (inQuote && line[i + 1] === '"') {
14642
+ cur += '"';
14643
+ i++;
14644
+ } else {
14645
+ inQuote = !inQuote;
14646
+ }
14647
+ } else if (line[i] === delimiter && !inQuote) {
14648
+ fields.push(cur);
14649
+ cur = "";
14650
+ } else {
14651
+ cur += line[i];
14652
+ }
14653
+ }
14654
+ fields.push(cur);
14655
+ return fields;
14656
+ }
14657
+ const allRows = lines.map(parseRow);
14658
+ const headers = hasHeader ? allRows[0] : allRows[0].map((_, idx) => `Column ${idx + 1}`);
14659
+ const rows = hasHeader ? allRows.slice(1) : allRows;
14660
+ if (rows.length === 0) return { error: "No data rows found" };
14661
+ return { headers, rows };
14662
+ }
14663
+ var _CSV_STATS_NUMERIC_RE_MCP = /^-?\d+(\.\d+)?([eE][+-]?\d+)?$/;
14664
+ function _parseCsvStatsNumericMcp(raw) {
14665
+ if (!_CSV_STATS_NUMERIC_RE_MCP.test(raw)) return null;
14666
+ const n = Number(raw);
14667
+ return Number.isFinite(n) ? n : null;
14668
+ }
14669
+ function _summarizeCsvColumnStatsMcp(input, delimiter, hasHeader) {
14670
+ const parsed = _parseCsvForStatsMcp(input, delimiter, hasHeader);
14671
+ if ("error" in parsed) return parsed;
14672
+ const columns = parsed.headers.map((name, colIdx) => {
14673
+ let nullCount = 0;
14674
+ let nonNumericCount = 0;
14675
+ const numericValues = [];
14676
+ const freq = /* @__PURE__ */ new Map();
14677
+ for (const row of parsed.rows) {
14678
+ const raw = (row[colIdx] ?? "").trim();
14679
+ if (raw === "") {
14680
+ nullCount++;
14681
+ continue;
14682
+ }
14683
+ freq.set(raw, (freq.get(raw) ?? 0) + 1);
14684
+ const n = _parseCsvStatsNumericMcp(raw);
14685
+ if (n !== null) numericValues.push(n);
14686
+ else nonNumericCount++;
14687
+ }
14688
+ const nonEmptyCount = parsed.rows.length - nullCount;
14689
+ const isNumeric = nonEmptyCount > 0 && nonNumericCount === 0;
14690
+ let mostCommon;
14691
+ for (const [value, count] of freq) {
14692
+ if (!mostCommon || count > mostCommon.count) mostCommon = { value, count };
14693
+ }
14694
+ const stats = {
14695
+ name,
14696
+ totalCount: parsed.rows.length,
14697
+ nullCount,
14698
+ uniqueCount: freq.size,
14699
+ isNumeric,
14700
+ numericCount: numericValues.length
14701
+ };
14702
+ if (isNumeric && numericValues.length > 0) {
14703
+ const sorted = [...numericValues].sort((a, b) => a - b);
14704
+ const sum = sorted.reduce((a, b) => a + b, 0);
14705
+ const mean = sum / sorted.length;
14706
+ const mid = Math.floor(sorted.length / 2);
14707
+ const median = sorted.length % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid];
14708
+ const variance = sorted.reduce((acc, v) => acc + (v - mean) ** 2, 0) / sorted.length;
14709
+ stats.min = sorted[0];
14710
+ stats.max = sorted[sorted.length - 1];
14711
+ stats.mean = mean;
14712
+ stats.median = median;
14713
+ stats.stdDev = Math.sqrt(variance);
14714
+ } else {
14715
+ stats.mostCommon = mostCommon;
14716
+ }
14717
+ return stats;
14718
+ });
14719
+ return { columns, rowCount: parsed.rows.length };
14720
+ }
14721
+ server.tool(
14722
+ "summarize_csv_column_stats",
14723
+ "Parse CSV/TSV data and compute per-column summary statistics: null and unique-value counts for every column, plus min/max/mean/median/standard deviation for any column where every non-empty value parses as a plain number. A column with any non-numeric non-empty value is treated as categorical, reporting the most frequent value and its count instead.",
14724
+ {
14725
+ csv: z.string().min(1).describe("The CSV/TSV data, with a header row by default"),
14726
+ delimiter: z.string().optional().describe('Field delimiter, default ","'),
14727
+ hasHeader: z.boolean().optional().describe("Whether the first row is a header row, default true")
14728
+ },
14729
+ async ({ csv, delimiter, hasHeader }) => {
14730
+ const result = _summarizeCsvColumnStatsMcp(csv, delimiter?.trim() || ",", hasHeader !== false);
14731
+ if ("error" in result) return errText(result);
14732
+ return text(JSON.stringify(result, null, 2));
14733
+ }
14734
+ );
14563
14735
  var transport = new StdioServerTransport();
14564
14736
  await server.connect(transport);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@utilix-tech/mcp",
3
- "version": "0.91.0",
4
- "description": "MCP server exposing 272 Utilix developer tools to Claude, Cursor, VS Code Copilot, and any MCP-compatible AI assistant.",
3
+ "version": "0.92.0",
4
+ "description": "MCP server exposing 277 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/mcp",