@dereekb/firebase-server 13.16.0 → 13.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/mcp/index.esm.js CHANGED
@@ -15,7 +15,7 @@ function _class_call_check$5(instance, Constructor) {
15
15
  throw new TypeError("Cannot call a class as a function");
16
16
  }
17
17
  }
18
- function _define_property$7(obj, key, value) {
18
+ function _define_property$8(obj, key, value) {
19
19
  if (key in obj) {
20
20
  Object.defineProperty(obj, key, {
21
21
  value: value,
@@ -69,7 +69,7 @@ function _define_property$7(obj, key, value) {
69
69
  * discovery document. Claude custom-connector reads this to discover the OIDC issuer.
70
70
  *
71
71
  * @example 'https://api.example.com/oidc'
72
- */ _define_property$7(this, "oidcIssuer", void 0);
72
+ */ _define_property$8(this, "oidcIssuer", void 0);
73
73
  /**
74
74
  * The canonical resource URL of the MCP endpoint.
75
75
  *
@@ -77,19 +77,19 @@ function _define_property$7(obj, key, value) {
77
77
  * and used as the audience the access token's `aud` claim should match.
78
78
  *
79
79
  * @example 'https://api.example.com/mcp'
80
- */ _define_property$7(this, "mcpUrl", void 0);
80
+ */ _define_property$8(this, "mcpUrl", void 0);
81
81
  /**
82
82
  * Optional name advertised on the MCP `initialize` handshake. Defaults to
83
83
  * {@link DEFAULT_MCP_SERVER_NAME}.
84
- */ _define_property$7(this, "serverName", void 0);
84
+ */ _define_property$8(this, "serverName", void 0);
85
85
  /**
86
86
  * Optional version advertised on the MCP `initialize` handshake.
87
- */ _define_property$7(this, "serverVersion", void 0);
87
+ */ _define_property$8(this, "serverVersion", void 0);
88
88
  /**
89
89
  * Optional override for the `instructions` string passed to `new McpServer(...)` and
90
90
  * advertised on the JSON-RPC `initialize` handshake. Defaults to
91
91
  * {@link DEFAULT_MCP_SERVER_INSTRUCTIONS}.
92
- */ _define_property$7(this, "serverInstructions", void 0);
92
+ */ _define_property$8(this, "serverInstructions", void 0);
93
93
  /**
94
94
  * Absolute path to a pre-rendered MCP manifest JSON file produced by
95
95
  * `dbx-cli-generate-mcp-manifest`. When set, the runtime reads it once at
@@ -99,7 +99,17 @@ function _define_property$7(obj, key, value) {
99
99
  * Optional. When unset or the file is missing, the runtime falls back to
100
100
  * today's behavior (auto-generated descriptions, ArkType-derived input
101
101
  * schemas, no output schemas) and emits a single boot warning.
102
- */ _define_property$7(this, "mcpManifestPath", void 0);
102
+ */ _define_property$8(this, "mcpManifestPath", void 0);
103
+ /**
104
+ * Absolute path to a pre-rendered route manifest JSON file produced by
105
+ * `dbx-cli-generate-route-manifest`. When set, the runtime reads it once at
106
+ * boot and registers the built-in `url-models` tool, which decodes an app URL
107
+ * into the Firestore models its page renders.
108
+ *
109
+ * Optional. When unset or the file is missing, the runtime skips registering
110
+ * `url-models` and emits a single boot warning when the path was set but
111
+ * unreadable.
112
+ */ _define_property$8(this, "mcpRouteManifestPath", void 0);
103
113
  /**
104
114
  * When `true`, the MCP server only advertises tools whose effective read-only classification is `true`.
105
115
  *
@@ -109,19 +119,19 @@ function _define_property$7(obj, key, value) {
109
119
  *
110
120
  * The advertised `serverName` on the JSON-RPC `initialize` handshake is suffixed with
111
121
  * ` (read-only)` so the client surface reflects the mode.
112
- */ _define_property$7(this, "readOnly", void 0);
122
+ */ _define_property$8(this, "readOnly", void 0);
113
123
  };
114
124
  /**
115
125
  * NestJS injection token for the optional {@link McpAuthRoleReader} provider.
116
126
  */ var MCP_AUTH_ROLE_READER = 'MCP_AUTH_ROLE_READER';
117
127
 
118
- function _type_of$3(obj) {
128
+ function _type_of$4(obj) {
119
129
  "@swc/helpers - typeof";
120
130
  return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
121
131
  }
122
132
  function __decorate(decorators, target, key, desc) {
123
133
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
124
- if ((typeof Reflect === "undefined" ? "undefined" : _type_of$3(Reflect)) === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
134
+ if ((typeof Reflect === "undefined" ? "undefined" : _type_of$4(Reflect)) === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
125
135
  else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
126
136
  return c > 3 && r && Object.defineProperty(target, key, r), r;
127
137
  }
@@ -190,7 +200,290 @@ function _class_call_check$4(instance, Constructor) {
190
200
  return isDefault ? "".concat(modelType, ".").concat(call, "._") : "".concat(modelType, ".").concat(call, ".").concat(specifier);
191
201
  }
192
202
 
193
- function asyncGeneratorStep$5(gen, resolve, reject, _next, _throw, key, arg) {
203
+ function _array_like_to_array$9(arr, len) {
204
+ if (len == null || len > arr.length) len = arr.length;
205
+ for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
206
+ return arr2;
207
+ }
208
+ function _array_with_holes$5(arr) {
209
+ if (Array.isArray(arr)) return arr;
210
+ }
211
+ function _iterable_to_array_limit$5(arr, i) {
212
+ var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
213
+ if (_i == null) return;
214
+ var _arr = [];
215
+ var _n = true;
216
+ var _d = false;
217
+ var _s, _e;
218
+ try {
219
+ for(_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true){
220
+ _arr.push(_s.value);
221
+ if (i && _arr.length === i) break;
222
+ }
223
+ } catch (err) {
224
+ _d = true;
225
+ _e = err;
226
+ } finally{
227
+ try {
228
+ if (!_n && _i["return"] != null) _i["return"]();
229
+ } finally{
230
+ if (_d) throw _e;
231
+ }
232
+ }
233
+ return _arr;
234
+ }
235
+ function _non_iterable_rest$5() {
236
+ throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
237
+ }
238
+ function _sliced_to_array$5(arr, i) {
239
+ return _array_with_holes$5(arr) || _iterable_to_array_limit$5(arr, i) || _unsupported_iterable_to_array$9(arr, i) || _non_iterable_rest$5();
240
+ }
241
+ function _unsupported_iterable_to_array$9(o, minLen) {
242
+ if (!o) return;
243
+ if (typeof o === "string") return _array_like_to_array$9(o, minLen);
244
+ var n = Object.prototype.toString.call(o).slice(8, -1);
245
+ if (n === "Object" && o.constructor) n = o.constructor.name;
246
+ if (n === "Map" || n === "Set") return Array.from(n);
247
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$9(o, minLen);
248
+ }
249
+ /**
250
+ * Runtime mirror of the build-time route manifest schema + a ported URL matcher.
251
+ *
252
+ * Structural mirror of `@dereekb/dbx-cli`'s `route-manifest.ts` types and the
253
+ * pure `url-match.ts` matcher, so the firebase-server/mcp runtime can consume
254
+ * the generated `route.manifest.json` without taking a runtime dependency on
255
+ * the build-time CLI package. Both sides bump {@link ROUTE_MANIFEST_VERSION}
256
+ * together when the manifest shape changes.
257
+ */ /**
258
+ * Version stamp embedded in `route.manifest.json`. Runtime loaders refuse
259
+ * manifests whose `version` does not match. Mirror in `@dereekb/dbx-cli`'s
260
+ * `ROUTE_MANIFEST_VERSION` — bump both together.
261
+ */ var ROUTE_MANIFEST_VERSION = 1;
262
+ /**
263
+ * Extracts the normalized pathname from a full URL or a bare path. Strips the
264
+ * scheme/host/port, query string, and hash; collapses an empty path to `/` and
265
+ * trims a single trailing slash.
266
+ *
267
+ * @param url - A full URL (`https://app.example.co/worker/x/timesheets/list`) or bare path.
268
+ * @returns The normalized pathname.
269
+ *
270
+ * @example
271
+ * ```ts
272
+ * parseUrlModelsPathname('https://app.hellosubs.co/worker/abc/timesheets/list/'); // => '/worker/abc/timesheets/list'
273
+ * ```
274
+ */ function parseUrlModelsPathname(url) {
275
+ var trimmed = url.trim();
276
+ var pathname;
277
+ if (RegExp("^[a-z][a-z0-9+.-]*:\\/\\/", "iu").test(trimmed)) {
278
+ pathname = new URL(trimmed).pathname;
279
+ } else {
280
+ var hashStripped = trimmed.split('#', 1)[0];
281
+ var pathPart = hashStripped.split('?', 1)[0];
282
+ pathname = pathPart.startsWith('/') ? pathPart : "/".concat(pathPart);
283
+ }
284
+ return normalizePathname(pathname);
285
+ }
286
+ /**
287
+ * Matches a URL against the manifest's states, preferring a literal match over
288
+ * a parameterised one. A tie at either tier collapses to `ambiguous`; otherwise
289
+ * the closest near-misses are scored and returned in a `none` result.
290
+ *
291
+ * @param input - The manifest and the URL (or pathname) to resolve.
292
+ * @returns A discriminated match / ambiguous / none result.
293
+ */ function matchRouteManifestUrl(input) {
294
+ var pathname = parseUrlModelsPathname(input.url);
295
+ var states = input.manifest.states;
296
+ var literal = states.filter(function(s) {
297
+ return s.fullUrl !== undefined && normalizePathname(s.fullUrl) === pathname;
298
+ });
299
+ var result;
300
+ if (literal.length === 1) {
301
+ result = {
302
+ kind: 'match',
303
+ via: 'literal',
304
+ state: literal[0],
305
+ params: {},
306
+ pathname: pathname
307
+ };
308
+ } else if (literal.length > 1) {
309
+ result = {
310
+ kind: 'ambiguous',
311
+ states: literal,
312
+ pathname: pathname
313
+ };
314
+ } else {
315
+ result = matchParamOrNone(states, pathname);
316
+ }
317
+ return result;
318
+ }
319
+ function matchParamOrNone(states, pathname) {
320
+ var inputSegments = splitSegments(pathname);
321
+ var hits = [];
322
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
323
+ try {
324
+ for(var _iterator = states[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
325
+ var state = _step.value;
326
+ if (state.fullUrl === undefined || !hasParamSegment(state.fullUrl)) {
327
+ continue;
328
+ }
329
+ var params = tryMatchSegments(splitSegments(state.fullUrl), inputSegments);
330
+ if (params) {
331
+ hits.push({
332
+ state: state,
333
+ params: params
334
+ });
335
+ }
336
+ }
337
+ } catch (err) {
338
+ _didIteratorError = true;
339
+ _iteratorError = err;
340
+ } finally{
341
+ try {
342
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
343
+ _iterator.return();
344
+ }
345
+ } finally{
346
+ if (_didIteratorError) {
347
+ throw _iteratorError;
348
+ }
349
+ }
350
+ }
351
+ var result;
352
+ if (hits.length === 1) {
353
+ result = {
354
+ kind: 'match',
355
+ via: 'param',
356
+ state: hits[0].state,
357
+ params: hits[0].params,
358
+ pathname: pathname
359
+ };
360
+ } else if (hits.length > 1) {
361
+ result = {
362
+ kind: 'ambiguous',
363
+ states: hits.map(function(h) {
364
+ return h.state;
365
+ }),
366
+ pathname: pathname
367
+ };
368
+ } else {
369
+ result = {
370
+ kind: 'none',
371
+ candidates: scoreCandidates(states, pathname),
372
+ pathname: pathname
373
+ };
374
+ }
375
+ return result;
376
+ }
377
+ // MARK: Pure URL helpers (mirror of @dereekb/dbx-cli url-match.ts)
378
+ function normalizePathname(pathname) {
379
+ var result;
380
+ if (pathname.length === 0) {
381
+ result = '/';
382
+ } else if (pathname.length > 1 && pathname.endsWith('/')) {
383
+ result = pathname.slice(0, -1);
384
+ } else {
385
+ result = pathname;
386
+ }
387
+ return result;
388
+ }
389
+ function splitSegments(path) {
390
+ var normalized = path.startsWith('/') ? path.slice(1) : path;
391
+ return normalized.length === 0 ? [] : normalized.split('/');
392
+ }
393
+ function hasParamSegment(path) {
394
+ return path.includes(':') || path.includes('{');
395
+ }
396
+ function tryMatchSegments(route, input) {
397
+ if (route.length !== input.length) {
398
+ return undefined;
399
+ }
400
+ var params = {};
401
+ var result = params;
402
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
403
+ try {
404
+ for(var _iterator = route.entries()[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
405
+ var _step_value = _sliced_to_array$5(_step.value, 2), i = _step_value[0], r = _step_value[1];
406
+ var v = input[i];
407
+ if (r.startsWith(':')) {
408
+ params[r.slice(1)] = decodeURIComponent(v);
409
+ } else if (r.startsWith('{') && r.endsWith('}')) {
410
+ var inner = r.slice(1, -1);
411
+ var colonIdx = inner.indexOf(':');
412
+ var key = colonIdx >= 0 ? inner.slice(0, colonIdx) : inner;
413
+ params[key] = decodeURIComponent(v);
414
+ } else if (r !== v) {
415
+ result = undefined;
416
+ break;
417
+ }
418
+ }
419
+ } catch (err) {
420
+ _didIteratorError = true;
421
+ _iteratorError = err;
422
+ } finally{
423
+ try {
424
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
425
+ _iterator.return();
426
+ }
427
+ } finally{
428
+ if (_didIteratorError) {
429
+ throw _iteratorError;
430
+ }
431
+ }
432
+ }
433
+ return result;
434
+ }
435
+ function scoreCandidates(states, pathname) {
436
+ var segments = splitSegments(pathname);
437
+ var scored = [];
438
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
439
+ try {
440
+ for(var _iterator = states[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
441
+ var state = _step.value;
442
+ if (state.fullUrl === undefined) {
443
+ continue;
444
+ }
445
+ var candidateSegments = splitSegments(state.fullUrl);
446
+ var score = 0;
447
+ var maxIndex = Math.min(segments.length, candidateSegments.length);
448
+ for(var i = 0; i < maxIndex; i += 1){
449
+ if (segments[i] === candidateSegments[i]) {
450
+ score += 2;
451
+ } else if (candidateSegments[i].startsWith(':') || candidateSegments[i].startsWith('{')) {
452
+ score += 1;
453
+ } else {
454
+ break;
455
+ }
456
+ }
457
+ if (score > 0) {
458
+ scored.push({
459
+ state: state,
460
+ score: score
461
+ });
462
+ }
463
+ }
464
+ } catch (err) {
465
+ _didIteratorError = true;
466
+ _iteratorError = err;
467
+ } finally{
468
+ try {
469
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
470
+ _iterator.return();
471
+ }
472
+ } finally{
473
+ if (_didIteratorError) {
474
+ throw _iteratorError;
475
+ }
476
+ }
477
+ }
478
+ scored.sort(function(a, b) {
479
+ return b.score - a.score;
480
+ });
481
+ return scored.slice(0, 5).map(function(s) {
482
+ return s.state;
483
+ });
484
+ }
485
+
486
+ function asyncGeneratorStep$6(gen, resolve, reject, _next, _throw, key, arg) {
194
487
  try {
195
488
  var info = gen[key](arg);
196
489
  var value = info.value;
@@ -204,16 +497,16 @@ function asyncGeneratorStep$5(gen, resolve, reject, _next, _throw, key, arg) {
204
497
  Promise.resolve(value).then(_next, _throw);
205
498
  }
206
499
  }
207
- function _async_to_generator$5(fn) {
500
+ function _async_to_generator$6(fn) {
208
501
  return function() {
209
502
  var self = this, args = arguments;
210
503
  return new Promise(function(resolve, reject) {
211
504
  var gen = fn.apply(self, args);
212
505
  function _next(value) {
213
- asyncGeneratorStep$5(gen, resolve, reject, _next, _throw, "next", value);
506
+ asyncGeneratorStep$6(gen, resolve, reject, _next, _throw, "next", value);
214
507
  }
215
508
  function _throw(err) {
216
- asyncGeneratorStep$5(gen, resolve, reject, _next, _throw, "throw", err);
509
+ asyncGeneratorStep$6(gen, resolve, reject, _next, _throw, "throw", err);
217
510
  }
218
511
  _next(undefined);
219
512
  });
@@ -227,7 +520,7 @@ function _instanceof$1(left, right) {
227
520
  return left instanceof right;
228
521
  }
229
522
  }
230
- function _ts_generator$5(thisArg, body) {
523
+ function _ts_generator$6(thisArg, body) {
231
524
  var f, y, t, _ = {
232
525
  label: 0,
233
526
  sent: function() {
@@ -355,9 +648,9 @@ function _ts_generator$5(thisArg, body) {
355
648
  * @param details - The handler-level API details (carries the mapper + Tier 2/3 formatters).
356
649
  * @returns The MCP tool response content.
357
650
  */ function formatMcpToolResponse(result, params, details) {
358
- return _async_to_generator$5(function() {
651
+ return _async_to_generator$6(function() {
359
652
  var mcp, value, _tmp, context, response, summary;
360
- return _ts_generator$5(this, function(_state) {
653
+ return _ts_generator$6(this, function(_state) {
361
654
  switch(_state.label){
362
655
  case 0:
363
656
  mcp = details === null || details === void 0 ? void 0 : details.mcp;
@@ -510,6 +803,28 @@ function stringifyResult(result) {
510
803
  }
511
804
  return READ_ONLY_BY_CALL_TYPE[callType];
512
805
  }
806
+ /**
807
+ * Maps an effective read-only classification to the standard MCP {@link ToolAnnotations} hints
808
+ * advertised on `tools/list`.
809
+ *
810
+ * A definitively read-only tool advertises `{ readOnlyHint: true }`. Everything else — a known
811
+ * write (`false`) or an unclassified verb (`undefined`, e.g. `invoke`/custom) — fails safe to
812
+ * `{ readOnlyHint: false, destructiveHint: true }` so a client never mistakes an ambiguous tool
813
+ * for a safe read. `idempotentHint` / `openWorldHint` are intentionally left unset.
814
+ *
815
+ * Co-located with {@link resolveEffectiveReadOnly} / {@link READ_ONLY_BY_CALL_TYPE} so the
816
+ * read/write classification rules stay in one file.
817
+ *
818
+ * @param effectiveReadOnly - The resolved read-only classification from {@link resolveEffectiveReadOnly}.
819
+ * @returns The MCP annotations describing the tool's read/write behaviour.
820
+ */ function resolveMcpToolAnnotations(effectiveReadOnly) {
821
+ return effectiveReadOnly === true ? {
822
+ readOnlyHint: true
823
+ } : {
824
+ readOnlyHint: false,
825
+ destructiveHint: true
826
+ };
827
+ }
513
828
  /**
514
829
  * Resolves the OIDC scope required to invoke a given call type, or `undefined` for
515
830
  * non-CRUD calls. Thin re-export so the tool generator doesn't need to reach into
@@ -521,15 +836,15 @@ function stringifyResult(result) {
521
836
  return callModelOidcScopeForCallType(callType);
522
837
  }
523
838
 
524
- function _array_like_to_array$7(arr, len) {
839
+ function _array_like_to_array$8(arr, len) {
525
840
  if (len == null || len > arr.length) len = arr.length;
526
841
  for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
527
842
  return arr2;
528
843
  }
529
- function _array_with_holes$3(arr) {
844
+ function _array_with_holes$4(arr) {
530
845
  if (Array.isArray(arr)) return arr;
531
846
  }
532
- function _iterable_to_array_limit$3(arr, i) {
847
+ function _iterable_to_array_limit$4(arr, i) {
533
848
  var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
534
849
  if (_i == null) return;
535
850
  var _arr = [];
@@ -553,23 +868,23 @@ function _iterable_to_array_limit$3(arr, i) {
553
868
  }
554
869
  return _arr;
555
870
  }
556
- function _non_iterable_rest$3() {
871
+ function _non_iterable_rest$4() {
557
872
  throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
558
873
  }
559
- function _sliced_to_array$3(arr, i) {
560
- return _array_with_holes$3(arr) || _iterable_to_array_limit$3(arr, i) || _unsupported_iterable_to_array$7(arr, i) || _non_iterable_rest$3();
874
+ function _sliced_to_array$4(arr, i) {
875
+ return _array_with_holes$4(arr) || _iterable_to_array_limit$4(arr, i) || _unsupported_iterable_to_array$8(arr, i) || _non_iterable_rest$4();
561
876
  }
562
- function _type_of$2(obj) {
877
+ function _type_of$3(obj) {
563
878
  "@swc/helpers - typeof";
564
879
  return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
565
880
  }
566
- function _unsupported_iterable_to_array$7(o, minLen) {
881
+ function _unsupported_iterable_to_array$8(o, minLen) {
567
882
  if (!o) return;
568
- if (typeof o === "string") return _array_like_to_array$7(o, minLen);
883
+ if (typeof o === "string") return _array_like_to_array$8(o, minLen);
569
884
  var n = Object.prototype.toString.call(o).slice(8, -1);
570
885
  if (n === "Object" && o.constructor) n = o.constructor.name;
571
886
  if (n === "Map" || n === "Set") return Array.from(n);
572
- if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$7(o, minLen);
887
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$8(o, minLen);
573
888
  }
574
889
  /**
575
890
  * Default `toJsonSchema()` options.
@@ -605,18 +920,33 @@ function _unsupported_iterable_to_array$7(o, minLen) {
605
920
  var inputSchema = (_input_inputSchema = input.inputSchema) !== null && _input_inputSchema !== void 0 ? _input_inputSchema : {
606
921
  type: 'object'
607
922
  };
608
- var entry = input.outputSchema == null ? {
923
+ var entry = {
609
924
  name: input.name,
610
925
  description: input.description,
611
926
  inputSchema: inputSchema
612
- } : {
613
- name: input.name,
614
- description: input.description,
615
- inputSchema: inputSchema,
616
- outputSchema: input.outputSchema
617
927
  };
928
+ if (input.outputSchema != null) {
929
+ entry.outputSchema = input.outputSchema;
930
+ }
931
+ if (input.annotations != null) {
932
+ entry.annotations = input.annotations;
933
+ }
618
934
  return Object.freeze(entry);
619
935
  }
936
+ /**
937
+ * Description prefix prepended to a mutating tool's description so MCP clients that don't render
938
+ * {@link ToolAnnotations} still surface the write signal in plain text.
939
+ */ var MCP_WRITE_TOOL_DESCRIPTION_PREFIX = '[WRITE] ';
940
+ /**
941
+ * Prepends {@link MCP_WRITE_TOOL_DESCRIPTION_PREFIX} to a description when its annotations mark the
942
+ * tool as a write (`readOnlyHint === false`). Read-only tools are returned unchanged.
943
+ *
944
+ * @param description - The base tool description.
945
+ * @param annotations - The resolved MCP annotations for the tool.
946
+ * @returns The description, prefixed with the write marker when the tool mutates.
947
+ */ function applyWriteMarker(description, annotations) {
948
+ return annotations.readOnlyHint === false ? "".concat(MCP_WRITE_TOOL_DESCRIPTION_PREFIX).concat(description) : description;
949
+ }
620
950
  // MARK: Naming
621
951
  /**
622
952
  * The default specifier key used when a handler is not behind a specifier router.
@@ -835,18 +1165,18 @@ function _unsupported_iterable_to_array$7(o, minLen) {
835
1165
  var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
836
1166
  try {
837
1167
  for(var _iterator = Object.entries(apiDetails.models)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
838
- var _step_value = _sliced_to_array$3(_step.value, 2), modelType = _step_value[0], modelEntry = _step_value[1];
1168
+ var _step_value = _sliced_to_array$4(_step.value, 2), modelType = _step_value[0], modelEntry = _step_value[1];
839
1169
  var _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
840
1170
  try {
841
1171
  for(var _iterator1 = Object.entries(modelEntry.calls)[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
842
- var _step_value1 = _sliced_to_array$3(_step1.value, 2), callType = _step_value1[0], callDetails = _step_value1[1];
1172
+ var _step_value1 = _sliced_to_array$4(_step1.value, 2), callType = _step_value1[0], callDetails = _step_value1[1];
843
1173
  if (callDetails == null) {
844
1174
  continue;
845
1175
  }
846
1176
  var _iteratorNormalCompletion2 = true, _didIteratorError2 = false, _iteratorError2 = undefined;
847
1177
  try {
848
1178
  for(var _iterator2 = Object.entries(callDetails.specifiers)[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true){
849
- var _step_value2 = _sliced_to_array$3(_step2.value, 2), specifierKey = _step_value2[0], handlerDetails = _step_value2[1];
1179
+ var _step_value2 = _sliced_to_array$4(_step2.value, 2), specifierKey = _step_value2[0], handlerDetails = _step_value2[1];
850
1180
  var _ref;
851
1181
  var _naming_modelSegments, _handlerDetails_mcp, _handlerDetails_mcp1;
852
1182
  if (handlerDetails == null) {
@@ -983,7 +1313,7 @@ function _unsupported_iterable_to_array$7(o, minLen) {
983
1313
  return;
984
1314
  }
985
1315
  var manifestEntry = manifest === null || manifest === void 0 ? void 0 : manifest.get(mcpManifestKey(modelType, callType, specifier));
986
- var description = (_ref = manifestEntry === null || manifestEntry === void 0 ? void 0 : manifestEntry.description) !== null && _ref !== void 0 ? _ref : buildDefaultMcpToolDescription(modelType, callType, specifier);
1316
+ var baseDescription = (_ref = manifestEntry === null || manifestEntry === void 0 ? void 0 : manifestEntry.description) !== null && _ref !== void 0 ? _ref : buildDefaultMcpToolDescription(modelType, callType, specifier);
987
1317
  var inputSchema = resolveInputSchema({
988
1318
  handlerDetails: handlerDetails,
989
1319
  manifestEntry: manifestEntry,
@@ -1017,6 +1347,8 @@ function _unsupported_iterable_to_array$7(o, minLen) {
1017
1347
  }
1018
1348
  var requiredScope = (_resolveRequiredScope = resolveRequiredScope(callType)) !== null && _resolveRequiredScope !== void 0 ? _resolveRequiredScope : undefined;
1019
1349
  var effectiveReadOnly = resolveEffectiveReadOnly((_handlerDetails_mcp = handlerDetails.mcp) === null || _handlerDetails_mcp === void 0 ? void 0 : _handlerDetails_mcp.readOnly, callType);
1350
+ var annotations = resolveMcpToolAnnotations(effectiveReadOnly);
1351
+ var description = applyWriteMarker(baseDescription, annotations);
1020
1352
  var filterMetadata;
1021
1353
  if (classified.visibilityKind === 'declarative') {
1022
1354
  filterMetadata = {
@@ -1059,13 +1391,15 @@ function _unsupported_iterable_to_array$7(o, minLen) {
1059
1391
  name: name,
1060
1392
  description: description,
1061
1393
  inputSchema: inputSchema,
1062
- outputSchema: outputSchema
1394
+ outputSchema: outputSchema,
1395
+ annotations: annotations
1063
1396
  });
1064
1397
  var definition = {
1065
1398
  name: name,
1066
1399
  description: description,
1067
1400
  inputSchema: inputSchema,
1068
1401
  outputSchema: outputSchema,
1402
+ annotations: annotations,
1069
1403
  details: handlerDetails,
1070
1404
  dispatch: dispatch,
1071
1405
  filterMetadata: filterMetadata,
@@ -1094,7 +1428,7 @@ function resolveInputSchema(context) {
1094
1428
  var result;
1095
1429
  try {
1096
1430
  var exported = arktypeToJsonSchemaForExport(handlerDetails.inputType);
1097
- if (exported && (typeof exported === "undefined" ? "undefined" : _type_of$2(exported)) === 'object') {
1431
+ if (exported && (typeof exported === "undefined" ? "undefined" : _type_of$3(exported)) === 'object') {
1098
1432
  result = exported;
1099
1433
  } else {
1100
1434
  // arktypeToJsonSchemaForExport returned a non-object (shouldn't happen for object schemas).
@@ -1113,15 +1447,15 @@ function resolveInputSchema(context) {
1113
1447
  return result;
1114
1448
  }
1115
1449
 
1116
- function _array_like_to_array$6(arr, len) {
1450
+ function _array_like_to_array$7(arr, len) {
1117
1451
  if (len == null || len > arr.length) len = arr.length;
1118
1452
  for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
1119
1453
  return arr2;
1120
1454
  }
1121
- function _array_without_holes$5(arr) {
1122
- if (Array.isArray(arr)) return _array_like_to_array$6(arr);
1455
+ function _array_without_holes$6(arr) {
1456
+ if (Array.isArray(arr)) return _array_like_to_array$7(arr);
1123
1457
  }
1124
- function asyncGeneratorStep$4(gen, resolve, reject, _next, _throw, key, arg) {
1458
+ function asyncGeneratorStep$5(gen, resolve, reject, _next, _throw, key, arg) {
1125
1459
  try {
1126
1460
  var info = gen[key](arg);
1127
1461
  var value = info.value;
@@ -1135,39 +1469,39 @@ function asyncGeneratorStep$4(gen, resolve, reject, _next, _throw, key, arg) {
1135
1469
  Promise.resolve(value).then(_next, _throw);
1136
1470
  }
1137
1471
  }
1138
- function _async_to_generator$4(fn) {
1472
+ function _async_to_generator$5(fn) {
1139
1473
  return function() {
1140
1474
  var self = this, args = arguments;
1141
1475
  return new Promise(function(resolve, reject) {
1142
1476
  var gen = fn.apply(self, args);
1143
1477
  function _next(value) {
1144
- asyncGeneratorStep$4(gen, resolve, reject, _next, _throw, "next", value);
1478
+ asyncGeneratorStep$5(gen, resolve, reject, _next, _throw, "next", value);
1145
1479
  }
1146
1480
  function _throw(err) {
1147
- asyncGeneratorStep$4(gen, resolve, reject, _next, _throw, "throw", err);
1481
+ asyncGeneratorStep$5(gen, resolve, reject, _next, _throw, "throw", err);
1148
1482
  }
1149
1483
  _next(undefined);
1150
1484
  });
1151
1485
  };
1152
1486
  }
1153
- function _iterable_to_array$5(iter) {
1487
+ function _iterable_to_array$6(iter) {
1154
1488
  if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
1155
1489
  }
1156
- function _non_iterable_spread$5() {
1490
+ function _non_iterable_spread$6() {
1157
1491
  throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
1158
1492
  }
1159
- function _to_consumable_array$5(arr) {
1160
- return _array_without_holes$5(arr) || _iterable_to_array$5(arr) || _unsupported_iterable_to_array$6(arr) || _non_iterable_spread$5();
1493
+ function _to_consumable_array$6(arr) {
1494
+ return _array_without_holes$6(arr) || _iterable_to_array$6(arr) || _unsupported_iterable_to_array$7(arr) || _non_iterable_spread$6();
1161
1495
  }
1162
- function _unsupported_iterable_to_array$6(o, minLen) {
1496
+ function _unsupported_iterable_to_array$7(o, minLen) {
1163
1497
  if (!o) return;
1164
- if (typeof o === "string") return _array_like_to_array$6(o, minLen);
1498
+ if (typeof o === "string") return _array_like_to_array$7(o, minLen);
1165
1499
  var n = Object.prototype.toString.call(o).slice(8, -1);
1166
1500
  if (n === "Object" && o.constructor) n = o.constructor.name;
1167
1501
  if (n === "Map" || n === "Set") return Array.from(n);
1168
- if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$6(o, minLen);
1502
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$7(o, minLen);
1169
1503
  }
1170
- function _ts_generator$4(thisArg, body) {
1504
+ function _ts_generator$5(thisArg, body) {
1171
1505
  var f, y, t, _ = {
1172
1506
  label: 0,
1173
1507
  sent: function() {
@@ -1337,9 +1671,9 @@ function _ts_generator$4(thisArg, body) {
1337
1671
  }
1338
1672
  // MARK: Handler
1339
1673
  function modelGetToolHandler(args, ctx, deps) {
1340
- return _async_to_generator$4(function() {
1674
+ return _async_to_generator$5(function() {
1341
1675
  var result, input, identity, resolvedKeys, merged, error;
1342
- return _ts_generator$4(this, function(_state) {
1676
+ return _ts_generator$5(this, function(_state) {
1343
1677
  switch(_state.label){
1344
1678
  case 0:
1345
1679
  _state.trys.push([
@@ -1356,7 +1690,7 @@ function modelGetToolHandler(args, ctx, deps) {
1356
1690
  resolvedKeys = resolveKeys(input.keys, identity);
1357
1691
  return [
1358
1692
  4,
1359
- readInChunks({
1693
+ readInChunks$1({
1360
1694
  modelType: input.modelType,
1361
1695
  keys: resolvedKeys,
1362
1696
  auth: ctx.auth,
@@ -1445,10 +1779,10 @@ function resolveKeys(keys, identity) {
1445
1779
  }
1446
1780
  return resolved;
1447
1781
  }
1448
- function readInChunks(input) {
1449
- return _async_to_generator$4(function() {
1782
+ function readInChunks$1(input) {
1783
+ return _async_to_generator$5(function() {
1450
1784
  var modelType, keys, auth, readDocuments, merged, offset, _merged_results, _merged_errors, batch, partial;
1451
- return _ts_generator$4(this, function(_state) {
1785
+ return _ts_generator$5(this, function(_state) {
1452
1786
  switch(_state.label){
1453
1787
  case 0:
1454
1788
  modelType = input.modelType, keys = input.keys, auth = input.auth, readDocuments = input.readDocuments;
@@ -1470,8 +1804,8 @@ function readInChunks(input) {
1470
1804
  ];
1471
1805
  case 2:
1472
1806
  partial = _state.sent();
1473
- (_merged_results = merged.results).push.apply(_merged_results, _to_consumable_array$5(partial.results));
1474
- (_merged_errors = merged.errors).push.apply(_merged_errors, _to_consumable_array$5(partial.errors));
1807
+ (_merged_results = merged.results).push.apply(_merged_results, _to_consumable_array$6(partial.results));
1808
+ (_merged_errors = merged.errors).push.apply(_merged_errors, _to_consumable_array$6(partial.errors));
1475
1809
  _state.label = 3;
1476
1810
  case 3:
1477
1811
  offset += MCP_MODEL_GET_BATCH_SIZE;
@@ -1562,15 +1896,15 @@ var MODEL_GET_OUTPUT_SCHEMA = {
1562
1896
  }
1563
1897
  };
1564
1898
 
1565
- function _array_like_to_array$5(arr, len) {
1899
+ function _array_like_to_array$6(arr, len) {
1566
1900
  if (len == null || len > arr.length) len = arr.length;
1567
1901
  for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
1568
1902
  return arr2;
1569
1903
  }
1570
- function _array_without_holes$4(arr) {
1571
- if (Array.isArray(arr)) return _array_like_to_array$5(arr);
1904
+ function _array_without_holes$5(arr) {
1905
+ if (Array.isArray(arr)) return _array_like_to_array$6(arr);
1572
1906
  }
1573
- function _define_property$6(obj, key, value) {
1907
+ function _define_property$7(obj, key, value) {
1574
1908
  if (key in obj) {
1575
1909
  Object.defineProperty(obj, key, {
1576
1910
  value: value,
@@ -1583,13 +1917,13 @@ function _define_property$6(obj, key, value) {
1583
1917
  }
1584
1918
  return obj;
1585
1919
  }
1586
- function _iterable_to_array$4(iter) {
1920
+ function _iterable_to_array$5(iter) {
1587
1921
  if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
1588
1922
  }
1589
- function _non_iterable_spread$4() {
1923
+ function _non_iterable_spread$5() {
1590
1924
  throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
1591
1925
  }
1592
- function _object_spread$2(target) {
1926
+ function _object_spread$4(target) {
1593
1927
  for(var i = 1; i < arguments.length; i++){
1594
1928
  var source = arguments[i] != null ? arguments[i] : {};
1595
1929
  var ownKeys = Object.keys(source);
@@ -1599,12 +1933,12 @@ function _object_spread$2(target) {
1599
1933
  }));
1600
1934
  }
1601
1935
  ownKeys.forEach(function(key) {
1602
- _define_property$6(target, key, source[key]);
1936
+ _define_property$7(target, key, source[key]);
1603
1937
  });
1604
1938
  }
1605
1939
  return target;
1606
1940
  }
1607
- function ownKeys(object, enumerableOnly) {
1941
+ function ownKeys$1(object, enumerableOnly) {
1608
1942
  var keys = Object.keys(object);
1609
1943
  if (Object.getOwnPropertySymbols) {
1610
1944
  var symbols = Object.getOwnPropertySymbols(object);
@@ -1612,27 +1946,27 @@ function ownKeys(object, enumerableOnly) {
1612
1946
  }
1613
1947
  return keys;
1614
1948
  }
1615
- function _object_spread_props(target, source) {
1949
+ function _object_spread_props$1(target, source) {
1616
1950
  source = source != null ? source : {};
1617
1951
  if (Object.getOwnPropertyDescriptors) {
1618
1952
  Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
1619
1953
  } else {
1620
- ownKeys(Object(source)).forEach(function(key) {
1954
+ ownKeys$1(Object(source)).forEach(function(key) {
1621
1955
  Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
1622
1956
  });
1623
1957
  }
1624
1958
  return target;
1625
1959
  }
1626
- function _to_consumable_array$4(arr) {
1627
- return _array_without_holes$4(arr) || _iterable_to_array$4(arr) || _unsupported_iterable_to_array$5(arr) || _non_iterable_spread$4();
1960
+ function _to_consumable_array$5(arr) {
1961
+ return _array_without_holes$5(arr) || _iterable_to_array$5(arr) || _unsupported_iterable_to_array$6(arr) || _non_iterable_spread$5();
1628
1962
  }
1629
- function _unsupported_iterable_to_array$5(o, minLen) {
1963
+ function _unsupported_iterable_to_array$6(o, minLen) {
1630
1964
  if (!o) return;
1631
- if (typeof o === "string") return _array_like_to_array$5(o, minLen);
1965
+ if (typeof o === "string") return _array_like_to_array$6(o, minLen);
1632
1966
  var n = Object.prototype.toString.call(o).slice(8, -1);
1633
1967
  if (n === "Object" && o.constructor) n = o.constructor.name;
1634
1968
  if (n === "Map" || n === "Set") return Array.from(n);
1635
- if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$5(o, minLen);
1969
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$6(o, minLen);
1636
1970
  }
1637
1971
  // MARK: Constants
1638
1972
  /**
@@ -1711,7 +2045,8 @@ function modelInfoToolHandler(args, _ctx, deps) {
1711
2045
  var result;
1712
2046
  try {
1713
2047
  var input = parseModelInfoInput(args);
1714
- var output = resolveModelInfoOutput(input, deps.manifest);
2048
+ var resolved = resolveModelInfoOutput(input, deps.manifest);
2049
+ var output = attachReferencedEnums(resolved, deps.enums);
1715
2050
  result = {
1716
2051
  content: [
1717
2052
  {
@@ -1748,13 +2083,120 @@ function resolveModelInfoOutput(input, manifest) {
1748
2083
  }
1749
2084
  return output;
1750
2085
  }
1751
- // MARK: Input parsing
1752
- function parseModelInfoInput(args) {
1753
- var modelQueries = parseModelArg(args['model']);
1754
- var modelGroup = parseModelGroupArg(args['modelGroup']);
1755
- var all = parseBooleanArg(args['all'], 'all');
2086
+ // MARK: Enum attachment
2087
+ /**
2088
+ * Attaches the value→label tables for every enum referenced (by `enumRef`) on the returned models'
2089
+ * fields — recursively through `nestedFields` — as a top-level `enums` section.
2090
+ *
2091
+ * Gated behind full field detail: only `single` / `multiple` rows and `list` rows that opted into
2092
+ * `fields:true` carry the persisted `fields` array, so the compact default modes are untouched.
2093
+ * Returns the input unchanged when no enum map is wired or no referenced enum resolves.
2094
+ *
2095
+ * @param output - The resolved model-info output.
2096
+ * @param enums - The optional enum value tables keyed by name.
2097
+ * @returns The output, with an `enums` section attached when detailed rows reference known enums.
2098
+ */ function attachReferencedEnums(output, enums) {
2099
+ var result = output;
2100
+ if (enums != null) {
2101
+ var referenced = new Set();
2102
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
2103
+ try {
2104
+ for(var _iterator = collectDetailedEntries(output)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
2105
+ var entry = _step.value;
2106
+ collectFieldEnumRefs(entry.fields, referenced);
2107
+ }
2108
+ } catch (err) {
2109
+ _didIteratorError = true;
2110
+ _iteratorError = err;
2111
+ } finally{
2112
+ try {
2113
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
2114
+ _iterator.return();
2115
+ }
2116
+ } finally{
2117
+ if (_didIteratorError) {
2118
+ throw _iteratorError;
2119
+ }
2120
+ }
2121
+ }
2122
+ var tables = [];
2123
+ var _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
2124
+ try {
2125
+ for(var _iterator1 = referenced[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
2126
+ var name = _step1.value;
2127
+ var table = enums[name];
2128
+ if (table != null) tables.push(table);
2129
+ }
2130
+ } catch (err) {
2131
+ _didIteratorError1 = true;
2132
+ _iteratorError1 = err;
2133
+ } finally{
2134
+ try {
2135
+ if (!_iteratorNormalCompletion1 && _iterator1.return != null) {
2136
+ _iterator1.return();
2137
+ }
2138
+ } finally{
2139
+ if (_didIteratorError1) {
2140
+ throw _iteratorError1;
2141
+ }
2142
+ }
2143
+ }
2144
+ if (tables.length > 0) {
2145
+ result = _object_spread_props$1(_object_spread$4({}, output), {
2146
+ enums: tables
2147
+ });
2148
+ }
2149
+ }
2150
+ return result;
2151
+ }
2152
+ /**
2153
+ * Collects the full {@link McpManifestModelEntry} rows (those carrying a persisted `fields` array)
2154
+ * from a model-info output. Summary rows (which expose `fieldCount`, not `fields`) are skipped.
2155
+ *
2156
+ * @param output - The resolved model-info output.
2157
+ * @returns The detailed model entries present in the output.
2158
+ */ function collectDetailedEntries(output) {
2159
+ var rows = [];
2160
+ if (output.mode === 'single') {
2161
+ rows.push(output.model);
2162
+ } else if (output.mode === 'list' || output.mode === 'multiple') {
2163
+ var _rows;
2164
+ (_rows = rows).push.apply(_rows, _to_consumable_array$5(output.models));
2165
+ }
2166
+ return rows.filter(function(row) {
2167
+ return Array.isArray(row.fields);
2168
+ });
2169
+ }
2170
+ function collectFieldEnumRefs(fields, into) {
2171
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
2172
+ try {
2173
+ for(var _iterator = fields[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
2174
+ var field = _step.value;
2175
+ if (field.enumRef) into.add(field.enumRef);
2176
+ if (field.nestedFields) collectFieldEnumRefs(field.nestedFields, into);
2177
+ }
2178
+ } catch (err) {
2179
+ _didIteratorError = true;
2180
+ _iteratorError = err;
2181
+ } finally{
2182
+ try {
2183
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
2184
+ _iterator.return();
2185
+ }
2186
+ } finally{
2187
+ if (_didIteratorError) {
2188
+ throw _iteratorError;
2189
+ }
2190
+ }
2191
+ }
2192
+ }
2193
+ // MARK: Input parsing
2194
+ function parseModelInfoInput(args) {
2195
+ var modelQueries = parseModelArg(args['model']);
2196
+ var modelGroup = parseModelGroupArg(args['modelGroup']);
2197
+ var all = parseBooleanArg(args['all'], 'all');
1756
2198
  var fields = parseBooleanArg(args['fields'], 'fields');
1757
- return _object_spread$2(_object_spread_props(_object_spread$2(_object_spread_props(_object_spread$2({}, modelQueries == null ? {} : {
2199
+ return _object_spread$4(_object_spread_props$1(_object_spread$4(_object_spread_props$1(_object_spread$4({}, modelQueries == null ? {} : {
1758
2200
  modelQueries: modelQueries
1759
2201
  }), {
1760
2202
  modelIsArray: Array.isArray(args['model'])
@@ -1772,14 +2214,14 @@ function parseModelArg(raw) {
1772
2214
  result = undefined;
1773
2215
  } else if (typeof raw === 'string') {
1774
2216
  result = [
1775
- requireNonEmptyString(raw, 'model')
2217
+ requireNonEmptyString$1(raw, 'model')
1776
2218
  ];
1777
2219
  } else if (Array.isArray(raw)) {
1778
2220
  if (raw.length === 0) {
1779
2221
  throw new Error('model-info: "model" array must contain at least one entry.');
1780
2222
  }
1781
2223
  result = raw.map(function(value, index) {
1782
- return requireNonEmptyString(value, "model[".concat(index, "]"));
2224
+ return requireNonEmptyString$1(value, "model[".concat(index, "]"));
1783
2225
  });
1784
2226
  } else {
1785
2227
  throw new TypeError('model-info: "model" must be a string or an array of strings when provided.');
@@ -1791,7 +2233,7 @@ function parseModelGroupArg(raw) {
1791
2233
  if (raw == null) {
1792
2234
  result = undefined;
1793
2235
  } else if (typeof raw === 'string') {
1794
- result = requireNonEmptyString(raw, 'modelGroup');
2236
+ result = requireNonEmptyString$1(raw, 'modelGroup');
1795
2237
  } else {
1796
2238
  throw new TypeError('model-info: "modelGroup" must be a string when provided.');
1797
2239
  }
@@ -1808,7 +2250,7 @@ function parseBooleanArg(raw, label) {
1808
2250
  }
1809
2251
  return result;
1810
2252
  }
1811
- function requireNonEmptyString(value, label) {
2253
+ function requireNonEmptyString$1(value, label) {
1812
2254
  if (typeof value !== 'string') {
1813
2255
  throw new TypeError('model-info: "'.concat(label, '" must be a string.'));
1814
2256
  }
@@ -1917,7 +2359,7 @@ function toModelRow(entry, detailed) {
1917
2359
  return detailed ? entry : toSummaryRow(entry);
1918
2360
  }
1919
2361
  function toSummaryRow(entry) {
1920
- return _object_spread$2({
2362
+ return _object_spread$4({
1921
2363
  modelType: entry.modelType,
1922
2364
  modelName: entry.modelName,
1923
2365
  identityConst: entry.identityConst,
@@ -1945,7 +2387,7 @@ function toSummaryRow(entry) {
1945
2387
  *
1946
2388
  * @__NO_SIDE_EFFECTS__
1947
2389
  */ function computeGroupCounts(manifest) {
1948
- var grouped = makeValuesGroupMap(_to_consumable_array$4(manifest), function(entry) {
2390
+ var grouped = makeValuesGroupMap(_to_consumable_array$5(manifest), function(entry) {
1949
2391
  var _entry_modelGroup;
1950
2392
  return (_entry_modelGroup = entry.modelGroup) !== null && _entry_modelGroup !== void 0 ? _entry_modelGroup : MODEL_INFO_UNGROUPED_LABEL;
1951
2393
  });
@@ -2155,6 +2597,48 @@ var MODEL_ROW_SCHEMA = {
2155
2597
  }
2156
2598
  }
2157
2599
  };
2600
+ /**
2601
+ * JSON-schema for one enum value→label table. Shared with the `enum-info` tool so both advertise the
2602
+ * identical enum shape.
2603
+ */ var ENUM_TABLE_SCHEMA = {
2604
+ type: 'object',
2605
+ required: [
2606
+ 'name',
2607
+ 'values'
2608
+ ],
2609
+ properties: {
2610
+ name: {
2611
+ type: 'string'
2612
+ },
2613
+ description: {
2614
+ type: 'string'
2615
+ },
2616
+ values: {
2617
+ type: 'array',
2618
+ items: {
2619
+ type: 'object',
2620
+ required: [
2621
+ 'name',
2622
+ 'value'
2623
+ ],
2624
+ properties: {
2625
+ name: {
2626
+ type: 'string'
2627
+ },
2628
+ value: {
2629
+ type: [
2630
+ 'string',
2631
+ 'number'
2632
+ ]
2633
+ },
2634
+ description: {
2635
+ type: 'string'
2636
+ }
2637
+ }
2638
+ }
2639
+ }
2640
+ }
2641
+ };
2158
2642
  // MCP's `tools/list` validator requires `outputSchema.type === 'object'` at the root
2159
2643
  // (see @modelcontextprotocol/sdk Zod schema), so the variants are expressed via a top-level
2160
2644
  // object whose nested `oneOf` discriminates by the `mode` literal.
@@ -2210,7 +2694,7 @@ var MODEL_INFO_OUTPUT_SCHEMA = {
2210
2694
  type: 'string',
2211
2695
  description: 'Present when `mode` is `"list"` from a `modelGroup` filter. The canonical group name.'
2212
2696
  },
2213
- model: _object_spread_props(_object_spread$2({}, MODEL_ROW_SCHEMA), {
2697
+ model: _object_spread_props$1(_object_spread$4({}, MODEL_ROW_SCHEMA), {
2214
2698
  description: 'Present when `mode` is `"single"`. The matched model entry (full detail unless `fields:false`).'
2215
2699
  }),
2216
2700
  notFound: {
@@ -2231,6 +2715,11 @@ var MODEL_INFO_OUTPUT_SCHEMA = {
2231
2715
  }
2232
2716
  }
2233
2717
  }
2718
+ },
2719
+ enums: {
2720
+ type: 'array',
2721
+ 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.',
2722
+ items: ENUM_TABLE_SCHEMA
2234
2723
  }
2235
2724
  },
2236
2725
  oneOf: [
@@ -2278,7 +2767,7 @@ var MODEL_INFO_OUTPUT_SCHEMA = {
2278
2767
  ]
2279
2768
  };
2280
2769
 
2281
- function _define_property$5(obj, key, value) {
2770
+ function _define_property$6(obj, key, value) {
2282
2771
  if (key in obj) {
2283
2772
  Object.defineProperty(obj, key, {
2284
2773
  value: value,
@@ -2291,7 +2780,7 @@ function _define_property$5(obj, key, value) {
2291
2780
  }
2292
2781
  return obj;
2293
2782
  }
2294
- function _object_spread$1(target) {
2783
+ function _object_spread$3(target) {
2295
2784
  for(var i = 1; i < arguments.length; i++){
2296
2785
  var source = arguments[i] != null ? arguments[i] : {};
2297
2786
  var ownKeys = Object.keys(source);
@@ -2301,7 +2790,7 @@ function _object_spread$1(target) {
2301
2790
  }));
2302
2791
  }
2303
2792
  ownKeys.forEach(function(key) {
2304
- _define_property$5(target, key, source[key]);
2793
+ _define_property$6(target, key, source[key]);
2305
2794
  });
2306
2795
  }
2307
2796
  return target;
@@ -2439,7 +2928,7 @@ function parseModelDecodeInput(args) {
2439
2928
  };
2440
2929
  }
2441
2930
  function toSegment(prefix, id, entry) {
2442
- var segment = entry ? _object_spread$1({
2931
+ var segment = entry ? _object_spread$3({
2443
2932
  prefix: prefix,
2444
2933
  id: id,
2445
2934
  modelType: entry.modelType,
@@ -2537,18 +3026,219 @@ var MODEL_DECODE_OUTPUT_SCHEMA = {
2537
3026
  }
2538
3027
  };
2539
3028
 
2540
- function _array_like_to_array$4(arr, len) {
3029
+ // MARK: Constants
3030
+ /**
3031
+ * Reserved tool name for the built-in `enum-info` static tool.
3032
+ */ var ENUM_INFO_TOOL_NAME = 'enum-info';
3033
+ /**
3034
+ * Synthetic call type used in the tool's dispatch identity. Mirrors the `info` call shared with
3035
+ * `model-info`.
3036
+ */ var ENUM_INFO_DISPATCH_CALL = 'info';
3037
+ /**
3038
+ * Synthetic model type used in the tool's dispatch identity. Apps avoiding collisions should not
3039
+ * register a real model literally named "enum".
3040
+ */ var ENUM_INFO_DISPATCH_MODEL_TYPE = 'enum';
3041
+ // MARK: Factory
3042
+ /**
3043
+ * Builds the built-in `enum-info` MCP tool definition — the symmetric counterpart to `model-info`
3044
+ * for raw enum decoding.
3045
+ *
3046
+ * Resolves each requested enum name against the manifest's `enums` block and returns the matching
3047
+ * value→label tables; misses land in `notFound`. A bare string is treated as a one-element array.
3048
+ *
3049
+ * Output is delivered as both stringified JSON in `content[0].text` and `structuredContent` so MCP
3050
+ * clients can consume either form.
3051
+ *
3052
+ * @param deps - The frozen enum value tables loaded at boot from the MCP manifest JSON.
3053
+ * @returns A statically-registered {@link McpToolDefinition} ready to be appended to the MCP server
3054
+ * factory's tool registry.
3055
+ */ function createEnumInfoTool(deps) {
3056
+ var handler = function handler(args, ctx) {
3057
+ return Promise.resolve(enumInfoToolHandler(args, ctx, deps));
3058
+ };
3059
+ var name = ENUM_INFO_TOOL_NAME;
3060
+ var count = Object.keys(deps.enums).length;
3061
+ 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`.');
3062
+ return {
3063
+ name: name,
3064
+ description: description,
3065
+ inputSchema: ENUM_INFO_INPUT_SCHEMA,
3066
+ outputSchema: ENUM_INFO_OUTPUT_SCHEMA,
3067
+ dispatch: {
3068
+ call: ENUM_INFO_DISPATCH_CALL,
3069
+ modelType: ENUM_INFO_DISPATCH_MODEL_TYPE
3070
+ },
3071
+ staticHandler: handler,
3072
+ filterMetadata: {
3073
+ visibilityKind: 'declarative',
3074
+ rule: {
3075
+ requireAuthenticated: true
3076
+ },
3077
+ effectiveReadOnly: true
3078
+ },
3079
+ staticWireEntry: buildStaticWireEntry({
3080
+ name: name,
3081
+ description: description,
3082
+ inputSchema: ENUM_INFO_INPUT_SCHEMA,
3083
+ outputSchema: ENUM_INFO_OUTPUT_SCHEMA
3084
+ })
3085
+ };
3086
+ }
3087
+ // MARK: Handler
3088
+ function enumInfoToolHandler(args, _ctx, deps) {
3089
+ var result;
3090
+ try {
3091
+ var names = parseEnumInfoInput(args);
3092
+ var output = resolveEnumInfoOutput(names, deps.enums);
3093
+ result = {
3094
+ content: [
3095
+ {
3096
+ type: 'text',
3097
+ text: JSON.stringify(output)
3098
+ }
3099
+ ],
3100
+ structuredContent: output
3101
+ };
3102
+ } catch (error) {
3103
+ result = formatMcpToolErrorResponse(error);
3104
+ }
3105
+ return result;
3106
+ }
3107
+ /**
3108
+ * Resolves each requested enum name against the registered value tables.
3109
+ *
3110
+ * @param names - The requested enum declaration names.
3111
+ * @param enums - The registered enum value tables keyed by name.
3112
+ * @returns The matched tables, with any unmatched names in `notFound`.
3113
+ *
3114
+ * @__NO_SIDE_EFFECTS__
3115
+ */ function resolveEnumInfoOutput(names, enums) {
3116
+ var found = [];
3117
+ var notFound = [];
3118
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
3119
+ try {
3120
+ for(var _iterator = names[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
3121
+ var name = _step.value;
3122
+ var table = enums[name];
3123
+ if (table == null) {
3124
+ notFound.push(name);
3125
+ } else {
3126
+ found.push(table);
3127
+ }
3128
+ }
3129
+ } catch (err) {
3130
+ _didIteratorError = true;
3131
+ _iteratorError = err;
3132
+ } finally{
3133
+ try {
3134
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
3135
+ _iterator.return();
3136
+ }
3137
+ } finally{
3138
+ if (_didIteratorError) {
3139
+ throw _iteratorError;
3140
+ }
3141
+ }
3142
+ }
3143
+ return notFound.length > 0 ? {
3144
+ enums: found,
3145
+ notFound: notFound
3146
+ } : {
3147
+ enums: found
3148
+ };
3149
+ }
3150
+ // MARK: Input parsing
3151
+ function parseEnumInfoInput(args) {
3152
+ var raw = args['enum'];
3153
+ var result;
3154
+ if (raw == null) {
3155
+ throw new Error('enum-info: "enum" is required and must be a string or an array of strings.');
3156
+ } else if (typeof raw === 'string') {
3157
+ result = [
3158
+ requireNonEmptyString(raw, 'enum')
3159
+ ];
3160
+ } else if (Array.isArray(raw)) {
3161
+ if (raw.length === 0) {
3162
+ throw new Error('enum-info: "enum" array must contain at least one entry.');
3163
+ }
3164
+ result = raw.map(function(value, index) {
3165
+ return requireNonEmptyString(value, "enum[".concat(index, "]"));
3166
+ });
3167
+ } else {
3168
+ throw new TypeError('enum-info: "enum" must be a string or an array of strings.');
3169
+ }
3170
+ return result;
3171
+ }
3172
+ function requireNonEmptyString(value, label) {
3173
+ if (typeof value !== 'string') {
3174
+ throw new TypeError('enum-info: "'.concat(label, '" must be a string.'));
3175
+ }
3176
+ var trimmed = value.trim();
3177
+ if (trimmed.length === 0) {
3178
+ throw new Error('enum-info: "'.concat(label, '" must be a non-empty string.'));
3179
+ }
3180
+ return trimmed;
3181
+ }
3182
+ // MARK: Schemas
3183
+ var ENUM_INFO_INPUT_SCHEMA = {
3184
+ type: 'object',
3185
+ required: [
3186
+ 'enum'
3187
+ ],
3188
+ properties: {
3189
+ enum: {
3190
+ description: 'One enum name (string) or many (string array), each matched exactly by declaration name (e.g. "JobWorkerTimesheetState"). Misses are reported in `notFound`.',
3191
+ oneOf: [
3192
+ {
3193
+ type: 'string',
3194
+ minLength: 1
3195
+ },
3196
+ {
3197
+ type: 'array',
3198
+ minItems: 1,
3199
+ items: {
3200
+ type: 'string',
3201
+ minLength: 1
3202
+ }
3203
+ }
3204
+ ]
3205
+ }
3206
+ },
3207
+ additionalProperties: false
3208
+ };
3209
+ var ENUM_INFO_OUTPUT_SCHEMA = {
3210
+ type: 'object',
3211
+ required: [
3212
+ 'enums'
3213
+ ],
3214
+ properties: {
3215
+ enums: {
3216
+ type: 'array',
3217
+ description: 'The resolved enum value→label tables, in request order.',
3218
+ items: ENUM_TABLE_SCHEMA
3219
+ },
3220
+ notFound: {
3221
+ type: 'array',
3222
+ description: 'Requested enum names that matched no registered enum.',
3223
+ items: {
3224
+ type: 'string'
3225
+ }
3226
+ }
3227
+ }
3228
+ };
3229
+
3230
+ function _array_like_to_array$5(arr, len) {
2541
3231
  if (len == null || len > arr.length) len = arr.length;
2542
3232
  for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
2543
3233
  return arr2;
2544
3234
  }
2545
- function _array_with_holes$2(arr) {
3235
+ function _array_with_holes$3(arr) {
2546
3236
  if (Array.isArray(arr)) return arr;
2547
3237
  }
2548
- function _array_without_holes$3(arr) {
2549
- if (Array.isArray(arr)) return _array_like_to_array$4(arr);
3238
+ function _array_without_holes$4(arr) {
3239
+ if (Array.isArray(arr)) return _array_like_to_array$5(arr);
2550
3240
  }
2551
- function _define_property$4(obj, key, value) {
3241
+ function _define_property$5(obj, key, value) {
2552
3242
  if (key in obj) {
2553
3243
  Object.defineProperty(obj, key, {
2554
3244
  value: value,
@@ -2561,10 +3251,10 @@ function _define_property$4(obj, key, value) {
2561
3251
  }
2562
3252
  return obj;
2563
3253
  }
2564
- function _iterable_to_array$3(iter) {
3254
+ function _iterable_to_array$4(iter) {
2565
3255
  if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
2566
3256
  }
2567
- function _iterable_to_array_limit$2(arr, i) {
3257
+ function _iterable_to_array_limit$3(arr, i) {
2568
3258
  var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
2569
3259
  if (_i == null) return;
2570
3260
  var _arr = [];
@@ -2588,13 +3278,13 @@ function _iterable_to_array_limit$2(arr, i) {
2588
3278
  }
2589
3279
  return _arr;
2590
3280
  }
2591
- function _non_iterable_rest$2() {
3281
+ function _non_iterable_rest$3() {
2592
3282
  throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
2593
3283
  }
2594
- function _non_iterable_spread$3() {
3284
+ function _non_iterable_spread$4() {
2595
3285
  throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
2596
3286
  }
2597
- function _object_spread(target) {
3287
+ function _object_spread$2(target) {
2598
3288
  for(var i = 1; i < arguments.length; i++){
2599
3289
  var source = arguments[i] != null ? arguments[i] : {};
2600
3290
  var ownKeys = Object.keys(source);
@@ -2604,24 +3294,24 @@ function _object_spread(target) {
2604
3294
  }));
2605
3295
  }
2606
3296
  ownKeys.forEach(function(key) {
2607
- _define_property$4(target, key, source[key]);
3297
+ _define_property$5(target, key, source[key]);
2608
3298
  });
2609
3299
  }
2610
3300
  return target;
2611
3301
  }
2612
- function _sliced_to_array$2(arr, i) {
2613
- return _array_with_holes$2(arr) || _iterable_to_array_limit$2(arr, i) || _unsupported_iterable_to_array$4(arr, i) || _non_iterable_rest$2();
3302
+ function _sliced_to_array$3(arr, i) {
3303
+ return _array_with_holes$3(arr) || _iterable_to_array_limit$3(arr, i) || _unsupported_iterable_to_array$5(arr, i) || _non_iterable_rest$3();
2614
3304
  }
2615
- function _to_consumable_array$3(arr) {
2616
- return _array_without_holes$3(arr) || _iterable_to_array$3(arr) || _unsupported_iterable_to_array$4(arr) || _non_iterable_spread$3();
3305
+ function _to_consumable_array$4(arr) {
3306
+ return _array_without_holes$4(arr) || _iterable_to_array$4(arr) || _unsupported_iterable_to_array$5(arr) || _non_iterable_spread$4();
2617
3307
  }
2618
- function _unsupported_iterable_to_array$4(o, minLen) {
3308
+ function _unsupported_iterable_to_array$5(o, minLen) {
2619
3309
  if (!o) return;
2620
- if (typeof o === "string") return _array_like_to_array$4(o, minLen);
3310
+ if (typeof o === "string") return _array_like_to_array$5(o, minLen);
2621
3311
  var n = Object.prototype.toString.call(o).slice(8, -1);
2622
3312
  if (n === "Object" && o.constructor) n = o.constructor.name;
2623
3313
  if (n === "Map" || n === "Set") return Array.from(n);
2624
- if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$4(o, minLen);
3314
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$5(o, minLen);
2625
3315
  }
2626
3316
  // MARK: Constants
2627
3317
  /**
@@ -2695,14 +3385,14 @@ function whoamiToolHandler(ctx, deps) {
2695
3385
  }
2696
3386
  var token = (_auth_token = auth.token) !== null && _auth_token !== void 0 ? _auth_token : {};
2697
3387
  var presentKeys = Object.entries(token).filter(function(param) {
2698
- var _param = _sliced_to_array$2(param, 2), value = _param[1];
3388
+ var _param = _sliced_to_array$3(param, 2), value = _param[1];
2699
3389
  return value !== undefined;
2700
3390
  }).map(function(param) {
2701
- var _param = _sliced_to_array$2(param, 1), key = _param[0];
3391
+ var _param = _sliced_to_array$3(param, 1), key = _param[0];
2702
3392
  return key;
2703
3393
  });
2704
3394
  var roleSet = deps.roleReader == null ? new Set() : deps.roleReader(token);
2705
- var roles = _to_consumable_array$3(roleSet).sort(function(a, b) {
3395
+ var roles = _to_consumable_array$4(roleSet).sort(function(a, b) {
2706
3396
  return a.localeCompare(b);
2707
3397
  });
2708
3398
  var claimsByKey = new Map();
@@ -2739,7 +3429,7 @@ function whoamiToolHandler(ctx, deps) {
2739
3429
  if (claim1 == null) {
2740
3430
  unknownClaimKeys.push(key);
2741
3431
  } else {
2742
- claimDetails.push(_object_spread({
3432
+ claimDetails.push(_object_spread$2({
2743
3433
  key: claim1.key,
2744
3434
  description: claim1.description,
2745
3435
  grantedRoles: claim1.mapping.roles,
@@ -2764,10 +3454,10 @@ function whoamiToolHandler(ctx, deps) {
2764
3454
  }
2765
3455
  }
2766
3456
  }
2767
- var output1 = _object_spread({
3457
+ var output1 = _object_spread$2({
2768
3458
  authenticated: true,
2769
3459
  uid: auth.uid,
2770
- claims: _object_spread({}, token),
3460
+ claims: _object_spread$2({}, token),
2771
3461
  roles: roles,
2772
3462
  claimDetails: claimDetails,
2773
3463
  unknownClaimKeys: unknownClaimKeys
@@ -2852,121 +3542,1020 @@ function appendClaimDetailLines(lines, claimDetails) {
2852
3542
  if (_didIteratorError) {
2853
3543
  throw _iteratorError;
2854
3544
  }
2855
- }
2856
- }
3545
+ }
3546
+ }
3547
+ }
3548
+ function appendUnknownClaimLines(lines, unknownClaimKeys) {
3549
+ if (unknownClaimKeys.length === 0) {
3550
+ return;
3551
+ }
3552
+ lines.push('', '## Unknown claim keys');
3553
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
3554
+ try {
3555
+ for(var _iterator = unknownClaimKeys[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
3556
+ var key = _step.value;
3557
+ lines.push("- `".concat(key, "`"));
3558
+ }
3559
+ } catch (err) {
3560
+ _didIteratorError = true;
3561
+ _iteratorError = err;
3562
+ } finally{
3563
+ try {
3564
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
3565
+ _iterator.return();
3566
+ }
3567
+ } finally{
3568
+ if (_didIteratorError) {
3569
+ throw _iteratorError;
3570
+ }
3571
+ }
3572
+ }
3573
+ }
3574
+ // MARK: Schemas
3575
+ var WHOAMI_INPUT_SCHEMA = {
3576
+ type: 'object',
3577
+ properties: {},
3578
+ additionalProperties: false
3579
+ };
3580
+ var WHOAMI_OUTPUT_SCHEMA = {
3581
+ type: 'object',
3582
+ required: [
3583
+ 'authenticated',
3584
+ 'claims',
3585
+ 'roles',
3586
+ 'claimDetails',
3587
+ 'unknownClaimKeys'
3588
+ ],
3589
+ description: 'Caller identity, raw token claims, decoded role set, and per-claim descriptions for every key present on the live token.',
3590
+ properties: {
3591
+ authenticated: {
3592
+ type: 'boolean'
3593
+ },
3594
+ uid: {
3595
+ type: 'string'
3596
+ },
3597
+ email: {
3598
+ type: 'string'
3599
+ },
3600
+ emailVerified: {
3601
+ type: 'boolean'
3602
+ },
3603
+ app: {
3604
+ type: 'string'
3605
+ },
3606
+ claims: {
3607
+ type: 'object',
3608
+ additionalProperties: true
3609
+ },
3610
+ roles: {
3611
+ type: 'array',
3612
+ items: {
3613
+ type: 'string'
3614
+ }
3615
+ },
3616
+ claimDetails: {
3617
+ type: 'array',
3618
+ items: {
3619
+ type: 'object',
3620
+ required: [
3621
+ 'key',
3622
+ 'description',
3623
+ 'grantedRoles',
3624
+ 'inverse',
3625
+ 'tags'
3626
+ ],
3627
+ properties: {
3628
+ key: {
3629
+ type: 'string'
3630
+ },
3631
+ interfaceName: {
3632
+ type: 'string'
3633
+ },
3634
+ description: {
3635
+ type: 'string'
3636
+ },
3637
+ grantedRoles: {
3638
+ type: 'array',
3639
+ items: {
3640
+ type: 'string'
3641
+ }
3642
+ },
3643
+ inverse: {
3644
+ type: 'boolean'
3645
+ },
3646
+ tags: {
3647
+ type: 'array',
3648
+ items: {
3649
+ type: 'string'
3650
+ }
3651
+ }
3652
+ }
3653
+ }
3654
+ },
3655
+ unknownClaimKeys: {
3656
+ type: 'array',
3657
+ items: {
3658
+ type: 'string'
3659
+ }
3660
+ }
3661
+ }
3662
+ };
3663
+
3664
+ function _array_like_to_array$4(arr, len) {
3665
+ if (len == null || len > arr.length) len = arr.length;
3666
+ for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
3667
+ return arr2;
3668
+ }
3669
+ function _array_with_holes$2(arr) {
3670
+ if (Array.isArray(arr)) return arr;
3671
+ }
3672
+ function _array_without_holes$3(arr) {
3673
+ if (Array.isArray(arr)) return _array_like_to_array$4(arr);
3674
+ }
3675
+ function asyncGeneratorStep$4(gen, resolve, reject, _next, _throw, key, arg) {
3676
+ try {
3677
+ var info = gen[key](arg);
3678
+ var value = info.value;
3679
+ } catch (error) {
3680
+ reject(error);
3681
+ return;
3682
+ }
3683
+ if (info.done) {
3684
+ resolve(value);
3685
+ } else {
3686
+ Promise.resolve(value).then(_next, _throw);
3687
+ }
3688
+ }
3689
+ function _async_to_generator$4(fn) {
3690
+ return function() {
3691
+ var self = this, args = arguments;
3692
+ return new Promise(function(resolve, reject) {
3693
+ var gen = fn.apply(self, args);
3694
+ function _next(value) {
3695
+ asyncGeneratorStep$4(gen, resolve, reject, _next, _throw, "next", value);
3696
+ }
3697
+ function _throw(err) {
3698
+ asyncGeneratorStep$4(gen, resolve, reject, _next, _throw, "throw", err);
3699
+ }
3700
+ _next(undefined);
3701
+ });
3702
+ };
3703
+ }
3704
+ function _define_property$4(obj, key, value) {
3705
+ if (key in obj) {
3706
+ Object.defineProperty(obj, key, {
3707
+ value: value,
3708
+ enumerable: true,
3709
+ configurable: true,
3710
+ writable: true
3711
+ });
3712
+ } else {
3713
+ obj[key] = value;
3714
+ }
3715
+ return obj;
3716
+ }
3717
+ function _iterable_to_array$3(iter) {
3718
+ if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
3719
+ }
3720
+ function _iterable_to_array_limit$2(arr, i) {
3721
+ var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
3722
+ if (_i == null) return;
3723
+ var _arr = [];
3724
+ var _n = true;
3725
+ var _d = false;
3726
+ var _s, _e;
3727
+ try {
3728
+ for(_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true){
3729
+ _arr.push(_s.value);
3730
+ if (i && _arr.length === i) break;
3731
+ }
3732
+ } catch (err) {
3733
+ _d = true;
3734
+ _e = err;
3735
+ } finally{
3736
+ try {
3737
+ if (!_n && _i["return"] != null) _i["return"]();
3738
+ } finally{
3739
+ if (_d) throw _e;
3740
+ }
3741
+ }
3742
+ return _arr;
3743
+ }
3744
+ function _non_iterable_rest$2() {
3745
+ throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
3746
+ }
3747
+ function _non_iterable_spread$3() {
3748
+ throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
3749
+ }
3750
+ function _object_spread$1(target) {
3751
+ for(var i = 1; i < arguments.length; i++){
3752
+ var source = arguments[i] != null ? arguments[i] : {};
3753
+ var ownKeys = Object.keys(source);
3754
+ if (typeof Object.getOwnPropertySymbols === "function") {
3755
+ ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
3756
+ return Object.getOwnPropertyDescriptor(source, sym).enumerable;
3757
+ }));
3758
+ }
3759
+ ownKeys.forEach(function(key) {
3760
+ _define_property$4(target, key, source[key]);
3761
+ });
3762
+ }
3763
+ return target;
3764
+ }
3765
+ function ownKeys(object, enumerableOnly) {
3766
+ var keys = Object.keys(object);
3767
+ if (Object.getOwnPropertySymbols) {
3768
+ var symbols = Object.getOwnPropertySymbols(object);
3769
+ keys.push.apply(keys, symbols);
3770
+ }
3771
+ return keys;
3772
+ }
3773
+ function _object_spread_props(target, source) {
3774
+ source = source != null ? source : {};
3775
+ if (Object.getOwnPropertyDescriptors) {
3776
+ Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
3777
+ } else {
3778
+ ownKeys(Object(source)).forEach(function(key) {
3779
+ Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
3780
+ });
3781
+ }
3782
+ return target;
3783
+ }
3784
+ function _sliced_to_array$2(arr, i) {
3785
+ return _array_with_holes$2(arr) || _iterable_to_array_limit$2(arr, i) || _unsupported_iterable_to_array$4(arr, i) || _non_iterable_rest$2();
3786
+ }
3787
+ function _to_consumable_array$3(arr) {
3788
+ return _array_without_holes$3(arr) || _iterable_to_array$3(arr) || _unsupported_iterable_to_array$4(arr) || _non_iterable_spread$3();
3789
+ }
3790
+ function _unsupported_iterable_to_array$4(o, minLen) {
3791
+ if (!o) return;
3792
+ if (typeof o === "string") return _array_like_to_array$4(o, minLen);
3793
+ var n = Object.prototype.toString.call(o).slice(8, -1);
3794
+ if (n === "Object" && o.constructor) n = o.constructor.name;
3795
+ if (n === "Map" || n === "Set") return Array.from(n);
3796
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$4(o, minLen);
3797
+ }
3798
+ function _ts_generator$4(thisArg, body) {
3799
+ var f, y, t, _ = {
3800
+ label: 0,
3801
+ sent: function() {
3802
+ if (t[0] & 1) throw t[1];
3803
+ return t[1];
3804
+ },
3805
+ trys: [],
3806
+ ops: []
3807
+ }, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype), d = Object.defineProperty;
3808
+ return d(g, "next", {
3809
+ value: verb(0)
3810
+ }), d(g, "throw", {
3811
+ value: verb(1)
3812
+ }), d(g, "return", {
3813
+ value: verb(2)
3814
+ }), typeof Symbol === "function" && d(g, Symbol.iterator, {
3815
+ value: function() {
3816
+ return this;
3817
+ }
3818
+ }), g;
3819
+ function verb(n) {
3820
+ return function(v) {
3821
+ return step([
3822
+ n,
3823
+ v
3824
+ ]);
3825
+ };
3826
+ }
3827
+ function step(op) {
3828
+ if (f) throw new TypeError("Generator is already executing.");
3829
+ while(g && (g = 0, op[0] && (_ = 0)), _)try {
3830
+ 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;
3831
+ if (y = 0, t) op = [
3832
+ op[0] & 2,
3833
+ t.value
3834
+ ];
3835
+ switch(op[0]){
3836
+ case 0:
3837
+ case 1:
3838
+ t = op;
3839
+ break;
3840
+ case 4:
3841
+ _.label++;
3842
+ return {
3843
+ value: op[1],
3844
+ done: false
3845
+ };
3846
+ case 5:
3847
+ _.label++;
3848
+ y = op[1];
3849
+ op = [
3850
+ 0
3851
+ ];
3852
+ continue;
3853
+ case 7:
3854
+ op = _.ops.pop();
3855
+ _.trys.pop();
3856
+ continue;
3857
+ default:
3858
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
3859
+ _ = 0;
3860
+ continue;
3861
+ }
3862
+ if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) {
3863
+ _.label = op[1];
3864
+ break;
3865
+ }
3866
+ if (op[0] === 6 && _.label < t[1]) {
3867
+ _.label = t[1];
3868
+ t = op;
3869
+ break;
3870
+ }
3871
+ if (t && _.label < t[2]) {
3872
+ _.label = t[2];
3873
+ _.ops.push(op);
3874
+ break;
3875
+ }
3876
+ if (t[2]) _.ops.pop();
3877
+ _.trys.pop();
3878
+ continue;
3879
+ }
3880
+ op = body.call(thisArg, _);
3881
+ } catch (e) {
3882
+ op = [
3883
+ 6,
3884
+ e
3885
+ ];
3886
+ y = 0;
3887
+ } finally{
3888
+ f = t = 0;
3889
+ }
3890
+ if (op[0] & 5) throw op[1];
3891
+ return {
3892
+ value: op[0] ? op[1] : void 0,
3893
+ done: true
3894
+ };
3895
+ }
3896
+ }
3897
+ // MARK: Constants
3898
+ /**
3899
+ * Reserved tool name for the built-in `url-models` static tool.
3900
+ */ var URL_MODELS_TOOL_NAME = 'url-models';
3901
+ /**
3902
+ * Synthetic call type used in the tool's dispatch identity.
3903
+ */ var URL_MODELS_DISPATCH_CALL = 'url-models';
3904
+ /**
3905
+ * Synthetic model type used in the tool's dispatch identity. The tool isn't
3906
+ * bound to a real model (the `url` is its input), so we use the literal "route".
3907
+ */ var URL_MODELS_DISPATCH_MODEL_TYPE = 'route';
3908
+ /**
3909
+ * Placeholder segment standing for the caller's authenticated uid.
3910
+ */ var AUTH_UID_PLACEHOLDER = '{authUid}';
3911
+ // MARK: Factory
3912
+ /**
3913
+ * Builds the built-in `url-models` MCP tool definition.
3914
+ *
3915
+ * Matches a pasted app URL against the build-time route manifest and returns the
3916
+ * Firestore models the page renders — model types plus concrete keys with the
3917
+ * route params and `{authUid}` substituted. Optionally filtered to specific
3918
+ * `models`, reduced to keys-only, or loaded via the same permission-checked read
3919
+ * path as `model-get`.
3920
+ *
3921
+ * A URL that matches no state returns a structured `{ matched: null, candidates }`
3922
+ * (not an error) so the caller can suggest near-misses.
3923
+ *
3924
+ * @param deps - The route manifest plus the shared read-documents / identity callbacks.
3925
+ * @returns A statically-registered {@link McpToolDefinition}.
3926
+ */ function createUrlModelsTool(deps) {
3927
+ var handler = function handler(args, ctx) {
3928
+ return urlModelsToolHandler(args, ctx, deps);
3929
+ };
3930
+ var name = URL_MODELS_TOOL_NAME;
3931
+ 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`).';
3932
+ return {
3933
+ name: name,
3934
+ description: description,
3935
+ inputSchema: URL_MODELS_INPUT_SCHEMA,
3936
+ outputSchema: URL_MODELS_OUTPUT_SCHEMA,
3937
+ dispatch: {
3938
+ call: URL_MODELS_DISPATCH_CALL,
3939
+ modelType: URL_MODELS_DISPATCH_MODEL_TYPE
3940
+ },
3941
+ staticHandler: handler,
3942
+ filterMetadata: {
3943
+ visibilityKind: 'declarative',
3944
+ rule: {
3945
+ requireAuthenticated: true
3946
+ },
3947
+ effectiveReadOnly: true
3948
+ },
3949
+ staticWireEntry: buildStaticWireEntry({
3950
+ name: name,
3951
+ description: description,
3952
+ inputSchema: URL_MODELS_INPUT_SCHEMA,
3953
+ outputSchema: URL_MODELS_OUTPUT_SCHEMA
3954
+ })
3955
+ };
3956
+ }
3957
+ // MARK: Handler
3958
+ function urlModelsToolHandler(args, ctx, deps) {
3959
+ return _async_to_generator$4(function() {
3960
+ var result, input, payload, error;
3961
+ return _ts_generator$4(this, function(_state) {
3962
+ switch(_state.label){
3963
+ case 0:
3964
+ _state.trys.push([
3965
+ 0,
3966
+ 2,
3967
+ ,
3968
+ 3
3969
+ ]);
3970
+ input = parseUrlModelsInput(args);
3971
+ return [
3972
+ 4,
3973
+ resolveUrlModels(input, ctx, deps)
3974
+ ];
3975
+ case 1:
3976
+ payload = _state.sent();
3977
+ result = {
3978
+ content: [
3979
+ {
3980
+ type: 'text',
3981
+ text: JSON.stringify(payload)
3982
+ }
3983
+ ],
3984
+ structuredContent: payload
3985
+ };
3986
+ return [
3987
+ 3,
3988
+ 3
3989
+ ];
3990
+ case 2:
3991
+ error = _state.sent();
3992
+ result = formatMcpToolErrorResponse(error);
3993
+ return [
3994
+ 3,
3995
+ 3
3996
+ ];
3997
+ case 3:
3998
+ return [
3999
+ 2,
4000
+ result
4001
+ ];
4002
+ }
4003
+ });
4004
+ })();
4005
+ }
4006
+ function parseUrlModelsInput(args) {
4007
+ var url = args.url;
4008
+ if (typeof url !== 'string' || url.length === 0) {
4009
+ throw new Error('url-models: "url" is required and must be a non-empty string.');
4010
+ }
4011
+ var keysOnly = args.keysOnly === true;
4012
+ var load = args.load === true;
4013
+ if (keysOnly && load) {
4014
+ throw new Error('url-models: "keysOnly" and "load" cannot be combined — "keysOnly" returns keys without loading documents.');
4015
+ }
4016
+ var models;
4017
+ if (args.models !== undefined) {
4018
+ if (!Array.isArray(args.models) || args.models.some(function(m) {
4019
+ return typeof m !== 'string';
4020
+ })) {
4021
+ throw new Error('url-models: "models" must be an array of model-type strings.');
4022
+ }
4023
+ models = args.models;
4024
+ }
4025
+ var currentUserUid;
4026
+ if (args.currentUserUid !== undefined) {
4027
+ if (typeof args.currentUserUid !== 'string' || args.currentUserUid.length === 0) {
4028
+ throw new Error('url-models: "currentUserUid" must be a non-empty string.');
4029
+ }
4030
+ currentUserUid = args.currentUserUid;
4031
+ }
4032
+ return _object_spread$1(_object_spread_props(_object_spread$1({
4033
+ url: url
4034
+ }, models === undefined ? {} : {
4035
+ models: models
4036
+ }), {
4037
+ keysOnly: keysOnly,
4038
+ load: load
4039
+ }), currentUserUid === undefined ? {} : {
4040
+ currentUserUid: currentUserUid
4041
+ });
4042
+ }
4043
+ function resolveUrlModels(input, ctx, deps) {
4044
+ return _async_to_generator$4(function() {
4045
+ var matchResult, payload, _tmp;
4046
+ return _ts_generator$4(this, function(_state) {
4047
+ switch(_state.label){
4048
+ case 0:
4049
+ matchResult = matchRouteManifestUrl({
4050
+ manifest: deps.routeManifest,
4051
+ url: input.url
4052
+ });
4053
+ if (!(matchResult.kind === 'none')) return [
4054
+ 3,
4055
+ 1
4056
+ ];
4057
+ payload = {
4058
+ url: input.url,
4059
+ pathname: matchResult.pathname,
4060
+ matched: null,
4061
+ candidates: matchResult.candidates.map(stateSummary)
4062
+ };
4063
+ return [
4064
+ 3,
4065
+ 4
4066
+ ];
4067
+ case 1:
4068
+ if (!(matchResult.kind === 'ambiguous')) return [
4069
+ 3,
4070
+ 2
4071
+ ];
4072
+ payload = {
4073
+ url: input.url,
4074
+ pathname: matchResult.pathname,
4075
+ matched: null,
4076
+ ambiguous: matchResult.states.map(stateSummary)
4077
+ };
4078
+ return [
4079
+ 3,
4080
+ 4
4081
+ ];
4082
+ case 2:
4083
+ _tmp = {
4084
+ url: input.url,
4085
+ pathname: matchResult.pathname
4086
+ };
4087
+ return [
4088
+ 4,
4089
+ buildMatchedPayload({
4090
+ match: matchResult,
4091
+ input: input,
4092
+ ctx: ctx,
4093
+ deps: deps
4094
+ })
4095
+ ];
4096
+ case 3:
4097
+ payload = (_tmp.matched = _state.sent(), _tmp);
4098
+ _state.label = 4;
4099
+ case 4:
4100
+ return [
4101
+ 2,
4102
+ payload
4103
+ ];
4104
+ }
4105
+ });
4106
+ })();
4107
+ }
4108
+ function stateSummary(state) {
4109
+ return {
4110
+ name: state.name,
4111
+ fullUrl: state.fullUrl
4112
+ };
4113
+ }
4114
+ function buildMatchedPayload(args) {
4115
+ return _async_to_generator$4(function() {
4116
+ var _input_currentUserUid, _ctx_auth, match, input, ctx, deps, uid, filtered, resolved, loaded, _tmp;
4117
+ return _ts_generator$4(this, function(_state) {
4118
+ switch(_state.label){
4119
+ case 0:
4120
+ match = args.match, input = args.input, ctx = args.ctx, deps = args.deps;
4121
+ // `currentUserUid` (when supplied) overrides the uid used to fill `{authUid}` placeholders so a
4122
+ // caller can preview another user's page-models. The document-load path still uses `ctx.auth`, so
4123
+ // this is a key-substitution preview only — no privilege escalation on the actual reads.
4124
+ 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;
4125
+ filtered = input.models === undefined ? match.state.models : match.state.models.filter(function(m) {
4126
+ var _input_models;
4127
+ return (_input_models = input.models) === null || _input_models === void 0 ? void 0 : _input_models.includes(m.modelType);
4128
+ });
4129
+ resolved = filtered.map(function(entry) {
4130
+ return resolveModelEntry({
4131
+ entry: entry,
4132
+ params: match.params,
4133
+ uid: uid,
4134
+ keysOnly: input.keysOnly === true,
4135
+ deps: deps,
4136
+ ctx: ctx
4137
+ });
4138
+ });
4139
+ if (!(input.load === true)) return [
4140
+ 3,
4141
+ 2
4142
+ ];
4143
+ return [
4144
+ 4,
4145
+ loadResolvedModels(resolved, ctx, deps)
4146
+ ];
4147
+ case 1:
4148
+ _tmp = _state.sent();
4149
+ return [
4150
+ 3,
4151
+ 3
4152
+ ];
4153
+ case 2:
4154
+ _tmp = undefined;
4155
+ _state.label = 3;
4156
+ case 3:
4157
+ loaded = _tmp;
4158
+ return [
4159
+ 2,
4160
+ _object_spread$1({
4161
+ state: {
4162
+ name: match.state.name,
4163
+ fullUrl: match.state.fullUrl,
4164
+ component: match.state.component,
4165
+ componentFile: match.state.componentFile
4166
+ },
4167
+ via: match.via,
4168
+ params: match.params,
4169
+ models: resolved
4170
+ }, loaded === undefined ? {} : {
4171
+ loaded: loaded
4172
+ })
4173
+ ];
4174
+ }
4175
+ });
4176
+ })();
4177
+ }
4178
+ function resolveModelEntry(input) {
4179
+ var entry = input.entry, keysOnly = input.keysOnly;
4180
+ var base = keysOnly ? {
4181
+ modelType: entry.modelType,
4182
+ kind: entry.kind
4183
+ } : _object_spread$1({
4184
+ modelType: entry.modelType,
4185
+ kind: entry.kind
4186
+ }, entry.keyTemplate === undefined ? {} : {
4187
+ keyTemplate: entry.keyTemplate
4188
+ }, entry.description === undefined ? {} : {
4189
+ description: entry.description
4190
+ }, entry.from === undefined ? {} : {
4191
+ from: entry.from
4192
+ });
4193
+ var result;
4194
+ if (entry.kind === 'list') {
4195
+ result = base;
4196
+ } else if (entry.kind === 'id') {
4197
+ result = _object_spread$1({}, base, resolveIdKey(input));
4198
+ } else {
4199
+ result = _object_spread$1({}, base, resolveFullKey(input));
4200
+ }
4201
+ return result;
4202
+ }
4203
+ function resolveIdKey(input) {
4204
+ var _entry_keyTemplate;
4205
+ var entry = input.entry, params = input.params, uid = input.uid, deps = input.deps, ctx = input.ctx;
4206
+ var value = substitutePlaceholder((_entry_keyTemplate = entry.keyTemplate) !== null && _entry_keyTemplate !== void 0 ? _entry_keyTemplate : '', params, uid);
4207
+ var result;
4208
+ if (value.kind === 'unresolved') {
4209
+ result = {
4210
+ unresolved: value.unresolved
4211
+ };
4212
+ } else {
4213
+ var identity = deps.resolveIdentity(entry.modelType, ctx.auth);
4214
+ if (identity == null) {
4215
+ result = {
4216
+ unresolved: {
4217
+ reason: 'unknown-model-type',
4218
+ message: "Unknown model type `".concat(entry.modelType, "`.")
4219
+ }
4220
+ };
4221
+ } else if (identity.type === 'root') {
4222
+ result = {
4223
+ key: "".concat(identity.collectionName, "/").concat(value.value)
4224
+ };
4225
+ } else {
4226
+ result = {
4227
+ unresolved: {
4228
+ reason: 'subcollection-requires-key-template',
4229
+ 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.")
4230
+ }
4231
+ };
4232
+ }
4233
+ }
4234
+ return result;
4235
+ }
4236
+ function resolveFullKey(input) {
4237
+ var _entry_keyTemplate;
4238
+ var entry = input.entry, params = input.params, uid = input.uid;
4239
+ var segments = ((_entry_keyTemplate = entry.keyTemplate) !== null && _entry_keyTemplate !== void 0 ? _entry_keyTemplate : '').split('/');
4240
+ var parts = [];
4241
+ var unresolved;
4242
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
4243
+ try {
4244
+ for(var _iterator = segments[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
4245
+ var segment = _step.value;
4246
+ if (segment.startsWith(':') || segment === AUTH_UID_PLACEHOLDER) {
4247
+ var value = substitutePlaceholder(segment, params, uid);
4248
+ if (value.kind === 'unresolved') {
4249
+ unresolved = value.unresolved;
4250
+ break;
4251
+ }
4252
+ parts.push(value.value);
4253
+ } else {
4254
+ parts.push(segment);
4255
+ }
4256
+ }
4257
+ } catch (err) {
4258
+ _didIteratorError = true;
4259
+ _iteratorError = err;
4260
+ } finally{
4261
+ try {
4262
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
4263
+ _iterator.return();
4264
+ }
4265
+ } finally{
4266
+ if (_didIteratorError) {
4267
+ throw _iteratorError;
4268
+ }
4269
+ }
4270
+ }
4271
+ return unresolved === undefined ? {
4272
+ key: parts.join('/')
4273
+ } : {
4274
+ unresolved: unresolved
4275
+ };
4276
+ }
4277
+ function substitutePlaceholder(segment, params, uid) {
4278
+ var result;
4279
+ if (segment === AUTH_UID_PLACEHOLDER) {
4280
+ result = uid == null || uid.length === 0 ? {
4281
+ kind: 'unresolved',
4282
+ unresolved: {
4283
+ reason: 'auth-required',
4284
+ message: 'This page keys a model by the caller uid (`{authUid}`) but the request is not authenticated.'
4285
+ }
4286
+ } : {
4287
+ kind: 'value',
4288
+ value: uid
4289
+ };
4290
+ } else if (segment.startsWith(':')) {
4291
+ var paramName = segment.slice(1);
4292
+ var value = params[paramName];
4293
+ result = value === undefined ? {
4294
+ kind: 'unresolved',
4295
+ unresolved: {
4296
+ reason: 'missing-param',
4297
+ message: "Route param `:".concat(paramName, "` was not captured from the URL.")
4298
+ }
4299
+ } : {
4300
+ kind: 'value',
4301
+ value: value
4302
+ };
4303
+ } else {
4304
+ result = {
4305
+ kind: 'value',
4306
+ value: segment
4307
+ };
4308
+ }
4309
+ return result;
4310
+ }
4311
+ // MARK: Load
4312
+ function loadResolvedModels(resolved, ctx, deps) {
4313
+ return _async_to_generator$4(function() {
4314
+ var keysByType, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, model, existing, out, _iteratorNormalCompletion1, _didIteratorError1, _iteratorError1, _iterator1, _step1, _step_value, modelType, keys, merged, err;
4315
+ return _ts_generator$4(this, function(_state) {
4316
+ switch(_state.label){
4317
+ case 0:
4318
+ keysByType = new Map();
4319
+ _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
4320
+ try {
4321
+ for(_iterator = resolved[Symbol.iterator](); !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
4322
+ model = _step.value;
4323
+ if (model.key !== undefined) {
4324
+ existing = keysByType.get(model.modelType);
4325
+ if (existing === undefined) {
4326
+ keysByType.set(model.modelType, [
4327
+ model.key
4328
+ ]);
4329
+ } else {
4330
+ existing.push(model.key);
4331
+ }
4332
+ }
4333
+ }
4334
+ } catch (err) {
4335
+ _didIteratorError = true;
4336
+ _iteratorError = err;
4337
+ } finally{
4338
+ try {
4339
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
4340
+ _iterator.return();
4341
+ }
4342
+ } finally{
4343
+ if (_didIteratorError) {
4344
+ throw _iteratorError;
4345
+ }
4346
+ }
4347
+ }
4348
+ out = [];
4349
+ _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
4350
+ _state.label = 1;
4351
+ case 1:
4352
+ _state.trys.push([
4353
+ 1,
4354
+ 6,
4355
+ 7,
4356
+ 8
4357
+ ]);
4358
+ _iterator1 = keysByType[Symbol.iterator]();
4359
+ _state.label = 2;
4360
+ case 2:
4361
+ if (!!(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done)) return [
4362
+ 3,
4363
+ 5
4364
+ ];
4365
+ _step_value = _sliced_to_array$2(_step1.value, 2), modelType = _step_value[0], keys = _step_value[1];
4366
+ return [
4367
+ 4,
4368
+ readInChunks({
4369
+ modelType: modelType,
4370
+ keys: keys,
4371
+ auth: ctx.auth,
4372
+ readDocuments: deps.readDocuments
4373
+ })
4374
+ ];
4375
+ case 3:
4376
+ merged = _state.sent();
4377
+ out.push({
4378
+ modelType: modelType,
4379
+ results: merged.results,
4380
+ errors: merged.errors
4381
+ });
4382
+ _state.label = 4;
4383
+ case 4:
4384
+ _iteratorNormalCompletion1 = true;
4385
+ return [
4386
+ 3,
4387
+ 2
4388
+ ];
4389
+ case 5:
4390
+ return [
4391
+ 3,
4392
+ 8
4393
+ ];
4394
+ case 6:
4395
+ err = _state.sent();
4396
+ _didIteratorError1 = true;
4397
+ _iteratorError1 = err;
4398
+ return [
4399
+ 3,
4400
+ 8
4401
+ ];
4402
+ case 7:
4403
+ try {
4404
+ if (!_iteratorNormalCompletion1 && _iterator1.return != null) {
4405
+ _iterator1.return();
4406
+ }
4407
+ } finally{
4408
+ if (_didIteratorError1) {
4409
+ throw _iteratorError1;
4410
+ }
4411
+ }
4412
+ return [
4413
+ 7
4414
+ ];
4415
+ case 8:
4416
+ return [
4417
+ 2,
4418
+ out
4419
+ ];
4420
+ }
4421
+ });
4422
+ })();
2857
4423
  }
2858
- function appendUnknownClaimLines(lines, unknownClaimKeys) {
2859
- if (unknownClaimKeys.length === 0) {
2860
- return;
2861
- }
2862
- lines.push('', '## Unknown claim keys');
2863
- var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
2864
- try {
2865
- for(var _iterator = unknownClaimKeys[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
2866
- var key = _step.value;
2867
- lines.push("- `".concat(key, "`"));
2868
- }
2869
- } catch (err) {
2870
- _didIteratorError = true;
2871
- _iteratorError = err;
2872
- } finally{
2873
- try {
2874
- if (!_iteratorNormalCompletion && _iterator.return != null) {
2875
- _iterator.return();
2876
- }
2877
- } finally{
2878
- if (_didIteratorError) {
2879
- throw _iteratorError;
4424
+ function readInChunks(input) {
4425
+ return _async_to_generator$4(function() {
4426
+ var modelType, keys, auth, readDocuments, merged, offset, _merged_results, _merged_errors, batch, partial;
4427
+ return _ts_generator$4(this, function(_state) {
4428
+ switch(_state.label){
4429
+ case 0:
4430
+ modelType = input.modelType, keys = input.keys, auth = input.auth, readDocuments = input.readDocuments;
4431
+ merged = {
4432
+ results: [],
4433
+ errors: []
4434
+ };
4435
+ offset = 0;
4436
+ _state.label = 1;
4437
+ case 1:
4438
+ if (!(offset < keys.length)) return [
4439
+ 3,
4440
+ 4
4441
+ ];
4442
+ batch = keys.slice(offset, offset + MCP_MODEL_GET_BATCH_SIZE);
4443
+ return [
4444
+ 4,
4445
+ readDocuments(modelType, batch, auth)
4446
+ ];
4447
+ case 2:
4448
+ partial = _state.sent();
4449
+ (_merged_results = merged.results).push.apply(_merged_results, _to_consumable_array$3(partial.results));
4450
+ (_merged_errors = merged.errors).push.apply(_merged_errors, _to_consumable_array$3(partial.errors));
4451
+ _state.label = 3;
4452
+ case 3:
4453
+ offset += MCP_MODEL_GET_BATCH_SIZE;
4454
+ return [
4455
+ 3,
4456
+ 1
4457
+ ];
4458
+ case 4:
4459
+ return [
4460
+ 2,
4461
+ merged
4462
+ ];
2880
4463
  }
2881
- }
2882
- }
4464
+ });
4465
+ })();
2883
4466
  }
2884
4467
  // MARK: Schemas
2885
- var WHOAMI_INPUT_SCHEMA = {
2886
- type: 'object',
2887
- properties: {},
2888
- additionalProperties: false
2889
- };
2890
- var WHOAMI_OUTPUT_SCHEMA = {
4468
+ var URL_MODELS_INPUT_SCHEMA = {
2891
4469
  type: 'object',
2892
4470
  required: [
2893
- 'authenticated',
2894
- 'claims',
2895
- 'roles',
2896
- 'claimDetails',
2897
- 'unknownClaimKeys'
4471
+ 'url'
2898
4472
  ],
2899
- description: 'Caller identity, raw token claims, decoded role set, and per-claim descriptions for every key present on the live token.',
2900
4473
  properties: {
2901
- authenticated: {
2902
- type: 'boolean'
4474
+ url: {
4475
+ type: 'string',
4476
+ minLength: 1,
4477
+ description: 'Full URL or pathname, e.g. "https://app.example.co/worker/abc/timesheets/list".'
2903
4478
  },
2904
- uid: {
2905
- type: 'string'
4479
+ models: {
4480
+ type: 'array',
4481
+ items: {
4482
+ type: 'string'
4483
+ },
4484
+ description: 'Restrict the output to these model types.'
2906
4485
  },
2907
- email: {
2908
- type: 'string'
4486
+ keysOnly: {
4487
+ type: 'boolean',
4488
+ description: 'Return only the resolved keys (no descriptions or documents). Cannot combine with "load".'
2909
4489
  },
2910
- emailVerified: {
2911
- type: 'boolean'
4490
+ load: {
4491
+ type: 'boolean',
4492
+ description: 'Load each resolved document via the permission-checked read path. Cannot combine with "keysOnly".'
2912
4493
  },
2913
- app: {
4494
+ currentUserUid: {
4495
+ type: 'string',
4496
+ minLength: 1,
4497
+ 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."
4498
+ }
4499
+ },
4500
+ additionalProperties: false
4501
+ };
4502
+ var URL_MODELS_OUTPUT_SCHEMA = {
4503
+ type: 'object',
4504
+ required: [
4505
+ 'url',
4506
+ 'pathname',
4507
+ 'matched'
4508
+ ],
4509
+ properties: {
4510
+ url: {
2914
4511
  type: 'string'
2915
4512
  },
2916
- claims: {
2917
- type: 'object',
2918
- additionalProperties: true
4513
+ pathname: {
4514
+ type: 'string'
2919
4515
  },
2920
- roles: {
2921
- type: 'array',
2922
- items: {
2923
- type: 'string'
4516
+ matched: {
4517
+ type: [
4518
+ 'object',
4519
+ 'null'
4520
+ ],
4521
+ description: 'The matched page and its models, or null when no state matched.',
4522
+ properties: {
4523
+ state: {
4524
+ type: 'object'
4525
+ },
4526
+ via: {
4527
+ type: 'string'
4528
+ },
4529
+ params: {
4530
+ type: 'object'
4531
+ },
4532
+ models: {
4533
+ type: 'array',
4534
+ items: {
4535
+ type: 'object'
4536
+ }
4537
+ },
4538
+ loaded: {
4539
+ type: 'array',
4540
+ items: {
4541
+ type: 'object'
4542
+ }
4543
+ }
2924
4544
  }
2925
4545
  },
2926
- claimDetails: {
4546
+ ambiguous: {
2927
4547
  type: 'array',
2928
4548
  items: {
2929
- type: 'object',
2930
- required: [
2931
- 'key',
2932
- 'description',
2933
- 'grantedRoles',
2934
- 'inverse',
2935
- 'tags'
2936
- ],
2937
- properties: {
2938
- key: {
2939
- type: 'string'
2940
- },
2941
- interfaceName: {
2942
- type: 'string'
2943
- },
2944
- description: {
2945
- type: 'string'
2946
- },
2947
- grantedRoles: {
2948
- type: 'array',
2949
- items: {
2950
- type: 'string'
2951
- }
2952
- },
2953
- inverse: {
2954
- type: 'boolean'
2955
- },
2956
- tags: {
2957
- type: 'array',
2958
- items: {
2959
- type: 'string'
2960
- }
2961
- }
2962
- }
2963
- }
4549
+ type: 'object'
4550
+ },
4551
+ description: 'Competing states when the URL matched more than one.'
2964
4552
  },
2965
- unknownClaimKeys: {
4553
+ candidates: {
2966
4554
  type: 'array',
2967
4555
  items: {
2968
- type: 'string'
2969
- }
4556
+ type: 'object'
4557
+ },
4558
+ description: 'Closest near-miss states when nothing matched.'
2970
4559
  }
2971
4560
  }
2972
4561
  };
@@ -3046,7 +4635,7 @@ function _non_iterable_rest$1() {
3046
4635
  function _sliced_to_array$1(arr, i) {
3047
4636
  return _array_with_holes$1(arr) || _iterable_to_array_limit$1(arr, i) || _unsupported_iterable_to_array$3(arr, i) || _non_iterable_rest$1();
3048
4637
  }
3049
- function _type_of$1(obj) {
4638
+ function _type_of$2(obj) {
3050
4639
  "@swc/helpers - typeof";
3051
4640
  return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
3052
4641
  }
@@ -3440,7 +5029,7 @@ function parseJsonArrayOperations(fileText) {
3440
5029
  return parsed;
3441
5030
  }
3442
5031
  function validateOperation(value, index) {
3443
- if (value == null || (typeof value === "undefined" ? "undefined" : _type_of$1(value)) !== 'object') {
5032
+ if (value == null || (typeof value === "undefined" ? "undefined" : _type_of$2(value)) !== 'object') {
3444
5033
  throw new Error("batch-execute: operation[".concat(index, "] must be an object."));
3445
5034
  }
3446
5035
  var candidate = value;
@@ -3932,12 +5521,31 @@ function _non_iterable_rest() {
3932
5521
  function _non_iterable_spread$2() {
3933
5522
  throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
3934
5523
  }
5524
+ function _object_spread(target) {
5525
+ for(var i = 1; i < arguments.length; i++){
5526
+ var source = arguments[i] != null ? arguments[i] : {};
5527
+ var ownKeys = Object.keys(source);
5528
+ if (typeof Object.getOwnPropertySymbols === "function") {
5529
+ ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
5530
+ return Object.getOwnPropertyDescriptor(source, sym).enumerable;
5531
+ }));
5532
+ }
5533
+ ownKeys.forEach(function(key) {
5534
+ _define_property$3(target, key, source[key]);
5535
+ });
5536
+ }
5537
+ return target;
5538
+ }
3935
5539
  function _sliced_to_array(arr, i) {
3936
5540
  return _array_with_holes(arr) || _iterable_to_array_limit(arr, i) || _unsupported_iterable_to_array$2(arr, i) || _non_iterable_rest();
3937
5541
  }
3938
5542
  function _to_consumable_array$2(arr) {
3939
5543
  return _array_without_holes$2(arr) || _iterable_to_array$2(arr) || _unsupported_iterable_to_array$2(arr) || _non_iterable_spread$2();
3940
5544
  }
5545
+ function _type_of$1(obj) {
5546
+ "@swc/helpers - typeof";
5547
+ return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
5548
+ }
3941
5549
  function _unsupported_iterable_to_array$2(o, minLen) {
3942
5550
  if (!o) return;
3943
5551
  if (typeof o === "string") return _array_like_to_array$2(o, minLen);
@@ -4066,7 +5674,10 @@ var McpServerFactoryService_1;
4066
5674
  _define_property$3(this, "_cachedStaticTools", void 0);
4067
5675
  _define_property$3(this, "_cachedManifest", void 0);
4068
5676
  _define_property$3(this, "_cachedManifestModels", void 0);
5677
+ _define_property$3(this, "_cachedManifestEnums", void 0);
4069
5678
  _define_property$3(this, "_cachedManifestAuth", void 0);
5679
+ _define_property$3(this, "_cachedRouteManifest", void 0);
5680
+ _define_property$3(this, "_routeManifestLoaded", false);
4070
5681
  _define_property$3(this, "_manifestLoaded", false);
4071
5682
  _define_property$3(this, "_loggedSkips", false);
4072
5683
  _define_property$3(this, "_warnedMissingRoleReader", false);
@@ -4341,15 +5952,39 @@ var McpServerFactoryService_1;
4341
5952
  return getService.getModelIdentity(modelType, auth);
4342
5953
  }
4343
5954
  }));
5955
+ // `url-models` reuses the model-get read/identity path, so it is only offered when the get
5956
+ // service is wired AND a route manifest with at least one state was loaded.
5957
+ var routeManifest = this._resolveRouteManifest();
5958
+ if (routeManifest != null && routeManifest.states.length > 0) {
5959
+ staticTools.push(createUrlModelsTool({
5960
+ routeManifest: routeManifest,
5961
+ readDocuments: function readDocuments(modelType, keys, auth) {
5962
+ return getService.readDocuments(modelType, keys, auth);
5963
+ },
5964
+ resolveIdentity: function resolveIdentity(modelType, auth) {
5965
+ return getService.getModelIdentity(modelType, auth);
5966
+ }
5967
+ }));
5968
+ }
4344
5969
  }
4345
5970
  var modelManifest = this._cachedManifestModels;
5971
+ var enumManifest = this._cachedManifestEnums;
4346
5972
  if (modelManifest != null && modelManifest.length > 0) {
4347
- staticTools.push(createModelInfoTool({
5973
+ staticTools.push(createModelInfoTool(_object_spread({
4348
5974
  manifest: modelManifest
4349
- }), createModelDecodeTool({
5975
+ }, enumManifest == null ? {} : {
5976
+ enums: enumManifest
5977
+ })), createModelDecodeTool({
4350
5978
  manifest: modelManifest
4351
5979
  }));
4352
5980
  }
5981
+ // `enum-info` only needs the enum value tables — register it whenever the manifest carries a
5982
+ // non-empty `enums` block, independent of the model catalog.
5983
+ if (enumManifest != null && Object.keys(enumManifest).length > 0) {
5984
+ staticTools.push(createEnumInfoTool({
5985
+ enums: enumManifest
5986
+ }));
5987
+ }
4353
5988
  var authManifest = this._cachedManifestAuth;
4354
5989
  if (authManifest != null) {
4355
5990
  staticTools.push(createWhoamiTool({
@@ -4511,9 +6146,55 @@ var McpServerFactoryService_1;
4511
6146
  value: function _resetManifestCache() {
4512
6147
  this._cachedManifest = undefined;
4513
6148
  this._cachedManifestModels = undefined;
6149
+ this._cachedManifestEnums = undefined;
4514
6150
  this._cachedManifestAuth = undefined;
4515
6151
  }
4516
6152
  },
6153
+ {
6154
+ /**
6155
+ * Reads the pre-rendered route manifest JSON once, validates its version, and caches the result
6156
+ * for the process lifetime. Drives whether the built-in `url-models` static tool is registered.
6157
+ *
6158
+ * Missing file or wrong version fall back to "no route manifest" with a single boot warning;
6159
+ * the `url-models` tool is then simply not offered.
6160
+ *
6161
+ * @returns The cached route manifest, or `undefined` when none was loaded.
6162
+ */ key: "_resolveRouteManifest",
6163
+ value: function _resolveRouteManifest() {
6164
+ if (!this._routeManifestLoaded) {
6165
+ this._routeManifestLoaded = true;
6166
+ var path = this.mcpConfig.mcpRouteManifestPath;
6167
+ if (path == null) {
6168
+ this._cachedRouteManifest = undefined;
6169
+ } else if (existsSync(path)) {
6170
+ this._parseRouteManifestFile(path);
6171
+ } else {
6172
+ this._logger.warn("MCP route manifest path is set but the file is missing: ".concat(path, ". The url-models tool will not be registered."));
6173
+ this._cachedRouteManifest = undefined;
6174
+ }
6175
+ }
6176
+ return this._cachedRouteManifest;
6177
+ }
6178
+ },
6179
+ {
6180
+ key: "_parseRouteManifestFile",
6181
+ value: function _parseRouteManifestFile(path) {
6182
+ try {
6183
+ var raw = readFileSync(path, 'utf8');
6184
+ var parsed = JSON.parse(raw);
6185
+ if (parsed.version === ROUTE_MANIFEST_VERSION) {
6186
+ this._cachedRouteManifest = parsed;
6187
+ this._logger.log("Loaded MCP route manifest from ".concat(path, ": ").concat(parsed.states.length, " states."));
6188
+ } else {
6189
+ 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."));
6190
+ this._cachedRouteManifest = undefined;
6191
+ }
6192
+ } catch (error) {
6193
+ this._logger.warn("Failed to read MCP route manifest at ".concat(path, ": ").concat(error.message, ". The url-models tool will not be registered."));
6194
+ this._cachedRouteManifest = undefined;
6195
+ }
6196
+ }
6197
+ },
4517
6198
  {
4518
6199
  key: "_parseManifestFile",
4519
6200
  value: function _parseManifestFile(path) {
@@ -4560,11 +6241,14 @@ var McpServerFactoryService_1;
4560
6241
  }
4561
6242
  var models = Array.isArray(parsed.models) && parsed.models.length > 0 ? parsed.models : undefined;
4562
6243
  var modelSuffix = models == null ? '' : ", ".concat(models.length, " model entries");
6244
+ var enums = parsed.enums != null && _type_of$1(parsed.enums) === 'object' && Object.keys(parsed.enums).length > 0 ? parsed.enums : undefined;
6245
+ var enumSuffix = enums == null ? '' : ", ".concat(Object.keys(enums).length, " enum entries");
4563
6246
  var auth = parsed.auth != null && Array.isArray(parsed.auth.claims) ? parsed.auth : undefined;
4564
6247
  var authSuffix = auth == null ? '' : ", ".concat(auth.claims.length, " auth claim entries");
4565
- this._logger.log("Loaded MCP manifest from ".concat(path, ": ").concat(map.size, " tool entries").concat(modelSuffix).concat(authSuffix, "."));
6248
+ this._logger.log("Loaded MCP manifest from ".concat(path, ": ").concat(map.size, " tool entries").concat(modelSuffix).concat(enumSuffix).concat(authSuffix, "."));
4566
6249
  this._cachedManifest = map;
4567
6250
  this._cachedManifestModels = models;
6251
+ this._cachedManifestEnums = enums;
4568
6252
  this._cachedManifestAuth = auth;
4569
6253
  }
4570
6254
  },
@@ -4748,6 +6432,9 @@ var McpServerFactoryService_1;
4748
6432
  if (tool.outputSchema != null) {
4749
6433
  wire.outputSchema = tool.outputSchema;
4750
6434
  }
6435
+ if (tool.annotations != null) {
6436
+ wire.annotations = tool.annotations;
6437
+ }
4751
6438
  result = wire;
4752
6439
  }
4753
6440
  return result;
@@ -5766,4 +7453,4 @@ function _unsupported_iterable_to_array(o, minLen) {
5766
7453
  };
5767
7454
  }
5768
7455
 
5769
- export { BATCH_EXECUTE_DISPATCH_CALL, BATCH_EXECUTE_DISPATCH_MODEL_TYPE, BATCH_EXECUTE_TOOL_NAME, DEFAULT_BATCH_EXECUTE_MAX_PARALLEL, DEFAULT_JSON_SCHEMA_GENERATION_OPTIONS, DEFAULT_MCP_ANALYTICS_EVENT_NAME, DEFAULT_MCP_PATH, DEFAULT_MCP_SERVER_INSTRUCTIONS, DEFAULT_MCP_SERVER_NAME, DEFAULT_SPECIFIER_KEY, DEFAULT_VOID_MCP_SUCCESS_VALUE, FIREBASE_SERVER_MCP_ANALYTICS_CONFIG, FIREBASE_SERVER_MCP_ROUTES_FOR_GLOBAL_ROUTE_EXCLUDE, FirebaseServerMcpAnalyticsService, MAX_BATCH_EXECUTE_FILE_BYTES, MAX_BATCH_EXECUTE_OPERATIONS, MCP_ANALYTICS_SERVICE, MCP_AUTH_ROLE_READER, MCP_CALL_TYPE_ABBREVIATIONS, MCP_MANIFEST_VERSION, MCP_MODEL_GET_BATCH_SIZE, MCP_TOOL_NAME_MAX_LENGTH, MCP_TOOL_NAME_WARN_LENGTH, MODEL_DECODE_DISPATCH_CALL, MODEL_DECODE_DISPATCH_MODEL_TYPE, MODEL_DECODE_TOOL_NAME, MODEL_GET_DISPATCH_CALL, MODEL_GET_DISPATCH_MODEL_TYPE, MODEL_GET_TOOL_NAME, MODEL_INFO_DISPATCH_CALL, MODEL_INFO_DISPATCH_MODEL_TYPE, MODEL_INFO_GROUPS_HINT, MODEL_INFO_TOOL_NAME, MODEL_INFO_UNGROUPED_LABEL, McpAnalyticsService, McpController, McpModuleConfig, McpServerFactoryService, McpWellKnownController, abbreviateMcpCallType, appMcpAnalyticsModuleMetadata, batchOperationCoordKey, buildDefaultMcpToolDescription, buildDisambiguatedMcpToolName, buildMcpToolName, buildStaticWireEntry, classifyVisibility, createBatchExecuteTool, createModelDecodeTool, createModelGetTool, createModelInfoTool, decodeFirestoreModelKey, findModelEntry, formatMcpToolErrorResponse, formatMcpToolResponse, generateMcpToolDefinitions, handleStreamableHttpMcpRequest, mcpManifestKey, mcpModuleMetadata, noopMcpAnalyticsService, resolveEffectiveReadOnly, resolveRequiredScope, validateMcpToolName };
7456
+ export { BATCH_EXECUTE_DISPATCH_CALL, BATCH_EXECUTE_DISPATCH_MODEL_TYPE, BATCH_EXECUTE_TOOL_NAME, DEFAULT_BATCH_EXECUTE_MAX_PARALLEL, DEFAULT_JSON_SCHEMA_GENERATION_OPTIONS, DEFAULT_MCP_ANALYTICS_EVENT_NAME, DEFAULT_MCP_PATH, DEFAULT_MCP_SERVER_INSTRUCTIONS, DEFAULT_MCP_SERVER_NAME, DEFAULT_SPECIFIER_KEY, DEFAULT_VOID_MCP_SUCCESS_VALUE, ENUM_INFO_DISPATCH_CALL, ENUM_INFO_DISPATCH_MODEL_TYPE, ENUM_INFO_TOOL_NAME, ENUM_TABLE_SCHEMA, FIREBASE_SERVER_MCP_ANALYTICS_CONFIG, FIREBASE_SERVER_MCP_ROUTES_FOR_GLOBAL_ROUTE_EXCLUDE, FirebaseServerMcpAnalyticsService, MAX_BATCH_EXECUTE_FILE_BYTES, MAX_BATCH_EXECUTE_OPERATIONS, MCP_ANALYTICS_SERVICE, MCP_AUTH_ROLE_READER, MCP_CALL_TYPE_ABBREVIATIONS, MCP_MANIFEST_VERSION, MCP_MODEL_GET_BATCH_SIZE, MCP_TOOL_NAME_MAX_LENGTH, MCP_TOOL_NAME_WARN_LENGTH, MCP_WRITE_TOOL_DESCRIPTION_PREFIX, MODEL_DECODE_DISPATCH_CALL, MODEL_DECODE_DISPATCH_MODEL_TYPE, MODEL_DECODE_TOOL_NAME, MODEL_GET_DISPATCH_CALL, MODEL_GET_DISPATCH_MODEL_TYPE, MODEL_GET_TOOL_NAME, MODEL_INFO_DISPATCH_CALL, MODEL_INFO_DISPATCH_MODEL_TYPE, MODEL_INFO_GROUPS_HINT, MODEL_INFO_TOOL_NAME, MODEL_INFO_UNGROUPED_LABEL, McpAnalyticsService, McpController, McpModuleConfig, McpServerFactoryService, McpWellKnownController, ROUTE_MANIFEST_VERSION, URL_MODELS_DISPATCH_CALL, URL_MODELS_DISPATCH_MODEL_TYPE, URL_MODELS_TOOL_NAME, abbreviateMcpCallType, appMcpAnalyticsModuleMetadata, applyWriteMarker, batchOperationCoordKey, buildDefaultMcpToolDescription, buildDisambiguatedMcpToolName, buildMcpToolName, buildStaticWireEntry, classifyVisibility, createBatchExecuteTool, createEnumInfoTool, createModelDecodeTool, createModelGetTool, createModelInfoTool, createUrlModelsTool, decodeFirestoreModelKey, findModelEntry, formatMcpToolErrorResponse, formatMcpToolResponse, generateMcpToolDefinitions, handleStreamableHttpMcpRequest, matchRouteManifestUrl, mcpManifestKey, mcpModuleMetadata, noopMcpAnalyticsService, parseUrlModelsPathname, resolveEffectiveReadOnly, resolveEnumInfoOutput, resolveMcpToolAnnotations, resolveRequiredScope, validateMcpToolName };