@cyanheads/mcp-ts-core 0.13.2 → 0.13.4

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 (117) hide show
  1. package/AGENTS.md +12 -10
  2. package/CLAUDE.md +12 -10
  3. package/README.md +2 -2
  4. package/changelog/0.13.x/0.13.3.md +44 -0
  5. package/changelog/0.13.x/0.13.4.md +65 -0
  6. package/changelog/template.md +7 -7
  7. package/dist/config/appRoot.d.ts.map +1 -1
  8. package/dist/config/appRoot.js +48 -16
  9. package/dist/config/appRoot.js.map +1 -1
  10. package/dist/core/app.d.ts +20 -0
  11. package/dist/core/app.d.ts.map +1 -1
  12. package/dist/core/app.js +1 -0
  13. package/dist/core/app.js.map +1 -1
  14. package/dist/core/index.d.ts +1 -0
  15. package/dist/core/index.d.ts.map +1 -1
  16. package/dist/core/index.js.map +1 -1
  17. package/dist/linter/rules/schema-rules.d.ts +19 -0
  18. package/dist/linter/rules/schema-rules.d.ts.map +1 -1
  19. package/dist/linter/rules/schema-rules.js +36 -0
  20. package/dist/linter/rules/schema-rules.js.map +1 -1
  21. package/dist/linter/rules/tool-rules.d.ts +17 -0
  22. package/dist/linter/rules/tool-rules.d.ts.map +1 -1
  23. package/dist/linter/rules/tool-rules.js +97 -1
  24. package/dist/linter/rules/tool-rules.js.map +1 -1
  25. package/dist/mcp-server/handlerContext.d.ts +6 -0
  26. package/dist/mcp-server/handlerContext.d.ts.map +1 -1
  27. package/dist/mcp-server/handlerContext.js.map +1 -1
  28. package/dist/mcp-server/resources/utils/resourceHandlerFactory.d.ts.map +1 -1
  29. package/dist/mcp-server/resources/utils/resourceHandlerFactory.js +17 -9
  30. package/dist/mcp-server/resources/utils/resourceHandlerFactory.js.map +1 -1
  31. package/dist/mcp-server/tools/utils/inputPrevalidation.d.ts +114 -0
  32. package/dist/mcp-server/tools/utils/inputPrevalidation.d.ts.map +1 -0
  33. package/dist/mcp-server/tools/utils/inputPrevalidation.js +428 -0
  34. package/dist/mcp-server/tools/utils/inputPrevalidation.js.map +1 -0
  35. package/dist/mcp-server/tools/utils/strictenRecord.d.ts +42 -0
  36. package/dist/mcp-server/tools/utils/strictenRecord.d.ts.map +1 -0
  37. package/dist/mcp-server/tools/utils/strictenRecord.js +48 -0
  38. package/dist/mcp-server/tools/utils/strictenRecord.js.map +1 -0
  39. package/dist/mcp-server/tools/utils/toolDefinition.d.ts +27 -0
  40. package/dist/mcp-server/tools/utils/toolDefinition.d.ts.map +1 -1
  41. package/dist/mcp-server/tools/utils/toolDefinition.js +35 -6
  42. package/dist/mcp-server/tools/utils/toolDefinition.js.map +1 -1
  43. package/dist/mcp-server/tools/utils/toolHandlerFactory.d.ts +28 -4
  44. package/dist/mcp-server/tools/utils/toolHandlerFactory.d.ts.map +1 -1
  45. package/dist/mcp-server/tools/utils/toolHandlerFactory.js +245 -23
  46. package/dist/mcp-server/tools/utils/toolHandlerFactory.js.map +1 -1
  47. package/dist/services/canvas/core/sqlGate.d.ts +14 -1
  48. package/dist/services/canvas/core/sqlGate.d.ts.map +1 -1
  49. package/dist/services/canvas/core/sqlGate.js +69 -7
  50. package/dist/services/canvas/core/sqlGate.js.map +1 -1
  51. package/dist/services/canvas/index.d.ts +1 -1
  52. package/dist/services/canvas/index.d.ts.map +1 -1
  53. package/dist/services/canvas/index.js +1 -1
  54. package/dist/services/canvas/index.js.map +1 -1
  55. package/dist/services/canvas/providers/duckdb/DuckdbProvider.d.ts +20 -0
  56. package/dist/services/canvas/providers/duckdb/DuckdbProvider.d.ts.map +1 -1
  57. package/dist/services/canvas/providers/duckdb/DuckdbProvider.js +75 -25
  58. package/dist/services/canvas/providers/duckdb/DuckdbProvider.js.map +1 -1
  59. package/dist/services/canvas/types.d.ts +6 -1
  60. package/dist/services/canvas/types.d.ts.map +1 -1
  61. package/dist/types-global/errors.d.ts.map +1 -1
  62. package/dist/types-global/errors.js +4 -3
  63. package/dist/types-global/errors.js.map +1 -1
  64. package/dist/utils/index.d.ts +3 -2
  65. package/dist/utils/index.d.ts.map +1 -1
  66. package/dist/utils/index.js +3 -2
  67. package/dist/utils/index.js.map +1 -1
  68. package/dist/utils/internal/error-handler/errorHandler.d.ts +33 -0
  69. package/dist/utils/internal/error-handler/errorHandler.d.ts.map +1 -1
  70. package/dist/utils/internal/error-handler/errorHandler.js +45 -3
  71. package/dist/utils/internal/error-handler/errorHandler.js.map +1 -1
  72. package/dist/utils/network/httpError.d.ts +13 -2
  73. package/dist/utils/network/httpError.d.ts.map +1 -1
  74. package/dist/utils/network/httpError.js +4 -2
  75. package/dist/utils/network/httpError.js.map +1 -1
  76. package/dist/utils/network/pacer.d.ts +117 -0
  77. package/dist/utils/network/pacer.d.ts.map +1 -0
  78. package/dist/utils/network/pacer.js +304 -0
  79. package/dist/utils/network/pacer.js.map +1 -0
  80. package/dist/utils/network/retry.d.ts +119 -3
  81. package/dist/utils/network/retry.d.ts.map +1 -1
  82. package/dist/utils/network/retry.js +176 -35
  83. package/dist/utils/network/retry.js.map +1 -1
  84. package/dist/utils/security/rateLimiter.d.ts +19 -1
  85. package/dist/utils/security/rateLimiter.d.ts.map +1 -1
  86. package/dist/utils/security/rateLimiter.js +49 -1
  87. package/dist/utils/security/rateLimiter.js.map +1 -1
  88. package/dist/utils/telemetry/attributes.d.ts +19 -0
  89. package/dist/utils/telemetry/attributes.d.ts.map +1 -1
  90. package/dist/utils/telemetry/attributes.js +30 -0
  91. package/dist/utils/telemetry/attributes.js.map +1 -1
  92. package/framework-skills/add-tool/SKILL.md +43 -2
  93. package/framework-skills/api-canvas/SKILL.md +8 -4
  94. package/framework-skills/api-config/SKILL.md +4 -4
  95. package/framework-skills/api-errors/SKILL.md +19 -10
  96. package/framework-skills/api-linter/SKILL.md +48 -3
  97. package/framework-skills/api-telemetry/SKILL.md +26 -2
  98. package/framework-skills/api-utils/SKILL.md +7 -3
  99. package/framework-skills/api-utils/references/security.md +2 -2
  100. package/framework-skills/design-mcp-server/SKILL.md +17 -2
  101. package/framework-skills/field-test/SKILL.md +3 -1
  102. package/framework-skills/git-wrapup/SKILL.md +90 -68
  103. package/framework-skills/orchestrations/SKILL.md +3 -3
  104. package/framework-skills/release-and-publish/SKILL.md +10 -10
  105. package/framework-skills/release-pr-review/SKILL.md +19 -26
  106. package/framework-skills/report-issue-framework/SKILL.md +6 -35
  107. package/framework-skills/report-issue-local/SKILL.md +6 -36
  108. package/framework-skills/tool-defs-analysis/SKILL.md +4 -4
  109. package/package.json +7 -7
  110. package/templates/.github/ISSUE_TEMPLATE/bug_report.yml +2 -2
  111. package/templates/.github/ISSUE_TEMPLATE/feature_request.yml +2 -2
  112. package/templates/.github/workflows/codeql.yml +39 -0
  113. package/templates/AGENTS.md +3 -1
  114. package/templates/CLAUDE.md +3 -1
  115. package/templates/changelog/template.md +7 -7
  116. package/templates/package.json +2 -2
  117. package/templates/src/index.ts +4 -3
@@ -0,0 +1,114 @@
1
+ /**
2
+ * @fileoverview The ordered step raw `tools/call` arguments pass through before
3
+ * — and, for one stage, after — the `input` schema parses them.
4
+ *
5
+ * Strict inputs (#232) reject an unrecognized root key by name, which is right
6
+ * for a misspelling the caller can fix and wrong for the three cases collected
7
+ * here, where the arguments the model wrote are correct and something between
8
+ * the model and the schema is not:
9
+ *
10
+ * 1. **Client-added keys (#453)** — a placeholder, a call description, a call
11
+ * id, or an `arguments`-level `_meta` block the model never wrote and cannot
12
+ * remove. Dropped before parsing.
13
+ * 2. **Key aliases (#452)** — a declared `inputAliases` entry, or the same name
14
+ * in another case style (`max_results` for `maxResults`). Rewritten to the
15
+ * canonical key before parsing. A one-to-one mapping fixed ahead of time, not
16
+ * the nearest-key guess #232 rejected.
17
+ * 3. **Representation repair (#234)** — a JSON-stringified array where an array
18
+ * was declared. Applied only *after* the parse has already failed, and kept
19
+ * only when it flips the arguments from invalid to valid: the author's schema
20
+ * is the sole arbiter, so a repair can never touch input that was already
21
+ * valid.
22
+ *
23
+ * All three are on by default and each has a server-level switch on
24
+ * `createApp({ input })` / `createWorkerHandler({ input })`. None of them
25
+ * changes what `tools/list` advertises, and none appears in a response: a drop,
26
+ * a rewrite, and a repair each emit one debug log and one counter increment
27
+ * instead.
28
+ *
29
+ * The split between the two is deliberate. Counter attributes are bounded and
30
+ * author- or framework-defined — the ignore-list entry that matched, the
31
+ * declared key a rewrite resolved to — because a metric label carrying the
32
+ * caller's own key text mints a permanent time series per spelling a client
33
+ * invents (#114). The raw key and alias go to the debug log, which is where an
34
+ * operator looks when a counter shows a new client artifact and where
35
+ * cardinality costs nothing.
36
+ *
37
+ * @module src/mcp-server/tools/utils/inputPrevalidation
38
+ */
39
+ import { type RequestContext } from '../../../utils/internal/requestContext.js';
40
+ import type { AnyToolDefinition } from './toolDefinition.js';
41
+ /**
42
+ * Server-level switches for the pre-validation step, set once via
43
+ * `createApp({ input })`. Every stage is on with no configuration; an entry
44
+ * here either extends a stage or turns it off for the whole server. There is no
45
+ * per-tool switch — a definition's only say is the `inputAliases` it declares.
46
+ */
47
+ export interface InputHandlingOptions {
48
+ /**
49
+ * Rewrite an undeclared root key whose case-folded form (`-`/`_` stripped,
50
+ * lowercased) matches exactly one declared key. `false` leaves declared
51
+ * `inputAliases` working and restores rejection for undeclared variants.
52
+ * Default `true`.
53
+ */
54
+ caseStyleAliases?: boolean;
55
+ /**
56
+ * Retry a failed parse once against repaired argument *values* — a
57
+ * JSON-stringified array parsed back into an array. `false` restores the
58
+ * single-parse behavior exactly. Default `true`.
59
+ */
60
+ coerce?: boolean;
61
+ /**
62
+ * Additional root keys to drop when the tool does not declare them, on top of
63
+ * the built-in client artifacts (`_meta`, `tool_call_description`,
64
+ * `toolCallId`). `false` disables the stage, so every undeclared key is
65
+ * rejected by name as it was before. Default: the built-in list only.
66
+ */
67
+ ignoreKeys?: readonly string[] | false;
68
+ }
69
+ /** One increment per repaired call, not per repaired value. */
70
+ export declare function countCoerced(toolName: string, context: RequestContext | undefined): void;
71
+ /**
72
+ * `Max-Results`, `max_results`, and `maxResults` all fold to `maxresults`.
73
+ *
74
+ * Exported so `lint:mcp` decides alias ambiguity by the same fold the rewrite
75
+ * uses — a rule computing its own would pass a definition the runtime declines
76
+ * to rewrite, or fail one it rewrites happily.
77
+ */
78
+ export declare function foldArgumentKey(key: string): string;
79
+ /**
80
+ * Runs the pre-parse half of the step — drop client-added keys, then rewrite
81
+ * key aliases — and returns the arguments to parse.
82
+ *
83
+ * Returns the caller's own object unchanged when nothing applied, so the common
84
+ * path allocates nothing. Non-object arguments are handed straight back: the
85
+ * schema's own rejection already says what arrived.
86
+ */
87
+ export declare function prevalidateToolArguments(def: AnyToolDefinition, args: unknown, options: InputHandlingOptions | undefined, context: RequestContext | undefined): unknown;
88
+ /**
89
+ * Parses back a string whose trimmed form is a JSON array, at each path the
90
+ * failed parse's issues named. Keys are never added, dropped, or renamed, and
91
+ * a value no issue points at is returned untouched.
92
+ *
93
+ * **Targeted, not a full walk.** A rejection names exactly the values the schema
94
+ * could not accept, and repairing anything else loses repairs that should have
95
+ * succeeded: one call carrying a stringified array for an array field *and* a
96
+ * free-text field legitimately holding `"[1,2]"` would have both rewritten, the
97
+ * re-parse would fail on the free-text field, and the whole call would be
98
+ * rejected over a value that was valid all along. An `invalid_union` issue's
99
+ * outer path counts like any other — that is the value the union rejected.
100
+ *
101
+ * `JSON.parse` is the exact inverse of the `JSON.stringify` that produced the
102
+ * value, so this undoes a known encoding rather than matching a nearest
103
+ * candidate — the distinction that makes it safe where nearest-key matching is
104
+ * not (#232). Safety does not rest on that alone: {@link parseToolArguments}
105
+ * runs this only after validation has already failed and keeps the result only
106
+ * when it then passes.
107
+ *
108
+ * Returns the argument itself when nothing was repairable, which is the signal
109
+ * the caller uses to skip the second parse.
110
+ */
111
+ export declare function repairRepresentations(args: unknown, issues: ReadonlyArray<{
112
+ readonly path: ReadonlyArray<PropertyKey>;
113
+ }>): unknown;
114
+ //# sourceMappingURL=inputPrevalidation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inputPrevalidation.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/utils/inputPrevalidation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAMH,OAAO,EACL,KAAK,cAAc,EAGpB,MAAM,oCAAoC,CAAC;AAW5C,OAAO,KAAK,EAAE,iBAAiB,EAAmB,MAAM,qBAAqB,CAAC;AAM9E;;;;;GAKG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,KAAK,CAAC;CACxC;AAoED,+DAA+D;AAC/D,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,SAAS,GAAG,IAAI,CAexF;AA8CD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEnD;AAyND;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,iBAAiB,EACtB,IAAI,EAAE,OAAO,EACb,OAAO,EAAE,oBAAoB,GAAG,SAAS,EACzC,OAAO,EAAE,cAAc,GAAG,SAAS,GAClC,OAAO,CAcT;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,OAAO,EACb,MAAM,EAAE,aAAa,CAAC;IAAE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC,WAAW,CAAC,CAAA;CAAE,CAAC,GACnE,OAAO,CAST"}
@@ -0,0 +1,428 @@
1
+ /**
2
+ * @fileoverview The ordered step raw `tools/call` arguments pass through before
3
+ * — and, for one stage, after — the `input` schema parses them.
4
+ *
5
+ * Strict inputs (#232) reject an unrecognized root key by name, which is right
6
+ * for a misspelling the caller can fix and wrong for the three cases collected
7
+ * here, where the arguments the model wrote are correct and something between
8
+ * the model and the schema is not:
9
+ *
10
+ * 1. **Client-added keys (#453)** — a placeholder, a call description, a call
11
+ * id, or an `arguments`-level `_meta` block the model never wrote and cannot
12
+ * remove. Dropped before parsing.
13
+ * 2. **Key aliases (#452)** — a declared `inputAliases` entry, or the same name
14
+ * in another case style (`max_results` for `maxResults`). Rewritten to the
15
+ * canonical key before parsing. A one-to-one mapping fixed ahead of time, not
16
+ * the nearest-key guess #232 rejected.
17
+ * 3. **Representation repair (#234)** — a JSON-stringified array where an array
18
+ * was declared. Applied only *after* the parse has already failed, and kept
19
+ * only when it flips the arguments from invalid to valid: the author's schema
20
+ * is the sole arbiter, so a repair can never touch input that was already
21
+ * valid.
22
+ *
23
+ * All three are on by default and each has a server-level switch on
24
+ * `createApp({ input })` / `createWorkerHandler({ input })`. None of them
25
+ * changes what `tools/list` advertises, and none appears in a response: a drop,
26
+ * a rewrite, and a repair each emit one debug log and one counter increment
27
+ * instead.
28
+ *
29
+ * The split between the two is deliberate. Counter attributes are bounded and
30
+ * author- or framework-defined — the ignore-list entry that matched, the
31
+ * declared key a rewrite resolved to — because a metric label carrying the
32
+ * caller's own key text mints a permanent time series per spelling a client
33
+ * invents (#114). The raw key and alias go to the debug log, which is where an
34
+ * operator looks when a counter shows a new client artifact and where
35
+ * cardinality costs nothing.
36
+ *
37
+ * @module src/mcp-server/tools/utils/inputPrevalidation
38
+ */
39
+ import { logger } from '../../../utils/internal/logger.js';
40
+ import { requestContextService, withExtra, } from '../../../utils/internal/requestContext.js';
41
+ import { ATTR_MCP_INPUT_ALIAS_KIND, ATTR_MCP_INPUT_COERCION, ATTR_MCP_INPUT_IGNORE_RULE, ATTR_MCP_INPUT_TARGET, ATTR_MCP_TOOL_NAME, } from '../../../utils/telemetry/attributes.js';
42
+ import { createCounter } from '../../../utils/telemetry/metrics.js';
43
+ import { scanHeaderDesignations } from './headerParam.js';
44
+ import { inputVariants, isDiscriminatedUnionSchema, zodDef } from './schemaShape.js';
45
+ /**
46
+ * Root keys known to be added by a client rather than written by the model, so
47
+ * rejecting them fails a call whose arguments were all correct. `_meta` belongs
48
+ * on `params`, not inside `arguments`; the other two are call bookkeeping.
49
+ */
50
+ const BUILT_IN_IGNORED_KEYS = ['_meta', 'tool_call_description', 'toolCallId'];
51
+ /** The one repair {@link repairRepresentations} performs, as the counter labels it. */
52
+ const COERCION_KIND_STRINGIFIED_ARRAY = 'stringified_array';
53
+ /**
54
+ * What the ignored-key counter reports for a key the underscore heuristic
55
+ * dropped. The key itself is caller-supplied and stays in the debug log.
56
+ */
57
+ const UNDERSCORE_RULE = 'underscore_prefix';
58
+ // ---------------------------------------------------------------------------
59
+ // Telemetry
60
+ // ---------------------------------------------------------------------------
61
+ let ignoredKeyCounter;
62
+ let aliasedCounter;
63
+ let coercedCounter;
64
+ /**
65
+ * Lazily created — a server whose callers never trip a stage emits no series.
66
+ *
67
+ * `rule` is the ignore-list entry that matched or {@link UNDERSCORE_RULE}, never
68
+ * the dropped key itself: the key is the caller's text, and one client inventing
69
+ * `_callId`, `_call_id`, `_callID` would mint three permanent series (#114). The
70
+ * ignore list is author- and framework-defined, so it is safe to name.
71
+ */
72
+ function countIgnoredKey(toolName, rule) {
73
+ ignoredKeyCounter ??= createCounter('mcp.input.ignored_key', 'Client-added root argument keys dropped before validation', '{keys}');
74
+ ignoredKeyCounter.add(1, {
75
+ [ATTR_MCP_TOOL_NAME]: toolName,
76
+ [ATTR_MCP_INPUT_IGNORE_RULE]: rule,
77
+ });
78
+ }
79
+ /**
80
+ * Labelled by the canonical *target* and which half of the stage fired — both
81
+ * bounded by the definition. The alias is the caller's text, and the case-style
82
+ * half accepts every `-`/`_`/case permutation of a declared key, so labelling it
83
+ * would put an unbounded set on a permanent series (#114).
84
+ */
85
+ function countAliased(toolName, target, kind) {
86
+ aliasedCounter ??= createCounter('mcp.input.aliased', 'Root argument keys rewritten to their canonical spelling', '{keys}');
87
+ aliasedCounter.add(1, {
88
+ [ATTR_MCP_TOOL_NAME]: toolName,
89
+ [ATTR_MCP_INPUT_TARGET]: target,
90
+ [ATTR_MCP_INPUT_ALIAS_KIND]: kind,
91
+ });
92
+ }
93
+ /** One increment per repaired call, not per repaired value. */
94
+ export function countCoerced(toolName, context) {
95
+ coercedCounter ??= createCounter('mcp.input.coerced', 'Tool calls that validated only after a representation repair', '{calls}');
96
+ coercedCounter.add(1, {
97
+ [ATTR_MCP_TOOL_NAME]: toolName,
98
+ [ATTR_MCP_INPUT_COERCION]: COERCION_KIND_STRINGIFIED_ARRAY,
99
+ });
100
+ debugLog(`Tool '${toolName}': arguments validated after repairing a stringified array.`, context, { toolName, coercion: COERCION_KIND_STRINGIFIED_ARRAY });
101
+ }
102
+ /**
103
+ * The step's own debug channel. A pre-validation decision is invisible in the
104
+ * response by design, so the log is the only place a server operator sees that
105
+ * a key was dropped or moved.
106
+ */
107
+ function debugLog(message, context, extra) {
108
+ const base = context ?? requestContextService.createRequestContext({ operation: 'ToolInputPrevalidation' });
109
+ logger.debug(message, withExtra(base, extra));
110
+ }
111
+ const plans = new WeakMap();
112
+ /**
113
+ * `Max-Results`, `max_results`, and `maxResults` all fold to `maxresults`.
114
+ *
115
+ * Exported so `lint:mcp` decides alias ambiguity by the same fold the rewrite
116
+ * uses — a rule computing its own would pass a definition the runtime declines
117
+ * to rewrite, or fail one it rewrites happily.
118
+ */
119
+ export function foldArgumentKey(key) {
120
+ return key.replaceAll(/[-_]/g, '').toLowerCase();
121
+ }
122
+ /**
123
+ * True when the author opened the object themselves. `.strict()` also records a
124
+ * catchall — a `ZodNever` one — so the type of the catchall, not its presence,
125
+ * is what separates an open root from a strict one.
126
+ */
127
+ function isOpenObject(schema) {
128
+ const catchall = schema.def.catchall;
129
+ return catchall !== undefined && zodDef(catchall)?.type !== 'never';
130
+ }
131
+ function buildVariantPlan(schema) {
132
+ const keys = new Set(Object.keys(schema.shape));
133
+ const folded = new Map();
134
+ for (const key of keys) {
135
+ const fold = foldArgumentKey(key);
136
+ folded.set(fold, folded.has(fold) ? null : key);
137
+ }
138
+ return { folded, keys, open: isOpenObject(schema) };
139
+ }
140
+ /**
141
+ * Derives the plan for a definition's input root, memoized on the root schema —
142
+ * the header scan emits JSON Schema, which is far too much work to repeat per
143
+ * call.
144
+ */
145
+ function planFor(input) {
146
+ const cached = plans.get(input);
147
+ if (cached)
148
+ return cached;
149
+ const variants = inputVariants(input).map((schema) => ({
150
+ plan: buildVariantPlan(schema),
151
+ schema,
152
+ }));
153
+ const allKeys = new Set();
154
+ for (const variant of variants)
155
+ for (const key of variant.plan.keys)
156
+ allKeys.add(key);
157
+ // A union root can carry no valid designation at all (every field sits under
158
+ // `oneOf`), and an invalid one already failed in `tool()`, so this only ever
159
+ // collects root properties of an object root.
160
+ const scan = scanHeaderDesignations(input);
161
+ const headerTargets = new Set(scan?.valid
162
+ ? scan.designations.flatMap(({ path }) => {
163
+ const step = path.length === 1 ? path[0] : undefined;
164
+ return step?.kind === 'property' ? [step.key] : [];
165
+ })
166
+ : []);
167
+ const discriminator = isDiscriminatedUnionSchema(input)
168
+ ? zodDef(input)?.discriminator
169
+ : undefined;
170
+ const plan = {
171
+ allKeys,
172
+ anyOpen: variants.some((variant) => variant.plan.open),
173
+ discriminator,
174
+ headerTargets,
175
+ underscoreDeclared: [...allKeys].some((key) => key.startsWith('_')),
176
+ variants,
177
+ };
178
+ plans.set(input, plan);
179
+ return plan;
180
+ }
181
+ /**
182
+ * The variant the arguments select, or `undefined` when nothing selects one.
183
+ *
184
+ * An object root has exactly one. A union root has no single declared-key set
185
+ * until the discriminator is read, and a call whose discriminator is absent or
186
+ * unrecognized fails on the discriminator either way — so the rewrite stage
187
+ * declines rather than guessing a branch.
188
+ */
189
+ function selectVariant(plan, args) {
190
+ if (plan.discriminator === undefined)
191
+ return plan.variants[0]?.plan;
192
+ if (!Object.hasOwn(args, plan.discriminator))
193
+ return undefined;
194
+ const value = args[plan.discriminator];
195
+ for (const variant of plan.variants) {
196
+ const field = variant.schema.shape[plan.discriminator];
197
+ if (field?.safeParse(value).success)
198
+ return variant.plan;
199
+ }
200
+ return undefined;
201
+ }
202
+ // ---------------------------------------------------------------------------
203
+ // Stage 1 — drop client-added keys (#453)
204
+ // ---------------------------------------------------------------------------
205
+ /**
206
+ * Drops a root key the tool does not declare when it is either underscore-
207
+ * prefixed or on the ignore list. Three boundaries:
208
+ *
209
+ * - **A declared key is never dropped**, including a declared underscore-prefixed
210
+ * one. On a union root "declared" is every variant's root keys, since the
211
+ * matching variant is unknown until the discriminator is read.
212
+ * - **An author-opened root is untouched** — its extra keys reach the handler
213
+ * today, and dropping them would delete data the author accepts on purpose.
214
+ * - **The underscore rule is off for a tool declaring any underscore-prefixed
215
+ * key.** Otherwise a misspelled `_cursor` would be silently dropped instead of
216
+ * rejected by name, which is the failure #232 removed. The ignore list still
217
+ * applies there.
218
+ */
219
+ function dropIgnoredKeys(def, args, plan, options, context) {
220
+ if (options?.ignoreKeys === false || plan.anyOpen)
221
+ return args;
222
+ const ignored = new Set([...BUILT_IN_IGNORED_KEYS, ...(options?.ignoreKeys ?? [])]);
223
+ const underscoreRule = !plan.underscoreDeclared;
224
+ let kept;
225
+ for (const key of Object.keys(args)) {
226
+ if (plan.allKeys.has(key))
227
+ continue;
228
+ const listed = ignored.has(key);
229
+ if (!listed && !(underscoreRule && key.startsWith('_')))
230
+ continue;
231
+ // Spread copies own properties by definition, so a caller's own `__proto__`
232
+ // survives as a key instead of re-prototyping the copy; `delete` on that own
233
+ // property is equally safe.
234
+ kept ??= { ...args };
235
+ delete kept[key];
236
+ countIgnoredKey(def.name, listed ? key : UNDERSCORE_RULE);
237
+ debugLog(`Tool '${def.name}': dropped client-added argument key '${key}'.`, context, {
238
+ toolName: def.name,
239
+ ignoredKey: key,
240
+ ignoreRule: listed ? key : UNDERSCORE_RULE,
241
+ });
242
+ }
243
+ return kept ?? args;
244
+ }
245
+ // ---------------------------------------------------------------------------
246
+ // Stage 2 — key aliases (#452)
247
+ // ---------------------------------------------------------------------------
248
+ /**
249
+ * Rewrites root keys to their canonical spelling in two passes: the definition's
250
+ * declared `inputAliases`, then — unless `caseStyleAliases: false` — any
251
+ * undeclared key whose case-folded form names exactly one declared key.
252
+ *
253
+ * A rewrite applies only when the target key is absent; with both present the
254
+ * arguments pass through and the strict rejection fires as it does today. An
255
+ * author-opened root is never rewritten (an unknown key there is already
256
+ * accepted verbatim), and a `headerParam`-designated target is never rewritten
257
+ * *to*: the SDK cross-checks the `Mcp-Param-<Name>` header against the raw body
258
+ * before dispatch, so a later rewrite would hand the handler a value no
259
+ * intermediary attested.
260
+ */
261
+ function applyAliases(def, args, plan, options, context) {
262
+ const declared = def.inputAliases;
263
+ const caseStyle = options?.caseStyleAliases !== false;
264
+ if (plan.anyOpen || (!declared && !caseStyle))
265
+ return args;
266
+ const variant = selectVariant(plan, args);
267
+ if (!variant)
268
+ return args;
269
+ let rewritten;
270
+ const current = () => rewritten ?? args;
271
+ const rewritable = (alias, target) => Object.hasOwn(current(), alias) &&
272
+ !variant.keys.has(alias) &&
273
+ variant.keys.has(target) &&
274
+ !plan.headerTargets.has(target) &&
275
+ !Object.hasOwn(current(), target);
276
+ const move = (alias, target, kind) => {
277
+ // `target` is a declared key read off the Zod shape, never caller text — an
278
+ // object literal resolves `__proto__` to the prototype setter rather than a
279
+ // key, so a shape cannot declare one and this assignment cannot reach it.
280
+ // `alias` is caller text, but it is only read and deleted, both own-property
281
+ // operations. The spread defines rather than assigns, so an own `__proto__`
282
+ // elsewhere in the arguments survives the copy.
283
+ rewritten ??= { ...args };
284
+ rewritten[target] = rewritten[alias];
285
+ delete rewritten[alias];
286
+ countAliased(def.name, target, kind);
287
+ debugLog(`Tool '${def.name}': rewrote argument key '${alias}' to '${target}'.`, context, {
288
+ toolName: def.name,
289
+ alias,
290
+ target,
291
+ aliasKind: kind,
292
+ });
293
+ };
294
+ for (const [alias, target] of Object.entries(declared ?? {})) {
295
+ if (rewritable(alias, target))
296
+ move(alias, target, 'declared');
297
+ }
298
+ if (caseStyle) {
299
+ // Snapshot first: `move` only ever removes an undeclared key and adds a
300
+ // declared one, so the remaining candidates are unaffected.
301
+ for (const key of Object.keys(current())) {
302
+ if (variant.keys.has(key))
303
+ continue;
304
+ const target = variant.folded.get(foldArgumentKey(key));
305
+ if (target && rewritable(key, target))
306
+ move(key, target, 'case_style');
307
+ }
308
+ }
309
+ return rewritten ?? args;
310
+ }
311
+ // ---------------------------------------------------------------------------
312
+ // Public entry points
313
+ // ---------------------------------------------------------------------------
314
+ /**
315
+ * Runs the pre-parse half of the step — drop client-added keys, then rewrite
316
+ * key aliases — and returns the arguments to parse.
317
+ *
318
+ * Returns the caller's own object unchanged when nothing applied, so the common
319
+ * path allocates nothing. Non-object arguments are handed straight back: the
320
+ * schema's own rejection already says what arrived.
321
+ */
322
+ export function prevalidateToolArguments(def, args, options, context) {
323
+ if (args === null || typeof args !== 'object' || Array.isArray(args))
324
+ return args;
325
+ const plan = planFor(def.input);
326
+ if (plan.variants.length === 0)
327
+ return args;
328
+ const record = args;
329
+ return applyAliases(def, dropIgnoredKeys(def, record, plan, options, context), plan, options, context);
330
+ }
331
+ /**
332
+ * Parses back a string whose trimmed form is a JSON array, at each path the
333
+ * failed parse's issues named. Keys are never added, dropped, or renamed, and
334
+ * a value no issue points at is returned untouched.
335
+ *
336
+ * **Targeted, not a full walk.** A rejection names exactly the values the schema
337
+ * could not accept, and repairing anything else loses repairs that should have
338
+ * succeeded: one call carrying a stringified array for an array field *and* a
339
+ * free-text field legitimately holding `"[1,2]"` would have both rewritten, the
340
+ * re-parse would fail on the free-text field, and the whole call would be
341
+ * rejected over a value that was valid all along. An `invalid_union` issue's
342
+ * outer path counts like any other — that is the value the union rejected.
343
+ *
344
+ * `JSON.parse` is the exact inverse of the `JSON.stringify` that produced the
345
+ * value, so this undoes a known encoding rather than matching a nearest
346
+ * candidate — the distinction that makes it safe where nearest-key matching is
347
+ * not (#232). Safety does not rest on that alone: {@link parseToolArguments}
348
+ * runs this only after validation has already failed and keeps the result only
349
+ * when it then passes.
350
+ *
351
+ * Returns the argument itself when nothing was repairable, which is the signal
352
+ * the caller uses to skip the second parse.
353
+ */
354
+ export function repairRepresentations(args, issues) {
355
+ let repaired = args;
356
+ for (const issue of issues) {
357
+ if (issue.path.length === 0)
358
+ continue;
359
+ const current = readAt(repaired, issue.path);
360
+ const next = repairValue(current);
361
+ if (next !== current)
362
+ repaired = writeAt(repaired, issue.path, next);
363
+ }
364
+ return repaired;
365
+ }
366
+ /** The one repair: a JSON-array string back into the array it encodes. */
367
+ function repairValue(value) {
368
+ if (typeof value !== 'string')
369
+ return value;
370
+ const trimmed = value.trim();
371
+ if (!trimmed.startsWith('['))
372
+ return value;
373
+ try {
374
+ const parsed = JSON.parse(trimmed);
375
+ return Array.isArray(parsed) ? parsed : value;
376
+ }
377
+ catch {
378
+ return value;
379
+ }
380
+ }
381
+ /** The value at `path`, or `undefined` when nothing owns one there. */
382
+ function readAt(root, path) {
383
+ let cursor = root;
384
+ for (const segment of path) {
385
+ if (cursor === null || typeof cursor !== 'object')
386
+ return undefined;
387
+ if (!Object.hasOwn(cursor, segment))
388
+ return undefined;
389
+ cursor = cursor[segment];
390
+ }
391
+ return cursor;
392
+ }
393
+ /**
394
+ * Replaces the value at `path`, copying each container along the way and
395
+ * leaving the caller's own objects untouched.
396
+ *
397
+ * Object copies go through `Object.fromEntries`, which *defines* each property
398
+ * rather than assigning it. `copy[key] = value` would route a caller's own
399
+ * `__proto__` key — which `JSON.parse` creates as an ordinary own data property
400
+ * — through `Object.prototype`'s setter: the key would vanish and the copy's
401
+ * prototype would become whatever the caller sent, which Zod then reads
402
+ * inherited values from. Defining keeps the key and the prototype, and keeps
403
+ * this function's promise that no key is added, dropped, or renamed.
404
+ */
405
+ function writeAt(root, path, value) {
406
+ const [head, ...rest] = path;
407
+ if (head === undefined)
408
+ return value;
409
+ if (Array.isArray(root)) {
410
+ const index = Number(head);
411
+ if (!Number.isInteger(index) || index < 0 || index >= root.length)
412
+ return root;
413
+ const copy = [...root];
414
+ copy[index] = writeAt(root[index], rest, value);
415
+ return copy;
416
+ }
417
+ if (root !== null && typeof root === 'object') {
418
+ const key = String(head);
419
+ if (!Object.hasOwn(root, key))
420
+ return root;
421
+ return Object.fromEntries(Object.entries(root).map(([entryKey, entryValue]) => [
422
+ entryKey,
423
+ entryKey === key ? writeAt(entryValue, rest, value) : entryValue,
424
+ ]));
425
+ }
426
+ return root;
427
+ }
428
+ //# sourceMappingURL=inputPrevalidation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inputPrevalidation.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/utils/inputPrevalidation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * @fileoverview What `strictenInput` threw away, kept where `lint:mcp` can read
3
+ * it (#358, #394).
4
+ *
5
+ * Zod 4 keys `.describe()` and `.meta()` to the schema **instance**, in
6
+ * `z.globalRegistry`, and `.strict()` is `catchall(z.never())` — a clone with no
7
+ * `_zod.parent` link, so it inherits no registry entry. `strictenInput` stores
8
+ * that clone, and whatever the author attached to the input root is gone:
9
+ * silently, since the linter reads the same stored schema the wire does and a
10
+ * root carries no describe anything would ask for.
11
+ *
12
+ * `tool()` is the only place both instances exist, so it records the discard
13
+ * here on the way past. The record is a symbol-keyed, non-enumerable property:
14
+ * invisible to `Object.keys`, `JSON.stringify`, a spread, `tools/list`, the
15
+ * server manifest and `_meta` alike, so nothing about what a client sees
16
+ * changes. The symbol comes from the global registry rather than a module
17
+ * binding, because `lint:mcp` can load the framework twice — the linter from
18
+ * the installed package, the definitions from wherever the server imports them
19
+ * — and a module-local key (or a `WeakMap`) would not be shared across the two.
20
+ *
21
+ * Re-applying the discarded entry to the strictened schema is separate, held
22
+ * work: it would change the advertised `inputSchema` bytes. This half changes
23
+ * none of them.
24
+ *
25
+ * @module src/mcp-server/tools/utils/strictenRecord
26
+ */
27
+ /** One schema instance whose registry entry `strictenInput` did not carry over. */
28
+ export interface StrictenDiscard {
29
+ /** The registry keys that were lost — `description` for `.describe()`, else `.meta()` keys. */
30
+ readonly keys: readonly string[];
31
+ /** Where it sat, in the linter's path vocabulary: `input`, or `input|<i>` for a variant. */
32
+ readonly scope: string;
33
+ }
34
+ /**
35
+ * Attaches the discards to a definition without making it part of the
36
+ * definition's value. Only called when something was actually discarded, so an
37
+ * unaffected definition carries no property at all.
38
+ */
39
+ export declare function recordStrictenDiscard(definition: object, discards: readonly StrictenDiscard[]): void;
40
+ /** The discards `tool()` recorded, or `undefined` when nothing was discarded. */
41
+ export declare function readStrictenDiscard(definition: unknown): readonly StrictenDiscard[] | undefined;
42
+ //# sourceMappingURL=strictenRecord.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"strictenRecord.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/utils/strictenRecord.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,mFAAmF;AACnF,MAAM,WAAW,eAAe;IAC9B,+FAA+F;IAC/F,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,4FAA4F;IAC5F,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAID;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,SAAS,eAAe,EAAE,GACnC,IAAI,CAON;AAED,iFAAiF;AACjF,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,OAAO,GAAG,SAAS,eAAe,EAAE,GAAG,SAAS,CAI/F"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * @fileoverview What `strictenInput` threw away, kept where `lint:mcp` can read
3
+ * it (#358, #394).
4
+ *
5
+ * Zod 4 keys `.describe()` and `.meta()` to the schema **instance**, in
6
+ * `z.globalRegistry`, and `.strict()` is `catchall(z.never())` — a clone with no
7
+ * `_zod.parent` link, so it inherits no registry entry. `strictenInput` stores
8
+ * that clone, and whatever the author attached to the input root is gone:
9
+ * silently, since the linter reads the same stored schema the wire does and a
10
+ * root carries no describe anything would ask for.
11
+ *
12
+ * `tool()` is the only place both instances exist, so it records the discard
13
+ * here on the way past. The record is a symbol-keyed, non-enumerable property:
14
+ * invisible to `Object.keys`, `JSON.stringify`, a spread, `tools/list`, the
15
+ * server manifest and `_meta` alike, so nothing about what a client sees
16
+ * changes. The symbol comes from the global registry rather than a module
17
+ * binding, because `lint:mcp` can load the framework twice — the linter from
18
+ * the installed package, the definitions from wherever the server imports them
19
+ * — and a module-local key (or a `WeakMap`) would not be shared across the two.
20
+ *
21
+ * Re-applying the discarded entry to the strictened schema is separate, held
22
+ * work: it would change the advertised `inputSchema` bytes. This half changes
23
+ * none of them.
24
+ *
25
+ * @module src/mcp-server/tools/utils/strictenRecord
26
+ */
27
+ const STRICTEN_DISCARD = Symbol.for('@cyanheads/mcp-ts-core:strictenDiscard');
28
+ /**
29
+ * Attaches the discards to a definition without making it part of the
30
+ * definition's value. Only called when something was actually discarded, so an
31
+ * unaffected definition carries no property at all.
32
+ */
33
+ export function recordStrictenDiscard(definition, discards) {
34
+ Object.defineProperty(definition, STRICTEN_DISCARD, {
35
+ configurable: true,
36
+ enumerable: false,
37
+ value: discards,
38
+ writable: false,
39
+ });
40
+ }
41
+ /** The discards `tool()` recorded, or `undefined` when nothing was discarded. */
42
+ export function readStrictenDiscard(definition) {
43
+ if (definition === null || typeof definition !== 'object')
44
+ return undefined;
45
+ const value = definition[STRICTEN_DISCARD];
46
+ return Array.isArray(value) ? value : undefined;
47
+ }
48
+ //# sourceMappingURL=strictenRecord.js.map