@cyanheads/mcp-ts-core 0.13.9 → 0.13.10

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 (119) hide show
  1. package/AGENTS.md +36 -13
  2. package/CLAUDE.md +36 -13
  3. package/README.md +9 -9
  4. package/changelog/0.13.x/0.13.10.md +118 -0
  5. package/dist/config/index.d.ts +3 -0
  6. package/dist/config/index.d.ts.map +1 -1
  7. package/dist/config/index.js +11 -0
  8. package/dist/config/index.js.map +1 -1
  9. package/dist/core/app.d.ts.map +1 -1
  10. package/dist/core/app.js +15 -1
  11. package/dist/core/app.js.map +1 -1
  12. package/dist/core/context.d.ts +89 -20
  13. package/dist/core/context.d.ts.map +1 -1
  14. package/dist/core/context.js +40 -0
  15. package/dist/core/context.js.map +1 -1
  16. package/dist/core/index.d.ts +1 -1
  17. package/dist/core/index.d.ts.map +1 -1
  18. package/dist/core/index.js.map +1 -1
  19. package/dist/core/worker.d.ts +6 -0
  20. package/dist/core/worker.d.ts.map +1 -1
  21. package/dist/core/worker.js +1 -0
  22. package/dist/core/worker.js.map +1 -1
  23. package/dist/linter/rules/error-contract-rules.d.ts +3 -44
  24. package/dist/linter/rules/error-contract-rules.d.ts.map +1 -1
  25. package/dist/linter/rules/error-contract-rules.js +8 -144
  26. package/dist/linter/rules/error-contract-rules.js.map +1 -1
  27. package/dist/linter/rules/index.d.ts +1 -1
  28. package/dist/linter/rules/index.d.ts.map +1 -1
  29. package/dist/linter/rules/index.js +1 -1
  30. package/dist/linter/rules/index.js.map +1 -1
  31. package/dist/linter/rules/resource-rules.d.ts.map +1 -1
  32. package/dist/linter/rules/resource-rules.js +1 -2
  33. package/dist/linter/rules/resource-rules.js.map +1 -1
  34. package/dist/linter/rules/tool-rules.d.ts.map +1 -1
  35. package/dist/linter/rules/tool-rules.js +1 -2
  36. package/dist/linter/rules/tool-rules.js.map +1 -1
  37. package/dist/mcp-server/handlerContext.d.ts +26 -13
  38. package/dist/mcp-server/handlerContext.d.ts.map +1 -1
  39. package/dist/mcp-server/handlerContext.js +32 -17
  40. package/dist/mcp-server/handlerContext.js.map +1 -1
  41. package/dist/mcp-server/inputRequired.d.ts +120 -8
  42. package/dist/mcp-server/inputRequired.d.ts.map +1 -1
  43. package/dist/mcp-server/inputRequired.js +177 -12
  44. package/dist/mcp-server/inputRequired.js.map +1 -1
  45. package/dist/mcp-server/prompts/prompt-registration.d.ts +10 -2
  46. package/dist/mcp-server/prompts/prompt-registration.d.ts.map +1 -1
  47. package/dist/mcp-server/prompts/prompt-registration.js +49 -11
  48. package/dist/mcp-server/prompts/prompt-registration.js.map +1 -1
  49. package/dist/mcp-server/resources/resource-registration.d.ts +4 -2
  50. package/dist/mcp-server/resources/resource-registration.d.ts.map +1 -1
  51. package/dist/mcp-server/resources/resource-registration.js +6 -4
  52. package/dist/mcp-server/resources/resource-registration.js.map +1 -1
  53. package/dist/mcp-server/resources/utils/resourceHandlerFactory.d.ts +4 -3
  54. package/dist/mcp-server/resources/utils/resourceHandlerFactory.d.ts.map +1 -1
  55. package/dist/mcp-server/resources/utils/resourceHandlerFactory.js +42 -14
  56. package/dist/mcp-server/resources/utils/resourceHandlerFactory.js.map +1 -1
  57. package/dist/mcp-server/server.d.ts +9 -0
  58. package/dist/mcp-server/server.d.ts.map +1 -1
  59. package/dist/mcp-server/server.js +14 -13
  60. package/dist/mcp-server/server.js.map +1 -1
  61. package/dist/mcp-server/tools/tool-registration.d.ts +7 -3
  62. package/dist/mcp-server/tools/tool-registration.d.ts.map +1 -1
  63. package/dist/mcp-server/tools/tool-registration.js +9 -5
  64. package/dist/mcp-server/tools/tool-registration.js.map +1 -1
  65. package/dist/mcp-server/tools/utils/toolHandlerFactory.d.ts +17 -9
  66. package/dist/mcp-server/tools/utils/toolHandlerFactory.d.ts.map +1 -1
  67. package/dist/mcp-server/tools/utils/toolHandlerFactory.js +91 -50
  68. package/dist/mcp-server/tools/utils/toolHandlerFactory.js.map +1 -1
  69. package/dist/mcp-server/transports/http/httpErrorHandler.d.ts.map +1 -1
  70. package/dist/mcp-server/transports/http/httpErrorHandler.js +7 -1
  71. package/dist/mcp-server/transports/http/httpErrorHandler.js.map +1 -1
  72. package/dist/testing/index.d.ts +17 -2
  73. package/dist/testing/index.d.ts.map +1 -1
  74. package/dist/testing/index.js +21 -7
  75. package/dist/testing/index.js.map +1 -1
  76. package/dist/types-global/errors.d.ts +18 -15
  77. package/dist/types-global/errors.d.ts.map +1 -1
  78. package/dist/utils/internal/error-handler/errorHandler.d.ts +5 -4
  79. package/dist/utils/internal/error-handler/errorHandler.d.ts.map +1 -1
  80. package/dist/utils/internal/error-handler/errorHandler.js +9 -7
  81. package/dist/utils/internal/error-handler/errorHandler.js.map +1 -1
  82. package/dist/utils/internal/error-handler/types.d.ts +3 -1
  83. package/dist/utils/internal/error-handler/types.d.ts.map +1 -1
  84. package/dist/utils/internal/performance.d.ts +4 -2
  85. package/dist/utils/internal/performance.d.ts.map +1 -1
  86. package/dist/utils/internal/performance.js +8 -6
  87. package/dist/utils/internal/performance.js.map +1 -1
  88. package/dist/utils/internal/telemetryMessages.d.ts +0 -1
  89. package/dist/utils/internal/telemetryMessages.d.ts.map +1 -1
  90. package/dist/utils/internal/telemetryMessages.js +0 -1
  91. package/dist/utils/internal/telemetryMessages.js.map +1 -1
  92. package/dist/utils/telemetry/attributes.d.ts +5 -4
  93. package/dist/utils/telemetry/attributes.d.ts.map +1 -1
  94. package/dist/utils/telemetry/attributes.js +5 -4
  95. package/dist/utils/telemetry/attributes.js.map +1 -1
  96. package/framework-skills/add-service/SKILL.md +3 -12
  97. package/framework-skills/add-test/SKILL.md +6 -3
  98. package/framework-skills/add-tool/SKILL.md +29 -33
  99. package/framework-skills/api-config/SKILL.md +2 -1
  100. package/framework-skills/api-context/SKILL.md +155 -40
  101. package/framework-skills/api-errors/SKILL.md +43 -47
  102. package/framework-skills/api-linter/SKILL.md +5 -29
  103. package/framework-skills/api-telemetry/SKILL.md +11 -7
  104. package/framework-skills/api-testing/SKILL.md +43 -11
  105. package/framework-skills/api-workers/SKILL.md +3 -1
  106. package/framework-skills/design-mcp-server/SKILL.md +5 -5
  107. package/framework-skills/field-test/SKILL.md +2 -2
  108. package/framework-skills/polish-docs-meta/SKILL.md +2 -2
  109. package/framework-skills/release-and-publish/SKILL.md +3 -3
  110. package/framework-skills/release-pr-review/SKILL.md +2 -2
  111. package/framework-skills/security-pass/SKILL.md +8 -7
  112. package/package.json +4 -3
  113. package/scripts/install-otel.ts +84 -0
  114. package/scripts/lint-packaging.ts +188 -27
  115. package/templates/.env.example +2 -0
  116. package/templates/AGENTS.md +5 -4
  117. package/templates/CLAUDE.md +5 -4
  118. package/templates/Dockerfile +67 -50
  119. package/templates/src/mcp-server/tools/definitions/echo.tool.ts +3 -8
@@ -1 +1 @@
1
- {"version":3,"file":"attributes.js","sourceRoot":"","sources":["../../../src/utils/telemetry/attributes.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,+EAA+E;AAC/E,yDAAyD;AACzD,+EAA+E;AAE/E;;;;GAIG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,oBAAoB,CAAC;AAE5D,iGAAiG;AACjG,MAAM,CAAC,MAAM,mBAAmB,GAAG,gBAAgB,CAAC;AAEpD,+EAA+E;AAC/E,gCAAgC;AAChC,+EAA+E;AAE/E;;GAEG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,eAAe,CAAC;AAElD,0DAA0D;AAC1D,MAAM,CAAC,MAAM,yBAAyB,GAAG,sBAAsB,CAAC;AAEhE,wDAAwD;AACxD,MAAM,CAAC,MAAM,0BAA0B,GAAG,uBAAuB,CAAC;AAElE,yEAAyE;AACzE,MAAM,CAAC,MAAM,yBAAyB,GAAG,sBAAsB,CAAC;AAEhE;;;;GAIG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,kBAAkB,CAAC;AAExD;;;;GAIG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,yBAAyB,CAAC;AAEtE;;;GAGG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,qBAAqB,CAAC;AAE9D,yGAAyG;AACzG,MAAM,CAAC,MAAM,4BAA4B,GAAG,yBAAyB,CAAC;AAEtE;;;;GAIG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,kBAAkB,CAAC;AAExD,iGAAiG;AACjG,MAAM,CAAC,MAAM,6BAA6B,GAAG,0BAA0B,CAAC;AAExE,sFAAsF;AACtF,MAAM,CAAC,MAAM,sBAAsB,GAAG,mBAAmB,CAAC;AAE1D,uEAAuE;AACvE,MAAM,CAAC,MAAM,6BAA6B,GAAG,gCAAgC,CAAC;AAE9E,oEAAoE;AACpE,MAAM,CAAC,MAAM,0BAA0B,GAAG,6BAA6B,CAAC;AAExE,+EAA+E;AAC/E,2CAA2C;AAC3C,+EAA+E;AAE/E,4EAA4E;AAC5E,gFAAgF;AAChF,+EAA+E;AAC/E,+EAA+E;AAC/E,gFAAgF;AAEhF;;;;GAIG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,uBAAuB,CAAC;AAElE,uFAAuF;AACvF,MAAM,CAAC,MAAM,qBAAqB,GAAG,kBAAkB,CAAC;AAExD,uEAAuE;AACvE,MAAM,CAAC,MAAM,yBAAyB,GAAG,sBAAsB,CAAC;AAEhE;;;;GAIG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,oBAAoB,CAAC;AAE5D,+EAA+E;AAC/E,gCAAgC;AAChC,+EAA+E;AAE/E;;;;;GAKG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,gBAAgB,CAAC;AAEpD,+EAA+E;AAC/E,0BAA0B;AAC1B,+EAA+E;AAE/E;;;GAGG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,kBAAkB,CAAC;AAExD,gFAAgF;AAChF,MAAM,CAAC,MAAM,sBAAsB,GAAG,mBAAmB,CAAC;AAE1D,kFAAkF;AAClF,MAAM,CAAC,MAAM,2BAA2B,GAAG,wBAAwB,CAAC;AAEpE,iEAAiE;AACjE,MAAM,CAAC,MAAM,4BAA4B,GAAG,yBAAyB,CAAC;AAEtE,mEAAmE;AACnE,MAAM,CAAC,MAAM,6BAA6B,GAAG,0BAA0B,CAAC;AAExE;;;GAGG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,sBAAsB,CAAC;AAEhE,gGAAgG;AAChG,MAAM,CAAC,MAAM,gCAAgC,GAAG,6BAA6B,CAAC;AAE9E,iGAAiG;AACjG,MAAM,CAAC,MAAM,4BAA4B,GAAG,yBAAyB,CAAC;AAEtE,+EAA+E;AAC/E,wBAAwB;AACxB,+EAA+E;AAE/E,+EAA+E;AAC/E,MAAM,CAAC,MAAM,oBAAoB,GAAG,iBAAiB,CAAC;AAEtD,6DAA6D;AAC7D,MAAM,CAAC,MAAM,2BAA2B,GAAG,wBAAwB,CAAC;AAEpE,6DAA6D;AAC7D,MAAM,CAAC,MAAM,4BAA4B,GAAG,yBAAyB,CAAC;AAEtE,uEAAuE;AACvE,MAAM,CAAC,MAAM,6BAA6B,GAAG,0BAA0B,CAAC;AAExE,yFAAyF;AACzF,MAAM,CAAC,MAAM,2BAA2B,GAAG,wBAAwB,CAAC;AAEpE;;;GAGG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,oBAAoB,CAAC;AAE5D,6FAA6F;AAC7F,MAAM,CAAC,MAAM,8BAA8B,GAAG,2BAA2B,CAAC;AAE1E,+FAA+F;AAC/F,MAAM,CAAC,MAAM,0BAA0B,GAAG,uBAAuB,CAAC;AAElE,+DAA+D;AAC/D,MAAM,CAAC,MAAM,8BAA8B,GAAG,2BAA2B,CAAC;AAE1E,+EAA+E;AAC/E,iCAAiC;AACjC,+EAA+E;AAE/E,yFAAyF;AACzF,MAAM,CAAC,MAAM,kBAAkB,GAAG,eAAe,CAAC;AAElD,uEAAuE;AACvE,MAAM,CAAC,MAAM,kBAAkB,GAAG,eAAe,CAAC;AAElD,+EAA+E;AAC/E,mCAAmC;AACnC,+EAA+E;AAE/E,0FAA0F;AAC1F,MAAM,CAAC,MAAM,sBAAsB,GAAG,mBAAmB,CAAC;AAE1D,+EAA+E;AAC/E,yBAAyB;AACzB,+EAA+E;AAE/E,2FAA2F;AAC3F,MAAM,CAAC,MAAM,0BAA0B,GAAG,uBAAuB,CAAC;AAElE,0FAA0F;AAC1F,MAAM,CAAC,MAAM,0BAA0B,GAAG,uBAAuB,CAAC;AAElE,oEAAoE;AACpE,MAAM,CAAC,MAAM,4BAA4B,GAAG,yBAAyB,CAAC;AAEtE,4DAA4D;AAC5D,MAAM,CAAC,MAAM,wBAAwB,GAAG,qBAAqB,CAAC;AAE9D,+EAA+E;AAC/E,mDAAmD;AACnD,2DAA2D;AAC3D,+EAA+E;AAE/E,qFAAqF;AACrF,MAAM,CAAC,MAAM,kBAAkB,GAAG,eAAe,CAAC;AAElD,oEAAoE;AACpE,MAAM,CAAC,MAAM,yBAAyB,GAAG,sBAAsB,CAAC;AAEhE,uDAAuD;AACvD,MAAM,CAAC,MAAM,8BAA8B,GAAG,2BAA2B,CAAC;AAE1E,sCAAsC;AACtC,MAAM,CAAC,MAAM,+BAA+B,GAAG,4BAA4B,CAAC;AAE5E,0CAA0C;AAC1C,MAAM,CAAC,MAAM,yBAAyB,GAAG,sBAAsB,CAAC;AAEhE,qDAAqD;AACrD,MAAM,CAAC,MAAM,6BAA6B,GAAG,0BAA0B,CAAC;AAExE,yEAAyE;AACzE,MAAM,CAAC,MAAM,0BAA0B,GAAG,uBAAuB,CAAC;AAElE,8CAA8C;AAC9C,MAAM,CAAC,MAAM,8BAA8B,GAAG,2BAA2B,CAAC;AAE1E,oDAAoD;AACpD,MAAM,CAAC,MAAM,+BAA+B,GAAG,4BAA4B,CAAC;AAE5E,qCAAqC;AACrC,MAAM,CAAC,MAAM,8BAA8B,GAAG,2BAA2B,CAAC;AAE1E,iDAAiD;AACjD,MAAM,CAAC,MAAM,sBAAsB,GAAG,mBAAmB,CAAC;AAE1D,+EAA+E;AAC/E,wBAAwB;AACxB,+EAA+E;AAE/E,mEAAmE;AACnE,MAAM,CAAC,MAAM,wBAAwB,GAAG,qBAAqB,CAAC;AAE9D,6CAA6C;AAC7C,MAAM,CAAC,MAAM,yBAAyB,GAAG,sBAAsB,CAAC;AAEhE,mEAAmE;AACnE,MAAM,CAAC,MAAM,2BAA2B,GAAG,wBAAwB,CAAC;AAEpE,2DAA2D;AAC3D,MAAM,CAAC,MAAM,uBAAuB,GAAG,oBAAoB,CAAC;AAE5D,qEAAqE;AACrE,MAAM,CAAC,MAAM,2BAA2B,GAAG,wBAAwB,CAAC;AAEpE,sEAAsE;AACtE,MAAM,CAAC,MAAM,4BAA4B,GAAG,yBAAyB,CAAC;AAEtE,+EAA+E;AAC/E,uBAAuB;AACvB,+EAA+E;AAE/E,qFAAqF;AACrF,MAAM,CAAC,MAAM,wBAAwB,GAAG,qBAAqB,CAAC;AAE9D,kEAAkE;AAClE,MAAM,CAAC,MAAM,0BAA0B,GAAG,uBAAuB,CAAC;AAElE,0DAA0D;AAC1D,MAAM,CAAC,MAAM,sBAAsB,GAAG,mBAAmB,CAAC;AAE1D,+EAA+E;AAC/E,sBAAsB;AACtB,+EAA+E;AAE/E,2DAA2D;AAC3D,MAAM,CAAC,MAAM,oBAAoB,GAAG,iBAAiB,CAAC;AAEtD,kEAAkE;AAClE,MAAM,CAAC,MAAM,qBAAqB,GAAG,kBAAkB,CAAC;AAExD,oGAAoG;AACpG,MAAM,CAAC,MAAM,4BAA4B,GAAG,yBAAyB,CAAC;AAEtE,kFAAkF;AAClF,MAAM,CAAC,MAAM,oBAAoB,GAAG,iBAAiB,CAAC;AAEtD,iEAAiE;AACjE,MAAM,CAAC,MAAM,qBAAqB,GAAG,kBAAkB,CAAC;AAExD,+EAA+E;AAC/E,wCAAwC;AACxC,+EAA+E;AAE/E,4EAA4E;AAC5E,MAAM,CAAC,MAAM,6BAA6B,GAAG,0BAA0B,CAAC;AAExE,+EAA+E;AAC/E,sCAAsC;AACtC,+EAA+E;AAE/E,mFAAmF;AACnF,MAAM,CAAC,MAAM,8BAA8B,GAAG,2BAA2B,CAAC;AAE1E;;;;GAIG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,oBAAoB,CAAC;AAE5D;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,oBAAoB,CAAC"}
1
+ {"version":3,"file":"attributes.js","sourceRoot":"","sources":["../../../src/utils/telemetry/attributes.ts"],"names":[],"mappings":"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"}
@@ -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.11"
7
+ version: "1.12"
8
8
  audience: external
9
9
  type: reference
10
10
  ---
@@ -231,7 +231,7 @@ Services don't declare `errors: [...]` contracts and don't have `ctx.fail` — t
231
231
  ```
232
232
 
233
233
  - **Tool/resource handlers bubble service errors unchanged** — the contract advertises the *advertised* failure surface, and any code thrown from a service still reaches the client correctly via the auto-classifier. The conformance lint scans handler source text only, so service-thrown codes aren't flagged.
234
- - **Carry contract `reason` via `data: { reason }`** when the calling tool declares an `errors[]` contract entry for this failure mode. Services can't call `ctx.fail`, but passing the reason in `data` flows through the auto-classifier untouched, so clients see the same `error.data.reason` they'd see from `ctx.fail` — no handler-side catch-and-rethrow needed:
234
+ - **Carry contract `reason` via `data: { reason }`** when the calling tool declares an `errors[]` contract entry for this failure mode. Services can't call `ctx.fail`, but passing the reason in `data` flows through the auto-classifier untouched, so clients see the same `error.data.reason` they'd see from `ctx.fail` — and the framework fills that entry's `recovery` as `data.recovery.hint` when the throw carries none. No handler-side catch-and-rethrow needed:
235
235
 
236
236
  ```ts
237
237
  // tool declares: errors: [{ reason: 'empty_expression', code: JsonRpcErrorCode.ValidationError,
@@ -241,16 +241,7 @@ Services don't declare `errors: [...]` contracts and don't have `ctx.fail` — t
241
241
 
242
242
  The tool's entry carries `thrownBy: 'service'` so `error-contract-unthrown` — which reads the handler body and cannot see this throw — skips it while still checking whatever the handler throws itself. Lint-only metadata; nothing at runtime reads it.
243
243
 
244
- - **Resolve contract `recovery` via `ctx.recoveryFor`** to land the contract's recovery hint on the wire without duplicating the string. Always-present on `Context`, returns `{}` when the calling tool has no matching reason — spread-safe regardless:
245
-
246
- ```ts
247
- throw validationError('Parse failed: ' + err.message, {
248
- reason: 'parse_failed',
249
- ...ctx.recoveryFor('parse_failed'), // resolves from caller's contract
250
- });
251
- ```
252
-
253
- The contract `recovery` (validated ≥5 words at lint time) is the single source of truth. Services that opt in via the resolver carry the same hint to the wire that handler-level `ctx.fail` callers do — no drift, no auto-population. For dynamic recovery (interpolating runtime values into the hint), pass an explicit `{ recovery: { hint: '…' } }` instead.
244
+ - **The contract `recovery` follows the reason.** The calling tool's declared `recovery` (validated ≥5 words at lint time) is the single source of truth, and the handler factory puts it on the wire for any failure carrying that reason — matched on the reason alone, whatever factory the service picked — so a service throw needs nothing beyond `{ reason }`. For dynamic recovery (interpolating runtime values into the hint), pass an explicit `{ recovery: { hint: '…' } }`, which always wins.
254
245
 
255
246
  ## API Efficiency
256
247
 
@@ -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.7"
7
+ version: "1.8"
8
8
  audience: external
9
9
  type: reference
10
10
  ---
@@ -36,7 +36,8 @@ 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** | Available on any mock context (tenant `'default'` unless `{ tenantId }` says otherwise). It runs the production storage path, so use storage-legal keys (`cache/v1/abc`, never `cache:v1:abc`) and assert TTL expiry with fake timers. |
39
- | **`ctx.requestInput` / `ctx.inputs`** | Two rounds. First round: assert the handler throws the input-required signal (`.rejects.toSatisfy(isInputRequiredSignal)`), or catch it and assert on `error.result.inputRequests`. Second round: seed `createMockContext({ inputResponses })` and assert the handler completes. Cover the decline/cancel branch too. |
39
+ | **`ctx.requestInput` / `ctx.inputs`** | Two rounds. First round: assert the handler throws the input-required signal (`.rejects.toSatisfy(isInputRequiredSignal)`), or catch it and assert on `error.result.inputRequests`. Second round: seed `createMockContext({ inputResponses })` and assert the handler completes. Cover the decline/cancel branch too. A consent gate that redeems a `ctx.state` record also needs that record written into the second round's context, and a replay case showing the spent record asks again (see `api-testing` § Mock inputs). |
40
+ | **`ctx.clientCapabilities`** | When the handler asks only if the client declared a capability, seed `createMockContext({ clientCapabilities })` with and without it and assert it asks in one case and falls through in the other. Seeding it also filters `inputResponses` to the declared kinds, as production does. |
40
41
  | **`ctx.signal`** | Pass `createMockContext({ signal: controller.signal })` and assert a long loop stops early rather than running to completion. |
41
42
  | **`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
43
  | **`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. |
@@ -222,6 +223,8 @@ it('does not re-ask after a decline', async () => {
222
223
  });
223
224
  ```
224
225
 
226
+ For a destructive consent gate, the second round completes only when the context holds the single-use record the first round stored, keyed by the `requestState` it returned — each mock context has its own `ctx.state`, so write the record into the second context before calling the handler. `api-testing` § Mock inputs has the full pattern, including the replay case.
227
+
225
228
  ### Cancellation test
226
229
 
227
230
  ```typescript
@@ -308,7 +311,7 @@ When scaffolding tests for an existing handler, use the Zod schemas to generate
308
311
  - [ ] Happy path tested with valid input → expected output
309
312
  - [ ] Error paths tested (at least one `.rejects.toThrow()`)
310
313
  - [ ] `format` function tested if defined
311
- - [ ] `createMockContext` options match handler's ctx usage (`tenantId`, `inputResponses`, `requestState`, `errors`, `signal`)
314
+ - [ ] `createMockContext` options match handler's ctx usage (`tenantId`, `inputResponses`, `requestState`, `clientCapabilities`, `errors`, `signal`)
312
315
  - [ ] Service re-initialized in `beforeEach` if handler depends on a service singleton
313
316
  - [ ] If handler has optional fields: tested with empty-string inner values (form-client simulation)
314
317
  - [ ] 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.31"
7
+ version: "2.32"
8
8
  audience: external
9
9
  type: reference
10
10
  ---
@@ -80,8 +80,8 @@ export const {{TOOL_EXPORT}} = tool('{{tool_name}}', {
80
80
  // `recovery` is required (≥ 5 words) — it's the agent's next move when this
81
81
  // failure fires. Forcing function for thoughtful guidance: placeholders like
82
82
  // "Try again." get flagged by the linter. The contract `recovery` is the
83
- // single source of truth for what flows to the wire — opt in at the throw
84
- // site by spreading `ctx.recoveryFor('reason')` into the `data` arg.
83
+ // single source of truth for what flows to the wire — the framework sends it
84
+ // with any failure carrying the reason and no hint of its own.
85
85
  errors: [
86
86
  { reason: 'queue_full', code: JsonRpcErrorCode.RateLimited,
87
87
  when: 'Local queue at capacity.', retryable: true,
@@ -95,10 +95,9 @@ export const {{TOOL_EXPORT}} = tool('{{tool_name}}', {
95
95
  // Without: throw via factories (`notFound`, `validationError`, …) or plain `Error`.
96
96
  const items = await search(input);
97
97
  if (queue.full()) {
98
- // Static recovery — resolve from the contract via ctx.recoveryFor('reason').
99
- // Single source of truth: the string lives in errors[] above; this spread
100
- // pulls it onto the wire so format()-only clients see the recovery hint.
101
- throw ctx.fail('queue_full', undefined, { ...ctx.recoveryFor('queue_full') });
98
+ // Static recovery — the string lives in errors[] above, and the framework
99
+ // puts it on both client surfaces as `data.recovery.hint`.
100
+ throw ctx.fail('queue_full');
102
101
  }
103
102
  // Surface what the agent reasons with — echoed query, true total — on BOTH
104
103
  // client surfaces, with no format() plumbing. An empty result is a notice,
@@ -137,29 +136,25 @@ A handler that needs something the caller didn't supply returns `ctx.requestInpu
137
136
  import { inputRequired, tool, z } from '@cyanheads/mcp-ts-core';
138
137
  import { validationError } from '@cyanheads/mcp-ts-core/errors';
139
138
 
140
- const Confirm = z.object({ confirm: z.boolean().describe('Whether to proceed.') });
139
+ const Choice = z.object({ region: z.enum(['us', 'eu']).describe('Region to deploy to.') });
141
140
 
142
141
  export const {{TOOL_EXPORT}} = tool('{{tool_name}}', {
143
142
  description: '{{TOOL_DESCRIPTION}}',
144
143
  input: z.object({ /* ... */ }),
145
144
  output: z.object({ /* ... */ }),
146
- annotations: { destructiveHint: true },
147
145
 
148
146
  handler(input, ctx) {
147
+ // A declined or cancelled prompt is a dead end — don't re-ask it.
148
+ const view = ctx.inputs.view('region');
149
+ if (view.kind === 'elicit' && view.action !== 'accept') {
150
+ throw validationError(`User ${view.action} the region prompt.`);
151
+ }
149
152
  // Read what a prior round collected before asking for anything.
150
- const answer = ctx.inputs.accepted('confirm', Confirm);
153
+ const answer = ctx.inputs.accepted('region', Choice);
151
154
  if (!answer) {
152
- // A declined or cancelled prompt is a dead end — don't re-ask it.
153
- const view = ctx.inputs.view('confirm');
154
- if (view.kind === 'elicit' && view.action !== 'accept') {
155
- throw validationError(`User ${view.action} the confirmation.`);
156
- }
157
155
  return ctx.requestInput({
158
156
  inputRequests: {
159
- confirm: inputRequired.elicit({
160
- message: `Proceed with ${input.target}?`,
161
- requestedSchema: Confirm,
162
- }),
157
+ region: inputRequired.elicit({ message: 'Which region?', requestedSchema: Choice }),
163
158
  },
164
159
  });
165
160
  }
@@ -169,6 +164,8 @@ export const {{TOOL_EXPORT}} = tool('{{tool_name}}', {
169
164
  });
170
165
  ```
171
166
 
167
+ **A confirmation before a destructive step is different.** `ctx.inputs` only carries response kinds the client declared, but a client that declared `elicitation` can still send an "accepted" answer on a call nothing asked, and any `requestState` replays within its lifetime. A consent gate stores `{ operation, clientId, subject, target, contentHash }` in `ctx.state` under a random id, sends only that id as `requestState`, redeems the record before anything else in the handler, and asks again on an unknown, used, or expired id or on any field that differs from this call — the full handler, and the concurrency limit an action that must not repeat has to design around, are in `api-context` § *Consent gates*. Pair it with `MCP_REQUEST_STATE_KEY` so a retry can only carry state this server minted.
168
+
172
169
  Write it as `return ctx.requestInput(...)` — the `never` return type makes it valid in return position for any output, and it is what lets TypeScript narrow the line below. Full reference (`inputRequired.elicitUrl` / `.createMessage` / `.listRoots`, `requestState`, decline handling): `framework-skills/api-context`.
173
170
 
174
171
  ### Registration
@@ -657,10 +654,9 @@ export const fetchArticles = tool('fetch_articles', {
657
654
  input: z.object({ pmids: z.array(z.string()).describe('PMIDs to fetch') }),
658
655
  output: z.object({ articles: z.array(ArticleSchema).describe('Resolved articles') }),
659
656
  async handler(input, ctx) {
660
- // Static recovery — ctx.recoveryFor pulls the contract recovery onto the wire.
661
- // The contract is the single source of truth; this spread surfaces it on the
662
- // wire so format()-only clients see the hint mirrored into content[] text.
663
- if (queue.full()) throw ctx.fail('queue_full', undefined, { ...ctx.recoveryFor('queue_full') });
657
+ // Static recovery — the framework fills the contract recovery onto the wire,
658
+ // mirrored into content[] text for format()-only clients.
659
+ if (queue.full()) throw ctx.fail('queue_full');
664
660
 
665
661
  const articles = await fetch(input.pmids);
666
662
  if (articles.length === 0) {
@@ -675,7 +671,7 @@ export const fetchArticles = tool('fetch_articles', {
675
671
  });
676
672
  ```
677
673
 
678
- **`ctx.recoveryFor(reason)`** resolves the contract's `recovery` string into the wire shape `{ recovery: { hint } }` — safe to spread into `data` so format()-only clients see the same recovery hint that structuredContent clients read. Always available on `Context` (no-op `{}` when no contract), strictly typed on `HandlerContext<R>` against the declared reasons. Use it for static recovery; pass `{ recovery: { hint: \`…${dynamic}…\` } }` directly when you need runtime context. The contract is the single source of truth — write the recovery once, lint validates it ≥5 words, the resolver carries it to every throw site.
674
+ **The declared `recovery` reaches the wire on its own.** When a failure whose `data.reason` names a contract entry leaves the handler without `data.recovery`, the framework sets `data.recovery.hint` to that entry's `recovery` — both client surfaces, the error log record, and `runToolContract` alike — whatever code it was thrown with. Pass `{ recovery: { hint: \`…${dynamic}…\` } }` when you need runtime context; a throw-site hint always wins. `ctx.recoveryFor(reason)` still resolves the entry into `{ recovery: { hint } }` for a hint that must ride the thrown error itself. The contract is the single source of truth — write the recovery once, lint validates it ≥5 words, the framework carries it to every failure with that reason.
679
675
 
680
676
  **Baseline codes** (`InternalError`, `ServiceUnavailable`, `Timeout`, `ValidationError`, `SerializationError`) bubble freely and don't need declaring. Wire-level behavior is identical when the contract is omitted, but you lose the type-checked `ctx.fail`, the `tools/list` advertisement, and conformance lint coverage — declare a contract whenever the tool has a domain-specific failure mode.
681
677
 
@@ -683,12 +679,12 @@ export const fetchArticles = tool('fetch_articles', {
683
679
 
684
680
  #### Service-layer throws
685
681
 
686
- API-wrapping tools usually delegate to a service: `await ncbi.fetch(input, ctx)`. The throw lives in the service, not the handler. Services accept `ctx` (the unified Context) so they can call `ctx.log`, `ctx.recoveryFor`, etc. The handler doesn't catch — it just bubbles, and the framework's auto-classifier preserves `data` on the wire.
682
+ API-wrapping tools usually delegate to a service: `await ncbi.fetch(input, ctx)`. The throw lives in the service, not the handler. Services accept `ctx` (the unified Context) so they can call `ctx.log`, `ctx.state`, etc. The handler doesn't catch — it just bubbles, and the framework's auto-classifier preserves `data` on the wire.
687
683
 
688
- The contract entry on the tool and the `data: { reason }` on the service throw need to use the **same reason string** so the two sides line up. `ctx.recoveryFor('reason')` resolves the contract recovery from the calling tool's `errors[]` — same single-source-of-truth pattern that works in handlers.
684
+ The contract entry on the tool and the `data: { reason }` on the service throw need to use the **same reason string** so the two sides line up. That reason is all it takes: the framework fills the calling tool's declared `recovery` onto the failure as it leaves the handler.
689
685
 
690
686
  ```typescript
691
- // service — receives ctx; passes data.reason and spreads ctx.recoveryFor
687
+ // service — receives ctx; passes data.reason
692
688
  import type { Context } from '@cyanheads/mcp-ts-core';
693
689
  import { serviceUnavailable } from '@cyanheads/mcp-ts-core/errors';
694
690
 
@@ -697,9 +693,8 @@ export class NcbiService {
697
693
  const response = await fetchWithRetry(...);
698
694
  if (!response.ok) {
699
695
  throw serviceUnavailable(`NCBI returned HTTP ${response.status}`, {
700
- reason: 'ncbi_unreachable',
696
+ reason: 'ncbi_unreachable', // the caller's declared recovery is filled from this
701
697
  status: response.status,
702
- ...ctx.recoveryFor('ncbi_unreachable'), // resolves from caller's contract
703
698
  });
704
699
  }
705
700
  return response.json();
@@ -719,7 +714,7 @@ export const fetchArticles = tool('fetch_articles', {
719
714
  });
720
715
  ```
721
716
 
722
- `ctx.recoveryFor` returns `{}` when the calling tool has no contract or the reason isn't declared, so the spread is always safe — services don't have to know which tool called them.
717
+ A tool that declares no matching entry gets no hint — the service doesn't have to know which tool called it.
723
718
 
724
719
  Add `thrownBy: 'service'` to a contract entry the service produces once the handler also throws one of its own. `error-contract-unthrown` reads the handler body alone: as soon as one literal `ctx.fail(` appears there, every declared reason the body does not name is flagged, and the marker is what tells the rule this one is thrown a layer down. Lint-only metadata — the entry stays typed, advertised, and thrown exactly as an unmarked one.
725
720
 
@@ -748,8 +743,9 @@ throw serviceUnavailable(`arXiv API returned HTTP ${status}. Retry in a few seco
748
743
  // clients (Claude Desktop) see the same guidance that structuredContent clients
749
744
  // (Claude Code) read from `error.data.recovery.hint`. A hint the message already
750
745
  // contains verbatim is dropped from the text rather than stated twice; it stays
751
- // on structuredContent regardless. `data.reason` and `data.retryable` render as
752
- // a closing `(reason … · not retryable)` line; other `data` keys reach
746
+ // on structuredContent regardless. `data.reason`, `data.retryable`, and the
747
+ // framework's own `data.requestId` render as a closing
748
+ // `(reason … · not retryable · request <id>)` line; other `data` keys reach
753
749
  // structuredContent only.
754
750
  import { invalidParams } from '@cyanheads/mcp-ts-core/errors';
755
751
  throw invalidParams(
@@ -886,7 +882,7 @@ return { items: hits };
886
882
  - [ ] `errors: [...]` contract declared for the tool's domain-specific failure modes — or block deleted if no domain failures apply (baseline codes bubble freely)
887
883
  - [ ] Error contract declared inline on this tool — not imported from a shared module, even when other tools have near-identical entries
888
884
  - [ ] Long loops check `ctx.signal.aborted` so a cancelled request (or a closed transport) stops the work
889
- - [ ] If the tool needs caller input it may not have been given: reads `ctx.inputs` first, requests only what is missing via `return ctx.requestInput(...)`, and treats a declined/cancelled response as terminal rather than re-asking
885
+ - [ ] If the tool needs caller input it may not have been given: reads `ctx.inputs` first, requests only what is missing via `return ctx.requestInput(...)`, and treats a declined/cancelled response as terminal rather than re-asking. A destructive confirmation redeems a `ctx.state` consent record bound to the operation, caller, and target (`api-context` § *Consent gates*) rather than trusting the answer alone
890
886
  - [ ] If tool returns unbounded arrays: pagination with total count, or `spillover()` / DataCanvas for *analytical* working sets (an agent would SQL them — not a discovery/search surface). If any tool emits a `canvas_id`, a `dataframe_query` tool is registered in the same server — a token with no query tool is dead output
891
887
  - [ ] If tool returns one large *document* (not a row set) that can overflow context: `outlineOnOverflow()` returns a `full | outline` union so the agent re-calls with `sections: [...]` — not one-sided truncation
892
888
  - [ ] If tool is feature-gated: evaluated whether `disabledTool()` wrapper is appropriate (present in manifest but uncallable)
@@ -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.22"
7
+ version: "1.23"
8
8
  audience: external
9
9
  type: reference
10
10
  ---
@@ -137,6 +137,7 @@ await createApp({ sessionMode: { default: 'stateful', require: 'stateful' } });
137
137
  | `MCP_AUTH_MODE` | `mcpAuthMode` | `none` | `none` \| `jwt` \| `oauth` |
138
138
  | `MCP_AUTH_SECRET_KEY` | `mcpAuthSecretKey` | — | Required for `jwt` mode; min 32 chars |
139
139
  | `MCP_AUTH_DISABLE_SCOPE_CHECKS` | `mcpAuthDisableScopeChecks` | `false` | When `true`, bypasses both `withRequiredScopes` (declared `auth: [...]`) and `checkScopes` (runtime/tenant scopes). Token validation (sig/aud/iss/exp) intact. Logs a `WARNING` at startup. See `api-auth` skill. |
140
+ | `MCP_REQUEST_STATE_KEY` | `mcpRequestStateKey` | — | Opt-in, any auth mode. When set, the framework seals the `requestState` a handler returns with `ctx.requestInput` (bound to the request's `clientId` / `subject` / `tenantId`, valid 900 s) and every server instance rejects any other state as `-32602` `invalid_request_state` before the handler runs; `ctx.inputs.state()` still returns the handler's string. Must be ≥ 32 UTF-8 bytes — shorter fails startup with a `ConfigurationError` naming the variable — and identical on every instance a retry can reach (stateless replicas, Worker isolates, restarts). Unset or empty: no verifier, state round-trips raw. See `api-context` § `requestState` |
140
141
  | `OAUTH_ISSUER_URL` | `oauthIssuerUrl` | — | Required for `oauth` mode |
141
142
  | `OAUTH_AUDIENCE` | `oauthAudience` | — | Required for `oauth` mode |
142
143
  | `OAUTH_JWKS_URI` | `oauthJwksUri` | — | Override JWKS endpoint (otherwise derived from issuer) |