@utilix-tech/mcp 0.46.0 → 0.47.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 +4 -0
- package/README.md +4 -2
- package/dist/index.js +190 -0
- package/package.json +1 -1
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.47.0
|
|
6
|
+
- Add `calculate_depreciation_schedule` tool: build a year-by-year fixed-asset depreciation schedule from cost, salvage value, and useful life, using straight-line, double-declining-balance, or sum-of-years-digits.
|
|
7
|
+
- Add `analyze_keyword_density` tool: count keyword/phrase frequency and density (occurrences per 100 words) across a block of text, with single-word or 2/3-word phrase modes.
|
|
8
|
+
|
|
5
9
|
## 0.46.0
|
|
6
10
|
- Add `calculate_cd_maturity` tool: compute a certificate of deposit's maturity value and effective annual yield (APY) from a principal, nominal rate, term, and compounding frequency, plus the net payout if withdrawn early.
|
|
7
11
|
- Add `build_client_hints`, `parse_client_hint_headers` tools: build an `Accept-CH`/`Critical-CH` response header from a list of client hint names with a plain-English note per hint, or decode raw Client Hints request-header text into structured values.
|
package/README.md
CHANGED
|
@@ -130,7 +130,7 @@ claude mcp add utilix -- npx -y @utilix-tech/mcp
|
|
|
130
130
|
| `generate_qr` | Generate a QR code as an SVG string |
|
|
131
131
|
| `generate_random_data` | Generate random mock data: names, emails, UUIDs, IPs, etc. |
|
|
132
132
|
|
|
133
|
-
### Text (
|
|
133
|
+
### Text (16 tools)
|
|
134
134
|
| Tool | Description |
|
|
135
135
|
|------|-------------|
|
|
136
136
|
| `convert_case` | Convert text case: camelCase, snake_case, PascalCase, etc. |
|
|
@@ -148,6 +148,7 @@ claude mcp add utilix -- npx -y @utilix-tech/mcp
|
|
|
148
148
|
| `calculate_string_distance` | Compute the Levenshtein (or Damerau) edit distance and normalized similarity between two strings |
|
|
149
149
|
| `analyze_sentence_lengths` | Split text into sentences and report a histogram of sentence lengths, plus mean, median, standard deviation, and a consistency label |
|
|
150
150
|
| `lookup_emoji` | Search a curated table of ~280 common emoji by name, keyword, or GitHub-style shortcode, or look one up exactly by shortcode, character, or category |
|
|
151
|
+
| `analyze_keyword_density` | Count keyword/phrase frequency and density (occurrences per 100 words) across a block of text, with single-word or 2/3-word phrase modes |
|
|
151
152
|
|
|
152
153
|
### Time (6 tools)
|
|
153
154
|
| Tool | Description |
|
|
@@ -159,7 +160,7 @@ claude mcp add utilix -- npx -y @utilix-tech/mcp
|
|
|
159
160
|
| `convert_timezone` | Convert a date/time between timezones |
|
|
160
161
|
| `convert_iso8601_duration` | Parse an ISO 8601 duration into components and total seconds, or format seconds into a duration string |
|
|
161
162
|
|
|
162
|
-
### Units (
|
|
163
|
+
### Units (29 tools)
|
|
163
164
|
| Tool | Description |
|
|
164
165
|
|------|-------------|
|
|
165
166
|
| `convert_bytes` | Convert between B, KB, MB, GB, TB, KiB, MiB, GiB, TiB |
|
|
@@ -190,6 +191,7 @@ claude mcp add utilix -- npx -y @utilix-tech/mcp
|
|
|
190
191
|
| `calculate_change` | Compute change owed for a payment/tender pair and break it down into the minimum count of bills/coins |
|
|
191
192
|
| `calculate_payback_period` | Compute the simple and discounted payback period from an initial investment and a series of periodic cash flows |
|
|
192
193
|
| `calculate_cd_maturity` | Compute a certificate of deposit's maturity value and effective annual yield (APY) from a principal, nominal rate, term, and compounding frequency, plus the net early-withdrawal payout |
|
|
194
|
+
| `calculate_depreciation_schedule` | Build a year-by-year fixed-asset depreciation schedule from cost, salvage value, and useful life, using straight-line, double-declining-balance, or sum-of-years-digits |
|
|
193
195
|
|
|
194
196
|
### API / Network (25 tools)
|
|
195
197
|
| Tool | Description |
|
package/dist/index.js
CHANGED
|
@@ -7811,5 +7811,195 @@ server.tool(
|
|
|
7811
7811
|
return text(JSON.stringify(result, null, 2));
|
|
7812
7812
|
}
|
|
7813
7813
|
);
|
|
7814
|
+
var _DEPRECIATION_METHODS_MCP = ["straight-line", "double-declining-balance", "sum-of-years-digits"];
|
|
7815
|
+
function _calculateDepreciationScheduleMcp(cost, salvageValue, usefulLifeYears, method) {
|
|
7816
|
+
if (!Number.isFinite(cost) || cost <= 0) return { error: "Cost must be a positive number" };
|
|
7817
|
+
if (!Number.isFinite(salvageValue) || salvageValue < 0) return { error: "Salvage value must be zero or a positive number" };
|
|
7818
|
+
if (salvageValue >= cost) return { error: "Salvage value must be less than cost" };
|
|
7819
|
+
if (!Number.isInteger(usefulLifeYears) || usefulLifeYears <= 0) {
|
|
7820
|
+
return { error: "Useful life must be a positive whole number of years" };
|
|
7821
|
+
}
|
|
7822
|
+
if (!_DEPRECIATION_METHODS_MCP.includes(method)) {
|
|
7823
|
+
return { error: `Method must be one of: ${_DEPRECIATION_METHODS_MCP.join(", ")}` };
|
|
7824
|
+
}
|
|
7825
|
+
const depreciableBase = _roundMoneyMcp(cost - salvageValue);
|
|
7826
|
+
const schedule = [];
|
|
7827
|
+
let accumulated = 0;
|
|
7828
|
+
if (method === "straight-line") {
|
|
7829
|
+
const annual = _roundMoneyMcp(depreciableBase / usefulLifeYears);
|
|
7830
|
+
for (let year = 1; year <= usefulLifeYears; year++) {
|
|
7831
|
+
const expense = year === usefulLifeYears ? _roundMoneyMcp(depreciableBase - accumulated) : annual;
|
|
7832
|
+
accumulated = _roundMoneyMcp(accumulated + expense);
|
|
7833
|
+
schedule.push({ year, depreciationExpense: expense, accumulatedDepreciation: accumulated, bookValue: _roundMoneyMcp(cost - accumulated) });
|
|
7834
|
+
}
|
|
7835
|
+
} else if (method === "double-declining-balance") {
|
|
7836
|
+
const rate = 2 / usefulLifeYears;
|
|
7837
|
+
let bookValue = cost;
|
|
7838
|
+
for (let year = 1; year <= usefulLifeYears; year++) {
|
|
7839
|
+
let expense = _roundMoneyMcp(bookValue * rate);
|
|
7840
|
+
if (bookValue - expense < salvageValue) expense = _roundMoneyMcp(bookValue - salvageValue);
|
|
7841
|
+
if (expense < 0) expense = 0;
|
|
7842
|
+
bookValue = _roundMoneyMcp(bookValue - expense);
|
|
7843
|
+
accumulated = _roundMoneyMcp(accumulated + expense);
|
|
7844
|
+
schedule.push({ year, depreciationExpense: expense, accumulatedDepreciation: accumulated, bookValue });
|
|
7845
|
+
}
|
|
7846
|
+
} else {
|
|
7847
|
+
const sumOfYears = usefulLifeYears * (usefulLifeYears + 1) / 2;
|
|
7848
|
+
for (let year = 1; year <= usefulLifeYears; year++) {
|
|
7849
|
+
const fraction = (usefulLifeYears - year + 1) / sumOfYears;
|
|
7850
|
+
const expense = year === usefulLifeYears ? _roundMoneyMcp(depreciableBase - accumulated) : _roundMoneyMcp(fraction * depreciableBase);
|
|
7851
|
+
accumulated = _roundMoneyMcp(accumulated + expense);
|
|
7852
|
+
schedule.push({ year, depreciationExpense: expense, accumulatedDepreciation: accumulated, bookValue: _roundMoneyMcp(cost - accumulated) });
|
|
7853
|
+
}
|
|
7854
|
+
}
|
|
7855
|
+
return { cost, salvageValue, usefulLifeYears, method, totalDepreciation: accumulated, schedule };
|
|
7856
|
+
}
|
|
7857
|
+
server.tool(
|
|
7858
|
+
"calculate_depreciation_schedule",
|
|
7859
|
+
"Build a year-by-year depreciation schedule for a fixed asset from its cost, salvage value, and useful life in years, using straight-line, double-declining-balance, or sum-of-years-digits. The double-declining-balance method clamps at salvage value rather than auto-switching to straight-line partway through.",
|
|
7860
|
+
{
|
|
7861
|
+
cost: z.number().describe("The asset's original cost"),
|
|
7862
|
+
salvageValue: z.number().describe("Estimated residual value at the end of its useful life; must be less than cost"),
|
|
7863
|
+
usefulLifeYears: z.number().int().describe("Useful life in whole years"),
|
|
7864
|
+
method: z.enum(["straight-line", "double-declining-balance", "sum-of-years-digits"]).describe("Depreciation method to use")
|
|
7865
|
+
},
|
|
7866
|
+
async ({ cost, salvageValue, usefulLifeYears, method }) => {
|
|
7867
|
+
const result = _calculateDepreciationScheduleMcp(cost, salvageValue, usefulLifeYears, method);
|
|
7868
|
+
if ("error" in result) return errText(result);
|
|
7869
|
+
return text(JSON.stringify(result, null, 2));
|
|
7870
|
+
}
|
|
7871
|
+
);
|
|
7872
|
+
var _KEYWORD_DENSITY_STOPWORDS_MCP = /* @__PURE__ */ new Set([
|
|
7873
|
+
"a",
|
|
7874
|
+
"an",
|
|
7875
|
+
"the",
|
|
7876
|
+
"and",
|
|
7877
|
+
"or",
|
|
7878
|
+
"but",
|
|
7879
|
+
"if",
|
|
7880
|
+
"then",
|
|
7881
|
+
"else",
|
|
7882
|
+
"for",
|
|
7883
|
+
"nor",
|
|
7884
|
+
"so",
|
|
7885
|
+
"yet",
|
|
7886
|
+
"of",
|
|
7887
|
+
"in",
|
|
7888
|
+
"on",
|
|
7889
|
+
"at",
|
|
7890
|
+
"to",
|
|
7891
|
+
"from",
|
|
7892
|
+
"by",
|
|
7893
|
+
"with",
|
|
7894
|
+
"about",
|
|
7895
|
+
"against",
|
|
7896
|
+
"between",
|
|
7897
|
+
"into",
|
|
7898
|
+
"through",
|
|
7899
|
+
"during",
|
|
7900
|
+
"before",
|
|
7901
|
+
"after",
|
|
7902
|
+
"above",
|
|
7903
|
+
"below",
|
|
7904
|
+
"up",
|
|
7905
|
+
"down",
|
|
7906
|
+
"out",
|
|
7907
|
+
"off",
|
|
7908
|
+
"over",
|
|
7909
|
+
"under",
|
|
7910
|
+
"again",
|
|
7911
|
+
"further",
|
|
7912
|
+
"once",
|
|
7913
|
+
"is",
|
|
7914
|
+
"are",
|
|
7915
|
+
"was",
|
|
7916
|
+
"were",
|
|
7917
|
+
"be",
|
|
7918
|
+
"been",
|
|
7919
|
+
"being",
|
|
7920
|
+
"am",
|
|
7921
|
+
"do",
|
|
7922
|
+
"does",
|
|
7923
|
+
"did",
|
|
7924
|
+
"doing",
|
|
7925
|
+
"have",
|
|
7926
|
+
"has",
|
|
7927
|
+
"had",
|
|
7928
|
+
"having",
|
|
7929
|
+
"i",
|
|
7930
|
+
"me",
|
|
7931
|
+
"my",
|
|
7932
|
+
"we",
|
|
7933
|
+
"our",
|
|
7934
|
+
"you",
|
|
7935
|
+
"your",
|
|
7936
|
+
"he",
|
|
7937
|
+
"him",
|
|
7938
|
+
"his",
|
|
7939
|
+
"she",
|
|
7940
|
+
"her",
|
|
7941
|
+
"it",
|
|
7942
|
+
"its",
|
|
7943
|
+
"they",
|
|
7944
|
+
"them",
|
|
7945
|
+
"their",
|
|
7946
|
+
"this",
|
|
7947
|
+
"that",
|
|
7948
|
+
"these",
|
|
7949
|
+
"those",
|
|
7950
|
+
"as",
|
|
7951
|
+
"not",
|
|
7952
|
+
"no",
|
|
7953
|
+
"can",
|
|
7954
|
+
"will",
|
|
7955
|
+
"just",
|
|
7956
|
+
"than",
|
|
7957
|
+
"too",
|
|
7958
|
+
"very",
|
|
7959
|
+
"would",
|
|
7960
|
+
"should",
|
|
7961
|
+
"could",
|
|
7962
|
+
"there",
|
|
7963
|
+
"here",
|
|
7964
|
+
"what",
|
|
7965
|
+
"which",
|
|
7966
|
+
"who",
|
|
7967
|
+
"whom"
|
|
7968
|
+
]);
|
|
7969
|
+
function _analyzeKeywordDensityMcp(input, topN, minWordLength, ngramSize) {
|
|
7970
|
+
if (!input || !input.trim()) return { error: "Input text is empty" };
|
|
7971
|
+
if (!Number.isInteger(topN) || topN <= 0) return { error: "topN must be a positive whole number" };
|
|
7972
|
+
if (!Number.isInteger(minWordLength) || minWordLength < 1) return { error: "minWordLength must be a positive whole number" };
|
|
7973
|
+
if (!Number.isInteger(ngramSize) || ngramSize < 1 || ngramSize > 3) return { error: "ngramSize must be 1, 2, or 3" };
|
|
7974
|
+
const tokens = input.toLowerCase().match(/[a-z0-9']+/g) ?? [];
|
|
7975
|
+
const totalWords = tokens.length;
|
|
7976
|
+
if (totalWords === 0) return { error: "No words found in input text" };
|
|
7977
|
+
if (totalWords < ngramSize) return { error: "Input text has fewer words than the requested phrase length" };
|
|
7978
|
+
const counts = /* @__PURE__ */ new Map();
|
|
7979
|
+
for (let i = 0; i <= tokens.length - ngramSize; i++) {
|
|
7980
|
+
const phraseWords = tokens.slice(i, i + ngramSize);
|
|
7981
|
+
if (phraseWords.some((w) => w.length < minWordLength)) continue;
|
|
7982
|
+
if (ngramSize === 1 && _KEYWORD_DENSITY_STOPWORDS_MCP.has(phraseWords[0])) continue;
|
|
7983
|
+
const phrase = phraseWords.join(" ");
|
|
7984
|
+
counts.set(phrase, (counts.get(phrase) ?? 0) + 1);
|
|
7985
|
+
}
|
|
7986
|
+
const keywords = Array.from(counts.entries()).map(([keyword, count]) => ({ keyword, count, density: Math.round(count / totalWords * 1e3) / 10 })).sort((a, b) => b.count - a.count || a.keyword.localeCompare(b.keyword)).slice(0, topN);
|
|
7987
|
+
return { totalWords, uniqueKeywords: counts.size, ngramSize, keywords };
|
|
7988
|
+
}
|
|
7989
|
+
server.tool(
|
|
7990
|
+
"analyze_keyword_density",
|
|
7991
|
+
"Count keyword or phrase frequency and density (occurrences per 100 words) across a block of text, the way an SEO keyword-density checker does. With the default ngramSize of 1, common English stopwords and words shorter than minWordLength are excluded; for ngramSize 2 or 3 (phrases), stopwords are kept since useful phrases often contain them.",
|
|
7992
|
+
{
|
|
7993
|
+
text: z.string().describe("The text to analyze"),
|
|
7994
|
+
topN: z.number().int().optional().describe("Number of top keywords/phrases to return (default 10)"),
|
|
7995
|
+
minWordLength: z.number().int().optional().describe("Minimum word length to include (default 3)"),
|
|
7996
|
+
ngramSize: z.number().int().min(1).max(3).optional().describe("Phrase length in words: 1 for single words, 2 or 3 for phrases (default 1)")
|
|
7997
|
+
},
|
|
7998
|
+
async ({ text: input, topN, minWordLength, ngramSize }) => {
|
|
7999
|
+
const result = _analyzeKeywordDensityMcp(input, topN ?? 10, minWordLength ?? 3, ngramSize ?? 1);
|
|
8000
|
+
if ("error" in result) return errText(result);
|
|
8001
|
+
return text(JSON.stringify(result, null, 2));
|
|
8002
|
+
}
|
|
8003
|
+
);
|
|
7814
8004
|
var transport = new StdioServerTransport();
|
|
7815
8005
|
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.47.0",
|
|
4
4
|
"description": "MCP server exposing 175 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",
|