@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 +323 -0
- package/CLAUDE.md +1 -1
- package/README.md +3 -2
- package/dist/mcp-server/tools/definitions/calculate.tool.d.ts +2 -2
- package/dist/mcp-server/tools/definitions/calculate.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/calculate.tool.js +19 -24
- package/dist/mcp-server/tools/definitions/calculate.tool.js.map +1 -1
- package/package.json +18 -8
- package/server.json +3 -3
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.
|
|
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
|
[](https://www.npmjs.com/package/@cyanheads/calculator-mcp-server)
|
|
10
10
|
[](https://github.com/users/cyanheads/packages/container/package/calculator-mcp-server)
|
|
11
|
-
[](./CHANGELOG.md) [](https://www.npmjs.com/package/@cyanheads/mcp-ts-core)
|
|
12
12
|
|
|
13
13
|
[](https://modelcontextprotocol.io/) [](./LICENSE) [](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;;;;;;;;;;;;;;
|
|
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
|
-
.
|
|
35
|
-
.
|
|
36
|
-
|
|
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
|
|
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,
|
|
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 (!
|
|
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,
|
|
70
|
+
const { result, resultType } = math.differentiateExpression(input.expression, variable);
|
|
76
71
|
ctx.log.info('Differentiated expression', {
|
|
77
72
|
expression: input.expression,
|
|
78
|
-
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,
|
|
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.
|
|
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.
|
|
57
|
-
"mathjs": "^15.
|
|
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.
|
|
62
|
-
"@types/node": "^25.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
72
|
+
"version": "0.1.9",
|
|
73
73
|
"packageArguments": [
|
|
74
74
|
{
|
|
75
75
|
"type": "positional",
|