@coo-quack/calc-mcp 1.4.0 → 1.5.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
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## v1.5.0 (2026-02-11)
4
+
5
+ ### Features
6
+
7
+ - **Password generation** — fine-grained options: `uppercase`, `numbers`, `symbols` (on/off), `readable` mode (excludes ambiguous chars like l/1/I/O/0/o), `excludeChars` for custom exclusions
8
+ - **Shuffle** — Fisher-Yates algorithm with `crypto.getRandomValues` for unbiased list shuffling
9
+
10
+ ### Documentation
11
+
12
+ - README title: `@coo-quack/calc-mcp` → `Calc MCP`
13
+ - Added "Why?" section with AI-alone vs calc-mcp comparison
14
+ - Quick Start moved to top
15
+ - Install guides consolidated (Claude Desktop/Cursor/Windsurf share same JSON format)
16
+
17
+ ### Tests
18
+
19
+ - Random tool tests: 12 → 27 (+15)
20
+ - Total: 194 tests, 280 assertions
21
+
3
22
  ## v1.4.0 (2026-02-11)
4
23
 
5
24
  ### Features
package/README.md CHANGED
@@ -1,10 +1,35 @@
1
- # @coo-quack/calc-mcp
1
+ # Calc MCP
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/@coo-quack/calc-mcp)](https://www.npmjs.com/package/@coo-quack/calc-mcp)
4
4
  [![CI](https://github.com/coo-quack/calc-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/coo-quack/calc-mcp/actions/workflows/ci.yml)
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
6
 
7
- An MCP server with 21 tools for things AI is bad at — math, hashing, encoding, date arithmetic, and more.
7
+ **21 tools for things AI is bad at**deterministic math, cryptographic randomness, accurate date arithmetic, encoding, hashing, and more.
8
+
9
+ LLMs hallucinate calculations, can't generate true random numbers, and struggle with timezones. This MCP server fixes that.
10
+
11
+ ### Quick Start
12
+
13
+ ```bash
14
+ # Claude Code
15
+ claude mcp add -s user calc-mcp -- npx -y @coo-quack/calc-mcp
16
+
17
+ # Or just run it
18
+ npx -y @coo-quack/calc-mcp
19
+ ```
20
+
21
+ > Works with Claude Desktop, VS Code Copilot, Cursor, Windsurf — [setup guides below](#install).
22
+
23
+ ---
24
+
25
+ ## Why?
26
+
27
+ | AI alone | With calc-mcp |
28
+ |----------|---------------|
29
+ | "10 + 34 × 341 ÷ 23 = 507.8" ❌ | `514.087` ✅ (math) |
30
+ | "Here's a UUID: 550e8400-..." 🤷 fake | Cryptographically random UUID v4/v7 ✅ (random) |
31
+ | "100 days from now is..." 🤔 guess | `2026-05-22` ✅ (date) |
32
+ | "SHA-256 of password123 is..." 💀 hallucinated | `ef92b778bafe...` ✅ (hash) |
8
33
 
9
34
  ## Examples
10
35
 
@@ -43,7 +68,8 @@ Ask in natural language — the AI picks the right tool automatically.
43
68
  | You ask | You get | Tool |
44
69
  |---------|---------|------|
45
70
  | Generate a UUID v7 | `019c4b54-aad2-7e52-...` | random |
46
- | Generate a 20-char password | `h#tjZDojX6sH!RJt8vaS` | random |
71
+ | Generate a readable 20-char password | `hT9jZDojX6sHRJt8vaKS` | random |
72
+ | Shuffle ["Alice", "Bob", "Charlie"] | `["Charlie", "Alice", "Bob"]` | random |
47
73
 
48
74
  ### Conversion
49
75
 
@@ -71,14 +97,14 @@ Ask in natural language — the AI picks the right tool automatically.
71
97
  | Decode this JWT: eyJhbGci... | `{ alg: "HS256", name: "John Doe" }` | jwt_decode |
72
98
  | Parse https://example.com/search?q=hello | `host: example.com, q: "hello"` | url_parse |
73
99
 
74
- ## All Tools
100
+ ## All 21 Tools
75
101
 
76
102
  | Tool | Description |
77
103
  |------|-------------|
78
104
  | `math` | Evaluate expressions, statistics |
79
105
  | `count` | Characters (grapheme-aware), words, lines, bytes |
80
106
  | `datetime` | Current time, timezone conversion, UNIX timestamps |
81
- | `random` | UUID v4/v7, ULID, passwords, random numbers |
107
+ | `random` | UUID v4/v7, ULID, passwords (readable, custom charset), shuffle |
82
108
  | `hash` | MD5, SHA-1, SHA-256, SHA-512, CRC32 |
83
109
  | `base64` | Encode / decode |
84
110
  | `encode` | URL, HTML entity, Unicode escape |
@@ -91,7 +117,7 @@ Ask in natural language — the AI picks the right tool automatically.
91
117
  | `luhn` | Validate / generate check digits |
92
118
  | `ip` | IPv4/IPv6 info, CIDR range |
93
119
  | `color` | HEX ↔ RGB ↔ HSL |
94
- | `convert` | 8 categories, 72 units: length (m, km, mi, ft, ...), weight (kg, lb, oz, ...), temperature (°C, °F, K), area (m², ha, acre, tsubo, tatami), volume (l, gal, cup, tbsp, ...), speed (km/h, mph, kn, ...), data (kb, mb, gb, tb, ...), time (ms, s, min, h, d, wk, mo, yr) |
120
+ | `convert` | 8 categories, 72 units: length, weight, temperature, area (tsubo, tatami), volume, speed, data, time |
95
121
  | `char_info` | Unicode code point, block, category |
96
122
  | `jwt_decode` | Decode header + payload (no verification) |
97
123
  | `url_parse` | Protocol, host, path, params, hash |
@@ -105,12 +131,16 @@ Ask in natural language — the AI picks the right tool automatically.
105
131
  claude mcp add -s user calc-mcp -- npx -y @coo-quack/calc-mcp
106
132
  ```
107
133
 
108
- ### Claude Desktop
134
+ ### Claude Desktop / Cursor / Windsurf
109
135
 
110
136
  Add to your config file:
111
137
 
112
- - macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
113
- - Windows: `%APPDATA%\Claude\claude_desktop_config.json`
138
+ | App | Config path |
139
+ |-----|-------------|
140
+ | Claude Desktop (macOS) | `~/Library/Application Support/Claude/claude_desktop_config.json` |
141
+ | Claude Desktop (Windows) | `%APPDATA%\Claude\claude_desktop_config.json` |
142
+ | Cursor | `~/.cursor/mcp.json` |
143
+ | Windsurf | `~/.codeium/windsurf/mcp_config.json` |
114
144
 
115
145
  ```json
116
146
  {
@@ -138,57 +168,12 @@ Add to `.vscode/mcp.json` in your workspace:
138
168
  }
139
169
  ```
140
170
 
141
- Or add globally via settings.json:
142
-
143
- ```json
144
- {
145
- "mcp": {
146
- "servers": {
147
- "calc-mcp": {
148
- "command": "npx",
149
- "args": ["-y", "@coo-quack/calc-mcp"]
150
- }
151
- }
152
- }
153
- }
154
- ```
155
-
156
- ### Cursor
157
-
158
- Add to `~/.cursor/mcp.json`:
159
-
160
- ```json
161
- {
162
- "mcpServers": {
163
- "calc-mcp": {
164
- "command": "npx",
165
- "args": ["-y", "@coo-quack/calc-mcp"]
166
- }
167
- }
168
- }
169
- ```
170
-
171
- ### Windsurf
172
-
173
- Add to `~/.codeium/windsurf/mcp_config.json`:
174
-
175
- ```json
176
- {
177
- "mcpServers": {
178
- "calc-mcp": {
179
- "command": "npx",
180
- "args": ["-y", "@coo-quack/calc-mcp"]
181
- }
182
- }
183
- }
184
- ```
185
-
186
171
  ## Development
187
172
 
188
173
  ```bash
189
174
  bun install
190
175
  bun run dev # Start dev server
191
- bun test # 160 tests
176
+ bun test # Run tests
192
177
  bun run lint # Biome
193
178
  bun run format # Biome
194
179
  ```
Binary file
package/dist/index.js CHANGED
@@ -75870,16 +75870,56 @@ var tool17 = {
75870
75870
 
75871
75871
  // src/tools/random.ts
75872
75872
  var schema18 = {
75873
- type: exports_external.enum(["uuid", "ulid", "password", "number"]).describe("Type of random value to generate"),
75873
+ type: exports_external.enum(["uuid", "ulid", "password", "number", "shuffle"]).describe("Type of random value to generate: uuid, ulid, password, number, or shuffle"),
75874
75874
  uuidVersion: exports_external.enum(["v4", "v7"]).optional().describe("UUID version: v4 (random, default) or v7 (time-ordered)"),
75875
75875
  length: exports_external.number().int().min(1).max(256).optional().describe("Length for password generation (default: 16)"),
75876
75876
  min: exports_external.number().optional().describe("Minimum value for number generation (default: 0)"),
75877
75877
  max: exports_external.number().optional().describe("Maximum value for number generation (default: 100)"),
75878
- charset: exports_external.string().optional().describe("Character set for password generation")
75878
+ charset: exports_external.string().optional().describe("Custom character set for password (overrides uppercase/numbers/symbols options)"),
75879
+ uppercase: exports_external.boolean().optional().describe("Include uppercase letters in password (default: true)"),
75880
+ numbers: exports_external.boolean().optional().describe("Include numbers in password (default: true)"),
75881
+ symbols: exports_external.boolean().optional().describe("Include symbols in password (default: true)"),
75882
+ excludeChars: exports_external.string().optional().describe('Characters to exclude from password (e.g. "\\\\|{}")'),
75883
+ readable: exports_external.boolean().optional().describe("Readable mode: excludes ambiguous characters (l/1/I/O/0/o) for easy reading"),
75884
+ items: exports_external.array(exports_external.string()).optional().describe("Items to shuffle (for type=shuffle)")
75879
75885
  };
75880
75886
  var inputSchema18 = exports_external.object(schema18);
75881
- var DEFAULT_CHARSET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*";
75887
+ var LOWERCASE = "abcdefghijklmnopqrstuvwxyz";
75888
+ var UPPERCASE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
75889
+ var NUMBERS = "0123456789";
75890
+ var SYMBOLS = "!@#$%^&*()-_=+[]{}|;:,.<>?/~`";
75891
+ var AMBIGUOUS = "lI1O0o";
75882
75892
  var ULID_ENCODING = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
75893
+ function buildCharset(input) {
75894
+ if (input.charset) {
75895
+ let cs2 = input.charset;
75896
+ if (input.excludeChars) {
75897
+ const exclude = new Set(input.excludeChars);
75898
+ cs2 = cs2.split("").filter((c) => !exclude.has(c)).join("");
75899
+ }
75900
+ if (cs2.length === 0)
75901
+ throw new Error("Charset is empty after exclusions");
75902
+ return cs2;
75903
+ }
75904
+ let cs = LOWERCASE;
75905
+ if (input.uppercase !== false)
75906
+ cs += UPPERCASE;
75907
+ if (input.numbers !== false)
75908
+ cs += NUMBERS;
75909
+ if (input.symbols !== false)
75910
+ cs += SYMBOLS;
75911
+ if (input.readable) {
75912
+ const ambiguous = new Set(AMBIGUOUS);
75913
+ cs = cs.split("").filter((c) => !ambiguous.has(c)).join("");
75914
+ }
75915
+ if (input.excludeChars) {
75916
+ const exclude = new Set(input.excludeChars);
75917
+ cs = cs.split("").filter((c) => !exclude.has(c)).join("");
75918
+ }
75919
+ if (cs.length === 0)
75920
+ throw new Error("Charset is empty after exclusions");
75921
+ return cs;
75922
+ }
75883
75923
  function generatePassword(length, charset) {
75884
75924
  const array3 = new Uint32Array(length);
75885
75925
  crypto.getRandomValues(array3);
@@ -75933,6 +75973,18 @@ function generateULID() {
75933
75973
  }
75934
75974
  return timeStr + randomStr;
75935
75975
  }
75976
+ function shuffle(items) {
75977
+ if (items.length === 0)
75978
+ throw new Error("Items array must not be empty");
75979
+ const result = [...items];
75980
+ for (let i2 = result.length - 1;i2 > 0; i2--) {
75981
+ const array3 = new Uint32Array(1);
75982
+ crypto.getRandomValues(array3);
75983
+ const j = array3[0] % (i2 + 1);
75984
+ [result[i2], result[j]] = [result[j], result[i2]];
75985
+ }
75986
+ return result;
75987
+ }
75936
75988
  function execute18(input) {
75937
75989
  switch (input.type) {
75938
75990
  case "uuid":
@@ -75941,7 +75993,7 @@ function execute18(input) {
75941
75993
  return generateULID();
75942
75994
  case "password": {
75943
75995
  const length = input.length ?? 16;
75944
- const charset = input.charset ?? DEFAULT_CHARSET;
75996
+ const charset = buildCharset(input);
75945
75997
  return generatePassword(length, charset);
75946
75998
  }
75947
75999
  case "number": {
@@ -75949,11 +76001,17 @@ function execute18(input) {
75949
76001
  const max3 = input.max ?? 100;
75950
76002
  return String(generateRandomNumber(min3, max3));
75951
76003
  }
76004
+ case "shuffle": {
76005
+ if (!input.items || input.items.length === 0) {
76006
+ throw new Error("Items array is required for shuffle");
76007
+ }
76008
+ return JSON.stringify(shuffle(input.items));
76009
+ }
75952
76010
  }
75953
76011
  }
75954
76012
  var tool18 = {
75955
76013
  name: "random",
75956
- description: "Generate random values: UUID, ULID, secure password, or random number",
76014
+ description: "Generate random values: UUID, ULID, secure password (with fine-grained options), random number, or shuffle a list",
75957
76015
  schema: schema18,
75958
76016
  handler: async (args) => {
75959
76017
  const input = inputSchema18.parse(args);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coo-quack/calc-mcp",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "calc-mcp": "dist/index.js"