@utilix-tech/mcp 0.9.0 → 0.10.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,9 @@
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.10.0
6
+ - Added Sitemap.xml Validator (`validate_sitemap`).
7
+
5
8
  ## 0.9.0
6
9
  - Added ID3 Tag Reader and .env.example Diff Checker.
7
10
 
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @utilix-tech/mcp
2
2
 
3
- MCP server exposing 106 developer utility tools to Claude, Cursor, VS Code Copilot, and any [Model Context Protocol](https://modelcontextprotocol.io) compatible AI assistant.
3
+ MCP server exposing 107 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
 
@@ -151,7 +151,7 @@ claude mcp add utilix -- npx -y @utilix-tech/mcp
151
151
  | `convert_bytes` | Convert between B, KB, MB, GB, TB, KiB, MiB, GiB, TiB |
152
152
  | `px_to_units` | Convert pixels to rem, em, pt, and vw |
153
153
 
154
- ### API / Network (6 tools)
154
+ ### API / Network (7 tools)
155
155
  | Tool | Description |
156
156
  |------|-------------|
157
157
  | `decode_jwt` | Decode a JWT: header, payload, and expiry |
@@ -160,6 +160,7 @@ claude mcp add utilix -- npx -y @utilix-tech/mcp
160
160
  | `curl_to_code` | Convert a cURL command to fetch, axios, Python, Go, PHP, or Ruby |
161
161
  | `cors_headers` | Generate CORS response headers for a configuration |
162
162
  | `parse_har` | Parse a DevTools .har network export into requests and summary stats |
163
+ | `validate_sitemap` | Validate a sitemap.xml or sitemap index against the sitemaps.org protocol |
163
164
 
164
165
  ### Color (4 tools)
165
166
  | Tool | Description |
package/dist/index.js CHANGED
@@ -1479,6 +1479,106 @@ server.tool(
1479
1479
  return text(JSON.stringify(result, null, 2));
1480
1480
  }
1481
1481
  );
1482
+ var _SITEMAP_VALID_CHANGEFREQ = /* @__PURE__ */ new Set(["always", "hourly", "daily", "weekly", "monthly", "yearly", "never"]);
1483
+ function _sitemapLineNumber(xml, index) {
1484
+ let line = 1;
1485
+ for (let i = 0; i < index; i++) {
1486
+ if (xml[i] === "\n") line++;
1487
+ }
1488
+ return line;
1489
+ }
1490
+ function _extractSitemapTag(block, tag) {
1491
+ const m = block.match(new RegExp(`<${tag}[^>]*>([\\s\\S]*?)</${tag}>`, "i"));
1492
+ return m ? m[1].trim() : void 0;
1493
+ }
1494
+ function _isValidW3CDatetime(s) {
1495
+ return /^\d{4}(-\d{2}(-\d{2}(T\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:\d{2}))?)?)?$/.test(s);
1496
+ }
1497
+ function _isAbsoluteHttpUrl(s) {
1498
+ return /^https?:\/\//i.test(s);
1499
+ }
1500
+ function _validateSitemap(xml) {
1501
+ if (!xml || !xml.trim()) return { error: "Input is empty" };
1502
+ const trimmed = xml.trim();
1503
+ const issues = [];
1504
+ if (!/^\s*<\?xml/i.test(trimmed)) {
1505
+ issues.push({ severity: "warning", line: 1, message: 'Missing XML declaration (<?xml version="1.0" encoding="UTF-8"?>)' });
1506
+ }
1507
+ const isUrlset = /<urlset[\s>]/i.test(trimmed);
1508
+ const isSitemapIndex = /<sitemapindex[\s>]/i.test(trimmed);
1509
+ if (!isUrlset && !isSitemapIndex) {
1510
+ return { error: "Not a valid sitemap: root element must be <urlset> or <sitemapindex>" };
1511
+ }
1512
+ if (isUrlset && isSitemapIndex) {
1513
+ return { error: "Ambiguous file: contains both <urlset> and <sitemapindex> root elements" };
1514
+ }
1515
+ const type = isUrlset ? "urlset" : "sitemapindex";
1516
+ if (isUrlset && !/xmlns\s*=\s*["']http:\/\/www\.sitemaps\.org\/schemas\/sitemap\/0\.9["']/i.test(trimmed)) {
1517
+ issues.push({ severity: "warning", line: 1, message: "Missing or non-standard xmlns namespace (expected http://www.sitemaps.org/schemas/sitemap/0.9)" });
1518
+ }
1519
+ const blockTag = isUrlset ? "url" : "sitemap";
1520
+ const blockRe = new RegExp(`<${blockTag}>([\\s\\S]*?)</${blockTag}>`, "gi");
1521
+ const seenLocs = /* @__PURE__ */ new Set();
1522
+ const entries = [];
1523
+ let match;
1524
+ while ((match = blockRe.exec(trimmed)) !== null) {
1525
+ const block = match[1];
1526
+ const line = _sitemapLineNumber(trimmed, match.index);
1527
+ const loc = _extractSitemapTag(block, "loc");
1528
+ if (!loc) {
1529
+ issues.push({ severity: "error", line, message: `<${blockTag}> entry is missing required <loc>` });
1530
+ continue;
1531
+ }
1532
+ if (!_isAbsoluteHttpUrl(loc)) {
1533
+ issues.push({ severity: "error", line, message: `<loc>${loc}</loc> must be an absolute URL starting with http:// or https://` });
1534
+ }
1535
+ if (/&(?!amp;|lt;|gt;|apos;|quot;|#\d+;)/.test(loc)) {
1536
+ issues.push({ severity: "error", line, message: `<loc>${loc}</loc> contains an unescaped '&' (use &amp; instead)` });
1537
+ }
1538
+ if (seenLocs.has(loc)) {
1539
+ issues.push({ severity: "warning", line, message: `Duplicate <loc>${loc}</loc>` });
1540
+ }
1541
+ seenLocs.add(loc);
1542
+ const lastmod = _extractSitemapTag(block, "lastmod");
1543
+ if (lastmod !== void 0 && !_isValidW3CDatetime(lastmod)) {
1544
+ issues.push({ severity: "warning", line, message: `<lastmod>${lastmod}</lastmod> is not a valid W3C datetime` });
1545
+ }
1546
+ if (type === "urlset") {
1547
+ const changefreq = _extractSitemapTag(block, "changefreq");
1548
+ if (changefreq !== void 0 && !_SITEMAP_VALID_CHANGEFREQ.has(changefreq.toLowerCase())) {
1549
+ issues.push({ severity: "error", line, message: `<changefreq>${changefreq}</changefreq> must be one of: ${[..._SITEMAP_VALID_CHANGEFREQ].join(", ")}` });
1550
+ }
1551
+ const priority = _extractSitemapTag(block, "priority");
1552
+ if (priority !== void 0) {
1553
+ const p = Number(priority);
1554
+ if (Number.isNaN(p) || p < 0 || p > 1) {
1555
+ issues.push({ severity: "error", line, message: `<priority>${priority}</priority> must be a number between 0.0 and 1.0` });
1556
+ }
1557
+ }
1558
+ entries.push({ loc, lastmod, changefreq, priority, line });
1559
+ } else {
1560
+ entries.push({ loc, lastmod, line });
1561
+ }
1562
+ }
1563
+ if (entries.length === 0) {
1564
+ issues.push({ severity: "error", line: 1, message: `No <${blockTag}> entries found` });
1565
+ }
1566
+ if (type === "urlset" && entries.length > 5e4) {
1567
+ issues.push({ severity: "error", line: 1, message: `Sitemap contains ${entries.length} URLs, exceeding the 50,000 URL limit per the sitemaps.org spec` });
1568
+ }
1569
+ const valid = !issues.some((i) => i.severity === "error");
1570
+ return { type, urlCount: entries.length, entries, issues, valid };
1571
+ }
1572
+ server.tool(
1573
+ "validate_sitemap",
1574
+ "Validate a sitemap.xml (urlset) or sitemap index (sitemapindex) against the sitemaps.org protocol: required <loc>, absolute URLs, valid <changefreq>/<priority>/<lastmod> values, the 50,000-URL limit, and duplicate entries.",
1575
+ { xml: z.string().describe("The sitemap.xml or sitemap index XML contents") },
1576
+ async ({ xml }) => {
1577
+ const result = _validateSitemap(xml);
1578
+ if ("error" in result) return errText(result);
1579
+ return text(JSON.stringify(result, null, 2));
1580
+ }
1581
+ );
1482
1582
  server.tool(
1483
1583
  "format_ndjson",
1484
1584
  "Validate newline-delimited JSON (NDJSON/JSON Lines), report per-line errors, and pretty-print each valid line.",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@utilix-tech/mcp",
3
- "version": "0.9.0",
4
- "description": "MCP server exposing 106 Utilix developer tools to Claude, Cursor, VS Code Copilot, and any MCP-compatible AI assistant.",
3
+ "version": "0.10.0",
4
+ "description": "MCP server exposing 107 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",