@cyanheads/calculator-mcp-server 0.1.7 → 0.1.9

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/AGENTS.md ADDED
@@ -0,0 +1,323 @@
1
+ # Agent Protocol
2
+
3
+ **Server:** calculator-mcp-server
4
+ **Version:** 0.1.9
5
+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core)
6
+
7
+ > **Read the framework docs first:** `node_modules/@cyanheads/mcp-ts-core/CLAUDE.md` contains the full API reference — builders, Context, error codes, exports, patterns. This file covers server-specific conventions only.
8
+
9
+ A publicly-hosted calculator MCP server that lets any LLM verify mathematical computations. Powered by [math.js](https://mathjs.org/) v15. No auth required — all operations are read-only and stateless.
10
+
11
+ ### MCP Surface
12
+
13
+ | Primitive | Name | Purpose |
14
+ |:----------|:-----|:--------|
15
+ | Tool | `calculate` | Evaluate, simplify, or differentiate math expressions. Single tool — `operation` param defaults to `evaluate`. |
16
+ | Resource | `calculator://help` | Static reference of available functions, operators, constants, and syntax. |
17
+
18
+ ### Security Model
19
+
20
+ MathService wraps a **hardened math.js instance** — dangerous functions (`import`, `createUnit`, `evaluate`, `parse`, `compile`, `chain`, `config`, `resolve`, `reviver`, `parser`) are disabled in the expression scope. `simplify` and `derivative` are also disabled in expressions but called programmatically by the tool handler. Evaluation runs inside `vm.runInNewContext()` with a timeout. Input length is capped. Expression separators (semicolons and newlines) are rejected — single expression per call only. Variable scope accepts `z.record(z.number())` only with prototype-polluting keys blocked. Result types are validated (functions, parsers, and result sets rejected). Result size is capped via `CALC_MAX_RESULT_LENGTH`. The math.js `version` constant is redacted to prevent fingerprinting.
21
+
22
+ ---
23
+
24
+
25
+ ## What's Next?
26
+
27
+ When the user asks what to do next, what's left, or needs direction, suggest relevant options based on the current project state:
28
+
29
+ 1. **Re-run the `setup` skill** — ensures AGENTS.md, skills, structure, and metadata are populated and up to date with the current codebase
30
+ 2. **Run the `design-mcp-server` skill** — if the tool/resource surface hasn't been mapped yet, work through domain design
31
+ 3. **Add tools/resources/prompts** — scaffold new definitions using the `add-tool`, `add-resource`, `add-prompt` skills
32
+ 4. **Add services** — scaffold domain service integrations using the `add-service` skill
33
+ 5. **Add tests** — scaffold tests for existing definitions using the `add-test` skill
34
+ 6. **Field-test definitions** — exercise tools/resources/prompts with real inputs using the `field-test` skill, get a report of issues and pain points
35
+ 7. **Run `devcheck`** — lint, format, typecheck, and security audit
36
+ 8. **Run the `polish-docs-meta` skill** — finalize README, CHANGELOG, metadata, and agent protocol for shipping
37
+ 9. **Run the `maintenance` skill** — sync skills and dependencies after framework updates
38
+
39
+ Tailor suggestions to what's actually missing or stale — don't recite the full list every time.
40
+
41
+ ---
42
+
43
+ ## Core Rules
44
+
45
+ - **Logic throws, framework catches.** Tool/resource handlers are pure — throw on failure, no `try/catch`. Plain `Error` is fine; the framework catches, classifies, and formats. Use error factories (`notFound()`, `validationError()`, etc.) when the error code matters.
46
+ - **Use `ctx.log`** for request-scoped logging. No `console` calls.
47
+ - **Use `ctx.state`** for tenant-scoped storage. Never access persistence directly.
48
+ - **Check `ctx.elicit` / `ctx.sample`** for presence before calling.
49
+ - **Secrets in env vars only** — never hardcoded.
50
+
51
+ ---
52
+
53
+ ## Patterns
54
+
55
+ ### Tool
56
+
57
+ ```ts
58
+ import { tool, z } from '@cyanheads/mcp-ts-core';
59
+ import { getMathService } from '@/services/math/math-service.js';
60
+ import { getServerConfig } from '@/config/server-config.js';
61
+
62
+ export const calculateTool = tool('calculate', {
63
+ description: 'Evaluate math expressions, simplify algebraic expressions, or compute symbolic derivatives.',
64
+ annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
65
+ input: z.object({
66
+ expression: z.string()
67
+ .describe('Mathematical expression to evaluate (e.g., "2 + 3 * 4", "sin(pi/4)", "5 kg to lbs").'),
68
+ operation: z.enum(['evaluate', 'simplify', 'derivative']).default('evaluate')
69
+ .describe('Operation: "evaluate" (default), "simplify", or "derivative".'),
70
+ variable: z.string().optional()
71
+ .describe('Variable for differentiation. Required when operation is "derivative".'),
72
+ scope: z.record(z.number()).optional()
73
+ .describe('Variable assignments. Example: { "x": 5, "y": 3 }.'),
74
+ precision: z.number().int().min(1).max(16).optional()
75
+ .describe('Significant digits for numeric results. Ignored for symbolic operations.'),
76
+ }),
77
+ output: z.object({
78
+ result: z.string().describe('Computed result as a string.'),
79
+ resultType: z.string().describe('Type: number, BigNumber, Complex, DenseMatrix, Unit, string, boolean.'),
80
+ expression: z.string().describe('Original expression as received.'),
81
+ }),
82
+
83
+ handler(input, ctx) {
84
+ const config = getServerConfig();
85
+ const math = getMathService();
86
+ // Dispatch based on operation mode — see design doc for full error table
87
+ const result = math.evaluate(input.expression, input.scope);
88
+ ctx.log.info('Evaluated expression', { expression: input.expression });
89
+ return { result: String(result), resultType: typeof result, expression: input.expression };
90
+ },
91
+
92
+ format: (output) => [{
93
+ type: 'text',
94
+ text: `**Expression:** \`${output.expression}\`\n**Result:** ${output.result}\n**Type:** ${output.resultType}`,
95
+ }],
96
+ });
97
+ ```
98
+
99
+ ### Resource
100
+
101
+ ```ts
102
+ import { resource } from '@cyanheads/mcp-ts-core';
103
+ import { getMathService } from '@/services/math/math-service.js';
104
+
105
+ export const helpResource = resource('calculator://help', {
106
+ description: 'Available functions, operators, constants, and syntax reference.',
107
+ handler() {
108
+ const math = getMathService();
109
+ return math.getHelpContent();
110
+ },
111
+ });
112
+ ```
113
+
114
+ ### Server config
115
+
116
+ ```ts
117
+ // src/config/server-config.ts — lazy-parsed, separate from framework config
118
+ const ServerConfigSchema = z.object({
119
+ maxExpressionLength: z.coerce.number().int().min(10).max(10_000).default(1000)
120
+ .describe('Maximum allowed expression string length (10–10,000)'),
121
+ evaluationTimeoutMs: z.coerce.number().int().min(100).max(30_000).default(5000)
122
+ .describe('Maximum evaluation time in milliseconds (100–30,000)'),
123
+ maxResultLength: z.coerce.number().int().min(1_000).max(1_000_000).default(100_000)
124
+ .describe('Maximum result string length in characters (1,000–1,000,000)'),
125
+ });
126
+ let _config: z.infer<typeof ServerConfigSchema> | undefined;
127
+ export function getServerConfig() {
128
+ _config ??= ServerConfigSchema.parse({
129
+ maxExpressionLength: process.env.CALC_MAX_EXPRESSION_LENGTH,
130
+ evaluationTimeoutMs: process.env.CALC_EVALUATION_TIMEOUT_MS,
131
+ maxResultLength: process.env.CALC_MAX_RESULT_LENGTH,
132
+ });
133
+ return _config;
134
+ }
135
+ ```
136
+
137
+ | Env Var | Default | Description |
138
+ |:--------|:--------|:------------|
139
+ | `CALC_MAX_EXPRESSION_LENGTH` | `1000` | Max input string length |
140
+ | `CALC_EVALUATION_TIMEOUT_MS` | `5000` | Evaluation timeout in ms |
141
+ | `CALC_MAX_RESULT_LENGTH` | `100000` | Max result string length |
142
+
143
+ ---
144
+
145
+ ## Context
146
+
147
+ Handlers receive a unified `ctx` object. Key properties:
148
+
149
+ | Property | Description |
150
+ |:---------|:------------|
151
+ | `ctx.log` | Request-scoped logger — `.debug()`, `.info()`, `.notice()`, `.warning()`, `.error()`. Auto-correlates requestId, traceId, tenantId. |
152
+ | `ctx.requestId` | Unique request ID. |
153
+ | `ctx.tenantId` | Tenant ID from JWT or `'default'` for stdio. |
154
+
155
+ ---
156
+
157
+ ## Errors
158
+
159
+ Handlers throw — the framework catches, classifies, and formats. Three escalation levels:
160
+
161
+ ```ts
162
+ // 1. Plain Error — framework auto-classifies from message patterns
163
+ throw new Error('Item not found'); // → NotFound
164
+ throw new Error('Invalid query format'); // → ValidationError
165
+
166
+ // 2. Error factories — explicit code, concise
167
+ import { notFound, validationError, forbidden, serviceUnavailable } from '@cyanheads/mcp-ts-core/errors';
168
+ throw notFound('Item not found', { itemId });
169
+ throw serviceUnavailable('API unavailable', { url }, { cause: err });
170
+
171
+ // 3. McpError — full control over code and data
172
+ import { McpError, JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
173
+ throw new McpError(JsonRpcErrorCode.DatabaseError, 'Connection failed', { pool: 'primary' });
174
+ ```
175
+
176
+ Plain `Error` is fine for most cases. Use factories when the error code matters. See framework CLAUDE.md for the full auto-classification table and all available factories.
177
+
178
+ ---
179
+
180
+ ## Structure
181
+
182
+ ```text
183
+ src/
184
+ index.ts # createApp() entry point
185
+ config/
186
+ server-config.ts # Server-specific env vars (Zod schema)
187
+ services/
188
+ [domain]/
189
+ [domain]-service.ts # Domain service (init/accessor pattern)
190
+ types.ts # Domain types
191
+ mcp-server/
192
+ tools/definitions/
193
+ [tool-name].tool.ts # Tool definitions
194
+ resources/definitions/
195
+ [resource-name].resource.ts # Resource definitions
196
+ prompts/definitions/
197
+ [prompt-name].prompt.ts # Prompt definitions
198
+ ```
199
+
200
+ ---
201
+
202
+ ## Naming
203
+
204
+ | What | Convention | Example |
205
+ |:-----|:-----------|:--------|
206
+ | Files | kebab-case with suffix | `search-docs.tool.ts` |
207
+ | Tool/resource/prompt names | snake_case | `search_docs` |
208
+ | Directories | kebab-case | `src/services/doc-search/` |
209
+ | Descriptions | Single string or template literal, no `+` concatenation | `'Search items by query and filter.'` |
210
+
211
+ ---
212
+
213
+ ## Skills
214
+
215
+ Skills are modular instructions in `skills/` at the project root. Read them directly when a task matches — e.g., `skills/add-tool/SKILL.md` when adding a tool.
216
+
217
+ **Agent skill directory:** Copy skills into the directory your agent discovers (Codex: `.Codex/skills/`, others: equivalent). This makes skills available as context without needing to reference `skills/` paths manually. After framework updates, re-copy to pick up changes.
218
+
219
+ Available skills:
220
+
221
+ | Skill | Purpose |
222
+ |:------|:--------|
223
+ | `setup` | Post-init project orientation |
224
+ | `design-mcp-server` | Design tool surface, resources, and services for a new server |
225
+ | `add-tool` | Scaffold a new tool definition |
226
+ | `add-resource` | Scaffold a new resource definition |
227
+ | `add-prompt` | Scaffold a new prompt definition |
228
+ | `add-service` | Scaffold a new service integration |
229
+ | `add-test` | Scaffold test file for a tool, resource, or service |
230
+ | `field-test` | Exercise tools/resources/prompts with real inputs, verify behavior, report issues |
231
+ | `devcheck` | Lint, format, typecheck, audit |
232
+ | `polish-docs-meta` | Finalize docs, README, metadata, and agent protocol for shipping |
233
+ | `maintenance` | Sync skills and dependencies after updates |
234
+ | `report-issue-framework` | File a bug or feature request against `@cyanheads/mcp-ts-core` via `gh` CLI |
235
+ | `report-issue-local` | File a bug or feature request against this server's own repo via `gh` CLI |
236
+ | `api-auth` | Auth modes, scopes, JWT/OAuth |
237
+ | `api-config` | AppConfig, parseConfig, env vars |
238
+ | `api-context` | Context interface, logger, state, progress |
239
+ | `api-errors` | McpError, JsonRpcErrorCode, error patterns |
240
+ | `api-services` | LLM, Speech, Graph services |
241
+ | `api-testing` | createMockContext, test patterns |
242
+ | `api-utils` | Formatting, parsing, security, pagination, scheduling |
243
+ | `api-workers` | Cloudflare Workers runtime |
244
+ | `migrate-mcp-ts-template` | Migrate a template fork to use `@cyanheads/mcp-ts-core` as a package |
245
+
246
+ When you complete a skill's checklist, check the boxes and add a completion timestamp at the end (e.g., `Completed: 2026-03-11`).
247
+
248
+ ---
249
+
250
+ ## Commands
251
+
252
+ | Command | Purpose |
253
+ |:--------|:--------|
254
+ | `bun run build` | Compile TypeScript |
255
+ | `bun run rebuild` | Clean + build |
256
+ | `bun run clean` | Remove build artifacts |
257
+ | `bun run devcheck` | Lint + format + typecheck + security |
258
+ | `bun run tree` | Generate directory structure doc |
259
+ | `bun run format` | Auto-fix formatting |
260
+ | `bun run lint:mcp` | Validate MCP definitions |
261
+ | `bun run test` | Run tests |
262
+ | `bun run dev:stdio` | Dev mode (stdio) |
263
+ | `bun run dev:http` | Dev mode (HTTP) |
264
+ | `bun run start:stdio` | Production mode (stdio) |
265
+ | `bun run start:http` | Production mode (HTTP) |
266
+
267
+ ---
268
+
269
+ ## Publishing
270
+
271
+ ### Wrapup flow
272
+
273
+ When running the git wrapup checklist (`polish-docs-meta` or equivalent):
274
+
275
+ - **Minimum version bump is `0.0.1` (patch)** unless the user specifies a larger bump.
276
+ - After the final commit, **create an annotated tag** and **push** (tags included):
277
+
278
+ ```bash
279
+ git tag -a v<version> -m "v<version>"
280
+ git push && git push --tags
281
+ ```
282
+
283
+ ### npm + Docker
284
+
285
+ After tagging, publish to both npm and GHCR:
286
+
287
+ ```bash
288
+ bun publish --access public
289
+
290
+ docker buildx build --platform linux/amd64,linux/arm64 \
291
+ -t ghcr.io/cyanheads/calculator-mcp-server:<version> \
292
+ -t ghcr.io/cyanheads/calculator-mcp-server:latest \
293
+ --push .
294
+ ```
295
+
296
+ Remind the user to run the npm/Docker publish commands after completing a release flow.
297
+
298
+ ---
299
+
300
+ ## Imports
301
+
302
+ ```ts
303
+ // Framework — z is re-exported, no separate zod import needed
304
+ import { tool, z } from '@cyanheads/mcp-ts-core';
305
+ import { McpError, JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
306
+
307
+ // Server's own code — via path alias
308
+ import { getMathService } from '@/services/math/math-service.js';
309
+ import { getServerConfig } from '@/config/server-config.js';
310
+ ```
311
+
312
+ ---
313
+
314
+ ## Checklist
315
+
316
+ - [ ] Zod schemas: all fields have `.describe()`, only JSON-Schema-serializable types (no `z.custom()`, `z.date()`, `z.transform()`, etc.)
317
+ - [ ] JSDoc `@fileoverview` + `@module` on every file
318
+ - [ ] `ctx.log` for logging, `ctx.state` for storage
319
+ - [ ] Handlers throw on failure — error factories or plain `Error`, no try/catch
320
+ - [ ] `format()` renders all data the LLM needs — `content[]` is the only field most clients forward to the model
321
+ - [ ] Registered in `createApp()` arrays (directly or via barrel exports)
322
+ - [ ] Tests use `createMockContext()` from `@cyanheads/mcp-ts-core/testing`
323
+ - [ ] `bun run devcheck` passes
package/CLAUDE.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Agent Protocol
2
2
 
3
3
  **Server:** calculator-mcp-server
4
- **Version:** 0.1.6
4
+ **Version:** 0.1.9
5
5
  **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core)
6
6
 
7
7
  > **Read the framework docs first:** `node_modules/@cyanheads/mcp-ts-core/CLAUDE.md` contains the full API reference — builders, Context, error codes, exports, patterns. This file covers server-specific conventions only.
package/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  [![npm](https://img.shields.io/npm/v/@cyanheads/calculator-mcp-server?style=flat-square&logo=npm&logoColor=white)](https://www.npmjs.com/package/@cyanheads/calculator-mcp-server)
10
10
  [![Docker](https://img.shields.io/badge/Docker-ghcr.io-2496ED?style=flat-square&logo=docker&logoColor=white)](https://github.com/users/cyanheads/packages/container/package/calculator-mcp-server)
11
- [![Version](https://img.shields.io/badge/Version-0.1.7-blue.svg?style=flat-square)](./CHANGELOG.md) [![Framework](https://img.shields.io/badge/Built%20on-@cyanheads/mcp--ts--core-259?style=flat-square)](https://www.npmjs.com/package/@cyanheads/mcp-ts-core)
11
+ [![Version](https://img.shields.io/badge/Version-0.1.9-blue.svg?style=flat-square)](./CHANGELOG.md) [![Framework](https://img.shields.io/badge/Built%20on-@cyanheads/mcp--ts--core-259?style=flat-square)](https://www.npmjs.com/package/@cyanheads/mcp-ts-core)
12
12
 
13
13
  [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-1.28.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/) [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE) [![TypeScript](https://img.shields.io/badge/TypeScript-6.0.2-3178C6.svg?style=flat-square)](https://www.typescriptlang.org/)
14
14
 
@@ -39,6 +39,7 @@ A single tool covering 100% of the server's purpose. The `operation` parameter d
39
39
  - **Derivative** — compute symbolic derivatives (e.g., `3x^2 + 2x + 1` -> `6 * x + 2`)
40
40
  - Variable scope via `scope` parameter: `{ "x": 5, "y": 3 }`
41
41
  - Configurable precision for numeric results
42
+ - Blank optional `variable` and `precision` values from form-based MCP clients are treated as omitted
42
43
 
43
44
  ---
44
45
 
@@ -178,7 +179,7 @@ docker run -p 3010:3010 calculator-mcp-server
178
179
 
179
180
  ## Development Guide
180
181
 
181
- See [`CLAUDE.md`](./CLAUDE.md) for development guidelines and architectural rules. The short version:
182
+ See [`AGENTS.md`](./AGENTS.md) or [`CLAUDE.md`](./CLAUDE.md) for development guidelines and architectural rules. The short version:
182
183
 
183
184
  - Handlers throw, framework catches — no `try/catch` in tool logic
184
185
  - Use `ctx.log` for logging
@@ -11,9 +11,9 @@ export declare const calculateTool: import("@cyanheads/mcp-ts-core").ToolDefinit
11
11
  simplify: "simplify";
12
12
  derivative: "derivative";
13
13
  }>>;
14
- variable: z.ZodOptional<z.ZodString>;
14
+ variable: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"">, z.ZodString]>>;
15
15
  scope: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodNumber>>;
16
- precision: z.ZodOptional<z.ZodNumber>;
16
+ precision: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"">, z.ZodNumber]>>;
17
17
  }, z.core.$strip>, z.ZodObject<{
18
18
  result: z.ZodString;
19
19
  resultType: z.ZodString;
@@ -1 +1 @@
1
- {"version":3,"file":"calculate.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/calculate.tool.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAIjD,eAAO,MAAM,aAAa;;;;;;;;;;;;;;kBA0GxB,CAAC"}
1
+ {"version":3,"file":"calculate.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/calculate.tool.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAIjD,eAAO,MAAM,aAAa;;;;;;;;;;;;;;kBAqGxB,CAAC"}
@@ -7,9 +7,7 @@ import { tool, z } from '@cyanheads/mcp-ts-core';
7
7
  import { invalidParams } from '@cyanheads/mcp-ts-core/errors';
8
8
  import { getMathService } from '../../../services/math/math-service.js';
9
9
  export const calculateTool = tool('calculate', {
10
- description: 'Evaluate math expressions, simplify algebraic expressions, or compute symbolic derivatives. ' +
11
- 'Supports arithmetic, trigonometry, statistics, matrices, complex numbers, units, and combinatorics. ' +
12
- 'Powered by math.js — use calculator://help for the full function reference.',
10
+ description: 'Evaluate math expressions, simplify algebraic expressions, or compute symbolic derivatives. Supports arithmetic, trigonometry, statistics, matrices, complex numbers, units, and combinatorics.',
13
11
  annotations: {
14
12
  readOnlyHint: true,
15
13
  idempotentHint: true,
@@ -19,34 +17,29 @@ export const calculateTool = tool('calculate', {
19
17
  expression: z
20
18
  .string()
21
19
  .min(1)
22
- .describe('Mathematical expression to evaluate. Supports standard notation: ' +
23
- 'arithmetic (+, -, *, /, ^, %), functions (sin, cos, sqrt, log, abs, round, etc.), ' +
24
- 'constants (pi, e, phi, i), matrices ([1, 2; 3, 4]), units (5 kg to lbs), ' +
25
- 'and variables (when scope is provided).'),
20
+ .describe('Mathematical expression to evaluate. Supports standard notation: arithmetic (+, -, *, /, ^, %), functions (sin, cos, sqrt, log, abs, round, etc.), constants (pi, e, phi, i), matrices ([1, 2; 3, 4]), units (5 kg to lbs), and variables (when scope is provided).'),
26
21
  operation: z
27
22
  .enum(['evaluate', 'simplify', 'derivative'])
28
23
  .default('evaluate')
29
- .describe('Operation to perform. ' +
30
- '"evaluate" computes a numeric result (default). ' +
31
- '"simplify" reduces an algebraic expression symbolically (e.g., "2x + 3x" -> "5 * x"). Supports algebraic and trigonometric identities. ' +
32
- '"derivative" computes the symbolic derivative (requires variable parameter).'),
24
+ .describe('Operation to perform. "evaluate" computes a numeric result (default). "simplify" reduces an algebraic expression symbolically (e.g., "2x + 3x" -> "5 * x"). Supports algebraic and trigonometric identities. "derivative" computes the symbolic derivative (requires the variable parameter).'),
33
25
  variable: z
34
- .string()
35
- .max(50)
36
- .regex(/^[a-zA-Z_][a-zA-Z0-9_]*$/, 'Variable name must be alphanumeric (a-z, A-Z, 0-9, _).')
26
+ .union([
27
+ z.literal(''),
28
+ z
29
+ .string()
30
+ .max(50)
31
+ .regex(/^[a-zA-Z_][a-zA-Z0-9_]*$/, 'Variable name must be alphanumeric (a-z, A-Z, 0-9, _).'),
32
+ ])
37
33
  .optional()
38
- .describe('Variable to differentiate with respect to. Required when operation is "derivative". Example: "x".'),
34
+ .describe('Variable to differentiate with respect to. Required when operation is "derivative". Blank values from form-based clients are treated as omitted. Example: "x".'),
39
35
  scope: z
40
36
  .record(z.string(), z.number())
41
37
  .optional()
42
38
  .describe('Variable assignments for the expression. Example: { "x": 5, "y": 3 } makes "x + y" evaluate to 8.'),
43
39
  precision: z
44
- .number()
45
- .int()
46
- .min(1)
47
- .max(16)
40
+ .union([z.literal(''), z.number().int().min(1).max(16)])
48
41
  .optional()
49
- .describe('Significant digits (1\u201316) for numeric results. Omit for full precision. Ignored for symbolic operations (simplify, derivative).'),
42
+ .describe('Significant digits (1–16) for numeric results. Omit for full precision. Blank values from form-based clients are treated as omitted. Ignored for symbolic operations (simplify, derivative).'),
50
43
  }),
51
44
  output: z.object({
52
45
  result: z.string().describe('The computed result as a string.'),
@@ -57,9 +50,11 @@ export const calculateTool = tool('calculate', {
57
50
  }),
58
51
  handler(input, ctx) {
59
52
  const math = getMathService();
53
+ const variable = input.variable || undefined;
54
+ const precision = typeof input.precision === 'number' ? input.precision : undefined;
60
55
  switch (input.operation) {
61
56
  case 'evaluate': {
62
- const { result, resultType } = math.evaluateExpression(input.expression, input.scope, input.precision);
57
+ const { result, resultType } = math.evaluateExpression(input.expression, input.scope, precision);
63
58
  ctx.log.info('Evaluated expression', { expression: input.expression });
64
59
  return { result, resultType, expression: input.expression };
65
60
  }
@@ -69,13 +64,13 @@ export const calculateTool = tool('calculate', {
69
64
  return { result, resultType, expression: input.expression };
70
65
  }
71
66
  case 'derivative': {
72
- if (!input.variable) {
67
+ if (!variable) {
73
68
  throw invalidParams("The 'variable' parameter is required when operation is 'derivative'.");
74
69
  }
75
- const { result, resultType } = math.differentiateExpression(input.expression, input.variable);
70
+ const { result, resultType } = math.differentiateExpression(input.expression, variable);
76
71
  ctx.log.info('Differentiated expression', {
77
72
  expression: input.expression,
78
- variable: input.variable,
73
+ variable,
79
74
  });
80
75
  return { result, resultType, expression: input.expression };
81
76
  }
@@ -1 +1 @@
1
- {"version":3,"file":"calculate.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/calculate.tool.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAEjE,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,CAAC,WAAW,EAAE;IAC7C,WAAW,EACT,8FAA8F;QAC9F,sGAAsG;QACtG,6EAA6E;IAC/E,WAAW,EAAE;QACX,YAAY,EAAE,IAAI;QAClB,cAAc,EAAE,IAAI;QACpB,aAAa,EAAE,KAAK;KACrB;IACD,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC;QACd,UAAU,EAAE,CAAC;aACV,MAAM,EAAE;aACR,GAAG,CAAC,CAAC,CAAC;aACN,QAAQ,CACP,mEAAmE;YACjE,oFAAoF;YACpF,2EAA2E;YAC3E,yCAAyC,CAC5C;QACH,SAAS,EAAE,CAAC;aACT,IAAI,CAAC,CAAC,UAAU,EAAE,UAAU,EAAE,YAAY,CAAC,CAAC;aAC5C,OAAO,CAAC,UAAU,CAAC;aACnB,QAAQ,CACP,wBAAwB;YACtB,kDAAkD;YAClD,yIAAyI;YACzI,8EAA8E,CACjF;QACH,QAAQ,EAAE,CAAC;aACR,MAAM,EAAE;aACR,GAAG,CAAC,EAAE,CAAC;aACP,KAAK,CAAC,0BAA0B,EAAE,wDAAwD,CAAC;aAC3F,QAAQ,EAAE;aACV,QAAQ,CACP,mGAAmG,CACpG;QACH,KAAK,EAAE,CAAC;aACL,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC;aAC9B,QAAQ,EAAE;aACV,QAAQ,CACP,mGAAmG,CACpG;QACH,SAAS,EAAE,CAAC;aACT,MAAM,EAAE;aACR,GAAG,EAAE;aACL,GAAG,CAAC,CAAC,CAAC;aACN,GAAG,CAAC,EAAE,CAAC;aACP,QAAQ,EAAE;aACV,QAAQ,CACP,sIAAsI,CACvI;KACJ,CAAC;IACF,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,kCAAkC,CAAC;QAC/D,UAAU,EAAE,CAAC;aACV,MAAM,EAAE;aACR,QAAQ,CACP,6IAA6I,CAC9I;QACH,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,sCAAsC,CAAC;KACxE,CAAC;IAEF,OAAO,CAAC,KAAK,EAAE,GAAG;QAChB,MAAM,IAAI,GAAG,cAAc,EAAE,CAAC;QAE9B,QAAQ,KAAK,CAAC,SAAS,EAAE,CAAC;YACxB,KAAK,UAAU,CAAC,CAAC,CAAC;gBAChB,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC,kBAAkB,CACpD,KAAK,CAAC,UAAU,EAChB,KAAK,CAAC,KAAK,EACX,KAAK,CAAC,SAAS,CAChB,CAAC;gBACF,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,sBAAsB,EAAE,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC;gBACvE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC;YAC9D,CAAC;YACD,KAAK,UAAU,CAAC,CAAC,CAAC;gBAChB,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;gBACzE,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,uBAAuB,EAAE,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC;gBACxE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC;YAC9D,CAAC;YACD,KAAK,YAAY,CAAC,CAAC,CAAC;gBAClB,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;oBACpB,MAAM,aAAa,CACjB,sEAAsE,CACvE,CAAC;gBACJ,CAAC;gBACD,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC,uBAAuB,CACzD,KAAK,CAAC,UAAU,EAChB,KAAK,CAAC,QAAQ,CACf,CAAC;gBACF,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,2BAA2B,EAAE;oBACxC,UAAU,EAAE,KAAK,CAAC,UAAU;oBAC5B,QAAQ,EAAE,KAAK,CAAC,QAAQ;iBACzB,CAAC,CAAC;gBACH,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC;YAC9D,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC;QAClB;YACE,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,qBAAqB,MAAM,CAAC,UAAU,mBAAmB,MAAM,CAAC,MAAM,eAAe,MAAM,CAAC,UAAU,EAAE;SAC/G;KACF;CACF,CAAC,CAAC"}
1
+ {"version":3,"file":"calculate.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/calculate.tool.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cyanheads/calculator-mcp-server",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "Calculator MCP server — evaluate, simplify, and differentiate math expressions.",
5
5
  "mcpName": "io.github.cyanheads/calculator-mcp-server",
6
6
  "type": "module",
@@ -9,7 +9,7 @@
9
9
  "bin": {
10
10
  "calculator-mcp-server": "dist/index.js"
11
11
  },
12
- "files": ["dist/", "README.md", "LICENSE", "CLAUDE.md", "Dockerfile", "server.json"],
12
+ "files": ["dist/", "README.md", "LICENSE", "CLAUDE.md", "AGENTS.md", "Dockerfile", "server.json"],
13
13
  "scripts": {
14
14
  "build": "bun scripts/build.ts",
15
15
  "rebuild": "bun scripts/clean.ts && bun scripts/build.ts",
@@ -42,8 +42,18 @@
42
42
  "bugs": {
43
43
  "url": "https://github.com/cyanheads/calculator-mcp-server/issues"
44
44
  },
45
- "author": "cyanheads (https://github.com/cyanheads)",
45
+ "author": "cyanheads <casey@caseyjhand.com> (https://github.com/cyanheads/calculator-mcp-server#readme)",
46
46
  "license": "Apache-2.0",
47
+ "funding": [
48
+ {
49
+ "type": "github",
50
+ "url": "https://github.com/sponsors/cyanheads"
51
+ },
52
+ {
53
+ "type": "buy_me_a_coffee",
54
+ "url": "https://www.buymeacoffee.com/cyanheads"
55
+ }
56
+ ],
47
57
  "engines": {
48
58
  "node": ">=22.0.0",
49
59
  "bun": ">=1.2.0"
@@ -53,18 +63,18 @@
53
63
  "access": "public"
54
64
  },
55
65
  "dependencies": {
56
- "@cyanheads/mcp-ts-core": "^0.2.10",
57
- "mathjs": "^15.1.1",
66
+ "@cyanheads/mcp-ts-core": "^0.3.5",
67
+ "mathjs": "^15.2.0",
58
68
  "pino-pretty": "^13.1.3"
59
69
  },
60
70
  "devDependencies": {
61
- "@biomejs/biome": "^2.4.9",
62
- "@types/node": "^25.5.0",
71
+ "@biomejs/biome": "^2.4.12",
72
+ "@types/node": "^25.6.0",
63
73
  "depcheck": "^1.4.7",
64
74
  "ignore": "^7.0.5",
65
75
  "tsc-alias": "^1.8.16",
66
76
  "tsx": "^4.21.0",
67
77
  "typescript": "^6.0.2",
68
- "vitest": "^4.1.2"
78
+ "vitest": "^4.1.4"
69
79
  }
70
80
  }
package/server.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "url": "https://github.com/cyanheads/calculator-mcp-server",
7
7
  "source": "github"
8
8
  },
9
- "version": "0.1.7",
9
+ "version": "0.1.9",
10
10
  "remotes": [
11
11
  {
12
12
  "type": "streamable-http",
@@ -19,7 +19,7 @@
19
19
  "registryBaseUrl": "https://registry.npmjs.org",
20
20
  "identifier": "@cyanheads/calculator-mcp-server",
21
21
  "runtimeHint": "bun",
22
- "version": "0.1.7",
22
+ "version": "0.1.9",
23
23
  "packageArguments": [
24
24
  {
25
25
  "type": "positional",
@@ -69,7 +69,7 @@
69
69
  "registryBaseUrl": "https://registry.npmjs.org",
70
70
  "identifier": "@cyanheads/calculator-mcp-server",
71
71
  "runtimeHint": "bun",
72
- "version": "0.1.7",
72
+ "version": "0.1.9",
73
73
  "packageArguments": [
74
74
  {
75
75
  "type": "positional",