@dereekb/firebase-server 13.15.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/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@dereekb/firebase-server/mcp",
3
- "version": "13.15.0",
3
+ "version": "13.17.0",
4
4
  "peerDependencies": {
5
- "@dereekb/analytics": "13.15.0",
6
- "@dereekb/date": "13.15.0",
7
- "@dereekb/firebase": "13.15.0",
8
- "@dereekb/firebase-server": "13.15.0",
9
- "@dereekb/firebase-server/oidc": "13.15.0",
10
- "@dereekb/model": "13.15.0",
11
- "@dereekb/nestjs": "13.15.0",
12
- "@dereekb/rxjs": "13.15.0",
13
- "@dereekb/util": "13.15.0",
14
- "@dereekb/zoho": "13.15.0",
5
+ "@dereekb/analytics": "13.17.0",
6
+ "@dereekb/date": "13.17.0",
7
+ "@dereekb/firebase": "13.17.0",
8
+ "@dereekb/firebase-server": "13.17.0",
9
+ "@dereekb/firebase-server/oidc": "13.17.0",
10
+ "@dereekb/model": "13.17.0",
11
+ "@dereekb/nestjs": "13.17.0",
12
+ "@dereekb/rxjs": "13.17.0",
13
+ "@dereekb/util": "13.17.0",
14
+ "@dereekb/zoho": "13.17.0",
15
15
  "@modelcontextprotocol/sdk": "1.29.0",
16
16
  "@nestjs/common": "^11.1.19",
17
17
  "@nestjs/core": "^11.1.19",
@@ -80,6 +80,17 @@ export declare abstract class McpModuleConfig {
80
80
  * schemas, no output schemas) and emits a single boot warning.
81
81
  */
82
82
  readonly mcpManifestPath?: string;
83
+ /**
84
+ * Absolute path to a pre-rendered route manifest JSON file produced by
85
+ * `dbx-cli-generate-route-manifest`. When set, the runtime reads it once at
86
+ * boot and registers the built-in `url-models` tool, which decodes an app URL
87
+ * into the Firestore models its page renders.
88
+ *
89
+ * Optional. When unset or the file is missing, the runtime skips registering
90
+ * `url-models` and emits a single boot warning when the path was set but
91
+ * unreadable.
92
+ */
93
+ readonly mcpRouteManifestPath?: string;
83
94
  /**
84
95
  * When `true`, the MCP server only advertises tools whose effective read-only classification is `true`.
85
96
  *
@@ -1,5 +1,6 @@
1
1
  export * from './analytics';
2
2
  export * from './mcp.manifest';
3
+ export * from './mcp.route-manifest';
3
4
  export * from './mcp.response-formatter';
4
5
  export * from './mcp.server.factory';
5
6
  export * from './mcp.tool-generator';
@@ -7,4 +8,6 @@ export * from './mcp.visibility';
7
8
  export * from './tools/mcp.tool.model-get';
8
9
  export * from './tools/mcp.tool.model-info';
9
10
  export * from './tools/mcp.tool.model-decode';
11
+ export * from './tools/mcp.tool.enum-info';
12
+ export * from './tools/mcp.tool.url-models';
10
13
  export * from './tools/mcp.tool.batch-execute';
@@ -82,6 +82,28 @@ export interface McpManifestModelEntry {
82
82
  readonly sourceFile: string;
83
83
  };
84
84
  }
85
+ /**
86
+ * One enum value with its persisted literal and the leading JSDoc paragraph.
87
+ *
88
+ * Structural mirror of `@dereekb/dbx-cli`'s `CliModelEnumValue`. Lets the
89
+ * runtime `model-info` / `enum-info` tools decode raw persisted integer/string
90
+ * values (`s: 4`) back to human-readable names without dropping into source.
91
+ */
92
+ export interface McpManifestEnumValue {
93
+ readonly name: string;
94
+ readonly value: number | string;
95
+ readonly description?: string;
96
+ }
97
+ /**
98
+ * One TypeScript enum referenced by some model field's `enumRef`.
99
+ *
100
+ * Structural mirror of `@dereekb/dbx-cli`'s `CliModelEnum`.
101
+ */
102
+ export interface McpManifestEnum {
103
+ readonly name: string;
104
+ readonly values: readonly McpManifestEnumValue[];
105
+ readonly description?: string;
106
+ }
85
107
  /**
86
108
  * One auth claim entry in the pre-rendered MCP manifest JSON.
87
109
  *
@@ -138,6 +160,10 @@ export interface McpManifestAuth {
138
160
  * manifests rendered before model catalog support landed), the runtime skips
139
161
  * registering those tools instead of failing the boot.
140
162
  *
163
+ * The optional `enums` map (keyed by enum name) carries the value→label tables
164
+ * `model-info` inlines and the `enum-info` static tool serves. Absent on legacy
165
+ * manifests; the runtime then skips registering `enum-info`.
166
+ *
141
167
  * The optional `auth` section drives the built-in `whoami` static tool.
142
168
  */
143
169
  export interface McpManifest {
@@ -147,6 +173,9 @@ export interface McpManifest {
147
173
  readonly [key: string]: McpManifestToolEntry | undefined;
148
174
  };
149
175
  readonly models?: readonly McpManifestModelEntry[];
176
+ readonly enums?: {
177
+ readonly [name: string]: McpManifestEnum;
178
+ };
150
179
  readonly auth?: McpManifestAuth;
151
180
  }
152
181
  /**
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Runtime mirror of the build-time route manifest schema + a ported URL matcher.
3
+ *
4
+ * Structural mirror of `@dereekb/dbx-cli`'s `route-manifest.ts` types and the
5
+ * pure `url-match.ts` matcher, so the firebase-server/mcp runtime can consume
6
+ * the generated `route.manifest.json` without taking a runtime dependency on
7
+ * the build-time CLI package. Both sides bump {@link ROUTE_MANIFEST_VERSION}
8
+ * together when the manifest shape changes.
9
+ */
10
+ /**
11
+ * Version stamp embedded in `route.manifest.json`. Runtime loaders refuse
12
+ * manifests whose `version` does not match. Mirror in `@dereekb/dbx-cli`'s
13
+ * `ROUTE_MANIFEST_VERSION` — bump both together.
14
+ */
15
+ export declare const ROUTE_MANIFEST_VERSION: 1;
16
+ /**
17
+ * Whether a route-model entry resolves to a promoted id, a full key, or a
18
+ * keyless list. Mirror of `@dereekb/dbx-cli`'s `RouteModelKind`.
19
+ */
20
+ export type RouteModelKind = 'id' | 'key' | 'list';
21
+ /**
22
+ * One model an app page renders. Structural mirror of `@dereekb/dbx-cli`'s
23
+ * `RouteManifestModelEntry`.
24
+ */
25
+ export interface RouteManifestModelEntry {
26
+ readonly modelType: string;
27
+ readonly kind: RouteModelKind;
28
+ readonly keyTemplate?: string;
29
+ readonly description?: string;
30
+ readonly from?: string;
31
+ }
32
+ /**
33
+ * One UIRouter state, inheritance pre-flattened into `models`. Structural mirror
34
+ * of `@dereekb/dbx-cli`'s `RouteManifestStateEntry`.
35
+ */
36
+ export interface RouteManifestStateEntry {
37
+ readonly name: string;
38
+ readonly url?: string;
39
+ readonly fullUrl?: string;
40
+ readonly parentName?: string;
41
+ readonly paramKeys: readonly string[];
42
+ readonly urlParamKeys: readonly string[];
43
+ readonly component?: string;
44
+ readonly componentFile?: string;
45
+ readonly abstract?: boolean;
46
+ readonly redirectTo?: string;
47
+ readonly models: readonly RouteManifestModelEntry[];
48
+ }
49
+ /**
50
+ * The full `route.manifest.json` shape. Structural mirror of `@dereekb/dbx-cli`'s
51
+ * `RouteManifest`.
52
+ */
53
+ export interface RouteManifest {
54
+ readonly version: typeof ROUTE_MANIFEST_VERSION;
55
+ readonly generatedAt: string;
56
+ readonly app: {
57
+ readonly name: string;
58
+ readonly baseUrl?: string;
59
+ };
60
+ readonly states: readonly RouteManifestStateEntry[];
61
+ }
62
+ /**
63
+ * A single resolved state match. `via` distinguishes a literal path match from
64
+ * a parameterised one; `params` carries the captured `:param` / `{param}`
65
+ * values (empty for literal matches).
66
+ */
67
+ export interface RouteUrlMatch {
68
+ readonly kind: 'match';
69
+ readonly via: 'literal' | 'param';
70
+ readonly state: RouteManifestStateEntry;
71
+ readonly params: Readonly<Record<string, string>>;
72
+ readonly pathname: string;
73
+ }
74
+ /**
75
+ * More than one state matched at the same tier.
76
+ */
77
+ export interface RouteUrlAmbiguous {
78
+ readonly kind: 'ambiguous';
79
+ readonly states: readonly RouteManifestStateEntry[];
80
+ readonly pathname: string;
81
+ }
82
+ /**
83
+ * No state matched; `candidates` holds the closest scored near-misses (top 5).
84
+ */
85
+ export interface RouteUrlNone {
86
+ readonly kind: 'none';
87
+ readonly candidates: readonly RouteManifestStateEntry[];
88
+ readonly pathname: string;
89
+ }
90
+ export type RouteUrlMatchResult = RouteUrlMatch | RouteUrlAmbiguous | RouteUrlNone;
91
+ /**
92
+ * Input to {@link matchRouteManifestUrl}.
93
+ */
94
+ export interface MatchRouteManifestUrlInput {
95
+ readonly manifest: RouteManifest;
96
+ readonly url: string;
97
+ }
98
+ /**
99
+ * Extracts the normalized pathname from a full URL or a bare path. Strips the
100
+ * scheme/host/port, query string, and hash; collapses an empty path to `/` and
101
+ * trims a single trailing slash.
102
+ *
103
+ * @param url - A full URL (`https://app.example.co/worker/x/timesheets/list`) or bare path.
104
+ * @returns The normalized pathname.
105
+ *
106
+ * @example
107
+ * ```ts
108
+ * parseUrlModelsPathname('https://app.hellosubs.co/worker/abc/timesheets/list/'); // => '/worker/abc/timesheets/list'
109
+ * ```
110
+ */
111
+ export declare function parseUrlModelsPathname(url: string): string;
112
+ /**
113
+ * Matches a URL against the manifest's states, preferring a literal match over
114
+ * a parameterised one. A tie at either tier collapses to `ambiguous`; otherwise
115
+ * the closest near-misses are scored and returned in a `none` result.
116
+ *
117
+ * @param input - The manifest and the URL (or pathname) to resolve.
118
+ * @returns A discriminated match / ambiguous / none result.
119
+ */
120
+ export declare function matchRouteManifestUrl(input: MatchRouteManifestUrlInput): RouteUrlMatchResult;
@@ -32,7 +32,10 @@ export declare class McpServerFactoryService {
32
32
  private _cachedStaticTools;
33
33
  private _cachedManifest;
34
34
  private _cachedManifestModels;
35
+ private _cachedManifestEnums;
35
36
  private _cachedManifestAuth;
37
+ private _cachedRouteManifest;
38
+ private _routeManifestLoaded;
36
39
  private _manifestLoaded;
37
40
  private _loggedSkips;
38
41
  private _warnedMissingRoleReader;
@@ -129,6 +132,17 @@ export declare class McpServerFactoryService {
129
132
  */
130
133
  private _resolveManifest;
131
134
  private _resetManifestCache;
135
+ /**
136
+ * Reads the pre-rendered route manifest JSON once, validates its version, and caches the result
137
+ * for the process lifetime. Drives whether the built-in `url-models` static tool is registered.
138
+ *
139
+ * Missing file or wrong version fall back to "no route manifest" with a single boot warning;
140
+ * the `url-models` tool is then simply not offered.
141
+ *
142
+ * @returns The cached route manifest, or `undefined` when none was loaded.
143
+ */
144
+ private _resolveRouteManifest;
145
+ private _parseRouteManifestFile;
132
146
  private _parseManifestFile;
133
147
  private _applyParsedManifest;
134
148
  /**
@@ -2,7 +2,7 @@ import { type Maybe } from '@dereekb/util';
2
2
  import { type KnownOnCallFunctionType } from '@dereekb/firebase';
3
3
  import { type ModelApiDetailsResult, type OnCallModelFunctionApiDetails, type FirebaseServerAuthData, type McpToolDetailsBuilder } from '@dereekb/firebase-server';
4
4
  import { type Request } from 'express';
5
- import { type CallToolResult } from '@modelcontextprotocol/sdk/types.js';
5
+ import { type CallToolResult, type ToolAnnotations } from '@modelcontextprotocol/sdk/types.js';
6
6
  import { type McpManifestToolEntry } from './mcp.manifest';
7
7
  import { type McpToolFilterMetadata } from './mcp.visibility';
8
8
  /**
@@ -17,6 +17,12 @@ export interface McpToolListEntry {
17
17
  readonly description: string;
18
18
  readonly inputSchema: object;
19
19
  readonly outputSchema?: object;
20
+ /**
21
+ * Standard MCP read/write hints (`readOnlyHint`, `destructiveHint`) advertised to clients so a
22
+ * tool's mutating behaviour is machine-readable. Resolved at boot from the effective read-only
23
+ * classification; see {@link resolveMcpToolAnnotations}.
24
+ */
25
+ readonly annotations?: ToolAnnotations;
20
26
  }
21
27
  /**
22
28
  * A single MCP tool definition generated from one (modelType, callType, specifier) triple.
@@ -54,6 +60,12 @@ export interface McpToolDefinition {
54
60
  * response only includes this when the pinned MCP SDK type allows it.
55
61
  */
56
62
  readonly outputSchema?: object;
63
+ /**
64
+ * Standard MCP read/write hints for this tool, mirrored onto {@link staticWireEntry} and the
65
+ * dynamic wire path so opt-in `toolDetails` handlers keep their annotations. Resolved at boot
66
+ * from the effective read-only classification; see {@link resolveMcpToolAnnotations}.
67
+ */
68
+ readonly annotations?: ToolAnnotations;
57
69
  /**
58
70
  * The original handler-level API details. Carries response formatters, analytics
59
71
  * config, etc. — the controller resolves Tier 1/2/3 response shape from this.
@@ -224,6 +236,7 @@ interface BuildStaticWireEntryInput {
224
236
  readonly description: string;
225
237
  readonly inputSchema?: object;
226
238
  readonly outputSchema?: object;
239
+ readonly annotations?: ToolAnnotations;
227
240
  }
228
241
  /**
229
242
  * Builds the frozen wire-shape entry used by the per-request `tools/list` hot path.
@@ -235,6 +248,20 @@ interface BuildStaticWireEntryInput {
235
248
  * @returns A frozen wire entry safe to share across requests.
236
249
  */
237
250
  export declare function buildStaticWireEntry(input: BuildStaticWireEntryInput): McpToolListEntry;
251
+ /**
252
+ * Description prefix prepended to a mutating tool's description so MCP clients that don't render
253
+ * {@link ToolAnnotations} still surface the write signal in plain text.
254
+ */
255
+ export declare const MCP_WRITE_TOOL_DESCRIPTION_PREFIX = "[WRITE] ";
256
+ /**
257
+ * Prepends {@link MCP_WRITE_TOOL_DESCRIPTION_PREFIX} to a description when its annotations mark the
258
+ * tool as a write (`readOnlyHint === false`). Read-only tools are returned unchanged.
259
+ *
260
+ * @param description - The base tool description.
261
+ * @param annotations - The resolved MCP annotations for the tool.
262
+ * @returns The description, prefixed with the write marker when the tool mutates.
263
+ */
264
+ export declare function applyWriteMarker(description: string, annotations: ToolAnnotations): string;
238
265
  /**
239
266
  * The default specifier key used when a handler is not behind a specifier router.
240
267
  */
@@ -1,6 +1,7 @@
1
1
  import { type Maybe } from '@dereekb/util';
2
2
  import { type CallModelOidcScope } from '@dereekb/firebase';
3
3
  import { type McpToolVisibility, type McpVisibilityContext, type McpVisibilityRule } from '@dereekb/firebase-server';
4
+ import { type ToolAnnotations } from '@modelcontextprotocol/sdk/types.js';
4
5
  /**
5
6
  * Normalized classification of a {@link McpToolVisibility} value computed at boot.
6
7
  *
@@ -87,6 +88,22 @@ export declare function classifyVisibility(visibility?: McpToolVisibility): Clas
87
88
  * @returns The effective read-only flag, or `undefined` when neither source resolves a value.
88
89
  */
89
90
  export declare function resolveEffectiveReadOnly(explicitReadOnly: Maybe<boolean>, callType: string): boolean | undefined;
91
+ /**
92
+ * Maps an effective read-only classification to the standard MCP {@link ToolAnnotations} hints
93
+ * advertised on `tools/list`.
94
+ *
95
+ * A definitively read-only tool advertises `{ readOnlyHint: true }`. Everything else — a known
96
+ * write (`false`) or an unclassified verb (`undefined`, e.g. `invoke`/custom) — fails safe to
97
+ * `{ readOnlyHint: false, destructiveHint: true }` so a client never mistakes an ambiguous tool
98
+ * for a safe read. `idempotentHint` / `openWorldHint` are intentionally left unset.
99
+ *
100
+ * Co-located with {@link resolveEffectiveReadOnly} / {@link READ_ONLY_BY_CALL_TYPE} so the
101
+ * read/write classification rules stay in one file.
102
+ *
103
+ * @param effectiveReadOnly - The resolved read-only classification from {@link resolveEffectiveReadOnly}.
104
+ * @returns The MCP annotations describing the tool's read/write behaviour.
105
+ */
106
+ export declare function resolveMcpToolAnnotations(effectiveReadOnly: boolean | undefined): ToolAnnotations;
90
107
  /**
91
108
  * Resolves the OIDC scope required to invoke a given call type, or `undefined` for
92
109
  * non-CRUD calls. Thin re-export so the tool generator doesn't need to reach into
@@ -0,0 +1,70 @@
1
+ import { type McpManifestEnum } from '../mcp.manifest';
2
+ import { type McpToolDefinition } from '../mcp.tool-generator';
3
+ /**
4
+ * Reserved tool name for the built-in `enum-info` static tool.
5
+ */
6
+ export declare const ENUM_INFO_TOOL_NAME = "enum-info";
7
+ /**
8
+ * Synthetic call type used in the tool's dispatch identity. Mirrors the `info` call shared with
9
+ * `model-info`.
10
+ */
11
+ export declare const ENUM_INFO_DISPATCH_CALL = "info";
12
+ /**
13
+ * Synthetic model type used in the tool's dispatch identity. Apps avoiding collisions should not
14
+ * register a real model literally named "enum".
15
+ */
16
+ export declare const ENUM_INFO_DISPATCH_MODEL_TYPE = "enum";
17
+ /**
18
+ * Constructor dependencies for {@link createEnumInfoTool}.
19
+ */
20
+ export interface CreateEnumInfoToolDeps {
21
+ /**
22
+ * Frozen enum value tables keyed by enum name, sourced from the build-time manifest's `enums`
23
+ * block.
24
+ */
25
+ readonly enums: {
26
+ readonly [name: string]: McpManifestEnum;
27
+ };
28
+ }
29
+ /**
30
+ * Shape of the `enum-info` tool input.
31
+ */
32
+ export interface EnumInfoToolInput {
33
+ /**
34
+ * One enum name (string) or several (string array), each matched exactly by declaration name.
35
+ */
36
+ readonly enum: string | ReadonlyArray<string>;
37
+ }
38
+ /**
39
+ * Output payload for the `enum-info` tool: the resolved value tables, with any unmatched names in
40
+ * `notFound`.
41
+ */
42
+ export interface EnumInfoToolOutput {
43
+ readonly enums: ReadonlyArray<McpManifestEnum>;
44
+ readonly notFound?: ReadonlyArray<string>;
45
+ }
46
+ /**
47
+ * Builds the built-in `enum-info` MCP tool definition — the symmetric counterpart to `model-info`
48
+ * for raw enum decoding.
49
+ *
50
+ * Resolves each requested enum name against the manifest's `enums` block and returns the matching
51
+ * value→label tables; misses land in `notFound`. A bare string is treated as a one-element array.
52
+ *
53
+ * Output is delivered as both stringified JSON in `content[0].text` and `structuredContent` so MCP
54
+ * clients can consume either form.
55
+ *
56
+ * @param deps - The frozen enum value tables loaded at boot from the MCP manifest JSON.
57
+ * @returns A statically-registered {@link McpToolDefinition} ready to be appended to the MCP server
58
+ * factory's tool registry.
59
+ */
60
+ export declare function createEnumInfoTool(deps: CreateEnumInfoToolDeps): McpToolDefinition;
61
+ /**
62
+ * Resolves each requested enum name against the registered value tables.
63
+ *
64
+ * @param names - The requested enum declaration names.
65
+ * @param enums - The registered enum value tables keyed by name.
66
+ * @returns The matched tables, with any unmatched names in `notFound`.
67
+ *
68
+ * @__NO_SIDE_EFFECTS__
69
+ */
70
+ export declare function resolveEnumInfoOutput(names: readonly string[], enums: CreateEnumInfoToolDeps['enums']): EnumInfoToolOutput;
@@ -1,4 +1,4 @@
1
- import { type McpManifestModelEntry } from '../mcp.manifest';
1
+ import { type McpManifestEnum, type McpManifestModelEntry } from '../mcp.manifest';
2
2
  import { type McpToolDefinition } from '../mcp.tool-generator';
3
3
  /**
4
4
  * Reserved tool name for the built-in `model-info` static tool.
@@ -32,6 +32,15 @@ export interface CreateModelInfoToolDeps {
32
32
  * manifest JSON.
33
33
  */
34
34
  readonly manifest: readonly McpManifestModelEntry[];
35
+ /**
36
+ * Optional enum value tables, keyed by enum name, sourced from the build-time manifest's `enums`
37
+ * block. When present, the value→label table for every enum referenced (by `enumRef`) on a
38
+ * returned model's fields is attached as a top-level `enums` section — but only when full field
39
+ * detail is returned, so the default compact modes stay small.
40
+ */
41
+ readonly enums?: {
42
+ readonly [name: string]: McpManifestEnum;
43
+ };
35
44
  }
36
45
  /**
37
46
  * Public shape of the `model-info` tool input. Every field is optional; the combination selects
@@ -112,8 +121,12 @@ export interface ModelInfoNotFound {
112
121
  * - `list`: a summary (or, with `fields: true`, full) list — used by `modelGroup` and `all`.
113
122
  * - `single`: full detail for one `model` string.
114
123
  * - `multiple`: full detail per match for a `model` array, with misses in `notFound`.
124
+ *
125
+ * The orthogonal `enums` section is attached only when full field detail is returned and the
126
+ * returned models reference at least one enum present in the manifest — the default compact modes
127
+ * (`groups`, summary `list`) never carry it.
115
128
  */
116
- export type ModelInfoToolOutput = {
129
+ export type ModelInfoToolOutput = ({
117
130
  readonly mode: 'groups';
118
131
  readonly groups: ReadonlyArray<ModelInfoGroupCount>;
119
132
  readonly totalModels: number;
@@ -129,6 +142,8 @@ export type ModelInfoToolOutput = {
129
142
  readonly mode: 'multiple';
130
143
  readonly models: ReadonlyArray<ModelInfoModelRow>;
131
144
  readonly notFound?: ReadonlyArray<ModelInfoNotFound>;
145
+ }) & {
146
+ readonly enums?: ReadonlyArray<McpManifestEnum>;
132
147
  };
133
148
  /**
134
149
  * Builds the built-in `model-info` MCP tool definition.
@@ -162,3 +177,37 @@ export declare function createModelInfoTool(deps: CreateModelInfoToolDeps): McpT
162
177
  * @__NO_SIDE_EFFECTS__
163
178
  */
164
179
  export declare function findModelEntry(query: string, manifest: ReadonlyArray<McpManifestModelEntry>): McpManifestModelEntry | undefined;
180
+ /**
181
+ * JSON-schema for one enum value→label table. Shared with the `enum-info` tool so both advertise the
182
+ * identical enum shape.
183
+ */
184
+ export declare const ENUM_TABLE_SCHEMA: {
185
+ readonly type: "object";
186
+ readonly required: readonly ["name", "values"];
187
+ readonly properties: {
188
+ readonly name: {
189
+ readonly type: "string";
190
+ };
191
+ readonly description: {
192
+ readonly type: "string";
193
+ };
194
+ readonly values: {
195
+ readonly type: "array";
196
+ readonly items: {
197
+ readonly type: "object";
198
+ readonly required: readonly ["name", "value"];
199
+ readonly properties: {
200
+ readonly name: {
201
+ readonly type: "string";
202
+ };
203
+ readonly value: {
204
+ readonly type: readonly ["string", "number"];
205
+ };
206
+ readonly description: {
207
+ readonly type: "string";
208
+ };
209
+ };
210
+ };
211
+ };
212
+ };
213
+ };
@@ -0,0 +1,101 @@
1
+ import { type FirestoreModelKey } from '@dereekb/firebase';
2
+ import { type ModelAccessMultiReadResult } from '@dereekb/firebase-server';
3
+ import { type McpToolDefinition } from '../mcp.tool-generator';
4
+ import { type RouteManifest, type RouteManifestModelEntry } from '../mcp.route-manifest';
5
+ import { type McpModelGetReadDocuments, type McpModelGetResolveIdentity } from './mcp.tool.model-get';
6
+ /**
7
+ * Reserved tool name for the built-in `url-models` static tool.
8
+ */
9
+ export declare const URL_MODELS_TOOL_NAME = "url-models";
10
+ /**
11
+ * Synthetic call type used in the tool's dispatch identity.
12
+ */
13
+ export declare const URL_MODELS_DISPATCH_CALL = "url-models";
14
+ /**
15
+ * Synthetic model type used in the tool's dispatch identity. The tool isn't
16
+ * bound to a real model (the `url` is its input), so we use the literal "route".
17
+ */
18
+ export declare const URL_MODELS_DISPATCH_MODEL_TYPE = "route";
19
+ /**
20
+ * Why a model binding could not be resolved into a concrete key.
21
+ */
22
+ export type UrlModelUnresolvedReason = 'missing-param' | 'subcollection-requires-key-template' | 'unknown-model-type' | 'auth-required';
23
+ /**
24
+ * One resolved model binding for a matched page.
25
+ */
26
+ export interface ResolvedRouteModel {
27
+ readonly modelType: string;
28
+ readonly kind: RouteManifestModelEntry['kind'];
29
+ readonly keyTemplate?: string;
30
+ readonly description?: string;
31
+ readonly from?: string;
32
+ /**
33
+ * The concrete FirestoreModelKey, when the binding resolved (`id`/`key` kinds).
34
+ */
35
+ readonly key?: FirestoreModelKey;
36
+ /**
37
+ * Present when the binding could not be turned into a key.
38
+ */
39
+ readonly unresolved?: {
40
+ readonly reason: UrlModelUnresolvedReason;
41
+ readonly message: string;
42
+ };
43
+ }
44
+ /**
45
+ * Documents loaded for one model type (when `load` is requested).
46
+ */
47
+ export interface UrlModelLoadedGroup {
48
+ readonly modelType: string;
49
+ readonly results: ModelAccessMultiReadResult['results'];
50
+ readonly errors: ModelAccessMultiReadResult['errors'];
51
+ }
52
+ /**
53
+ * Constructor dependencies for {@link createUrlModelsTool}.
54
+ */
55
+ export interface CreateUrlModelsToolDeps {
56
+ /**
57
+ * The pre-rendered route manifest used to match URLs to states.
58
+ */
59
+ readonly routeManifest: RouteManifest;
60
+ /**
61
+ * Reads a batch of model documents (shared with `model-get`).
62
+ */
63
+ readonly readDocuments: McpModelGetReadDocuments;
64
+ /**
65
+ * Resolves a model type's registered identity so `id` key templates can be
66
+ * promoted to `<collectionName>/<id>` (shared with `model-get`).
67
+ */
68
+ readonly resolveIdentity: McpModelGetResolveIdentity;
69
+ }
70
+ /**
71
+ * Shape of the `url-models` tool input.
72
+ */
73
+ export interface UrlModelsToolInput {
74
+ readonly url: string;
75
+ readonly models?: ReadonlyArray<string>;
76
+ readonly keysOnly?: boolean;
77
+ readonly load?: boolean;
78
+ /**
79
+ * Overrides the uid used to fill `{authUid}` placeholders when resolving model
80
+ * keys (defaults to the authenticated caller). Use to preview the models
81
+ * another user would see on a page. Does not affect the document-load path —
82
+ * `load` still reads via the calling user's permissions.
83
+ */
84
+ readonly currentUserUid?: string;
85
+ }
86
+ /**
87
+ * Builds the built-in `url-models` MCP tool definition.
88
+ *
89
+ * Matches a pasted app URL against the build-time route manifest and returns the
90
+ * Firestore models the page renders — model types plus concrete keys with the
91
+ * route params and `{authUid}` substituted. Optionally filtered to specific
92
+ * `models`, reduced to keys-only, or loaded via the same permission-checked read
93
+ * path as `model-get`.
94
+ *
95
+ * A URL that matches no state returns a structured `{ matched: null, candidates }`
96
+ * (not an error) so the caller can suggest near-misses.
97
+ *
98
+ * @param deps - The route manifest plus the shared read-documents / identity callbacks.
99
+ * @returns A statically-registered {@link McpToolDefinition}.
100
+ */
101
+ export declare function createUrlModelsTool(deps: CreateUrlModelsToolDeps): McpToolDefinition;