@utilix-tech/mcp 0.31.0 → 0.33.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 +8 -0
- package/README.md +9 -5
- package/dist/index.js +231 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
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.33.0
|
|
6
|
+
- Add `analyze_sentence_lengths` tool: histogram of sentence lengths in words, plus mean, median, standard deviation, and a coefficient-of-variation consistency label, distinct from readability grade scores.
|
|
7
|
+
- Add `format_few_shot_examples` tool: format input/output example pairs into a numbered, XML-tagged, or delimiter-separated few-shot prompt template.
|
|
8
|
+
|
|
9
|
+
## 0.32.0
|
|
10
|
+
- Add `calculate_emi` tool: equated monthly installment, total interest, and a year-by-year principal/interest breakdown from principal, annual rate, and tenure in years or months.
|
|
11
|
+
- Add `parse_markdown_front_matter` tool: split a Markdown document into its YAML front matter fields and body content, parsing scalars, inline arrays, and block lists.
|
|
12
|
+
|
|
5
13
|
## 0.31.0
|
|
6
14
|
- Add `json_schema_to_typescript` tool: convert a JSON Schema document into TypeScript interfaces/types, reading `required`/`enum`/`oneOf`/`anyOf`/`allOf`/`$ref` directly instead of inferring from an example payload.
|
|
7
15
|
- Add `estimate_password_entropy` tool: bits of entropy from a password's character pool, a strength label, and crack-time estimates across four guess-rate scenarios.
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @utilix-tech/mcp
|
|
2
2
|
|
|
3
|
-
MCP server exposing
|
|
3
|
+
MCP server exposing 152 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
|
|
|
@@ -128,7 +128,7 @@ claude mcp add utilix -- npx -y @utilix-tech/mcp
|
|
|
128
128
|
| `generate_qr` | Generate a QR code as an SVG string |
|
|
129
129
|
| `generate_random_data` | Generate random mock data: names, emails, UUIDs, IPs, etc. |
|
|
130
130
|
|
|
131
|
-
### Text (
|
|
131
|
+
### Text (14 tools)
|
|
132
132
|
| Tool | Description |
|
|
133
133
|
|------|-------------|
|
|
134
134
|
| `convert_case` | Convert text case: camelCase, snake_case, PascalCase, etc. |
|
|
@@ -144,6 +144,7 @@ claude mcp add utilix -- npx -y @utilix-tech/mcp
|
|
|
144
144
|
| `score_readability` | Score text with Flesch Reading Ease, Flesch-Kincaid Grade Level, and Gunning Fog Index |
|
|
145
145
|
| `detect_filler_words` | Scan text for filler words, hedge phrases, and clichés with per-phrase counts and a density score |
|
|
146
146
|
| `calculate_string_distance` | Compute the Levenshtein (or Damerau) edit distance and normalized similarity between two strings |
|
|
147
|
+
| `analyze_sentence_lengths` | Split text into sentences and report a histogram of sentence lengths, plus mean, median, standard deviation, and a consistency label |
|
|
147
148
|
|
|
148
149
|
### Time (6 tools)
|
|
149
150
|
| Tool | Description |
|
|
@@ -155,7 +156,7 @@ claude mcp add utilix -- npx -y @utilix-tech/mcp
|
|
|
155
156
|
| `convert_timezone` | Convert a date/time between timezones |
|
|
156
157
|
| `convert_iso8601_duration` | Parse an ISO 8601 duration into components and total seconds, or format seconds into a duration string |
|
|
157
158
|
|
|
158
|
-
### Units (
|
|
159
|
+
### Units (21 tools)
|
|
159
160
|
| Tool | Description |
|
|
160
161
|
|------|-------------|
|
|
161
162
|
| `convert_bytes` | Convert between B, KB, MB, GB, TB, KiB, MiB, GiB, TiB |
|
|
@@ -178,6 +179,7 @@ claude mcp add utilix -- npx -y @utilix-tech/mcp
|
|
|
178
179
|
| `calculate_break_even` | Compute the break-even unit count and revenue from fixed costs, variable cost per unit, and price per unit |
|
|
179
180
|
| `calculate_video_bitrate` | Calculate a video's file size from a bitrate and duration, or the bitrate needed to hit a target file size |
|
|
180
181
|
| `calculate_rule_of_72` | Estimate years to double an investment from a rate, or the rate needed to double in a given number of years, via the Rule of 72 shortcut and the exact logarithmic result |
|
|
182
|
+
| `calculate_emi` | Compute the Equated Monthly Installment for a loan from principal, annual rate, and tenure in years or months, with a year-by-year principal/interest breakdown |
|
|
181
183
|
|
|
182
184
|
### API / Network (12 tools)
|
|
183
185
|
| Tool | Description |
|
|
@@ -217,7 +219,7 @@ claude mcp add utilix -- npx -y @utilix-tech/mcp
|
|
|
217
219
|
| `parse_github_url` | Parse a github.com URL (repo, pull request, issue, commit, file, or release) into structured components |
|
|
218
220
|
| `detect_redos` | Statically flag catastrophic-backtracking (ReDoS) risk in a regex: nested quantifiers, overlapping alternation, adjacent overlapping quantifiers |
|
|
219
221
|
|
|
220
|
-
### Data (
|
|
222
|
+
### Data (7 tools)
|
|
221
223
|
| Tool | Description |
|
|
222
224
|
|------|-------------|
|
|
223
225
|
| `validate_yaml` | Validate YAML syntax |
|
|
@@ -226,6 +228,7 @@ claude mcp add utilix -- npx -y @utilix-tech/mcp
|
|
|
226
228
|
| `parse_csv` | Parse a CSV string and return the data as JSON |
|
|
227
229
|
| `format_ndjson` | Validate and pretty-print newline-delimited JSON (NDJSON) |
|
|
228
230
|
| `diff_env_example` | Diff a .env file against its .env.example template: flags keys missing from either side, plus empty placeholder values |
|
|
231
|
+
| `parse_markdown_front_matter` | Split a Markdown document into its YAML front matter fields and body content |
|
|
229
232
|
|
|
230
233
|
### CSS (2 tools)
|
|
231
234
|
| Tool | Description |
|
|
@@ -240,7 +243,7 @@ claude mcp add utilix -- npx -y @utilix-tech/mcp
|
|
|
240
243
|
| `unicode_inspect` | Inspect Unicode code points of a string |
|
|
241
244
|
| `optimize_svg` | Optimize and minify an SVG string |
|
|
242
245
|
|
|
243
|
-
### AI / Agent (
|
|
246
|
+
### AI / Agent (27 tools)
|
|
244
247
|
| Tool | Description |
|
|
245
248
|
|------|-------------|
|
|
246
249
|
| `estimate_tokens` | Estimate token count and cost for an LLM prompt |
|
|
@@ -269,6 +272,7 @@ claude mcp add utilix -- npx -y @utilix-tech/mcp
|
|
|
269
272
|
| `compress_json` | Compress JSON for LLM context budgets |
|
|
270
273
|
| `diff_json_detailed` | Diff two parsed JSON values, returning every path with its op and unchanged paths |
|
|
271
274
|
| `vector_similarity` | Cosine similarity, dot product, and Euclidean / Manhattan distance between two numeric vectors |
|
|
275
|
+
| `format_few_shot_examples` | Format input/output example pairs into a numbered, XML-tagged, or delimiter-separated few-shot prompt template |
|
|
272
276
|
|
|
273
277
|
### Image (3 tools)
|
|
274
278
|
| Tool | Description |
|
package/dist/index.js
CHANGED
|
@@ -1092,6 +1092,30 @@ server.tool("chunk_text", "Split text into overlapping chunks for RAG.", { text:
|
|
|
1092
1092
|
server.tool("extract_urls", "Extract all URLs from text or HTML.", { text: z.string() }, async ({ text: text2 }) => {
|
|
1093
1093
|
return { content: [{ type: "text", text: JSON.stringify(extractUrls(text2), null, 2) }] };
|
|
1094
1094
|
});
|
|
1095
|
+
function _formatFewShotExamples(examples, format, delimiter) {
|
|
1096
|
+
const valid = (examples ?? []).filter((e) => e?.input?.trim() && e?.output?.trim());
|
|
1097
|
+
if (valid.length === 0) return { formatted: "", exampleCount: 0, format };
|
|
1098
|
+
let formatted;
|
|
1099
|
+
if (format === "xml") {
|
|
1100
|
+
formatted = valid.map((e) => `<example>
|
|
1101
|
+
<input>${e.input.trim()}</input>
|
|
1102
|
+
<output>${e.output.trim()}</output>
|
|
1103
|
+
</example>`).join("\n\n");
|
|
1104
|
+
} else if (format === "delimiter") {
|
|
1105
|
+
formatted = valid.map((e) => `Input: ${e.input.trim()}
|
|
1106
|
+
Output: ${e.output.trim()}`).join(`
|
|
1107
|
+
${delimiter}
|
|
1108
|
+
`);
|
|
1109
|
+
} else {
|
|
1110
|
+
formatted = valid.map((e, i) => `Example ${i + 1}:
|
|
1111
|
+
Input: ${e.input.trim()}
|
|
1112
|
+
Output: ${e.output.trim()}`).join("\n\n");
|
|
1113
|
+
}
|
|
1114
|
+
return { formatted, exampleCount: valid.length, format };
|
|
1115
|
+
}
|
|
1116
|
+
server.tool("format_few_shot_examples", "Format a list of input/output example pairs into a standard few-shot prompt template: numbered, XML-tagged, or delimiter-separated.", { examples: z.array(z.object({ input: z.string(), output: z.string() })), format: z.enum(["numbered", "xml", "delimiter"]).default("numbered"), delimiter: z.string().default("---") }, async ({ examples, format, delimiter }) => {
|
|
1117
|
+
return { content: [{ type: "text", text: JSON.stringify(_formatFewShotExamples(examples, format, delimiter), null, 2) }] };
|
|
1118
|
+
});
|
|
1095
1119
|
server.tool("sanitize_html", "Strip HTML tags, scripts, and event handlers for LLM ingestion.", { html: z.string(), keep_links: z.boolean().default(false) }, async ({ html, keep_links }) => {
|
|
1096
1120
|
return { content: [{ type: "text", text: JSON.stringify(sanitizeHtml(html, keep_links), null, 2) }] };
|
|
1097
1121
|
});
|
|
@@ -3138,6 +3162,74 @@ function _scoreReadability(input) {
|
|
|
3138
3162
|
server.tool("score_readability", "Score text with Flesch Reading Ease, Flesch-Kincaid Grade Level, and Gunning Fog Index, using a heuristic syllable counter.", { text: z.string() }, async ({ text: input }) => {
|
|
3139
3163
|
return { content: [{ type: "text", text: JSON.stringify(_scoreReadability(input), null, 2) }] };
|
|
3140
3164
|
});
|
|
3165
|
+
function _splitIntoSentencesForLengthDist(text2) {
|
|
3166
|
+
const matches = text2.match(/[^.!?]+[.!?]*/g);
|
|
3167
|
+
if (!matches) return [];
|
|
3168
|
+
return matches.map((s) => s.trim()).filter((s) => s.length > 0);
|
|
3169
|
+
}
|
|
3170
|
+
function _countWordsInSentenceForLengthDist(sentence) {
|
|
3171
|
+
return (sentence.match(/[a-zA-Z']+/g) ?? []).length;
|
|
3172
|
+
}
|
|
3173
|
+
function _analyzeSentenceLengths(input) {
|
|
3174
|
+
const sentences = _splitIntoSentencesForLengthDist(input);
|
|
3175
|
+
const lengths = sentences.map(_countWordsInSentenceForLengthDist).filter((n) => n > 0);
|
|
3176
|
+
if (lengths.length === 0) {
|
|
3177
|
+
return {
|
|
3178
|
+
sentenceCount: 0,
|
|
3179
|
+
lengths: [],
|
|
3180
|
+
minLength: 0,
|
|
3181
|
+
maxLength: 0,
|
|
3182
|
+
meanLength: 0,
|
|
3183
|
+
medianLength: 0,
|
|
3184
|
+
stdDev: 0,
|
|
3185
|
+
coefficientOfVariation: 0,
|
|
3186
|
+
consistency: "No sentences to analyze",
|
|
3187
|
+
buckets: []
|
|
3188
|
+
};
|
|
3189
|
+
}
|
|
3190
|
+
const sentenceCount = lengths.length;
|
|
3191
|
+
const minLength = Math.min(...lengths);
|
|
3192
|
+
const maxLength = Math.max(...lengths);
|
|
3193
|
+
const meanLength = lengths.reduce((a, b) => a + b, 0) / sentenceCount;
|
|
3194
|
+
const sorted = [...lengths].sort((a, b) => a - b);
|
|
3195
|
+
const mid = Math.floor(sorted.length / 2);
|
|
3196
|
+
const medianLength = sorted.length % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid];
|
|
3197
|
+
const variance = lengths.reduce((sum, n) => sum + (n - meanLength) ** 2, 0) / sentenceCount;
|
|
3198
|
+
const stdDev = Math.sqrt(variance);
|
|
3199
|
+
const coefficientOfVariation = meanLength > 0 ? stdDev / meanLength : 0;
|
|
3200
|
+
let consistency;
|
|
3201
|
+
if (coefficientOfVariation < 0.3) consistency = "Consistent \u2014 sentence lengths vary little";
|
|
3202
|
+
else if (coefficientOfVariation < 0.6) consistency = "Moderate variation in sentence length";
|
|
3203
|
+
else consistency = "Highly variable sentence length";
|
|
3204
|
+
const bucketWidth = 5;
|
|
3205
|
+
const bucketCount = Math.max(1, Math.ceil(maxLength / bucketWidth));
|
|
3206
|
+
const buckets = [];
|
|
3207
|
+
for (let i = 0; i < bucketCount; i++) {
|
|
3208
|
+
const min = i * bucketWidth + 1;
|
|
3209
|
+
const max = (i + 1) * bucketWidth;
|
|
3210
|
+
buckets.push({ label: `${min}-${max}`, min, max, count: 0 });
|
|
3211
|
+
}
|
|
3212
|
+
for (const len of lengths) {
|
|
3213
|
+
const idx = Math.min(Math.floor((len - 1) / bucketWidth), bucketCount - 1);
|
|
3214
|
+
buckets[idx].count++;
|
|
3215
|
+
}
|
|
3216
|
+
const round2 = (n) => Math.round(n * 100) / 100;
|
|
3217
|
+
return {
|
|
3218
|
+
sentenceCount,
|
|
3219
|
+
lengths,
|
|
3220
|
+
minLength,
|
|
3221
|
+
maxLength,
|
|
3222
|
+
meanLength: round2(meanLength),
|
|
3223
|
+
medianLength: round2(medianLength),
|
|
3224
|
+
stdDev: round2(stdDev),
|
|
3225
|
+
coefficientOfVariation: round2(coefficientOfVariation),
|
|
3226
|
+
consistency,
|
|
3227
|
+
buckets
|
|
3228
|
+
};
|
|
3229
|
+
}
|
|
3230
|
+
server.tool("analyze_sentence_lengths", "Split text into sentences and report a histogram of sentence lengths in words, plus mean, median, standard deviation, and a consistency label. Distinct from readability grade scores.", { text: z.string() }, async ({ text: input }) => {
|
|
3231
|
+
return { content: [{ type: "text", text: JSON.stringify(_analyzeSentenceLengths(input), null, 2) }] };
|
|
3232
|
+
});
|
|
3141
3233
|
var _ID3V1_GENRES = [
|
|
3142
3234
|
"Blues",
|
|
3143
3235
|
"Classic Rock",
|
|
@@ -5407,5 +5499,144 @@ server.tool(
|
|
|
5407
5499
|
return text(JSON.stringify(result, null, 2));
|
|
5408
5500
|
}
|
|
5409
5501
|
);
|
|
5502
|
+
function _calculateEmiMcp(principal, annualRatePercent, tenure, tenureUnit) {
|
|
5503
|
+
if (!Number.isFinite(principal) || principal <= 0) return { error: "Principal must be a positive number" };
|
|
5504
|
+
if (!Number.isFinite(annualRatePercent) || annualRatePercent < 0) {
|
|
5505
|
+
return { error: "Annual interest rate must be zero or a positive number" };
|
|
5506
|
+
}
|
|
5507
|
+
if (tenureUnit !== "years" && tenureUnit !== "months") {
|
|
5508
|
+
return { error: 'tenureUnit must be "years" or "months"' };
|
|
5509
|
+
}
|
|
5510
|
+
if (!Number.isFinite(tenure) || tenure <= 0) return { error: "Tenure must be a positive number" };
|
|
5511
|
+
const tenureMonths = Math.round(tenureUnit === "years" ? tenure * 12 : tenure);
|
|
5512
|
+
if (tenureMonths <= 0) return { error: "Tenure must resolve to at least 1 month" };
|
|
5513
|
+
const monthlyRate = annualRatePercent / 100 / 12;
|
|
5514
|
+
let emi;
|
|
5515
|
+
if (monthlyRate === 0) {
|
|
5516
|
+
emi = principal / tenureMonths;
|
|
5517
|
+
} else {
|
|
5518
|
+
const factor = Math.pow(1 + monthlyRate, tenureMonths);
|
|
5519
|
+
emi = principal * monthlyRate * factor / (factor - 1);
|
|
5520
|
+
}
|
|
5521
|
+
const yearlyBreakdown = [];
|
|
5522
|
+
let balance = _roundMoneyMcp(principal);
|
|
5523
|
+
let totalPayment = 0;
|
|
5524
|
+
let yearPrincipal = 0;
|
|
5525
|
+
let yearInterest = 0;
|
|
5526
|
+
let year = 1;
|
|
5527
|
+
for (let month = 1; month <= tenureMonths; month++) {
|
|
5528
|
+
const interest = monthlyRate === 0 ? 0 : _roundMoneyMcp(balance * monthlyRate);
|
|
5529
|
+
let principalPortion = _roundMoneyMcp(emi) - interest;
|
|
5530
|
+
if (month === tenureMonths || principalPortion > balance) principalPortion = balance;
|
|
5531
|
+
const payment = _roundMoneyMcp(principalPortion + interest);
|
|
5532
|
+
balance = Math.max(_roundMoneyMcp(balance - principalPortion), 0);
|
|
5533
|
+
totalPayment += payment;
|
|
5534
|
+
yearPrincipal += principalPortion;
|
|
5535
|
+
yearInterest += interest;
|
|
5536
|
+
const isYearEnd = month % 12 === 0 || month === tenureMonths;
|
|
5537
|
+
if (isYearEnd) {
|
|
5538
|
+
yearlyBreakdown.push({ year, principalPaid: _roundMoneyMcp(yearPrincipal), interestPaid: _roundMoneyMcp(yearInterest), balance });
|
|
5539
|
+
year += 1;
|
|
5540
|
+
yearPrincipal = 0;
|
|
5541
|
+
yearInterest = 0;
|
|
5542
|
+
}
|
|
5543
|
+
}
|
|
5544
|
+
return {
|
|
5545
|
+
emi: _roundMoneyMcp(emi),
|
|
5546
|
+
totalInterest: _roundMoneyMcp(totalPayment - principal),
|
|
5547
|
+
totalPayment: _roundMoneyMcp(totalPayment),
|
|
5548
|
+
tenureMonths,
|
|
5549
|
+
yearlyBreakdown
|
|
5550
|
+
};
|
|
5551
|
+
}
|
|
5552
|
+
server.tool(
|
|
5553
|
+
"calculate_emi",
|
|
5554
|
+
"Compute the Equated Monthly Installment (EMI) for a loan given principal, annual interest rate (as a percent), and tenure in years or months. Same reducing-balance math as calculate_loan, but reports a year-by-year principal/interest breakdown instead of a full monthly schedule.",
|
|
5555
|
+
{
|
|
5556
|
+
principal: z.number().describe("Loan principal amount"),
|
|
5557
|
+
annualRatePercent: z.number().describe("Annual interest rate as a percent, e.g. 8.5"),
|
|
5558
|
+
tenure: z.number().describe("Loan tenure, in the unit given by tenureUnit"),
|
|
5559
|
+
tenureUnit: z.enum(["years", "months"]).optional().describe('Unit for the tenure field, defaults to "years"')
|
|
5560
|
+
},
|
|
5561
|
+
async ({ principal, annualRatePercent, tenure, tenureUnit }) => {
|
|
5562
|
+
const result = _calculateEmiMcp(principal, annualRatePercent, tenure, tenureUnit ?? "years");
|
|
5563
|
+
if ("error" in result) return errText(result);
|
|
5564
|
+
return text(JSON.stringify(result, null, 2));
|
|
5565
|
+
}
|
|
5566
|
+
);
|
|
5567
|
+
function _parseFrontMatterScalarMcp(value) {
|
|
5568
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
5569
|
+
return value.slice(1, -1);
|
|
5570
|
+
}
|
|
5571
|
+
if (value === "true") return true;
|
|
5572
|
+
if (value === "false") return false;
|
|
5573
|
+
if (value === "null" || value === "~") return null;
|
|
5574
|
+
if (value !== "" && !Number.isNaN(Number(value))) return Number(value);
|
|
5575
|
+
return value;
|
|
5576
|
+
}
|
|
5577
|
+
function _parseFrontMatterMcp(input) {
|
|
5578
|
+
if (typeof input !== "string" || input.length === 0) return { error: "input is required" };
|
|
5579
|
+
const normalized = input.replace(/\r\n/g, "\n");
|
|
5580
|
+
if (!normalized.startsWith("---\n") && normalized !== "---") {
|
|
5581
|
+
return { frontMatter: {}, body: input, hasFrontMatter: false, raw: "" };
|
|
5582
|
+
}
|
|
5583
|
+
const closingIdx = normalized.indexOf("\n---", 4);
|
|
5584
|
+
if (closingIdx === -1) {
|
|
5585
|
+
return { error: 'Front matter opening delimiter found but no closing "---" delimiter' };
|
|
5586
|
+
}
|
|
5587
|
+
const raw = normalized.slice(4, closingIdx);
|
|
5588
|
+
let afterClose = normalized.slice(closingIdx + 4);
|
|
5589
|
+
if (afterClose.startsWith("\n")) afterClose = afterClose.slice(1);
|
|
5590
|
+
const frontMatter = {};
|
|
5591
|
+
const rawLines = raw.split("\n");
|
|
5592
|
+
let i = 0;
|
|
5593
|
+
while (i < rawLines.length) {
|
|
5594
|
+
const line = rawLines[i];
|
|
5595
|
+
if (line.trim() === "" || line.trim().startsWith("#")) {
|
|
5596
|
+
i++;
|
|
5597
|
+
continue;
|
|
5598
|
+
}
|
|
5599
|
+
const colonIdx = line.indexOf(":");
|
|
5600
|
+
if (colonIdx === -1) return { error: `Invalid front matter line ${i + 1}: expected "key: value"` };
|
|
5601
|
+
const key = line.slice(0, colonIdx).trim();
|
|
5602
|
+
if (!key) return { error: `Invalid front matter line ${i + 1}: empty key` };
|
|
5603
|
+
const rest = line.slice(colonIdx + 1).trim();
|
|
5604
|
+
if (rest === "") {
|
|
5605
|
+
const listItems = [];
|
|
5606
|
+
let j = i + 1;
|
|
5607
|
+
while (j < rawLines.length && /^\s*-\s+/.test(rawLines[j])) {
|
|
5608
|
+
listItems.push(rawLines[j].replace(/^\s*-\s+/, "").trim());
|
|
5609
|
+
j++;
|
|
5610
|
+
}
|
|
5611
|
+
if (listItems.length > 0) {
|
|
5612
|
+
frontMatter[key] = listItems.map(_parseFrontMatterScalarMcp);
|
|
5613
|
+
i = j;
|
|
5614
|
+
continue;
|
|
5615
|
+
}
|
|
5616
|
+
frontMatter[key] = null;
|
|
5617
|
+
i++;
|
|
5618
|
+
continue;
|
|
5619
|
+
}
|
|
5620
|
+
if (rest.startsWith("[") && rest.endsWith("]")) {
|
|
5621
|
+
const inner = rest.slice(1, -1).trim();
|
|
5622
|
+
frontMatter[key] = inner === "" ? [] : inner.split(",").map((s) => _parseFrontMatterScalarMcp(s.trim()));
|
|
5623
|
+
i++;
|
|
5624
|
+
continue;
|
|
5625
|
+
}
|
|
5626
|
+
frontMatter[key] = _parseFrontMatterScalarMcp(rest);
|
|
5627
|
+
i++;
|
|
5628
|
+
}
|
|
5629
|
+
return { frontMatter, body: afterClose, hasFrontMatter: true, raw };
|
|
5630
|
+
}
|
|
5631
|
+
server.tool(
|
|
5632
|
+
"parse_markdown_front_matter",
|
|
5633
|
+
"Split a Markdown document into its YAML front matter (delimited by --- lines at the top) and body content, parsing simple scalar, list, and inline-array front matter values. Not a full YAML parser: nested mappings and multi-line block scalars are not supported.",
|
|
5634
|
+
{ input: z.string().describe("The Markdown document contents") },
|
|
5635
|
+
async ({ input }) => {
|
|
5636
|
+
const result = _parseFrontMatterMcp(input);
|
|
5637
|
+
if ("error" in result) return errText(result);
|
|
5638
|
+
return text(JSON.stringify(result, null, 2));
|
|
5639
|
+
}
|
|
5640
|
+
);
|
|
5410
5641
|
var transport = new StdioServerTransport();
|
|
5411
5642
|
await server.connect(transport);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@utilix-tech/mcp",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "MCP server exposing
|
|
3
|
+
"version": "0.33.0",
|
|
4
|
+
"description": "MCP server exposing 152 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",
|