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