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