@cyanheads/cyanheads-mcp-server 0.3.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/AGENTS.md +30 -26
  2. package/CLAUDE.md +30 -26
  3. package/Dockerfile +16 -7
  4. package/README.md +17 -17
  5. package/changelog/0.3.x/0.3.3.md +24 -0
  6. package/changelog/0.3.x/0.3.4.md +33 -0
  7. package/changelog/0.4.x/0.4.0.md +21 -0
  8. package/changelog/template.md +5 -3
  9. package/dist/config/server-config.d.ts +1 -1
  10. package/dist/config/server-config.d.ts.map +1 -1
  11. package/dist/config/server-config.js +13 -6
  12. package/dist/config/server-config.js.map +1 -1
  13. package/dist/index.js +10 -7
  14. package/dist/index.js.map +1 -1
  15. package/dist/mcp-server/tools/definitions/{describe.tool.d.ts → describe-entry.tool.d.ts} +9 -9
  16. package/dist/mcp-server/tools/definitions/describe-entry.tool.d.ts.map +1 -0
  17. package/dist/mcp-server/tools/definitions/{describe.tool.js → describe-entry.tool.js} +19 -10
  18. package/dist/mcp-server/tools/definitions/describe-entry.tool.js.map +1 -0
  19. package/dist/mcp-server/tools/definitions/{search.tool.d.ts → search-catalog.tool.d.ts} +9 -9
  20. package/dist/mcp-server/tools/definitions/search-catalog.tool.d.ts.map +1 -0
  21. package/dist/mcp-server/tools/definitions/{search.tool.js → search-catalog.tool.js} +17 -8
  22. package/dist/mcp-server/tools/definitions/search-catalog.tool.js.map +1 -0
  23. package/dist/services/catalog/catalog-service.d.ts +14 -3
  24. package/dist/services/catalog/catalog-service.d.ts.map +1 -1
  25. package/dist/services/catalog/catalog-service.js +20 -16
  26. package/dist/services/catalog/catalog-service.js.map +1 -1
  27. package/dist/services/catalog/embeddings-runtime.d.ts.map +1 -1
  28. package/dist/services/catalog/embeddings-runtime.js.map +1 -1
  29. package/dist/services/catalog/remote-catalog-provider.d.ts.map +1 -1
  30. package/dist/services/catalog/self-record.d.ts +26 -0
  31. package/dist/services/catalog/self-record.d.ts.map +1 -0
  32. package/dist/services/catalog/self-record.js +88 -0
  33. package/dist/services/catalog/self-record.js.map +1 -0
  34. package/dist/services/catalog/service-instance.d.ts +17 -0
  35. package/dist/services/catalog/service-instance.d.ts.map +1 -0
  36. package/dist/services/catalog/service-instance.js +28 -0
  37. package/dist/services/catalog/service-instance.js.map +1 -0
  38. package/dist/services/catalog/types.d.ts +2 -0
  39. package/dist/services/catalog/types.d.ts.map +1 -1
  40. package/manifest.json +3 -3
  41. package/package.json +25 -22
  42. package/server.json +7 -7
  43. package/dist/mcp-server/tools/definitions/describe.tool.d.ts.map +0 -1
  44. package/dist/mcp-server/tools/definitions/describe.tool.js.map +0 -1
  45. package/dist/mcp-server/tools/definitions/search.tool.d.ts.map +0 -1
  46. package/dist/mcp-server/tools/definitions/search.tool.js.map +0 -1
package/AGENTS.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # Developer Protocol
2
2
 
3
3
  **Server:** cyanheads-mcp-server
4
- **Version:** 0.3.2
5
- **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.9.21`
4
+ **Version:** 0.4.0
5
+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.11.0`
6
6
  **Engines:** Bun ≥1.3.0, Node ≥24.0.0
7
7
  **MCP SDK:** `@modelcontextprotocol/sdk` ^1.29.0
8
8
  **Zod:** ^4.4.3
@@ -15,11 +15,13 @@
15
15
 
16
16
  A fleet discovery surface for the cyanheads MCP ecosystem. Two tools:
17
17
 
18
- - **`cyanheads_search`** — semantic search across fleet tools and servers using cosine similarity over Matryoshka-truncated 256-dim embeddings from `Snowflake/snowflake-arctic-embed-m-v1.5`.
19
- - **`cyanheads_describe`** — returns the description, connection URL, and per-client install snippet for any named tool or server.
18
+ - **`cyanheads_search_catalog`** — semantic search across fleet tools and servers using cosine similarity over Matryoshka-truncated 256-dim embeddings from `Snowflake/snowflake-arctic-embed-m-v1.5`.
19
+ - **`cyanheads_describe_entry`** — returns the description, connection URL, and per-client install snippet for any named tool or server.
20
20
 
21
21
  The catalog (`fleet.json`) is produced by `caseyjhand-portfolio/scripts/build-fleet-json.ts` at portfolio deploy time. Document vectors are baked into the JSON. The server only embeds the user's query at runtime via `@huggingface/transformers`. See `docs/design.md` for the full architecture.
22
22
 
23
+ The generated catalog does not include this server, so `src/services/catalog/self-record.ts` supplies a static `CatalogRecord` for `cyanheads-mcp-server` that `CatalogService.getServer()` / `getTool()` consult **only after** the remote index misses. A remote entry always wins, so the fallback self-heals to dead code if the generator ever starts emitting one. The record is never part of the vector index, so semantic search does not surface it — only exact-name resolution does.
24
+
23
25
  ---
24
26
 
25
27
  ## What's Next?
@@ -46,7 +48,7 @@ Tailor suggestions to what's actually missing or stale — don't recite the full
46
48
  - **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.
47
49
  - **Use `ctx.log`** for request-scoped logging. No `console` calls.
48
50
  - **Use `ctx.state`** for tenant-scoped storage. Never access persistence directly.
49
- - **Check `ctx.elicit` / `ctx.sample`** for presence before calling.
51
+ - **Check `ctx.elicit`** for presence before calling.
50
52
  - **Secrets in env vars only** — never hardcoded.
51
53
  - **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.
52
54
 
@@ -160,8 +162,7 @@ Handlers receive a unified `ctx` object. Key properties:
160
162
  |:---------|:------------|
161
163
  | `ctx.log` | Request-scoped logger — `.debug()`, `.info()`, `.notice()`, `.warning()`, `.error()`. Auto-correlates requestId, traceId, tenantId. |
162
164
  | `ctx.state` | Tenant-scoped KV — `.get(key)`, `.set(key, value, { ttl? })`, `.delete(key)`, `.list(prefix, { cursor, limit })`. Accepts any serializable value. |
163
- | `ctx.elicit` | Ask user for structured input. **Check for presence first:** `if (ctx.elicit) { ... }` |
164
- | `ctx.sample` | Request LLM completion from the client. **Check for presence first:** `if (ctx.sample) { ... }` |
165
+ | `ctx.elicit` | Ask user for structured input — form call `(message, schema)` or `.url(message, url)` for an external link. **Check for presence first:** `if (ctx.elicit) { ... }` |
165
166
  | `ctx.signal` | `AbortSignal` for cancellation. |
166
167
  | `ctx.progress` | Task progress (present when `task: true`) — `.setTotal(n)`, `.increment()`, `.update(message)`. |
167
168
  | `ctx.requestId` | Unique request ID. |
@@ -246,7 +247,7 @@ src/
246
247
 
247
248
  ## Skills
248
249
 
249
- 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.
250
+ 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. `bun run list-skills` prints the full registry.
250
251
 
251
252
  **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.
252
253
 
@@ -266,7 +267,6 @@ Available skills:
266
267
  | `tool-defs-analysis` | Read-only audit of MCP definition language across the surface — voice, leaks, defaults, recovery hints, output descriptions |
267
268
  | `security-pass` | Audit server for MCP-flavored security gaps: output injection, scope blast radius, input sinks, tenant isolation |
268
269
  | `code-simplifier` | Post-session cleanup against `git diff` — modernize syntax, consolidate duplication, align with the codebase |
269
- | `devcheck` | Lint, format, typecheck, audit |
270
270
  | `polish-docs-meta` | Finalize docs, README, metadata, and agent protocol for shipping |
271
271
  | `git-wrapup` | Land working-tree changes as a versioned commit + annotated tag — version bump, changelog, verify, tag. Local only. |
272
272
  | `release-and-publish` | Push + npm + MCP Registry + GH Release + Docker. Picks up from `git-wrapup` |
@@ -274,13 +274,14 @@ Available skills:
274
274
  | `orchestrations` | Chain task skills into a gated multi-phase pipeline — build-out, QA-fix, update-ship — when you can spawn sub-agents |
275
275
  | `report-issue-framework` | File a bug or feature request against `@cyanheads/mcp-ts-core` via `gh` CLI |
276
276
  | `report-issue-local` | File a bug or feature request against this server's own repo via `gh` CLI |
277
+ | `techniques` | Catalog of response/data-shaping techniques — overflow handling, payload shaping, retrieval patterns |
277
278
  | `api-auth` | Auth modes, scopes, JWT/OAuth |
278
279
  | `api-canvas` | DataCanvas: register tabular data, run SQL, export, plus the `spillover()` helper for big result sets — Tier 3 opt-in |
279
280
  | `api-config` | AppConfig, parseConfig, env vars |
280
281
  | `api-context` | Context interface, logger, state, progress |
281
282
  | `api-errors` | McpError, JsonRpcErrorCode, error patterns |
282
283
  | `api-linter` | Definition linter rule catalog — invoked by `bun run lint:mcp` and `devcheck` |
283
- | `api-mirror` | MirrorService: persistent, self-refreshing local mirror of a bulk upstream dataset (embedded SQLite + FTS5) |
284
+ | `api-mirror` | MirrorService: persistent self-refreshing local mirror (embedded SQLite + FTS5) of a bulk upstream dataset — Tier 3 opt-in |
284
285
  | `api-services` | LLM, Speech, Graph services |
285
286
  | `api-testing` | createMockContext, test patterns |
286
287
  | `api-utils` | Formatting, parsing, security, pagination, scheduling, telemetry helpers |
@@ -295,31 +296,34 @@ When you complete a skill's checklist, check the boxes and add a completion time
295
296
 
296
297
  ## Commands
297
298
 
298
- **Runtime:** Scripts use `tsx` — both `npm run <cmd>` and `bun run <cmd>` work. `bun` is slightly faster for script invocation but not required.
299
+ **Runtime:** Scripts use Bun's native TypeScript execution — `bun run <cmd>` is the standard invocation. `npm run <cmd>` also works (npm delegates to bun).
299
300
 
300
301
  | Command | Purpose |
301
302
  |:--------|:--------|
302
- | `npm run build` | Compile TypeScript |
303
- | `npm run rebuild` | Clean + build |
304
- | `npm run clean` | Remove build artifacts |
305
- | `npm run devcheck` | Lint + format + typecheck + security + changelog sync |
303
+ | `bun run build` | Compile TypeScript |
304
+ | `bun run rebuild` | Clean + build |
305
+ | `bun run clean` | Remove build artifacts |
306
+ | `bun run devcheck` | Lint + format + typecheck + security + changelog sync |
306
307
  | `bun run audit:refresh` | Delete `bun.lock`, reinstall, and re-run `bun audit`. Use when `devcheck` flags a transitive advisory — Bun's `update` is sticky on transitive resolutions, so the advisory may be a stale-lockfile false positive. If it survives the refresh, it's real. |
307
- | `npm run tree` | Generate directory structure doc |
308
- | `npm run format` | Auto-fix formatting (safe fixes only) |
309
- | `npm run format:unsafe` | Also apply Biome's unsafe autofixes — review the diff; they can change behavior |
310
- | `npm test` | Run tests |
311
- | `npm run start:stdio` | Production mode (stdio) |
312
- | `npm run start:http` | Production mode (HTTP) |
313
- | `npm run changelog:build` | Regenerate `CHANGELOG.md` from `changelog/*.md` |
314
- | `npm run changelog:check` | Verify `CHANGELOG.md` is in sync (used by devcheck) |
315
- | `npm run release:github` | Create GitHub Release from annotated tag (title format: `v<VERSION>: <subject>`) |
316
- | `npm run bundle` | Build and pack as `.mcpb` for one-click Claude Desktop install |
308
+ | `bun run lint:mcp` | Run the MCP definition linter standalone (rule catalog: `api-linter` skill) |
309
+ | `bun run lint:packaging` | Packaging surface checks — `server.json`/`manifest.json` env-var parity and plugin manifest identity (run by devcheck) |
310
+ | `bun run list-skills` | Print the skill registry |
311
+ | `bun run tree` | Generate directory structure doc |
312
+ | `bun run format` | Auto-fix formatting (safe fixes only) |
313
+ | `bun run format:unsafe` | Also apply Biome's unsafe autofixes — review the diff; they can change behavior |
314
+ | `bun run test` | Run tests (Vitest — use `bun run test`, not `bun test`) |
315
+ | `bun run start:stdio` | Production mode (stdio) |
316
+ | `bun run start:http` | Production mode (HTTP) |
317
+ | `bun run changelog:build` | Regenerate `CHANGELOG.md` from `changelog/*.md` |
318
+ | `bun run changelog:check` | Verify `CHANGELOG.md` is in sync (used by devcheck) |
319
+ | `bun run release:github` | Create GitHub Release from annotated tag (title format: `v<VERSION>: <subject>`) |
320
+ | `bun run bundle` | Build, pack, and clean a `.mcpb` for one-click Claude Desktop install |
317
321
 
318
322
  ---
319
323
 
320
324
  ## Bundling
321
325
 
322
- `npm run bundle` produces a `.mcpb` extension bundle for one-click install in Claude Desktop. MCPB is stdio-only — HTTP and Cloudflare Workers deployments are unaffected. Consumers who don't need it can delete `manifest.json` and `.mcpbignore`; `lint:packaging` skips cleanly.
326
+ `npm run bundle` produces a `.mcpb` extension bundle for one-click install in Claude Desktop. The pack step is followed by `scripts/clean-mcpb.ts`, which prunes dev dependencies (`mcpb clean`) and strips dependency-shipped agent docs (`node_modules/**` `skills/`, `.claude/`, `.agents/`, `SKILL.md`) that root-anchored `.mcpbignore` patterns cannot reach. MCPB is stdio-only — HTTP and Cloudflare Workers deployments are unaffected. Consumers who don't need it can delete `manifest.json` and `.mcpbignore`; `lint:packaging` skips cleanly.
323
327
 
324
328
  **Adding an env var requires both files:** `server.json` (registry discovery, `environmentVariables[]`) and `manifest.json` (bundle install UX, `mcp_config.env` + `user_config`). `lint:packaging` (run by `devcheck`) verifies the env var names match.
325
329
 
package/CLAUDE.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # Developer Protocol
2
2
 
3
3
  **Server:** cyanheads-mcp-server
4
- **Version:** 0.3.2
5
- **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.9.21`
4
+ **Version:** 0.4.0
5
+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.11.0`
6
6
  **Engines:** Bun ≥1.3.0, Node ≥24.0.0
7
7
  **MCP SDK:** `@modelcontextprotocol/sdk` ^1.29.0
8
8
  **Zod:** ^4.4.3
@@ -15,11 +15,13 @@
15
15
 
16
16
  A fleet discovery surface for the cyanheads MCP ecosystem. Two tools:
17
17
 
18
- - **`cyanheads_search`** — semantic search across fleet tools and servers using cosine similarity over Matryoshka-truncated 256-dim embeddings from `Snowflake/snowflake-arctic-embed-m-v1.5`.
19
- - **`cyanheads_describe`** — returns the description, connection URL, and per-client install snippet for any named tool or server.
18
+ - **`cyanheads_search_catalog`** — semantic search across fleet tools and servers using cosine similarity over Matryoshka-truncated 256-dim embeddings from `Snowflake/snowflake-arctic-embed-m-v1.5`.
19
+ - **`cyanheads_describe_entry`** — returns the description, connection URL, and per-client install snippet for any named tool or server.
20
20
 
21
21
  The catalog (`fleet.json`) is produced by `caseyjhand-portfolio/scripts/build-fleet-json.ts` at portfolio deploy time. Document vectors are baked into the JSON. The server only embeds the user's query at runtime via `@huggingface/transformers`. See `docs/design.md` for the full architecture.
22
22
 
23
+ The generated catalog does not include this server, so `src/services/catalog/self-record.ts` supplies a static `CatalogRecord` for `cyanheads-mcp-server` that `CatalogService.getServer()` / `getTool()` consult **only after** the remote index misses. A remote entry always wins, so the fallback self-heals to dead code if the generator ever starts emitting one. The record is never part of the vector index, so semantic search does not surface it — only exact-name resolution does.
24
+
23
25
  ---
24
26
 
25
27
  ## What's Next?
@@ -46,7 +48,7 @@ Tailor suggestions to what's actually missing or stale — don't recite the full
46
48
  - **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.
47
49
  - **Use `ctx.log`** for request-scoped logging. No `console` calls.
48
50
  - **Use `ctx.state`** for tenant-scoped storage. Never access persistence directly.
49
- - **Check `ctx.elicit` / `ctx.sample`** for presence before calling.
51
+ - **Check `ctx.elicit`** for presence before calling.
50
52
  - **Secrets in env vars only** — never hardcoded.
51
53
  - **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.
52
54
 
@@ -160,8 +162,7 @@ Handlers receive a unified `ctx` object. Key properties:
160
162
  |:---------|:------------|
161
163
  | `ctx.log` | Request-scoped logger — `.debug()`, `.info()`, `.notice()`, `.warning()`, `.error()`. Auto-correlates requestId, traceId, tenantId. |
162
164
  | `ctx.state` | Tenant-scoped KV — `.get(key)`, `.set(key, value, { ttl? })`, `.delete(key)`, `.list(prefix, { cursor, limit })`. Accepts any serializable value. |
163
- | `ctx.elicit` | Ask user for structured input. **Check for presence first:** `if (ctx.elicit) { ... }` |
164
- | `ctx.sample` | Request LLM completion from the client. **Check for presence first:** `if (ctx.sample) { ... }` |
165
+ | `ctx.elicit` | Ask user for structured input — form call `(message, schema)` or `.url(message, url)` for an external link. **Check for presence first:** `if (ctx.elicit) { ... }` |
165
166
  | `ctx.signal` | `AbortSignal` for cancellation. |
166
167
  | `ctx.progress` | Task progress (present when `task: true`) — `.setTotal(n)`, `.increment()`, `.update(message)`. |
167
168
  | `ctx.requestId` | Unique request ID. |
@@ -246,7 +247,7 @@ src/
246
247
 
247
248
  ## Skills
248
249
 
249
- 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.
250
+ 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. `bun run list-skills` prints the full registry.
250
251
 
251
252
  **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.
252
253
 
@@ -266,7 +267,6 @@ Available skills:
266
267
  | `tool-defs-analysis` | Read-only audit of MCP definition language across the surface — voice, leaks, defaults, recovery hints, output descriptions |
267
268
  | `security-pass` | Audit server for MCP-flavored security gaps: output injection, scope blast radius, input sinks, tenant isolation |
268
269
  | `code-simplifier` | Post-session cleanup against `git diff` — modernize syntax, consolidate duplication, align with the codebase |
269
- | `devcheck` | Lint, format, typecheck, audit |
270
270
  | `polish-docs-meta` | Finalize docs, README, metadata, and agent protocol for shipping |
271
271
  | `git-wrapup` | Land working-tree changes as a versioned commit + annotated tag — version bump, changelog, verify, tag. Local only. |
272
272
  | `release-and-publish` | Push + npm + MCP Registry + GH Release + Docker. Picks up from `git-wrapup` |
@@ -274,13 +274,14 @@ Available skills:
274
274
  | `orchestrations` | Chain task skills into a gated multi-phase pipeline — build-out, QA-fix, update-ship — when you can spawn sub-agents |
275
275
  | `report-issue-framework` | File a bug or feature request against `@cyanheads/mcp-ts-core` via `gh` CLI |
276
276
  | `report-issue-local` | File a bug or feature request against this server's own repo via `gh` CLI |
277
+ | `techniques` | Catalog of response/data-shaping techniques — overflow handling, payload shaping, retrieval patterns |
277
278
  | `api-auth` | Auth modes, scopes, JWT/OAuth |
278
279
  | `api-canvas` | DataCanvas: register tabular data, run SQL, export, plus the `spillover()` helper for big result sets — Tier 3 opt-in |
279
280
  | `api-config` | AppConfig, parseConfig, env vars |
280
281
  | `api-context` | Context interface, logger, state, progress |
281
282
  | `api-errors` | McpError, JsonRpcErrorCode, error patterns |
282
283
  | `api-linter` | Definition linter rule catalog — invoked by `bun run lint:mcp` and `devcheck` |
283
- | `api-mirror` | MirrorService: persistent, self-refreshing local mirror of a bulk upstream dataset (embedded SQLite + FTS5) |
284
+ | `api-mirror` | MirrorService: persistent self-refreshing local mirror (embedded SQLite + FTS5) of a bulk upstream dataset — Tier 3 opt-in |
284
285
  | `api-services` | LLM, Speech, Graph services |
285
286
  | `api-testing` | createMockContext, test patterns |
286
287
  | `api-utils` | Formatting, parsing, security, pagination, scheduling, telemetry helpers |
@@ -295,31 +296,34 @@ When you complete a skill's checklist, check the boxes and add a completion time
295
296
 
296
297
  ## Commands
297
298
 
298
- **Runtime:** Scripts use `tsx` — both `npm run <cmd>` and `bun run <cmd>` work. `bun` is slightly faster for script invocation but not required.
299
+ **Runtime:** Scripts use Bun's native TypeScript execution — `bun run <cmd>` is the standard invocation. `npm run <cmd>` also works (npm delegates to bun).
299
300
 
300
301
  | Command | Purpose |
301
302
  |:--------|:--------|
302
- | `npm run build` | Compile TypeScript |
303
- | `npm run rebuild` | Clean + build |
304
- | `npm run clean` | Remove build artifacts |
305
- | `npm run devcheck` | Lint + format + typecheck + security + changelog sync |
303
+ | `bun run build` | Compile TypeScript |
304
+ | `bun run rebuild` | Clean + build |
305
+ | `bun run clean` | Remove build artifacts |
306
+ | `bun run devcheck` | Lint + format + typecheck + security + changelog sync |
306
307
  | `bun run audit:refresh` | Delete `bun.lock`, reinstall, and re-run `bun audit`. Use when `devcheck` flags a transitive advisory — Bun's `update` is sticky on transitive resolutions, so the advisory may be a stale-lockfile false positive. If it survives the refresh, it's real. |
307
- | `npm run tree` | Generate directory structure doc |
308
- | `npm run format` | Auto-fix formatting (safe fixes only) |
309
- | `npm run format:unsafe` | Also apply Biome's unsafe autofixes — review the diff; they can change behavior |
310
- | `npm test` | Run tests |
311
- | `npm run start:stdio` | Production mode (stdio) |
312
- | `npm run start:http` | Production mode (HTTP) |
313
- | `npm run changelog:build` | Regenerate `CHANGELOG.md` from `changelog/*.md` |
314
- | `npm run changelog:check` | Verify `CHANGELOG.md` is in sync (used by devcheck) |
315
- | `npm run release:github` | Create GitHub Release from annotated tag (title format: `v<VERSION>: <subject>`) |
316
- | `npm run bundle` | Build and pack as `.mcpb` for one-click Claude Desktop install |
308
+ | `bun run lint:mcp` | Run the MCP definition linter standalone (rule catalog: `api-linter` skill) |
309
+ | `bun run lint:packaging` | Packaging surface checks — `server.json`/`manifest.json` env-var parity and plugin manifest identity (run by devcheck) |
310
+ | `bun run list-skills` | Print the skill registry |
311
+ | `bun run tree` | Generate directory structure doc |
312
+ | `bun run format` | Auto-fix formatting (safe fixes only) |
313
+ | `bun run format:unsafe` | Also apply Biome's unsafe autofixes — review the diff; they can change behavior |
314
+ | `bun run test` | Run tests (Vitest — use `bun run test`, not `bun test`) |
315
+ | `bun run start:stdio` | Production mode (stdio) |
316
+ | `bun run start:http` | Production mode (HTTP) |
317
+ | `bun run changelog:build` | Regenerate `CHANGELOG.md` from `changelog/*.md` |
318
+ | `bun run changelog:check` | Verify `CHANGELOG.md` is in sync (used by devcheck) |
319
+ | `bun run release:github` | Create GitHub Release from annotated tag (title format: `v<VERSION>: <subject>`) |
320
+ | `bun run bundle` | Build, pack, and clean a `.mcpb` for one-click Claude Desktop install |
317
321
 
318
322
  ---
319
323
 
320
324
  ## Bundling
321
325
 
322
- `npm run bundle` produces a `.mcpb` extension bundle for one-click install in Claude Desktop. MCPB is stdio-only — HTTP and Cloudflare Workers deployments are unaffected. Consumers who don't need it can delete `manifest.json` and `.mcpbignore`; `lint:packaging` skips cleanly.
326
+ `npm run bundle` produces a `.mcpb` extension bundle for one-click install in Claude Desktop. The pack step is followed by `scripts/clean-mcpb.ts`, which prunes dev dependencies (`mcpb clean`) and strips dependency-shipped agent docs (`node_modules/**` `skills/`, `.claude/`, `.agents/`, `SKILL.md`) that root-anchored `.mcpbignore` patterns cannot reach. MCPB is stdio-only — HTTP and Cloudflare Workers deployments are unaffected. Consumers who don't need it can delete `manifest.json` and `.mcpbignore`; `lint:packaging` skips cleanly.
323
327
 
324
328
  **Adding an env var requires both files:** `server.json` (registry discovery, `environmentVariables[]`) and `manifest.json` (bundle install UX, `mcp_config.env` + `user_config`). `lint:packaging` (run by `devcheck`) verifies the env var names match.
325
329
 
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.3 AS build
7
+ FROM oven/bun:1.3.14 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
- RUN bun install --frozen-lockfile
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 . .
@@ -29,7 +31,7 @@ RUN bun scripts/build.ts
29
31
  # application. It uses a slim base image and only includes production
30
32
  # dependencies and build artifacts.
31
33
  # ==============================================================================
32
- FROM oven/bun:1.3-slim AS production
34
+ FROM oven/bun:1.3.14-slim AS production
33
35
 
34
36
  WORKDIR /usr/src/app
35
37
 
@@ -38,9 +40,11 @@ WORKDIR /usr/src/app
38
40
  ENV NODE_ENV=production
39
41
 
40
42
  # OCI image metadata (https://github.com/opencontainers/image-spec/blob/main/annotations.md)
43
+ ARG APP_VERSION
41
44
  LABEL org.opencontainers.image.title="cyanheads-mcp-server"
42
45
  LABEL org.opencontainers.image.description="Fleet discovery server for the cyanheads MCP ecosystem."
43
46
  LABEL org.opencontainers.image.licenses="Apache-2.0"
47
+ LABEL org.opencontainers.image.version="${APP_VERSION}"
44
48
  LABEL org.opencontainers.image.source="https://github.com/cyanheads/cyanheads-mcp-server"
45
49
 
46
50
  # Copy dependency manifests
@@ -48,14 +52,16 @@ COPY package.json bun.lock ./
48
52
 
49
53
  # Install only production dependencies, ignoring any lifecycle scripts (like 'prepare')
50
54
  # that are not needed in the final production image.
51
- RUN bun install --production --frozen-lockfile --ignore-scripts
55
+ RUN --mount=type=cache,target=/root/.bun/install/cache \
56
+ bun install --production --frozen-lockfile --ignore-scripts
52
57
 
53
58
  # Conditionally install OpenTelemetry optional peer dependencies (Tier 3).
54
59
  # These are not bundled by default to keep the base image lean. Enable at build time
55
60
  # with: docker build --build-arg OTEL_ENABLED=true
56
61
  ARG OTEL_ENABLED=true
57
- RUN if [ "$OTEL_ENABLED" = "true" ]; then \
58
- bun add @hono/otel \
62
+ RUN --mount=type=cache,target=/root/.bun/install/cache \
63
+ if [ "$OTEL_ENABLED" = "true" ]; then \
64
+ bun add --omit=dev --ignore-scripts @hono/otel \
59
65
  @opentelemetry/instrumentation-http \
60
66
  @opentelemetry/exporter-metrics-otlp-http \
61
67
  @opentelemetry/exporter-trace-otlp-http \
@@ -96,5 +102,8 @@ ENV MCP_FORCE_CONSOLE_LOGGING="true"
96
102
  # Expose the port the server listens on
97
103
  EXPOSE ${MCP_HTTP_PORT}
98
104
 
105
+ # Health check using a bun-native fetch (slim image ships no curl/wget)
106
+ HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 CMD bun -e "fetch('http://localhost:'+(process.env.MCP_HTTP_PORT??'3010')+'/healthz').then((r)=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"
107
+
99
108
  # The command to start the server
100
109
  CMD ["bun", "run", "dist/index.js"]
package/README.md CHANGED
@@ -1,13 +1,13 @@
1
1
  <div align="center">
2
2
  <h1>@cyanheads/cyanheads-mcp-server</h1>
3
- <p><b>Discover MCP servers via semantic search. One endpoint, 40+ hosted servers, per-client install snippets. STDIO or Streamable HTTP.</b>
3
+ <p><b>Fleet discovery for the cyanheads MCP ecosystem — semantic search + install snippets.</b>
4
4
  <div>2 Tools • 0 Resources • 0 Prompts</div>
5
5
  </p>
6
6
  </div>
7
7
 
8
8
  <div align="center">
9
9
 
10
- [![Version](https://img.shields.io/badge/Version-0.3.2-blue.svg?style=flat-square)](./CHANGELOG.md) [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE) [![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/cyanheads-mcp-server) [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-^1.29.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/) [![npm](https://img.shields.io/npm/v/@cyanheads/cyanheads-mcp-server?style=flat-square&logo=npm&logoColor=white)](https://www.npmjs.com/package/@cyanheads/cyanheads-mcp-server) [![TypeScript](https://img.shields.io/badge/TypeScript-^6.0.3-3178C6.svg?style=flat-square)](https://www.typescriptlang.org/) [![Bun](https://img.shields.io/badge/Bun-%3E=1.3.0-blueviolet.svg?style=flat-square)](https://bun.sh/)
10
+ [![Version](https://img.shields.io/badge/Version-0.4.0-blue.svg?style=flat-square)](./CHANGELOG.md) [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE) [![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/cyanheads-mcp-server) [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-^1.29.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/) [![npm](https://img.shields.io/npm/v/@cyanheads/cyanheads-mcp-server?style=flat-square&logo=npm&logoColor=white)](https://www.npmjs.com/package/@cyanheads/cyanheads-mcp-server) [![TypeScript](https://img.shields.io/badge/TypeScript-^7.0.2-3178C6.svg?style=flat-square)](https://www.typescriptlang.org/) [![Bun](https://img.shields.io/badge/Bun-%3E=1.3.0-blueviolet.svg?style=flat-square)](https://bun.sh/)
11
11
 
12
12
  </div>
13
13
 
@@ -33,24 +33,25 @@ Two tools, semantic ranking, hosted catalog. The catalog itself lives at [`casey
33
33
 
34
34
  | Tool | Description |
35
35
  |:---|:---|
36
- | `cyanheads_search` | Search fleet tools and servers by natural-language query. Returns ranked matches with brief summaries and the owning server. |
37
- | `cyanheads_describe` | Return the connection URL and per-client install snippets for a named tool or server. |
36
+ | `cyanheads_search_catalog` | Search fleet tools and servers by natural-language query. Returns ranked matches with brief summaries and the owning server. |
37
+ | `cyanheads_describe_entry` | Return the connection URL and per-client install snippets for a named tool or server. |
38
38
 
39
- ### `cyanheads_search`
39
+ ### `cyanheads_search_catalog`
40
40
 
41
41
  Semantic search across the fleet. Embeds the query with [Snowflake Arctic Embed M v1.5](https://huggingface.co/Snowflake/snowflake-arctic-embed-m-v1.5) (Matryoshka-truncated to 256 dimensions) and computes cosine similarity against the catalog's pre-computed document vectors.
42
42
 
43
+ - `query` accepts 1-500 characters
43
44
  - `scope: "tools"` (default) returns individual tool matches; `scope: "servers"` returns server-level matches
44
45
  - `category` filter narrows to one of `research`, `government`, `public-data`, `utility`
45
46
  - Configurable `limit` (1-20, default 5) and a server-side `SIMILARITY_FLOOR` threshold drop low-confidence hits
46
47
  - Returns `score` (cosine similarity in [0, 1]) on every result for trust calibration
47
- - `totalMatched` reports the count above the floor before the limit was applied
48
+ - `totalCount` reports the count above the floor before the limit was applied
48
49
 
49
50
  ---
50
51
 
51
- ### `cyanheads_describe`
52
+ ### `cyanheads_describe_entry`
52
53
 
53
- Resolve a name to its install instructions. Accepts either a tool name (snake_case, e.g. `earthquake_search`) or a server name (kebab-case, e.g. `earthquake-mcp-server`) — auto-detected from the format, or pinned via the `kind` parameter.
54
+ Resolve a name to its install instructions. Accepts either a tool name (snake_case, e.g. `earthquake_search`) or a server name (kebab-case, e.g. `earthquake-mcp-server`), up to 64 characters — auto-detected from the format, or pinned via the `kind` parameter.
54
55
 
55
56
  - For tools: returns the description and the owning server name
56
57
  - For servers: returns description, version, npm package, GitHub URL, the full tool list (each tool's name and description), and per-client install snippets — **local (stdio, via `npx`) for every server, plus remote (Streamable HTTP) when a hosted endpoint exists**
@@ -77,7 +78,7 @@ Fleet-specific:
77
78
 
78
79
  ## What's in the fleet
79
80
 
80
- 40+ MCP servers spanning four categories. Each is open source and individually addressable — `cyanheads_describe` returns its direct connection URL alongside the install snippet.
81
+ 100+ MCP servers spanning four categories. Each is open source and individually addressable — `cyanheads_describe_entry` returns its direct connection URL alongside the install snippet. It also describes this server: ask for `cyanheads-mcp-server` and you get the front door's own endpoint and install snippets.
81
82
 
82
83
  | Category | Examples |
83
84
  |:--|:--|
@@ -204,11 +205,11 @@ All configuration is validated at startup via Zod schemas in `src/config/server-
204
205
  | `MCP_HTTP_ENDPOINT_PATH` | HTTP endpoint path where the MCP server is mounted | `/mcp` |
205
206
  | `MCP_AUTH_MODE` | Authentication: `none`, `jwt`, or `oauth` | `none` |
206
207
  | `MCP_LOG_LEVEL` | Log level (`debug`, `info`, `warning`, `error`, etc.) | `info` |
207
- | `CATALOG_URL` | Remote fleet.json endpoint (schema v2 with baked embeddings). Override to front your own fleet. | `https://caseyjhand.com/fleet.json` |
208
- | `CATALOG_FETCH_TIMEOUT_MS` | Per-request timeout for fleet.json fetches in ms | `10000` |
209
- | `CATALOG_REFRESH_SECONDS` | Background poll interval for fleet.json refresh. `0` disables. | `3600` |
208
+ | `CATALOG_URL` | Remote fleet.json endpoint (schema v2 with baked embeddings). Must be an absolute URL. Override to front your own fleet. | `https://caseyjhand.com/fleet.json` |
209
+ | `CATALOG_FETCH_TIMEOUT_MS` | Per-request timeout for fleet.json fetches in ms. Must be > 0. | `10000` |
210
+ | `CATALOG_REFRESH_SECONDS` | Background poll interval for fleet.json refresh. `0` disables; otherwise must be > 0. | `3600` |
210
211
  | `EMBEDDING_MODEL_ID` | Hugging Face model id for query embedding. Must match `fleet.json.embeddingModel`. | `Snowflake/snowflake-arctic-embed-m-v1.5` |
211
- | `SIMILARITY_FLOOR` | Cosine similarity cutoff for `cyanheads_search` results (0-1) | `0.3` |
212
+ | `SIMILARITY_FLOOR` | Cosine similarity cutoff for `cyanheads_search_catalog` results. Must be within `[0, 1]`. | `0.3` |
212
213
  | `OTEL_ENABLED` | Enable OpenTelemetry | `false` |
213
214
 
214
215
  To point at a different catalog, change `CATALOG_URL` to your own hosted JSON file. See [`docs/design.md`](./docs/design.md) for the producer-side script and schema.
@@ -239,9 +240,8 @@ To point at a different catalog, change `CATALOG_URL` to your own hosted JSON fi
239
240
 
240
241
  | Directory | Purpose |
241
242
  |:---|:---|
242
- | `src/mcp-server/tools` | Tool definitions (`*.tool.ts`). Two tools — `cyanheads_search` and `cyanheads_describe`. |
243
- | `src/services/catalog` | Catalog service — remote fleet.json provider with atomic-swap refresh, vector index, snippet builders. |
244
- | `src/services/embeddings` | Embedding runtime — query-time inference via `@huggingface/transformers`, with an injectable interface for deterministic tests. |
243
+ | `src/mcp-server/tools` | Tool definitions (`*.tool.ts`). Two tools — `cyanheads_search_catalog` and `cyanheads_describe_entry`. |
244
+ | `src/services/catalog` | Catalog service — remote fleet.json provider with atomic-swap refresh, vector index, snippet builders, the self-description fallback record, and the query-time embedding runtime (`@huggingface/transformers`, behind an injectable interface for deterministic tests). |
245
245
  | `src/config` | Server-specific environment variable parsing and validation with Zod. |
246
246
  | `tests/` | Unit and integration tests, mirroring the `src/` structure. |
247
247
  | `docs/` | Design doc and schema reference. |
@@ -252,7 +252,7 @@ See [`CLAUDE.md`](./CLAUDE.md) for development guidelines and architectural rule
252
252
 
253
253
  - Handlers throw, framework catches — no `try/catch` in tool logic
254
254
  - Use `ctx.log` for logging, `ctx.state` for storage
255
- - Register new tools in `src/mcp-server/tools/definitions/index.ts`
255
+ - Register new tools in the `tools` array passed to `createApp()` in `src/index.ts`
256
256
 
257
257
  ## Contributing
258
258
 
@@ -0,0 +1,24 @@
1
+ ---
2
+ summary: "Adopt @cyanheads/mcp-ts-core ^0.10.6 — server name/title identity, Docker HEALTHCHECK + version label, bundle cleaner, tsx → bun run script migration."
3
+ breaking: false
4
+ security: false
5
+ ---
6
+
7
+ # 0.3.3 — 2026-06-11
8
+
9
+ ## Added
10
+
11
+ - **Server identity pair** — `createApp()` now sets an explicit `name` / `title` of `cyanheads-mcp-server`, so the hyphenated machine name is the display identity on every surface ([#231](https://github.com/cyanheads/mcp-ts-core/issues/231)).
12
+ - **`stdio` / `streamable-http` keywords** added to `package.json` to surface both supported transports in registry search.
13
+ - **`Dockerfile` `HEALTHCHECK`** — bun-native `fetch` against `/healthz` every 30s (slim image ships no `curl`/`wget`), plus an `APP_VERSION` build arg that stamps `org.opencontainers.image.version` ([#217](https://github.com/cyanheads/mcp-ts-core/issues/217)).
14
+ - **`scripts/clean-mcpb.ts`** — post-pack bundle cleaner wired into `bundle`; runs `mcpb clean` then strips dependency-shipped agent docs (`node_modules/**` `skills/`, `.claude/`, `.agents/`, `SKILL.md`) that root-anchored `.mcpbignore` patterns cannot reach ([#230](https://github.com/cyanheads/mcp-ts-core/issues/230)).
15
+ - **`lint-packaging` checks 8–9** — post-bundle content guard (zero `node_modules/**` agent-doc entries) and entrypoint/manifest identity check (`name`/`title`/`display_name` must equal the unscoped package name) ([#230](https://github.com/cyanheads/mcp-ts-core/issues/230), [#231](https://github.com/cyanheads/mcp-ts-core/issues/231)).
16
+ - **`coerce-boolean-env-flag` antipattern rule** in `check-framework-antipatterns.ts` — flags `z.coerce.boolean()` on env flags (`Boolean("false") === true`), recommending `z.stringbool()`; the scanner now skips comment lines ([#201](https://github.com/cyanheads/mcp-ts-core/issues/201)).
17
+ - **`techniques` skill** — catalog of reusable response/data-shaping patterns; first reference documents outline-on-overflow ([#204](https://github.com/cyanheads/mcp-ts-core/issues/204)).
18
+
19
+ ## Changed
20
+
21
+ - **`cyanheads_describe`** — the `ambiguous_kind` error code moved from `InvalidParams` to `ValidationError`; a name colliding across catalog kinds is an application-level validation failure, not a malformed-params protocol error.
22
+ - **Build scripts run under Bun** — every `package.json` script switched from `tsx scripts/*.ts` to `bun run scripts/*.ts`, and `tsx` was dropped from devDependencies ([#205](https://github.com/cyanheads/mcp-ts-core/issues/205)).
23
+ - **`.mcpbignore` dev-dir patterns root-anchored** — leading `/` added to `/.claude/`, `/.agents/`, `/Dockerfile`, etc. so they match only at bundle root and can't strip nested `node_modules` runtime paths ([#207](https://github.com/cyanheads/mcp-ts-core/issues/207)).
24
+ - Dependencies: `@cyanheads/mcp-ts-core` ^0.9.21 → ^0.10.6, `@types/node` ^25.9.1 → ^25.9.3.
@@ -0,0 +1,33 @@
1
+ ---
2
+ summary: "Fix unbounded config env vars (#8); adopt @cyanheads/mcp-ts-core ^0.11.0 with a supply-chain install guard, floating-specifier and plugin-manifest lint checks, and native-binding bundle stripping."
3
+ breaking: false
4
+ security: false
5
+ ---
6
+
7
+ # 0.3.4 — 2026-07-26
8
+
9
+ ## Fixed
10
+
11
+ - **`server-config.ts` bounds enforcement** — the Zod schema now rejects values outside the bounds it documents: `SIMILARITY_FLOOR` constrained to `[0, 1]`, `CATALOG_FETCH_TIMEOUT_MS` must be positive, `CATALOG_REFRESH_SECONDS` must be `0` or positive, `CATALOG_URL` must be an absolute URL (`z.url()`), and `EMBEDDING_MODEL_ID` must be non-empty. Previously all five accepted any coerced value, including negative timeouts and an out-of-range similarity floor ([#8](https://github.com/cyanheads/cyanheads-mcp-server/issues/8)).
12
+ - New `tests/config/server-config.test.ts` covers the documented defaults and the accept/reject boundary for each of the five env vars.
13
+
14
+ ## Changed
15
+
16
+ - **`@cyanheads/mcp-ts-core`** `^0.10.6` → `^0.11.0`; `typescript` `^6.0.3` → `^7.0.2`; `@biomejs/biome` `^2.4.16` → `^2.5.5`; `@types/node` `^25.9.3` → `^26.1.1`; `tsc-alias` `^1.8.17` → `^1.9.1`; `vitest` `^4.1.8` → `^4.1.10`; `ignore` `^7.0.5` → `^7.0.6`.
17
+ - **`package.json` `author`** — canonicalized to `Casey Hand <casey@caseyjhand.com> (https://caseyjhand.com)`; `packageManager` field pinned to `bun@1.3.14`.
18
+ - **`bunfig.toml` supply-chain guard** — `[install].minimumReleaseAge` set to 259200s (3 days), blocking newly-published package versions from install; `@cyanheads/mcp-ts-core` is excluded via `minimumReleaseAgeExcludes` so framework releases adopt same-day. `[install.security].scanner` set to `@socketsecurity/bun-security-scanner`.
19
+ - **`Dockerfile`** — base image pinned to `oven/bun:1.3.14` (both stages); `bun install` steps now use a BuildKit cache mount (`--mount=type=cache,target=/root/.bun/install/cache`); the build-stage install adds `--ignore-scripts`; the OTEL peer-dependency install adds `--omit=dev --ignore-scripts`.
20
+ - **`devcheck`'s Outdated check** no longer flags a package as a finding when `Update === Current` — covers versions held back by the new `minimumReleaseAge` guard or a deliberately capped range, and stops flagging `peerDependency` rows (a peer range states a floor, not a version to track).
21
+ - **`devcheck`'s git-dependent checks** (TODOs/FIXMEs, Tracked Secrets, Framework Antipatterns) skip cleanly instead of failing when `.git` is absent (fresh scaffold before `git init`).
22
+ - **`lint-mcp.ts`** passes `packageJson` to the linter only when present, instead of always.
23
+ - **`scripts/clean-mcpb.ts`** / **`lint-packaging.ts` check 8** now also strip and verify zero `node_modules/@duckdb/node-bindings-*/` entries in a built `.mcpb` — a native binding otherwise locks the bundle to the build host's platform and blows past the 25 MB registry cap; `NATIVE_BINDING_ENTRY` is kept identical across both files (unit-tested).
24
+ - **`.gitignore`** — `data/` anchored to `/data/` (was matching `data/` at any depth).
25
+
26
+ ## Added
27
+
28
+ - **`scripts/check-dependency-specifiers.ts`** (new `devcheck` step, "Dependency Specifiers") — rejects floating specifiers (`latest`, `*`, `next`/`beta`/`canary`/`rc`) in `package.json`'s dependency sections and `bun.lock`'s `workspaces` maps; `overrides` is held to the strictest rule since a floating override silently re-resolves the vulnerable transitive it was meant to pin. `peerDependencies`/`optionalDependencies` allow `*`/dist-tags but never `latest`.
29
+ - **`lint-packaging.ts` check 10** — validates the new plugin marketplace manifests (`.claude-plugin/plugin.json`, `.codex-plugin/plugin.json`, `.codex-plugin/mcp.json`): non-empty descriptions, and that display fields (`name`, server key, `interface.displayName`) carry the unscoped machine name while the `npx -y` install arg carries the full (possibly scoped) `package.json` name. Gated by `devcheck.config.json` `packaging.pluginManifests` (default on).
30
+ - **`.claude-plugin/plugin.json`**, **`.codex-plugin/plugin.json`**, **`.codex-plugin/mcp.json`** — plugin marketplace manifests for Claude Code and Codex install flows.
31
+ - **`.gitattributes`** — normalizes line endings to LF, marks binary asset extensions, and flags `bun.lock`/`CHANGELOG.md`/`docs/tree.md` as `linguist-generated`.
32
+ - **`.github/SECURITY.md`** — vulnerability reporting policy (GitHub Security tab or `casey@caseyjhand.com`).
33
+ - **`.github/FUNDING.yml`** — GitHub Sponsors / Buy Me a Coffee funding links.
@@ -0,0 +1,21 @@
1
+ ---
2
+ summary: "Breaking tool rename (cyanheads_search_catalog / cyanheads_describe_entry), input-length bounds, and a self-description fallback for the front door's own server entry"
3
+ breaking: true
4
+ security: false
5
+ ---
6
+
7
+ # 0.4.0 — 2026-07-26
8
+
9
+ ## Added
10
+
11
+ - **Self-description fallback.** `cyanheads_describe_entry` now resolves `cyanheads-mcp-server` and its own two tools even though the generated fleet catalog excludes the discovery front door. `src/services/catalog/self-record.ts` supplies a static `CatalogRecord` built from this package's own `package.json` (version, description) and its registered tool definitions; `CatalogService.getServer()` / `getTool()` consult it only after a remote-catalog lookup misses, so a future catalog entry for this server takes precedence automatically. The record is excluded from the vector index, so `cyanheads_search_catalog` does not surface it — only exact-name resolution does. ([#7](https://github.com/cyanheads/cyanheads-mcp-server/issues/7))
12
+
13
+ ## Changed
14
+
15
+ - **Breaking: both public tools renamed.** `cyanheads_search` → `cyanheads_search_catalog`; `cyanheads_describe` → `cyanheads_describe_entry`. No compatibility aliases — the old names are gone from `tools/list`. Inputs, outputs, errors, and runtime behavior are unchanged. Clients that call either old name directly must switch to the new name; clients that discover tools dynamically pick up the new names automatically on next connect. ([#10](https://github.com/cyanheads/cyanheads-mcp-server/issues/10))
16
+ - **Input length bounds.** `cyanheads_search_catalog.query` is now capped at 500 characters; `cyanheads_describe_entry.name` at 64. Both previously accepted unbounded strings, letting oversized input reach the local embedding model before domain logic could reject it. ([#9](https://github.com/cyanheads/cyanheads-mcp-server/issues/9))
17
+ - **`CatalogService` accessor moved.** `getCatalogService()` now lives in the new leaf module `src/services/catalog/service-instance.ts` instead of `catalog-service.ts`, breaking an import cycle between the tool definitions and the self-record (which derives its tool list from those same definitions). `initCatalogService()` stays in `catalog-service.ts` and installs the instance via the new module's `setCatalogService()`. `shutdown()` is promoted from `CatalogService` onto the `ICatalogService` interface.
18
+
19
+ ## Fixed
20
+
21
+ - **`docs/design.md` reconciled against current code.** The search-tool output schema, error contract, and `format()` parity note were stale from before the servers roll-up and enrichment-block rework; the `ICatalogService` sketch was missing `shutdown()`. All brought back in sync with the current definition files. ([#11](https://github.com/cyanheads/cyanheads-mcp-server/issues/11))
@@ -15,9 +15,11 @@ summary: ""
15
15
  # usage. Flagged as `Breaking` in the rollup.
16
16
  breaking: false
17
17
 
18
- # Set `true` if this release contains any security fix. Pairs with the
19
- # `## Security` section below. Flagged as `Security` in the rollup so
20
- # users can triage upgrade urgency at a glance.
18
+ # Set `true` ONLY for a security fix in THIS project's own source code — a
19
+ # vulnerability or hardening in code you ship. A dependency or transitive CVE
20
+ # bump is routine maintenance, NOT a security release: record it under
21
+ # `## Dependencies` (with the advisory ID) and leave this `false`. When true,
22
+ # pairs with the `## Security` section below and flags `Security` in the rollup.
21
23
  security: false
22
24
 
23
25
  # Optional free-form notes for maintenance agents processing this release.
@@ -5,7 +5,7 @@
5
5
  */
6
6
  import { z } from '@cyanheads/mcp-ts-core';
7
7
  declare const ServerConfigSchema: z.ZodObject<{
8
- catalogUrl: z.ZodDefault<z.ZodString>;
8
+ catalogUrl: z.ZodDefault<z.ZodURL>;
9
9
  catalogFetchTimeoutMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
10
10
  catalogRefreshSeconds: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
11
11
  embeddingModelId: z.ZodDefault<z.ZodString>;
@@ -1 +1 @@
1
- {"version":3,"file":"server-config.d.ts","sourceRoot":"","sources":["../../src/config/server-config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAG3C,QAAA,MAAM,kBAAkB;;;;;;iBAgDtB,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAI9D,wBAAgB,eAAe,IAAI,YAAY,CAS9C;AAED,uCAAuC;AACvC,wBAAgB,iBAAiB,IAAI,IAAI,CAExC"}
1
+ {"version":3,"file":"server-config.d.ts","sourceRoot":"","sources":["../../src/config/server-config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAG3C,QAAA,MAAM,kBAAkB;;;;;;iBAyDtB,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAI9D,wBAAgB,eAAe,IAAI,YAAY,CAS9C;AAED,uCAAuC;AACvC,wBAAgB,iBAAiB,IAAI,IAAI,CAExC"}
@@ -11,25 +11,29 @@ const ServerConfigSchema = z.object({
11
11
  * Loaded at server startup via RemoteJsonCatalogProvider.
12
12
  */
13
13
  catalogUrl: z
14
- .string()
14
+ .url()
15
15
  .default('https://caseyjhand.com/fleet.json')
16
16
  .describe('URL of the remote fleet.json catalog endpoint.'),
17
17
  /**
18
- * Timeout in milliseconds for the remote catalog fetch.
18
+ * Timeout in milliseconds for the remote catalog fetch. Must be a positive,
19
+ * finite duration — a zero or negative timeout aborts every fetch before it
20
+ * can complete. `z.coerce.number()` already rejects NaN and Infinity.
19
21
  */
20
22
  catalogFetchTimeoutMs: z.coerce
21
23
  .number()
24
+ .positive()
22
25
  .default(10000)
23
- .describe('HTTP timeout for remote catalog fetch in milliseconds.'),
26
+ .describe('HTTP timeout for remote catalog fetch in milliseconds. Must be > 0.'),
24
27
  /**
25
28
  * Background poll interval. The service re-fetches fleet.json on this cadence
26
29
  * and swaps in the new vector index if `generatedAt` changed.
27
- * Set to 0 to disable background refresh.
30
+ * Set to 0 to disable background refresh; any other value must be positive.
28
31
  */
29
32
  catalogRefreshSeconds: z.coerce
30
33
  .number()
34
+ .nonnegative()
31
35
  .default(3600)
32
- .describe('Seconds between background catalog re-fetches. 0 disables background refresh.'),
36
+ .describe('Seconds between background catalog re-fetches. 0 disables background refresh; otherwise must be > 0.'),
33
37
  /**
34
38
  * Embedding model identifier. Must match `embeddingModel` declared in fleet.json,
35
39
  * or the server refuses to load (since vectors live in the same space as the
@@ -37,14 +41,17 @@ const ServerConfigSchema = z.object({
37
41
  */
38
42
  embeddingModelId: z
39
43
  .string()
44
+ .min(1)
40
45
  .default('Snowflake/snowflake-arctic-embed-m-v1.5')
41
46
  .describe('Hugging Face model id used for query embedding at search time. Must match fleet.json.embeddingModel.'),
42
47
  /**
43
- * Minimum cosine similarity for a result to surface in cyanheads_search output.
48
+ * Minimum cosine similarity for a result to surface in cyanheads_search_catalog output.
44
49
  * Tunable post-launch based on observed query patterns.
45
50
  */
46
51
  similarityFloor: z.coerce
47
52
  .number()
53
+ .min(0)
54
+ .max(1)
48
55
  .default(0.3)
49
56
  .describe('Minimum cosine similarity in [0, 1] for a match to appear in search results.'),
50
57
  });