@cyanheads/mcp-ts-core 0.10.6 → 0.10.8

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 (52) hide show
  1. package/AGENTS.md +1 -1
  2. package/CLAUDE.md +1 -1
  3. package/README.md +1 -1
  4. package/biome.json +2 -2
  5. package/changelog/0.10.x/0.10.6.md +2 -2
  6. package/changelog/0.10.x/0.10.7.md +49 -0
  7. package/changelog/0.10.x/0.10.8.md +19 -0
  8. package/dist/core/app.d.ts +6 -1
  9. package/dist/core/app.d.ts.map +1 -1
  10. package/dist/core/app.js.map +1 -1
  11. package/dist/core/context.d.ts +63 -1
  12. package/dist/core/context.d.ts.map +1 -1
  13. package/dist/core/context.js +56 -0
  14. package/dist/core/context.js.map +1 -1
  15. package/dist/core/index.d.ts +1 -1
  16. package/dist/core/index.d.ts.map +1 -1
  17. package/dist/core/index.js.map +1 -1
  18. package/dist/logs/combined.log +8 -8
  19. package/dist/logs/error.log +4 -4
  20. package/dist/mcp-server/tools/utils/toolHandlerFactory.d.ts.map +1 -1
  21. package/dist/mcp-server/tools/utils/toolHandlerFactory.js +9 -1
  22. package/dist/mcp-server/tools/utils/toolHandlerFactory.js.map +1 -1
  23. package/dist/services/canvas/core/sqlGate.d.ts +2 -0
  24. package/dist/services/canvas/core/sqlGate.d.ts.map +1 -1
  25. package/dist/services/canvas/core/sqlGate.js +2 -0
  26. package/dist/services/canvas/core/sqlGate.js.map +1 -1
  27. package/dist/services/canvas/providers/duckdb/DuckdbProvider.d.ts.map +1 -1
  28. package/dist/services/canvas/providers/duckdb/DuckdbProvider.js +48 -4
  29. package/dist/services/canvas/providers/duckdb/DuckdbProvider.js.map +1 -1
  30. package/dist/testing/index.d.ts +15 -1
  31. package/dist/testing/index.d.ts.map +1 -1
  32. package/dist/testing/index.js +22 -1
  33. package/dist/testing/index.js.map +1 -1
  34. package/package.json +20 -27
  35. package/scripts/build-changelog.ts +9 -0
  36. package/scripts/check-framework-antipatterns.ts +10 -0
  37. package/scripts/check-skill-versions.ts +1 -0
  38. package/scripts/devcheck.ts +30 -13
  39. package/skills/add-tool/SKILL.md +15 -1
  40. package/skills/api-auth/SKILL.md +1 -1
  41. package/skills/api-canvas/SKILL.md +3 -1
  42. package/skills/api-config/SKILL.md +2 -2
  43. package/skills/api-context/SKILL.md +55 -2
  44. package/skills/api-errors/SKILL.md +1 -1
  45. package/skills/api-services/SKILL.md +1 -1
  46. package/skills/api-telemetry/SKILL.md +2 -2
  47. package/skills/field-test/SKILL.md +1 -1
  48. package/skills/git-wrapup/SKILL.md +4 -3
  49. package/skills/orchestrations/SKILL.md +2 -2
  50. package/skills/polish-docs-meta/SKILL.md +2 -2
  51. package/skills/report-issue-local/SKILL.md +1 -1
  52. package/skills/tool-defs-analysis/SKILL.md +1 -1
@@ -5,7 +5,7 @@
5
5
  * `createInMemoryStorage()` for unit-testing services in isolation.
6
6
  * @module src/testing/index
7
7
  */
8
- import type { ElicitResult } from '@modelcontextprotocol/sdk/types.js';
8
+ import type { ContentBlock, ElicitResult } from '@modelcontextprotocol/sdk/types.js';
9
9
  import type { z } from 'zod';
10
10
  import type { AuthContext, Context, ContextLogger } from '../core/context.js';
11
11
  import { StorageService } from '../storage/core/StorageService.js';
@@ -105,6 +105,20 @@ export declare function createMockContext(options?: MockContextOptions): Context
105
105
  * ```
106
106
  */
107
107
  export declare function getEnrichment(ctx: Context): Record<string, unknown>;
108
+ /**
109
+ * Reads the content blocks a handler emitted via `ctx.content(...)` on a mock
110
+ * context, for assertions. Returns them in insertion order (empty array when none
111
+ * were emitted) — the same blocks the handler factory prepends to `content[]`,
112
+ * never placed in `structuredContent`.
113
+ *
114
+ * @example
115
+ * ```ts
116
+ * const ctx = createMockContext();
117
+ * await render.handler(render.input.parse({ text: 'hi' }), ctx);
118
+ * expect(getContentBlocks(ctx)).toEqual([{ type: 'image', data: '...', mimeType: 'image/png' }]);
119
+ * ```
120
+ */
121
+ export declare function getContentBlocks(ctx: Context): ContentBlock[];
108
122
  /**
109
123
  * Build a real `StorageService` backed by an in-memory provider, suitable for
110
124
  * unit-testing services that accept a `StorageService` dependency.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AACvE,OAAO,KAAK,EAAW,CAAC,EAAE,MAAM,KAAK,CAAC;AACtC,OAAO,KAAK,EACV,WAAW,EACX,OAAO,EACP,aAAa,EAId,MAAM,mBAAmB,CAAC;AAQ3B,OAAO,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAClE,OAAO,EAEL,KAAK,uBAAuB,EAC7B,MAAM,kDAAkD,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAM9D,MAAM,WAAW,kBAAkB;IACjC,oBAAoB;IACpB,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;IACxF;;;;;OAKG;IACH,MAAM,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;IAClC,yCAAyC;IACzC,uBAAuB,CAAC,EAAE,MAAM,IAAI,CAAC;IACrC,2CAA2C;IAC3C,yBAAyB,CAAC,EAAE,MAAM,IAAI,CAAC;IACvC,sCAAsC;IACtC,qBAAqB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,uCAAuC;IACvC,qBAAqB,CAAC,EAAE,MAAM,IAAI,CAAC;IACnC,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wEAAwE;IACxE,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iDAAiD;IACjD,GAAG,CAAC,EAAE,GAAG,CAAC;CACX;AAMD,iFAAiF;AACjF,MAAM,MAAM,iBAAiB,GAAG,aAAa,GAAG;IAC9C,mFAAmF;IACnF,KAAK,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CAC9D,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,IAAI,iBAAiB,CAiBpD;AAwGD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAgD3E;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEnE;AAMD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,CAAC,EAAE,uBAAuB,GAAG,cAAc,CAEvF"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AACrF,OAAO,KAAK,EAAW,CAAC,EAAE,MAAM,KAAK,CAAC;AACtC,OAAO,KAAK,EACV,WAAW,EACX,OAAO,EACP,aAAa,EAId,MAAM,mBAAmB,CAAC;AAY3B,OAAO,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAClE,OAAO,EAEL,KAAK,uBAAuB,EAC7B,MAAM,kDAAkD,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAM9D,MAAM,WAAW,kBAAkB;IACjC,oBAAoB;IACpB,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;IACxF;;;;;OAKG;IACH,MAAM,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;IAClC,yCAAyC;IACzC,uBAAuB,CAAC,EAAE,MAAM,IAAI,CAAC;IACrC,2CAA2C;IAC3C,yBAAyB,CAAC,EAAE,MAAM,IAAI,CAAC;IACvC,sCAAsC;IACtC,qBAAqB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,uCAAuC;IACvC,qBAAqB,CAAC,EAAE,MAAM,IAAI,CAAC;IACnC,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wEAAwE;IACxE,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iDAAiD;IACjD,GAAG,CAAC,EAAE,GAAG,CAAC;CACX;AAMD,iFAAiF;AACjF,MAAM,MAAM,iBAAiB,GAAG,aAAa,GAAG;IAC9C,mFAAmF;IACnF,KAAK,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CAC9D,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,IAAI,iBAAiB,CAiBpD;AAwGD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAqD3E;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEnE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,YAAY,EAAE,CAE7D;AAMD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,CAAC,EAAE,uBAAuB,GAAG,cAAc,CAEvF"}
@@ -5,7 +5,7 @@
5
5
  * `createInMemoryStorage()` for unit-testing services in isolation.
6
6
  * @module src/testing/index
7
7
  */
8
- import { attachTypedFail, createEnrich, createEnrichmentStore, readEnrichmentStore, stashEnrichmentStore, } from '../core/context.js';
8
+ import { attachTypedFail, createContentCollect, createContentStore, createEnrich, createEnrichmentStore, readContentStore, readEnrichmentStore, stashContentStore, stashEnrichmentStore, } from '../core/context.js';
9
9
  import { StorageService } from '../storage/core/StorageService.js';
10
10
  import { InMemoryProvider, } from '../storage/providers/inMemory/inMemoryProvider.js';
11
11
  /**
@@ -149,6 +149,7 @@ export function createMockContext(options = {}) {
149
149
  const state = createMockState(options.tenantId);
150
150
  const progress = options.progress ? createMockProgress() : undefined;
151
151
  const enrichmentStore = createEnrichmentStore();
152
+ const contentStore = createContentStore();
152
153
  // Wrap the caller's elicit mock into an ElicitFn so that tests calling
153
154
  // ctx.elicit.url(...) don't throw TypeError. The default url stub returns a
154
155
  // cancelled result and can be overridden by casting the mock to ElicitFn.
@@ -174,6 +175,7 @@ export function createMockContext(options = {}) {
174
175
  notifyToolListChanged: options.notifyToolListChanged,
175
176
  progress,
176
177
  uri: options.uri,
178
+ content: createContentCollect(contentStore),
177
179
  enrich: createEnrich(enrichmentStore),
178
180
  // No-op resolver for definitions without a contract. `attachTypedFail` below
179
181
  // overwrites it with a contract-aware resolver when `options.errors` is set.
@@ -182,6 +184,9 @@ export function createMockContext(options = {}) {
182
184
  // Stash the enrichment store so `getEnrichment(ctx)` can read what a handler
183
185
  // (or the service layer) accumulated via `ctx.enrich(...)` during the test.
184
186
  stashEnrichmentStore(ctx, enrichmentStore);
187
+ // Stash the content store so `getContentBlocks(ctx)` can read what a handler
188
+ // emitted via `ctx.content(...)` during the test.
189
+ stashContentStore(ctx, contentStore);
185
190
  // Mirror the production handler factory: when a contract is declared, attach
186
191
  // a typed `fail` and `recoveryFor` keyed by the contract's reasons. Empty
187
192
  // contracts leave the no-op resolver in place.
@@ -202,6 +207,22 @@ export function createMockContext(options = {}) {
202
207
  export function getEnrichment(ctx) {
203
208
  return readEnrichmentStore(ctx)?.values ?? {};
204
209
  }
210
+ /**
211
+ * Reads the content blocks a handler emitted via `ctx.content(...)` on a mock
212
+ * context, for assertions. Returns them in insertion order (empty array when none
213
+ * were emitted) — the same blocks the handler factory prepends to `content[]`,
214
+ * never placed in `structuredContent`.
215
+ *
216
+ * @example
217
+ * ```ts
218
+ * const ctx = createMockContext();
219
+ * await render.handler(render.input.parse({ text: 'hi' }), ctx);
220
+ * expect(getContentBlocks(ctx)).toEqual([{ type: 'image', data: '...', mimeType: 'image/png' }]);
221
+ * ```
222
+ */
223
+ export function getContentBlocks(ctx) {
224
+ return readContentStore(ctx)?.blocks ?? [];
225
+ }
205
226
  // ---------------------------------------------------------------------------
206
227
  // Storage helpers
207
228
  // ---------------------------------------------------------------------------
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cyanheads/mcp-ts-core",
3
- "version": "0.10.6",
3
+ "version": "0.10.8",
4
4
  "mcpName": "io.github.cyanheads/mcp-ts-core",
5
5
  "description": "Agent-native TypeScript framework for building MCP servers. Declarative definitions with auth, multi-backend storage, OpenTelemetry, and first-class support for Bun/Node/Cloudflare Workers.",
6
6
  "main": "dist/core/index.js",
@@ -193,20 +193,13 @@
193
193
  "publish-mcp": "mcp-publisher login github -token \"$(security find-generic-password -a \"$USER\" -s mcp-publisher-github-pat -w)\" && mcp-publisher publish"
194
194
  },
195
195
  "resolutions": {
196
- "brace-expansion": "5.0.6",
197
- "flatted": "3.4.2",
198
- "handlebars": "4.7.9",
199
- "lodash": "4.18.1",
200
- "path-to-regexp": "8.4.2",
201
- "picomatch": "4.0.4",
202
- "protobufjs": "8.3.0",
203
- "yaml": "2.9.0"
196
+ "js-yaml": "^4.2.0"
204
197
  },
205
198
  "devDependencies": {
206
- "@biomejs/biome": "2.4.16",
207
- "@cloudflare/vitest-pool-workers": "^0.16.15",
208
- "@cloudflare/workers-types": "4.20260611.1",
209
- "@duckdb/node-api": "^1.5.3-r.3",
199
+ "@biomejs/biome": "2.5.0",
200
+ "@cloudflare/vitest-pool-workers": "^0.16.18",
201
+ "@cloudflare/workers-types": "4.20260619.1",
202
+ "@duckdb/node-api": "^1.5.4-r.1",
210
203
  "@hono/otel": "^1.1.2",
211
204
  "@opentelemetry/exporter-metrics-otlp-http": "^0.219.0",
212
205
  "@opentelemetry/exporter-trace-otlp-http": "^0.219.0",
@@ -217,35 +210,35 @@
217
210
  "@opentelemetry/sdk-node": "^0.219.0",
218
211
  "@opentelemetry/sdk-trace-node": "^2.8.0",
219
212
  "@opentelemetry/semantic-conventions": "^1.41.1",
220
- "@supabase/supabase-js": "^2.108.1",
213
+ "@supabase/supabase-js": "^2.108.2",
221
214
  "@types/bun": "^1.3.14",
222
215
  "@types/js-yaml": "^4.0.9",
223
- "@types/node": "25.9.3",
216
+ "@types/node": "26.0.0",
224
217
  "@types/papaparse": "^5.5.2",
225
218
  "@types/sanitize-html": "^2.16.1",
226
219
  "@types/validator": "^13.15.10",
227
- "@vitest/coverage-istanbul": "4.1.8",
228
- "@vitest/ui": "4.1.8",
229
- "better-sqlite3": "^12.10.0",
220
+ "@vitest/coverage-istanbul": "4.1.9",
221
+ "@vitest/ui": "4.1.9",
222
+ "better-sqlite3": "^12.11.1",
230
223
  "bun-types": "^1.3.14",
231
224
  "chrono-node": "^2.9.1",
232
225
  "clipboardy": "^5.3.1",
233
- "defuddle": "^0.18.1",
226
+ "defuddle": "^0.19.0",
234
227
  "depcheck": "^1.4.7",
235
228
  "diff": "^9.0.0",
236
229
  "execa": "^9.6.1",
237
230
  "fast-check": "^4.8.0",
238
- "fast-xml-parser": "^5.8.0",
231
+ "fast-xml-parser": "^5.9.3",
239
232
  "ignore": "^7.0.5",
240
233
  "js-yaml": "^4.2.0",
241
234
  "linkedom": "^0.18.12",
242
- "node-cron": "^4.2.1",
243
- "openai": "^6.42.0",
244
- "papaparse": "^5.5.3",
235
+ "node-cron": "^4.4.1",
236
+ "openai": "^6.44.0",
237
+ "papaparse": "^5.5.4",
245
238
  "partial-json": "^0.1.7",
246
239
  "pdf-lib": "^1.17.1",
247
240
  "pino-pretty": "^13.1.3",
248
- "repomix": "^1.14.1",
241
+ "repomix": "^1.15.0",
249
242
  "sanitize-html": "^2.17.5",
250
243
  "tsc-alias": "^1.8.17",
251
244
  "typedoc": "^0.28.19",
@@ -253,7 +246,7 @@
253
246
  "unpdf": "^1.6.2",
254
247
  "validator": "^13.15.35",
255
248
  "vite": "8.0.16",
256
- "vitest": "^4.1.8"
249
+ "vitest": "^4.1.9"
257
250
  },
258
251
  "keywords": [
259
252
  "agent",
@@ -305,12 +298,12 @@
305
298
  },
306
299
  "dependencies": {
307
300
  "@hono/mcp": "^0.3.0",
308
- "@hono/node-server": "^2.0.4",
301
+ "@hono/node-server": "^2.0.5",
309
302
  "@modelcontextprotocol/ext-apps": "^1.7.4",
310
303
  "@modelcontextprotocol/sdk": "^1.29.0",
311
304
  "@opentelemetry/api": "^1.9.1",
312
305
  "dotenv": "^17.4.2",
313
- "hono": "^4.12.25",
306
+ "hono": "^4.12.26",
314
307
  "jose": "^6.2.3",
315
308
  "pino": "^10.3.1",
316
309
  "zod": "^4.4.3"
@@ -211,6 +211,15 @@ function main(): void {
211
211
  process.exit(0);
212
212
  }
213
213
 
214
+ // A fresh scaffold ships `changelog/template.md` (excluded) and no `<major.minor>.x/`
215
+ // version files yet, so `changelog/` exists but `buildRollup()` would throw. Under
216
+ // --check, that's not drift — skip cleanly. A manual `changelog:build` still throws,
217
+ // surfacing the empty-tree mistake when someone explicitly regenerates.
218
+ if (checkOnly && collectVersionFiles().length === 0) {
219
+ console.log(`Skipped: no per-version changelog files under ${CHANGELOG_DIR}/<major.minor>.x/.`);
220
+ process.exit(0);
221
+ }
222
+
214
223
  const { content: generated, missingSummary } = buildRollup();
215
224
 
216
225
  if (checkOnly) {
@@ -36,8 +36,18 @@
36
36
  */
37
37
 
38
38
  import { spawnSync } from 'node:child_process';
39
+ import { existsSync } from 'node:fs';
39
40
  import process from 'node:process';
40
41
 
42
+ // All rules scan via `git grep`, which exits 128 (not a finding) outside a repo.
43
+ // A fresh `init` scaffold has no `.git` until the user runs `git init`, so skip
44
+ // cleanly rather than crash on the first run. Mirrors build-changelog.ts's
45
+ // early-exit when its source directory is absent.
46
+ if (!existsSync('.git')) {
47
+ console.log('Skipped: not a git repository.');
48
+ process.exit(0);
49
+ }
50
+
41
51
  interface Rule {
42
52
  id: string;
43
53
  /** Human-readable message printed when the rule fires. */
@@ -107,6 +107,7 @@ const violations: { file: string; version: string }[] = [];
107
107
  for (const file of changed) {
108
108
  const oldContent = headContent(file);
109
109
  if (oldContent === null) continue; // new skill — no prior version to compare
110
+ if (!existsSync(resolve(ROOT, file))) continue; // deleted in worktree — no body to compare, can't violate
110
111
  const newContent = readFileSync(resolve(ROOT, file), 'utf-8');
111
112
 
112
113
  if (!bodiesDiffer(extractBody(oldContent), extractBody(newContent))) continue; // whitespace-only
@@ -229,6 +229,15 @@ const Shell = {
229
229
 
230
230
  const ROOT_DIR = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
231
231
 
232
+ /**
233
+ * Whether the project root is inside a git repository. The git-dependent checks
234
+ * (TODOs/FIXMEs, Tracked Secrets, Framework Antipatterns) shell out to
235
+ * `git grep`/`git ls-files`, which exit 128 — not a real finding — when there's
236
+ * no repo. A fresh `init` scaffold has no `.git` until the user runs `git init`,
237
+ * so those checks guard on this and skip cleanly instead of failing.
238
+ */
239
+ const isGitRepo = (): boolean => existsSync(path.join(ROOT_DIR, '.git'));
240
+
232
241
  // ── Project-local config (devcheck.config.json) ─────────────────────
233
242
 
234
243
  interface DevcheckConfig {
@@ -356,6 +365,7 @@ const ALL_CHECKS: Check[] = [
356
365
  flag: '--no-todos',
357
366
  canFix: false,
358
367
  getCommand: (ctx) => {
368
+ if (!isGitRepo()) return null; // no repo to grep — fresh scaffold before `git init`
359
369
  // git grep -n (line number) -E (extended regex) -i (case-insensitive)
360
370
  const baseCmd = ['git', 'grep', '-nEi', '\\b(TODO|FIXME)\\b'];
361
371
  // Exclude files where TODO/FIXME appears as prose or intentional stubs
@@ -388,18 +398,21 @@ const ALL_CHECKS: Check[] = [
388
398
  flag: '--no-secrets',
389
399
  canFix: false,
390
400
  // Check if common sensitive files are tracked by git.
391
- getCommand: () => [
392
- 'git',
393
- 'ls-files',
394
- '*.env*',
395
- '**/.npmrc',
396
- '**/.netrc',
397
- '**/credentials.json',
398
- '**/*.pem',
399
- '**/*.key',
400
- '**/secret*',
401
- '**/.htpasswd',
402
- ],
401
+ getCommand: () => {
402
+ if (!isGitRepo()) return null; // no repo — `git ls-files` would exit 128, not a finding
403
+ return [
404
+ 'git',
405
+ 'ls-files',
406
+ '*.env*',
407
+ '**/.npmrc',
408
+ '**/.netrc',
409
+ '**/credentials.json',
410
+ '**/*.pem',
411
+ '**/*.key',
412
+ '**/secret*',
413
+ '**/.htpasswd',
414
+ ];
415
+ },
403
416
  // Success if output is empty OR only contains safe patterns.
404
417
  isSuccess: (result, _mode) => {
405
418
  if (result.exitCode !== 0) return false;
@@ -437,7 +450,11 @@ const ALL_CHECKS: Check[] = [
437
450
  name: 'Framework Antipatterns',
438
451
  flag: '--no-framework-antipatterns',
439
452
  canFix: false,
440
- getCommand: () => ['bun', 'run', 'scripts/check-framework-antipatterns.ts'],
453
+ // Runs `git grep` per rule; skip cleanly without a repo (fresh scaffold before `git init`).
454
+ getCommand: () => {
455
+ if (!isGitRepo()) return null;
456
+ return ['bun', 'run', 'scripts/check-framework-antipatterns.ts'];
457
+ },
441
458
  tip: (c) =>
442
459
  `Remove the flagged SDK-coupling shortcut. See ${c.bold('scripts/check-framework-antipatterns.ts')} for rule rationale.`,
443
460
  },
@@ -4,7 +4,7 @@ description: >
4
4
  Scaffold a new MCP tool definition. Use when the user asks to add a tool, create a new tool, or implement a new capability for the server.
5
5
  metadata:
6
6
  author: cyanheads
7
- version: "2.14"
7
+ version: "2.15"
8
8
  audience: external
9
9
  type: reference
10
10
  ---
@@ -252,6 +252,20 @@ enrichmentTrailer: {
252
252
 
253
253
  `structuredContent` always keeps the full structured value; `enrichmentTrailer` only controls the human-facing `content[]` line.
254
254
 
255
+ ### Image / audio output belongs in `ctx.content`
256
+
257
+ When a tool produces image or audio bytes for the calling model to *see or hear* — a rendered chart, a generated frame, synthesized speech — emit them via `ctx.content`, not an `output` field. `ctx.content.image(data, mimeType)` / `.audio(data, mimeType)` prepend a content block to `content[]` after `format()` runs and **never** write to `structuredContent`, so the base64 is carried once instead of duplicating into the typed output. Like `ctx.enrich`, it lives on the base `Context` and is callable from the service layer.
258
+
259
+ ```ts
260
+ async handler(input, ctx) {
261
+ const png = await render(input.spec); // base64 PNG
262
+ ctx.content.image(png, 'image/png'); // → content[] block, not structuredContent
263
+ return { width: input.spec.w, height: input.spec.h }; // typed result stays small
264
+ },
265
+ ```
266
+
267
+ The alternative — declaring `previewData: z.string()` in `output` and emitting the block from `format()` — ships the bytes twice (once in `structuredContent`, once in the block). Reserve `output` for data the agent reasons over; route raw media through `ctx.content`. Test with `getContentBlocks(ctx)`. Full reference: `skills/api-context` § `ctx.content`.
268
+
255
269
  ### Capped lists must disclose truncation
256
270
 
257
271
  When a tool accepts a cap-like input (`limit`, `per_page`, `page_size`, `max_results`, `max_items`) and returns an array, disclose when the cap was hit — the agent otherwise treats a partial set as complete.
@@ -4,7 +4,7 @@ description: >
4
4
  Authentication, authorization, and multi-tenancy patterns for `@cyanheads/mcp-ts-core`. Use when implementing auth scopes on tools/resources, configuring auth modes (none/jwt/oauth), working with JWT/OAuth env vars, or understanding how tenantId flows through ctx.state.
5
5
  metadata:
6
6
  author: cyanheads
7
- version: "1.1"
7
+ version: "1.2"
8
8
  audience: external
9
9
  type: reference
10
10
  ---
@@ -4,7 +4,7 @@ description: >
4
4
  DataCanvas primitive reference — a Tier 3 SQL/analytical workspace for tabular MCP servers, backed by DuckDB. Use when registering tables from upstream APIs, running ad-hoc SQL across them, and exporting results. Covers the acquire → register → query → export flow, per-table TTL, the token-sharing pattern for multi-agent collaboration, env config, and Cloudflare Workers fail-closed behavior.
5
5
  metadata:
6
6
  author: cyanheads
7
- version: "1.6"
7
+ version: "1.7"
8
8
  audience: external
9
9
  type: reference
10
10
  ---
@@ -150,6 +150,8 @@ Run SQL across registered tables. Returns at most `rowLimit` rows (default 10 00
150
150
 
151
151
  Querying a table that does not exist throws `NotFound` (`data.reason: 'missing_table'`) with a recovery hint to re-stage the table or call `describe()`. This happens when a table has expired (per-table TTL), been dropped, or the name is mistyped. The error is `NotFound`, not `ValidationError` — agents should re-stage, not fix the SQL shape.
152
152
 
153
+ A `SELECT` that parses but fails to prepare for any other reason — a mistyped column, an unknown function, an invalid expression — throws `ValidationError` (`data.reason: 'invalid_sql'`) and preserves the DuckDB binder detail in `data.binderMessage` (e.g. `Referenced column "x" not found...`, often with a candidate suggestion). This is distinct from `non_select_statement`, reserved for statements that genuinely aren't `SELECT`s — here the shape is fine, so the agent should fix the named column or function.
154
+
153
155
  ```ts
154
156
  const result = await instance.query(`
155
157
  SELECT germplasmName, COUNT(*) AS n
@@ -4,7 +4,7 @@ description: >
4
4
  Reference for core and server configuration in `@cyanheads/mcp-ts-core`. Covers env var tables with defaults, priority order, server-specific Zod schema pattern, and Workers lazy-parsing requirement.
5
5
  metadata:
6
6
  author: cyanheads
7
- version: "1.7"
7
+ version: "1.8"
8
8
  audience: external
9
9
  type: reference
10
10
  ---
@@ -167,7 +167,7 @@ Activated when both `SUPABASE_URL` and `SUPABASE_ANON_KEY` are set.
167
167
  | Env Var | `AppConfig` field | Default | Notes |
168
168
  |:--------|:-----------------|:--------|:------|
169
169
  | `OTEL_ENABLED` | `openTelemetry.enabled` | `false` | Enable OpenTelemetry export |
170
- | `OTEL_SERVICE_NAME` | `openTelemetry.serviceName` | `package.json` `name` | |
170
+ | `OTEL_SERVICE_NAME` | `openTelemetry.serviceName` | `createApp` `name` → `package.json` `name` | Seeded from `createApp({ name })` when unset; an env value wins |
171
171
  | `OTEL_SERVICE_VERSION` | `openTelemetry.serviceVersion` | `package.json` `version` | |
172
172
  | `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` | `openTelemetry.tracesEndpoint` | — | OTLP traces endpoint URL |
173
173
  | `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT` | `openTelemetry.metricsEndpoint` | — | OTLP metrics endpoint URL |
@@ -1,10 +1,10 @@
1
1
  ---
2
2
  name: api-context
3
3
  description: >
4
- Canonical reference for the unified `Context` object passed to every tool and resource handler in `@cyanheads/mcp-ts-core`. Covers the full interface, all sub-APIs (`ctx.log`, `ctx.state`, `ctx.elicit`, `ctx.progress`, `ctx.enrich`), and when to use each.
4
+ Canonical reference for the unified `Context` object passed to every tool and resource handler in `@cyanheads/mcp-ts-core`. Covers the full interface, all sub-APIs (`ctx.log`, `ctx.state`, `ctx.elicit`, `ctx.progress`, `ctx.enrich`, `ctx.content`), and when to use each.
5
5
  metadata:
6
6
  author: cyanheads
7
- version: "1.8"
7
+ version: "1.9"
8
8
  audience: external
9
9
  type: reference
10
10
  ---
@@ -63,6 +63,12 @@ interface Context {
63
63
  // declared fields. Kind-tagged helpers: enrich.notice / .total / .echo.
64
64
  readonly enrich: Enrich;
65
65
 
66
+ // Non-text content blocks (image/audio bytes) for the calling model — prepended
67
+ // to content[] after format() runs, never placed in structuredContent. Always
68
+ // present (no-op when never called). Helpers: content.image / .audio; content(block)
69
+ // pushes a raw ContentBlock.
70
+ readonly content: ContentCollect;
71
+
66
72
  // Opt-in contract resolver — always present (returns {} when no contract is attached
67
73
  // or the reason is unknown), strictly typed on HandlerContext<R> against declared reasons.
68
74
  recoveryFor(reason: string): { recovery: { hint: string } } | {};
@@ -637,6 +643,52 @@ See `add-tool`'s **Tool Response Design** and `skills/api-linter` (`enrichment-*
637
643
 
638
644
  ---
639
645
 
646
+ ## `ctx.content`
647
+
648
+ Always present on `Context`. Collects **non-text content blocks** — image or audio bytes the calling model should see or hear — and prepends them to the tool's `content[]` after `format()` runs. Collected blocks **never** enter `structuredContent`, so the base64 payload is carried once (in `content[]`) instead of duplicating into the typed output field. The media counterpart to `ctx.enrich`: both ride alongside the domain result without bloating it.
649
+
650
+ ```ts
651
+ export const renderChart = tool('render_chart', {
652
+ description: 'Render a chart from a series and return its summary.',
653
+ input: z.object({ series: z.array(z.number()).describe('Data points') }),
654
+ output: z.object({ points: z.number().describe('Number of points plotted') }),
655
+ async handler(input, ctx) {
656
+ const png = await draw(input.series); // base64 PNG
657
+ ctx.content.image(png, 'image/png'); // → content[] block, NOT structuredContent
658
+ return { points: input.series.length }; // the typed result stays small
659
+ },
660
+ });
661
+ ```
662
+
663
+ Without `ctx.content`, the only way to surface bytes to the model is to declare them in `output` and emit an image block from `format()` — which ships the base64 twice (once in `structuredContent`, once in the block). `ctx.content` removes the duplication.
664
+
665
+ ### Signature
666
+
667
+ ```ts
668
+ // Callable — push a raw ContentBlock (escape hatch for embedded resources, resource links):
669
+ ctx.content(block: ContentBlock): void
670
+
671
+ // Typed helpers for the two base64 media blocks:
672
+ ctx.content.image(data: string, mimeType: string): void // → { type: 'image', data, mimeType }
673
+ ctx.content.audio(data: string, mimeType: string): void // → { type: 'audio', data, mimeType }
674
+ ```
675
+
676
+ ### Behavior
677
+
678
+ | Aspect | Detail |
679
+ |:-------|:-------|
680
+ | `content[]` only | Blocks are prepended to `content[]` and never written to `structuredContent`. Data meant for the typed result stays on the handler's return value. |
681
+ | Order | `content[]` is `[...collected blocks, ...format()/JSON output, ...enrichment trailer]` — media first, domain content next, enrichment trailer last. |
682
+ | Accumulation | Each call appends; blocks render in call order. |
683
+ | No-op | A handler that never calls `ctx.content` produces a `content[]` / `structuredContent` byte-identical to before — the feature is purely additive and opt-in. |
684
+ | Error path | If the handler throws, collected blocks are dropped — a failed call returns the error result only, never a partial image. |
685
+ | Service usage | Services accepting `ctx: Context` can call `ctx.content(...)`; the blocks reach `content[]` exactly as if the handler had. |
686
+ | No schema involvement | Blocks bypass `output` entirely, so no linter rule requires them in `format()` and they never appear in the advertised `outputSchema`. |
687
+
688
+ Test content blocks with `getContentBlocks(ctx)` from `@cyanheads/mcp-ts-core/testing`.
689
+
690
+ ---
691
+
640
692
  ## Quick reference
641
693
 
642
694
  | Property | Type | Present when |
@@ -652,6 +704,7 @@ See `add-tool`'s **Tool Response Design** and `skills/api-linter` (`enrichment-*
652
704
  | `ctx.state` | `ContextState` | Always (throws if `tenantId` missing) |
653
705
  | `ctx.signal` | `AbortSignal` | Always |
654
706
  | `ctx.enrich` | `Enrich` | Always; typed on `HandlerContext<R, E>` when an `enrichment` block is declared |
707
+ | `ctx.content` | `ContentCollect` | Always — prepends image/audio blocks to `content[]`, never `structuredContent` |
655
708
  | `ctx.elicit` | `function \| undefined` | Client supports elicitation |
656
709
  | `ctx.notifyResourceListChanged` | `function \| undefined` | Always in handler ctx; delivery request-scoped (see [§ list-changed notifications](#list-changed-notifications-ctxnotify)) |
657
710
  | `ctx.notifyResourceUpdated` | `function \| undefined` | Always in handler ctx; delivery request-scoped |
@@ -4,7 +4,7 @@ description: >
4
4
  McpError constructor, JsonRpcErrorCode reference, and error handling patterns for `@cyanheads/mcp-ts-core`. Use when looking up error codes, understanding where errors should be thrown vs. caught, or using ErrorHandler.tryCatch in services.
5
5
  metadata:
6
6
  author: cyanheads
7
- version: "1.6"
7
+ version: "1.7"
8
8
  audience: external
9
9
  type: reference
10
10
  ---
@@ -4,7 +4,7 @@ description: >
4
4
  API reference for built-in service providers (LLM, Speech, Graph). Use when looking up service interfaces, provider capabilities, or integration patterns.
5
5
  metadata:
6
6
  author: cyanheads
7
- version: "1.3"
7
+ version: "1.4"
8
8
  audience: external
9
9
  type: reference
10
10
  ---
@@ -4,7 +4,7 @@ description: >
4
4
  Catalog of OpenTelemetry instrumentation built into framework `@cyanheads/mcp-ts-core` — spans, metrics, completion logs, env config, runtime caveats, custom instrumentation patterns, and cardinality rules. Use when enabling OTel export, adding custom spans or metrics in services, debugging missing telemetry, looking up attribute names, or deciding what's safe to put on a metric attribute vs. a span.
5
5
  metadata:
6
6
  author: cyanheads
7
- version: "1.0"
7
+ version: "1.2"
8
8
  audience: external
9
9
  type: reference
10
10
  ---
@@ -28,7 +28,7 @@ OTel is **off by default**. `OTEL_ENABLED=true` alone does nothing — you also
28
28
  | `OTEL_ENABLED` | `false` | Master switch. Must be `true` to start the SDK. |
29
29
  | `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` | — | OTLP/HTTP traces endpoint (e.g. `http://localhost:4318/v1/traces`). |
30
30
  | `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT` | — | OTLP/HTTP metrics endpoint (e.g. `http://localhost:4318/v1/metrics`). |
31
- | `OTEL_SERVICE_NAME` | `package.json` `name` | `service.name` resource attribute. |
31
+ | `OTEL_SERVICE_NAME` | `createApp` `name` → `package.json` `name` | `service.name` resource attribute. Seeded from `createApp({ name })` when unset; an env value wins. |
32
32
  | `OTEL_SERVICE_VERSION` | `package.json` `version` | `service.version` resource attribute. |
33
33
  | `OTEL_TRACES_SAMPLER_ARG` | `1.0` | Trace sampling ratio (0–1) for `TraceIdRatioBasedSampler`. |
34
34
  | `OTEL_LOG_LEVEL` | `INFO` | OTel diagnostic logger level (`NONE`/`ERROR`/`WARN`/`INFO`/`DEBUG`/`VERBOSE`/`ALL`). |
@@ -4,7 +4,7 @@ description: >
4
4
  Exercise tools, resources, and prompts against a live HTTP server via MCP JSON-RPC over curl. Starts the server, surfaces the catalog, runs real and adversarial inputs, and produces a tight report with concrete findings and numbered follow-up options. Use after adding or modifying definitions, or when the user asks to test, try out, or verify their MCP surface.
5
5
  metadata:
6
6
  author: cyanheads
7
- version: "2.5"
7
+ version: "2.6"
8
8
  audience: external
9
9
  type: debug
10
10
  ---
@@ -4,7 +4,7 @@ description: >
4
4
  Land working-tree changes as logical commits — the work grouped by concern, topped by a release commit (version bump, changelog, regenerated artifacts) and an annotated tag. Verify, commit, tag. Stops at "committed and tagged locally" — no push, no publish. The release-and-publish skill picks up from here. Distilled from the git_wrapup_instructions protocol.
5
5
  metadata:
6
6
  author: cyanheads
7
- version: "1.2"
7
+ version: "1.4"
8
8
  audience: external
9
9
  type: workflow
10
10
  ---
@@ -159,7 +159,7 @@ Use `-m` with embedded newlines in the string (the commit `-m`-only constraint a
159
159
  ```
160
160
  <theme — omit version number, GitHub prepends v<VERSION>:>
161
161
 
162
- <1-2 sentence context: what this release does>
162
+ <optional context one concise line, two max>
163
163
 
164
164
  <Sections — Keep a Changelog names, only those with entries>
165
165
 
@@ -182,6 +182,7 @@ Dependency bumps:
182
182
 
183
183
  **Rules:**
184
184
  - Subject line omits the version number (GitHub prepends `v<VERSION>:` to the release title)
185
+ - **No narrative preamble** — context under the subject is one concise line, two max; never paragraph blocks. Detail belongs in the bullets
185
186
  - Not a CHANGELOG copy — terse, scannable
186
187
  - No marketing adjectives
187
188
  - Length is earned — two-line tags are fine for small patches
@@ -204,7 +205,7 @@ If the working tree isn't clean or the tag doesn't point at HEAD, something went
204
205
  - **Local only.** No `git push`, no remote operations
205
206
  - **Never stash.** Not for quick checks, not for testing, not for any reason
206
207
  - **Never destructive.** No `git reset --hard`, `git restore .`, `git clean -f`, `git checkout -- .`
207
- - **Bash git only** when running inside orchestrated sub-agents (git-mcp-server session state leaks across parallel agents)
208
+ - **Bash git only.** Drive every git operation through the shell
208
209
  - If `v<version>` already exists as a tag, **halt and report the conflict** — include the version string, existing tag SHA, and current HEAD SHA so the caller can resolve it. Do not delete or move tags without explicit authorization
209
210
 
210
211
  ## Checklist
@@ -4,7 +4,7 @@ description: >
4
4
  Pick and run a multi-phase workflow that chains foundational task skills (`git-wrapup`, `release-and-publish`, `maintenance`, `field-test`, `setup`, etc.) end-to-end. Routes user intent to a workflow file under `workflows/` — greenfield builds, maintenance + release, field-test + fix, or known-work + release. Single source for the universal rules (no commits without authorization, no destructive git, no marketing language), the orchestrator posture (own the goal, ground sub-agents in primary sources, verify against the goal), and the sub-agent strategy (orient block, parallel fanout, isolation, normalization) that apply across every workflow. Sub-agents are an optional capability — workflows run linearly when fanout isn't available.
5
5
  metadata:
6
6
  author: cyanheads
7
- version: "1.3"
7
+ version: "1.4"
8
8
  audience: external
9
9
  type: workflow
10
10
  ---
@@ -140,7 +140,7 @@ The sub-agent reads the primary sources directly during orient (step 6) — do n
140
140
 
141
141
  ### Isolation rules
142
142
 
143
- 1. **Bash `git` only in parallel sub-agents.** Do not let parallel sub-agents call `mcp__git-mcp-server__*` tools session state (`set_working_dir`) leaks across parallel calls in the same orchestrator session, causing silent no-ops, wrong-directory operations, and false "tag already exists" errors. Bash `git` in the agent's CWD is reliable. The orchestrator may still use `git-mcp-server` itself in serial.
143
+ 1. **Bash `git` in each sub-agent's own CWD.** Parallel sub-agents run git through the shell against their own working directory independent, no shared state across agents.
144
144
 
145
145
  2. **Sub-agents do not receive this orchestrations skill or workflow files.** Their prompts include Tier 1 skill paths only. This prevents recursive sub-agent spawning — if a sub-agent decides it needs to fan out work, that's a signal the orchestrator sliced the work too wide. Re-slice; don't let the sub-agent recurse.
146
146