@coo-quack/calc-mcp 1.4.0 → 1.6.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,37 @@
1
1
  # Changelog
2
2
 
3
+ ## v1.6.0 (2026-02-12)
4
+
5
+ ### Features
6
+
7
+ - **Documentation site** — Full VitePress-powered docs at [coo-quack.github.io/calc-mcp](https://coo-quack.github.io/calc-mcp/) with tool reference, examples, install guides, and changelog (#13)
8
+
9
+ ### Documentation
10
+
11
+ - Added documentation site link to README
12
+ - Added release checklist to CONTRIBUTING.md (#15)
13
+ - Updated npx examples with `--prefix /tmp` for better node_modules compatibility
14
+ - Added common ignore patterns to .gitignore
15
+
16
+ ## v1.5.0 (2026-02-11)
17
+
18
+ ### Features
19
+
20
+ - **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
21
+ - **Shuffle** — Fisher-Yates algorithm with `crypto.getRandomValues` for unbiased list shuffling
22
+
23
+ ### Documentation
24
+
25
+ - README title: `@coo-quack/calc-mcp` → `Calc MCP`
26
+ - Added "Why?" section with AI-alone vs calc-mcp comparison
27
+ - Quick Start moved to top
28
+ - Install guides consolidated (Claude Desktop/Cursor/Windsurf share same JSON format)
29
+
30
+ ### Tests
31
+
32
+ - Random tool tests: 12 → 27 (+15)
33
+ - Total: 194 tests, 280 assertions
34
+
3
35
  ## v1.4.0 (2026-02-11)
4
36
 
5
37
  ### Features
package/CONTRIBUTING.md CHANGED
@@ -27,6 +27,17 @@ bun run build # Build for production
27
27
  3. Register in `src/index.ts` — import and add to the `tools` array
28
28
  4. Update `README.md` — add to examples and tool table
29
29
 
30
+ ## Release Checklist
31
+
32
+ When bumping a version:
33
+
34
+ 1. Update `CHANGELOG.md` with new version entry
35
+ 2. Update `docs/changelog.md` to match
36
+ 3. Review `docs/tools.md` — add/update any changed tool parameters or examples
37
+ 4. Review `docs/examples.md` — add examples for new features
38
+ 5. Review `README.md` — update tool table and examples if needed
39
+ 6. Create GitHub Release with release notes
40
+
30
41
  ## Pull Requests
31
42
 
32
43
  - Branch from `main`
package/README.md CHANGED
@@ -1,10 +1,37 @@
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
+ 📖 **[Documentation](https://coo-quack.github.io/calc-mcp/)**Full tool reference, examples, and install guides.
8
+
9
+ **21 tools for things AI is bad at** — deterministic math, cryptographic randomness, accurate date arithmetic, encoding, hashing, and more.
10
+
11
+ LLMs hallucinate calculations, can't generate true random numbers, and struggle with timezones. This MCP server fixes that.
12
+
13
+ ### Quick Start
14
+
15
+ ```bash
16
+ # Claude Code
17
+ claude mcp add -s user calc-mcp -- npx --prefix /tmp -y @coo-quack/calc-mcp
18
+
19
+ # Or just run it
20
+ npx --prefix /tmp -y @coo-quack/calc-mcp
21
+ ```
22
+
23
+ > Works with Claude Desktop, VS Code Copilot, Cursor, Windsurf — [setup guides below](#install).
24
+
25
+ ---
26
+
27
+ ## Why?
28
+
29
+ | AI alone | With calc-mcp |
30
+ |----------|---------------|
31
+ | "10 + 34 × 341 ÷ 23 = 507.8" ❌ | `514.087` ✅ (math) |
32
+ | "Here's a UUID: 550e8400-..." 🤷 fake | Cryptographically random UUID v4/v7 ✅ (random) |
33
+ | "100 days from now is..." 🤔 guess | `2026-05-22` ✅ (date) |
34
+ | "SHA-256 of password123 is..." 💀 hallucinated | `ef92b778bafe...` ✅ (hash) |
8
35
 
9
36
  ## Examples
10
37
 
@@ -43,7 +70,8 @@ Ask in natural language — the AI picks the right tool automatically.
43
70
  | You ask | You get | Tool |
44
71
  |---------|---------|------|
45
72
  | Generate a UUID v7 | `019c4b54-aad2-7e52-...` | random |
46
- | Generate a 20-char password | `h#tjZDojX6sH!RJt8vaS` | random |
73
+ | Generate a readable 20-char password | `hT9jZDojX6sHRJt8vaKS` | random |
74
+ | Shuffle ["Alice", "Bob", "Charlie"] | `["Charlie", "Alice", "Bob"]` | random |
47
75
 
48
76
  ### Conversion
49
77
 
@@ -71,14 +99,14 @@ Ask in natural language — the AI picks the right tool automatically.
71
99
  | Decode this JWT: eyJhbGci... | `{ alg: "HS256", name: "John Doe" }` | jwt_decode |
72
100
  | Parse https://example.com/search?q=hello | `host: example.com, q: "hello"` | url_parse |
73
101
 
74
- ## All Tools
102
+ ## All 21 Tools
75
103
 
76
104
  | Tool | Description |
77
105
  |------|-------------|
78
106
  | `math` | Evaluate expressions, statistics |
79
107
  | `count` | Characters (grapheme-aware), words, lines, bytes |
80
108
  | `datetime` | Current time, timezone conversion, UNIX timestamps |
81
- | `random` | UUID v4/v7, ULID, passwords, random numbers |
109
+ | `random` | UUID v4/v7, ULID, passwords (readable, custom charset), shuffle |
82
110
  | `hash` | MD5, SHA-1, SHA-256, SHA-512, CRC32 |
83
111
  | `base64` | Encode / decode |
84
112
  | `encode` | URL, HTML entity, Unicode escape |
@@ -91,7 +119,7 @@ Ask in natural language — the AI picks the right tool automatically.
91
119
  | `luhn` | Validate / generate check digits |
92
120
  | `ip` | IPv4/IPv6 info, CIDR range |
93
121
  | `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) |
122
+ | `convert` | 8 categories, 72 units: length, weight, temperature, area (tsubo, tatami), volume, speed, data, time |
95
123
  | `char_info` | Unicode code point, block, category |
96
124
  | `jwt_decode` | Decode header + payload (no verification) |
97
125
  | `url_parse` | Protocol, host, path, params, hash |
@@ -102,22 +130,26 @@ Ask in natural language — the AI picks the right tool automatically.
102
130
  ### Claude Code
103
131
 
104
132
  ```bash
105
- claude mcp add -s user calc-mcp -- npx -y @coo-quack/calc-mcp
133
+ claude mcp add -s user calc-mcp -- npx --prefix /tmp -y @coo-quack/calc-mcp
106
134
  ```
107
135
 
108
- ### Claude Desktop
136
+ ### Claude Desktop / Cursor / Windsurf
109
137
 
110
138
  Add to your config file:
111
139
 
112
- - macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
113
- - Windows: `%APPDATA%\Claude\claude_desktop_config.json`
140
+ | App | Config path |
141
+ |-----|-------------|
142
+ | Claude Desktop (macOS) | `~/Library/Application Support/Claude/claude_desktop_config.json` |
143
+ | Claude Desktop (Windows) | `%APPDATA%\Claude\claude_desktop_config.json` |
144
+ | Cursor | `~/.cursor/mcp.json` |
145
+ | Windsurf | `~/.codeium/windsurf/mcp_config.json` |
114
146
 
115
147
  ```json
116
148
  {
117
149
  "mcpServers": {
118
150
  "calc-mcp": {
119
151
  "command": "npx",
120
- "args": ["-y", "@coo-quack/calc-mcp"]
152
+ "args": ["--prefix", "/tmp", "-y", "@coo-quack/calc-mcp"]
121
153
  }
122
154
  }
123
155
  }
@@ -132,52 +164,7 @@ Add to `.vscode/mcp.json` in your workspace:
132
164
  "servers": {
133
165
  "calc-mcp": {
134
166
  "command": "npx",
135
- "args": ["-y", "@coo-quack/calc-mcp"]
136
- }
137
- }
138
- }
139
- ```
140
-
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"]
167
+ "args": ["--prefix", "/tmp", "-y", "@coo-quack/calc-mcp"]
181
168
  }
182
169
  }
183
170
  }
@@ -188,7 +175,7 @@ Add to `~/.codeium/windsurf/mcp_config.json`:
188
175
  ```bash
189
176
  bun install
190
177
  bun run dev # Start dev server
191
- bun test # 160 tests
178
+ bun test # Run tests
192
179
  bun run lint # Biome
193
180
  bun run format # Biome
194
181
  ```
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);
@@ -0,0 +1,56 @@
1
+ import { defineConfig } from "vitepress";
2
+
3
+ export default defineConfig({
4
+ title: "Calc MCP",
5
+ description: "21 tools for things AI is bad at",
6
+ base: "/calc-mcp/",
7
+
8
+ themeConfig: {
9
+ logo: "/logo.svg",
10
+
11
+ nav: [
12
+ { text: "Home", link: "/" },
13
+ { text: "Tools", link: "/tools" },
14
+ { text: "Install", link: "/install" },
15
+ { text: "Examples", link: "/examples" },
16
+ { text: "Changelog", link: "/changelog" },
17
+ ],
18
+
19
+ sidebar: [
20
+ {
21
+ text: "Guide",
22
+ items: [
23
+ { text: "Getting Started", link: "/" },
24
+ { text: "Installation", link: "/install" },
25
+ { text: "Examples", link: "/examples" },
26
+ ],
27
+ },
28
+ {
29
+ text: "Reference",
30
+ items: [
31
+ { text: "All Tools", link: "/tools" },
32
+ { text: "Changelog", link: "/changelog" },
33
+ ],
34
+ },
35
+ ],
36
+
37
+ socialLinks: [
38
+ { icon: "github", link: "https://github.com/coo-quack/calc-mcp" },
39
+ {
40
+ icon: "npm",
41
+ link: "https://www.npmjs.com/package/@coo-quack/calc-mcp",
42
+ },
43
+ ],
44
+
45
+ footer: {
46
+ message: "Released under the MIT License.",
47
+ copyright: "Copyright © 2026 coo-quack",
48
+ },
49
+
50
+ search: {
51
+ provider: "local",
52
+ },
53
+ },
54
+
55
+ head: [["link", { rel: "icon", href: "/calc-mcp/favicon.ico" }]],
56
+ });
@@ -0,0 +1,4 @@
1
+ import DefaultTheme from "vitepress/theme";
2
+ import "./style.css";
3
+
4
+ export default DefaultTheme;
@@ -0,0 +1,21 @@
1
+ :root {
2
+ --vp-c-brand-1: #00a000;
3
+ --vp-c-brand-2: #00c000;
4
+ --vp-c-brand-3: #00e000;
5
+ --vp-c-brand-soft: rgba(0, 160, 0, 0.14);
6
+ }
7
+
8
+ .dark {
9
+ --vp-c-brand-1: #00d000;
10
+ --vp-c-brand-2: #00b000;
11
+ --vp-c-brand-3: #00a000;
12
+ --vp-c-brand-soft: rgba(0, 208, 0, 0.16);
13
+ }
14
+
15
+ .vp-doc a {
16
+ color: var(--vp-c-brand-1);
17
+ }
18
+
19
+ .vp-doc a:hover {
20
+ color: var(--vp-c-brand-2);
21
+ }
@@ -0,0 +1,111 @@
1
+ # Changelog
2
+
3
+ All notable changes to Calc MCP are documented here.
4
+
5
+ ## v1.6.0 (2026-02-12)
6
+
7
+ ### Features
8
+
9
+ - **Documentation site** — Full VitePress-powered docs at [coo-quack.github.io/calc-mcp](https://coo-quack.github.io/calc-mcp/) with tool reference, examples, install guides, and changelog (#13)
10
+
11
+ ### Documentation
12
+
13
+ - Added documentation site link to README
14
+ - Added release checklist to CONTRIBUTING.md (#15)
15
+ - Updated npx examples with `--prefix /tmp` for better node_modules compatibility
16
+ - Added common ignore patterns to .gitignore
17
+
18
+ ## v1.5.0 (2026-02-11)
19
+
20
+ ### Features
21
+
22
+ - **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
23
+ - **Shuffle** — Fisher-Yates algorithm with `crypto.getRandomValues` for unbiased list shuffling
24
+
25
+ ### Documentation
26
+
27
+ - README title: `@coo-quack/calc-mcp` → `Calc MCP`
28
+ - Added "Why?" section with AI-alone vs calc-mcp comparison
29
+ - Quick Start moved to top
30
+ - Install guides consolidated (Claude Desktop/Cursor/Windsurf share same JSON format)
31
+
32
+ ### Tests
33
+
34
+ - Random tool tests: 12 → 27 (+15)
35
+ - Total: 194 tests, 280 assertions
36
+
37
+ ## v1.4.0 (2026-02-11)
38
+
39
+ ### Features
40
+
41
+ - **Time conversion** — ms, s, min, h, d, wk, mo, yr (now 8 categories, 72 units)
42
+ - **`--version` flag** — `npx @coo-quack/calc-mcp --version`
43
+
44
+ ### Improvements
45
+
46
+ - serverInfo.version synced with package.json (was hardcoded "0.1.0")
47
+ - Error messages now list all supported units on unknown unit
48
+ - Semver test coverage: 9 → 23 tests
49
+ - CI git config for tag authoring
50
+ - README badges (npm, CI, license)
51
+ - CONTRIBUTING.md added
52
+ - GitHub topics set
53
+ - README examples: Japanese text replaced with English alternatives
54
+
55
+ ## v1.3.0 (2026-02-11)
56
+
57
+ ### Documentation
58
+
59
+ - Overhaul README with natural language examples (22 verified examples)
60
+ - Add install guides for Claude Code, Claude Desktop, VS Code, Cursor, Windsurf
61
+ - Add unit conversion coverage details (7 categories, 58 units)
62
+ - Add encode/decode examples (Base64, URL, HTML)
63
+
64
+ ## v1.1.0 (2026-02-11)
65
+
66
+ ### Features
67
+
68
+ - **random**: Add UUID v7 support (`uuidVersion: "v7"`) — Time-ordered UUIDs, ideal for database primary keys
69
+
70
+ ## v1.0.0 (2026-02-11)
71
+
72
+ ### Features
73
+
74
+ Initial release with **21 MCP tools** for calculations and operations AI models struggle with:
75
+
76
+ - **random** — UUID (v4), ULID, secure password, random number
77
+ - **hash** — SHA-1, SHA-256, SHA-512, MD5
78
+ - **base64** — Encode/decode Base64
79
+ - **encode** — URL encode/decode, HTML entity encode/decode
80
+ - **datetime** — Current time, convert, format, Unix timestamp
81
+ - **count** — Character, word, line, byte counting (grapheme-aware)
82
+ - **math** — Precise math evaluation via mathjs
83
+ - **date** — Date arithmetic (add/subtract/diff)
84
+ - **regex** — Test, match, replace with regex
85
+ - **base** — Number base conversion (bin/oct/dec/hex/custom)
86
+ - **diff** — Text diff between two strings
87
+ - **json_validate** — JSON schema validation
88
+ - **cron_parse** — Cron expression to human-readable description
89
+ - **luhn** — Luhn algorithm validation (credit cards, etc.)
90
+ - **ip** — IPv4/IPv6 parsing, CIDR subnet info
91
+ - **color** — Color format conversion (hex/rgb/hsl/hwb)
92
+ - **convert** — Unit conversion (length, weight, temp, data, time)
93
+ - **char_info** — Unicode character info (codepoint, name, category)
94
+ - **jwt_decode** — Decode JWT tokens (header + payload)
95
+ - **url_parse** — URL parsing into components
96
+ - **semver** — Semantic versioning operations (compare, satisfy, sort)
97
+
98
+ ### CI/CD
99
+
100
+ - GitHub Actions: test + lint on push/PR
101
+ - Publish workflow: test + lint gate before npm publish on tag
102
+
103
+ ### Infrastructure
104
+
105
+ - Biome for linting and formatting
106
+ - Renovate with OSV vulnerability alerts
107
+ - 160 tests, 92%+ line coverage
108
+
109
+ ---
110
+
111
+ For the latest changes, see [GitHub Releases](https://github.com/coo-quack/calc-mcp/releases).