@cyanheads/calculator-mcp-server 0.4.0 → 0.4.2
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 +41 -39
- package/CLAUDE.md +41 -39
- package/Dockerfile +14 -10
- package/LICENSE +1 -1
- package/README.md +88 -30
- package/changelog/0.1.x/0.1.0.md +20 -0
- package/changelog/0.1.x/0.1.1.md +28 -0
- package/changelog/0.1.x/0.1.10.md +25 -0
- package/changelog/0.1.x/0.1.11.md +29 -0
- package/changelog/0.1.x/0.1.12.md +21 -0
- package/changelog/0.1.x/0.1.13.md +20 -0
- package/changelog/0.1.x/0.1.14.md +18 -0
- package/changelog/0.1.x/0.1.15.md +20 -0
- package/changelog/0.1.x/0.1.16.md +26 -0
- package/changelog/0.1.x/0.1.17.md +24 -0
- package/changelog/0.1.x/0.1.18.md +21 -0
- package/changelog/0.1.x/0.1.19.md +18 -0
- package/changelog/0.1.x/0.1.2.md +22 -0
- package/changelog/0.1.x/0.1.20.md +21 -0
- package/changelog/0.1.x/0.1.21.md +26 -0
- package/changelog/0.1.x/0.1.22.md +33 -0
- package/changelog/0.1.x/0.1.23.md +32 -0
- package/changelog/0.1.x/0.1.24.md +16 -0
- package/changelog/0.1.x/0.1.25.md +23 -0
- package/changelog/0.1.x/0.1.26.md +16 -0
- package/changelog/0.1.x/0.1.3.md +19 -0
- package/changelog/0.1.x/0.1.4.md +20 -0
- package/changelog/0.1.x/0.1.5.md +19 -0
- package/changelog/0.1.x/0.1.6.md +26 -0
- package/changelog/0.1.x/0.1.7.md +19 -0
- package/changelog/0.1.x/0.1.8.md +18 -0
- package/changelog/0.1.x/0.1.9.md +29 -0
- package/changelog/0.2.x/0.2.0.md +18 -0
- package/changelog/0.3.x/0.3.0.md +19 -0
- package/changelog/0.3.x/0.3.1.md +30 -0
- package/changelog/0.3.x/0.3.2.md +18 -0
- package/changelog/0.3.x/0.3.3.md +27 -0
- package/changelog/0.3.x/0.3.4.md +17 -0
- package/changelog/0.3.x/0.3.5.md +23 -0
- package/changelog/0.4.x/0.4.0.md +28 -0
- package/changelog/0.4.x/0.4.1.md +28 -0
- package/changelog/0.4.x/0.4.2.md +26 -0
- package/changelog/template.md +151 -0
- package/dist/config/server-config.d.ts +3 -3
- package/dist/config/server-config.d.ts.map +1 -1
- package/dist/config/server-config.js +6 -8
- package/dist/config/server-config.js.map +1 -1
- package/dist/mcp-server/resources/definitions/help.resource.d.ts.map +1 -1
- package/dist/mcp-server/resources/definitions/help.resource.js +1 -0
- package/dist/mcp-server/resources/definitions/help.resource.js.map +1 -1
- package/dist/mcp-server/tools/definitions/calculate.tool.d.ts +3 -3
- package/dist/mcp-server/tools/definitions/calculate.tool.js.map +1 -1
- package/dist/services/math/math-service.d.ts.map +1 -1
- package/dist/services/math/math-service.js.map +1 -1
- package/package.json +27 -16
- package/server.json +5 -5
package/AGENTS.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# Agent Protocol
|
|
2
2
|
|
|
3
3
|
**Server:** calculator-mcp-server
|
|
4
|
-
**Version:** 0.4.
|
|
5
|
-
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.
|
|
6
|
-
**Engines:** Bun ≥1.
|
|
7
|
-
**MCP SDK:** `@modelcontextprotocol/
|
|
8
|
-
**Zod:** 4.
|
|
4
|
+
**Version:** 0.4.2
|
|
5
|
+
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.0`
|
|
6
|
+
**Engines:** Bun ≥1.4.0, Node ≥24.0.0
|
|
7
|
+
**MCP SDK:** `@modelcontextprotocol/server` 2.0.0
|
|
8
|
+
**Zod:** 4.6.1
|
|
9
9
|
|
|
10
10
|
> **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.
|
|
11
11
|
|
|
@@ -50,7 +50,7 @@ Tailor suggestions to what's actually missing or stale — don't recite the full
|
|
|
50
50
|
- **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.
|
|
51
51
|
- **Use `ctx.log`** for request-scoped logging. No `console` calls.
|
|
52
52
|
- **Use `ctx.state`** for tenant-scoped storage. Never access persistence directly.
|
|
53
|
-
- **
|
|
53
|
+
- **Need input the caller didn't supply?** `return ctx.requestInput(...)` and read `ctx.inputs` when the handler is re-entered. Never `await` for user input mid-handler.
|
|
54
54
|
- **Secrets in env vars only** — never hardcoded.
|
|
55
55
|
- **Close the loop on issues.** When implementing work tracked by a GitHub issue, comment on the issue with what landed and close it. Do both — a comment without a close leaves stale issues open; a close without a comment leaves no record of what shipped. The comment is for future readers — state the concrete changes, not the conversation that produced them.
|
|
56
56
|
|
|
@@ -121,6 +121,9 @@ export const helpResource = resource('calculator://help', {
|
|
|
121
121
|
|
|
122
122
|
```ts
|
|
123
123
|
// src/config/server-config.ts — lazy-parsed, separate from framework config
|
|
124
|
+
import { z } from '@cyanheads/mcp-ts-core';
|
|
125
|
+
import { parseEnvConfig } from '@cyanheads/mcp-ts-core/config';
|
|
126
|
+
|
|
124
127
|
const ServerConfigSchema = z.object({
|
|
125
128
|
maxExpressionLength: z.coerce.number().int().min(10).max(10_000).default(1000)
|
|
126
129
|
.describe('Maximum allowed expression string length (10–10,000)'),
|
|
@@ -131,10 +134,10 @@ const ServerConfigSchema = z.object({
|
|
|
131
134
|
});
|
|
132
135
|
let _config: z.infer<typeof ServerConfigSchema> | undefined;
|
|
133
136
|
export function getServerConfig() {
|
|
134
|
-
_config ??= ServerConfigSchema
|
|
135
|
-
maxExpressionLength:
|
|
136
|
-
evaluationTimeoutMs:
|
|
137
|
-
maxResultLength:
|
|
137
|
+
_config ??= parseEnvConfig(ServerConfigSchema, {
|
|
138
|
+
maxExpressionLength: 'CALC_MAX_EXPRESSION_LENGTH',
|
|
139
|
+
evaluationTimeoutMs: 'CALC_EVALUATION_TIMEOUT_MS',
|
|
140
|
+
maxResultLength: 'CALC_MAX_RESULT_LENGTH',
|
|
138
141
|
});
|
|
139
142
|
return _config;
|
|
140
143
|
}
|
|
@@ -154,13 +157,13 @@ Handlers receive a unified `ctx` object. Key properties:
|
|
|
154
157
|
|
|
155
158
|
| Property | Description |
|
|
156
159
|
|:---------|:------------|
|
|
157
|
-
| `ctx.log` | Request-scoped logger — `.debug()`, `.info()`, `.notice()`, `.warning()`, `.error()`. Auto-correlates requestId, traceId, tenantId. |
|
|
158
|
-
| `ctx.state` | Tenant-scoped KV — `.get(key)`, `.set(key, value, { ttl? })`, `.delete(key)`, `.list(prefix, { cursor, limit })`. Accepts any serializable value. |
|
|
159
|
-
| `ctx.
|
|
160
|
-
| `ctx.
|
|
160
|
+
| `ctx.log` | Request-scoped logger — `.debug()`, `.info()`, `.notice()`, `.warning()`, `.error()`. Auto-correlates requestId, traceId, tenantId. Dual-sink: Pino and `notifications/message` to the client, so treat it as client-visible. |
|
|
161
|
+
| `ctx.state` | Tenant-scoped KV — `.get(key)`, `.set(key, value, { ttl? })`, `.delete(key)`, `.getMany(keys)`, `.list(prefix, { cursor, limit })`. Accepts any serializable value. |
|
|
162
|
+
| `ctx.requestInput` | Suspend and ask the caller for more input — `return ctx.requestInput({ inputRequests: { key: inputRequired.elicit({ message, requestedSchema }) } })`. Never returns; the handler is re-entered with the answers. Always present. |
|
|
163
|
+
| `ctx.inputs` | Reader over a retried request's responses — `.accepted(key, schema)`, `.view(key)`, `.state()`, `.dropped`. Empty on the first round. |
|
|
164
|
+
| `ctx.enrich` | Success-path agent context — `ctx.enrich(...)` or `.notice()` / `.total()` / `.echo()` / `.truncated()`. Reaches both response surfaces only when the definition declares an `enrichment` block. |
|
|
165
|
+
| `ctx.content` | Non-text content blocks — `.image(data, mimeType)`, `.audio(data, mimeType)`, or `ctx.content(block)`. Prepended to `content[]`; never enters `structuredContent`. |
|
|
161
166
|
| `ctx.signal` | `AbortSignal` for cancellation. |
|
|
162
|
-
| `ctx.progress` | Task progress (present when `task: true`) — `.setTotal(n)`, `.increment()`, `.update(message)`. |
|
|
163
|
-
| `ctx.enrich` | Success-path enrichment — `.notice(text)`, `.total(n)`, `.echo(query)`, `.delta({ field, before, after })`. Merges into `structuredContent` and `content[]` trailer automatically. |
|
|
164
167
|
| `ctx.requestId` | Unique request ID. |
|
|
165
168
|
| `ctx.tenantId` | Tenant ID from JWT or `'default'` for stdio. |
|
|
166
169
|
|
|
@@ -170,7 +173,7 @@ Handlers receive a unified `ctx` object. Key properties:
|
|
|
170
173
|
|
|
171
174
|
Handlers throw — the framework catches, classifies, and formats.
|
|
172
175
|
|
|
173
|
-
**Recommended: typed error contract.** Declare `errors: [{ reason, code, when, recovery, retryable? }]` on `tool()` / `resource()` to receive
|
|
176
|
+
**Recommended: typed error contract.** Declare `errors: [{ reason, code, when, recovery, retryable? }]` on `tool()` / `resource()` to receive `ctx.fail(reason, …)` typed against the reason union. TypeScript catches typos, `data.reason` is auto-populated for observability, and the linter enforces conformance against the handler body. `recovery` is required (≥ 5 words) and is the single source of truth for the agent's next move. Pass `ctx.recoveryFor('reason')` as the throw's data to put it on the wire; override with an explicit recovery hint only when dynamic context matters. Baseline codes (`InternalError`, `ServiceUnavailable`, `Timeout`, `ValidationError`, `SerializationError`, `RequestCancelled`) bubble freely and don't need declaring.
|
|
174
177
|
|
|
175
178
|
On the wire, tool errors mirror the success-path `format-parity` invariant — both `content[]` (markdown, read by clients like Claude Desktop) and `structuredContent.error` (JSON `{ code, message, data? }`, read by clients like Claude Code) carry the same payload, with `data.recovery.hint` mirrored into the markdown text when present.
|
|
176
179
|
|
|
@@ -184,7 +187,7 @@ errors: [
|
|
|
184
187
|
],
|
|
185
188
|
async handler(input, ctx) {
|
|
186
189
|
const item = await db.find(input.id);
|
|
187
|
-
if (!item) throw ctx.fail('no_match', `No item ${input.id}
|
|
190
|
+
if (!item) throw ctx.fail('no_match', `No item ${input.id}`, ctx.recoveryFor('no_match'));
|
|
188
191
|
return item;
|
|
189
192
|
}
|
|
190
193
|
```
|
|
@@ -249,9 +252,9 @@ src/
|
|
|
249
252
|
|
|
250
253
|
## Skills
|
|
251
254
|
|
|
252
|
-
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.
|
|
255
|
+
Skills are modular instructions in `framework-skills/` at the project root. Read them directly when a task matches — e.g., `framework-skills/add-tool/SKILL.md` when adding a tool. `bun run list-skills` prints the registry. Keep development skills out of the plugin-host auto-loaded root `skills/` directory.
|
|
253
256
|
|
|
254
|
-
**Agent skill directory:** Copy skills into the directory your agent discovers (Claude Code: `.claude/skills/`, others: equivalent). Skills then load as context without referencing `skills/` paths. After framework updates, run the `maintenance` skill — Phase B re-syncs the agent directory.
|
|
257
|
+
**Agent skill directory:** Copy skills into the directory your agent discovers (Claude Code: `.claude/skills/`, others: equivalent). Skills then load as context without referencing `framework-skills/` paths. After framework updates, run the `maintenance` skill — Phase B re-syncs the agent directory.
|
|
255
258
|
|
|
256
259
|
Available skills:
|
|
257
260
|
|
|
@@ -267,12 +270,13 @@ Available skills:
|
|
|
267
270
|
| `add-test` | Scaffold test file for a tool, resource, or service |
|
|
268
271
|
| `field-test` | Exercise tools/resources/prompts with real inputs, verify behavior, report issues |
|
|
269
272
|
| `code-simplifier` | Post-session code review and cleanup against `git diff` — modernize syntax, consolidate duplication, align with codebase |
|
|
270
|
-
| `git-wrapup` | Land working-tree changes as a
|
|
273
|
+
| `git-wrapup` | Land working-tree changes as a commit stack — version bump, changelog, verify, commit by concern. No tag or push to main. |
|
|
271
274
|
| `tool-defs-analysis` | Read-only audit of MCP definition language across the surface — voice, leaks, defaults, recovery hints, output descriptions |
|
|
272
275
|
| `techniques` | Reusable response/data-shaping patterns (outline-on-overflow, capped-list disclosure) |
|
|
273
276
|
| `security-pass` | Audit server for MCP-flavored security gaps: output injection, scope blast radius, input sinks, tenant isolation |
|
|
274
277
|
| `polish-docs-meta` | Finalize docs, README, metadata, and agent protocol for shipping |
|
|
275
|
-
| `release-
|
|
278
|
+
| `release-pr-review` | Review an open release PR; applies only to projects declaring release PR mode. |
|
|
279
|
+
| `release-and-publish` | Tag + push + npm + MCP Registry + GH Release + Docker. Picks up from `git-wrapup` |
|
|
276
280
|
| `maintenance` | Investigate changelogs, adopt upstream changes, sync skills to agent dirs |
|
|
277
281
|
| `orchestrations` | Chain task skills into a gated multi-phase pipeline — build-out, QA-fix, update-ship — when you can spawn sub-agents |
|
|
278
282
|
| `report-issue-framework` | File a bug or feature request against `@cyanheads/mcp-ts-core` via `gh` CLI |
|
|
@@ -280,7 +284,7 @@ Available skills:
|
|
|
280
284
|
| `api-auth` | Auth modes, scopes, JWT/OAuth |
|
|
281
285
|
| `api-canvas` | DataCanvas: register tabular data, run SQL, export, plus the `spillover()` helper for big result sets — Tier 3 opt-in |
|
|
282
286
|
| `api-config` | AppConfig, parseConfig, env vars |
|
|
283
|
-
| `api-context` | Context interface, logger, state,
|
|
287
|
+
| `api-context` | Context interface, logger, state, input requests, enrichment |
|
|
284
288
|
| `api-errors` | McpError, JsonRpcErrorCode, error patterns |
|
|
285
289
|
| `api-linter` | Definition linter rule catalog — invoked by `bun run lint:mcp` and `devcheck` |
|
|
286
290
|
| `api-mirror` | MirrorService: persistent SQLite-backed local mirror of bulk upstream datasets — Tier 3 opt-in, Node/Bun only |
|
|
@@ -290,7 +294,7 @@ Available skills:
|
|
|
290
294
|
| `api-utils` | Formatting, parsing, security, pagination, scheduling, telemetry helpers |
|
|
291
295
|
| `api-workers` | Cloudflare Workers runtime |
|
|
292
296
|
|
|
293
|
-
**Chaining skills into pipelines.** When the user wants a multi-phase effort — build this server out, QA-and-fix the surface, update-and-ship — *and you can spawn sub-agents*, `skills/orchestrations/SKILL.md` sequences the task skills above into a gated pipeline with verification at each step. Read it to drive the run. Optional: skip it if you can't orchestrate sub-agents, and ignore it entirely if you were *spawned* as one — you've already been scoped to a single phase.
|
|
297
|
+
**Chaining skills into pipelines.** When the user wants a multi-phase effort — build this server out, QA-and-fix the surface, update-and-ship — *and you can spawn sub-agents*, `framework-skills/orchestrations/SKILL.md` sequences the task skills above into a gated pipeline with verification at each step. Read it to drive the run. Optional: skip it if you can't orchestrate sub-agents, and ignore it entirely if you were *spawned* as one — you've already been scoped to a single phase.
|
|
294
298
|
|
|
295
299
|
When you complete a skill's checklist, check the boxes and add a completion timestamp at the end (e.g., `Completed: 2026-03-11`).
|
|
296
300
|
|
|
@@ -304,9 +308,10 @@ When you complete a skill's checklist, check the boxes and add a completion time
|
|
|
304
308
|
| `bun run rebuild` | Clean + build |
|
|
305
309
|
| `bun run clean` | Remove build artifacts |
|
|
306
310
|
| `bun run devcheck` | Lint + format + typecheck + security + changelog sync |
|
|
307
|
-
| `bun run audit:
|
|
311
|
+
| `bun run audit:fix` | Upgrade vulnerable packages within existing ranges via `bun audit fix`; first response to a transitive advisory. |
|
|
312
|
+
| `bun run audit:refresh` | Delete `bun.lock`, reinstall, and audit. Last resort after `audit:fix`, `bun update <name>`, and `bun dedupe`; re-resolves every ranged dependency. |
|
|
308
313
|
| `bun run tree` | Generate directory structure doc |
|
|
309
|
-
| `bun run list-skills` | Print skill index from `skills/` frontmatter |
|
|
314
|
+
| `bun run list-skills` | Print skill index from `framework-skills/` frontmatter |
|
|
310
315
|
| `bun run format` | Auto-fix formatting (safe fixes only) |
|
|
311
316
|
| `bun run format:unsafe` | Also apply Biome's unsafe autofixes — review the diff; they can change behavior |
|
|
312
317
|
| `bun run lint:mcp` | Validate MCP definitions |
|
|
@@ -323,11 +328,13 @@ When you complete a skill's checklist, check the boxes and add a completion time
|
|
|
323
328
|
|
|
324
329
|
## Bundling
|
|
325
330
|
|
|
331
|
+
The MCPB `manifest.json` keeps `author.name: "cyanheads"` as its established display identity. `package.json` uses the full Casey Hand author string; this is an intentional exception to generic author-name synchronization.
|
|
332
|
+
|
|
326
333
|
`bun run bundle` produces a `.mcpb` extension bundle for one-click install in Claude Desktop. MCPB is stdio-only — HTTP deployments are unaffected. Delete `manifest.json` and `.mcpbignore` to skip; `lint:packaging` skips cleanly when `manifest.json` is absent.
|
|
327
334
|
|
|
328
335
|
**Adding an env var requires both files:** `server.json` (`environmentVariables[]`) and `manifest.json` (`mcp_config.env` + `user_config`). `lint:packaging` (run by `devcheck`) verifies env var names match.
|
|
329
336
|
|
|
330
|
-
**README install badges** (Claude Desktop `.mcpb`, Cursor, VS Code) and the `base64` / `encodeURIComponent` config-generation commands are ship-time concerns — run the `polish-docs-meta` skill, which carries the badge format, layout, and generation snippets in `skills/polish-docs-meta/references/readme.md`.
|
|
337
|
+
**README install badges** (Claude Desktop `.mcpb`, Cursor, VS Code) and the `base64` / `encodeURIComponent` config-generation commands are ship-time concerns — run the `polish-docs-meta` skill, which carries the badge format, layout, and generation snippets in `framework-skills/polish-docs-meta/references/readme.md`.
|
|
331
338
|
|
|
332
339
|
---
|
|
333
340
|
|
|
@@ -352,27 +359,22 @@ security: false # optional — true flags security fi
|
|
|
352
359
|
|
|
353
360
|
`agent-notes` is an optional free-form field for maintenance agents processing the release downstream. Content here won't appear in the rendered CHANGELOG — it's consumed by agents running the `maintenance` skill. Use it for adoption instructions that don't fit the human-facing sections. Omit entirely when there's nothing to say.
|
|
354
361
|
|
|
355
|
-
**Section order** (Keep a Changelog): Added, Changed, Deprecated, Removed, Fixed, Security. Include only sections with entries — don't ship empty headers.
|
|
362
|
+
**Section order** (Keep a Changelog): Added, Changed, Deprecated, Removed, Fixed, Security, Dependencies. Include only sections with entries — don't ship empty headers.
|
|
356
363
|
|
|
357
|
-
**Tag annotations** render as GitHub Release bodies via `--notes-from-tag`. They must be structured markdown — never a flat comma-separated string. Subject omits the version number (GitHub prepends it). See `
|
|
364
|
+
**Tag annotations** render as GitHub Release bodies via `--notes-from-tag`. They must be structured markdown — never a flat comma-separated string. Subject omits the version number (GitHub prepends it). See `framework-skills/release-and-publish/SKILL.md` for the tag format.
|
|
358
365
|
|
|
359
366
|
---
|
|
360
367
|
|
|
361
368
|
## Publishing
|
|
362
369
|
|
|
363
|
-
After git
|
|
370
|
+
This project releases directly from `main`. After `git-wrapup` finishes the versioned commit stack, run **`release-and-publish`** for the annotated tag, push, and publishing targets below.
|
|
364
371
|
|
|
365
372
|
### Wrapup flow
|
|
366
373
|
|
|
367
374
|
When running the git wrapup checklist (`polish-docs-meta` or equivalent):
|
|
368
375
|
|
|
369
376
|
- **Minimum version bump is `0.0.1` (patch)** unless the user specifies a larger bump.
|
|
370
|
-
-
|
|
371
|
-
|
|
372
|
-
```bash
|
|
373
|
-
git tag -a v<version> -m "v<version>"
|
|
374
|
-
git push && git push --tags
|
|
375
|
-
```
|
|
377
|
+
- `git-wrapup` owns the commit stack; `release-and-publish` owns the tag and push. Follow those skills rather than duplicating their commands here.
|
|
376
378
|
|
|
377
379
|
### Targets
|
|
378
380
|
|
|
@@ -417,7 +419,7 @@ import { getServerConfig } from '@/config/server-config.js';
|
|
|
417
419
|
- [ ] `format()` renders all data the LLM needs — different clients forward different surfaces (Claude Code → `structuredContent`, Claude Desktop → `content[]`); both must carry the same data
|
|
418
420
|
- [ ] Registered in `createApp()` arrays (directly or via barrel exports)
|
|
419
421
|
- [ ] Tests use `createMockContext()` from `@cyanheads/mcp-ts-core/testing`
|
|
420
|
-
- [ ] `.codex-plugin/plugin.json` populated — `name`, `version`, `description`, `repository`, `license` from `package.json`; `interface.displayName` =
|
|
421
|
-
- [ ] `.codex-plugin/mcp.json` updated — server name key
|
|
422
|
-
- [ ] `.claude-plugin/plugin.json` populated — `name`, `version`, `description`, `repository`, `license` from `package.json`; inline `mcpServers` entry
|
|
422
|
+
- [ ] `.codex-plugin/plugin.json` populated — `name`, `version`, `description`, `repository`, `license` from `package.json`; `interface.displayName` = the unscoped repo name; `interface.shortDescription` from `package.json` description
|
|
423
|
+
- [ ] `.codex-plugin/mcp.json` updated — server name key is the unscoped repo name; user-supplied variables are forwarded with `env_vars`, never empty `env` values
|
|
424
|
+
- [ ] `.claude-plugin/plugin.json` populated — `name`, `version`, `description`, `repository`, `license` from `package.json`; inline `mcpServers` entry keyed by the unscoped repo name; user-supplied variables use declared `userConfig` options and `${user_config.<option>}` references, never empty `env` values
|
|
423
425
|
- [ ] `bun run devcheck` passes
|
package/CLAUDE.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# Agent Protocol
|
|
2
2
|
|
|
3
3
|
**Server:** calculator-mcp-server
|
|
4
|
-
**Version:** 0.4.
|
|
5
|
-
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.
|
|
6
|
-
**Engines:** Bun ≥1.
|
|
7
|
-
**MCP SDK:** `@modelcontextprotocol/
|
|
8
|
-
**Zod:** 4.
|
|
4
|
+
**Version:** 0.4.2
|
|
5
|
+
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.0`
|
|
6
|
+
**Engines:** Bun ≥1.4.0, Node ≥24.0.0
|
|
7
|
+
**MCP SDK:** `@modelcontextprotocol/server` 2.0.0
|
|
8
|
+
**Zod:** 4.6.1
|
|
9
9
|
|
|
10
10
|
> **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.
|
|
11
11
|
|
|
@@ -50,7 +50,7 @@ Tailor suggestions to what's actually missing or stale — don't recite the full
|
|
|
50
50
|
- **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.
|
|
51
51
|
- **Use `ctx.log`** for request-scoped logging. No `console` calls.
|
|
52
52
|
- **Use `ctx.state`** for tenant-scoped storage. Never access persistence directly.
|
|
53
|
-
- **
|
|
53
|
+
- **Need input the caller didn't supply?** `return ctx.requestInput(...)` and read `ctx.inputs` when the handler is re-entered. Never `await` for user input mid-handler.
|
|
54
54
|
- **Secrets in env vars only** — never hardcoded.
|
|
55
55
|
- **Close the loop on issues.** When implementing work tracked by a GitHub issue, comment on the issue with what landed and close it. Do both — a comment without a close leaves stale issues open; a close without a comment leaves no record of what shipped. The comment is for future readers — state the concrete changes, not the conversation that produced them.
|
|
56
56
|
|
|
@@ -121,6 +121,9 @@ export const helpResource = resource('calculator://help', {
|
|
|
121
121
|
|
|
122
122
|
```ts
|
|
123
123
|
// src/config/server-config.ts — lazy-parsed, separate from framework config
|
|
124
|
+
import { z } from '@cyanheads/mcp-ts-core';
|
|
125
|
+
import { parseEnvConfig } from '@cyanheads/mcp-ts-core/config';
|
|
126
|
+
|
|
124
127
|
const ServerConfigSchema = z.object({
|
|
125
128
|
maxExpressionLength: z.coerce.number().int().min(10).max(10_000).default(1000)
|
|
126
129
|
.describe('Maximum allowed expression string length (10–10,000)'),
|
|
@@ -131,10 +134,10 @@ const ServerConfigSchema = z.object({
|
|
|
131
134
|
});
|
|
132
135
|
let _config: z.infer<typeof ServerConfigSchema> | undefined;
|
|
133
136
|
export function getServerConfig() {
|
|
134
|
-
_config ??= ServerConfigSchema
|
|
135
|
-
maxExpressionLength:
|
|
136
|
-
evaluationTimeoutMs:
|
|
137
|
-
maxResultLength:
|
|
137
|
+
_config ??= parseEnvConfig(ServerConfigSchema, {
|
|
138
|
+
maxExpressionLength: 'CALC_MAX_EXPRESSION_LENGTH',
|
|
139
|
+
evaluationTimeoutMs: 'CALC_EVALUATION_TIMEOUT_MS',
|
|
140
|
+
maxResultLength: 'CALC_MAX_RESULT_LENGTH',
|
|
138
141
|
});
|
|
139
142
|
return _config;
|
|
140
143
|
}
|
|
@@ -154,13 +157,13 @@ Handlers receive a unified `ctx` object. Key properties:
|
|
|
154
157
|
|
|
155
158
|
| Property | Description |
|
|
156
159
|
|:---------|:------------|
|
|
157
|
-
| `ctx.log` | Request-scoped logger — `.debug()`, `.info()`, `.notice()`, `.warning()`, `.error()`. Auto-correlates requestId, traceId, tenantId. |
|
|
158
|
-
| `ctx.state` | Tenant-scoped KV — `.get(key)`, `.set(key, value, { ttl? })`, `.delete(key)`, `.list(prefix, { cursor, limit })`. Accepts any serializable value. |
|
|
159
|
-
| `ctx.
|
|
160
|
-
| `ctx.
|
|
160
|
+
| `ctx.log` | Request-scoped logger — `.debug()`, `.info()`, `.notice()`, `.warning()`, `.error()`. Auto-correlates requestId, traceId, tenantId. Dual-sink: Pino and `notifications/message` to the client, so treat it as client-visible. |
|
|
161
|
+
| `ctx.state` | Tenant-scoped KV — `.get(key)`, `.set(key, value, { ttl? })`, `.delete(key)`, `.getMany(keys)`, `.list(prefix, { cursor, limit })`. Accepts any serializable value. |
|
|
162
|
+
| `ctx.requestInput` | Suspend and ask the caller for more input — `return ctx.requestInput({ inputRequests: { key: inputRequired.elicit({ message, requestedSchema }) } })`. Never returns; the handler is re-entered with the answers. Always present. |
|
|
163
|
+
| `ctx.inputs` | Reader over a retried request's responses — `.accepted(key, schema)`, `.view(key)`, `.state()`, `.dropped`. Empty on the first round. |
|
|
164
|
+
| `ctx.enrich` | Success-path agent context — `ctx.enrich(...)` or `.notice()` / `.total()` / `.echo()` / `.truncated()`. Reaches both response surfaces only when the definition declares an `enrichment` block. |
|
|
165
|
+
| `ctx.content` | Non-text content blocks — `.image(data, mimeType)`, `.audio(data, mimeType)`, or `ctx.content(block)`. Prepended to `content[]`; never enters `structuredContent`. |
|
|
161
166
|
| `ctx.signal` | `AbortSignal` for cancellation. |
|
|
162
|
-
| `ctx.progress` | Task progress (present when `task: true`) — `.setTotal(n)`, `.increment()`, `.update(message)`. |
|
|
163
|
-
| `ctx.enrich` | Success-path enrichment — `.notice(text)`, `.total(n)`, `.echo(query)`, `.delta({ field, before, after })`. Merges into `structuredContent` and `content[]` trailer automatically. |
|
|
164
167
|
| `ctx.requestId` | Unique request ID. |
|
|
165
168
|
| `ctx.tenantId` | Tenant ID from JWT or `'default'` for stdio. |
|
|
166
169
|
|
|
@@ -170,7 +173,7 @@ Handlers receive a unified `ctx` object. Key properties:
|
|
|
170
173
|
|
|
171
174
|
Handlers throw — the framework catches, classifies, and formats.
|
|
172
175
|
|
|
173
|
-
**Recommended: typed error contract.** Declare `errors: [{ reason, code, when, recovery, retryable? }]` on `tool()` / `resource()` to receive
|
|
176
|
+
**Recommended: typed error contract.** Declare `errors: [{ reason, code, when, recovery, retryable? }]` on `tool()` / `resource()` to receive `ctx.fail(reason, …)` typed against the reason union. TypeScript catches typos, `data.reason` is auto-populated for observability, and the linter enforces conformance against the handler body. `recovery` is required (≥ 5 words) and is the single source of truth for the agent's next move. Pass `ctx.recoveryFor('reason')` as the throw's data to put it on the wire; override with an explicit recovery hint only when dynamic context matters. Baseline codes (`InternalError`, `ServiceUnavailable`, `Timeout`, `ValidationError`, `SerializationError`, `RequestCancelled`) bubble freely and don't need declaring.
|
|
174
177
|
|
|
175
178
|
On the wire, tool errors mirror the success-path `format-parity` invariant — both `content[]` (markdown, read by clients like Claude Desktop) and `structuredContent.error` (JSON `{ code, message, data? }`, read by clients like Claude Code) carry the same payload, with `data.recovery.hint` mirrored into the markdown text when present.
|
|
176
179
|
|
|
@@ -184,7 +187,7 @@ errors: [
|
|
|
184
187
|
],
|
|
185
188
|
async handler(input, ctx) {
|
|
186
189
|
const item = await db.find(input.id);
|
|
187
|
-
if (!item) throw ctx.fail('no_match', `No item ${input.id}
|
|
190
|
+
if (!item) throw ctx.fail('no_match', `No item ${input.id}`, ctx.recoveryFor('no_match'));
|
|
188
191
|
return item;
|
|
189
192
|
}
|
|
190
193
|
```
|
|
@@ -249,9 +252,9 @@ src/
|
|
|
249
252
|
|
|
250
253
|
## Skills
|
|
251
254
|
|
|
252
|
-
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.
|
|
255
|
+
Skills are modular instructions in `framework-skills/` at the project root. Read them directly when a task matches — e.g., `framework-skills/add-tool/SKILL.md` when adding a tool. `bun run list-skills` prints the registry. Keep development skills out of the plugin-host auto-loaded root `skills/` directory.
|
|
253
256
|
|
|
254
|
-
**Agent skill directory:** Copy skills into the directory your agent discovers (Claude Code: `.claude/skills/`, others: equivalent). Skills then load as context without referencing `skills/` paths. After framework updates, run the `maintenance` skill — Phase B re-syncs the agent directory.
|
|
257
|
+
**Agent skill directory:** Copy skills into the directory your agent discovers (Claude Code: `.claude/skills/`, others: equivalent). Skills then load as context without referencing `framework-skills/` paths. After framework updates, run the `maintenance` skill — Phase B re-syncs the agent directory.
|
|
255
258
|
|
|
256
259
|
Available skills:
|
|
257
260
|
|
|
@@ -267,12 +270,13 @@ Available skills:
|
|
|
267
270
|
| `add-test` | Scaffold test file for a tool, resource, or service |
|
|
268
271
|
| `field-test` | Exercise tools/resources/prompts with real inputs, verify behavior, report issues |
|
|
269
272
|
| `code-simplifier` | Post-session code review and cleanup against `git diff` — modernize syntax, consolidate duplication, align with codebase |
|
|
270
|
-
| `git-wrapup` | Land working-tree changes as a
|
|
273
|
+
| `git-wrapup` | Land working-tree changes as a commit stack — version bump, changelog, verify, commit by concern. No tag or push to main. |
|
|
271
274
|
| `tool-defs-analysis` | Read-only audit of MCP definition language across the surface — voice, leaks, defaults, recovery hints, output descriptions |
|
|
272
275
|
| `techniques` | Reusable response/data-shaping patterns (outline-on-overflow, capped-list disclosure) |
|
|
273
276
|
| `security-pass` | Audit server for MCP-flavored security gaps: output injection, scope blast radius, input sinks, tenant isolation |
|
|
274
277
|
| `polish-docs-meta` | Finalize docs, README, metadata, and agent protocol for shipping |
|
|
275
|
-
| `release-
|
|
278
|
+
| `release-pr-review` | Review an open release PR; applies only to projects declaring release PR mode. |
|
|
279
|
+
| `release-and-publish` | Tag + push + npm + MCP Registry + GH Release + Docker. Picks up from `git-wrapup` |
|
|
276
280
|
| `maintenance` | Investigate changelogs, adopt upstream changes, sync skills to agent dirs |
|
|
277
281
|
| `orchestrations` | Chain task skills into a gated multi-phase pipeline — build-out, QA-fix, update-ship — when you can spawn sub-agents |
|
|
278
282
|
| `report-issue-framework` | File a bug or feature request against `@cyanheads/mcp-ts-core` via `gh` CLI |
|
|
@@ -280,7 +284,7 @@ Available skills:
|
|
|
280
284
|
| `api-auth` | Auth modes, scopes, JWT/OAuth |
|
|
281
285
|
| `api-canvas` | DataCanvas: register tabular data, run SQL, export, plus the `spillover()` helper for big result sets — Tier 3 opt-in |
|
|
282
286
|
| `api-config` | AppConfig, parseConfig, env vars |
|
|
283
|
-
| `api-context` | Context interface, logger, state,
|
|
287
|
+
| `api-context` | Context interface, logger, state, input requests, enrichment |
|
|
284
288
|
| `api-errors` | McpError, JsonRpcErrorCode, error patterns |
|
|
285
289
|
| `api-linter` | Definition linter rule catalog — invoked by `bun run lint:mcp` and `devcheck` |
|
|
286
290
|
| `api-mirror` | MirrorService: persistent SQLite-backed local mirror of bulk upstream datasets — Tier 3 opt-in, Node/Bun only |
|
|
@@ -290,7 +294,7 @@ Available skills:
|
|
|
290
294
|
| `api-utils` | Formatting, parsing, security, pagination, scheduling, telemetry helpers |
|
|
291
295
|
| `api-workers` | Cloudflare Workers runtime |
|
|
292
296
|
|
|
293
|
-
**Chaining skills into pipelines.** When the user wants a multi-phase effort — build this server out, QA-and-fix the surface, update-and-ship — *and you can spawn sub-agents*, `skills/orchestrations/SKILL.md` sequences the task skills above into a gated pipeline with verification at each step. Read it to drive the run. Optional: skip it if you can't orchestrate sub-agents, and ignore it entirely if you were *spawned* as one — you've already been scoped to a single phase.
|
|
297
|
+
**Chaining skills into pipelines.** When the user wants a multi-phase effort — build this server out, QA-and-fix the surface, update-and-ship — *and you can spawn sub-agents*, `framework-skills/orchestrations/SKILL.md` sequences the task skills above into a gated pipeline with verification at each step. Read it to drive the run. Optional: skip it if you can't orchestrate sub-agents, and ignore it entirely if you were *spawned* as one — you've already been scoped to a single phase.
|
|
294
298
|
|
|
295
299
|
When you complete a skill's checklist, check the boxes and add a completion timestamp at the end (e.g., `Completed: 2026-03-11`).
|
|
296
300
|
|
|
@@ -304,9 +308,10 @@ When you complete a skill's checklist, check the boxes and add a completion time
|
|
|
304
308
|
| `bun run rebuild` | Clean + build |
|
|
305
309
|
| `bun run clean` | Remove build artifacts |
|
|
306
310
|
| `bun run devcheck` | Lint + format + typecheck + security + changelog sync |
|
|
307
|
-
| `bun run audit:
|
|
311
|
+
| `bun run audit:fix` | Upgrade vulnerable packages within existing ranges via `bun audit fix`; first response to a transitive advisory. |
|
|
312
|
+
| `bun run audit:refresh` | Delete `bun.lock`, reinstall, and audit. Last resort after `audit:fix`, `bun update <name>`, and `bun dedupe`; re-resolves every ranged dependency. |
|
|
308
313
|
| `bun run tree` | Generate directory structure doc |
|
|
309
|
-
| `bun run list-skills` | Print skill index from `skills/` frontmatter |
|
|
314
|
+
| `bun run list-skills` | Print skill index from `framework-skills/` frontmatter |
|
|
310
315
|
| `bun run format` | Auto-fix formatting (safe fixes only) |
|
|
311
316
|
| `bun run format:unsafe` | Also apply Biome's unsafe autofixes — review the diff; they can change behavior |
|
|
312
317
|
| `bun run lint:mcp` | Validate MCP definitions |
|
|
@@ -323,11 +328,13 @@ When you complete a skill's checklist, check the boxes and add a completion time
|
|
|
323
328
|
|
|
324
329
|
## Bundling
|
|
325
330
|
|
|
331
|
+
The MCPB `manifest.json` keeps `author.name: "cyanheads"` as its established display identity. `package.json` uses the full Casey Hand author string; this is an intentional exception to generic author-name synchronization.
|
|
332
|
+
|
|
326
333
|
`bun run bundle` produces a `.mcpb` extension bundle for one-click install in Claude Desktop. MCPB is stdio-only — HTTP deployments are unaffected. Delete `manifest.json` and `.mcpbignore` to skip; `lint:packaging` skips cleanly when `manifest.json` is absent.
|
|
327
334
|
|
|
328
335
|
**Adding an env var requires both files:** `server.json` (`environmentVariables[]`) and `manifest.json` (`mcp_config.env` + `user_config`). `lint:packaging` (run by `devcheck`) verifies env var names match.
|
|
329
336
|
|
|
330
|
-
**README install badges** (Claude Desktop `.mcpb`, Cursor, VS Code) and the `base64` / `encodeURIComponent` config-generation commands are ship-time concerns — run the `polish-docs-meta` skill, which carries the badge format, layout, and generation snippets in `skills/polish-docs-meta/references/readme.md`.
|
|
337
|
+
**README install badges** (Claude Desktop `.mcpb`, Cursor, VS Code) and the `base64` / `encodeURIComponent` config-generation commands are ship-time concerns — run the `polish-docs-meta` skill, which carries the badge format, layout, and generation snippets in `framework-skills/polish-docs-meta/references/readme.md`.
|
|
331
338
|
|
|
332
339
|
---
|
|
333
340
|
|
|
@@ -352,27 +359,22 @@ security: false # optional — true flags security fi
|
|
|
352
359
|
|
|
353
360
|
`agent-notes` is an optional free-form field for maintenance agents processing the release downstream. Content here won't appear in the rendered CHANGELOG — it's consumed by agents running the `maintenance` skill. Use it for adoption instructions that don't fit the human-facing sections. Omit entirely when there's nothing to say.
|
|
354
361
|
|
|
355
|
-
**Section order** (Keep a Changelog): Added, Changed, Deprecated, Removed, Fixed, Security. Include only sections with entries — don't ship empty headers.
|
|
362
|
+
**Section order** (Keep a Changelog): Added, Changed, Deprecated, Removed, Fixed, Security, Dependencies. Include only sections with entries — don't ship empty headers.
|
|
356
363
|
|
|
357
|
-
**Tag annotations** render as GitHub Release bodies via `--notes-from-tag`. They must be structured markdown — never a flat comma-separated string. Subject omits the version number (GitHub prepends it). See `
|
|
364
|
+
**Tag annotations** render as GitHub Release bodies via `--notes-from-tag`. They must be structured markdown — never a flat comma-separated string. Subject omits the version number (GitHub prepends it). See `framework-skills/release-and-publish/SKILL.md` for the tag format.
|
|
358
365
|
|
|
359
366
|
---
|
|
360
367
|
|
|
361
368
|
## Publishing
|
|
362
369
|
|
|
363
|
-
After git
|
|
370
|
+
This project releases directly from `main`. After `git-wrapup` finishes the versioned commit stack, run **`release-and-publish`** for the annotated tag, push, and publishing targets below.
|
|
364
371
|
|
|
365
372
|
### Wrapup flow
|
|
366
373
|
|
|
367
374
|
When running the git wrapup checklist (`polish-docs-meta` or equivalent):
|
|
368
375
|
|
|
369
376
|
- **Minimum version bump is `0.0.1` (patch)** unless the user specifies a larger bump.
|
|
370
|
-
-
|
|
371
|
-
|
|
372
|
-
```bash
|
|
373
|
-
git tag -a v<version> -m "v<version>"
|
|
374
|
-
git push && git push --tags
|
|
375
|
-
```
|
|
377
|
+
- `git-wrapup` owns the commit stack; `release-and-publish` owns the tag and push. Follow those skills rather than duplicating their commands here.
|
|
376
378
|
|
|
377
379
|
### Targets
|
|
378
380
|
|
|
@@ -417,7 +419,7 @@ import { getServerConfig } from '@/config/server-config.js';
|
|
|
417
419
|
- [ ] `format()` renders all data the LLM needs — different clients forward different surfaces (Claude Code → `structuredContent`, Claude Desktop → `content[]`); both must carry the same data
|
|
418
420
|
- [ ] Registered in `createApp()` arrays (directly or via barrel exports)
|
|
419
421
|
- [ ] Tests use `createMockContext()` from `@cyanheads/mcp-ts-core/testing`
|
|
420
|
-
- [ ] `.codex-plugin/plugin.json` populated — `name`, `version`, `description`, `repository`, `license` from `package.json`; `interface.displayName` =
|
|
421
|
-
- [ ] `.codex-plugin/mcp.json` updated — server name key
|
|
422
|
-
- [ ] `.claude-plugin/plugin.json` populated — `name`, `version`, `description`, `repository`, `license` from `package.json`; inline `mcpServers` entry
|
|
422
|
+
- [ ] `.codex-plugin/plugin.json` populated — `name`, `version`, `description`, `repository`, `license` from `package.json`; `interface.displayName` = the unscoped repo name; `interface.shortDescription` from `package.json` description
|
|
423
|
+
- [ ] `.codex-plugin/mcp.json` updated — server name key is the unscoped repo name; user-supplied variables are forwarded with `env_vars`, never empty `env` values
|
|
424
|
+
- [ ] `.claude-plugin/plugin.json` populated — `name`, `version`, `description`, `repository`, `license` from `package.json`; inline `mcpServers` entry keyed by the unscoped repo name; user-supplied variables use declared `userConfig` options and `${user_config.<option>}` references, never empty `env` values
|
|
423
425
|
- [ ] `bun run devcheck` passes
|
package/Dockerfile
CHANGED
|
@@ -4,15 +4,17 @@
|
|
|
4
4
|
# This stage installs all dependencies (including dev), builds the TypeScript
|
|
5
5
|
# source code into JavaScript, and prepares the production assets.
|
|
6
6
|
# ==============================================================================
|
|
7
|
-
FROM oven/bun:1.
|
|
7
|
+
FROM --platform=$BUILDPLATFORM oven/bun:1.4.0 AS build
|
|
8
8
|
|
|
9
9
|
WORKDIR /usr/src/app
|
|
10
10
|
|
|
11
11
|
# Copy dependency manifests for optimized layer caching
|
|
12
12
|
COPY package.json bun.lock ./
|
|
13
13
|
|
|
14
|
-
# Install all dependencies (including dev dependencies for building)
|
|
15
|
-
|
|
14
|
+
# Install all dependencies (including dev dependencies for building).
|
|
15
|
+
# The BuildKit cache mount persists Bun's global package cache across builds.
|
|
16
|
+
RUN --mount=type=cache,target=/root/.bun/install/cache \
|
|
17
|
+
bun install --frozen-lockfile --ignore-scripts
|
|
16
18
|
|
|
17
19
|
# Copy the rest of the source code
|
|
18
20
|
COPY . .
|
|
@@ -28,7 +30,7 @@ RUN bun run build
|
|
|
28
30
|
# application. It uses a slim base image and only includes production
|
|
29
31
|
# dependencies and build artifacts.
|
|
30
32
|
# ==============================================================================
|
|
31
|
-
FROM oven/bun:1.
|
|
33
|
+
FROM oven/bun:1.4.0-slim AS production
|
|
32
34
|
|
|
33
35
|
WORKDIR /usr/src/app
|
|
34
36
|
|
|
@@ -39,7 +41,7 @@ ENV NODE_ENV=production
|
|
|
39
41
|
# OCI image metadata (https://github.com/opencontainers/image-spec/blob/main/annotations.md)
|
|
40
42
|
ARG APP_VERSION
|
|
41
43
|
LABEL org.opencontainers.image.title="calculator-mcp-server"
|
|
42
|
-
LABEL org.opencontainers.image.description="
|
|
44
|
+
LABEL org.opencontainers.image.description="Evaluate, simplify, and differentiate mathematical expressions via MCP. STDIO or Streamable HTTP."
|
|
43
45
|
LABEL org.opencontainers.image.source="https://github.com/cyanheads/calculator-mcp-server"
|
|
44
46
|
LABEL org.opencontainers.image.licenses="Apache-2.0"
|
|
45
47
|
LABEL org.opencontainers.image.version="${APP_VERSION}"
|
|
@@ -47,16 +49,18 @@ LABEL org.opencontainers.image.version="${APP_VERSION}"
|
|
|
47
49
|
# Copy dependency manifests
|
|
48
50
|
COPY package.json bun.lock ./
|
|
49
51
|
|
|
50
|
-
# Install only production dependencies, ignoring
|
|
51
|
-
# that are not needed in the final production image.
|
|
52
|
-
RUN bun
|
|
52
|
+
# Install only production dependencies, ignoring optional peer tiers and lifecycle
|
|
53
|
+
# scripts that are not needed in the final production image.
|
|
54
|
+
RUN --mount=type=cache,target=/root/.bun/install/cache \
|
|
55
|
+
bun install --production --omit=peer --frozen-lockfile --ignore-scripts
|
|
53
56
|
|
|
54
57
|
# Conditionally install OpenTelemetry optional peer dependencies (Tier 3).
|
|
55
58
|
# These are not bundled by default to keep the base image lean. Enable at build time
|
|
56
59
|
# with: docker build --build-arg OTEL_ENABLED=true
|
|
57
60
|
ARG OTEL_ENABLED=true
|
|
58
|
-
RUN
|
|
59
|
-
|
|
61
|
+
RUN --mount=type=cache,target=/root/.bun/install/cache \
|
|
62
|
+
if [ "$OTEL_ENABLED" = "true" ]; then \
|
|
63
|
+
bun add --omit=dev --omit=peer --ignore-scripts @hono/otel \
|
|
60
64
|
@opentelemetry/instrumentation-http \
|
|
61
65
|
@opentelemetry/exporter-metrics-otlp-http \
|
|
62
66
|
@opentelemetry/exporter-trace-otlp-http \
|
package/LICENSE
CHANGED