@utilix-tech/mcp 0.86.0 → 0.87.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.87.0
6
+ - Add `validate_markdown_heading_hierarchy` tool: scan a Markdown document's ATX headings and flag heading-structure issues: skipped levels, multiple H1s, empty headings, duplicate headings at the same level, and a document that doesn't start at H1.
7
+
5
8
  ## 0.86.0
6
9
  - Add `calculate_sales_commission` tool: compute a flat or tiered (marginal) sales commission from a sale amount, where tiered mode applies each rate only to the slice of sales within that tier, plus an optional base salary added to the total payout.
7
10
  - Add `build_character_class` tool: assemble a `[...]` regex character class from named building blocks (lowercase, uppercase, digits, whitespace), custom ranges, and literal custom characters, with optional negation.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @utilix-tech/mcp
2
2
 
3
- MCP server exposing 266 developer utility tools to Claude, Cursor, VS Code Copilot, and any [Model Context Protocol](https://modelcontextprotocol.io) compatible AI assistant.
3
+ MCP server exposing 267 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
 
@@ -317,7 +317,7 @@ claude mcp add utilix -- npx -y @utilix-tech/mcp
317
317
  | `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 |
318
318
  | `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 |
319
319
 
320
- ### Data (16 tools)
320
+ ### Data (18 tools)
321
321
  | Tool | Description |
322
322
  |------|-------------|
323
323
  | `validate_yaml` | Validate YAML syntax |
@@ -337,6 +337,7 @@ claude mcp add utilix -- npx -y @utilix-tech/mcp
337
337
  | `analyze_caption_speed` | Parse an SRT or WebVTT file and report the words-per-minute reading speed of every cue, flagging ones that exceed common broadcast accessibility guidelines |
338
338
  | `infer_csv_column_types` | Parse CSV/TSV data and infer each column's data type (integer, float, boolean, ISO 8601 date, or string), flagging nullable columns and empty-cell counts |
339
339
  | `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 |
340
+ | `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 |
340
341
 
341
342
  ### CSS (2 tools)
342
343
  | Tool | Description |
package/dist/index.js CHANGED
@@ -6793,6 +6793,95 @@ server.tool(
6793
6793
  return text(JSON.stringify(result, null, 2));
6794
6794
  }
6795
6795
  );
6796
+ var _HEADING_HIERARCHY_RE_MCP = /^(#{1,6})(?!#)(?:[ \t]+(.*?))?[ \t]*#*[ \t]*$/;
6797
+ function _validateMarkdownHeadingHierarchyMcp(markdown) {
6798
+ const lines = markdown.replace(/\r\n/g, "\n").split("\n");
6799
+ const headings = [];
6800
+ let inFence = false;
6801
+ let fenceMarker = "";
6802
+ for (let i = 0; i < lines.length; i++) {
6803
+ const line = lines[i];
6804
+ const fenceMatch = line.match(/^\s*(```+|~~~+)/);
6805
+ if (fenceMatch) {
6806
+ if (!inFence) {
6807
+ inFence = true;
6808
+ fenceMarker = fenceMatch[1][0];
6809
+ } else if (fenceMatch[1][0] === fenceMarker) {
6810
+ inFence = false;
6811
+ }
6812
+ continue;
6813
+ }
6814
+ if (inFence) continue;
6815
+ const match = line.match(_HEADING_HIERARCHY_RE_MCP);
6816
+ if (!match) continue;
6817
+ const level = match[1].length;
6818
+ const text2 = _stripInlineMarkdownMcp(match[2] ?? "");
6819
+ headings.push({ line: i + 1, level, text: text2 });
6820
+ }
6821
+ const issues = [];
6822
+ const seen = /* @__PURE__ */ new Map();
6823
+ let h1Count = 0;
6824
+ let firstH1Line = null;
6825
+ let prevLevel = null;
6826
+ headings.forEach((h, idx) => {
6827
+ if (h.text === "") {
6828
+ issues.push({ line: h.line, type: "empty-heading", message: `Heading at line ${h.line} has no text` });
6829
+ }
6830
+ if (idx === 0 && h.level !== 1) {
6831
+ issues.push({
6832
+ line: h.line,
6833
+ type: "starts-below-h1",
6834
+ message: `Document starts with an H${h.level} heading instead of H1`
6835
+ });
6836
+ }
6837
+ if (h.level === 1) {
6838
+ h1Count++;
6839
+ if (h1Count === 1) {
6840
+ firstH1Line = h.line;
6841
+ } else {
6842
+ issues.push({
6843
+ line: h.line,
6844
+ type: "multiple-h1",
6845
+ message: `Multiple H1 headings found: another H1 at line ${h.line} (first was at line ${firstH1Line})`
6846
+ });
6847
+ }
6848
+ }
6849
+ if (prevLevel !== null && h.level > prevLevel + 1) {
6850
+ const skipped = h.level - prevLevel > 2 ? `H${prevLevel + 1}-H${h.level - 1}` : `H${prevLevel + 1}`;
6851
+ issues.push({
6852
+ line: h.line,
6853
+ type: "skipped-level",
6854
+ message: `Heading level jumps from H${prevLevel} to H${h.level} at line ${h.line}, skipping ${skipped}`
6855
+ });
6856
+ }
6857
+ prevLevel = h.level;
6858
+ if (h.text !== "") {
6859
+ const key = `${h.level}:${h.text.toLowerCase()}`;
6860
+ const earlierLine = seen.get(key);
6861
+ if (earlierLine !== void 0) {
6862
+ issues.push({
6863
+ line: h.line,
6864
+ type: "duplicate-heading",
6865
+ message: `Heading "${h.text}" at line ${h.line} duplicates an H${h.level} heading at line ${earlierLine}`
6866
+ });
6867
+ } else {
6868
+ seen.set(key, h.line);
6869
+ }
6870
+ }
6871
+ });
6872
+ return { headings, issues, h1Count };
6873
+ }
6874
+ server.tool(
6875
+ "validate_markdown_heading_hierarchy",
6876
+ "Scan a Markdown document's ATX headings and flag heading-structure issues: a level that skips one or more levels going deeper, more than one H1, an empty heading, a duplicate heading at the same level, or a document that doesn't start at H1. Setext-style headings are not recognized, and headings inside fenced code blocks are ignored.",
6877
+ {
6878
+ markdown: z.string().describe("The Markdown document to scan")
6879
+ },
6880
+ async ({ markdown }) => {
6881
+ const result = _validateMarkdownHeadingHierarchyMcp(markdown);
6882
+ return text(JSON.stringify(result, null, 2));
6883
+ }
6884
+ );
6796
6885
  var _COMMIT_DEFAULT_TYPES_MCP = ["build", "chore", "ci", "docs", "feat", "fix", "perf", "refactor", "revert", "style", "test"];
6797
6886
  var _COMMIT_HEADER_RE_MCP = /^([A-Za-z][A-Za-z0-9-]*)(\(([^()]*)\))?(!)?: (.+)$/;
6798
6887
  var _COMMIT_FOOTER_LINE_RE_MCP = /^([A-Za-z][A-Za-z0-9-]*|BREAKING CHANGE)(: ?| #)(.*)$/;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@utilix-tech/mcp",
3
- "version": "0.86.0",
3
+ "version": "0.87.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",