@dereekb/firebase-server 13.16.0 → 13.18.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/mcp/index.esm.js CHANGED
@@ -15,7 +15,7 @@ function _class_call_check$5(instance, Constructor) {
15
15
  throw new TypeError("Cannot call a class as a function");
16
16
  }
17
17
  }
18
- function _define_property$7(obj, key, value) {
18
+ function _define_property$9(obj, key, value) {
19
19
  if (key in obj) {
20
20
  Object.defineProperty(obj, key, {
21
21
  value: value,
@@ -45,6 +45,23 @@ function _define_property$7(obj, key, value) {
45
45
  * Default `instructions` string advertised by the MCP server on the JSON-RPC `initialize`
46
46
  * handshake. Apps may override this via {@link McpModuleConfig.serverInstructions}.
47
47
  */ var DEFAULT_MCP_SERVER_INSTRUCTIONS = 'A set of call-model tools generated automatically.';
48
+ /**
49
+ * Default name of the auto-injected reason parameter added to every advertised MCP tool's input
50
+ * schema. See {@link McpReasonParameterConfig.parameterName}.
51
+ */ var DEFAULT_MCP_REASON_PARAMETER_NAME = 'reason';
52
+ /**
53
+ * Default maximum length (in characters) of the auto-injected reason parameter. Values longer than
54
+ * this are clamped server-side before being forwarded to analytics. See
55
+ * {@link McpReasonParameterConfig.maxLength}.
56
+ */ var DEFAULT_MCP_REASON_PARAMETER_MAX_LENGTH = 250;
57
+ /**
58
+ * Default required-ness of the auto-injected reason parameter. See
59
+ * {@link McpReasonParameterConfig.required}.
60
+ */ var DEFAULT_MCP_REASON_PARAMETER_REQUIRED = true;
61
+ /**
62
+ * Default description advertised for the auto-injected reason parameter on every tool's input schema.
63
+ * See {@link McpReasonParameterConfig.description}.
64
+ */ var DEFAULT_MCP_REASON_PARAMETER_DESCRIPTION = 'A brief human-readable reason (≤250 chars) explaining why this tool is being called. Recorded for analytics/audit only; not part of the operation.';
48
65
  /**
49
66
  * Configuration for the firebase-server/mcp module.
50
67
  *
@@ -69,7 +86,7 @@ function _define_property$7(obj, key, value) {
69
86
  * discovery document. Claude custom-connector reads this to discover the OIDC issuer.
70
87
  *
71
88
  * @example 'https://api.example.com/oidc'
72
- */ _define_property$7(this, "oidcIssuer", void 0);
89
+ */ _define_property$9(this, "oidcIssuer", void 0);
73
90
  /**
74
91
  * The canonical resource URL of the MCP endpoint.
75
92
  *
@@ -77,19 +94,19 @@ function _define_property$7(obj, key, value) {
77
94
  * and used as the audience the access token's `aud` claim should match.
78
95
  *
79
96
  * @example 'https://api.example.com/mcp'
80
- */ _define_property$7(this, "mcpUrl", void 0);
97
+ */ _define_property$9(this, "mcpUrl", void 0);
81
98
  /**
82
99
  * Optional name advertised on the MCP `initialize` handshake. Defaults to
83
100
  * {@link DEFAULT_MCP_SERVER_NAME}.
84
- */ _define_property$7(this, "serverName", void 0);
101
+ */ _define_property$9(this, "serverName", void 0);
85
102
  /**
86
103
  * Optional version advertised on the MCP `initialize` handshake.
87
- */ _define_property$7(this, "serverVersion", void 0);
104
+ */ _define_property$9(this, "serverVersion", void 0);
88
105
  /**
89
106
  * Optional override for the `instructions` string passed to `new McpServer(...)` and
90
107
  * advertised on the JSON-RPC `initialize` handshake. Defaults to
91
108
  * {@link DEFAULT_MCP_SERVER_INSTRUCTIONS}.
92
- */ _define_property$7(this, "serverInstructions", void 0);
109
+ */ _define_property$9(this, "serverInstructions", void 0);
93
110
  /**
94
111
  * Absolute path to a pre-rendered MCP manifest JSON file produced by
95
112
  * `dbx-cli-generate-mcp-manifest`. When set, the runtime reads it once at
@@ -99,7 +116,17 @@ function _define_property$7(obj, key, value) {
99
116
  * Optional. When unset or the file is missing, the runtime falls back to
100
117
  * today's behavior (auto-generated descriptions, ArkType-derived input
101
118
  * schemas, no output schemas) and emits a single boot warning.
102
- */ _define_property$7(this, "mcpManifestPath", void 0);
119
+ */ _define_property$9(this, "mcpManifestPath", void 0);
120
+ /**
121
+ * Absolute path to a pre-rendered route manifest JSON file produced by
122
+ * `dbx-cli-generate-route-manifest`. When set, the runtime reads it once at
123
+ * boot and registers the built-in `url-models` tool, which decodes an app URL
124
+ * into the Firestore models its page renders.
125
+ *
126
+ * Optional. When unset or the file is missing, the runtime skips registering
127
+ * `url-models` and emits a single boot warning when the path was set but
128
+ * unreadable.
129
+ */ _define_property$9(this, "mcpRouteManifestPath", void 0);
103
130
  /**
104
131
  * When `true`, the MCP server only advertises tools whose effective read-only classification is `true`.
105
132
  *
@@ -109,19 +136,29 @@ function _define_property$7(obj, key, value) {
109
136
  *
110
137
  * The advertised `serverName` on the JSON-RPC `initialize` handshake is suffixed with
111
138
  * ` (read-only)` so the client surface reflects the mode.
112
- */ _define_property$7(this, "readOnly", void 0);
139
+ */ _define_property$9(this, "readOnly", void 0);
140
+ /**
141
+ * Controls the auto-injected `reason` parameter added to every advertised tool's input schema.
142
+ *
143
+ * Enabled by default (unset / `true` = defaults on). Pass a {@link McpReasonParameterConfig} object
144
+ * to tune `required`, `maxLength`, `description`, or `parameterName`, or `false` to disable it.
145
+ *
146
+ * When enabled, every advertised tool's `inputSchema` carries a required `reason` string the model
147
+ * fills with a short justification for the call. The value is forwarded to analytics and stripped
148
+ * from the dispatched handler body. See {@link McpReasonParameterConfig}.
149
+ */ _define_property$9(this, "reasonParameter", void 0);
113
150
  };
114
151
  /**
115
152
  * NestJS injection token for the optional {@link McpAuthRoleReader} provider.
116
153
  */ var MCP_AUTH_ROLE_READER = 'MCP_AUTH_ROLE_READER';
117
154
 
118
- function _type_of$3(obj) {
155
+ function _type_of$5(obj) {
119
156
  "@swc/helpers - typeof";
120
157
  return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
121
158
  }
122
159
  function __decorate(decorators, target, key, desc) {
123
160
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
124
- if ((typeof Reflect === "undefined" ? "undefined" : _type_of$3(Reflect)) === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
161
+ if ((typeof Reflect === "undefined" ? "undefined" : _type_of$5(Reflect)) === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
125
162
  else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
126
163
  return c > 3 && r && Object.defineProperty(target, key, r), r;
127
164
  }
@@ -135,6 +172,206 @@ typeof SuppressedError === "function" ? SuppressedError : function(error, suppre
135
172
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
136
173
  };
137
174
 
175
+ function _array_like_to_array$a(arr, len) {
176
+ if (len == null || len > arr.length) len = arr.length;
177
+ for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
178
+ return arr2;
179
+ }
180
+ function _array_without_holes$7(arr) {
181
+ if (Array.isArray(arr)) return _array_like_to_array$a(arr);
182
+ }
183
+ function _define_property$8(obj, key, value) {
184
+ if (key in obj) {
185
+ Object.defineProperty(obj, key, {
186
+ value: value,
187
+ enumerable: true,
188
+ configurable: true,
189
+ writable: true
190
+ });
191
+ } else {
192
+ obj[key] = value;
193
+ }
194
+ return obj;
195
+ }
196
+ function _iterable_to_array$7(iter) {
197
+ if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
198
+ }
199
+ function _non_iterable_spread$7() {
200
+ throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
201
+ }
202
+ function _object_spread$5(target) {
203
+ for(var i = 1; i < arguments.length; i++){
204
+ var source = arguments[i] != null ? arguments[i] : {};
205
+ var ownKeys = Object.keys(source);
206
+ if (typeof Object.getOwnPropertySymbols === "function") {
207
+ ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
208
+ return Object.getOwnPropertyDescriptor(source, sym).enumerable;
209
+ }));
210
+ }
211
+ ownKeys.forEach(function(key) {
212
+ _define_property$8(target, key, source[key]);
213
+ });
214
+ }
215
+ return target;
216
+ }
217
+ function ownKeys$3(object, enumerableOnly) {
218
+ var keys = Object.keys(object);
219
+ if (Object.getOwnPropertySymbols) {
220
+ var symbols = Object.getOwnPropertySymbols(object);
221
+ keys.push.apply(keys, symbols);
222
+ }
223
+ return keys;
224
+ }
225
+ function _object_spread_props$3(target, source) {
226
+ source = source != null ? source : {};
227
+ if (Object.getOwnPropertyDescriptors) {
228
+ Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
229
+ } else {
230
+ ownKeys$3(Object(source)).forEach(function(key) {
231
+ Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
232
+ });
233
+ }
234
+ return target;
235
+ }
236
+ function _to_consumable_array$7(arr) {
237
+ return _array_without_holes$7(arr) || _iterable_to_array$7(arr) || _unsupported_iterable_to_array$a(arr) || _non_iterable_spread$7();
238
+ }
239
+ function _type_of$4(obj) {
240
+ "@swc/helpers - typeof";
241
+ return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
242
+ }
243
+ function _unsupported_iterable_to_array$a(o, minLen) {
244
+ if (!o) return;
245
+ if (typeof o === "string") return _array_like_to_array$a(o, minLen);
246
+ var n = Object.prototype.toString.call(o).slice(8, -1);
247
+ if (n === "Object" && o.constructor) n = o.constructor.name;
248
+ if (n === "Map" || n === "Set") return Array.from(n);
249
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$a(o, minLen);
250
+ }
251
+ /**
252
+ * Normalizes the app-supplied {@link McpModuleConfig.reasonParameter} value (object / boolean /
253
+ * undefined) into a fully-resolved {@link ResolvedMcpReasonParameterConfig} with the package defaults
254
+ * applied.
255
+ *
256
+ * - `undefined` / `true` → defaults, enabled.
257
+ * - `false` → defaults, but `enabled: false`.
258
+ * - object → per-field overrides on top of the defaults (`enabled` defaults to `true`).
259
+ *
260
+ * @param config - The raw config from {@link McpModuleConfig.reasonParameter}.
261
+ * @returns The resolved config with all defaults applied.
262
+ */ function resolveMcpReasonParameterConfig(config) {
263
+ var result;
264
+ if (config === false) {
265
+ result = {
266
+ enabled: false,
267
+ required: DEFAULT_MCP_REASON_PARAMETER_REQUIRED,
268
+ maxLength: DEFAULT_MCP_REASON_PARAMETER_MAX_LENGTH,
269
+ description: DEFAULT_MCP_REASON_PARAMETER_DESCRIPTION,
270
+ parameterName: DEFAULT_MCP_REASON_PARAMETER_NAME
271
+ };
272
+ } else if (config == null || config === true) {
273
+ result = {
274
+ enabled: true,
275
+ required: DEFAULT_MCP_REASON_PARAMETER_REQUIRED,
276
+ maxLength: DEFAULT_MCP_REASON_PARAMETER_MAX_LENGTH,
277
+ description: DEFAULT_MCP_REASON_PARAMETER_DESCRIPTION,
278
+ parameterName: DEFAULT_MCP_REASON_PARAMETER_NAME
279
+ };
280
+ } else {
281
+ var _config_enabled, _config_required, _config_maxLength, _config_description, _config_parameterName;
282
+ result = {
283
+ enabled: (_config_enabled = config.enabled) !== null && _config_enabled !== void 0 ? _config_enabled : true,
284
+ required: (_config_required = config.required) !== null && _config_required !== void 0 ? _config_required : DEFAULT_MCP_REASON_PARAMETER_REQUIRED,
285
+ maxLength: (_config_maxLength = config.maxLength) !== null && _config_maxLength !== void 0 ? _config_maxLength : DEFAULT_MCP_REASON_PARAMETER_MAX_LENGTH,
286
+ description: (_config_description = config.description) !== null && _config_description !== void 0 ? _config_description : DEFAULT_MCP_REASON_PARAMETER_DESCRIPTION,
287
+ parameterName: (_config_parameterName = config.parameterName) !== null && _config_parameterName !== void 0 ? _config_parameterName : DEFAULT_MCP_REASON_PARAMETER_NAME
288
+ };
289
+ }
290
+ return result;
291
+ }
292
+ /**
293
+ * Returns `true` when the JSON schema already declares a property named `name` under `properties`.
294
+ *
295
+ * Used as the collision guard so a handler that legitimately consumes its own field of the same name
296
+ * is never double-declared (schema injection) or stripped (arg extraction).
297
+ *
298
+ * @param schema - The candidate JSON schema object (may be `undefined`).
299
+ * @param name - The property name to look for.
300
+ * @returns Whether the schema's `properties` already contains `name`.
301
+ */ function mcpSchemaDeclaresProperty(schema, name) {
302
+ var properties = schema === null || schema === void 0 ? void 0 : schema.properties;
303
+ return properties != null && (typeof properties === "undefined" ? "undefined" : _type_of$4(properties)) === 'object' && Object.prototype.hasOwnProperty.call(properties, name);
304
+ }
305
+ /**
306
+ * Returns a NEW JSON schema object with the reason parameter property merged into `properties` (and
307
+ * appended to `required` when configured). Never mutates the input — the static wire schemas are
308
+ * shared module-level constants reused across requests.
309
+ *
310
+ * Self-skips (returns the input schema unchanged) when the config is disabled or the schema already
311
+ * declares the parameter name. Defensive against a non-object / missing-`properties` input.
312
+ *
313
+ * @param inputSchema - The tool's resolved input schema.
314
+ * @param resolved - The resolved reason-parameter config.
315
+ * @returns A new schema carrying the reason property, or the original schema when skipped.
316
+ */ function applyMcpReasonParameterToSchema(inputSchema, resolved) {
317
+ var result = inputSchema;
318
+ if (resolved.enabled && !mcpSchemaDeclaresProperty(inputSchema, resolved.parameterName)) {
319
+ var _base_type;
320
+ var base = (typeof inputSchema === "undefined" ? "undefined" : _type_of$4(inputSchema)) === 'object' && inputSchema != null ? inputSchema : {};
321
+ var existingProperties = _type_of$4(base.properties) === 'object' && base.properties != null ? base.properties : {};
322
+ var next = _object_spread_props$3(_object_spread$5({}, base), {
323
+ type: (_base_type = base.type) !== null && _base_type !== void 0 ? _base_type : 'object',
324
+ properties: _object_spread_props$3(_object_spread$5({}, existingProperties), _define_property$8({}, resolved.parameterName, {
325
+ type: 'string',
326
+ maxLength: resolved.maxLength,
327
+ description: resolved.description
328
+ }))
329
+ });
330
+ if (resolved.required) {
331
+ var existingRequired = Array.isArray(base.required) ? base.required : [];
332
+ next.required = existingRequired.includes(resolved.parameterName) ? _to_consumable_array$7(existingRequired) : _to_consumable_array$7(existingRequired).concat([
333
+ resolved.parameterName
334
+ ]);
335
+ }
336
+ result = next;
337
+ }
338
+ return result;
339
+ }
340
+ /**
341
+ * Splits the auto-injected reason value out of a tool call's raw arguments.
342
+ *
343
+ * When enabled and the tool did NOT declare its own field of the same name: shallow-copies the args,
344
+ * deletes the parameter key, and coerces + clamps the value to `maxLength` (the client is not trusted).
345
+ * An empty / absent value yields `reason: undefined`.
346
+ *
347
+ * Otherwise (disabled, or the handler legitimately owns the field) returns the args unchanged so a
348
+ * field the handler actually consumes is never stripped.
349
+ *
350
+ * @param args - The raw `tools/call` arguments.
351
+ * @param resolved - The resolved reason-parameter config.
352
+ * @param toolDeclaresOwnReason - Whether the tool's own input schema already declares the parameter.
353
+ * @returns The extracted reason plus the (possibly stripped) args to dispatch.
354
+ */ function extractMcpReasonFromArgs(args, resolved, toolDeclaresOwnReason) {
355
+ var result;
356
+ if (resolved.enabled && !toolDeclaresOwnReason && Object.prototype.hasOwnProperty.call(args, resolved.parameterName)) {
357
+ var rawValue = args[resolved.parameterName];
358
+ var nextArgs = _object_spread$5({}, args);
359
+ delete nextArgs[resolved.parameterName];
360
+ var stringValue = rawValue == null ? '' : String(rawValue);
361
+ var clamped = stringValue.length > resolved.maxLength ? stringValue.slice(0, resolved.maxLength) : stringValue;
362
+ result = {
363
+ reason: clamped.length > 0 ? clamped : undefined,
364
+ args: nextArgs
365
+ };
366
+ } else {
367
+ result = {
368
+ reason: undefined,
369
+ args: args
370
+ };
371
+ }
372
+ return result;
373
+ }
374
+
138
375
  function _class_call_check$4(instance, Constructor) {
139
376
  if (!(instance instanceof Constructor)) {
140
377
  throw new TypeError("Cannot call a class as a function");
@@ -190,73 +427,356 @@ function _class_call_check$4(instance, Constructor) {
190
427
  return isDefault ? "".concat(modelType, ".").concat(call, "._") : "".concat(modelType, ".").concat(call, ".").concat(specifier);
191
428
  }
192
429
 
193
- function asyncGeneratorStep$5(gen, resolve, reject, _next, _throw, key, arg) {
430
+ function _array_like_to_array$9(arr, len) {
431
+ if (len == null || len > arr.length) len = arr.length;
432
+ for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
433
+ return arr2;
434
+ }
435
+ function _array_with_holes$5(arr) {
436
+ if (Array.isArray(arr)) return arr;
437
+ }
438
+ function _iterable_to_array_limit$5(arr, i) {
439
+ var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
440
+ if (_i == null) return;
441
+ var _arr = [];
442
+ var _n = true;
443
+ var _d = false;
444
+ var _s, _e;
194
445
  try {
195
- var info = gen[key](arg);
196
- var value = info.value;
197
- } catch (error) {
198
- reject(error);
199
- return;
446
+ for(_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true){
447
+ _arr.push(_s.value);
448
+ if (i && _arr.length === i) break;
449
+ }
450
+ } catch (err) {
451
+ _d = true;
452
+ _e = err;
453
+ } finally{
454
+ try {
455
+ if (!_n && _i["return"] != null) _i["return"]();
456
+ } finally{
457
+ if (_d) throw _e;
458
+ }
200
459
  }
201
- if (info.done) {
202
- resolve(value);
460
+ return _arr;
461
+ }
462
+ function _non_iterable_rest$5() {
463
+ throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
464
+ }
465
+ function _sliced_to_array$5(arr, i) {
466
+ return _array_with_holes$5(arr) || _iterable_to_array_limit$5(arr, i) || _unsupported_iterable_to_array$9(arr, i) || _non_iterable_rest$5();
467
+ }
468
+ function _unsupported_iterable_to_array$9(o, minLen) {
469
+ if (!o) return;
470
+ if (typeof o === "string") return _array_like_to_array$9(o, minLen);
471
+ var n = Object.prototype.toString.call(o).slice(8, -1);
472
+ if (n === "Object" && o.constructor) n = o.constructor.name;
473
+ if (n === "Map" || n === "Set") return Array.from(n);
474
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$9(o, minLen);
475
+ }
476
+ /**
477
+ * Runtime mirror of the build-time route manifest schema + a ported URL matcher.
478
+ *
479
+ * Structural mirror of `@dereekb/dbx-cli`'s `route-manifest.ts` types and the
480
+ * pure `url-match.ts` matcher, so the firebase-server/mcp runtime can consume
481
+ * the generated `route.manifest.json` without taking a runtime dependency on
482
+ * the build-time CLI package. Both sides bump {@link ROUTE_MANIFEST_VERSION}
483
+ * together when the manifest shape changes.
484
+ */ /**
485
+ * Version stamp embedded in `route.manifest.json`. Runtime loaders refuse
486
+ * manifests whose `version` does not match. Mirror in `@dereekb/dbx-cli`'s
487
+ * `ROUTE_MANIFEST_VERSION` — bump both together.
488
+ */ var ROUTE_MANIFEST_VERSION = 1;
489
+ /**
490
+ * Extracts the normalized pathname from a full URL or a bare path. Strips the
491
+ * scheme/host/port, query string, and hash; collapses an empty path to `/` and
492
+ * trims a single trailing slash.
493
+ *
494
+ * @param url - A full URL (`https://app.example.co/worker/x/timesheets/list`) or bare path.
495
+ * @returns The normalized pathname.
496
+ *
497
+ * @example
498
+ * ```ts
499
+ * parseUrlModelsPathname('https://app.hellosubs.co/worker/abc/timesheets/list/'); // => '/worker/abc/timesheets/list'
500
+ * ```
501
+ */ function parseUrlModelsPathname(url) {
502
+ var trimmed = url.trim();
503
+ var pathname;
504
+ if (RegExp("^[a-z][a-z0-9+.-]*:\\/\\/", "iu").test(trimmed)) {
505
+ pathname = new URL(trimmed).pathname;
203
506
  } else {
204
- Promise.resolve(value).then(_next, _throw);
507
+ var hashStripped = trimmed.split('#', 1)[0];
508
+ var pathPart = hashStripped.split('?', 1)[0];
509
+ pathname = pathPart.startsWith('/') ? pathPart : "/".concat(pathPart);
205
510
  }
511
+ return normalizePathname(pathname);
206
512
  }
207
- function _async_to_generator$5(fn) {
208
- return function() {
209
- var self = this, args = arguments;
210
- return new Promise(function(resolve, reject) {
211
- var gen = fn.apply(self, args);
212
- function _next(value) {
213
- asyncGeneratorStep$5(gen, resolve, reject, _next, _throw, "next", value);
513
+ /**
514
+ * Matches a URL against the manifest's states, preferring a literal match over
515
+ * a parameterised one. A tie at either tier collapses to `ambiguous`; otherwise
516
+ * the closest near-misses are scored and returned in a `none` result.
517
+ *
518
+ * @param input - The manifest and the URL (or pathname) to resolve.
519
+ * @returns A discriminated match / ambiguous / none result.
520
+ */ function matchRouteManifestUrl(input) {
521
+ var pathname = parseUrlModelsPathname(input.url);
522
+ var states = input.manifest.states;
523
+ var literal = states.filter(function(s) {
524
+ return s.fullUrl !== undefined && normalizePathname(s.fullUrl) === pathname;
525
+ });
526
+ var result;
527
+ if (literal.length === 1) {
528
+ result = {
529
+ kind: 'match',
530
+ via: 'literal',
531
+ state: literal[0],
532
+ params: {},
533
+ pathname: pathname
534
+ };
535
+ } else if (literal.length > 1) {
536
+ result = {
537
+ kind: 'ambiguous',
538
+ states: literal,
539
+ pathname: pathname
540
+ };
541
+ } else {
542
+ result = matchParamOrNone(states, pathname);
543
+ }
544
+ return result;
545
+ }
546
+ function matchParamOrNone(states, pathname) {
547
+ var inputSegments = splitSegments(pathname);
548
+ var hits = [];
549
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
550
+ try {
551
+ for(var _iterator = states[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
552
+ var state = _step.value;
553
+ if (state.fullUrl === undefined || !hasParamSegment(state.fullUrl)) {
554
+ continue;
214
555
  }
215
- function _throw(err) {
216
- asyncGeneratorStep$5(gen, resolve, reject, _next, _throw, "throw", err);
556
+ var params = tryMatchSegments(splitSegments(state.fullUrl), inputSegments);
557
+ if (params) {
558
+ hits.push({
559
+ state: state,
560
+ params: params
561
+ });
217
562
  }
218
- _next(undefined);
219
- });
220
- };
563
+ }
564
+ } catch (err) {
565
+ _didIteratorError = true;
566
+ _iteratorError = err;
567
+ } finally{
568
+ try {
569
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
570
+ _iterator.return();
571
+ }
572
+ } finally{
573
+ if (_didIteratorError) {
574
+ throw _iteratorError;
575
+ }
576
+ }
577
+ }
578
+ var result;
579
+ if (hits.length === 1) {
580
+ result = {
581
+ kind: 'match',
582
+ via: 'param',
583
+ state: hits[0].state,
584
+ params: hits[0].params,
585
+ pathname: pathname
586
+ };
587
+ } else if (hits.length > 1) {
588
+ result = {
589
+ kind: 'ambiguous',
590
+ states: hits.map(function(h) {
591
+ return h.state;
592
+ }),
593
+ pathname: pathname
594
+ };
595
+ } else {
596
+ result = {
597
+ kind: 'none',
598
+ candidates: scoreCandidates(states, pathname),
599
+ pathname: pathname
600
+ };
601
+ }
602
+ return result;
221
603
  }
222
- function _instanceof$1(left, right) {
223
- "@swc/helpers - instanceof";
224
- if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
225
- return !!right[Symbol.hasInstance](left);
604
+ // MARK: Pure URL helpers (mirror of @dereekb/dbx-cli url-match.ts)
605
+ function normalizePathname(pathname) {
606
+ var result;
607
+ if (pathname.length === 0) {
608
+ result = '/';
609
+ } else if (pathname.length > 1 && pathname.endsWith('/')) {
610
+ result = pathname.slice(0, -1);
226
611
  } else {
227
- return left instanceof right;
612
+ result = pathname;
228
613
  }
614
+ return result;
229
615
  }
230
- function _ts_generator$5(thisArg, body) {
231
- var f, y, t, _ = {
232
- label: 0,
233
- sent: function() {
234
- if (t[0] & 1) throw t[1];
235
- return t[1];
236
- },
237
- trys: [],
238
- ops: []
239
- }, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype), d = Object.defineProperty;
240
- return d(g, "next", {
241
- value: verb(0)
242
- }), d(g, "throw", {
243
- value: verb(1)
244
- }), d(g, "return", {
245
- value: verb(2)
246
- }), typeof Symbol === "function" && d(g, Symbol.iterator, {
247
- value: function() {
248
- return this;
249
- }
250
- }), g;
251
- function verb(n) {
252
- return function(v) {
253
- return step([
254
- n,
255
- v
256
- ]);
257
- };
616
+ function splitSegments(path) {
617
+ var normalized = path.startsWith('/') ? path.slice(1) : path;
618
+ return normalized.length === 0 ? [] : normalized.split('/');
619
+ }
620
+ function hasParamSegment(path) {
621
+ return path.includes(':') || path.includes('{');
622
+ }
623
+ function tryMatchSegments(route, input) {
624
+ if (route.length !== input.length) {
625
+ return undefined;
258
626
  }
259
- function step(op) {
627
+ var params = {};
628
+ var result = params;
629
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
630
+ try {
631
+ for(var _iterator = route.entries()[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
632
+ var _step_value = _sliced_to_array$5(_step.value, 2), i = _step_value[0], r = _step_value[1];
633
+ var v = input[i];
634
+ if (r.startsWith(':')) {
635
+ params[r.slice(1)] = decodeURIComponent(v);
636
+ } else if (r.startsWith('{') && r.endsWith('}')) {
637
+ var inner = r.slice(1, -1);
638
+ var colonIdx = inner.indexOf(':');
639
+ var key = colonIdx >= 0 ? inner.slice(0, colonIdx) : inner;
640
+ params[key] = decodeURIComponent(v);
641
+ } else if (r !== v) {
642
+ result = undefined;
643
+ break;
644
+ }
645
+ }
646
+ } catch (err) {
647
+ _didIteratorError = true;
648
+ _iteratorError = err;
649
+ } finally{
650
+ try {
651
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
652
+ _iterator.return();
653
+ }
654
+ } finally{
655
+ if (_didIteratorError) {
656
+ throw _iteratorError;
657
+ }
658
+ }
659
+ }
660
+ return result;
661
+ }
662
+ function scoreCandidates(states, pathname) {
663
+ var segments = splitSegments(pathname);
664
+ var scored = [];
665
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
666
+ try {
667
+ for(var _iterator = states[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
668
+ var state = _step.value;
669
+ if (state.fullUrl === undefined) {
670
+ continue;
671
+ }
672
+ var candidateSegments = splitSegments(state.fullUrl);
673
+ var score = 0;
674
+ var maxIndex = Math.min(segments.length, candidateSegments.length);
675
+ for(var i = 0; i < maxIndex; i += 1){
676
+ if (segments[i] === candidateSegments[i]) {
677
+ score += 2;
678
+ } else if (candidateSegments[i].startsWith(':') || candidateSegments[i].startsWith('{')) {
679
+ score += 1;
680
+ } else {
681
+ break;
682
+ }
683
+ }
684
+ if (score > 0) {
685
+ scored.push({
686
+ state: state,
687
+ score: score
688
+ });
689
+ }
690
+ }
691
+ } catch (err) {
692
+ _didIteratorError = true;
693
+ _iteratorError = err;
694
+ } finally{
695
+ try {
696
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
697
+ _iterator.return();
698
+ }
699
+ } finally{
700
+ if (_didIteratorError) {
701
+ throw _iteratorError;
702
+ }
703
+ }
704
+ }
705
+ scored.sort(function(a, b) {
706
+ return b.score - a.score;
707
+ });
708
+ return scored.slice(0, 5).map(function(s) {
709
+ return s.state;
710
+ });
711
+ }
712
+
713
+ function asyncGeneratorStep$6(gen, resolve, reject, _next, _throw, key, arg) {
714
+ try {
715
+ var info = gen[key](arg);
716
+ var value = info.value;
717
+ } catch (error) {
718
+ reject(error);
719
+ return;
720
+ }
721
+ if (info.done) {
722
+ resolve(value);
723
+ } else {
724
+ Promise.resolve(value).then(_next, _throw);
725
+ }
726
+ }
727
+ function _async_to_generator$6(fn) {
728
+ return function() {
729
+ var self = this, args = arguments;
730
+ return new Promise(function(resolve, reject) {
731
+ var gen = fn.apply(self, args);
732
+ function _next(value) {
733
+ asyncGeneratorStep$6(gen, resolve, reject, _next, _throw, "next", value);
734
+ }
735
+ function _throw(err) {
736
+ asyncGeneratorStep$6(gen, resolve, reject, _next, _throw, "throw", err);
737
+ }
738
+ _next(undefined);
739
+ });
740
+ };
741
+ }
742
+ function _instanceof$1(left, right) {
743
+ "@swc/helpers - instanceof";
744
+ if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
745
+ return !!right[Symbol.hasInstance](left);
746
+ } else {
747
+ return left instanceof right;
748
+ }
749
+ }
750
+ function _ts_generator$6(thisArg, body) {
751
+ var f, y, t, _ = {
752
+ label: 0,
753
+ sent: function() {
754
+ if (t[0] & 1) throw t[1];
755
+ return t[1];
756
+ },
757
+ trys: [],
758
+ ops: []
759
+ }, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype), d = Object.defineProperty;
760
+ return d(g, "next", {
761
+ value: verb(0)
762
+ }), d(g, "throw", {
763
+ value: verb(1)
764
+ }), d(g, "return", {
765
+ value: verb(2)
766
+ }), typeof Symbol === "function" && d(g, Symbol.iterator, {
767
+ value: function() {
768
+ return this;
769
+ }
770
+ }), g;
771
+ function verb(n) {
772
+ return function(v) {
773
+ return step([
774
+ n,
775
+ v
776
+ ]);
777
+ };
778
+ }
779
+ function step(op) {
260
780
  if (f) throw new TypeError("Generator is already executing.");
261
781
  while(g && (g = 0, op[0] && (_ = 0)), _)try {
262
782
  if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
@@ -355,9 +875,9 @@ function _ts_generator$5(thisArg, body) {
355
875
  * @param details - The handler-level API details (carries the mapper + Tier 2/3 formatters).
356
876
  * @returns The MCP tool response content.
357
877
  */ function formatMcpToolResponse(result, params, details) {
358
- return _async_to_generator$5(function() {
878
+ return _async_to_generator$6(function() {
359
879
  var mcp, value, _tmp, context, response, summary;
360
- return _ts_generator$5(this, function(_state) {
880
+ return _ts_generator$6(this, function(_state) {
361
881
  switch(_state.label){
362
882
  case 0:
363
883
  mcp = details === null || details === void 0 ? void 0 : details.mcp;
@@ -510,6 +1030,28 @@ function stringifyResult(result) {
510
1030
  }
511
1031
  return READ_ONLY_BY_CALL_TYPE[callType];
512
1032
  }
1033
+ /**
1034
+ * Maps an effective read-only classification to the standard MCP {@link ToolAnnotations} hints
1035
+ * advertised on `tools/list`.
1036
+ *
1037
+ * A definitively read-only tool advertises `{ readOnlyHint: true }`. Everything else — a known
1038
+ * write (`false`) or an unclassified verb (`undefined`, e.g. `invoke`/custom) — fails safe to
1039
+ * `{ readOnlyHint: false, destructiveHint: true }` so a client never mistakes an ambiguous tool
1040
+ * for a safe read. `idempotentHint` / `openWorldHint` are intentionally left unset.
1041
+ *
1042
+ * Co-located with {@link resolveEffectiveReadOnly} / {@link READ_ONLY_BY_CALL_TYPE} so the
1043
+ * read/write classification rules stay in one file.
1044
+ *
1045
+ * @param effectiveReadOnly - The resolved read-only classification from {@link resolveEffectiveReadOnly}.
1046
+ * @returns The MCP annotations describing the tool's read/write behaviour.
1047
+ */ function resolveMcpToolAnnotations(effectiveReadOnly) {
1048
+ return effectiveReadOnly === true ? {
1049
+ readOnlyHint: true
1050
+ } : {
1051
+ readOnlyHint: false,
1052
+ destructiveHint: true
1053
+ };
1054
+ }
513
1055
  /**
514
1056
  * Resolves the OIDC scope required to invoke a given call type, or `undefined` for
515
1057
  * non-CRUD calls. Thin re-export so the tool generator doesn't need to reach into
@@ -521,15 +1063,15 @@ function stringifyResult(result) {
521
1063
  return callModelOidcScopeForCallType(callType);
522
1064
  }
523
1065
 
524
- function _array_like_to_array$7(arr, len) {
1066
+ function _array_like_to_array$8(arr, len) {
525
1067
  if (len == null || len > arr.length) len = arr.length;
526
1068
  for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
527
1069
  return arr2;
528
1070
  }
529
- function _array_with_holes$3(arr) {
1071
+ function _array_with_holes$4(arr) {
530
1072
  if (Array.isArray(arr)) return arr;
531
1073
  }
532
- function _iterable_to_array_limit$3(arr, i) {
1074
+ function _iterable_to_array_limit$4(arr, i) {
533
1075
  var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
534
1076
  if (_i == null) return;
535
1077
  var _arr = [];
@@ -553,23 +1095,23 @@ function _iterable_to_array_limit$3(arr, i) {
553
1095
  }
554
1096
  return _arr;
555
1097
  }
556
- function _non_iterable_rest$3() {
1098
+ function _non_iterable_rest$4() {
557
1099
  throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
558
1100
  }
559
- function _sliced_to_array$3(arr, i) {
560
- return _array_with_holes$3(arr) || _iterable_to_array_limit$3(arr, i) || _unsupported_iterable_to_array$7(arr, i) || _non_iterable_rest$3();
1101
+ function _sliced_to_array$4(arr, i) {
1102
+ return _array_with_holes$4(arr) || _iterable_to_array_limit$4(arr, i) || _unsupported_iterable_to_array$8(arr, i) || _non_iterable_rest$4();
561
1103
  }
562
- function _type_of$2(obj) {
1104
+ function _type_of$3(obj) {
563
1105
  "@swc/helpers - typeof";
564
1106
  return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
565
1107
  }
566
- function _unsupported_iterable_to_array$7(o, minLen) {
1108
+ function _unsupported_iterable_to_array$8(o, minLen) {
567
1109
  if (!o) return;
568
- if (typeof o === "string") return _array_like_to_array$7(o, minLen);
1110
+ if (typeof o === "string") return _array_like_to_array$8(o, minLen);
569
1111
  var n = Object.prototype.toString.call(o).slice(8, -1);
570
1112
  if (n === "Object" && o.constructor) n = o.constructor.name;
571
1113
  if (n === "Map" || n === "Set") return Array.from(n);
572
- if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$7(o, minLen);
1114
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$8(o, minLen);
573
1115
  }
574
1116
  /**
575
1117
  * Default `toJsonSchema()` options.
@@ -605,18 +1147,73 @@ function _unsupported_iterable_to_array$7(o, minLen) {
605
1147
  var inputSchema = (_input_inputSchema = input.inputSchema) !== null && _input_inputSchema !== void 0 ? _input_inputSchema : {
606
1148
  type: 'object'
607
1149
  };
608
- var entry = input.outputSchema == null ? {
1150
+ var entry = {
609
1151
  name: input.name,
610
1152
  description: input.description,
611
1153
  inputSchema: inputSchema
612
- } : {
613
- name: input.name,
614
- description: input.description,
615
- inputSchema: inputSchema,
616
- outputSchema: input.outputSchema
617
1154
  };
1155
+ if (input.outputSchema != null) {
1156
+ entry.outputSchema = input.outputSchema;
1157
+ }
1158
+ if (input.annotations != null) {
1159
+ entry.annotations = input.annotations;
1160
+ }
618
1161
  return Object.freeze(entry);
619
1162
  }
1163
+ /**
1164
+ * Description prefix prepended to a mutating tool's description so MCP clients that don't render
1165
+ * {@link ToolAnnotations} still surface the write signal in plain text.
1166
+ */ var MCP_WRITE_TOOL_DESCRIPTION_PREFIX = '[WRITE] ';
1167
+ /**
1168
+ * Prepends {@link MCP_WRITE_TOOL_DESCRIPTION_PREFIX} to a description when its annotations mark the
1169
+ * tool as a write (`readOnlyHint === false`). Read-only tools are returned unchanged.
1170
+ *
1171
+ * @param description - The base tool description.
1172
+ * @param annotations - The resolved MCP annotations for the tool.
1173
+ * @returns The description, prefixed with the write marker when the tool mutates.
1174
+ */ function applyWriteMarker(description, annotations) {
1175
+ return annotations.readOnlyHint === false ? "".concat(MCP_WRITE_TOOL_DESCRIPTION_PREFIX).concat(description) : description;
1176
+ }
1177
+ /**
1178
+ * Assembles a statically-registered {@link McpToolDefinition} with consistent MCP annotations.
1179
+ *
1180
+ * Built-in tools (e.g. `whoami`, `model-get`, `batch-execute`) don't flow through
1181
+ * {@link buildToolFromCandidate}, so this is their analogous single source of truth: it derives the
1182
+ * read/write annotations from {@link BuildStaticToolDefinitionInput.effectiveReadOnly} via
1183
+ * {@link resolveMcpToolAnnotations}, applies the {@link MCP_WRITE_TOOL_DESCRIPTION_PREFIX} write
1184
+ * marker to the description, then mirrors the annotations onto both the definition's `annotations`
1185
+ * field and the frozen {@link McpToolDefinition.staticWireEntry}. Centralizing this guarantees no
1186
+ * built-in tool reaches `tools/list` un-annotated (which would surface as an uncategorized "other"
1187
+ * tool in clients that bucket by `readOnlyHint`).
1188
+ *
1189
+ * @param input - The tool identity, schemas, dispatch target, static handler, and read-only classification.
1190
+ * @returns A statically-registered {@link McpToolDefinition} to append to the MCP server factory's tool registry.
1191
+ */ function buildStaticToolDefinition(input) {
1192
+ var name = input.name, inputSchema = input.inputSchema, outputSchema = input.outputSchema, dispatch = input.dispatch, staticHandler = input.staticHandler, effectiveReadOnly = input.effectiveReadOnly, rule = input.rule;
1193
+ var annotations = resolveMcpToolAnnotations(effectiveReadOnly);
1194
+ var description = applyWriteMarker(input.description, annotations);
1195
+ return {
1196
+ name: name,
1197
+ description: description,
1198
+ inputSchema: inputSchema,
1199
+ outputSchema: outputSchema,
1200
+ annotations: annotations,
1201
+ dispatch: dispatch,
1202
+ staticHandler: staticHandler,
1203
+ filterMetadata: {
1204
+ visibilityKind: 'declarative',
1205
+ rule: rule !== null && rule !== void 0 ? rule : {},
1206
+ effectiveReadOnly: effectiveReadOnly
1207
+ },
1208
+ staticWireEntry: buildStaticWireEntry({
1209
+ name: name,
1210
+ description: description,
1211
+ inputSchema: inputSchema,
1212
+ outputSchema: outputSchema,
1213
+ annotations: annotations
1214
+ })
1215
+ };
1216
+ }
620
1217
  // MARK: Naming
621
1218
  /**
622
1219
  * The default specifier key used when a handler is not behind a specifier router.
@@ -835,18 +1432,18 @@ function _unsupported_iterable_to_array$7(o, minLen) {
835
1432
  var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
836
1433
  try {
837
1434
  for(var _iterator = Object.entries(apiDetails.models)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
838
- var _step_value = _sliced_to_array$3(_step.value, 2), modelType = _step_value[0], modelEntry = _step_value[1];
1435
+ var _step_value = _sliced_to_array$4(_step.value, 2), modelType = _step_value[0], modelEntry = _step_value[1];
839
1436
  var _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
840
1437
  try {
841
1438
  for(var _iterator1 = Object.entries(modelEntry.calls)[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
842
- var _step_value1 = _sliced_to_array$3(_step1.value, 2), callType = _step_value1[0], callDetails = _step_value1[1];
1439
+ var _step_value1 = _sliced_to_array$4(_step1.value, 2), callType = _step_value1[0], callDetails = _step_value1[1];
843
1440
  if (callDetails == null) {
844
1441
  continue;
845
1442
  }
846
1443
  var _iteratorNormalCompletion2 = true, _didIteratorError2 = false, _iteratorError2 = undefined;
847
1444
  try {
848
1445
  for(var _iterator2 = Object.entries(callDetails.specifiers)[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true){
849
- var _step_value2 = _sliced_to_array$3(_step2.value, 2), specifierKey = _step_value2[0], handlerDetails = _step_value2[1];
1446
+ var _step_value2 = _sliced_to_array$4(_step2.value, 2), specifierKey = _step_value2[0], handlerDetails = _step_value2[1];
850
1447
  var _ref;
851
1448
  var _naming_modelSegments, _handlerDetails_mcp, _handlerDetails_mcp1;
852
1449
  if (handlerDetails == null) {
@@ -983,7 +1580,7 @@ function _unsupported_iterable_to_array$7(o, minLen) {
983
1580
  return;
984
1581
  }
985
1582
  var manifestEntry = manifest === null || manifest === void 0 ? void 0 : manifest.get(mcpManifestKey(modelType, callType, specifier));
986
- var description = (_ref = manifestEntry === null || manifestEntry === void 0 ? void 0 : manifestEntry.description) !== null && _ref !== void 0 ? _ref : buildDefaultMcpToolDescription(modelType, callType, specifier);
1583
+ var baseDescription = (_ref = manifestEntry === null || manifestEntry === void 0 ? void 0 : manifestEntry.description) !== null && _ref !== void 0 ? _ref : buildDefaultMcpToolDescription(modelType, callType, specifier);
987
1584
  var inputSchema = resolveInputSchema({
988
1585
  handlerDetails: handlerDetails,
989
1586
  manifestEntry: manifestEntry,
@@ -1017,6 +1614,8 @@ function _unsupported_iterable_to_array$7(o, minLen) {
1017
1614
  }
1018
1615
  var requiredScope = (_resolveRequiredScope = resolveRequiredScope(callType)) !== null && _resolveRequiredScope !== void 0 ? _resolveRequiredScope : undefined;
1019
1616
  var effectiveReadOnly = resolveEffectiveReadOnly((_handlerDetails_mcp = handlerDetails.mcp) === null || _handlerDetails_mcp === void 0 ? void 0 : _handlerDetails_mcp.readOnly, callType);
1617
+ var annotations = resolveMcpToolAnnotations(effectiveReadOnly);
1618
+ var description = applyWriteMarker(baseDescription, annotations);
1020
1619
  var filterMetadata;
1021
1620
  if (classified.visibilityKind === 'declarative') {
1022
1621
  filterMetadata = {
@@ -1059,13 +1658,15 @@ function _unsupported_iterable_to_array$7(o, minLen) {
1059
1658
  name: name,
1060
1659
  description: description,
1061
1660
  inputSchema: inputSchema,
1062
- outputSchema: outputSchema
1661
+ outputSchema: outputSchema,
1662
+ annotations: annotations
1063
1663
  });
1064
1664
  var definition = {
1065
1665
  name: name,
1066
1666
  description: description,
1067
1667
  inputSchema: inputSchema,
1068
1668
  outputSchema: outputSchema,
1669
+ annotations: annotations,
1069
1670
  details: handlerDetails,
1070
1671
  dispatch: dispatch,
1071
1672
  filterMetadata: filterMetadata,
@@ -1094,7 +1695,7 @@ function resolveInputSchema(context) {
1094
1695
  var result;
1095
1696
  try {
1096
1697
  var exported = arktypeToJsonSchemaForExport(handlerDetails.inputType);
1097
- if (exported && (typeof exported === "undefined" ? "undefined" : _type_of$2(exported)) === 'object') {
1698
+ if (exported && (typeof exported === "undefined" ? "undefined" : _type_of$3(exported)) === 'object') {
1098
1699
  result = exported;
1099
1700
  } else {
1100
1701
  // arktypeToJsonSchemaForExport returned a non-object (shouldn't happen for object schemas).
@@ -1113,15 +1714,15 @@ function resolveInputSchema(context) {
1113
1714
  return result;
1114
1715
  }
1115
1716
 
1116
- function _array_like_to_array$6(arr, len) {
1717
+ function _array_like_to_array$7(arr, len) {
1117
1718
  if (len == null || len > arr.length) len = arr.length;
1118
1719
  for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
1119
1720
  return arr2;
1120
1721
  }
1121
- function _array_without_holes$5(arr) {
1122
- if (Array.isArray(arr)) return _array_like_to_array$6(arr);
1722
+ function _array_without_holes$6(arr) {
1723
+ if (Array.isArray(arr)) return _array_like_to_array$7(arr);
1123
1724
  }
1124
- function asyncGeneratorStep$4(gen, resolve, reject, _next, _throw, key, arg) {
1725
+ function asyncGeneratorStep$5(gen, resolve, reject, _next, _throw, key, arg) {
1125
1726
  try {
1126
1727
  var info = gen[key](arg);
1127
1728
  var value = info.value;
@@ -1135,39 +1736,39 @@ function asyncGeneratorStep$4(gen, resolve, reject, _next, _throw, key, arg) {
1135
1736
  Promise.resolve(value).then(_next, _throw);
1136
1737
  }
1137
1738
  }
1138
- function _async_to_generator$4(fn) {
1739
+ function _async_to_generator$5(fn) {
1139
1740
  return function() {
1140
1741
  var self = this, args = arguments;
1141
1742
  return new Promise(function(resolve, reject) {
1142
1743
  var gen = fn.apply(self, args);
1143
1744
  function _next(value) {
1144
- asyncGeneratorStep$4(gen, resolve, reject, _next, _throw, "next", value);
1745
+ asyncGeneratorStep$5(gen, resolve, reject, _next, _throw, "next", value);
1145
1746
  }
1146
1747
  function _throw(err) {
1147
- asyncGeneratorStep$4(gen, resolve, reject, _next, _throw, "throw", err);
1748
+ asyncGeneratorStep$5(gen, resolve, reject, _next, _throw, "throw", err);
1148
1749
  }
1149
1750
  _next(undefined);
1150
1751
  });
1151
1752
  };
1152
1753
  }
1153
- function _iterable_to_array$5(iter) {
1754
+ function _iterable_to_array$6(iter) {
1154
1755
  if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
1155
1756
  }
1156
- function _non_iterable_spread$5() {
1757
+ function _non_iterable_spread$6() {
1157
1758
  throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
1158
1759
  }
1159
- function _to_consumable_array$5(arr) {
1160
- return _array_without_holes$5(arr) || _iterable_to_array$5(arr) || _unsupported_iterable_to_array$6(arr) || _non_iterable_spread$5();
1760
+ function _to_consumable_array$6(arr) {
1761
+ return _array_without_holes$6(arr) || _iterable_to_array$6(arr) || _unsupported_iterable_to_array$7(arr) || _non_iterable_spread$6();
1161
1762
  }
1162
- function _unsupported_iterable_to_array$6(o, minLen) {
1763
+ function _unsupported_iterable_to_array$7(o, minLen) {
1163
1764
  if (!o) return;
1164
- if (typeof o === "string") return _array_like_to_array$6(o, minLen);
1765
+ if (typeof o === "string") return _array_like_to_array$7(o, minLen);
1165
1766
  var n = Object.prototype.toString.call(o).slice(8, -1);
1166
1767
  if (n === "Object" && o.constructor) n = o.constructor.name;
1167
1768
  if (n === "Map" || n === "Set") return Array.from(n);
1168
- if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$6(o, minLen);
1769
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$7(o, minLen);
1169
1770
  }
1170
- function _ts_generator$4(thisArg, body) {
1771
+ function _ts_generator$5(thisArg, body) {
1171
1772
  var f, y, t, _ = {
1172
1773
  label: 0,
1173
1774
  sent: function() {
@@ -1310,7 +1911,7 @@ function _ts_generator$4(thisArg, body) {
1310
1911
  };
1311
1912
  var name = MODEL_GET_TOOL_NAME;
1312
1913
  var description = 'Fetch one or more Firestore model documents by key or bare id. Values containing `/` are treated as full keys; bare ids are auto-promoted to `<collectionName>/<id>` for root models. Subcollection models require full keys.';
1313
- return {
1914
+ return buildStaticToolDefinition({
1314
1915
  name: name,
1315
1916
  description: description,
1316
1917
  inputSchema: MODEL_GET_INPUT_SCHEMA,
@@ -1320,26 +1921,17 @@ function _ts_generator$4(thisArg, body) {
1320
1921
  modelType: MODEL_GET_DISPATCH_MODEL_TYPE
1321
1922
  },
1322
1923
  staticHandler: handler,
1323
- filterMetadata: {
1324
- visibilityKind: 'declarative',
1325
- rule: {
1326
- requireAuthenticated: true
1327
- },
1328
- effectiveReadOnly: true
1329
- },
1330
- staticWireEntry: buildStaticWireEntry({
1331
- name: name,
1332
- description: description,
1333
- inputSchema: MODEL_GET_INPUT_SCHEMA,
1334
- outputSchema: MODEL_GET_OUTPUT_SCHEMA
1335
- })
1336
- };
1924
+ effectiveReadOnly: true,
1925
+ rule: {
1926
+ requireAuthenticated: true
1927
+ }
1928
+ });
1337
1929
  }
1338
1930
  // MARK: Handler
1339
1931
  function modelGetToolHandler(args, ctx, deps) {
1340
- return _async_to_generator$4(function() {
1932
+ return _async_to_generator$5(function() {
1341
1933
  var result, input, identity, resolvedKeys, merged, error;
1342
- return _ts_generator$4(this, function(_state) {
1934
+ return _ts_generator$5(this, function(_state) {
1343
1935
  switch(_state.label){
1344
1936
  case 0:
1345
1937
  _state.trys.push([
@@ -1356,7 +1948,7 @@ function modelGetToolHandler(args, ctx, deps) {
1356
1948
  resolvedKeys = resolveKeys(input.keys, identity);
1357
1949
  return [
1358
1950
  4,
1359
- readInChunks({
1951
+ readInChunks$1({
1360
1952
  modelType: input.modelType,
1361
1953
  keys: resolvedKeys,
1362
1954
  auth: ctx.auth,
@@ -1445,10 +2037,10 @@ function resolveKeys(keys, identity) {
1445
2037
  }
1446
2038
  return resolved;
1447
2039
  }
1448
- function readInChunks(input) {
1449
- return _async_to_generator$4(function() {
2040
+ function readInChunks$1(input) {
2041
+ return _async_to_generator$5(function() {
1450
2042
  var modelType, keys, auth, readDocuments, merged, offset, _merged_results, _merged_errors, batch, partial;
1451
- return _ts_generator$4(this, function(_state) {
2043
+ return _ts_generator$5(this, function(_state) {
1452
2044
  switch(_state.label){
1453
2045
  case 0:
1454
2046
  modelType = input.modelType, keys = input.keys, auth = input.auth, readDocuments = input.readDocuments;
@@ -1470,8 +2062,8 @@ function readInChunks(input) {
1470
2062
  ];
1471
2063
  case 2:
1472
2064
  partial = _state.sent();
1473
- (_merged_results = merged.results).push.apply(_merged_results, _to_consumable_array$5(partial.results));
1474
- (_merged_errors = merged.errors).push.apply(_merged_errors, _to_consumable_array$5(partial.errors));
2065
+ (_merged_results = merged.results).push.apply(_merged_results, _to_consumable_array$6(partial.results));
2066
+ (_merged_errors = merged.errors).push.apply(_merged_errors, _to_consumable_array$6(partial.errors));
1475
2067
  _state.label = 3;
1476
2068
  case 3:
1477
2069
  offset += MCP_MODEL_GET_BATCH_SIZE;
@@ -1562,15 +2154,15 @@ var MODEL_GET_OUTPUT_SCHEMA = {
1562
2154
  }
1563
2155
  };
1564
2156
 
1565
- function _array_like_to_array$5(arr, len) {
2157
+ function _array_like_to_array$6(arr, len) {
1566
2158
  if (len == null || len > arr.length) len = arr.length;
1567
2159
  for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
1568
2160
  return arr2;
1569
2161
  }
1570
- function _array_without_holes$4(arr) {
1571
- if (Array.isArray(arr)) return _array_like_to_array$5(arr);
2162
+ function _array_without_holes$5(arr) {
2163
+ if (Array.isArray(arr)) return _array_like_to_array$6(arr);
1572
2164
  }
1573
- function _define_property$6(obj, key, value) {
2165
+ function _define_property$7(obj, key, value) {
1574
2166
  if (key in obj) {
1575
2167
  Object.defineProperty(obj, key, {
1576
2168
  value: value,
@@ -1583,13 +2175,13 @@ function _define_property$6(obj, key, value) {
1583
2175
  }
1584
2176
  return obj;
1585
2177
  }
1586
- function _iterable_to_array$4(iter) {
2178
+ function _iterable_to_array$5(iter) {
1587
2179
  if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
1588
2180
  }
1589
- function _non_iterable_spread$4() {
2181
+ function _non_iterable_spread$5() {
1590
2182
  throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
1591
2183
  }
1592
- function _object_spread$2(target) {
2184
+ function _object_spread$4(target) {
1593
2185
  for(var i = 1; i < arguments.length; i++){
1594
2186
  var source = arguments[i] != null ? arguments[i] : {};
1595
2187
  var ownKeys = Object.keys(source);
@@ -1599,12 +2191,12 @@ function _object_spread$2(target) {
1599
2191
  }));
1600
2192
  }
1601
2193
  ownKeys.forEach(function(key) {
1602
- _define_property$6(target, key, source[key]);
2194
+ _define_property$7(target, key, source[key]);
1603
2195
  });
1604
2196
  }
1605
2197
  return target;
1606
2198
  }
1607
- function ownKeys(object, enumerableOnly) {
2199
+ function ownKeys$2(object, enumerableOnly) {
1608
2200
  var keys = Object.keys(object);
1609
2201
  if (Object.getOwnPropertySymbols) {
1610
2202
  var symbols = Object.getOwnPropertySymbols(object);
@@ -1612,27 +2204,27 @@ function ownKeys(object, enumerableOnly) {
1612
2204
  }
1613
2205
  return keys;
1614
2206
  }
1615
- function _object_spread_props(target, source) {
2207
+ function _object_spread_props$2(target, source) {
1616
2208
  source = source != null ? source : {};
1617
2209
  if (Object.getOwnPropertyDescriptors) {
1618
2210
  Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
1619
2211
  } else {
1620
- ownKeys(Object(source)).forEach(function(key) {
2212
+ ownKeys$2(Object(source)).forEach(function(key) {
1621
2213
  Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
1622
2214
  });
1623
2215
  }
1624
2216
  return target;
1625
2217
  }
1626
- function _to_consumable_array$4(arr) {
1627
- return _array_without_holes$4(arr) || _iterable_to_array$4(arr) || _unsupported_iterable_to_array$5(arr) || _non_iterable_spread$4();
2218
+ function _to_consumable_array$5(arr) {
2219
+ return _array_without_holes$5(arr) || _iterable_to_array$5(arr) || _unsupported_iterable_to_array$6(arr) || _non_iterable_spread$5();
1628
2220
  }
1629
- function _unsupported_iterable_to_array$5(o, minLen) {
2221
+ function _unsupported_iterable_to_array$6(o, minLen) {
1630
2222
  if (!o) return;
1631
- if (typeof o === "string") return _array_like_to_array$5(o, minLen);
2223
+ if (typeof o === "string") return _array_like_to_array$6(o, minLen);
1632
2224
  var n = Object.prototype.toString.call(o).slice(8, -1);
1633
2225
  if (n === "Object" && o.constructor) n = o.constructor.name;
1634
2226
  if (n === "Map" || n === "Set") return Array.from(n);
1635
- if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$5(o, minLen);
2227
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$6(o, minLen);
1636
2228
  }
1637
2229
  // MARK: Constants
1638
2230
  /**
@@ -1681,7 +2273,7 @@ function _unsupported_iterable_to_array$5(o, minLen) {
1681
2273
  var name = MODEL_INFO_TOOL_NAME;
1682
2274
  var count = deps.manifest.length;
1683
2275
  var description = "Browse the Firestore model catalog (".concat(count, " model").concat(count === 1 ? '' : 's', "). No args returns model groups + counts (start here). ") + '`model` (a modelType/identityConst/collectionPrefix string, or an array of them) returns full detail per match — misses are reported in `notFound`. ' + '`modelGroup` returns the summary list for that group. `all:true` returns the full catalog summary. ' + '`fields` (boolean) forces or suppresses persisted-field detail in list/multiple modes.';
1684
- return {
2276
+ return buildStaticToolDefinition({
1685
2277
  name: name,
1686
2278
  description: description,
1687
2279
  inputSchema: MODEL_INFO_INPUT_SCHEMA,
@@ -1691,27 +2283,19 @@ function _unsupported_iterable_to_array$5(o, minLen) {
1691
2283
  modelType: MODEL_INFO_DISPATCH_MODEL_TYPE
1692
2284
  },
1693
2285
  staticHandler: handler,
1694
- filterMetadata: {
1695
- visibilityKind: 'declarative',
1696
- rule: {
1697
- requireAuthenticated: true
1698
- },
1699
- effectiveReadOnly: true
1700
- },
1701
- staticWireEntry: buildStaticWireEntry({
1702
- name: name,
1703
- description: description,
1704
- inputSchema: MODEL_INFO_INPUT_SCHEMA,
1705
- outputSchema: MODEL_INFO_OUTPUT_SCHEMA
1706
- })
1707
- };
2286
+ effectiveReadOnly: true,
2287
+ rule: {
2288
+ requireAuthenticated: true
2289
+ }
2290
+ });
1708
2291
  }
1709
2292
  // MARK: Handler
1710
2293
  function modelInfoToolHandler(args, _ctx, deps) {
1711
2294
  var result;
1712
2295
  try {
1713
2296
  var input = parseModelInfoInput(args);
1714
- var output = resolveModelInfoOutput(input, deps.manifest);
2297
+ var resolved = resolveModelInfoOutput(input, deps.manifest);
2298
+ var output = attachReferencedEnums(resolved, deps.enums);
1715
2299
  result = {
1716
2300
  content: [
1717
2301
  {
@@ -1748,16 +2332,123 @@ function resolveModelInfoOutput(input, manifest) {
1748
2332
  }
1749
2333
  return output;
1750
2334
  }
1751
- // MARK: Input parsing
1752
- function parseModelInfoInput(args) {
1753
- var modelQueries = parseModelArg(args['model']);
1754
- var modelGroup = parseModelGroupArg(args['modelGroup']);
1755
- var all = parseBooleanArg(args['all'], 'all');
1756
- var fields = parseBooleanArg(args['fields'], 'fields');
1757
- return _object_spread$2(_object_spread_props(_object_spread$2(_object_spread_props(_object_spread$2({}, modelQueries == null ? {} : {
1758
- modelQueries: modelQueries
1759
- }), {
1760
- modelIsArray: Array.isArray(args['model'])
2335
+ // MARK: Enum attachment
2336
+ /**
2337
+ * Attaches the value→label tables for every enum referenced (by `enumRef`) on the returned models'
2338
+ * fields — recursively through `nestedFields` — as a top-level `enums` section.
2339
+ *
2340
+ * Gated behind full field detail: only `single` / `multiple` rows and `list` rows that opted into
2341
+ * `fields:true` carry the persisted `fields` array, so the compact default modes are untouched.
2342
+ * Returns the input unchanged when no enum map is wired or no referenced enum resolves.
2343
+ *
2344
+ * @param output - The resolved model-info output.
2345
+ * @param enums - The optional enum value tables keyed by name.
2346
+ * @returns The output, with an `enums` section attached when detailed rows reference known enums.
2347
+ */ function attachReferencedEnums(output, enums) {
2348
+ var result = output;
2349
+ if (enums != null) {
2350
+ var referenced = new Set();
2351
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
2352
+ try {
2353
+ for(var _iterator = collectDetailedEntries(output)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
2354
+ var entry = _step.value;
2355
+ collectFieldEnumRefs(entry.fields, referenced);
2356
+ }
2357
+ } catch (err) {
2358
+ _didIteratorError = true;
2359
+ _iteratorError = err;
2360
+ } finally{
2361
+ try {
2362
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
2363
+ _iterator.return();
2364
+ }
2365
+ } finally{
2366
+ if (_didIteratorError) {
2367
+ throw _iteratorError;
2368
+ }
2369
+ }
2370
+ }
2371
+ var tables = [];
2372
+ var _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
2373
+ try {
2374
+ for(var _iterator1 = referenced[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
2375
+ var name = _step1.value;
2376
+ var table = enums[name];
2377
+ if (table != null) tables.push(table);
2378
+ }
2379
+ } catch (err) {
2380
+ _didIteratorError1 = true;
2381
+ _iteratorError1 = err;
2382
+ } finally{
2383
+ try {
2384
+ if (!_iteratorNormalCompletion1 && _iterator1.return != null) {
2385
+ _iterator1.return();
2386
+ }
2387
+ } finally{
2388
+ if (_didIteratorError1) {
2389
+ throw _iteratorError1;
2390
+ }
2391
+ }
2392
+ }
2393
+ if (tables.length > 0) {
2394
+ result = _object_spread_props$2(_object_spread$4({}, output), {
2395
+ enums: tables
2396
+ });
2397
+ }
2398
+ }
2399
+ return result;
2400
+ }
2401
+ /**
2402
+ * Collects the full {@link McpManifestModelEntry} rows (those carrying a persisted `fields` array)
2403
+ * from a model-info output. Summary rows (which expose `fieldCount`, not `fields`) are skipped.
2404
+ *
2405
+ * @param output - The resolved model-info output.
2406
+ * @returns The detailed model entries present in the output.
2407
+ */ function collectDetailedEntries(output) {
2408
+ var rows = [];
2409
+ if (output.mode === 'single') {
2410
+ rows.push(output.model);
2411
+ } else if (output.mode === 'list' || output.mode === 'multiple') {
2412
+ var _rows;
2413
+ (_rows = rows).push.apply(_rows, _to_consumable_array$5(output.models));
2414
+ }
2415
+ return rows.filter(function(row) {
2416
+ return Array.isArray(row.fields);
2417
+ });
2418
+ }
2419
+ function collectFieldEnumRefs(fields, into) {
2420
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
2421
+ try {
2422
+ for(var _iterator = fields[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
2423
+ var field = _step.value;
2424
+ if (field.enumRef) into.add(field.enumRef);
2425
+ if (field.nestedFields) collectFieldEnumRefs(field.nestedFields, into);
2426
+ }
2427
+ } catch (err) {
2428
+ _didIteratorError = true;
2429
+ _iteratorError = err;
2430
+ } finally{
2431
+ try {
2432
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
2433
+ _iterator.return();
2434
+ }
2435
+ } finally{
2436
+ if (_didIteratorError) {
2437
+ throw _iteratorError;
2438
+ }
2439
+ }
2440
+ }
2441
+ }
2442
+ // MARK: Input parsing
2443
+ function parseModelInfoInput(args) {
2444
+ var modelQueries = parseModelArg(args['model']);
2445
+ var modelGroup = parseModelGroupArg(args['modelGroup']);
2446
+ var all = parseBooleanArg(args['all'], 'all');
2447
+ var fields = parseBooleanArg(args['fields'], 'fields');
2448
+ return _object_spread$4(_object_spread_props$2(_object_spread$4(_object_spread_props$2(_object_spread$4({}, modelQueries == null ? {} : {
2449
+ modelQueries: modelQueries
2450
+ }), {
2451
+ modelIsArray: Array.isArray(args['model'])
1761
2452
  }), modelGroup == null ? {} : {
1762
2453
  modelGroup: modelGroup
1763
2454
  }), {
@@ -1772,14 +2463,14 @@ function parseModelArg(raw) {
1772
2463
  result = undefined;
1773
2464
  } else if (typeof raw === 'string') {
1774
2465
  result = [
1775
- requireNonEmptyString(raw, 'model')
2466
+ requireNonEmptyString$1(raw, 'model')
1776
2467
  ];
1777
2468
  } else if (Array.isArray(raw)) {
1778
2469
  if (raw.length === 0) {
1779
2470
  throw new Error('model-info: "model" array must contain at least one entry.');
1780
2471
  }
1781
2472
  result = raw.map(function(value, index) {
1782
- return requireNonEmptyString(value, "model[".concat(index, "]"));
2473
+ return requireNonEmptyString$1(value, "model[".concat(index, "]"));
1783
2474
  });
1784
2475
  } else {
1785
2476
  throw new TypeError('model-info: "model" must be a string or an array of strings when provided.');
@@ -1791,7 +2482,7 @@ function parseModelGroupArg(raw) {
1791
2482
  if (raw == null) {
1792
2483
  result = undefined;
1793
2484
  } else if (typeof raw === 'string') {
1794
- result = requireNonEmptyString(raw, 'modelGroup');
2485
+ result = requireNonEmptyString$1(raw, 'modelGroup');
1795
2486
  } else {
1796
2487
  throw new TypeError('model-info: "modelGroup" must be a string when provided.');
1797
2488
  }
@@ -1808,7 +2499,7 @@ function parseBooleanArg(raw, label) {
1808
2499
  }
1809
2500
  return result;
1810
2501
  }
1811
- function requireNonEmptyString(value, label) {
2502
+ function requireNonEmptyString$1(value, label) {
1812
2503
  if (typeof value !== 'string') {
1813
2504
  throw new TypeError('model-info: "'.concat(label, '" must be a string.'));
1814
2505
  }
@@ -1917,7 +2608,7 @@ function toModelRow(entry, detailed) {
1917
2608
  return detailed ? entry : toSummaryRow(entry);
1918
2609
  }
1919
2610
  function toSummaryRow(entry) {
1920
- return _object_spread$2({
2611
+ return _object_spread$4({
1921
2612
  modelType: entry.modelType,
1922
2613
  modelName: entry.modelName,
1923
2614
  identityConst: entry.identityConst,
@@ -1945,7 +2636,7 @@ function toSummaryRow(entry) {
1945
2636
  *
1946
2637
  * @__NO_SIDE_EFFECTS__
1947
2638
  */ function computeGroupCounts(manifest) {
1948
- var grouped = makeValuesGroupMap(_to_consumable_array$4(manifest), function(entry) {
2639
+ var grouped = makeValuesGroupMap(_to_consumable_array$5(manifest), function(entry) {
1949
2640
  var _entry_modelGroup;
1950
2641
  return (_entry_modelGroup = entry.modelGroup) !== null && _entry_modelGroup !== void 0 ? _entry_modelGroup : MODEL_INFO_UNGROUPED_LABEL;
1951
2642
  });
@@ -2155,6 +2846,48 @@ var MODEL_ROW_SCHEMA = {
2155
2846
  }
2156
2847
  }
2157
2848
  };
2849
+ /**
2850
+ * JSON-schema for one enum value→label table. Shared with the `enum-info` tool so both advertise the
2851
+ * identical enum shape.
2852
+ */ var ENUM_TABLE_SCHEMA = {
2853
+ type: 'object',
2854
+ required: [
2855
+ 'name',
2856
+ 'values'
2857
+ ],
2858
+ properties: {
2859
+ name: {
2860
+ type: 'string'
2861
+ },
2862
+ description: {
2863
+ type: 'string'
2864
+ },
2865
+ values: {
2866
+ type: 'array',
2867
+ items: {
2868
+ type: 'object',
2869
+ required: [
2870
+ 'name',
2871
+ 'value'
2872
+ ],
2873
+ properties: {
2874
+ name: {
2875
+ type: 'string'
2876
+ },
2877
+ value: {
2878
+ type: [
2879
+ 'string',
2880
+ 'number'
2881
+ ]
2882
+ },
2883
+ description: {
2884
+ type: 'string'
2885
+ }
2886
+ }
2887
+ }
2888
+ }
2889
+ }
2890
+ };
2158
2891
  // MCP's `tools/list` validator requires `outputSchema.type === 'object'` at the root
2159
2892
  // (see @modelcontextprotocol/sdk Zod schema), so the variants are expressed via a top-level
2160
2893
  // object whose nested `oneOf` discriminates by the `mode` literal.
@@ -2210,7 +2943,7 @@ var MODEL_INFO_OUTPUT_SCHEMA = {
2210
2943
  type: 'string',
2211
2944
  description: 'Present when `mode` is `"list"` from a `modelGroup` filter. The canonical group name.'
2212
2945
  },
2213
- model: _object_spread_props(_object_spread$2({}, MODEL_ROW_SCHEMA), {
2946
+ model: _object_spread_props$2(_object_spread$4({}, MODEL_ROW_SCHEMA), {
2214
2947
  description: 'Present when `mode` is `"single"`. The matched model entry (full detail unless `fields:false`).'
2215
2948
  }),
2216
2949
  notFound: {
@@ -2231,6 +2964,11 @@ var MODEL_INFO_OUTPUT_SCHEMA = {
2231
2964
  }
2232
2965
  }
2233
2966
  }
2967
+ },
2968
+ enums: {
2969
+ type: 'array',
2970
+ description: 'Value→label tables for the enums referenced by the returned models’ fields. Present only when full field detail is returned (single/multiple modes, or list/all with `fields:true`) and at least one referenced enum is registered.',
2971
+ items: ENUM_TABLE_SCHEMA
2234
2972
  }
2235
2973
  },
2236
2974
  oneOf: [
@@ -2278,7 +3016,7 @@ var MODEL_INFO_OUTPUT_SCHEMA = {
2278
3016
  ]
2279
3017
  };
2280
3018
 
2281
- function _define_property$5(obj, key, value) {
3019
+ function _define_property$6(obj, key, value) {
2282
3020
  if (key in obj) {
2283
3021
  Object.defineProperty(obj, key, {
2284
3022
  value: value,
@@ -2291,7 +3029,7 @@ function _define_property$5(obj, key, value) {
2291
3029
  }
2292
3030
  return obj;
2293
3031
  }
2294
- function _object_spread$1(target) {
3032
+ function _object_spread$3(target) {
2295
3033
  for(var i = 1; i < arguments.length; i++){
2296
3034
  var source = arguments[i] != null ? arguments[i] : {};
2297
3035
  var ownKeys = Object.keys(source);
@@ -2301,7 +3039,7 @@ function _object_spread$1(target) {
2301
3039
  }));
2302
3040
  }
2303
3041
  ownKeys.forEach(function(key) {
2304
- _define_property$5(target, key, source[key]);
3042
+ _define_property$6(target, key, source[key]);
2305
3043
  });
2306
3044
  }
2307
3045
  return target;
@@ -2340,7 +3078,7 @@ function _object_spread$1(target) {
2340
3078
  };
2341
3079
  var name = MODEL_DECODE_TOOL_NAME;
2342
3080
  var description = 'Decode a Firestore model key ("prefix/id", or subcollection path "parentPrefix/parentId/childPrefix/childId") into { leaf, ancestors, unresolvedPrefixes } using the registered manifest ('.concat(deps.manifest.length, " model").concat(deps.manifest.length === 1 ? '' : 's', ").");
2343
- return {
3081
+ return buildStaticToolDefinition({
2344
3082
  name: name,
2345
3083
  description: description,
2346
3084
  inputSchema: MODEL_DECODE_INPUT_SCHEMA,
@@ -2350,20 +3088,11 @@ function _object_spread$1(target) {
2350
3088
  modelType: MODEL_DECODE_DISPATCH_MODEL_TYPE
2351
3089
  },
2352
3090
  staticHandler: handler,
2353
- filterMetadata: {
2354
- visibilityKind: 'declarative',
2355
- rule: {
2356
- requireAuthenticated: true
2357
- },
2358
- effectiveReadOnly: true
2359
- },
2360
- staticWireEntry: buildStaticWireEntry({
2361
- name: name,
2362
- description: description,
2363
- inputSchema: MODEL_DECODE_INPUT_SCHEMA,
2364
- outputSchema: MODEL_DECODE_OUTPUT_SCHEMA
2365
- })
2366
- };
3091
+ effectiveReadOnly: true,
3092
+ rule: {
3093
+ requireAuthenticated: true
3094
+ }
3095
+ });
2367
3096
  }
2368
3097
  // MARK: Handler
2369
3098
  function modelDecodeToolHandler(args, _ctx, deps) {
@@ -2439,7 +3168,7 @@ function parseModelDecodeInput(args) {
2439
3168
  };
2440
3169
  }
2441
3170
  function toSegment(prefix, id, entry) {
2442
- var segment = entry ? _object_spread$1({
3171
+ var segment = entry ? _object_spread$3({
2443
3172
  prefix: prefix,
2444
3173
  id: id,
2445
3174
  modelType: entry.modelType,
@@ -2537,18 +3266,210 @@ var MODEL_DECODE_OUTPUT_SCHEMA = {
2537
3266
  }
2538
3267
  };
2539
3268
 
2540
- function _array_like_to_array$4(arr, len) {
3269
+ // MARK: Constants
3270
+ /**
3271
+ * Reserved tool name for the built-in `enum-info` static tool.
3272
+ */ var ENUM_INFO_TOOL_NAME = 'enum-info';
3273
+ /**
3274
+ * Synthetic call type used in the tool's dispatch identity. Mirrors the `info` call shared with
3275
+ * `model-info`.
3276
+ */ var ENUM_INFO_DISPATCH_CALL = 'info';
3277
+ /**
3278
+ * Synthetic model type used in the tool's dispatch identity. Apps avoiding collisions should not
3279
+ * register a real model literally named "enum".
3280
+ */ var ENUM_INFO_DISPATCH_MODEL_TYPE = 'enum';
3281
+ // MARK: Factory
3282
+ /**
3283
+ * Builds the built-in `enum-info` MCP tool definition — the symmetric counterpart to `model-info`
3284
+ * for raw enum decoding.
3285
+ *
3286
+ * Resolves each requested enum name against the manifest's `enums` block and returns the matching
3287
+ * value→label tables; misses land in `notFound`. A bare string is treated as a one-element array.
3288
+ *
3289
+ * Output is delivered as both stringified JSON in `content[0].text` and `structuredContent` so MCP
3290
+ * clients can consume either form.
3291
+ *
3292
+ * @param deps - The frozen enum value tables loaded at boot from the MCP manifest JSON.
3293
+ * @returns A statically-registered {@link McpToolDefinition} ready to be appended to the MCP server
3294
+ * factory's tool registry.
3295
+ */ function createEnumInfoTool(deps) {
3296
+ var handler = function handler(args, ctx) {
3297
+ return Promise.resolve(enumInfoToolHandler(args, ctx, deps));
3298
+ };
3299
+ var name = ENUM_INFO_TOOL_NAME;
3300
+ var count = Object.keys(deps.enums).length;
3301
+ var description = "Look up enum value→label tables by name (".concat(count, " enum").concat(count === 1 ? '' : 's', ' registered). Pass `enum` (a string or array of enum declaration names, e.g. "JobWorkerTimesheetState") to decode the raw persisted integer/string values a model field stores. Misses are reported in `notFound`.');
3302
+ return buildStaticToolDefinition({
3303
+ name: name,
3304
+ description: description,
3305
+ inputSchema: ENUM_INFO_INPUT_SCHEMA,
3306
+ outputSchema: ENUM_INFO_OUTPUT_SCHEMA,
3307
+ dispatch: {
3308
+ call: ENUM_INFO_DISPATCH_CALL,
3309
+ modelType: ENUM_INFO_DISPATCH_MODEL_TYPE
3310
+ },
3311
+ staticHandler: handler,
3312
+ effectiveReadOnly: true,
3313
+ rule: {
3314
+ requireAuthenticated: true
3315
+ }
3316
+ });
3317
+ }
3318
+ // MARK: Handler
3319
+ function enumInfoToolHandler(args, _ctx, deps) {
3320
+ var result;
3321
+ try {
3322
+ var names = parseEnumInfoInput(args);
3323
+ var output = resolveEnumInfoOutput(names, deps.enums);
3324
+ result = {
3325
+ content: [
3326
+ {
3327
+ type: 'text',
3328
+ text: JSON.stringify(output)
3329
+ }
3330
+ ],
3331
+ structuredContent: output
3332
+ };
3333
+ } catch (error) {
3334
+ result = formatMcpToolErrorResponse(error);
3335
+ }
3336
+ return result;
3337
+ }
3338
+ /**
3339
+ * Resolves each requested enum name against the registered value tables.
3340
+ *
3341
+ * @param names - The requested enum declaration names.
3342
+ * @param enums - The registered enum value tables keyed by name.
3343
+ * @returns The matched tables, with any unmatched names in `notFound`.
3344
+ *
3345
+ * @__NO_SIDE_EFFECTS__
3346
+ */ function resolveEnumInfoOutput(names, enums) {
3347
+ var found = [];
3348
+ var notFound = [];
3349
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
3350
+ try {
3351
+ for(var _iterator = names[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
3352
+ var name = _step.value;
3353
+ var table = enums[name];
3354
+ if (table == null) {
3355
+ notFound.push(name);
3356
+ } else {
3357
+ found.push(table);
3358
+ }
3359
+ }
3360
+ } catch (err) {
3361
+ _didIteratorError = true;
3362
+ _iteratorError = err;
3363
+ } finally{
3364
+ try {
3365
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
3366
+ _iterator.return();
3367
+ }
3368
+ } finally{
3369
+ if (_didIteratorError) {
3370
+ throw _iteratorError;
3371
+ }
3372
+ }
3373
+ }
3374
+ return notFound.length > 0 ? {
3375
+ enums: found,
3376
+ notFound: notFound
3377
+ } : {
3378
+ enums: found
3379
+ };
3380
+ }
3381
+ // MARK: Input parsing
3382
+ function parseEnumInfoInput(args) {
3383
+ var raw = args['enum'];
3384
+ var result;
3385
+ if (raw == null) {
3386
+ throw new Error('enum-info: "enum" is required and must be a string or an array of strings.');
3387
+ } else if (typeof raw === 'string') {
3388
+ result = [
3389
+ requireNonEmptyString(raw, 'enum')
3390
+ ];
3391
+ } else if (Array.isArray(raw)) {
3392
+ if (raw.length === 0) {
3393
+ throw new Error('enum-info: "enum" array must contain at least one entry.');
3394
+ }
3395
+ result = raw.map(function(value, index) {
3396
+ return requireNonEmptyString(value, "enum[".concat(index, "]"));
3397
+ });
3398
+ } else {
3399
+ throw new TypeError('enum-info: "enum" must be a string or an array of strings.');
3400
+ }
3401
+ return result;
3402
+ }
3403
+ function requireNonEmptyString(value, label) {
3404
+ if (typeof value !== 'string') {
3405
+ throw new TypeError('enum-info: "'.concat(label, '" must be a string.'));
3406
+ }
3407
+ var trimmed = value.trim();
3408
+ if (trimmed.length === 0) {
3409
+ throw new Error('enum-info: "'.concat(label, '" must be a non-empty string.'));
3410
+ }
3411
+ return trimmed;
3412
+ }
3413
+ // MARK: Schemas
3414
+ var ENUM_INFO_INPUT_SCHEMA = {
3415
+ type: 'object',
3416
+ required: [
3417
+ 'enum'
3418
+ ],
3419
+ properties: {
3420
+ enum: {
3421
+ description: 'One enum name (string) or many (string array), each matched exactly by declaration name (e.g. "JobWorkerTimesheetState"). Misses are reported in `notFound`.',
3422
+ oneOf: [
3423
+ {
3424
+ type: 'string',
3425
+ minLength: 1
3426
+ },
3427
+ {
3428
+ type: 'array',
3429
+ minItems: 1,
3430
+ items: {
3431
+ type: 'string',
3432
+ minLength: 1
3433
+ }
3434
+ }
3435
+ ]
3436
+ }
3437
+ },
3438
+ additionalProperties: false
3439
+ };
3440
+ var ENUM_INFO_OUTPUT_SCHEMA = {
3441
+ type: 'object',
3442
+ required: [
3443
+ 'enums'
3444
+ ],
3445
+ properties: {
3446
+ enums: {
3447
+ type: 'array',
3448
+ description: 'The resolved enum value→label tables, in request order.',
3449
+ items: ENUM_TABLE_SCHEMA
3450
+ },
3451
+ notFound: {
3452
+ type: 'array',
3453
+ description: 'Requested enum names that matched no registered enum.',
3454
+ items: {
3455
+ type: 'string'
3456
+ }
3457
+ }
3458
+ }
3459
+ };
3460
+
3461
+ function _array_like_to_array$5(arr, len) {
2541
3462
  if (len == null || len > arr.length) len = arr.length;
2542
3463
  for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
2543
3464
  return arr2;
2544
3465
  }
2545
- function _array_with_holes$2(arr) {
3466
+ function _array_with_holes$3(arr) {
2546
3467
  if (Array.isArray(arr)) return arr;
2547
3468
  }
2548
- function _array_without_holes$3(arr) {
2549
- if (Array.isArray(arr)) return _array_like_to_array$4(arr);
3469
+ function _array_without_holes$4(arr) {
3470
+ if (Array.isArray(arr)) return _array_like_to_array$5(arr);
2550
3471
  }
2551
- function _define_property$4(obj, key, value) {
3472
+ function _define_property$5(obj, key, value) {
2552
3473
  if (key in obj) {
2553
3474
  Object.defineProperty(obj, key, {
2554
3475
  value: value,
@@ -2561,10 +3482,10 @@ function _define_property$4(obj, key, value) {
2561
3482
  }
2562
3483
  return obj;
2563
3484
  }
2564
- function _iterable_to_array$3(iter) {
3485
+ function _iterable_to_array$4(iter) {
2565
3486
  if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
2566
3487
  }
2567
- function _iterable_to_array_limit$2(arr, i) {
3488
+ function _iterable_to_array_limit$3(arr, i) {
2568
3489
  var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
2569
3490
  if (_i == null) return;
2570
3491
  var _arr = [];
@@ -2588,13 +3509,13 @@ function _iterable_to_array_limit$2(arr, i) {
2588
3509
  }
2589
3510
  return _arr;
2590
3511
  }
2591
- function _non_iterable_rest$2() {
3512
+ function _non_iterable_rest$3() {
2592
3513
  throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
2593
3514
  }
2594
- function _non_iterable_spread$3() {
3515
+ function _non_iterable_spread$4() {
2595
3516
  throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
2596
3517
  }
2597
- function _object_spread(target) {
3518
+ function _object_spread$2(target) {
2598
3519
  for(var i = 1; i < arguments.length; i++){
2599
3520
  var source = arguments[i] != null ? arguments[i] : {};
2600
3521
  var ownKeys = Object.keys(source);
@@ -2604,24 +3525,24 @@ function _object_spread(target) {
2604
3525
  }));
2605
3526
  }
2606
3527
  ownKeys.forEach(function(key) {
2607
- _define_property$4(target, key, source[key]);
3528
+ _define_property$5(target, key, source[key]);
2608
3529
  });
2609
3530
  }
2610
3531
  return target;
2611
3532
  }
2612
- function _sliced_to_array$2(arr, i) {
2613
- return _array_with_holes$2(arr) || _iterable_to_array_limit$2(arr, i) || _unsupported_iterable_to_array$4(arr, i) || _non_iterable_rest$2();
3533
+ function _sliced_to_array$3(arr, i) {
3534
+ return _array_with_holes$3(arr) || _iterable_to_array_limit$3(arr, i) || _unsupported_iterable_to_array$5(arr, i) || _non_iterable_rest$3();
2614
3535
  }
2615
- function _to_consumable_array$3(arr) {
2616
- return _array_without_holes$3(arr) || _iterable_to_array$3(arr) || _unsupported_iterable_to_array$4(arr) || _non_iterable_spread$3();
3536
+ function _to_consumable_array$4(arr) {
3537
+ return _array_without_holes$4(arr) || _iterable_to_array$4(arr) || _unsupported_iterable_to_array$5(arr) || _non_iterable_spread$4();
2617
3538
  }
2618
- function _unsupported_iterable_to_array$4(o, minLen) {
3539
+ function _unsupported_iterable_to_array$5(o, minLen) {
2619
3540
  if (!o) return;
2620
- if (typeof o === "string") return _array_like_to_array$4(o, minLen);
3541
+ if (typeof o === "string") return _array_like_to_array$5(o, minLen);
2621
3542
  var n = Object.prototype.toString.call(o).slice(8, -1);
2622
3543
  if (n === "Object" && o.constructor) n = o.constructor.name;
2623
3544
  if (n === "Map" || n === "Set") return Array.from(n);
2624
- if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$4(o, minLen);
3545
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$5(o, minLen);
2625
3546
  }
2626
3547
  // MARK: Constants
2627
3548
  /**
@@ -2655,7 +3576,7 @@ function _unsupported_iterable_to_array$4(o, minLen) {
2655
3576
  var name = WHOAMI_TOOL_NAME;
2656
3577
  var appLabel = (_ref = (_deps_auth_app = deps.auth.app) === null || _deps_auth_app === void 0 ? void 0 : _deps_auth_app.app) !== null && _ref !== void 0 ? _ref : 'this server';
2657
3578
  var description = "Reports the authenticated caller's uid, raw custom claims, and decoded role set for `".concat(appLabel, "`. Returns `authenticated: false` for anonymous callers. Claim descriptions are drawn from the build-time auth manifest; only keys actually present on the live token are detailed.");
2658
- return {
3579
+ return buildStaticToolDefinition({
2659
3580
  name: name,
2660
3581
  description: description,
2661
3582
  inputSchema: WHOAMI_INPUT_SCHEMA,
@@ -2665,18 +3586,9 @@ function _unsupported_iterable_to_array$4(o, minLen) {
2665
3586
  modelType: WHOAMI_DISPATCH_MODEL_TYPE
2666
3587
  },
2667
3588
  staticHandler: handler,
2668
- filterMetadata: {
2669
- visibilityKind: 'declarative',
2670
- rule: {},
2671
- effectiveReadOnly: true
2672
- },
2673
- staticWireEntry: buildStaticWireEntry({
2674
- name: name,
2675
- description: description,
2676
- inputSchema: WHOAMI_INPUT_SCHEMA,
2677
- outputSchema: WHOAMI_OUTPUT_SCHEMA
2678
- })
2679
- };
3589
+ effectiveReadOnly: true,
3590
+ rule: {}
3591
+ });
2680
3592
  }
2681
3593
  // MARK: Handler
2682
3594
  function whoamiToolHandler(ctx, deps) {
@@ -2695,14 +3607,14 @@ function whoamiToolHandler(ctx, deps) {
2695
3607
  }
2696
3608
  var token = (_auth_token = auth.token) !== null && _auth_token !== void 0 ? _auth_token : {};
2697
3609
  var presentKeys = Object.entries(token).filter(function(param) {
2698
- var _param = _sliced_to_array$2(param, 2), value = _param[1];
3610
+ var _param = _sliced_to_array$3(param, 2), value = _param[1];
2699
3611
  return value !== undefined;
2700
3612
  }).map(function(param) {
2701
- var _param = _sliced_to_array$2(param, 1), key = _param[0];
3613
+ var _param = _sliced_to_array$3(param, 1), key = _param[0];
2702
3614
  return key;
2703
3615
  });
2704
3616
  var roleSet = deps.roleReader == null ? new Set() : deps.roleReader(token);
2705
- var roles = _to_consumable_array$3(roleSet).sort(function(a, b) {
3617
+ var roles = _to_consumable_array$4(roleSet).sort(function(a, b) {
2706
3618
  return a.localeCompare(b);
2707
3619
  });
2708
3620
  var claimsByKey = new Map();
@@ -2739,7 +3651,7 @@ function whoamiToolHandler(ctx, deps) {
2739
3651
  if (claim1 == null) {
2740
3652
  unknownClaimKeys.push(key);
2741
3653
  } else {
2742
- claimDetails.push(_object_spread({
3654
+ claimDetails.push(_object_spread$2({
2743
3655
  key: claim1.key,
2744
3656
  description: claim1.description,
2745
3657
  grantedRoles: claim1.mapping.roles,
@@ -2764,10 +3676,10 @@ function whoamiToolHandler(ctx, deps) {
2764
3676
  }
2765
3677
  }
2766
3678
  }
2767
- var output1 = _object_spread({
3679
+ var output1 = _object_spread$2({
2768
3680
  authenticated: true,
2769
3681
  uid: auth.uid,
2770
- claims: _object_spread({}, token),
3682
+ claims: _object_spread$2({}, token),
2771
3683
  roles: roles,
2772
3684
  claimDetails: claimDetails,
2773
3685
  unknownClaimKeys: unknownClaimKeys
@@ -2874,99 +3786,989 @@ function appendUnknownClaimLines(lines, unknownClaimKeys) {
2874
3786
  if (!_iteratorNormalCompletion && _iterator.return != null) {
2875
3787
  _iterator.return();
2876
3788
  }
2877
- } finally{
2878
- if (_didIteratorError) {
2879
- throw _iteratorError;
3789
+ } finally{
3790
+ if (_didIteratorError) {
3791
+ throw _iteratorError;
3792
+ }
3793
+ }
3794
+ }
3795
+ }
3796
+ // MARK: Schemas
3797
+ var WHOAMI_INPUT_SCHEMA = {
3798
+ type: 'object',
3799
+ properties: {},
3800
+ additionalProperties: false
3801
+ };
3802
+ var WHOAMI_OUTPUT_SCHEMA = {
3803
+ type: 'object',
3804
+ required: [
3805
+ 'authenticated',
3806
+ 'claims',
3807
+ 'roles',
3808
+ 'claimDetails',
3809
+ 'unknownClaimKeys'
3810
+ ],
3811
+ description: 'Caller identity, raw token claims, decoded role set, and per-claim descriptions for every key present on the live token.',
3812
+ properties: {
3813
+ authenticated: {
3814
+ type: 'boolean'
3815
+ },
3816
+ uid: {
3817
+ type: 'string'
3818
+ },
3819
+ email: {
3820
+ type: 'string'
3821
+ },
3822
+ emailVerified: {
3823
+ type: 'boolean'
3824
+ },
3825
+ app: {
3826
+ type: 'string'
3827
+ },
3828
+ claims: {
3829
+ type: 'object',
3830
+ additionalProperties: true
3831
+ },
3832
+ roles: {
3833
+ type: 'array',
3834
+ items: {
3835
+ type: 'string'
3836
+ }
3837
+ },
3838
+ claimDetails: {
3839
+ type: 'array',
3840
+ items: {
3841
+ type: 'object',
3842
+ required: [
3843
+ 'key',
3844
+ 'description',
3845
+ 'grantedRoles',
3846
+ 'inverse',
3847
+ 'tags'
3848
+ ],
3849
+ properties: {
3850
+ key: {
3851
+ type: 'string'
3852
+ },
3853
+ interfaceName: {
3854
+ type: 'string'
3855
+ },
3856
+ description: {
3857
+ type: 'string'
3858
+ },
3859
+ grantedRoles: {
3860
+ type: 'array',
3861
+ items: {
3862
+ type: 'string'
3863
+ }
3864
+ },
3865
+ inverse: {
3866
+ type: 'boolean'
3867
+ },
3868
+ tags: {
3869
+ type: 'array',
3870
+ items: {
3871
+ type: 'string'
3872
+ }
3873
+ }
3874
+ }
3875
+ }
3876
+ },
3877
+ unknownClaimKeys: {
3878
+ type: 'array',
3879
+ items: {
3880
+ type: 'string'
3881
+ }
3882
+ }
3883
+ }
3884
+ };
3885
+
3886
+ function _array_like_to_array$4(arr, len) {
3887
+ if (len == null || len > arr.length) len = arr.length;
3888
+ for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
3889
+ return arr2;
3890
+ }
3891
+ function _array_with_holes$2(arr) {
3892
+ if (Array.isArray(arr)) return arr;
3893
+ }
3894
+ function _array_without_holes$3(arr) {
3895
+ if (Array.isArray(arr)) return _array_like_to_array$4(arr);
3896
+ }
3897
+ function asyncGeneratorStep$4(gen, resolve, reject, _next, _throw, key, arg) {
3898
+ try {
3899
+ var info = gen[key](arg);
3900
+ var value = info.value;
3901
+ } catch (error) {
3902
+ reject(error);
3903
+ return;
3904
+ }
3905
+ if (info.done) {
3906
+ resolve(value);
3907
+ } else {
3908
+ Promise.resolve(value).then(_next, _throw);
3909
+ }
3910
+ }
3911
+ function _async_to_generator$4(fn) {
3912
+ return function() {
3913
+ var self = this, args = arguments;
3914
+ return new Promise(function(resolve, reject) {
3915
+ var gen = fn.apply(self, args);
3916
+ function _next(value) {
3917
+ asyncGeneratorStep$4(gen, resolve, reject, _next, _throw, "next", value);
3918
+ }
3919
+ function _throw(err) {
3920
+ asyncGeneratorStep$4(gen, resolve, reject, _next, _throw, "throw", err);
3921
+ }
3922
+ _next(undefined);
3923
+ });
3924
+ };
3925
+ }
3926
+ function _define_property$4(obj, key, value) {
3927
+ if (key in obj) {
3928
+ Object.defineProperty(obj, key, {
3929
+ value: value,
3930
+ enumerable: true,
3931
+ configurable: true,
3932
+ writable: true
3933
+ });
3934
+ } else {
3935
+ obj[key] = value;
3936
+ }
3937
+ return obj;
3938
+ }
3939
+ function _iterable_to_array$3(iter) {
3940
+ if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
3941
+ }
3942
+ function _iterable_to_array_limit$2(arr, i) {
3943
+ var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
3944
+ if (_i == null) return;
3945
+ var _arr = [];
3946
+ var _n = true;
3947
+ var _d = false;
3948
+ var _s, _e;
3949
+ try {
3950
+ for(_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true){
3951
+ _arr.push(_s.value);
3952
+ if (i && _arr.length === i) break;
3953
+ }
3954
+ } catch (err) {
3955
+ _d = true;
3956
+ _e = err;
3957
+ } finally{
3958
+ try {
3959
+ if (!_n && _i["return"] != null) _i["return"]();
3960
+ } finally{
3961
+ if (_d) throw _e;
3962
+ }
3963
+ }
3964
+ return _arr;
3965
+ }
3966
+ function _non_iterable_rest$2() {
3967
+ throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
3968
+ }
3969
+ function _non_iterable_spread$3() {
3970
+ throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
3971
+ }
3972
+ function _object_spread$1(target) {
3973
+ for(var i = 1; i < arguments.length; i++){
3974
+ var source = arguments[i] != null ? arguments[i] : {};
3975
+ var ownKeys = Object.keys(source);
3976
+ if (typeof Object.getOwnPropertySymbols === "function") {
3977
+ ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
3978
+ return Object.getOwnPropertyDescriptor(source, sym).enumerable;
3979
+ }));
3980
+ }
3981
+ ownKeys.forEach(function(key) {
3982
+ _define_property$4(target, key, source[key]);
3983
+ });
3984
+ }
3985
+ return target;
3986
+ }
3987
+ function ownKeys$1(object, enumerableOnly) {
3988
+ var keys = Object.keys(object);
3989
+ if (Object.getOwnPropertySymbols) {
3990
+ var symbols = Object.getOwnPropertySymbols(object);
3991
+ keys.push.apply(keys, symbols);
3992
+ }
3993
+ return keys;
3994
+ }
3995
+ function _object_spread_props$1(target, source) {
3996
+ source = source != null ? source : {};
3997
+ if (Object.getOwnPropertyDescriptors) {
3998
+ Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
3999
+ } else {
4000
+ ownKeys$1(Object(source)).forEach(function(key) {
4001
+ Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
4002
+ });
4003
+ }
4004
+ return target;
4005
+ }
4006
+ function _sliced_to_array$2(arr, i) {
4007
+ return _array_with_holes$2(arr) || _iterable_to_array_limit$2(arr, i) || _unsupported_iterable_to_array$4(arr, i) || _non_iterable_rest$2();
4008
+ }
4009
+ function _to_consumable_array$3(arr) {
4010
+ return _array_without_holes$3(arr) || _iterable_to_array$3(arr) || _unsupported_iterable_to_array$4(arr) || _non_iterable_spread$3();
4011
+ }
4012
+ function _unsupported_iterable_to_array$4(o, minLen) {
4013
+ if (!o) return;
4014
+ if (typeof o === "string") return _array_like_to_array$4(o, minLen);
4015
+ var n = Object.prototype.toString.call(o).slice(8, -1);
4016
+ if (n === "Object" && o.constructor) n = o.constructor.name;
4017
+ if (n === "Map" || n === "Set") return Array.from(n);
4018
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$4(o, minLen);
4019
+ }
4020
+ function _ts_generator$4(thisArg, body) {
4021
+ var f, y, t, _ = {
4022
+ label: 0,
4023
+ sent: function() {
4024
+ if (t[0] & 1) throw t[1];
4025
+ return t[1];
4026
+ },
4027
+ trys: [],
4028
+ ops: []
4029
+ }, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype), d = Object.defineProperty;
4030
+ return d(g, "next", {
4031
+ value: verb(0)
4032
+ }), d(g, "throw", {
4033
+ value: verb(1)
4034
+ }), d(g, "return", {
4035
+ value: verb(2)
4036
+ }), typeof Symbol === "function" && d(g, Symbol.iterator, {
4037
+ value: function() {
4038
+ return this;
4039
+ }
4040
+ }), g;
4041
+ function verb(n) {
4042
+ return function(v) {
4043
+ return step([
4044
+ n,
4045
+ v
4046
+ ]);
4047
+ };
4048
+ }
4049
+ function step(op) {
4050
+ if (f) throw new TypeError("Generator is already executing.");
4051
+ while(g && (g = 0, op[0] && (_ = 0)), _)try {
4052
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
4053
+ if (y = 0, t) op = [
4054
+ op[0] & 2,
4055
+ t.value
4056
+ ];
4057
+ switch(op[0]){
4058
+ case 0:
4059
+ case 1:
4060
+ t = op;
4061
+ break;
4062
+ case 4:
4063
+ _.label++;
4064
+ return {
4065
+ value: op[1],
4066
+ done: false
4067
+ };
4068
+ case 5:
4069
+ _.label++;
4070
+ y = op[1];
4071
+ op = [
4072
+ 0
4073
+ ];
4074
+ continue;
4075
+ case 7:
4076
+ op = _.ops.pop();
4077
+ _.trys.pop();
4078
+ continue;
4079
+ default:
4080
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
4081
+ _ = 0;
4082
+ continue;
4083
+ }
4084
+ if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) {
4085
+ _.label = op[1];
4086
+ break;
4087
+ }
4088
+ if (op[0] === 6 && _.label < t[1]) {
4089
+ _.label = t[1];
4090
+ t = op;
4091
+ break;
4092
+ }
4093
+ if (t && _.label < t[2]) {
4094
+ _.label = t[2];
4095
+ _.ops.push(op);
4096
+ break;
4097
+ }
4098
+ if (t[2]) _.ops.pop();
4099
+ _.trys.pop();
4100
+ continue;
4101
+ }
4102
+ op = body.call(thisArg, _);
4103
+ } catch (e) {
4104
+ op = [
4105
+ 6,
4106
+ e
4107
+ ];
4108
+ y = 0;
4109
+ } finally{
4110
+ f = t = 0;
4111
+ }
4112
+ if (op[0] & 5) throw op[1];
4113
+ return {
4114
+ value: op[0] ? op[1] : void 0,
4115
+ done: true
4116
+ };
4117
+ }
4118
+ }
4119
+ // MARK: Constants
4120
+ /**
4121
+ * Reserved tool name for the built-in `url-models` static tool.
4122
+ */ var URL_MODELS_TOOL_NAME = 'url-models';
4123
+ /**
4124
+ * Synthetic call type used in the tool's dispatch identity.
4125
+ */ var URL_MODELS_DISPATCH_CALL = 'url-models';
4126
+ /**
4127
+ * Synthetic model type used in the tool's dispatch identity. The tool isn't
4128
+ * bound to a real model (the `url` is its input), so we use the literal "route".
4129
+ */ var URL_MODELS_DISPATCH_MODEL_TYPE = 'route';
4130
+ /**
4131
+ * Placeholder segment standing for the caller's authenticated uid.
4132
+ */ var AUTH_UID_PLACEHOLDER = '{authUid}';
4133
+ // MARK: Factory
4134
+ /**
4135
+ * Builds the built-in `url-models` MCP tool definition.
4136
+ *
4137
+ * Matches a pasted app URL against the build-time route manifest and returns the
4138
+ * Firestore models the page renders — model types plus concrete keys with the
4139
+ * route params and `{authUid}` substituted. Optionally filtered to specific
4140
+ * `models`, reduced to keys-only, or loaded via the same permission-checked read
4141
+ * path as `model-get`.
4142
+ *
4143
+ * A URL that matches no state returns a structured `{ matched: null, candidates }`
4144
+ * (not an error) so the caller can suggest near-misses.
4145
+ *
4146
+ * @param deps - The route manifest plus the shared read-documents / identity callbacks.
4147
+ * @returns A statically-registered {@link McpToolDefinition}.
4148
+ */ function createUrlModelsTool(deps) {
4149
+ var handler = function handler(args, ctx) {
4150
+ return urlModelsToolHandler(args, ctx, deps);
4151
+ };
4152
+ var name = URL_MODELS_TOOL_NAME;
4153
+ var description = 'Decode an app URL (or path) into the Firestore models its page renders. Pass a URL like `https://app.example.co/worker/abc/timesheets/list`; the tool matches it against the build-time route manifest and returns each model type plus the concrete key (route params + `{authUid}` substituted). Use `models` to filter to specific types, `keysOnly` for just the resolved keys, or `load: true` to fetch the documents via the same permission-checked path as `model-get` (cannot combine `load` with `keysOnly`).';
4154
+ return buildStaticToolDefinition({
4155
+ name: name,
4156
+ description: description,
4157
+ inputSchema: URL_MODELS_INPUT_SCHEMA,
4158
+ outputSchema: URL_MODELS_OUTPUT_SCHEMA,
4159
+ dispatch: {
4160
+ call: URL_MODELS_DISPATCH_CALL,
4161
+ modelType: URL_MODELS_DISPATCH_MODEL_TYPE
4162
+ },
4163
+ staticHandler: handler,
4164
+ effectiveReadOnly: true,
4165
+ rule: {
4166
+ requireAuthenticated: true
4167
+ }
4168
+ });
4169
+ }
4170
+ // MARK: Handler
4171
+ function urlModelsToolHandler(args, ctx, deps) {
4172
+ return _async_to_generator$4(function() {
4173
+ var result, input, payload, error;
4174
+ return _ts_generator$4(this, function(_state) {
4175
+ switch(_state.label){
4176
+ case 0:
4177
+ _state.trys.push([
4178
+ 0,
4179
+ 2,
4180
+ ,
4181
+ 3
4182
+ ]);
4183
+ input = parseUrlModelsInput(args);
4184
+ return [
4185
+ 4,
4186
+ resolveUrlModels(input, ctx, deps)
4187
+ ];
4188
+ case 1:
4189
+ payload = _state.sent();
4190
+ result = {
4191
+ content: [
4192
+ {
4193
+ type: 'text',
4194
+ text: JSON.stringify(payload)
4195
+ }
4196
+ ],
4197
+ structuredContent: payload
4198
+ };
4199
+ return [
4200
+ 3,
4201
+ 3
4202
+ ];
4203
+ case 2:
4204
+ error = _state.sent();
4205
+ result = formatMcpToolErrorResponse(error);
4206
+ return [
4207
+ 3,
4208
+ 3
4209
+ ];
4210
+ case 3:
4211
+ return [
4212
+ 2,
4213
+ result
4214
+ ];
4215
+ }
4216
+ });
4217
+ })();
4218
+ }
4219
+ function parseUrlModelsInput(args) {
4220
+ var url = args.url;
4221
+ if (typeof url !== 'string' || url.length === 0) {
4222
+ throw new Error('url-models: "url" is required and must be a non-empty string.');
4223
+ }
4224
+ var keysOnly = args.keysOnly === true;
4225
+ var load = args.load === true;
4226
+ if (keysOnly && load) {
4227
+ throw new Error('url-models: "keysOnly" and "load" cannot be combined — "keysOnly" returns keys without loading documents.');
4228
+ }
4229
+ var models;
4230
+ if (args.models !== undefined) {
4231
+ if (!Array.isArray(args.models) || args.models.some(function(m) {
4232
+ return typeof m !== 'string';
4233
+ })) {
4234
+ throw new Error('url-models: "models" must be an array of model-type strings.');
4235
+ }
4236
+ models = args.models;
4237
+ }
4238
+ var currentUserUid;
4239
+ if (args.currentUserUid !== undefined) {
4240
+ if (typeof args.currentUserUid !== 'string' || args.currentUserUid.length === 0) {
4241
+ throw new Error('url-models: "currentUserUid" must be a non-empty string.');
4242
+ }
4243
+ currentUserUid = args.currentUserUid;
4244
+ }
4245
+ return _object_spread$1(_object_spread_props$1(_object_spread$1({
4246
+ url: url
4247
+ }, models === undefined ? {} : {
4248
+ models: models
4249
+ }), {
4250
+ keysOnly: keysOnly,
4251
+ load: load
4252
+ }), currentUserUid === undefined ? {} : {
4253
+ currentUserUid: currentUserUid
4254
+ });
4255
+ }
4256
+ function resolveUrlModels(input, ctx, deps) {
4257
+ return _async_to_generator$4(function() {
4258
+ var matchResult, payload, _tmp;
4259
+ return _ts_generator$4(this, function(_state) {
4260
+ switch(_state.label){
4261
+ case 0:
4262
+ matchResult = matchRouteManifestUrl({
4263
+ manifest: deps.routeManifest,
4264
+ url: input.url
4265
+ });
4266
+ if (!(matchResult.kind === 'none')) return [
4267
+ 3,
4268
+ 1
4269
+ ];
4270
+ payload = {
4271
+ url: input.url,
4272
+ pathname: matchResult.pathname,
4273
+ matched: null,
4274
+ candidates: matchResult.candidates.map(stateSummary)
4275
+ };
4276
+ return [
4277
+ 3,
4278
+ 4
4279
+ ];
4280
+ case 1:
4281
+ if (!(matchResult.kind === 'ambiguous')) return [
4282
+ 3,
4283
+ 2
4284
+ ];
4285
+ payload = {
4286
+ url: input.url,
4287
+ pathname: matchResult.pathname,
4288
+ matched: null,
4289
+ ambiguous: matchResult.states.map(stateSummary)
4290
+ };
4291
+ return [
4292
+ 3,
4293
+ 4
4294
+ ];
4295
+ case 2:
4296
+ _tmp = {
4297
+ url: input.url,
4298
+ pathname: matchResult.pathname
4299
+ };
4300
+ return [
4301
+ 4,
4302
+ buildMatchedPayload({
4303
+ match: matchResult,
4304
+ input: input,
4305
+ ctx: ctx,
4306
+ deps: deps
4307
+ })
4308
+ ];
4309
+ case 3:
4310
+ payload = (_tmp.matched = _state.sent(), _tmp);
4311
+ _state.label = 4;
4312
+ case 4:
4313
+ return [
4314
+ 2,
4315
+ payload
4316
+ ];
4317
+ }
4318
+ });
4319
+ })();
4320
+ }
4321
+ function stateSummary(state) {
4322
+ return {
4323
+ name: state.name,
4324
+ fullUrl: state.fullUrl
4325
+ };
4326
+ }
4327
+ function buildMatchedPayload(args) {
4328
+ return _async_to_generator$4(function() {
4329
+ var _input_currentUserUid, _ctx_auth, match, input, ctx, deps, uid, filtered, resolved, loaded, _tmp;
4330
+ return _ts_generator$4(this, function(_state) {
4331
+ switch(_state.label){
4332
+ case 0:
4333
+ match = args.match, input = args.input, ctx = args.ctx, deps = args.deps;
4334
+ // `currentUserUid` (when supplied) overrides the uid used to fill `{authUid}` placeholders so a
4335
+ // caller can preview another user's page-models. The document-load path still uses `ctx.auth`, so
4336
+ // this is a key-substitution preview only — no privilege escalation on the actual reads.
4337
+ uid = (_input_currentUserUid = input.currentUserUid) !== null && _input_currentUserUid !== void 0 ? _input_currentUserUid : (_ctx_auth = ctx.auth) === null || _ctx_auth === void 0 ? void 0 : _ctx_auth.uid;
4338
+ filtered = input.models === undefined ? match.state.models : match.state.models.filter(function(m) {
4339
+ var _input_models;
4340
+ return (_input_models = input.models) === null || _input_models === void 0 ? void 0 : _input_models.includes(m.modelType);
4341
+ });
4342
+ resolved = filtered.map(function(entry) {
4343
+ return resolveModelEntry({
4344
+ entry: entry,
4345
+ params: match.params,
4346
+ uid: uid,
4347
+ keysOnly: input.keysOnly === true,
4348
+ deps: deps,
4349
+ ctx: ctx
4350
+ });
4351
+ });
4352
+ if (!(input.load === true)) return [
4353
+ 3,
4354
+ 2
4355
+ ];
4356
+ return [
4357
+ 4,
4358
+ loadResolvedModels(resolved, ctx, deps)
4359
+ ];
4360
+ case 1:
4361
+ _tmp = _state.sent();
4362
+ return [
4363
+ 3,
4364
+ 3
4365
+ ];
4366
+ case 2:
4367
+ _tmp = undefined;
4368
+ _state.label = 3;
4369
+ case 3:
4370
+ loaded = _tmp;
4371
+ return [
4372
+ 2,
4373
+ _object_spread$1({
4374
+ state: {
4375
+ name: match.state.name,
4376
+ fullUrl: match.state.fullUrl,
4377
+ component: match.state.component,
4378
+ componentFile: match.state.componentFile
4379
+ },
4380
+ via: match.via,
4381
+ params: match.params,
4382
+ models: resolved
4383
+ }, loaded === undefined ? {} : {
4384
+ loaded: loaded
4385
+ })
4386
+ ];
4387
+ }
4388
+ });
4389
+ })();
4390
+ }
4391
+ function resolveModelEntry(input) {
4392
+ var entry = input.entry, keysOnly = input.keysOnly;
4393
+ var base = keysOnly ? {
4394
+ modelType: entry.modelType,
4395
+ kind: entry.kind
4396
+ } : _object_spread$1({
4397
+ modelType: entry.modelType,
4398
+ kind: entry.kind
4399
+ }, entry.keyTemplate === undefined ? {} : {
4400
+ keyTemplate: entry.keyTemplate
4401
+ }, entry.description === undefined ? {} : {
4402
+ description: entry.description
4403
+ }, entry.from === undefined ? {} : {
4404
+ from: entry.from
4405
+ });
4406
+ var result;
4407
+ if (entry.kind === 'list') {
4408
+ result = base;
4409
+ } else if (entry.kind === 'id') {
4410
+ result = _object_spread$1({}, base, resolveIdKey(input));
4411
+ } else {
4412
+ result = _object_spread$1({}, base, resolveFullKey(input));
4413
+ }
4414
+ return result;
4415
+ }
4416
+ function resolveIdKey(input) {
4417
+ var _entry_keyTemplate;
4418
+ var entry = input.entry, params = input.params, uid = input.uid, deps = input.deps, ctx = input.ctx;
4419
+ var value = substitutePlaceholder((_entry_keyTemplate = entry.keyTemplate) !== null && _entry_keyTemplate !== void 0 ? _entry_keyTemplate : '', params, uid);
4420
+ var result;
4421
+ if (value.kind === 'unresolved') {
4422
+ result = {
4423
+ unresolved: value.unresolved
4424
+ };
4425
+ } else {
4426
+ var identity = deps.resolveIdentity(entry.modelType, ctx.auth);
4427
+ if (identity == null) {
4428
+ result = {
4429
+ unresolved: {
4430
+ reason: 'unknown-model-type',
4431
+ message: "Unknown model type `".concat(entry.modelType, "`.")
4432
+ }
4433
+ };
4434
+ } else if (identity.type === 'root') {
4435
+ result = {
4436
+ key: "".concat(identity.collectionName, "/").concat(value.value)
4437
+ };
4438
+ } else {
4439
+ result = {
4440
+ unresolved: {
4441
+ reason: 'subcollection-requires-key-template',
4442
+ message: "Model `".concat(entry.modelType, "` is a subcollection; an `id` key template cannot be promoted to a full key. Annotate it with a full `gb/:id/...` key template.")
4443
+ }
4444
+ };
4445
+ }
4446
+ }
4447
+ return result;
4448
+ }
4449
+ function resolveFullKey(input) {
4450
+ var _entry_keyTemplate;
4451
+ var entry = input.entry, params = input.params, uid = input.uid;
4452
+ var segments = ((_entry_keyTemplate = entry.keyTemplate) !== null && _entry_keyTemplate !== void 0 ? _entry_keyTemplate : '').split('/');
4453
+ var parts = [];
4454
+ var unresolved;
4455
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
4456
+ try {
4457
+ for(var _iterator = segments[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
4458
+ var segment = _step.value;
4459
+ if (segment.startsWith(':') || segment === AUTH_UID_PLACEHOLDER) {
4460
+ var value = substitutePlaceholder(segment, params, uid);
4461
+ if (value.kind === 'unresolved') {
4462
+ unresolved = value.unresolved;
4463
+ break;
4464
+ }
4465
+ parts.push(value.value);
4466
+ } else {
4467
+ parts.push(segment);
4468
+ }
4469
+ }
4470
+ } catch (err) {
4471
+ _didIteratorError = true;
4472
+ _iteratorError = err;
4473
+ } finally{
4474
+ try {
4475
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
4476
+ _iterator.return();
4477
+ }
4478
+ } finally{
4479
+ if (_didIteratorError) {
4480
+ throw _iteratorError;
4481
+ }
4482
+ }
4483
+ }
4484
+ return unresolved === undefined ? {
4485
+ key: parts.join('/')
4486
+ } : {
4487
+ unresolved: unresolved
4488
+ };
4489
+ }
4490
+ function substitutePlaceholder(segment, params, uid) {
4491
+ var result;
4492
+ if (segment === AUTH_UID_PLACEHOLDER) {
4493
+ result = uid == null || uid.length === 0 ? {
4494
+ kind: 'unresolved',
4495
+ unresolved: {
4496
+ reason: 'auth-required',
4497
+ message: 'This page keys a model by the caller uid (`{authUid}`) but the request is not authenticated.'
4498
+ }
4499
+ } : {
4500
+ kind: 'value',
4501
+ value: uid
4502
+ };
4503
+ } else if (segment.startsWith(':')) {
4504
+ var paramName = segment.slice(1);
4505
+ var value = params[paramName];
4506
+ result = value === undefined ? {
4507
+ kind: 'unresolved',
4508
+ unresolved: {
4509
+ reason: 'missing-param',
4510
+ message: "Route param `:".concat(paramName, "` was not captured from the URL.")
4511
+ }
4512
+ } : {
4513
+ kind: 'value',
4514
+ value: value
4515
+ };
4516
+ } else {
4517
+ result = {
4518
+ kind: 'value',
4519
+ value: segment
4520
+ };
4521
+ }
4522
+ return result;
4523
+ }
4524
+ // MARK: Load
4525
+ function loadResolvedModels(resolved, ctx, deps) {
4526
+ return _async_to_generator$4(function() {
4527
+ var keysByType, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, model, existing, out, _iteratorNormalCompletion1, _didIteratorError1, _iteratorError1, _iterator1, _step1, _step_value, modelType, keys, merged, err;
4528
+ return _ts_generator$4(this, function(_state) {
4529
+ switch(_state.label){
4530
+ case 0:
4531
+ keysByType = new Map();
4532
+ _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
4533
+ try {
4534
+ for(_iterator = resolved[Symbol.iterator](); !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
4535
+ model = _step.value;
4536
+ if (model.key !== undefined) {
4537
+ existing = keysByType.get(model.modelType);
4538
+ if (existing === undefined) {
4539
+ keysByType.set(model.modelType, [
4540
+ model.key
4541
+ ]);
4542
+ } else {
4543
+ existing.push(model.key);
4544
+ }
4545
+ }
4546
+ }
4547
+ } catch (err) {
4548
+ _didIteratorError = true;
4549
+ _iteratorError = err;
4550
+ } finally{
4551
+ try {
4552
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
4553
+ _iterator.return();
4554
+ }
4555
+ } finally{
4556
+ if (_didIteratorError) {
4557
+ throw _iteratorError;
4558
+ }
4559
+ }
4560
+ }
4561
+ out = [];
4562
+ _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
4563
+ _state.label = 1;
4564
+ case 1:
4565
+ _state.trys.push([
4566
+ 1,
4567
+ 6,
4568
+ 7,
4569
+ 8
4570
+ ]);
4571
+ _iterator1 = keysByType[Symbol.iterator]();
4572
+ _state.label = 2;
4573
+ case 2:
4574
+ if (!!(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done)) return [
4575
+ 3,
4576
+ 5
4577
+ ];
4578
+ _step_value = _sliced_to_array$2(_step1.value, 2), modelType = _step_value[0], keys = _step_value[1];
4579
+ return [
4580
+ 4,
4581
+ readInChunks({
4582
+ modelType: modelType,
4583
+ keys: keys,
4584
+ auth: ctx.auth,
4585
+ readDocuments: deps.readDocuments
4586
+ })
4587
+ ];
4588
+ case 3:
4589
+ merged = _state.sent();
4590
+ out.push({
4591
+ modelType: modelType,
4592
+ results: merged.results,
4593
+ errors: merged.errors
4594
+ });
4595
+ _state.label = 4;
4596
+ case 4:
4597
+ _iteratorNormalCompletion1 = true;
4598
+ return [
4599
+ 3,
4600
+ 2
4601
+ ];
4602
+ case 5:
4603
+ return [
4604
+ 3,
4605
+ 8
4606
+ ];
4607
+ case 6:
4608
+ err = _state.sent();
4609
+ _didIteratorError1 = true;
4610
+ _iteratorError1 = err;
4611
+ return [
4612
+ 3,
4613
+ 8
4614
+ ];
4615
+ case 7:
4616
+ try {
4617
+ if (!_iteratorNormalCompletion1 && _iterator1.return != null) {
4618
+ _iterator1.return();
4619
+ }
4620
+ } finally{
4621
+ if (_didIteratorError1) {
4622
+ throw _iteratorError1;
4623
+ }
4624
+ }
4625
+ return [
4626
+ 7
4627
+ ];
4628
+ case 8:
4629
+ return [
4630
+ 2,
4631
+ out
4632
+ ];
4633
+ }
4634
+ });
4635
+ })();
4636
+ }
4637
+ function readInChunks(input) {
4638
+ return _async_to_generator$4(function() {
4639
+ var modelType, keys, auth, readDocuments, merged, offset, _merged_results, _merged_errors, batch, partial;
4640
+ return _ts_generator$4(this, function(_state) {
4641
+ switch(_state.label){
4642
+ case 0:
4643
+ modelType = input.modelType, keys = input.keys, auth = input.auth, readDocuments = input.readDocuments;
4644
+ merged = {
4645
+ results: [],
4646
+ errors: []
4647
+ };
4648
+ offset = 0;
4649
+ _state.label = 1;
4650
+ case 1:
4651
+ if (!(offset < keys.length)) return [
4652
+ 3,
4653
+ 4
4654
+ ];
4655
+ batch = keys.slice(offset, offset + MCP_MODEL_GET_BATCH_SIZE);
4656
+ return [
4657
+ 4,
4658
+ readDocuments(modelType, batch, auth)
4659
+ ];
4660
+ case 2:
4661
+ partial = _state.sent();
4662
+ (_merged_results = merged.results).push.apply(_merged_results, _to_consumable_array$3(partial.results));
4663
+ (_merged_errors = merged.errors).push.apply(_merged_errors, _to_consumable_array$3(partial.errors));
4664
+ _state.label = 3;
4665
+ case 3:
4666
+ offset += MCP_MODEL_GET_BATCH_SIZE;
4667
+ return [
4668
+ 3,
4669
+ 1
4670
+ ];
4671
+ case 4:
4672
+ return [
4673
+ 2,
4674
+ merged
4675
+ ];
2880
4676
  }
2881
- }
2882
- }
4677
+ });
4678
+ })();
2883
4679
  }
2884
4680
  // MARK: Schemas
2885
- var WHOAMI_INPUT_SCHEMA = {
2886
- type: 'object',
2887
- properties: {},
2888
- additionalProperties: false
2889
- };
2890
- var WHOAMI_OUTPUT_SCHEMA = {
4681
+ var URL_MODELS_INPUT_SCHEMA = {
2891
4682
  type: 'object',
2892
4683
  required: [
2893
- 'authenticated',
2894
- 'claims',
2895
- 'roles',
2896
- 'claimDetails',
2897
- 'unknownClaimKeys'
4684
+ 'url'
2898
4685
  ],
2899
- description: 'Caller identity, raw token claims, decoded role set, and per-claim descriptions for every key present on the live token.',
2900
4686
  properties: {
2901
- authenticated: {
2902
- type: 'boolean'
4687
+ url: {
4688
+ type: 'string',
4689
+ minLength: 1,
4690
+ description: 'Full URL or pathname, e.g. "https://app.example.co/worker/abc/timesheets/list".'
2903
4691
  },
2904
- uid: {
2905
- type: 'string'
4692
+ models: {
4693
+ type: 'array',
4694
+ items: {
4695
+ type: 'string'
4696
+ },
4697
+ description: 'Restrict the output to these model types.'
2906
4698
  },
2907
- email: {
2908
- type: 'string'
4699
+ keysOnly: {
4700
+ type: 'boolean',
4701
+ description: 'Return only the resolved keys (no descriptions or documents). Cannot combine with "load".'
2909
4702
  },
2910
- emailVerified: {
2911
- type: 'boolean'
4703
+ load: {
4704
+ type: 'boolean',
4705
+ description: 'Load each resolved document via the permission-checked read path. Cannot combine with "keysOnly".'
2912
4706
  },
2913
- app: {
4707
+ currentUserUid: {
4708
+ type: 'string',
4709
+ minLength: 1,
4710
+ description: "Override the uid used to fill `{authUid}` placeholders when resolving model keys (defaults to the authenticated caller). Use to preview the models another user would see on a page. `load` still reads via the calling user's permissions."
4711
+ }
4712
+ },
4713
+ additionalProperties: false
4714
+ };
4715
+ var URL_MODELS_OUTPUT_SCHEMA = {
4716
+ type: 'object',
4717
+ required: [
4718
+ 'url',
4719
+ 'pathname',
4720
+ 'matched'
4721
+ ],
4722
+ properties: {
4723
+ url: {
2914
4724
  type: 'string'
2915
4725
  },
2916
- claims: {
2917
- type: 'object',
2918
- additionalProperties: true
4726
+ pathname: {
4727
+ type: 'string'
2919
4728
  },
2920
- roles: {
2921
- type: 'array',
2922
- items: {
2923
- type: 'string'
4729
+ matched: {
4730
+ type: [
4731
+ 'object',
4732
+ 'null'
4733
+ ],
4734
+ description: 'The matched page and its models, or null when no state matched.',
4735
+ properties: {
4736
+ state: {
4737
+ type: 'object'
4738
+ },
4739
+ via: {
4740
+ type: 'string'
4741
+ },
4742
+ params: {
4743
+ type: 'object'
4744
+ },
4745
+ models: {
4746
+ type: 'array',
4747
+ items: {
4748
+ type: 'object'
4749
+ }
4750
+ },
4751
+ loaded: {
4752
+ type: 'array',
4753
+ items: {
4754
+ type: 'object'
4755
+ }
4756
+ }
2924
4757
  }
2925
4758
  },
2926
- claimDetails: {
4759
+ ambiguous: {
2927
4760
  type: 'array',
2928
4761
  items: {
2929
- type: 'object',
2930
- required: [
2931
- 'key',
2932
- 'description',
2933
- 'grantedRoles',
2934
- 'inverse',
2935
- 'tags'
2936
- ],
2937
- properties: {
2938
- key: {
2939
- type: 'string'
2940
- },
2941
- interfaceName: {
2942
- type: 'string'
2943
- },
2944
- description: {
2945
- type: 'string'
2946
- },
2947
- grantedRoles: {
2948
- type: 'array',
2949
- items: {
2950
- type: 'string'
2951
- }
2952
- },
2953
- inverse: {
2954
- type: 'boolean'
2955
- },
2956
- tags: {
2957
- type: 'array',
2958
- items: {
2959
- type: 'string'
2960
- }
2961
- }
2962
- }
2963
- }
4762
+ type: 'object'
4763
+ },
4764
+ description: 'Competing states when the URL matched more than one.'
2964
4765
  },
2965
- unknownClaimKeys: {
4766
+ candidates: {
2966
4767
  type: 'array',
2967
4768
  items: {
2968
- type: 'string'
2969
- }
4769
+ type: 'object'
4770
+ },
4771
+ description: 'Closest near-miss states when nothing matched.'
2970
4772
  }
2971
4773
  }
2972
4774
  };
@@ -3046,7 +4848,7 @@ function _non_iterable_rest$1() {
3046
4848
  function _sliced_to_array$1(arr, i) {
3047
4849
  return _array_with_holes$1(arr) || _iterable_to_array_limit$1(arr, i) || _unsupported_iterable_to_array$3(arr, i) || _non_iterable_rest$1();
3048
4850
  }
3049
- function _type_of$1(obj) {
4851
+ function _type_of$2(obj) {
3050
4852
  "@swc/helpers - typeof";
3051
4853
  return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
3052
4854
  }
@@ -3220,7 +5022,7 @@ function _ts_generator$3(thisArg, body) {
3220
5022
  };
3221
5023
  var name = BATCH_EXECUTE_TOOL_NAME;
3222
5024
  var description = 'Execute a batch of MCP operations server-side from an uploaded JSON/NDJSON file, returning a success/failure summary. Each line/element is an operation `{ call, modelType, specifier?, data }` matching any other write tool you could call individually. Generate the file with a script and upload it instead of invoking a per-record tool many times. Every operation is authorized against your visible tools before any run; a forbidden or malformed operation aborts the whole batch with nothing executed.';
3223
- return {
5025
+ return buildStaticToolDefinition({
3224
5026
  name: name,
3225
5027
  description: description,
3226
5028
  inputSchema: BATCH_EXECUTE_INPUT_SCHEMA,
@@ -3230,20 +5032,11 @@ function _ts_generator$3(thisArg, body) {
3230
5032
  modelType: BATCH_EXECUTE_DISPATCH_MODEL_TYPE
3231
5033
  },
3232
5034
  staticHandler: handler,
3233
- filterMetadata: {
3234
- visibilityKind: 'declarative',
3235
- rule: {
3236
- requireAuthenticated: true
3237
- },
3238
- effectiveReadOnly: false
3239
- },
3240
- staticWireEntry: buildStaticWireEntry({
3241
- name: name,
3242
- description: description,
3243
- inputSchema: BATCH_EXECUTE_INPUT_SCHEMA,
3244
- outputSchema: BATCH_EXECUTE_OUTPUT_SCHEMA
3245
- })
3246
- };
5035
+ effectiveReadOnly: false,
5036
+ rule: {
5037
+ requireAuthenticated: true
5038
+ }
5039
+ });
3247
5040
  }
3248
5041
  // MARK: Handler
3249
5042
  function batchExecuteToolHandler(args, ctx, deps) {
@@ -3440,7 +5233,7 @@ function parseJsonArrayOperations(fileText) {
3440
5233
  return parsed;
3441
5234
  }
3442
5235
  function validateOperation(value, index) {
3443
- if (value == null || (typeof value === "undefined" ? "undefined" : _type_of$1(value)) !== 'object') {
5236
+ if (value == null || (typeof value === "undefined" ? "undefined" : _type_of$2(value)) !== 'object') {
3444
5237
  throw new Error("batch-execute: operation[".concat(index, "] must be an object."));
3445
5238
  }
3446
5239
  var candidate = value;
@@ -3932,12 +5725,50 @@ function _non_iterable_rest() {
3932
5725
  function _non_iterable_spread$2() {
3933
5726
  throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
3934
5727
  }
5728
+ function _object_spread(target) {
5729
+ for(var i = 1; i < arguments.length; i++){
5730
+ var source = arguments[i] != null ? arguments[i] : {};
5731
+ var ownKeys = Object.keys(source);
5732
+ if (typeof Object.getOwnPropertySymbols === "function") {
5733
+ ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
5734
+ return Object.getOwnPropertyDescriptor(source, sym).enumerable;
5735
+ }));
5736
+ }
5737
+ ownKeys.forEach(function(key) {
5738
+ _define_property$3(target, key, source[key]);
5739
+ });
5740
+ }
5741
+ return target;
5742
+ }
5743
+ function ownKeys(object, enumerableOnly) {
5744
+ var keys = Object.keys(object);
5745
+ if (Object.getOwnPropertySymbols) {
5746
+ var symbols = Object.getOwnPropertySymbols(object);
5747
+ keys.push.apply(keys, symbols);
5748
+ }
5749
+ return keys;
5750
+ }
5751
+ function _object_spread_props(target, source) {
5752
+ source = source != null ? source : {};
5753
+ if (Object.getOwnPropertyDescriptors) {
5754
+ Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
5755
+ } else {
5756
+ ownKeys(Object(source)).forEach(function(key) {
5757
+ Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
5758
+ });
5759
+ }
5760
+ return target;
5761
+ }
3935
5762
  function _sliced_to_array(arr, i) {
3936
5763
  return _array_with_holes(arr) || _iterable_to_array_limit(arr, i) || _unsupported_iterable_to_array$2(arr, i) || _non_iterable_rest();
3937
5764
  }
3938
5765
  function _to_consumable_array$2(arr) {
3939
5766
  return _array_without_holes$2(arr) || _iterable_to_array$2(arr) || _unsupported_iterable_to_array$2(arr) || _non_iterable_spread$2();
3940
5767
  }
5768
+ function _type_of$1(obj) {
5769
+ "@swc/helpers - typeof";
5770
+ return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
5771
+ }
3941
5772
  function _unsupported_iterable_to_array$2(o, minLen) {
3942
5773
  if (!o) return;
3943
5774
  if (typeof o === "string") return _array_like_to_array$2(o, minLen);
@@ -4066,10 +5897,14 @@ var McpServerFactoryService_1;
4066
5897
  _define_property$3(this, "_cachedStaticTools", void 0);
4067
5898
  _define_property$3(this, "_cachedManifest", void 0);
4068
5899
  _define_property$3(this, "_cachedManifestModels", void 0);
5900
+ _define_property$3(this, "_cachedManifestEnums", void 0);
4069
5901
  _define_property$3(this, "_cachedManifestAuth", void 0);
5902
+ _define_property$3(this, "_cachedRouteManifest", void 0);
5903
+ _define_property$3(this, "_routeManifestLoaded", false);
4070
5904
  _define_property$3(this, "_manifestLoaded", false);
4071
5905
  _define_property$3(this, "_loggedSkips", false);
4072
5906
  _define_property$3(this, "_warnedMissingRoleReader", false);
5907
+ _define_property$3(this, "_resolvedReasonConfig", void 0);
4073
5908
  _define_property$3(this, "_analyticsService", void 0);
4074
5909
  this.mcpConfig = mcpConfig;
4075
5910
  this.dispatchService = dispatchService;
@@ -4341,15 +6176,39 @@ var McpServerFactoryService_1;
4341
6176
  return getService.getModelIdentity(modelType, auth);
4342
6177
  }
4343
6178
  }));
6179
+ // `url-models` reuses the model-get read/identity path, so it is only offered when the get
6180
+ // service is wired AND a route manifest with at least one state was loaded.
6181
+ var routeManifest = this._resolveRouteManifest();
6182
+ if (routeManifest != null && routeManifest.states.length > 0) {
6183
+ staticTools.push(createUrlModelsTool({
6184
+ routeManifest: routeManifest,
6185
+ readDocuments: function readDocuments(modelType, keys, auth) {
6186
+ return getService.readDocuments(modelType, keys, auth);
6187
+ },
6188
+ resolveIdentity: function resolveIdentity(modelType, auth) {
6189
+ return getService.getModelIdentity(modelType, auth);
6190
+ }
6191
+ }));
6192
+ }
4344
6193
  }
4345
6194
  var modelManifest = this._cachedManifestModels;
6195
+ var enumManifest = this._cachedManifestEnums;
4346
6196
  if (modelManifest != null && modelManifest.length > 0) {
4347
- staticTools.push(createModelInfoTool({
6197
+ staticTools.push(createModelInfoTool(_object_spread({
4348
6198
  manifest: modelManifest
4349
- }), createModelDecodeTool({
6199
+ }, enumManifest == null ? {} : {
6200
+ enums: enumManifest
6201
+ })), createModelDecodeTool({
4350
6202
  manifest: modelManifest
4351
6203
  }));
4352
6204
  }
6205
+ // `enum-info` only needs the enum value tables — register it whenever the manifest carries a
6206
+ // non-empty `enums` block, independent of the model catalog.
6207
+ if (enumManifest != null && Object.keys(enumManifest).length > 0) {
6208
+ staticTools.push(createEnumInfoTool({
6209
+ enums: enumManifest
6210
+ }));
6211
+ }
4353
6212
  var authManifest = this._cachedManifestAuth;
4354
6213
  if (authManifest != null) {
4355
6214
  staticTools.push(createWhoamiTool({
@@ -4511,9 +6370,55 @@ var McpServerFactoryService_1;
4511
6370
  value: function _resetManifestCache() {
4512
6371
  this._cachedManifest = undefined;
4513
6372
  this._cachedManifestModels = undefined;
6373
+ this._cachedManifestEnums = undefined;
4514
6374
  this._cachedManifestAuth = undefined;
4515
6375
  }
4516
6376
  },
6377
+ {
6378
+ /**
6379
+ * Reads the pre-rendered route manifest JSON once, validates its version, and caches the result
6380
+ * for the process lifetime. Drives whether the built-in `url-models` static tool is registered.
6381
+ *
6382
+ * Missing file or wrong version fall back to "no route manifest" with a single boot warning;
6383
+ * the `url-models` tool is then simply not offered.
6384
+ *
6385
+ * @returns The cached route manifest, or `undefined` when none was loaded.
6386
+ */ key: "_resolveRouteManifest",
6387
+ value: function _resolveRouteManifest() {
6388
+ if (!this._routeManifestLoaded) {
6389
+ this._routeManifestLoaded = true;
6390
+ var path = this.mcpConfig.mcpRouteManifestPath;
6391
+ if (path == null) {
6392
+ this._cachedRouteManifest = undefined;
6393
+ } else if (existsSync(path)) {
6394
+ this._parseRouteManifestFile(path);
6395
+ } else {
6396
+ this._logger.warn("MCP route manifest path is set but the file is missing: ".concat(path, ". The url-models tool will not be registered."));
6397
+ this._cachedRouteManifest = undefined;
6398
+ }
6399
+ }
6400
+ return this._cachedRouteManifest;
6401
+ }
6402
+ },
6403
+ {
6404
+ key: "_parseRouteManifestFile",
6405
+ value: function _parseRouteManifestFile(path) {
6406
+ try {
6407
+ var raw = readFileSync(path, 'utf8');
6408
+ var parsed = JSON.parse(raw);
6409
+ if (parsed.version === ROUTE_MANIFEST_VERSION) {
6410
+ this._cachedRouteManifest = parsed;
6411
+ this._logger.log("Loaded MCP route manifest from ".concat(path, ": ").concat(parsed.states.length, " states."));
6412
+ } else {
6413
+ this._logger.warn("MCP route manifest version mismatch at ".concat(path, ": got ").concat(String(parsed.version), ", expected ").concat(ROUTE_MANIFEST_VERSION, ". The url-models tool will not be registered."));
6414
+ this._cachedRouteManifest = undefined;
6415
+ }
6416
+ } catch (error) {
6417
+ this._logger.warn("Failed to read MCP route manifest at ".concat(path, ": ").concat(error.message, ". The url-models tool will not be registered."));
6418
+ this._cachedRouteManifest = undefined;
6419
+ }
6420
+ }
6421
+ },
4517
6422
  {
4518
6423
  key: "_parseManifestFile",
4519
6424
  value: function _parseManifestFile(path) {
@@ -4560,11 +6465,14 @@ var McpServerFactoryService_1;
4560
6465
  }
4561
6466
  var models = Array.isArray(parsed.models) && parsed.models.length > 0 ? parsed.models : undefined;
4562
6467
  var modelSuffix = models == null ? '' : ", ".concat(models.length, " model entries");
6468
+ var enums = parsed.enums != null && _type_of$1(parsed.enums) === 'object' && Object.keys(parsed.enums).length > 0 ? parsed.enums : undefined;
6469
+ var enumSuffix = enums == null ? '' : ", ".concat(Object.keys(enums).length, " enum entries");
4563
6470
  var auth = parsed.auth != null && Array.isArray(parsed.auth.claims) ? parsed.auth : undefined;
4564
6471
  var authSuffix = auth == null ? '' : ", ".concat(auth.claims.length, " auth claim entries");
4565
- this._logger.log("Loaded MCP manifest from ".concat(path, ": ").concat(map.size, " tool entries").concat(modelSuffix).concat(authSuffix, "."));
6472
+ this._logger.log("Loaded MCP manifest from ".concat(path, ": ").concat(map.size, " tool entries").concat(modelSuffix).concat(enumSuffix).concat(authSuffix, "."));
4566
6473
  this._cachedManifest = map;
4567
6474
  this._cachedManifestModels = models;
6475
+ this._cachedManifestEnums = enums;
4568
6476
  this._cachedManifestAuth = auth;
4569
6477
  }
4570
6478
  },
@@ -4699,11 +6607,32 @@ var McpServerFactoryService_1;
4699
6607
  },
4700
6608
  {
4701
6609
  /**
6610
+ * Resolves the app's reason-parameter config once and caches it for the process lifetime.
6611
+ *
6612
+ * The underlying {@link McpModuleConfig.reasonParameter} value is fixed at boot, so the normalized
6613
+ * form is safe to memoize and reuse across every `tools/list` and `tools/call`.
6614
+ *
6615
+ * @returns The resolved reason-parameter config (defaults applied).
6616
+ */ key: "_resolveReasonConfig",
6617
+ value: function _resolveReasonConfig() {
6618
+ var result = this._resolvedReasonConfig;
6619
+ if (result == null) {
6620
+ result = resolveMcpReasonParameterConfig(this.mcpConfig.reasonParameter);
6621
+ this._resolvedReasonConfig = result;
6622
+ }
6623
+ return result;
6624
+ }
6625
+ },
6626
+ {
6627
+ /**
4702
6628
  * Resolves the wire-shape `tools/list` entry for a single tool.
4703
6629
  *
4704
6630
  * Hot-path short-circuit: tools without a `toolDetailsBuilder` reuse the precomputed,
4705
6631
  * frozen {@link McpToolDefinition.staticWireEntry} verbatim — zero allocations per
4706
- * request for the common case.
6632
+ * request for the common case. When the auto-injected reason parameter is enabled (the
6633
+ * default), the entry is no longer returned frozen-verbatim: its `inputSchema` is wrapped
6634
+ * with the reason property per request (`tools/list` is low-frequency, so the allocation is
6635
+ * acceptable). Tools that already declare the parameter name are still reused verbatim.
4707
6636
  *
4708
6637
  * Tools that opted in to {@link McpToolDetailsBuilder} get a fresh wire entry built
4709
6638
  * from the builder's overrides. If the builder throws, the framework falls back to
@@ -4715,9 +6644,20 @@ var McpServerFactoryService_1;
4715
6644
  * @returns The wire-shape entry to emit for this tool on `tools/list`.
4716
6645
  */ key: "_buildToolListEntry",
4717
6646
  value: function _buildToolListEntry(tool, ctx, scopes) {
6647
+ var reasonConfig = this._resolveReasonConfig();
4718
6648
  var result;
4719
6649
  if (tool.toolDetailsBuilder == null) {
4720
6650
  result = tool.staticWireEntry;
6651
+ if (reasonConfig.enabled) {
6652
+ var reasoned = applyMcpReasonParameterToSchema(tool.staticWireEntry.inputSchema, reasonConfig);
6653
+ // applyMcpReasonParameterToSchema self-skips (returns the same reference) on collision, so the
6654
+ // frozen-verbatim hot path still holds for tools that already declare the parameter name.
6655
+ if (reasoned !== tool.staticWireEntry.inputSchema) {
6656
+ result = _object_spread_props(_object_spread({}, tool.staticWireEntry), {
6657
+ inputSchema: reasoned
6658
+ });
6659
+ }
6660
+ }
4721
6661
  } else {
4722
6662
  var description = tool.description;
4723
6663
  var inputSchema = tool.inputSchema;
@@ -4738,16 +6678,23 @@ var McpServerFactoryService_1;
4738
6678
  } catch (error) {
4739
6679
  this._logger.warn("MCP tool ".concat(tool.name, " toolDetails builder threw; falling back to defaults: ").concat(error.message));
4740
6680
  }
6681
+ var resolvedInputSchema = inputSchema !== null && inputSchema !== void 0 ? inputSchema : {
6682
+ type: 'object'
6683
+ };
6684
+ if (reasonConfig.enabled) {
6685
+ resolvedInputSchema = applyMcpReasonParameterToSchema(resolvedInputSchema, reasonConfig);
6686
+ }
4741
6687
  var wire = {
4742
6688
  name: tool.name,
4743
6689
  description: description,
4744
- inputSchema: inputSchema !== null && inputSchema !== void 0 ? inputSchema : {
4745
- type: 'object'
4746
- }
6690
+ inputSchema: resolvedInputSchema
4747
6691
  };
4748
6692
  if (tool.outputSchema != null) {
4749
6693
  wire.outputSchema = tool.outputSchema;
4750
6694
  }
6695
+ if (tool.annotations != null) {
6696
+ wire.annotations = tool.annotations;
6697
+ }
4751
6698
  result = wire;
4752
6699
  }
4753
6700
  return result;
@@ -4770,14 +6717,20 @@ var McpServerFactoryService_1;
4770
6717
  * @returns The MCP tool response.
4771
6718
  */ function _handleToolCall(request, definitionsByName, ctx) {
4772
6719
  return _async_to_generator$2(function() {
4773
- var _request_params_arguments, _ctx_auth, toolName, definition, args, startedAt, outcome, isSuccessful, toolKind;
6720
+ var _request_params_arguments, _ctx_auth, toolName, definition, rawArgs, startedAt, reasonConfig, declaresOwn, _extractMcpReasonFromArgs, reason, args, outcome, isSuccessful, toolKind;
4774
6721
  return _ts_generator$2(this, function(_state) {
4775
6722
  switch(_state.label){
4776
6723
  case 0:
4777
6724
  toolName = request.params.name;
4778
6725
  definition = definitionsByName.get(toolName);
4779
- args = (_request_params_arguments = request.params.arguments) !== null && _request_params_arguments !== void 0 ? _request_params_arguments : {};
6726
+ rawArgs = (_request_params_arguments = request.params.arguments) !== null && _request_params_arguments !== void 0 ? _request_params_arguments : {};
4780
6727
  startedAt = Date.now();
6728
+ // Split the auto-injected reason out of the args: forward it to analytics, but never let it reach
6729
+ // the underlying handler. Tools that declare their own field of the same name keep it (driven by
6730
+ // the tool's own resolved inputSchema, not the reason-augmented wire schema).
6731
+ reasonConfig = this._resolveReasonConfig();
6732
+ declaresOwn = mcpSchemaDeclaresProperty(definition === null || definition === void 0 ? void 0 : definition.inputSchema, reasonConfig.parameterName);
6733
+ _extractMcpReasonFromArgs = extractMcpReasonFromArgs(rawArgs, reasonConfig, declaresOwn), reason = _extractMcpReasonFromArgs.reason, args = _extractMcpReasonFromArgs.args;
4781
6734
  if (!(definition == null)) return [
4782
6735
  3,
4783
6736
  1
@@ -4827,6 +6780,7 @@ var McpServerFactoryService_1;
4827
6780
  auth: ctx.auth,
4828
6781
  readOnly: this.mcpConfig.readOnly === true,
4829
6782
  args: args,
6783
+ reason: reason,
4830
6784
  isSuccessful: isSuccessful,
4831
6785
  error: outcome.error,
4832
6786
  durationMs: Date.now() - startedAt
@@ -5668,18 +7622,20 @@ var FirebaseServerMcpAnalyticsService_1;
5668
7622
  if (this._logEvents) {
5669
7623
  var uidSuffix = event.uid ? " uid=".concat(event.uid) : '';
5670
7624
  var durationSuffix = event.durationMs == null ? '' : " (".concat(event.durationMs, "ms)");
7625
+ var reasonSuffix = event.reason ? ' reason="'.concat(event.reason, '"') : '';
5671
7626
  var outcome = event.isSuccessful ? 'Succeeded' : 'Failed';
5672
- this._logger.log("MCP ".concat(event.toolName, " [").concat(event.toolKind, "] ").concat(outcome).concat(uidSuffix).concat(durationSuffix));
7627
+ this._logger.log("MCP ".concat(event.toolName, " [").concat(event.toolKind, "] ").concat(outcome).concat(uidSuffix).concat(durationSuffix).concat(reasonSuffix));
5673
7628
  }
5674
7629
  if (this.analyticsService != null) {
5675
- var _event_call, _event_modelType, _event_specifier;
7630
+ var _event_call, _event_modelType, _event_specifier, _event_reason;
5676
7631
  this.analyticsService.sendEventData(event.uid, this._eventName, {
5677
7632
  tool: event.toolName,
5678
7633
  toolKind: event.toolKind,
5679
7634
  call: (_event_call = event.call) !== null && _event_call !== void 0 ? _event_call : '',
5680
7635
  modelType: (_event_modelType = event.modelType) !== null && _event_modelType !== void 0 ? _event_modelType : '',
5681
7636
  specifier: (_event_specifier = event.specifier) !== null && _event_specifier !== void 0 ? _event_specifier : '',
5682
- success: event.isSuccessful
7637
+ success: event.isSuccessful,
7638
+ reason: (_event_reason = event.reason) !== null && _event_reason !== void 0 ? _event_reason : ''
5683
7639
  });
5684
7640
  }
5685
7641
  }
@@ -5766,4 +7722,4 @@ function _unsupported_iterable_to_array(o, minLen) {
5766
7722
  };
5767
7723
  }
5768
7724
 
5769
- export { BATCH_EXECUTE_DISPATCH_CALL, BATCH_EXECUTE_DISPATCH_MODEL_TYPE, BATCH_EXECUTE_TOOL_NAME, DEFAULT_BATCH_EXECUTE_MAX_PARALLEL, DEFAULT_JSON_SCHEMA_GENERATION_OPTIONS, DEFAULT_MCP_ANALYTICS_EVENT_NAME, DEFAULT_MCP_PATH, DEFAULT_MCP_SERVER_INSTRUCTIONS, DEFAULT_MCP_SERVER_NAME, DEFAULT_SPECIFIER_KEY, DEFAULT_VOID_MCP_SUCCESS_VALUE, FIREBASE_SERVER_MCP_ANALYTICS_CONFIG, FIREBASE_SERVER_MCP_ROUTES_FOR_GLOBAL_ROUTE_EXCLUDE, FirebaseServerMcpAnalyticsService, MAX_BATCH_EXECUTE_FILE_BYTES, MAX_BATCH_EXECUTE_OPERATIONS, MCP_ANALYTICS_SERVICE, MCP_AUTH_ROLE_READER, MCP_CALL_TYPE_ABBREVIATIONS, MCP_MANIFEST_VERSION, MCP_MODEL_GET_BATCH_SIZE, MCP_TOOL_NAME_MAX_LENGTH, MCP_TOOL_NAME_WARN_LENGTH, MODEL_DECODE_DISPATCH_CALL, MODEL_DECODE_DISPATCH_MODEL_TYPE, MODEL_DECODE_TOOL_NAME, MODEL_GET_DISPATCH_CALL, MODEL_GET_DISPATCH_MODEL_TYPE, MODEL_GET_TOOL_NAME, MODEL_INFO_DISPATCH_CALL, MODEL_INFO_DISPATCH_MODEL_TYPE, MODEL_INFO_GROUPS_HINT, MODEL_INFO_TOOL_NAME, MODEL_INFO_UNGROUPED_LABEL, McpAnalyticsService, McpController, McpModuleConfig, McpServerFactoryService, McpWellKnownController, abbreviateMcpCallType, appMcpAnalyticsModuleMetadata, batchOperationCoordKey, buildDefaultMcpToolDescription, buildDisambiguatedMcpToolName, buildMcpToolName, buildStaticWireEntry, classifyVisibility, createBatchExecuteTool, createModelDecodeTool, createModelGetTool, createModelInfoTool, decodeFirestoreModelKey, findModelEntry, formatMcpToolErrorResponse, formatMcpToolResponse, generateMcpToolDefinitions, handleStreamableHttpMcpRequest, mcpManifestKey, mcpModuleMetadata, noopMcpAnalyticsService, resolveEffectiveReadOnly, resolveRequiredScope, validateMcpToolName };
7725
+ export { BATCH_EXECUTE_DISPATCH_CALL, BATCH_EXECUTE_DISPATCH_MODEL_TYPE, BATCH_EXECUTE_TOOL_NAME, DEFAULT_BATCH_EXECUTE_MAX_PARALLEL, DEFAULT_JSON_SCHEMA_GENERATION_OPTIONS, DEFAULT_MCP_ANALYTICS_EVENT_NAME, DEFAULT_MCP_PATH, DEFAULT_MCP_REASON_PARAMETER_DESCRIPTION, DEFAULT_MCP_REASON_PARAMETER_MAX_LENGTH, DEFAULT_MCP_REASON_PARAMETER_NAME, DEFAULT_MCP_REASON_PARAMETER_REQUIRED, DEFAULT_MCP_SERVER_INSTRUCTIONS, DEFAULT_MCP_SERVER_NAME, DEFAULT_SPECIFIER_KEY, DEFAULT_VOID_MCP_SUCCESS_VALUE, ENUM_INFO_DISPATCH_CALL, ENUM_INFO_DISPATCH_MODEL_TYPE, ENUM_INFO_TOOL_NAME, ENUM_TABLE_SCHEMA, FIREBASE_SERVER_MCP_ANALYTICS_CONFIG, FIREBASE_SERVER_MCP_ROUTES_FOR_GLOBAL_ROUTE_EXCLUDE, FirebaseServerMcpAnalyticsService, MAX_BATCH_EXECUTE_FILE_BYTES, MAX_BATCH_EXECUTE_OPERATIONS, MCP_ANALYTICS_SERVICE, MCP_AUTH_ROLE_READER, MCP_CALL_TYPE_ABBREVIATIONS, MCP_MANIFEST_VERSION, MCP_MODEL_GET_BATCH_SIZE, MCP_TOOL_NAME_MAX_LENGTH, MCP_TOOL_NAME_WARN_LENGTH, MCP_WRITE_TOOL_DESCRIPTION_PREFIX, MODEL_DECODE_DISPATCH_CALL, MODEL_DECODE_DISPATCH_MODEL_TYPE, MODEL_DECODE_TOOL_NAME, MODEL_GET_DISPATCH_CALL, MODEL_GET_DISPATCH_MODEL_TYPE, MODEL_GET_TOOL_NAME, MODEL_INFO_DISPATCH_CALL, MODEL_INFO_DISPATCH_MODEL_TYPE, MODEL_INFO_GROUPS_HINT, MODEL_INFO_TOOL_NAME, MODEL_INFO_UNGROUPED_LABEL, McpAnalyticsService, McpController, McpModuleConfig, McpServerFactoryService, McpWellKnownController, ROUTE_MANIFEST_VERSION, URL_MODELS_DISPATCH_CALL, URL_MODELS_DISPATCH_MODEL_TYPE, URL_MODELS_TOOL_NAME, abbreviateMcpCallType, appMcpAnalyticsModuleMetadata, applyMcpReasonParameterToSchema, applyWriteMarker, batchOperationCoordKey, buildDefaultMcpToolDescription, buildDisambiguatedMcpToolName, buildMcpToolName, buildStaticToolDefinition, buildStaticWireEntry, classifyVisibility, createBatchExecuteTool, createEnumInfoTool, createModelDecodeTool, createModelGetTool, createModelInfoTool, createUrlModelsTool, decodeFirestoreModelKey, extractMcpReasonFromArgs, findModelEntry, formatMcpToolErrorResponse, formatMcpToolResponse, generateMcpToolDefinitions, handleStreamableHttpMcpRequest, matchRouteManifestUrl, mcpManifestKey, mcpModuleMetadata, mcpSchemaDeclaresProperty, noopMcpAnalyticsService, parseUrlModelsPathname, resolveEffectiveReadOnly, resolveEnumInfoOutput, resolveMcpReasonParameterConfig, resolveMcpToolAnnotations, resolveRequiredScope, validateMcpToolName };