@utilix-tech/mcp 0.85.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 +7 -0
- package/README.md +7 -4
- package/dist/index.js +264 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
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
|
+
|
|
8
|
+
## 0.86.0
|
|
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.
|
|
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.
|
|
11
|
+
|
|
5
12
|
## 0.85.0
|
|
6
13
|
- Add `calculate_credit_card_minimum_payment` tool: simulate paying only the minimum payment on a revolving credit card balance, where the minimum is the larger of a percent of the balance or a fixed dollar floor, and report months/years to payoff, total interest, and total paid.
|
|
7
14
|
- Add `check_dash_usage` tool: scan text for likely dash misuse: a hyphen surrounded by spaces, an unconverted double hyphen, and inconsistent spacing around em dashes across the same document.
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @utilix-tech/mcp
|
|
2
2
|
|
|
3
|
-
MCP server exposing
|
|
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
|
|
|
@@ -168,7 +168,7 @@ claude mcp add utilix -- npx -y @utilix-tech/mcp
|
|
|
168
168
|
| `convert_timezone` | Convert a date/time between timezones |
|
|
169
169
|
| `convert_iso8601_duration` | Parse an ISO 8601 duration into components and total seconds, or format seconds into a duration string |
|
|
170
170
|
|
|
171
|
-
### Units (
|
|
171
|
+
### Units (48 tools)
|
|
172
172
|
| Tool | Description |
|
|
173
173
|
|------|-------------|
|
|
174
174
|
| `convert_bytes` | Convert between B, KB, MB, GB, TB, KiB, MiB, GiB, TiB |
|
|
@@ -218,6 +218,7 @@ claude mcp add utilix -- npx -y @utilix-tech/mcp
|
|
|
218
218
|
| `estimate_data_transfer_time` | Estimate the theoretical time to transfer a file of a given size over a connection of a given speed, or (given a target time instead) the bandwidth required to hit it |
|
|
219
219
|
| `calculate_real_rate_of_return` | Apply the Fisher equation to solve for whichever of the nominal rate, inflation rate, or inflation-adjusted real rate is missing, given exactly two of the three |
|
|
220
220
|
| `calculate_credit_card_minimum_payment` | Simulate paying only the minimum payment on a revolving credit card balance, where the minimum is the larger of a percent of the balance or a fixed dollar floor, and report months/years to payoff, total interest, and total paid |
|
|
221
|
+
| `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 |
|
|
221
222
|
|
|
222
223
|
### API / Network (51 tools)
|
|
223
224
|
| Tool | Description |
|
|
@@ -285,7 +286,7 @@ claude mcp add utilix -- npx -y @utilix-tech/mcp
|
|
|
285
286
|
| `color_palette` | Generate a color palette from a base color |
|
|
286
287
|
| `color_shades` | Generate shades or tints from a base color |
|
|
287
288
|
|
|
288
|
-
### Code (
|
|
289
|
+
### Code (27 tools)
|
|
289
290
|
| Tool | Description |
|
|
290
291
|
|------|-------------|
|
|
291
292
|
| `format_sql` | Format a SQL query with proper indentation |
|
|
@@ -314,8 +315,9 @@ claude mcp add utilix -- npx -y @utilix-tech/mcp
|
|
|
314
315
|
| `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 |
|
|
315
316
|
| `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 |
|
|
316
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
|
+
| `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 |
|
|
317
319
|
|
|
318
|
-
### Data (
|
|
320
|
+
### Data (18 tools)
|
|
319
321
|
| Tool | Description |
|
|
320
322
|
|------|-------------|
|
|
321
323
|
| `validate_yaml` | Validate YAML syntax |
|
|
@@ -335,6 +337,7 @@ claude mcp add utilix -- npx -y @utilix-tech/mcp
|
|
|
335
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 |
|
|
336
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 |
|
|
337
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 |
|
|
338
341
|
|
|
339
342
|
### CSS (2 tools)
|
|
340
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)(: ?| #)(.*)$/;
|
|
@@ -13677,5 +13766,180 @@ server.tool(
|
|
|
13677
13766
|
return text(JSON.stringify(_checkDashUsageMcp(input), null, 2));
|
|
13678
13767
|
}
|
|
13679
13768
|
);
|
|
13769
|
+
var _CommissionTierSchema = z.object({
|
|
13770
|
+
upTo: z.number().nullable().describe("Upper bound of this tier (exclusive). null means unbounded and is only valid on the last tier."),
|
|
13771
|
+
ratePercent: z.number().min(0).describe("Commission rate as a percent applied to the slice of salesAmount within this tier")
|
|
13772
|
+
});
|
|
13773
|
+
function _calculateSalesCommissionMcp(salesAmount, flatRatePercent, tiers, baseSalary) {
|
|
13774
|
+
if (!Number.isFinite(salesAmount) || salesAmount < 0) {
|
|
13775
|
+
return { error: "salesAmount must be a finite, non-negative number" };
|
|
13776
|
+
}
|
|
13777
|
+
if (!Number.isFinite(baseSalary) || baseSalary < 0) {
|
|
13778
|
+
return { error: "baseSalary must be a finite, non-negative number when provided" };
|
|
13779
|
+
}
|
|
13780
|
+
const hasFlat = flatRatePercent !== void 0;
|
|
13781
|
+
const hasTiers = tiers !== void 0;
|
|
13782
|
+
if (hasFlat === hasTiers) {
|
|
13783
|
+
return { error: "Provide exactly one of flatRatePercent or tiers, not both and not neither" };
|
|
13784
|
+
}
|
|
13785
|
+
const round4 = (n) => Math.round(n * 1e4) / 1e4;
|
|
13786
|
+
if (hasFlat) {
|
|
13787
|
+
if (!Number.isFinite(flatRatePercent) || flatRatePercent < 0) {
|
|
13788
|
+
return { error: "flatRatePercent must be a finite, non-negative number" };
|
|
13789
|
+
}
|
|
13790
|
+
const commission2 = salesAmount * flatRatePercent / 100;
|
|
13791
|
+
return {
|
|
13792
|
+
salesAmount: round4(salesAmount),
|
|
13793
|
+
mode: "flat",
|
|
13794
|
+
commission: round4(commission2),
|
|
13795
|
+
baseSalary: round4(baseSalary),
|
|
13796
|
+
totalPayout: round4(baseSalary + commission2)
|
|
13797
|
+
};
|
|
13798
|
+
}
|
|
13799
|
+
const tierList = tiers;
|
|
13800
|
+
if (tierList.length === 0) {
|
|
13801
|
+
return { error: "tiers must contain at least one entry" };
|
|
13802
|
+
}
|
|
13803
|
+
let previousUpTo = 0;
|
|
13804
|
+
for (let i = 0; i < tierList.length; i++) {
|
|
13805
|
+
const tier = tierList[i];
|
|
13806
|
+
const isLast = i === tierList.length - 1;
|
|
13807
|
+
if (!Number.isFinite(tier.ratePercent) || tier.ratePercent < 0) {
|
|
13808
|
+
return { error: `tiers[${i}].ratePercent must be a finite, non-negative number` };
|
|
13809
|
+
}
|
|
13810
|
+
if (tier.upTo === null) {
|
|
13811
|
+
if (!isLast) {
|
|
13812
|
+
return { error: `tiers[${i}].upTo may only be null on the last tier (an unbounded top tier)` };
|
|
13813
|
+
}
|
|
13814
|
+
} else {
|
|
13815
|
+
if (!Number.isFinite(tier.upTo) || tier.upTo <= previousUpTo) {
|
|
13816
|
+
return { error: `tiers[${i}].upTo must be a finite number greater than the previous tier's upTo (${previousUpTo})` };
|
|
13817
|
+
}
|
|
13818
|
+
previousUpTo = tier.upTo;
|
|
13819
|
+
}
|
|
13820
|
+
}
|
|
13821
|
+
let lowerBound = 0;
|
|
13822
|
+
let commission = 0;
|
|
13823
|
+
const breakdown = [];
|
|
13824
|
+
for (const tier of tierList) {
|
|
13825
|
+
const upper = tier.upTo === null ? Infinity : tier.upTo;
|
|
13826
|
+
const amountInRange = Math.min(Math.max(salesAmount - lowerBound, 0), upper - lowerBound);
|
|
13827
|
+
const commissionForRange = amountInRange * tier.ratePercent / 100;
|
|
13828
|
+
commission += commissionForRange;
|
|
13829
|
+
breakdown.push({
|
|
13830
|
+
rangeStart: round4(lowerBound),
|
|
13831
|
+
rangeEnd: tier.upTo === null ? null : round4(tier.upTo),
|
|
13832
|
+
ratePercent: tier.ratePercent,
|
|
13833
|
+
amountInRange: round4(amountInRange),
|
|
13834
|
+
commissionForRange: round4(commissionForRange)
|
|
13835
|
+
});
|
|
13836
|
+
lowerBound = upper;
|
|
13837
|
+
}
|
|
13838
|
+
return {
|
|
13839
|
+
salesAmount: round4(salesAmount),
|
|
13840
|
+
mode: "tiered",
|
|
13841
|
+
commission: round4(commission),
|
|
13842
|
+
baseSalary: round4(baseSalary),
|
|
13843
|
+
totalPayout: round4(baseSalary + commission),
|
|
13844
|
+
breakdown
|
|
13845
|
+
};
|
|
13846
|
+
}
|
|
13847
|
+
server.tool(
|
|
13848
|
+
"calculate_sales_commission",
|
|
13849
|
+
"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 the sale amount within it, the same way progressive tax brackets work. An optional base salary is added to the commission to get a total payout. Pure arithmetic, no live data.",
|
|
13850
|
+
{
|
|
13851
|
+
salesAmount: z.number().min(0).describe("The total sale amount commission is computed on"),
|
|
13852
|
+
flatRatePercent: z.number().min(0).optional().describe("A flat commission rate as a percent, applied to the whole salesAmount. Mutually exclusive with tiers."),
|
|
13853
|
+
tiers: z.array(_CommissionTierSchema).optional().describe("Marginal commission tiers, sorted ascending by upTo. Mutually exclusive with flatRatePercent."),
|
|
13854
|
+
baseSalary: z.number().min(0).optional().describe("Optional base salary added to the commission to get totalPayout, default 0")
|
|
13855
|
+
},
|
|
13856
|
+
async ({ salesAmount, flatRatePercent, tiers, baseSalary }) => {
|
|
13857
|
+
const result = _calculateSalesCommissionMcp(salesAmount, flatRatePercent, tiers, baseSalary ?? 0);
|
|
13858
|
+
if ("error" in result) return errText(result);
|
|
13859
|
+
return text(JSON.stringify(result, null, 2));
|
|
13860
|
+
}
|
|
13861
|
+
);
|
|
13862
|
+
var _CHAR_CLASS_SPECIAL_CHARS_MCP = /* @__PURE__ */ new Set(["]", "\\", "^", "-"]);
|
|
13863
|
+
function _escapeForCharClassMcp(ch) {
|
|
13864
|
+
return _CHAR_CLASS_SPECIAL_CHARS_MCP.has(ch) ? `\\${ch}` : ch;
|
|
13865
|
+
}
|
|
13866
|
+
var _CHAR_CLASS_RANGE_RE_MCP = /^(.)-(.)$/u;
|
|
13867
|
+
function _buildCharacterClassMcp(lowercase, uppercase, digits, whitespace, customRanges, customChars, negate) {
|
|
13868
|
+
const hasAny = lowercase || uppercase || digits || whitespace || customRanges.length > 0 || customChars.length > 0;
|
|
13869
|
+
if (!hasAny) {
|
|
13870
|
+
return { error: "At least one character set, custom range, or custom character must be specified" };
|
|
13871
|
+
}
|
|
13872
|
+
const parts = [];
|
|
13873
|
+
const includes = [];
|
|
13874
|
+
if (lowercase) {
|
|
13875
|
+
parts.push("a-z");
|
|
13876
|
+
includes.push("a-z: lowercase letters");
|
|
13877
|
+
}
|
|
13878
|
+
if (uppercase) {
|
|
13879
|
+
parts.push("A-Z");
|
|
13880
|
+
includes.push("A-Z: uppercase letters");
|
|
13881
|
+
}
|
|
13882
|
+
if (digits) {
|
|
13883
|
+
parts.push("0-9");
|
|
13884
|
+
includes.push("0-9: digits");
|
|
13885
|
+
}
|
|
13886
|
+
if (whitespace) {
|
|
13887
|
+
parts.push("\\s");
|
|
13888
|
+
includes.push("\\s: whitespace characters (space, tab, newline, etc.)");
|
|
13889
|
+
}
|
|
13890
|
+
for (let i = 0; i < customRanges.length; i++) {
|
|
13891
|
+
const range = customRanges[i];
|
|
13892
|
+
const match = _CHAR_CLASS_RANGE_RE_MCP.exec(range);
|
|
13893
|
+
if (!match) {
|
|
13894
|
+
return { error: `customRanges[${i}] ("${range}") must be exactly two characters joined by a hyphen, e.g. "f-k"` };
|
|
13895
|
+
}
|
|
13896
|
+
const [, start, end] = match;
|
|
13897
|
+
if (start.codePointAt(0) > end.codePointAt(0)) {
|
|
13898
|
+
return { error: `customRanges[${i}] ("${range}") has a start character that comes after its end character` };
|
|
13899
|
+
}
|
|
13900
|
+
parts.push(`${_escapeForCharClassMcp(start)}-${_escapeForCharClassMcp(end)}`);
|
|
13901
|
+
includes.push(`${range}: custom range from '${start}' to '${end}' inclusive`);
|
|
13902
|
+
}
|
|
13903
|
+
if (customChars.length > 0) {
|
|
13904
|
+
const seen = /* @__PURE__ */ new Set();
|
|
13905
|
+
const unique = [];
|
|
13906
|
+
for (const ch of customChars) {
|
|
13907
|
+
if (!seen.has(ch)) {
|
|
13908
|
+
seen.add(ch);
|
|
13909
|
+
unique.push(ch);
|
|
13910
|
+
}
|
|
13911
|
+
}
|
|
13912
|
+
parts.push(unique.map(_escapeForCharClassMcp).join(""));
|
|
13913
|
+
includes.push(`custom characters: ${unique.join(", ")}`);
|
|
13914
|
+
}
|
|
13915
|
+
const pattern = `[${negate ? "^" : ""}${parts.join("")}]`;
|
|
13916
|
+
return { pattern, negate, includes };
|
|
13917
|
+
}
|
|
13918
|
+
server.tool(
|
|
13919
|
+
"build_character_class",
|
|
13920
|
+
"Assemble a [...] regex character class from named building blocks (lowercase, uppercase, digits, whitespace), custom character ranges, and literal custom characters, with optional negation. Pure string assembly, it never runs the resulting pattern against any input.",
|
|
13921
|
+
{
|
|
13922
|
+
lowercase: z.boolean().optional().describe("Include a-z"),
|
|
13923
|
+
uppercase: z.boolean().optional().describe("Include A-Z"),
|
|
13924
|
+
digits: z.boolean().optional().describe("Include 0-9"),
|
|
13925
|
+
whitespace: z.boolean().optional().describe("Include \\s (space, tab, newline, etc.)"),
|
|
13926
|
+
customRanges: z.array(z.string()).optional().describe('Custom ranges like "f-k", each exactly two characters joined by a hyphen'),
|
|
13927
|
+
customChars: z.string().optional().describe("Literal characters to include as-is; duplicates are ignored"),
|
|
13928
|
+
negate: z.boolean().optional().describe("Wrap the class in [^...] to match any character NOT in the set")
|
|
13929
|
+
},
|
|
13930
|
+
async ({ lowercase, uppercase, digits, whitespace, customRanges, customChars, negate }) => {
|
|
13931
|
+
const result = _buildCharacterClassMcp(
|
|
13932
|
+
lowercase ?? false,
|
|
13933
|
+
uppercase ?? false,
|
|
13934
|
+
digits ?? false,
|
|
13935
|
+
whitespace ?? false,
|
|
13936
|
+
customRanges ?? [],
|
|
13937
|
+
customChars ?? "",
|
|
13938
|
+
negate ?? false
|
|
13939
|
+
);
|
|
13940
|
+
if ("error" in result) return errText(result);
|
|
13941
|
+
return text(JSON.stringify(result, null, 2));
|
|
13942
|
+
}
|
|
13943
|
+
);
|
|
13680
13944
|
var transport = new StdioServerTransport();
|
|
13681
13945
|
await server.connect(transport);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@utilix-tech/mcp",
|
|
3
|
-
"version": "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",
|