@cyanheads/mcp-ts-core 0.10.1 → 0.10.3

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 (116) hide show
  1. package/AGENTS.md +15 -9
  2. package/CLAUDE.md +15 -9
  3. package/README.md +5 -6
  4. package/changelog/0.10.x/0.10.2.md +35 -0
  5. package/changelog/0.10.x/0.10.3.md +46 -0
  6. package/dist/core/app.d.ts +36 -0
  7. package/dist/core/app.d.ts.map +1 -1
  8. package/dist/core/app.js +9 -4
  9. package/dist/core/app.js.map +1 -1
  10. package/dist/core/context.d.ts +57 -15
  11. package/dist/core/context.d.ts.map +1 -1
  12. package/dist/core/context.js +12 -1
  13. package/dist/core/context.js.map +1 -1
  14. package/dist/core/index.d.ts +5 -2
  15. package/dist/core/index.d.ts.map +1 -1
  16. package/dist/core/index.js +1 -0
  17. package/dist/core/index.js.map +1 -1
  18. package/dist/core/serverManifest.d.ts +18 -0
  19. package/dist/core/serverManifest.d.ts.map +1 -1
  20. package/dist/core/serverManifest.js +10 -2
  21. package/dist/core/serverManifest.js.map +1 -1
  22. package/dist/linter/rules/enrichment-rules.d.ts +20 -0
  23. package/dist/linter/rules/enrichment-rules.d.ts.map +1 -1
  24. package/dist/linter/rules/enrichment-rules.js +74 -9
  25. package/dist/linter/rules/enrichment-rules.js.map +1 -1
  26. package/dist/linter/rules/index.d.ts +2 -2
  27. package/dist/linter/rules/index.d.ts.map +1 -1
  28. package/dist/linter/rules/index.js +2 -2
  29. package/dist/linter/rules/index.js.map +1 -1
  30. package/dist/linter/rules/schema-rules.d.ts +4 -0
  31. package/dist/linter/rules/schema-rules.d.ts.map +1 -1
  32. package/dist/linter/rules/schema-rules.js +13 -0
  33. package/dist/linter/rules/schema-rules.js.map +1 -1
  34. package/dist/linter/rules/tool-rules.d.ts +12 -0
  35. package/dist/linter/rules/tool-rules.d.ts.map +1 -1
  36. package/dist/linter/rules/tool-rules.js +48 -1
  37. package/dist/linter/rules/tool-rules.js.map +1 -1
  38. package/dist/linter/types.d.ts +17 -0
  39. package/dist/linter/types.d.ts.map +1 -1
  40. package/dist/linter/validate.d.ts.map +1 -1
  41. package/dist/linter/validate.js +55 -1
  42. package/dist/linter/validate.js.map +1 -1
  43. package/dist/logs/combined.log +4 -0
  44. package/dist/logs/error.log +2 -0
  45. package/dist/logs/interactions.log +0 -0
  46. package/dist/mcp-server/notifications.d.ts +4 -4
  47. package/dist/mcp-server/prompts/prompt-registration.d.ts.map +1 -1
  48. package/dist/mcp-server/prompts/prompt-registration.js +1 -0
  49. package/dist/mcp-server/prompts/prompt-registration.js.map +1 -1
  50. package/dist/mcp-server/prompts/utils/promptDefinition.d.ts +6 -0
  51. package/dist/mcp-server/prompts/utils/promptDefinition.d.ts.map +1 -1
  52. package/dist/mcp-server/prompts/utils/promptDefinition.js.map +1 -1
  53. package/dist/mcp-server/resources/resource-registration.d.ts.map +1 -1
  54. package/dist/mcp-server/resources/resource-registration.js +3 -0
  55. package/dist/mcp-server/resources/resource-registration.js.map +1 -1
  56. package/dist/mcp-server/resources/utils/resourceDefinition.d.ts +9 -0
  57. package/dist/mcp-server/resources/utils/resourceDefinition.d.ts.map +1 -1
  58. package/dist/mcp-server/resources/utils/resourceDefinition.js.map +1 -1
  59. package/dist/mcp-server/resources/utils/resourceHandlerFactory.d.ts +13 -2
  60. package/dist/mcp-server/resources/utils/resourceHandlerFactory.d.ts.map +1 -1
  61. package/dist/mcp-server/resources/utils/resourceHandlerFactory.js +23 -12
  62. package/dist/mcp-server/resources/utils/resourceHandlerFactory.js.map +1 -1
  63. package/dist/mcp-server/server.d.ts +18 -2
  64. package/dist/mcp-server/server.d.ts.map +1 -1
  65. package/dist/mcp-server/server.js +4 -1
  66. package/dist/mcp-server/server.js.map +1 -1
  67. package/dist/mcp-server/tools/tool-registration.d.ts.map +1 -1
  68. package/dist/mcp-server/tools/tool-registration.js +2 -0
  69. package/dist/mcp-server/tools/tool-registration.js.map +1 -1
  70. package/dist/mcp-server/tools/utils/toolHandlerFactory.d.ts +14 -2
  71. package/dist/mcp-server/tools/utils/toolHandlerFactory.d.ts.map +1 -1
  72. package/dist/mcp-server/tools/utils/toolHandlerFactory.js +37 -13
  73. package/dist/mcp-server/tools/utils/toolHandlerFactory.js.map +1 -1
  74. package/dist/services/canvas/core/CanvasInstance.d.ts +1 -1
  75. package/dist/services/canvas/core/CanvasInstance.d.ts.map +1 -1
  76. package/dist/services/canvas/core/CanvasInstance.js +36 -11
  77. package/dist/services/canvas/core/CanvasInstance.js.map +1 -1
  78. package/dist/services/canvas/core/CanvasRegistry.d.ts +50 -3
  79. package/dist/services/canvas/core/CanvasRegistry.d.ts.map +1 -1
  80. package/dist/services/canvas/core/CanvasRegistry.js +163 -6
  81. package/dist/services/canvas/core/CanvasRegistry.js.map +1 -1
  82. package/dist/services/canvas/spillover.d.ts +6 -0
  83. package/dist/services/canvas/spillover.d.ts.map +1 -1
  84. package/dist/services/canvas/spillover.js +1 -0
  85. package/dist/services/canvas/spillover.js.map +1 -1
  86. package/dist/services/canvas/types.d.ts +21 -0
  87. package/dist/services/canvas/types.d.ts.map +1 -1
  88. package/dist/testing/index.d.ts +8 -14
  89. package/dist/testing/index.d.ts.map +1 -1
  90. package/dist/testing/index.js +10 -11
  91. package/dist/testing/index.js.map +1 -1
  92. package/package.json +7 -7
  93. package/skills/add-prompt/SKILL.md +33 -2
  94. package/skills/add-resource/SKILL.md +25 -1
  95. package/skills/add-service/SKILL.md +2 -2
  96. package/skills/add-test/SKILL.md +3 -3
  97. package/skills/add-tool/SKILL.md +38 -1
  98. package/skills/api-canvas/SKILL.md +23 -3
  99. package/skills/api-config/SKILL.md +12 -3
  100. package/skills/api-context/SKILL.md +57 -31
  101. package/skills/api-linter/SKILL.md +77 -3
  102. package/skills/api-mirror/SKILL.md +30 -1
  103. package/skills/api-testing/SKILL.md +2 -16
  104. package/skills/code-simplifier/SKILL.md +2 -2
  105. package/skills/design-mcp-server/SKILL.md +2 -1
  106. package/skills/polish-docs-meta/SKILL.md +1 -1
  107. package/skills/polish-docs-meta/references/agent-protocol.md +1 -1
  108. package/skills/report-issue-framework/SKILL.md +2 -2
  109. package/skills/security-pass/SKILL.md +6 -11
  110. package/templates/AGENTS.md +17 -5
  111. package/templates/CLAUDE.md +17 -5
  112. package/templates/Dockerfile +17 -0
  113. package/dist/mcp-server/roots/roots-registration.d.ts +0 -22
  114. package/dist/mcp-server/roots/roots-registration.d.ts.map +0 -1
  115. package/dist/mcp-server/roots/roots-registration.js +0 -25
  116. package/dist/mcp-server/roots/roots-registration.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EACV,mBAAmB,EACnB,YAAY,EACZ,eAAe,EAChB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,KAAK,EAAW,CAAC,EAAE,MAAM,KAAK,CAAC;AACtC,OAAO,KAAK,EACV,WAAW,EACX,OAAO,EACP,aAAa,EAGb,YAAY,EACb,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,gCAAgC;IAChC,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,6BAA6B;IAC7B,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,eAAe,EAAE,EAAE,IAAI,CAAC,EAAE,YAAY,KAAK,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAC5F;;;;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;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAsC3E;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,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"}
@@ -140,15 +140,6 @@ function createMockProgress() {
140
140
  * // With tenant (for tools that use ctx.state)
141
141
  * const ctx = createMockContext({ tenantId: 'test-tenant' });
142
142
  *
143
- * // With sampling capability
144
- * const ctx = createMockContext({
145
- * sample: vi.fn().mockResolvedValue({
146
- * role: 'assistant',
147
- * content: { type: 'text', text: 'Response' },
148
- * model: 'test',
149
- * }),
150
- * });
151
- *
152
143
  * // With task progress
153
144
  * const ctx = createMockContext({ progress: true });
154
145
  * ```
@@ -158,6 +149,15 @@ export function createMockContext(options = {}) {
158
149
  const state = createMockState(options.tenantId);
159
150
  const progress = options.progress ? createMockProgress() : undefined;
160
151
  const enrichmentStore = createEnrichmentStore();
152
+ // Wrap the caller's elicit mock into an ElicitFn so that tests calling
153
+ // ctx.elicit.url(...) don't throw TypeError. The default url stub returns a
154
+ // cancelled result and can be overridden by casting the mock to ElicitFn.
155
+ let elicit;
156
+ if (options.elicit) {
157
+ const base = options.elicit;
158
+ base.url = async (_message, _url) => ({ action: 'cancel' });
159
+ elicit = base;
160
+ }
161
161
  const ctx = {
162
162
  requestId: options.requestId ?? 'test-request-id',
163
163
  timestamp: new Date().toISOString(),
@@ -167,8 +167,7 @@ export function createMockContext(options = {}) {
167
167
  tenantId: options.tenantId,
168
168
  sessionId: options.sessionId,
169
169
  auth: options.auth,
170
- elicit: options.elicit,
171
- sample: options.sample,
170
+ elicit,
172
171
  notifyPromptListChanged: options.notifyPromptListChanged,
173
172
  notifyResourceListChanged: options.notifyResourceListChanged,
174
173
  notifyResourceUpdated: options.notifyResourceUpdated,
@@ -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.1",
3
+ "version": "0.10.3",
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",
@@ -177,8 +177,8 @@
177
177
  },
178
178
  "devDependencies": {
179
179
  "@biomejs/biome": "2.4.16",
180
- "@cloudflare/vitest-pool-workers": "^0.16.13",
181
- "@cloudflare/workers-types": "4.20260608.1",
180
+ "@cloudflare/vitest-pool-workers": "^0.16.14",
181
+ "@cloudflare/workers-types": "4.20260610.1",
182
182
  "@duckdb/node-api": "^1.5.3-r.3",
183
183
  "@hono/otel": "^1.1.2",
184
184
  "@opentelemetry/exporter-metrics-otlp-http": "^0.218.0",
@@ -190,10 +190,10 @@
190
190
  "@opentelemetry/sdk-node": "^0.218.0",
191
191
  "@opentelemetry/sdk-trace-node": "^2.7.1",
192
192
  "@opentelemetry/semantic-conventions": "^1.41.1",
193
- "@supabase/supabase-js": "^2.108.0",
193
+ "@supabase/supabase-js": "^2.108.1",
194
194
  "@types/bun": "^1.3.14",
195
195
  "@types/js-yaml": "^4.0.9",
196
- "@types/node": "^25.9.2",
196
+ "@types/node": "25.9.3",
197
197
  "@types/papaparse": "^5.5.2",
198
198
  "@types/sanitize-html": "^2.16.1",
199
199
  "@types/validator": "^13.15.10",
@@ -218,7 +218,7 @@
218
218
  "pdf-lib": "^1.17.1",
219
219
  "pino-pretty": "^13.1.3",
220
220
  "repomix": "^1.14.1",
221
- "sanitize-html": "^2.17.4",
221
+ "sanitize-html": "^2.17.5",
222
222
  "tsc-alias": "^1.8.17",
223
223
  "typedoc": "^0.28.19",
224
224
  "typescript": "^6.0.3",
@@ -282,7 +282,7 @@
282
282
  "@modelcontextprotocol/sdk": "^1.29.0",
283
283
  "@opentelemetry/api": "^1.9.1",
284
284
  "dotenv": "^17.4.2",
285
- "hono": "^4.12.24",
285
+ "hono": "^4.12.25",
286
286
  "jose": "^6.2.3",
287
287
  "pino": "^10.3.1",
288
288
  "zod": "^4.4.3"
@@ -4,7 +4,7 @@ description: >
4
4
  Scaffold a new MCP prompt template. Use when the user asks to add a prompt, create a reusable message template, or define a prompt for LLM interactions.
5
5
  metadata:
6
6
  author: cyanheads
7
- version: "1.2"
7
+ version: "1.3"
8
8
  audience: external
9
9
  type: reference
10
10
  ---
@@ -30,14 +30,18 @@ Prompts are pure message templates — no `Context`, no auth, no side effects. `
30
30
  * @module mcp-server/prompts/definitions/{{PROMPT_NAME}}
31
31
  */
32
32
 
33
- import { prompt, z } from '@cyanheads/mcp-ts-core';
33
+ import { completable, prompt, z } from '@cyanheads/mcp-ts-core';
34
34
 
35
35
  export const {{PROMPT_EXPORT}} = prompt('{{prompt_name}}', {
36
36
  description: '{{PROMPT_DESCRIPTION}}',
37
+ // title is optional — human-readable display name surfaced in prompts/list.
38
+ // title: '{{PROMPT_DISPLAY_TITLE}}',
37
39
  // args is optional — omit entirely for prompts with no parameters.
38
40
  // When present, all fields need .describe(). Only JSON-Schema-serializable types allowed.
41
+ // Wrap any field with completable() to enable argument autocompletion.
39
42
  args: z.object({
40
43
  // All fields need .describe()
44
+ // language: completable(z.string().describe('Language'), async (partial) => matchingLanguages(partial)),
41
45
  }),
42
46
  generate: (args) => [
43
47
  {
@@ -92,13 +96,40 @@ If the repo already uses `src/mcp-server/prompts/definitions/index.ts`, add the
92
96
  export { {{PROMPT_EXPORT}} } from './{{prompt-name}}.prompt.js';
93
97
  ```
94
98
 
99
+ ## Argument autocompletion
100
+
101
+ Wrap any `args` field with `completable()` (re-exported from `@cyanheads/mcp-ts-core`) to enable argument autocompletion. The SDK auto-installs `completion/complete` handling and advertises the `completions` capability when any registered prompt has a completable argument — no other changes are needed.
102
+
103
+ ```typescript
104
+ import { completable, prompt, z } from '@cyanheads/mcp-ts-core';
105
+
106
+ export const codeReview = prompt('code_review', {
107
+ description: 'Review code for issues.',
108
+ title: 'Code Review',
109
+ args: z.object({
110
+ language: completable(
111
+ z.string().describe('Programming language'),
112
+ async (partial) => ['typescript', 'python', 'rust'].filter((l) => l.startsWith(partial)),
113
+ ),
114
+ code: z.string().describe('Code to review'),
115
+ }),
116
+ generate: (args) => [
117
+ { role: 'user', content: { type: 'text', text: `Review this ${args.language} code:\n${args.code}` } },
118
+ ],
119
+ });
120
+ ```
121
+
122
+ `completable()` is transparent to the linter — it does not affect `describe-on-fields` or `schema-serializable` rules. All completable-wrapped fields still require `.describe()` on the underlying schema.
123
+
95
124
  ## Checklist
96
125
 
97
126
  - [ ] File created at `src/mcp-server/prompts/definitions/{{prompt-name}}.prompt.ts`
98
127
  - [ ] Prompt name passed to `prompt()` uses snake_case
99
128
  - [ ] `description` field set (lint warns if absent, but `devcheck` won't hard-fail — verify it's present)
129
+ - [ ] `title` field set if a human-readable display name is needed in `prompts/list`
100
130
  - [ ] All Zod `args` fields have `.describe()` annotations — or `args` omitted entirely for no-parameter prompts
101
131
  - [ ] `args` fields use only JSON-Schema-serializable Zod types (no `z.date()`, `z.transform()`, `z.bigint()`, `z.symbol()`, `z.custom()`, etc.)
132
+ - [ ] If using `completable()`, the underlying schema still has `.describe()` on each wrapped field
102
133
  - [ ] JSDoc `@fileoverview` and `@module` header present
103
134
  - [ ] `generate` function present and returns at least one `{ role, content: { type: 'text', text } }` message
104
135
  - [ ] No side effects — prompts are pure templates
@@ -4,7 +4,7 @@ description: >
4
4
  Scaffold a new MCP resource definition. Use when the user asks to add a resource, expose data via URI, or create a readable endpoint.
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
  ---
@@ -141,6 +141,29 @@ export const articleResource = resource('article://{pmid}', {
141
141
 
142
142
  Without `errors[]`, the handler receives plain `Context` (no `fail` method) and throws via error factories (`notFound`, `serviceUnavailable`, …) directly. The contract is opt-in. See `skills/api-errors/SKILL.md` for the full pattern, baseline codes, and conformance rules.
143
143
 
144
+ ### URI template variable completion
145
+
146
+ Add a `complete` map to enable autocompletion for URI template variables. The SDK auto-installs `completion/complete` handling and advertises the `completions` capability when a registered resource template has completion callbacks — no other changes needed.
147
+
148
+ ```typescript
149
+ import { resource, z } from '@cyanheads/mcp-ts-core';
150
+
151
+ const ITEM_IDS = ['item-001', 'item-002', 'item-abc'];
152
+
153
+ export const itemResource = resource('items://{itemId}', {
154
+ description: 'Retrieve an item by ID.',
155
+ params: z.object({ itemId: z.string().describe('Item identifier') }),
156
+ handler: (params) => ({ id: params.itemId }),
157
+ list: () => ({ resources: ITEM_IDS.map((id) => ({ uri: `items://${id}`, name: id })) }),
158
+ // Per-variable completion callbacks — keys must match URI template variable names.
159
+ complete: {
160
+ itemId: async (partial) => ITEM_IDS.filter((id) => id.startsWith(partial)),
161
+ },
162
+ });
163
+ ```
164
+
165
+ Only applies to templated resources (URI templates with `{variable}` syntax). Static URIs don't support completion.
166
+
144
167
  ### Other `resource()` options
145
168
 
146
169
  Beyond `description`, `params`, `handler`, and `list`, the builder also supports:
@@ -153,6 +176,7 @@ Beyond `description`, `params`, `handler`, and `list`, the builder also supports
153
176
  | `annotations` | Resource annotations (e.g., `audience`, `priority`) — see `ResourceAnnotations`. |
154
177
  | `title` | Human-readable display title (defaults to `name`). |
155
178
  | `examples` | Array of `{ name, uri }` example entries surfaced in `resources/list` for discoverability. |
179
+ | `complete` | Per-variable completion callbacks for URI template variables. Keys match template variable names. Enables `completion/complete` and the `completions` capability. |
156
180
 
157
181
  ## Checklist
158
182
 
@@ -4,7 +4,7 @@ description: >
4
4
  Scaffold a new service integration. Use when the user asks to add a service, integrate an external API, or create a reusable domain module with its own initialization and state.
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
  ---
@@ -13,7 +13,7 @@ metadata:
13
13
 
14
14
  Services use the init/accessor pattern: initialized once in `createApp`'s `setup()` callback, then accessed at request time via a lazy getter. Each service lives in `src/services/[domain]/` with an init function and accessor.
15
15
 
16
- Service methods receive `Context` for correlated logging (`ctx.log`) and tenant-scoped storage (`ctx.state`). Convention: `ctx.elicit` and `ctx.sample` should only be called from tool handlers, not from services.
16
+ Service methods receive `Context` for correlated logging (`ctx.log`) and tenant-scoped storage (`ctx.state`). Convention: `ctx.elicit` should only be called from tool handlers, not from services.
17
17
 
18
18
  For the full service pattern, `CoreServices`, and `Context` interface, read the framework's `CLAUDE.md`/`AGENTS.md` (loaded at session start).
19
19
 
@@ -4,7 +4,7 @@ description: >
4
4
  Scaffold a test file for an existing tool, resource, or service. Use when the user asks to add tests, improve coverage, or when a definition exists without a matching test file.
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
  ---
@@ -36,7 +36,7 @@ Read the handler and identify:
36
36
  | **Input variations** | Optional fields omitted, defaults applied, boundary values |
37
37
  | **Error paths** | Invalid state, missing resources, service failures → correct error thrown |
38
38
  | **`ctx.state` usage** | Use `createMockContext({ tenantId: 'test' })` to enable storage |
39
- | **`ctx.elicit` / `ctx.sample`** | Mock with `vi.fn()`, also test the absent case (undefined) |
39
+ | **`ctx.elicit`** | Mock with `vi.fn()`, also test the absent case (undefined) |
40
40
  | **`ctx.progress`** | Use `createMockContext({ progress: true })` for task tools |
41
41
  | **`ctx.fail` (typed contract)** | Definitions with `errors[]` need `fail` attached to the mock ctx — `createMockContext({ errors: myTool.errors })` does it for you. Assert on `data.reason` (stable per-contract entry), not just `code`. |
42
42
  | **`format` function** | Test separately if defined — it's pure, no ctx needed. Verify it renders the IDs and fields the model needs, not just a count or title. For projection-style tools, test non-default field selections. |
@@ -286,7 +286,7 @@ When scaffolding tests for an existing handler, use the Zod schemas to generate
286
286
  - [ ] Happy path tested with valid input → expected output
287
287
  - [ ] Error paths tested (at least one `.rejects.toThrow()`)
288
288
  - [ ] `format` function tested if defined
289
- - [ ] `createMockContext` options match handler's ctx usage (`tenantId`, `progress`, `elicit`, `sample`)
289
+ - [ ] `createMockContext` options match handler's ctx usage (`tenantId`, `progress`, `elicit`)
290
290
  - [ ] Service re-initialized in `beforeEach` if handler depends on a service singleton
291
291
  - [ ] If handler has optional fields: tested with empty-string inner values (form-client simulation)
292
292
  - [ ] If wrapping external API: sparse-payload case tested — fixture omits at least one optional upstream field; output still validates and `format()` renders uncertainty honestly instead of inventing values
@@ -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.13"
7
+ version: "2.14"
8
8
  audience: external
9
9
  type: reference
10
10
  ---
@@ -252,6 +252,43 @@ enrichmentTrailer: {
252
252
 
253
253
  `structuredContent` always keeps the full structured value; `enrichmentTrailer` only controls the human-facing `content[]` line.
254
254
 
255
+ ### Capped lists must disclose truncation
256
+
257
+ 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.
258
+
259
+ The one-liner: `ctx.enrich.truncated({ shown, cap })`. Declare the fields in the `enrichment` block:
260
+
261
+ ```ts
262
+ enrichment: {
263
+ truncated: z.boolean().describe('True when the list was capped at the limit.'),
264
+ shown: z.number().describe('Number of items returned.'),
265
+ cap: z.number().describe('The limit that was applied.'),
266
+ },
267
+ async handler(input, ctx) {
268
+ const items = await fetchItems(input.limit);
269
+ if (items.length >= input.limit) {
270
+ ctx.enrich.truncated({ shown: items.length, cap: input.limit });
271
+ }
272
+ return { items };
273
+ },
274
+ ```
275
+
276
+ Alternatively, if the upstream total is known, `ctx.enrich.total(n)` (writes `totalCount`) also satisfies the lint rule.
277
+
278
+ **Threshold bound** — when the upstream total is unknowable but the list is sorted by the cap key, the smallest shown value is a rigorous upper bound on all omitted items (Fagin Threshold Algorithm). Pass it as `ceiling`:
279
+
280
+ ```ts
281
+ // items is sorted descending by count; anything hidden has count ≤ items.at(-1).count
282
+ ctx.enrich.truncated({
283
+ shown: items.length,
284
+ cap: input.limit,
285
+ ceiling: items.at(-1)?.count,
286
+ guidance: 'Narrow with filters or raise per_page (max 200).',
287
+ });
288
+ ```
289
+
290
+ Declare `truncationCeiling: z.number().optional()` in the `enrichment` block to surface it. The `capped-list-no-truncation` lint rule warns when this disclosure is absent — see `api-linter`.
291
+
255
292
  ### Communicate filtering and exclusions
256
293
 
257
294
  If the tool omitted, truncated, or filtered anything, say what and how to get it back. Silent omission is invisible to the agent — it can't act on what it doesn't know about.
@@ -1,10 +1,10 @@
1
1
  ---
2
2
  name: api-canvas
3
3
  description: >
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, the token-sharing pattern for multi-agent collaboration, env config, and Cloudflare Workers fail-closed behavior.
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.4"
7
+ version: "1.5"
8
8
  audience: external
9
9
  type: reference
10
10
  ---
@@ -129,10 +129,21 @@ await instance.registerTable('big_dataset', asyncRows, {
129
129
  { name: 'label', type: 'VARCHAR', nullable: true },
130
130
  ],
131
131
  });
132
+
133
+ // Per-table TTL — this table ages on its own clock (30 min sliding window).
134
+ // The canvas itself is unaffected; other tables on the same canvas are not touched.
135
+ await instance.registerTable('recent_fetch', rows, { ttlMs: 30 * 60 * 1000 });
132
136
  ```
133
137
 
134
138
  **Schema inference** when `schema` is omitted: sniffer materializes the first 100 rows, unions JS-side types per column, and maps to DuckDB types. Fall-backs to `VARCHAR` for ambiguous unions (string mixed with numerics). Numeric widening: `INTEGER + DOUBLE → DOUBLE`, `INTEGER + BIGINT → BIGINT`. Column ordering follows first-appearance.
135
139
 
140
+ **Per-table TTL (`ttlMs`)** — optional sliding TTL for this table specifically. When set:
141
+ - The sweep loop drops the table (and clears its bookkeeping) when its window expires.
142
+ - The TTL slides on any read or write against this table: on `registerTable` (initial set), on `query()` (both when the table appears in the SQL text and when it is the `registerAs` target).
143
+ - The canvas itself is unaffected — canvas-level expiry is independent.
144
+ - Tables registered without `ttlMs` inherit the canvas lifecycle exactly as before (no change to default behavior).
145
+ - `instance.describe()` surfaces `TableInfo.expiresAt` (ISO 8601) for tables that have a per-table TTL; absent otherwise.
146
+
136
147
  ### `instance.query(sql, options?)`
137
148
 
138
149
  Run SQL across registered tables. Returns at most `rowLimit` rows (default 10 000). For full result sets, pass `registerAs` — the result is materialized as a new canvas table; the response carries a `preview` slice plus the table reference.
@@ -149,10 +160,18 @@ const joined = await instance.query(`
149
160
  FROM germplasm g JOIN observations o ON g.germplasmDbId = o.germplasmDbId
150
161
  `, { registerAs: 'g_with_obs', preview: 10 });
151
162
  // joined.tableName === 'g_with_obs'; joined.rows.length === 10; joined.rowCount === <full count>
163
+
164
+ // Materialize with a per-table TTL so the chained result ages independently.
165
+ const chained = await instance.query(
166
+ 'SELECT * FROM recent_fetch WHERE score > 0.8',
167
+ { registerAs: 'high_score', ttlMs: 15 * 60 * 1000 },
168
+ );
152
169
  ```
153
170
 
154
171
  `registerAs` rejects with `ValidationError` (`data.reason: 'register_as_clash'`) if the target name already exists — drop it first.
155
172
 
173
+ `ttlMs` on `query({ registerAs })` assigns a per-table TTL to the materialized table — the same sliding semantics as `registerTable({ ttlMs })`. The SQL text is also scanned for referenced table names; any tracked per-table TTL entry found is slid on each `query()` call.
174
+
156
175
  **Read-only enforcement** (four layers):
157
176
  1. Text-level deny-list — pre-parse scan for file/HTTP-reading table functions (`read_csv*`, `read_json*`, `read_parquet*`, `read_text`, `read_blob`, `glob`, `iceberg_scan`, `delta_scan`, `postgres_scan`, `mysql_scan`, `sqlite_scan`, plus pre-staged spatial ones).
158
177
  2. Statement count (must be 1) via `extractStatements`.
@@ -354,7 +373,7 @@ export const dataframeQuery = tool('dataframe_query', {
354
373
  | Underlying data is publicly accessible | ✅ |
355
374
  | Single-user deployment (stdio, or HTTP with one user) | ✅ — no cross-user surface regardless of data sensitivity |
356
375
  | Use case is research / analytics, not multi-tenant SaaS | ✅ |
357
- | Dataframes must age individually | ⚠️ TTL is canvas-level today (a hot canvas keeps stale tables alive); per-table TTL is tracked in [#140](https://github.com/cyanheads/mcp-ts-core/issues/140). Backstop with `ctx.state` bookkeeping in the interim. |
376
+ | Dataframes must age individually | Use `registerTable({ ttlMs })` or `query({ registerAs, ttlMs })` per-table TTL is independent of canvas-level expiry. The sweep loop drops expired tables while keeping the canvas (and other tables) alive. |
358
377
  | Per-user row visibility matters in a multi-user deployment | ❌ — add session/tenant scoping at the server level |
359
378
 
360
379
  The germplasm-flavored [consumer tool template](#consumer-tool-template) below is the same pattern with domain-specific naming.
@@ -421,6 +440,7 @@ const result = await spillover({
421
440
  previewChars: 100_000, // ≈ 25k tokens of inline rows
422
441
  caps: { maxRows: 50_000 }, // hard upper bound on registered rows
423
442
  signal: ctx.signal,
443
+ ttlMs: 30 * 60 * 1000, // optional: per-table TTL forwarded to registerTable
424
444
  });
425
445
 
426
446
  if (result.spilled) {
@@ -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.6"
7
+ version: "1.7"
8
8
  audience: external
9
9
  type: reference
10
10
  ---
@@ -23,7 +23,7 @@ Managed by `@cyanheads/mcp-ts-core`. Validated via Zod from environment variable
23
23
 
24
24
  **Priority (highest to lowest):**
25
25
 
26
- 1. `name`/`version` overrides passed to `createApp()` or `createWorkerHandler()`
26
+ 1. `name`/`version`/`title`/`websiteUrl`/`description`/`icons` options passed to `createApp()` or `createWorkerHandler()`
27
27
  2. Environment variables
28
28
  3. `package.json` fields
29
29
 
@@ -35,9 +35,18 @@ Managed by `@cyanheads/mcp-ts-core`. Validated via Zod from environment variable
35
35
  |:--------|:-----------------|:--------|:------|
36
36
  | `MCP_SERVER_NAME` | `mcpServerName` | `package.json` `name` | Overrides package name |
37
37
  | `MCP_SERVER_VERSION` | `mcpServerVersion` | `package.json` `version` | Overrides package version |
38
- | `MCP_SERVER_DESCRIPTION` | `mcpServerDescription` | `package.json` `description` | Optional |
38
+ | `MCP_SERVER_DESCRIPTION` | `mcpServerDescription` | `package.json` `description` | Optional; `createApp({ description })` wins when set |
39
39
  | `PACKAGE_NAME` | `pkg.name` | `package.json` `name` | Rarely needed |
40
40
  | `PACKAGE_VERSION` | `pkg.version` | `package.json` `version` | Rarely needed |
41
+
42
+ **SDK identity fields** (API-only, no env var equivalent — passed to `createApp()` / `createWorkerHandler()`, forwarded to `initialize` and `/.well-known/mcp.json`):
43
+
44
+ | Option | Type | Notes |
45
+ |:-------|:-----|:------|
46
+ | `title` | `string?` | Human-readable display name shown in client listings |
47
+ | `websiteUrl` | `string?` | Canonical homepage / repository URL |
48
+ | `description` | `string?` | One-line description; wins over `MCP_SERVER_DESCRIPTION` when set |
49
+ | `icons` | `Implementation['icons']?` | Array of icon objects: `{ src, mimeType?, sizes?: string[], theme?: 'light'\|'dark' }` |
41
50
  | `NODE_ENV` | `environment` | `development` | Aliases: `dev`→`development`, `prod`→`production`, `test`→`testing` |
42
51
  | `MCP_LOG_LEVEL` | `logLevel` | `debug` | Aliases: `warn`→`warning`, `err`→`error`, `fatal`/`silent`→`emerg`, `trace`→`debug`, `information`→`info` |
43
52
  | `LOGS_DIR` | `logsPath` | `<project-root>/logs` | Node.js only; absolute or relative to project root |
@@ -1,17 +1,17 @@
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.sample`, `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`), and when to use each.
5
5
  metadata:
6
6
  author: cyanheads
7
- version: "1.6"
7
+ version: "1.8"
8
8
  audience: external
9
9
  type: reference
10
10
  ---
11
11
 
12
12
  ## Overview
13
13
 
14
- Every tool and resource handler receives a single `Context` (`ctx`) argument. It provides request identity, structured logging, tenant-scoped storage, optional protocol capabilities (elicitation, sampling), cancellation, and task progress — all auto-correlated to the current request.
14
+ Every tool and resource handler receives a single `Context` (`ctx`) argument. It provides request identity, structured logging, tenant-scoped storage, optional protocol capabilities (elicitation), cancellation, and task progress — all auto-correlated to the current request.
15
15
 
16
16
  The framework auto-instruments every handler call (OTel span, duration, payload metrics). Use `ctx.log` for domain-specific logging and `ctx.state` for storage inside handlers. Use the global `logger` and `StorageService` directly only in lifecycle/background code (`setup()`, services).
17
17
 
@@ -39,8 +39,7 @@ interface Context {
39
39
  readonly state: ContextState;
40
40
 
41
41
  // Optional protocol capabilities (undefined when client doesn't support them)
42
- readonly elicit?: (message: string, schema: z.ZodObject<z.ZodRawShape>) => Promise<ElicitResult>;
43
- readonly sample?: (messages: SamplingMessage[], opts?: SamplingOpts) => Promise<CreateMessageResult>;
42
+ readonly elicit?: ElicitFn; // callable (message, schema) for form mode + .url(message, url) see § ctx.elicit
44
43
 
45
44
  // List-changed / resource-updated notifications — wired in every handler ctx;
46
45
  // delivery is request-scoped (see § list-changed notifications)
@@ -254,9 +253,11 @@ await ctx.state.set(sessionKey, value);
254
253
 
255
254
  ---
256
255
 
257
- ## `ctx.elicit` / `ctx.sample`
256
+ ## `ctx.elicit`
258
257
 
259
- Both are optional — `undefined` when the connected client doesn't support the capability. Check for presence before calling. A simple truthiness check is enough; no type guards needed.
258
+ Optional — `undefined` when the connected client doesn't advertise the `elicitation` capability (checked per request, after the initialize handshake). Check for presence before calling. A simple truthiness check is enough; no type guards needed.
259
+
260
+ `ctx.elicit` is an `ElicitFn` (exported from the main entry): directly callable for form-mode elicitation, with a `.url(message, url)` method for URL-mode. On the wire, the Zod schema is converted to the restricted flat JSON Schema the MCP spec requires — handlers never deal with that detail.
260
261
 
261
262
  ### `ctx.elicit` — ask the user for structured input
262
263
 
@@ -295,37 +296,23 @@ interface ElicitResult {
295
296
 
296
297
  > **Note:** `content` is not typed against the Zod schema you pass — it is a `Record` of primitives. Validate `content` against your schema manually (e.g. `MySchema.parse(result.content)`) when `action === 'accept'`.
297
298
 
298
- **Convention:** Only call `ctx.elicit` from tool handlers, not from services.
299
-
300
- ### `ctx.sample` — request an LLM completion from the client
299
+ ### `ctx.elicit.url` hand the user an external link
301
300
 
302
- Requests a completion from the client's LLM via the MCP sampling protocol. Useful for AI-assisted tool behavior without managing a separate LLM provider.
301
+ URL-mode elicitation (MCP 2025-11-25): instead of an inline form, the client directs the user to an external URL authorization flows, hosted forms. The framework generates the protocol-required `elicitationId` internally.
303
302
 
304
303
  ```ts
305
- if (ctx.sample) {
306
- const result = await ctx.sample(
307
- [
308
- { role: 'user', content: { type: 'text', text: `Summarize: ${data}` } },
309
- ],
310
- { maxTokens: 500 },
304
+ if (ctx.elicit) {
305
+ const result = await ctx.elicit.url(
306
+ 'Authorize access to your account',
307
+ 'https://example.com/oauth/authorize?state=...',
311
308
  );
312
- return { summary: result.content.text };
309
+ if (result.action !== 'accept') throw forbidden('Authorization declined');
313
310
  }
314
311
  ```
315
312
 
316
- `SamplingOpts`:
317
-
318
- ```ts
319
- interface SamplingOpts {
320
- includeContext?: 'none' | 'thisServer' | 'allServers';
321
- maxTokens?: number;
322
- modelPreferences?: ModelPreferences;
323
- stopSequences?: string[];
324
- temperature?: number;
325
- }
326
- ```
313
+ `result.content` is absent in URL mode — the interaction completes out-of-band; only `action` reports the outcome.
327
314
 
328
- **Convention:** Only call `ctx.sample` from tool handlers, not from services.
315
+ **Convention:** Only call `ctx.elicit` from tool handlers, not from services.
329
316
 
330
317
  ---
331
318
 
@@ -591,6 +578,11 @@ ctx.enrich.notice(text: string): void // writes `notice` → blockq
591
578
  ctx.enrich.total(count: number): void // writes `totalCount` → "N total"
592
579
  ctx.enrich.echo(query: string): void // writes `effectiveQuery` → "Query: …"
593
580
  ctx.enrich.delta({ field, before, after }): void // writes `{before, after}` → "field: before → after"
581
+
582
+ // Truncation disclosure — for capped lists:
583
+ ctx.enrich.truncated({ shown, cap, ceiling?, guidance? }): void
584
+ // writes: truncated=true, shown, cap, truncationCeiling? (if ceiling provided)
585
+ // also writes notice via guidance or a generated default (last-wins with other notice calls)
594
586
  ```
595
587
 
596
588
  ### Behavior
@@ -606,6 +598,41 @@ ctx.enrich.delta({ field, before, after }): void // writes `{before, after}`
606
598
  | `format-parity` | Enrichment lives outside `output`, so the `format-parity` lint never requires it in `format()`. |
607
599
  | Trailer rendering | Per field: kind-tag if set (notice/total/echo/delta), else the definition's `enrichmentTrailer.render`/`label`, else `**key:** value` (objects/arrays `JSON.stringify`'d). A structured field with no `render` errors under `enrichment-trailer-render` — supply one so it renders as markdown; `structuredContent` keeps the full value regardless. |
608
600
 
601
+ ### `ctx.enrich.truncated()` — capped-list disclosure
602
+
603
+ For tools that cap a list (i.e. have a `limit`/`per_page`/`page_size`/`max_results`/`max_items` input), call `truncated()` when the cap was actually hit:
604
+
605
+ ```ts
606
+ enrichment: {
607
+ truncated: z.boolean().describe('True when the list was capped.'),
608
+ shown: z.number().describe('Number of items returned.'),
609
+ cap: z.number().describe('The limit that was applied.'),
610
+ truncationCeiling: z.number().optional().describe('Upper bound for omitted items (threshold bound).'),
611
+ },
612
+ async handler(input, ctx) {
613
+ const items = await fetch(input.limit);
614
+ if (items.length >= input.limit) {
615
+ ctx.enrich.truncated({
616
+ shown: items.length,
617
+ cap: input.limit,
618
+ ceiling: items.at(-1)?.count, // optional — only when list sorted by cap key
619
+ guidance: 'Narrow with filters or raise per_page (max 200).',
620
+ });
621
+ }
622
+ return { items };
623
+ },
624
+ ```
625
+
626
+ | Field written | Key | Notes |
627
+ |:---|:---|:---|
628
+ | `truncated` | `true` | Always |
629
+ | `shown` | `number` | Always |
630
+ | `cap` | `number` | Always |
631
+ | `truncationCeiling` | `number` | Only when `ceiling` is passed |
632
+ | `notice` | `string` | Via `guidance` or a generated default; **last-wins** — a handler with multiple notice sources (e.g. both truncation and empty-result) should compose them into one string passed as `guidance`, or call `truncated()` after the other notice calls. |
633
+
634
+ The `capped-list-no-truncation` lint rule fires when a cap-like input + array output shape is present without any of: `truncated` or `totalCount` in the declared `enrichment`, or `truncated` or `totalCount` in `output`. Using `ctx.enrich.total(n)` (writes `totalCount`) is also recognized as honest disclosure.
635
+
609
636
  See `add-tool`'s **Tool Response Design** and `skills/api-linter` (`enrichment-*` rules) for the full pattern. Test enrichment with `getEnrichment(ctx)` from `@cyanheads/mcp-ts-core/testing`.
610
637
 
611
638
  ---
@@ -626,7 +653,6 @@ See `add-tool`'s **Tool Response Design** and `skills/api-linter` (`enrichment-*
626
653
  | `ctx.signal` | `AbortSignal` | Always |
627
654
  | `ctx.enrich` | `Enrich` | Always; typed on `HandlerContext<R, E>` when an `enrichment` block is declared |
628
655
  | `ctx.elicit` | `function \| undefined` | Client supports elicitation |
629
- | `ctx.sample` | `function \| undefined` | Client supports sampling |
630
656
  | `ctx.notifyResourceListChanged` | `function \| undefined` | Always in handler ctx; delivery request-scoped (see [§ list-changed notifications](#list-changed-notifications-ctxnotify)) |
631
657
  | `ctx.notifyResourceUpdated` | `function \| undefined` | Always in handler ctx; delivery request-scoped |
632
658
  | `ctx.notifyPromptListChanged` | `function \| undefined` | Always in handler ctx; delivery request-scoped |