@ultimat3/mcp 7.0.0 → 9.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CLAUDE.md CHANGED
@@ -73,6 +73,15 @@ import. The CLI wires it.
73
73
  the raw error, zero frames written, the request unanswered and every later request on that buffer
74
74
  never processed. Same shape `toolsCall` uses — a framework error keeps its code/cause/fix, anything
75
75
  else is `-32603` with no internals.
76
+ - **A declared `pattern` is compiled once per schema NODE, never once per `tools/call`.** A tool's
77
+ schema is registered at boot and validated on every call, so `new RegExp(pattern)` in `string()`
78
+ was per-request work over a constant — `@ultimat3/schema`'s `patternTester` already draws the
79
+ line in the same place for the same contract. The memo is a `WeakMap` keyed on the node, not a
80
+ `Map` keyed on the pattern string: a process registering tools dynamically would otherwise
81
+ accumulate one entry per distinct pattern forever with nothing to evict it. An uncompilable
82
+ pattern caches its `null` verdict too — it is the branch with the highest per-call cost.
83
+ `compiledPatternCount()` is the test-only probe (not in `index.ts`); a count that climbs once
84
+ per CALL is the memo gone, which `validate-args.test.ts` asserts over 100 calls.
76
85
  - **`format` is NOT in the wire subset**, and `wire.ts` types it `never` so re-adding it does not
77
86
  compile. It names a rule whose meaning lives in `@ultimat3/schema` (`uuid`, `email`,
78
87
  `iana-time-zone`), and this package cannot check it without a second definition of each that can
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ultimat3/mcp",
3
- "version": "7.0.0",
3
+ "version": "9.0.0",
4
4
  "description": "MCP server, dev tools, and the action-to-tool projection — one authz system, two surfaces",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -31,12 +31,12 @@
31
31
  "test": "bun test"
32
32
  },
33
33
  "dependencies": {
34
- "@ultimat3/action": "7.0.0",
35
- "@ultimat3/core": "7.0.0",
36
- "@ultimat3/entity": "7.0.0",
37
- "@ultimat3/jobs": "7.0.0",
38
- "@ultimat3/policy": "7.0.0",
39
- "@ultimat3/query": "7.0.0",
40
- "@ultimat3/schema": "7.0.0"
34
+ "@ultimat3/action": "9.0.0",
35
+ "@ultimat3/core": "9.0.0",
36
+ "@ultimat3/entity": "9.0.0",
37
+ "@ultimat3/jobs": "9.0.0",
38
+ "@ultimat3/policy": "9.0.0",
39
+ "@ultimat3/query": "9.0.0",
40
+ "@ultimat3/schema": "9.0.0"
41
41
  }
42
42
  }
@@ -136,21 +136,54 @@ function string(schema: JsonSchema, input: unknown, path: string, issues: ArgIss
136
136
  if (schema.maxLength !== undefined && input.length > schema.maxLength) {
137
137
  issues.push({ path, message: `must be at most ${schema.maxLength} characters` });
138
138
  }
139
- if (schema.pattern !== undefined) matchesPattern(schema.pattern, input, path, issues);
139
+ if (schema.pattern !== undefined) matchesPattern(schema, schema.pattern, input, path, issues);
140
140
  return input;
141
141
  }
142
142
 
143
+ /**
144
+ * Compiled ONCE per schema NODE, never once per call. A tool's schema is registered at boot and
145
+ * every `tools/call` validates against that same object, so `new RegExp(pattern)` in the hot path
146
+ * was per-request work over a constant — `@ultimat3/schema`'s `patternTester` already draws the
147
+ * line in the same place for the same contract.
148
+ *
149
+ * Keyed on the NODE rather than on the pattern STRING, and a `WeakMap` rather than a `Map`: a
150
+ * pattern reaches this file from a tool an app registered, and a process that registers tools
151
+ * dynamically would otherwise accumulate one entry per distinct pattern string forever, with no
152
+ * bound and nothing to evict it. Keyed on the node, the entry dies with the schema.
153
+ *
154
+ * `null` is a CACHED verdict, not a miss — an uncompilable pattern would otherwise re-enter the
155
+ * `try` on every call, which is the case with the highest per-call cost.
156
+ */
157
+ const compiledPatterns = new WeakMap<JsonSchema, RegExp | null>();
158
+
159
+ /** Test-only probe. A count that climbs once per CALL rather than once per schema is the memo gone. */
160
+ let compilations = 0;
161
+ export const compiledPatternCount = (): number => compilations;
162
+
143
163
  /**
144
164
  * A pattern this server cannot COMPILE is refused, not skipped. `tools/list` published it, so an
145
165
  * agent has already been told the rule — passing a call the server cannot check is the silent-pass
146
166
  * this whole module exists to prevent. Every framework-projected pattern is a `RegExp.source` and
147
167
  * compiles; only a hand-written tool can reach the second branch, and that is its author's bug.
148
168
  */
149
- function matchesPattern(pattern: string, input: string, path: string, issues: ArgIssue[]): void {
150
- let compiled: RegExp;
151
- try {
152
- compiled = new RegExp(pattern);
153
- } catch {
169
+ function matchesPattern(
170
+ schema: JsonSchema,
171
+ pattern: string,
172
+ input: string,
173
+ path: string,
174
+ issues: ArgIssue[],
175
+ ): void {
176
+ let compiled = compiledPatterns.get(schema);
177
+ if (compiled === undefined) {
178
+ compilations += 1;
179
+ try {
180
+ compiled = new RegExp(pattern);
181
+ } catch {
182
+ compiled = null;
183
+ }
184
+ compiledPatterns.set(schema, compiled);
185
+ }
186
+ if (compiled === null) {
154
187
  issues.push({ path, message: `declares a pattern this server cannot compile: ${pattern}` });
155
188
  return;
156
189
  }