@dereekb/firebase-server 13.11.17 → 13.12.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/index.cjs.js +218 -31
- package/index.esm.js +217 -34
- package/mailgun/package.json +9 -9
- package/mcp/index.cjs.default.js +1 -0
- package/mcp/index.cjs.js +3646 -0
- package/mcp/index.cjs.mjs +2 -0
- package/mcp/index.d.ts +1 -0
- package/mcp/index.esm.js +3609 -0
- package/mcp/package.json +33 -0
- package/mcp/src/index.d.ts +1 -0
- package/mcp/src/lib/controller/index.d.ts +2 -0
- package/mcp/src/lib/controller/mcp.controller.d.ts +19 -0
- package/mcp/src/lib/controller/mcp.wellknown.controller.d.ts +25 -0
- package/mcp/src/lib/index.d.ts +5 -0
- package/mcp/src/lib/mcp.config.d.ts +100 -0
- package/mcp/src/lib/mcp.module.d.ts +49 -0
- package/mcp/src/lib/service/index.d.ts +8 -0
- package/mcp/src/lib/service/mcp.manifest.d.ts +150 -0
- package/mcp/src/lib/service/mcp.response-formatter.d.ts +36 -0
- package/mcp/src/lib/service/mcp.server.factory.d.ts +124 -0
- package/mcp/src/lib/service/mcp.tool-generator.d.ts +235 -0
- package/mcp/src/lib/service/mcp.visibility.d.ts +99 -0
- package/mcp/src/lib/service/tools/mcp.tool.model-decode.d.ts +87 -0
- package/mcp/src/lib/service/tools/mcp.tool.model-get.d.ts +81 -0
- package/mcp/src/lib/service/tools/mcp.tool.model-info.d.ts +84 -0
- package/mcp/src/lib/service/tools/mcp.tool.whoami.d.ts +72 -0
- package/mcp/src/lib/transport/index.d.ts +1 -0
- package/mcp/src/lib/transport/streamable-http.transport.d.ts +21 -0
- package/model/index.cjs.js +639 -266
- package/model/index.esm.js +640 -270
- package/model/package.json +9 -9
- package/model/src/lib/storagefile/extension/compress.pdf.d.ts +29 -1
- package/model/src/lib/storagefile/index.d.ts +1 -0
- package/model/src/lib/storagefile/storagefile.action.server.d.ts +29 -2
- package/model/src/lib/storagefile/storagefile.mcp.d.ts +32 -0
- package/model/src/lib/storagefile/storagefile.module.d.ts +20 -3
- package/oidc/index.cjs.js +322 -147
- package/oidc/index.esm.js +321 -148
- package/oidc/package.json +10 -10
- package/oidc/src/lib/controller/oidc.wellknown.controller.d.ts +6 -15
- package/oidc/src/lib/middleware/oauth-auth.middleware.d.ts +22 -2
- package/oidc/src/lib/middleware/oauth-auth.module.d.ts +57 -0
- package/oidc/src/lib/oidc.config.d.ts +73 -0
- package/oidc/src/lib/oidc.module.d.ts +44 -2
- package/package.json +16 -10
- package/src/lib/auth/auth.service.d.ts +16 -1
- package/src/lib/auth/auth.service.error.util.d.ts +24 -5
- package/src/lib/env/env.config.d.ts +16 -0
- package/src/lib/env/env.service.d.ts +9 -0
- package/src/lib/nest/controller/model/model.api.get.service.d.ts +35 -1
- package/src/lib/nest/env/env.service.d.ts +1 -0
- package/src/lib/nest/model/api.details.d.ts +116 -5
- package/src/lib/nest/model/crud.assert.function.d.ts +1 -1
- package/src/lib/nest/model/index.d.ts +1 -0
- package/src/lib/nest/model/invoke.model.function.d.ts +89 -0
- package/test/package.json +11 -11
- package/twilio/LICENSE +21 -0
- package/twilio/index.cjs.default.js +1 -0
- package/twilio/index.cjs.js +404 -0
- package/twilio/index.cjs.mjs +2 -0
- package/twilio/index.d.ts +1 -0
- package/twilio/index.esm.js +398 -0
- package/twilio/package.json +25 -0
- package/twilio/src/index.d.ts +1 -0
- package/twilio/src/lib/index.d.ts +1 -0
- package/twilio/src/lib/notification.send.service.twilio.d.ts +148 -0
- package/zoho/package.json +9 -9
package/mcp/index.esm.js
ADDED
|
@@ -0,0 +1,3609 @@
|
|
|
1
|
+
import { Injectable, Inject, Optional, Logger, Post, Req, Res, Controller, Get } from '@nestjs/common';
|
|
2
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
4
|
+
import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
5
|
+
import { getOidcScopesFromRequest } from '@dereekb/firebase-server/oidc';
|
|
6
|
+
import { authRolesSetHasRoles } from '@dereekb/util';
|
|
7
|
+
import { ModelApiCallModelDispatchService, ModelApiGetService } from '@dereekb/firebase-server';
|
|
8
|
+
import { inspect } from 'node:util';
|
|
9
|
+
import { arktypeToJsonSchemaForExport } from '@dereekb/model';
|
|
10
|
+
import { callModelOidcScopeForCallType } from '@dereekb/firebase';
|
|
11
|
+
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
12
|
+
|
|
13
|
+
function _class_call_check$3(instance, Constructor) {
|
|
14
|
+
if (!(instance instanceof Constructor)) {
|
|
15
|
+
throw new TypeError("Cannot call a class as a function");
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function _define_property$6(obj, key, value) {
|
|
19
|
+
if (key in obj) {
|
|
20
|
+
Object.defineProperty(obj, key, {
|
|
21
|
+
value: value,
|
|
22
|
+
enumerable: true,
|
|
23
|
+
configurable: true,
|
|
24
|
+
writable: true
|
|
25
|
+
});
|
|
26
|
+
} else {
|
|
27
|
+
obj[key] = value;
|
|
28
|
+
}
|
|
29
|
+
return obj;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Default path the MCP Streamable HTTP transport is mounted at.
|
|
33
|
+
*
|
|
34
|
+
* Combined with the app origin to form the canonical MCP resource URL
|
|
35
|
+
* (e.g., `https://api.example.com/mcp`) advertised in the protected-resource
|
|
36
|
+
* discovery document at `/.well-known/oauth-protected-resource`.
|
|
37
|
+
*/ var DEFAULT_MCP_PATH = '/mcp';
|
|
38
|
+
/**
|
|
39
|
+
* Default name advertised by the MCP server on the JSON-RPC `initialize` handshake.
|
|
40
|
+
*
|
|
41
|
+
* Apps may override this via {@link McpModuleConfig.serverName} to identify their
|
|
42
|
+
* deployment (e.g., `demo-api-mcp`, `hellosubs-api-mcp`).
|
|
43
|
+
*/ var DEFAULT_MCP_SERVER_NAME = 'dbx-firebase-server-mcp';
|
|
44
|
+
/**
|
|
45
|
+
* Configuration for the firebase-server/mcp module.
|
|
46
|
+
*
|
|
47
|
+
* Apps construct this in their `*McpModule` provider and pass it through
|
|
48
|
+
* {@link mcpModuleMetadata}. It carries:
|
|
49
|
+
*
|
|
50
|
+
* - The canonical issuer URL for the OIDC provider that gates this MCP endpoint
|
|
51
|
+
* (used in the protected-resource discovery document).
|
|
52
|
+
* - The canonical resource URL where the MCP transport is mounted (also part of
|
|
53
|
+
* the discovery document; doubles as the `resource` claim consumers should
|
|
54
|
+
* verify their access tokens against).
|
|
55
|
+
* - Optional server identity (name, version) advertised on the MCP `initialize` handshake.
|
|
56
|
+
*
|
|
57
|
+
* The OAuth bearer middleware that authenticates `/mcp` is configured on
|
|
58
|
+
* {@link OidcModuleConfig.protectedPaths} — that's outside this config's scope.
|
|
59
|
+
*/ var McpModuleConfig = function McpModuleConfig() {
|
|
60
|
+
_class_call_check$3(this, McpModuleConfig);
|
|
61
|
+
/**
|
|
62
|
+
* The canonical issuer URL of the OIDC provider that gates this MCP endpoint.
|
|
63
|
+
*
|
|
64
|
+
* Surfaced in the `authorization_servers` field of the protected-resource
|
|
65
|
+
* discovery document. Claude custom-connector reads this to discover the OIDC issuer.
|
|
66
|
+
*
|
|
67
|
+
* @example 'https://api.example.com/oidc'
|
|
68
|
+
*/ _define_property$6(this, "oidcIssuer", void 0);
|
|
69
|
+
/**
|
|
70
|
+
* The canonical resource URL of the MCP endpoint.
|
|
71
|
+
*
|
|
72
|
+
* Surfaced as the `resource` field of the protected-resource discovery document,
|
|
73
|
+
* and used as the audience the access token's `aud` claim should match.
|
|
74
|
+
*
|
|
75
|
+
* @example 'https://api.example.com/mcp'
|
|
76
|
+
*/ _define_property$6(this, "mcpUrl", void 0);
|
|
77
|
+
/**
|
|
78
|
+
* Optional name advertised on the MCP `initialize` handshake. Defaults to
|
|
79
|
+
* {@link DEFAULT_MCP_SERVER_NAME}.
|
|
80
|
+
*/ _define_property$6(this, "serverName", void 0);
|
|
81
|
+
/**
|
|
82
|
+
* Optional version advertised on the MCP `initialize` handshake.
|
|
83
|
+
*/ _define_property$6(this, "serverVersion", void 0);
|
|
84
|
+
/**
|
|
85
|
+
* Absolute path to a pre-rendered MCP manifest JSON file produced by
|
|
86
|
+
* `dbx-cli-generate-mcp-manifest`. When set, the runtime reads it once at
|
|
87
|
+
* boot and uses each tool's `description`, `inputSchema`, and `outputSchema`
|
|
88
|
+
* during tool generation — no per-request file I/O.
|
|
89
|
+
*
|
|
90
|
+
* Optional. When unset or the file is missing, the runtime falls back to
|
|
91
|
+
* today's behavior (auto-generated descriptions, ArkType-derived input
|
|
92
|
+
* schemas, no output schemas) and emits a single boot warning.
|
|
93
|
+
*/ _define_property$6(this, "mcpManifestPath", void 0);
|
|
94
|
+
/**
|
|
95
|
+
* When `true`, the MCP server only advertises tools whose effective read-only classification is `true`.
|
|
96
|
+
*
|
|
97
|
+
* Write tools (`create`/`update`/`delete`) and tools with unknown classification (e.g., `invoke`
|
|
98
|
+
* with no explicit `mcp.readOnly` override) are dropped from `tools/list` — fail-safe under the
|
|
99
|
+
* principle that anything not provably read-only is treated as a write.
|
|
100
|
+
*
|
|
101
|
+
* The advertised `serverName` on the JSON-RPC `initialize` handshake is suffixed with
|
|
102
|
+
* ` (read-only)` so the client surface reflects the mode.
|
|
103
|
+
*/ _define_property$6(this, "readOnly", void 0);
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* NestJS injection token for the optional {@link McpAuthRoleReader} provider.
|
|
107
|
+
*/ var MCP_AUTH_ROLE_READER = 'MCP_AUTH_ROLE_READER';
|
|
108
|
+
|
|
109
|
+
function _type_of$1(obj) {
|
|
110
|
+
"@swc/helpers - typeof";
|
|
111
|
+
return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
|
|
112
|
+
}
|
|
113
|
+
function __decorate(decorators, target, key, desc) {
|
|
114
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
115
|
+
if ((typeof Reflect === "undefined" ? "undefined" : _type_of$1(Reflect)) === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
116
|
+
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;
|
|
117
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
118
|
+
}
|
|
119
|
+
function __param(paramIndex, decorator) {
|
|
120
|
+
return function(target, key) {
|
|
121
|
+
decorator(target, key, paramIndex);
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
typeof SuppressedError === "function" ? SuppressedError : function(error, suppressed, message) {
|
|
125
|
+
var e = new Error(message);
|
|
126
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Version stamp embedded in the build-time MCP manifest JSON. Runtime loaders
|
|
131
|
+
* refuse manifests whose `version` does not match this constant.
|
|
132
|
+
*
|
|
133
|
+
* Mirrors {@link MCP_MANIFEST_VERSION} in `@dereekb/dbx-cli` — both packages
|
|
134
|
+
* version-bump together when the manifest shape changes.
|
|
135
|
+
*/ var MCP_MANIFEST_VERSION = 1;
|
|
136
|
+
/**
|
|
137
|
+
* Builds the canonical MCP manifest key for a (modelType, callType, specifier) triple.
|
|
138
|
+
*
|
|
139
|
+
* Default-specifier entries collapse to `_`. Must stay in sync with the build-time
|
|
140
|
+
* helper of the same name in `@dereekb/dbx-cli`.
|
|
141
|
+
*
|
|
142
|
+
* @param modelType - The Firestore model type segment of the key.
|
|
143
|
+
* @param call - The call type segment of the key.
|
|
144
|
+
* @param specifier - The specifier segment, or `_` / undefined for the default entry.
|
|
145
|
+
* @returns The canonical `modelType.call.specifier` manifest key.
|
|
146
|
+
*/ function mcpManifestKey(modelType, call, specifier) {
|
|
147
|
+
var isDefault = specifier == null || specifier === '_';
|
|
148
|
+
return isDefault ? "".concat(modelType, ".").concat(call, "._") : "".concat(modelType, ".").concat(call, ".").concat(specifier);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function _instanceof(left, right) {
|
|
152
|
+
"@swc/helpers - instanceof";
|
|
153
|
+
if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
|
|
154
|
+
return !!right[Symbol.hasInstance](left);
|
|
155
|
+
} else {
|
|
156
|
+
return left instanceof right;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Default structured value emitted by MCP when a handler returns `undefined`.
|
|
161
|
+
*
|
|
162
|
+
* Keeps the response usable by clients that key off either the text content or the
|
|
163
|
+
* structured/object body. Tests and any future overrides should compare against this
|
|
164
|
+
* constant rather than re-literalling `{ ok: true }`.
|
|
165
|
+
*/ var DEFAULT_VOID_MCP_SUCCESS_VALUE = Object.freeze({
|
|
166
|
+
ok: true
|
|
167
|
+
});
|
|
168
|
+
/**
|
|
169
|
+
* Resolves a dispatch result + handler API details into the MCP `CallToolResult` shape.
|
|
170
|
+
*
|
|
171
|
+
* Three-tier resolution as documented on {@link OnCallModelFunctionApiDetails.mcp}:
|
|
172
|
+
*
|
|
173
|
+
* - **Tier 3** — when `mcp.formatResponse` is set, its return value is used verbatim.
|
|
174
|
+
* - **Tier 2** — when `mcp.summarizeResponse` is set, the summary string is wrapped into a
|
|
175
|
+
* single text content block with the raw `result` exposed as `structuredContent`.
|
|
176
|
+
* - **Tier 1** — default: JSON-stringify `result` as a single text content block, also
|
|
177
|
+
* exposing the raw value as `structuredContent`.
|
|
178
|
+
*
|
|
179
|
+
* @param result - The handler's return value.
|
|
180
|
+
* @param params - The {@link OnCallTypedModelParams} that were dispatched.
|
|
181
|
+
* @param details - The handler-level API details (carries Tier 2/3 formatters).
|
|
182
|
+
* @returns The MCP tool response content.
|
|
183
|
+
*/ function formatMcpToolResponse(result, params, details) {
|
|
184
|
+
var mcp = details === null || details === void 0 ? void 0 : details.mcp;
|
|
185
|
+
var response;
|
|
186
|
+
if (mcp === null || mcp === void 0 ? void 0 : mcp.formatResponse) {
|
|
187
|
+
response = mcp.formatResponse(result, params);
|
|
188
|
+
} else if (mcp === null || mcp === void 0 ? void 0 : mcp.summarizeResponse) {
|
|
189
|
+
var summary = mcp.summarizeResponse(result, params);
|
|
190
|
+
response = {
|
|
191
|
+
content: [
|
|
192
|
+
{
|
|
193
|
+
type: 'text',
|
|
194
|
+
text: summary
|
|
195
|
+
}
|
|
196
|
+
],
|
|
197
|
+
structuredContent: result
|
|
198
|
+
};
|
|
199
|
+
} else {
|
|
200
|
+
response = {
|
|
201
|
+
content: [
|
|
202
|
+
{
|
|
203
|
+
type: 'text',
|
|
204
|
+
text: stringifyResult(result)
|
|
205
|
+
}
|
|
206
|
+
],
|
|
207
|
+
structuredContent: result === undefined ? DEFAULT_VOID_MCP_SUCCESS_VALUE : result
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
return response;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Converts an error thrown from the dispatch chain into the MCP error response shape.
|
|
214
|
+
*
|
|
215
|
+
* @param error - The thrown error.
|
|
216
|
+
* @returns An MCP tool response with `isError: true` and the error message as a text block.
|
|
217
|
+
*/ function formatMcpToolErrorResponse(error) {
|
|
218
|
+
var message = _instanceof(error, Error) ? error.message : String(error);
|
|
219
|
+
return {
|
|
220
|
+
isError: true,
|
|
221
|
+
content: [
|
|
222
|
+
{
|
|
223
|
+
type: 'text',
|
|
224
|
+
text: message
|
|
225
|
+
}
|
|
226
|
+
]
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
function stringifyResult(result) {
|
|
230
|
+
var text;
|
|
231
|
+
if (result === undefined) {
|
|
232
|
+
text = JSON.stringify(DEFAULT_VOID_MCP_SUCCESS_VALUE);
|
|
233
|
+
} else if (typeof result === 'string') {
|
|
234
|
+
text = result;
|
|
235
|
+
} else {
|
|
236
|
+
try {
|
|
237
|
+
text = JSON.stringify(result, null, 2);
|
|
238
|
+
} catch (unused) {
|
|
239
|
+
text = inspect(result, {
|
|
240
|
+
depth: 4
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return text;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Classifies a {@link McpToolVisibility} value into its normalized boot-time form.
|
|
249
|
+
*
|
|
250
|
+
* Defaults `undefined` to `'always'` so handlers without a visibility field stay visible.
|
|
251
|
+
*
|
|
252
|
+
* @param visibility - The raw visibility value from the handler's `mcp.visibility` field.
|
|
253
|
+
* @returns The discriminated classification used by the per-request filter loop.
|
|
254
|
+
*/ function classifyVisibility(visibility) {
|
|
255
|
+
var result;
|
|
256
|
+
if (visibility == null || visibility === true) {
|
|
257
|
+
result = {
|
|
258
|
+
visibilityKind: 'always'
|
|
259
|
+
};
|
|
260
|
+
} else if (visibility === false) {
|
|
261
|
+
result = {
|
|
262
|
+
visibilityKind: 'never'
|
|
263
|
+
};
|
|
264
|
+
} else if (typeof visibility === 'function') {
|
|
265
|
+
result = {
|
|
266
|
+
visibilityKind: 'dynamic',
|
|
267
|
+
visibilityFn: visibility
|
|
268
|
+
};
|
|
269
|
+
} else {
|
|
270
|
+
result = {
|
|
271
|
+
visibilityKind: 'declarative',
|
|
272
|
+
rule: visibility
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
return result;
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Inference table from CRUD call type to "is this a read operation?".
|
|
279
|
+
*
|
|
280
|
+
* `read` and `query` → true. `create`/`update`/`delete` → false. Anything else → undefined.
|
|
281
|
+
* Unknown call types (custom verbs, `invoke`) intentionally leave `effectiveReadOnly`
|
|
282
|
+
* undefined so the module-level `readOnly` filter treats them as writes (fail-safe).
|
|
283
|
+
*/ var READ_ONLY_BY_CALL_TYPE = {
|
|
284
|
+
read: true,
|
|
285
|
+
query: true,
|
|
286
|
+
create: false,
|
|
287
|
+
update: false,
|
|
288
|
+
delete: false
|
|
289
|
+
};
|
|
290
|
+
/**
|
|
291
|
+
* Resolves the effective read-only classification for a handler.
|
|
292
|
+
*
|
|
293
|
+
* Explicit handler override wins. Otherwise infers from the call type. Returns
|
|
294
|
+
* `undefined` when neither source provides a definite classification.
|
|
295
|
+
*
|
|
296
|
+
* @param explicitReadOnly - The handler's explicit `mcp.readOnly` value, if any.
|
|
297
|
+
* @param callType - The dispatch call type used to infer read-only when no override is present.
|
|
298
|
+
* @returns The effective read-only flag, or `undefined` when neither source resolves a value.
|
|
299
|
+
*/ function resolveEffectiveReadOnly(explicitReadOnly, callType) {
|
|
300
|
+
if (explicitReadOnly != null) {
|
|
301
|
+
return explicitReadOnly;
|
|
302
|
+
}
|
|
303
|
+
return READ_ONLY_BY_CALL_TYPE[callType];
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Resolves the OIDC scope required to invoke a given call type, or `undefined` for
|
|
307
|
+
* non-CRUD calls. Thin re-export so the tool generator doesn't need to reach into
|
|
308
|
+
* `@dereekb/firebase` directly.
|
|
309
|
+
*
|
|
310
|
+
* @param callType - The dispatch call type to map to a CRUD OIDC scope.
|
|
311
|
+
* @returns The required scope for this call type, or `undefined` when no scope is enforced.
|
|
312
|
+
*/ function resolveRequiredScope(callType) {
|
|
313
|
+
return callModelOidcScopeForCallType(callType);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function _array_like_to_array$4(arr, len) {
|
|
317
|
+
if (len == null || len > arr.length) len = arr.length;
|
|
318
|
+
for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
|
|
319
|
+
return arr2;
|
|
320
|
+
}
|
|
321
|
+
function _array_with_holes$2(arr) {
|
|
322
|
+
if (Array.isArray(arr)) return arr;
|
|
323
|
+
}
|
|
324
|
+
function _iterable_to_array_limit$2(arr, i) {
|
|
325
|
+
var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
|
|
326
|
+
if (_i == null) return;
|
|
327
|
+
var _arr = [];
|
|
328
|
+
var _n = true;
|
|
329
|
+
var _d = false;
|
|
330
|
+
var _s, _e;
|
|
331
|
+
try {
|
|
332
|
+
for(_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true){
|
|
333
|
+
_arr.push(_s.value);
|
|
334
|
+
if (i && _arr.length === i) break;
|
|
335
|
+
}
|
|
336
|
+
} catch (err) {
|
|
337
|
+
_d = true;
|
|
338
|
+
_e = err;
|
|
339
|
+
} finally{
|
|
340
|
+
try {
|
|
341
|
+
if (!_n && _i["return"] != null) _i["return"]();
|
|
342
|
+
} finally{
|
|
343
|
+
if (_d) throw _e;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return _arr;
|
|
347
|
+
}
|
|
348
|
+
function _non_iterable_rest$2() {
|
|
349
|
+
throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
350
|
+
}
|
|
351
|
+
function _sliced_to_array$2(arr, i) {
|
|
352
|
+
return _array_with_holes$2(arr) || _iterable_to_array_limit$2(arr, i) || _unsupported_iterable_to_array$4(arr, i) || _non_iterable_rest$2();
|
|
353
|
+
}
|
|
354
|
+
function _type_of(obj) {
|
|
355
|
+
"@swc/helpers - typeof";
|
|
356
|
+
return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
|
|
357
|
+
}
|
|
358
|
+
function _unsupported_iterable_to_array$4(o, minLen) {
|
|
359
|
+
if (!o) return;
|
|
360
|
+
if (typeof o === "string") return _array_like_to_array$4(o, minLen);
|
|
361
|
+
var n = Object.prototype.toString.call(o).slice(8, -1);
|
|
362
|
+
if (n === "Object" && o.constructor) n = o.constructor.name;
|
|
363
|
+
if (n === "Map" || n === "Set") return Array.from(n);
|
|
364
|
+
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$4(o, minLen);
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Default `toJsonSchema()` options.
|
|
368
|
+
*
|
|
369
|
+
* ArkType throws on predicate definitions and on `undefined` fields by default;
|
|
370
|
+
* these fallback handlers downgrade those errors so a partial schema is emitted
|
|
371
|
+
* for tools that mostly fit JSON Schema. The `unit` fallback covers `clearable(t)`
|
|
372
|
+
* from `@dereekb/model` (which expands to `t | undefined` and surfaces as an ArkType
|
|
373
|
+
* `unit: undefined` schema node).
|
|
374
|
+
*/ var DEFAULT_JSON_SCHEMA_GENERATION_OPTIONS = {
|
|
375
|
+
fallback: {
|
|
376
|
+
predicate: function predicate() {
|
|
377
|
+
return {};
|
|
378
|
+
},
|
|
379
|
+
unit: function unit() {
|
|
380
|
+
return {};
|
|
381
|
+
},
|
|
382
|
+
undefinedAsClearable: function undefinedAsClearable() {
|
|
383
|
+
return {};
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
/**
|
|
388
|
+
* Builds the frozen wire-shape entry used by the per-request `tools/list` hot path.
|
|
389
|
+
*
|
|
390
|
+
* Tools without an `inputSchema` default to `{ type: 'object' }` to match the runtime
|
|
391
|
+
* behaviour the factory used before the precompute optimisation existed.
|
|
392
|
+
*
|
|
393
|
+
* @param input - The tool name, description, and optional input/output schemas used to assemble the wire entry.
|
|
394
|
+
* @returns A frozen wire entry safe to share across requests.
|
|
395
|
+
*/ function buildStaticWireEntry(input) {
|
|
396
|
+
var _input_inputSchema;
|
|
397
|
+
var inputSchema = (_input_inputSchema = input.inputSchema) !== null && _input_inputSchema !== void 0 ? _input_inputSchema : {
|
|
398
|
+
type: 'object'
|
|
399
|
+
};
|
|
400
|
+
var entry = input.outputSchema == null ? {
|
|
401
|
+
name: input.name,
|
|
402
|
+
description: input.description,
|
|
403
|
+
inputSchema: inputSchema
|
|
404
|
+
} : {
|
|
405
|
+
name: input.name,
|
|
406
|
+
description: input.description,
|
|
407
|
+
inputSchema: inputSchema,
|
|
408
|
+
outputSchema: input.outputSchema
|
|
409
|
+
};
|
|
410
|
+
return Object.freeze(entry);
|
|
411
|
+
}
|
|
412
|
+
// MARK: Naming
|
|
413
|
+
/**
|
|
414
|
+
* The default specifier key used when a handler is not behind a specifier router.
|
|
415
|
+
*/ var DEFAULT_SPECIFIER_KEY = '_';
|
|
416
|
+
/**
|
|
417
|
+
* Builds the MCP tool name for a (modelType, callType, specifier) triple.
|
|
418
|
+
*
|
|
419
|
+
* Apps can override the auto-generated name by setting
|
|
420
|
+
* {@link OnCallModelFunctionApiDetails.mcp.name} on the handler.
|
|
421
|
+
*
|
|
422
|
+
* @param modelType - The Firestore model type (e.g., `storageFile`).
|
|
423
|
+
* @param callType - The call type (e.g., `invoke`).
|
|
424
|
+
* @param specifier - The specifier key, or `_` / undefined for the default entry.
|
|
425
|
+
* @returns The hyphen-joined tool name advertised on `tools/list`.
|
|
426
|
+
*/ function buildMcpToolName(modelType, callType, specifier) {
|
|
427
|
+
var isDefault = specifier == null || specifier === DEFAULT_SPECIFIER_KEY;
|
|
428
|
+
return isDefault ? "".concat(modelType, "-").concat(callType) : "".concat(modelType, "-").concat(callType, "-").concat(specifier);
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Builds the default description used when no build-time MCP manifest
|
|
432
|
+
* entry is available for the (modelType, callType, specifier) tuple.
|
|
433
|
+
*
|
|
434
|
+
* @param modelType - The Firestore model type segment used in the generated description.
|
|
435
|
+
* @param callType - The call type segment used in the generated description.
|
|
436
|
+
* @param specifier - The specifier segment, or `_` / undefined for the default entry.
|
|
437
|
+
* @returns A human-readable fallback description for the tool.
|
|
438
|
+
*/ function buildDefaultMcpToolDescription(modelType, callType, specifier) {
|
|
439
|
+
var isDefault = specifier == null || specifier === DEFAULT_SPECIFIER_KEY;
|
|
440
|
+
return isDefault ? 'Performs the "'.concat(callType, '" call on the "').concat(modelType, '" model.') : 'Performs the "'.concat(callType, '" call on the "').concat(modelType, '" model with the "').concat(specifier, '" specifier.');
|
|
441
|
+
}
|
|
442
|
+
// MARK: Generation
|
|
443
|
+
/**
|
|
444
|
+
* Generates MCP tool definitions from a model-first API details tree.
|
|
445
|
+
*
|
|
446
|
+
* Walks each (modelType, callType, specifier) triple in the tree, calls
|
|
447
|
+
* `inputType.toJsonSchema(options)` for the schema, and applies any handler-level
|
|
448
|
+
* MCP `name` override. Descriptions and input/output schemas are pulled from the
|
|
449
|
+
* build-time manifest when supplied. Tools without an `inputType` are skipped and
|
|
450
|
+
* reported so callers can log the gap at startup.
|
|
451
|
+
*
|
|
452
|
+
* @param apiDetails - The model-first API details tree returned by `getModelApiDetails(callModelFn)`.
|
|
453
|
+
* @param options - Optional schema generation options forwarded to `toJsonSchema()`. Defaults to {@link DEFAULT_JSON_SCHEMA_GENERATION_OPTIONS}.
|
|
454
|
+
* @param manifest - Optional build-time manifest map; supplies overrides for descriptions and input/output schemas keyed by {@link mcpManifestKey}.
|
|
455
|
+
* @returns The list of generated tool definitions plus any skip reports.
|
|
456
|
+
*/ function generateMcpToolDefinitions(apiDetails) {
|
|
457
|
+
var options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : DEFAULT_JSON_SCHEMA_GENERATION_OPTIONS, manifest = arguments.length > 2 ? arguments[2] : void 0;
|
|
458
|
+
var tools = [];
|
|
459
|
+
var neverVisibleTools = [];
|
|
460
|
+
var skipped = [];
|
|
461
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
462
|
+
try {
|
|
463
|
+
for(var _iterator = Object.entries(apiDetails.models)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
464
|
+
var _step_value = _sliced_to_array$2(_step.value, 2), modelType = _step_value[0], modelEntry = _step_value[1];
|
|
465
|
+
var _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
|
|
466
|
+
try {
|
|
467
|
+
for(var _iterator1 = Object.entries(modelEntry.calls)[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
|
|
468
|
+
var _step_value1 = _sliced_to_array$2(_step1.value, 2), callType = _step_value1[0], callDetails = _step_value1[1];
|
|
469
|
+
if (callDetails == null) {
|
|
470
|
+
continue;
|
|
471
|
+
}
|
|
472
|
+
generateToolsForModelCall({
|
|
473
|
+
modelType: modelType,
|
|
474
|
+
callType: callType,
|
|
475
|
+
callDetails: callDetails,
|
|
476
|
+
options: options,
|
|
477
|
+
manifest: manifest,
|
|
478
|
+
outTools: tools,
|
|
479
|
+
outNeverVisibleTools: neverVisibleTools,
|
|
480
|
+
outSkipped: skipped
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
} catch (err) {
|
|
484
|
+
_didIteratorError1 = true;
|
|
485
|
+
_iteratorError1 = err;
|
|
486
|
+
} finally{
|
|
487
|
+
try {
|
|
488
|
+
if (!_iteratorNormalCompletion1 && _iterator1.return != null) {
|
|
489
|
+
_iterator1.return();
|
|
490
|
+
}
|
|
491
|
+
} finally{
|
|
492
|
+
if (_didIteratorError1) {
|
|
493
|
+
throw _iteratorError1;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
} catch (err) {
|
|
499
|
+
_didIteratorError = true;
|
|
500
|
+
_iteratorError = err;
|
|
501
|
+
} finally{
|
|
502
|
+
try {
|
|
503
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
504
|
+
_iterator.return();
|
|
505
|
+
}
|
|
506
|
+
} finally{
|
|
507
|
+
if (_didIteratorError) {
|
|
508
|
+
throw _iteratorError;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
return {
|
|
513
|
+
tools: tools,
|
|
514
|
+
neverVisibleTools: neverVisibleTools,
|
|
515
|
+
skipped: skipped
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
function generateToolsForModelCall(context) {
|
|
519
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
520
|
+
try {
|
|
521
|
+
for(var _iterator = Object.entries(context.callDetails.specifiers)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
522
|
+
var _step_value = _sliced_to_array$2(_step.value, 2), specifierKey = _step_value[0], handlerDetails = _step_value[1];
|
|
523
|
+
if (handlerDetails != null) {
|
|
524
|
+
generateToolForSpecifier(context, specifierKey, handlerDetails);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
} catch (err) {
|
|
528
|
+
_didIteratorError = true;
|
|
529
|
+
_iteratorError = err;
|
|
530
|
+
} finally{
|
|
531
|
+
try {
|
|
532
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
533
|
+
_iterator.return();
|
|
534
|
+
}
|
|
535
|
+
} finally{
|
|
536
|
+
if (_didIteratorError) {
|
|
537
|
+
throw _iteratorError;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
function generateToolForSpecifier(context, specifierKey, handlerDetails) {
|
|
543
|
+
var _ref, _ref1, _resolveRequiredScope;
|
|
544
|
+
var _handlerDetails_mcp, _handlerDetails_mcp1, _handlerDetails_mcp2, _handlerDetails_mcp3;
|
|
545
|
+
var modelType = context.modelType, callType = context.callType, callDetails = context.callDetails, options = context.options, manifest = context.manifest, outTools = context.outTools, outNeverVisibleTools = context.outNeverVisibleTools, outSkipped = context.outSkipped;
|
|
546
|
+
var specifier = callDetails.isSpecifier ? specifierKey : undefined;
|
|
547
|
+
var dispatch = {
|
|
548
|
+
call: callType,
|
|
549
|
+
modelType: modelType,
|
|
550
|
+
specifier: specifier
|
|
551
|
+
};
|
|
552
|
+
var name = (_ref = (_handlerDetails_mcp = handlerDetails.mcp) === null || _handlerDetails_mcp === void 0 ? void 0 : _handlerDetails_mcp.name) !== null && _ref !== void 0 ? _ref : buildMcpToolName(modelType, callType, specifier);
|
|
553
|
+
var manifestEntry = manifest === null || manifest === void 0 ? void 0 : manifest.get(mcpManifestKey(modelType, callType, specifier));
|
|
554
|
+
var description = (_ref1 = manifestEntry === null || manifestEntry === void 0 ? void 0 : manifestEntry.description) !== null && _ref1 !== void 0 ? _ref1 : buildDefaultMcpToolDescription(modelType, callType, specifier);
|
|
555
|
+
var inputSchema = resolveInputSchema({
|
|
556
|
+
handlerDetails: handlerDetails,
|
|
557
|
+
manifestEntry: manifestEntry,
|
|
558
|
+
options: options,
|
|
559
|
+
dispatch: dispatch,
|
|
560
|
+
toolName: name,
|
|
561
|
+
outSkipped: outSkipped
|
|
562
|
+
});
|
|
563
|
+
if (inputSchema == null) {
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
var classified = classifyVisibility((_handlerDetails_mcp1 = handlerDetails.mcp) === null || _handlerDetails_mcp1 === void 0 ? void 0 : _handlerDetails_mcp1.visibility);
|
|
567
|
+
var requiredScope = (_resolveRequiredScope = resolveRequiredScope(callType)) !== null && _resolveRequiredScope !== void 0 ? _resolveRequiredScope : undefined;
|
|
568
|
+
var effectiveReadOnly = resolveEffectiveReadOnly((_handlerDetails_mcp2 = handlerDetails.mcp) === null || _handlerDetails_mcp2 === void 0 ? void 0 : _handlerDetails_mcp2.readOnly, callType);
|
|
569
|
+
var filterMetadata;
|
|
570
|
+
if (classified.visibilityKind === 'declarative') {
|
|
571
|
+
filterMetadata = {
|
|
572
|
+
visibilityKind: 'declarative',
|
|
573
|
+
rule: classified.rule,
|
|
574
|
+
requiredScope: requiredScope,
|
|
575
|
+
effectiveReadOnly: effectiveReadOnly
|
|
576
|
+
};
|
|
577
|
+
} else if (classified.visibilityKind === 'dynamic') {
|
|
578
|
+
filterMetadata = {
|
|
579
|
+
visibilityKind: 'dynamic',
|
|
580
|
+
visibilityFn: classified.visibilityFn,
|
|
581
|
+
requiredScope: requiredScope,
|
|
582
|
+
effectiveReadOnly: effectiveReadOnly
|
|
583
|
+
};
|
|
584
|
+
} else {
|
|
585
|
+
filterMetadata = {
|
|
586
|
+
visibilityKind: classified.visibilityKind,
|
|
587
|
+
requiredScope: requiredScope,
|
|
588
|
+
effectiveReadOnly: effectiveReadOnly
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
var outputSchema = manifestEntry === null || manifestEntry === void 0 ? void 0 : manifestEntry.outputSchema;
|
|
592
|
+
var staticWireEntry = buildStaticWireEntry({
|
|
593
|
+
name: name,
|
|
594
|
+
description: description,
|
|
595
|
+
inputSchema: inputSchema,
|
|
596
|
+
outputSchema: outputSchema
|
|
597
|
+
});
|
|
598
|
+
var definition = {
|
|
599
|
+
name: name,
|
|
600
|
+
description: description,
|
|
601
|
+
inputSchema: inputSchema,
|
|
602
|
+
outputSchema: outputSchema,
|
|
603
|
+
details: handlerDetails,
|
|
604
|
+
dispatch: dispatch,
|
|
605
|
+
filterMetadata: filterMetadata,
|
|
606
|
+
staticWireEntry: staticWireEntry,
|
|
607
|
+
toolDetailsBuilder: (_handlerDetails_mcp3 = handlerDetails.mcp) === null || _handlerDetails_mcp3 === void 0 ? void 0 : _handlerDetails_mcp3.toolDetails
|
|
608
|
+
};
|
|
609
|
+
if (filterMetadata.visibilityKind === 'never') {
|
|
610
|
+
outNeverVisibleTools.push(definition);
|
|
611
|
+
} else {
|
|
612
|
+
outTools.push(definition);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
function resolveInputSchema(context) {
|
|
616
|
+
var handlerDetails = context.handlerDetails, manifestEntry = context.manifestEntry, options = context.options, dispatch = context.dispatch, toolName = context.toolName, outSkipped = context.outSkipped;
|
|
617
|
+
if ((manifestEntry === null || manifestEntry === void 0 ? void 0 : manifestEntry.inputSchema) != null) {
|
|
618
|
+
return manifestEntry.inputSchema;
|
|
619
|
+
}
|
|
620
|
+
if (handlerDetails.inputType == null) {
|
|
621
|
+
outSkipped.push({
|
|
622
|
+
toolName: toolName,
|
|
623
|
+
reason: 'missing_input_type',
|
|
624
|
+
dispatch: dispatch
|
|
625
|
+
});
|
|
626
|
+
return undefined;
|
|
627
|
+
}
|
|
628
|
+
var result;
|
|
629
|
+
try {
|
|
630
|
+
var exported = arktypeToJsonSchemaForExport(handlerDetails.inputType);
|
|
631
|
+
if (exported && (typeof exported === "undefined" ? "undefined" : _type_of(exported)) === 'object') {
|
|
632
|
+
result = exported;
|
|
633
|
+
} else {
|
|
634
|
+
// arktypeToJsonSchemaForExport returned a non-object (shouldn't happen for object schemas).
|
|
635
|
+
// Fall back to the raw arktype output preserving the legacy options for safety.
|
|
636
|
+
result = handlerDetails.inputType.toJsonSchema(options);
|
|
637
|
+
}
|
|
638
|
+
} catch (error) {
|
|
639
|
+
outSkipped.push({
|
|
640
|
+
toolName: toolName,
|
|
641
|
+
reason: 'schema_generation_failed',
|
|
642
|
+
dispatch: dispatch,
|
|
643
|
+
error: error
|
|
644
|
+
});
|
|
645
|
+
result = undefined;
|
|
646
|
+
}
|
|
647
|
+
return result;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
function _array_like_to_array$3(arr, len) {
|
|
651
|
+
if (len == null || len > arr.length) len = arr.length;
|
|
652
|
+
for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
|
|
653
|
+
return arr2;
|
|
654
|
+
}
|
|
655
|
+
function _array_without_holes$3(arr) {
|
|
656
|
+
if (Array.isArray(arr)) return _array_like_to_array$3(arr);
|
|
657
|
+
}
|
|
658
|
+
function asyncGeneratorStep$3(gen, resolve, reject, _next, _throw, key, arg) {
|
|
659
|
+
try {
|
|
660
|
+
var info = gen[key](arg);
|
|
661
|
+
var value = info.value;
|
|
662
|
+
} catch (error) {
|
|
663
|
+
reject(error);
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
if (info.done) {
|
|
667
|
+
resolve(value);
|
|
668
|
+
} else {
|
|
669
|
+
Promise.resolve(value).then(_next, _throw);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
function _async_to_generator$3(fn) {
|
|
673
|
+
return function() {
|
|
674
|
+
var self = this, args = arguments;
|
|
675
|
+
return new Promise(function(resolve, reject) {
|
|
676
|
+
var gen = fn.apply(self, args);
|
|
677
|
+
function _next(value) {
|
|
678
|
+
asyncGeneratorStep$3(gen, resolve, reject, _next, _throw, "next", value);
|
|
679
|
+
}
|
|
680
|
+
function _throw(err) {
|
|
681
|
+
asyncGeneratorStep$3(gen, resolve, reject, _next, _throw, "throw", err);
|
|
682
|
+
}
|
|
683
|
+
_next(undefined);
|
|
684
|
+
});
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
function _iterable_to_array$3(iter) {
|
|
688
|
+
if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
|
|
689
|
+
}
|
|
690
|
+
function _non_iterable_spread$3() {
|
|
691
|
+
throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
692
|
+
}
|
|
693
|
+
function _to_consumable_array$3(arr) {
|
|
694
|
+
return _array_without_holes$3(arr) || _iterable_to_array$3(arr) || _unsupported_iterable_to_array$3(arr) || _non_iterable_spread$3();
|
|
695
|
+
}
|
|
696
|
+
function _unsupported_iterable_to_array$3(o, minLen) {
|
|
697
|
+
if (!o) return;
|
|
698
|
+
if (typeof o === "string") return _array_like_to_array$3(o, minLen);
|
|
699
|
+
var n = Object.prototype.toString.call(o).slice(8, -1);
|
|
700
|
+
if (n === "Object" && o.constructor) n = o.constructor.name;
|
|
701
|
+
if (n === "Map" || n === "Set") return Array.from(n);
|
|
702
|
+
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$3(o, minLen);
|
|
703
|
+
}
|
|
704
|
+
function _ts_generator$3(thisArg, body) {
|
|
705
|
+
var f, y, t, _ = {
|
|
706
|
+
label: 0,
|
|
707
|
+
sent: function() {
|
|
708
|
+
if (t[0] & 1) throw t[1];
|
|
709
|
+
return t[1];
|
|
710
|
+
},
|
|
711
|
+
trys: [],
|
|
712
|
+
ops: []
|
|
713
|
+
}, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype), d = Object.defineProperty;
|
|
714
|
+
return d(g, "next", {
|
|
715
|
+
value: verb(0)
|
|
716
|
+
}), d(g, "throw", {
|
|
717
|
+
value: verb(1)
|
|
718
|
+
}), d(g, "return", {
|
|
719
|
+
value: verb(2)
|
|
720
|
+
}), typeof Symbol === "function" && d(g, Symbol.iterator, {
|
|
721
|
+
value: function() {
|
|
722
|
+
return this;
|
|
723
|
+
}
|
|
724
|
+
}), g;
|
|
725
|
+
function verb(n) {
|
|
726
|
+
return function(v) {
|
|
727
|
+
return step([
|
|
728
|
+
n,
|
|
729
|
+
v
|
|
730
|
+
]);
|
|
731
|
+
};
|
|
732
|
+
}
|
|
733
|
+
function step(op) {
|
|
734
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
735
|
+
while(g && (g = 0, op[0] && (_ = 0)), _)try {
|
|
736
|
+
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;
|
|
737
|
+
if (y = 0, t) op = [
|
|
738
|
+
op[0] & 2,
|
|
739
|
+
t.value
|
|
740
|
+
];
|
|
741
|
+
switch(op[0]){
|
|
742
|
+
case 0:
|
|
743
|
+
case 1:
|
|
744
|
+
t = op;
|
|
745
|
+
break;
|
|
746
|
+
case 4:
|
|
747
|
+
_.label++;
|
|
748
|
+
return {
|
|
749
|
+
value: op[1],
|
|
750
|
+
done: false
|
|
751
|
+
};
|
|
752
|
+
case 5:
|
|
753
|
+
_.label++;
|
|
754
|
+
y = op[1];
|
|
755
|
+
op = [
|
|
756
|
+
0
|
|
757
|
+
];
|
|
758
|
+
continue;
|
|
759
|
+
case 7:
|
|
760
|
+
op = _.ops.pop();
|
|
761
|
+
_.trys.pop();
|
|
762
|
+
continue;
|
|
763
|
+
default:
|
|
764
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
|
|
765
|
+
_ = 0;
|
|
766
|
+
continue;
|
|
767
|
+
}
|
|
768
|
+
if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) {
|
|
769
|
+
_.label = op[1];
|
|
770
|
+
break;
|
|
771
|
+
}
|
|
772
|
+
if (op[0] === 6 && _.label < t[1]) {
|
|
773
|
+
_.label = t[1];
|
|
774
|
+
t = op;
|
|
775
|
+
break;
|
|
776
|
+
}
|
|
777
|
+
if (t && _.label < t[2]) {
|
|
778
|
+
_.label = t[2];
|
|
779
|
+
_.ops.push(op);
|
|
780
|
+
break;
|
|
781
|
+
}
|
|
782
|
+
if (t[2]) _.ops.pop();
|
|
783
|
+
_.trys.pop();
|
|
784
|
+
continue;
|
|
785
|
+
}
|
|
786
|
+
op = body.call(thisArg, _);
|
|
787
|
+
} catch (e) {
|
|
788
|
+
op = [
|
|
789
|
+
6,
|
|
790
|
+
e
|
|
791
|
+
];
|
|
792
|
+
y = 0;
|
|
793
|
+
} finally{
|
|
794
|
+
f = t = 0;
|
|
795
|
+
}
|
|
796
|
+
if (op[0] & 5) throw op[1];
|
|
797
|
+
return {
|
|
798
|
+
value: op[0] ? op[1] : void 0,
|
|
799
|
+
done: true
|
|
800
|
+
};
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
// MARK: Constants
|
|
804
|
+
/**
|
|
805
|
+
* Reserved tool name for the built-in `model-get` static tool.
|
|
806
|
+
*/ var MODEL_GET_TOOL_NAME = 'model-get';
|
|
807
|
+
/**
|
|
808
|
+
* Synthetic call type used in the tool's dispatch identity. Aligns with the dbx-cli `get`
|
|
809
|
+
* command and the `/api/model/:modelType/get` HTTP endpoint that backs it.
|
|
810
|
+
*/ var MODEL_GET_DISPATCH_CALL = 'get';
|
|
811
|
+
/**
|
|
812
|
+
* Synthetic model type used in the tool's dispatch identity. The tool itself isn't bound to a
|
|
813
|
+
* specific model type (the `modelType` is part of its input), so we use the literal "model" as
|
|
814
|
+
* the identity. Apps avoiding collision should not register a real model literally named
|
|
815
|
+
* "model".
|
|
816
|
+
*/ var MODEL_GET_DISPATCH_MODEL_TYPE = 'model';
|
|
817
|
+
/**
|
|
818
|
+
* Maximum number of keys per backend multi-read request. Matches
|
|
819
|
+
* `MAX_MODEL_ACCESS_MULTI_READ_KEYS` exported from `@dereekb/firebase-server`; mirrored as a
|
|
820
|
+
* local constant so the bundler doesn't have to follow the import chain just for one number.
|
|
821
|
+
*/ var MCP_MODEL_GET_BATCH_SIZE = 50;
|
|
822
|
+
// MARK: Factory
|
|
823
|
+
/**
|
|
824
|
+
* Builds the built-in `model-get` MCP tool definition.
|
|
825
|
+
*
|
|
826
|
+
* Mirrors dbx-cli's `get` / `get-many` behavior:
|
|
827
|
+
* - Accepts an array of keys; values containing `/` are treated as full keys and passed verbatim
|
|
828
|
+
* while bare ids are promoted to `${collectionName}/${id}` for root models.
|
|
829
|
+
* - Nested (subcollection) models reject bare ids since a parent path is required.
|
|
830
|
+
* - Auto-chunks at {@link MCP_MODEL_GET_BATCH_SIZE} keys per backend call, mirroring
|
|
831
|
+
* `getMultipleModelsOverHttpChunked` on the CLI side.
|
|
832
|
+
*
|
|
833
|
+
* The handler returns the same `{ results, errors }` shape produced by
|
|
834
|
+
* `ModelApiGetService.readDocuments`, exposed via both the text content block and
|
|
835
|
+
* `structuredContent` so MCP clients can consume either form.
|
|
836
|
+
*
|
|
837
|
+
* @param deps - Read-documents and identity-resolver callbacks (typically wired to
|
|
838
|
+
* `ModelApiGetService`).
|
|
839
|
+
* @returns A statically-registered {@link McpToolDefinition} ready to be appended to the MCP
|
|
840
|
+
* server factory's tool registry.
|
|
841
|
+
*/ function createModelGetTool(deps) {
|
|
842
|
+
var handler = function handler(args, ctx) {
|
|
843
|
+
return modelGetToolHandler(args, ctx, deps);
|
|
844
|
+
};
|
|
845
|
+
var name = MODEL_GET_TOOL_NAME;
|
|
846
|
+
var description = 'Fetch one or more Firestore model documents by key or bare id. Values containing `/` are treated as full keys; bare ids are auto-promoted to `<collectionName>/<id>` for root models. Subcollection models require full keys.';
|
|
847
|
+
return {
|
|
848
|
+
name: name,
|
|
849
|
+
description: description,
|
|
850
|
+
inputSchema: MODEL_GET_INPUT_SCHEMA,
|
|
851
|
+
outputSchema: MODEL_GET_OUTPUT_SCHEMA,
|
|
852
|
+
dispatch: {
|
|
853
|
+
call: MODEL_GET_DISPATCH_CALL,
|
|
854
|
+
modelType: MODEL_GET_DISPATCH_MODEL_TYPE
|
|
855
|
+
},
|
|
856
|
+
staticHandler: handler,
|
|
857
|
+
filterMetadata: {
|
|
858
|
+
visibilityKind: 'declarative',
|
|
859
|
+
rule: {
|
|
860
|
+
requireAuthenticated: true
|
|
861
|
+
},
|
|
862
|
+
effectiveReadOnly: true
|
|
863
|
+
},
|
|
864
|
+
staticWireEntry: buildStaticWireEntry({
|
|
865
|
+
name: name,
|
|
866
|
+
description: description,
|
|
867
|
+
inputSchema: MODEL_GET_INPUT_SCHEMA,
|
|
868
|
+
outputSchema: MODEL_GET_OUTPUT_SCHEMA
|
|
869
|
+
})
|
|
870
|
+
};
|
|
871
|
+
}
|
|
872
|
+
// MARK: Handler
|
|
873
|
+
function modelGetToolHandler(args, ctx, deps) {
|
|
874
|
+
return _async_to_generator$3(function() {
|
|
875
|
+
var result, input, identity, resolvedKeys, merged, error;
|
|
876
|
+
return _ts_generator$3(this, function(_state) {
|
|
877
|
+
switch(_state.label){
|
|
878
|
+
case 0:
|
|
879
|
+
_state.trys.push([
|
|
880
|
+
0,
|
|
881
|
+
2,
|
|
882
|
+
,
|
|
883
|
+
3
|
|
884
|
+
]);
|
|
885
|
+
input = parseModelGetInput(args);
|
|
886
|
+
identity = deps.resolveIdentity(input.modelType, ctx.auth);
|
|
887
|
+
if (identity == null) {
|
|
888
|
+
throw new Error("Unknown modelType: ".concat(input.modelType));
|
|
889
|
+
}
|
|
890
|
+
resolvedKeys = resolveKeys(input.keys, identity);
|
|
891
|
+
return [
|
|
892
|
+
4,
|
|
893
|
+
readInChunks({
|
|
894
|
+
modelType: input.modelType,
|
|
895
|
+
keys: resolvedKeys,
|
|
896
|
+
auth: ctx.auth,
|
|
897
|
+
readDocuments: deps.readDocuments
|
|
898
|
+
})
|
|
899
|
+
];
|
|
900
|
+
case 1:
|
|
901
|
+
merged = _state.sent();
|
|
902
|
+
result = {
|
|
903
|
+
content: [
|
|
904
|
+
{
|
|
905
|
+
type: 'text',
|
|
906
|
+
text: JSON.stringify(merged)
|
|
907
|
+
}
|
|
908
|
+
],
|
|
909
|
+
structuredContent: merged
|
|
910
|
+
};
|
|
911
|
+
return [
|
|
912
|
+
3,
|
|
913
|
+
3
|
|
914
|
+
];
|
|
915
|
+
case 2:
|
|
916
|
+
error = _state.sent();
|
|
917
|
+
result = formatMcpToolErrorResponse(error);
|
|
918
|
+
return [
|
|
919
|
+
3,
|
|
920
|
+
3
|
|
921
|
+
];
|
|
922
|
+
case 3:
|
|
923
|
+
return [
|
|
924
|
+
2,
|
|
925
|
+
result
|
|
926
|
+
];
|
|
927
|
+
}
|
|
928
|
+
});
|
|
929
|
+
})();
|
|
930
|
+
}
|
|
931
|
+
function parseModelGetInput(args) {
|
|
932
|
+
var modelType = args.modelType;
|
|
933
|
+
var keys = args.keys;
|
|
934
|
+
if (typeof modelType !== 'string' || modelType.length === 0) {
|
|
935
|
+
throw new Error('model-get: "modelType" is required and must be a non-empty string.');
|
|
936
|
+
}
|
|
937
|
+
if (!Array.isArray(keys) || keys.length === 0) {
|
|
938
|
+
throw new Error('model-get: "keys" is required and must be a non-empty array.');
|
|
939
|
+
}
|
|
940
|
+
var normalizedKeys = keys.map(function(value, index) {
|
|
941
|
+
if (typeof value !== 'string' || value.length === 0) {
|
|
942
|
+
throw new Error("model-get: keys[".concat(index, "] must be a non-empty string."));
|
|
943
|
+
}
|
|
944
|
+
return value;
|
|
945
|
+
});
|
|
946
|
+
return {
|
|
947
|
+
modelType: modelType,
|
|
948
|
+
keys: normalizedKeys
|
|
949
|
+
};
|
|
950
|
+
}
|
|
951
|
+
function resolveKeys(keys, identity) {
|
|
952
|
+
var isRoot = identity.type === 'root';
|
|
953
|
+
var resolved = [];
|
|
954
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
955
|
+
try {
|
|
956
|
+
for(var _iterator = keys[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
957
|
+
var value = _step.value;
|
|
958
|
+
if (value.includes('/')) {
|
|
959
|
+
resolved.push(value);
|
|
960
|
+
} else if (isRoot) {
|
|
961
|
+
resolved.push("".concat(identity.collectionName, "/").concat(value));
|
|
962
|
+
} else {
|
|
963
|
+
throw new Error('model-get: modelType "'.concat(identity.modelType, '" is a subcollection; bare ids are not allowed. Provide full keys (e.g. "parentPrefix/parentId/').concat(identity.collectionName, "/").concat(value, '").'));
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
} catch (err) {
|
|
967
|
+
_didIteratorError = true;
|
|
968
|
+
_iteratorError = err;
|
|
969
|
+
} finally{
|
|
970
|
+
try {
|
|
971
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
972
|
+
_iterator.return();
|
|
973
|
+
}
|
|
974
|
+
} finally{
|
|
975
|
+
if (_didIteratorError) {
|
|
976
|
+
throw _iteratorError;
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
return resolved;
|
|
981
|
+
}
|
|
982
|
+
function readInChunks(input) {
|
|
983
|
+
return _async_to_generator$3(function() {
|
|
984
|
+
var modelType, keys, auth, readDocuments, merged, offset, _merged_results, _merged_errors, batch, partial;
|
|
985
|
+
return _ts_generator$3(this, function(_state) {
|
|
986
|
+
switch(_state.label){
|
|
987
|
+
case 0:
|
|
988
|
+
modelType = input.modelType, keys = input.keys, auth = input.auth, readDocuments = input.readDocuments;
|
|
989
|
+
merged = {
|
|
990
|
+
results: [],
|
|
991
|
+
errors: []
|
|
992
|
+
};
|
|
993
|
+
offset = 0;
|
|
994
|
+
_state.label = 1;
|
|
995
|
+
case 1:
|
|
996
|
+
if (!(offset < keys.length)) return [
|
|
997
|
+
3,
|
|
998
|
+
4
|
|
999
|
+
];
|
|
1000
|
+
batch = keys.slice(offset, offset + MCP_MODEL_GET_BATCH_SIZE);
|
|
1001
|
+
return [
|
|
1002
|
+
4,
|
|
1003
|
+
readDocuments(modelType, batch, auth)
|
|
1004
|
+
];
|
|
1005
|
+
case 2:
|
|
1006
|
+
partial = _state.sent();
|
|
1007
|
+
(_merged_results = merged.results).push.apply(_merged_results, _to_consumable_array$3(partial.results));
|
|
1008
|
+
(_merged_errors = merged.errors).push.apply(_merged_errors, _to_consumable_array$3(partial.errors));
|
|
1009
|
+
_state.label = 3;
|
|
1010
|
+
case 3:
|
|
1011
|
+
offset += MCP_MODEL_GET_BATCH_SIZE;
|
|
1012
|
+
return [
|
|
1013
|
+
3,
|
|
1014
|
+
1
|
|
1015
|
+
];
|
|
1016
|
+
case 4:
|
|
1017
|
+
return [
|
|
1018
|
+
2,
|
|
1019
|
+
merged
|
|
1020
|
+
];
|
|
1021
|
+
}
|
|
1022
|
+
});
|
|
1023
|
+
})();
|
|
1024
|
+
}
|
|
1025
|
+
// MARK: Schemas
|
|
1026
|
+
var MODEL_GET_INPUT_SCHEMA = {
|
|
1027
|
+
type: 'object',
|
|
1028
|
+
required: [
|
|
1029
|
+
'modelType',
|
|
1030
|
+
'keys'
|
|
1031
|
+
],
|
|
1032
|
+
properties: {
|
|
1033
|
+
modelType: {
|
|
1034
|
+
type: 'string',
|
|
1035
|
+
minLength: 1,
|
|
1036
|
+
description: 'Firestore model type to fetch (e.g. "guestbook", "profile").'
|
|
1037
|
+
},
|
|
1038
|
+
keys: {
|
|
1039
|
+
type: 'array',
|
|
1040
|
+
minItems: 1,
|
|
1041
|
+
description: 'Full keys ("prefix/id") or bare ids (root models only).',
|
|
1042
|
+
items: {
|
|
1043
|
+
type: 'string',
|
|
1044
|
+
minLength: 1
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
},
|
|
1048
|
+
additionalProperties: false
|
|
1049
|
+
};
|
|
1050
|
+
var MODEL_GET_OUTPUT_SCHEMA = {
|
|
1051
|
+
type: 'object',
|
|
1052
|
+
required: [
|
|
1053
|
+
'results',
|
|
1054
|
+
'errors'
|
|
1055
|
+
],
|
|
1056
|
+
properties: {
|
|
1057
|
+
results: {
|
|
1058
|
+
type: 'array',
|
|
1059
|
+
description: 'Per-key successful reads.',
|
|
1060
|
+
items: {
|
|
1061
|
+
type: 'object',
|
|
1062
|
+
required: [
|
|
1063
|
+
'key',
|
|
1064
|
+
'data'
|
|
1065
|
+
],
|
|
1066
|
+
properties: {
|
|
1067
|
+
key: {
|
|
1068
|
+
type: 'string'
|
|
1069
|
+
},
|
|
1070
|
+
data: {}
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
},
|
|
1074
|
+
errors: {
|
|
1075
|
+
type: 'array',
|
|
1076
|
+
description: 'Per-key failures (not-found, forbidden, etc.).',
|
|
1077
|
+
items: {
|
|
1078
|
+
type: 'object',
|
|
1079
|
+
required: [
|
|
1080
|
+
'key',
|
|
1081
|
+
'message'
|
|
1082
|
+
],
|
|
1083
|
+
properties: {
|
|
1084
|
+
key: {
|
|
1085
|
+
type: 'string'
|
|
1086
|
+
},
|
|
1087
|
+
message: {
|
|
1088
|
+
type: 'string'
|
|
1089
|
+
},
|
|
1090
|
+
code: {
|
|
1091
|
+
type: 'string'
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
};
|
|
1098
|
+
|
|
1099
|
+
function _define_property$5(obj, key, value) {
|
|
1100
|
+
if (key in obj) {
|
|
1101
|
+
Object.defineProperty(obj, key, {
|
|
1102
|
+
value: value,
|
|
1103
|
+
enumerable: true,
|
|
1104
|
+
configurable: true,
|
|
1105
|
+
writable: true
|
|
1106
|
+
});
|
|
1107
|
+
} else {
|
|
1108
|
+
obj[key] = value;
|
|
1109
|
+
}
|
|
1110
|
+
return obj;
|
|
1111
|
+
}
|
|
1112
|
+
function _object_spread$2(target) {
|
|
1113
|
+
for(var i = 1; i < arguments.length; i++){
|
|
1114
|
+
var source = arguments[i] != null ? arguments[i] : {};
|
|
1115
|
+
var ownKeys = Object.keys(source);
|
|
1116
|
+
if (typeof Object.getOwnPropertySymbols === "function") {
|
|
1117
|
+
ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
|
|
1118
|
+
return Object.getOwnPropertyDescriptor(source, sym).enumerable;
|
|
1119
|
+
}));
|
|
1120
|
+
}
|
|
1121
|
+
ownKeys.forEach(function(key) {
|
|
1122
|
+
_define_property$5(target, key, source[key]);
|
|
1123
|
+
});
|
|
1124
|
+
}
|
|
1125
|
+
return target;
|
|
1126
|
+
}
|
|
1127
|
+
// MARK: Constants
|
|
1128
|
+
/**
|
|
1129
|
+
* Reserved tool name for the built-in `model-info` static tool.
|
|
1130
|
+
*/ var MODEL_INFO_TOOL_NAME = 'model-info';
|
|
1131
|
+
/**
|
|
1132
|
+
* Synthetic call type used in the tool's dispatch identity. Aligns with the dbx-cli `model-info`
|
|
1133
|
+
* command.
|
|
1134
|
+
*/ var MODEL_INFO_DISPATCH_CALL = 'info';
|
|
1135
|
+
/**
|
|
1136
|
+
* Synthetic model type used in the tool's dispatch identity. Mirrors {@link MODEL_GET_DISPATCH_MODEL_TYPE};
|
|
1137
|
+
* apps avoiding collisions should not register a real model literally named "model".
|
|
1138
|
+
*/ var MODEL_INFO_DISPATCH_MODEL_TYPE = 'model';
|
|
1139
|
+
// MARK: Factory
|
|
1140
|
+
/**
|
|
1141
|
+
* Builds the built-in `model-info` MCP tool definition.
|
|
1142
|
+
*
|
|
1143
|
+
* Mirrors dbx-cli's `model-info [model]` command:
|
|
1144
|
+
* - Without a `model` arg: returns a summary list (modelType, modelName, prefix, group, identity, field count).
|
|
1145
|
+
* - With a `model` arg: matches by `modelType`, `identityConst`, or `collectionPrefix` and returns the full entry.
|
|
1146
|
+
*
|
|
1147
|
+
* Output is delivered as both stringified JSON in `content[0].text` and `structuredContent`
|
|
1148
|
+
* so MCP clients can consume either form.
|
|
1149
|
+
*
|
|
1150
|
+
* @param deps - The frozen model manifest loaded at boot from the MCP manifest JSON.
|
|
1151
|
+
* @returns A statically-registered {@link McpToolDefinition} ready to be appended to the MCP
|
|
1152
|
+
* server factory's tool registry.
|
|
1153
|
+
*/ function createModelInfoTool(deps) {
|
|
1154
|
+
var handler = function handler(args, ctx) {
|
|
1155
|
+
return Promise.resolve(modelInfoToolHandler(args, ctx, deps));
|
|
1156
|
+
};
|
|
1157
|
+
var name = MODEL_INFO_TOOL_NAME;
|
|
1158
|
+
var description = "Browse the Firestore model catalog (".concat(deps.manifest.length, " model").concat(deps.manifest.length === 1 ? '' : 's', "). Without `model`, returns a summary list of every model. With `model` (matched by modelType, identityConst, or collectionPrefix), returns the full entry with persisted fields.");
|
|
1159
|
+
return {
|
|
1160
|
+
name: name,
|
|
1161
|
+
description: description,
|
|
1162
|
+
inputSchema: MODEL_INFO_INPUT_SCHEMA,
|
|
1163
|
+
outputSchema: MODEL_INFO_OUTPUT_SCHEMA,
|
|
1164
|
+
dispatch: {
|
|
1165
|
+
call: MODEL_INFO_DISPATCH_CALL,
|
|
1166
|
+
modelType: MODEL_INFO_DISPATCH_MODEL_TYPE
|
|
1167
|
+
},
|
|
1168
|
+
staticHandler: handler,
|
|
1169
|
+
filterMetadata: {
|
|
1170
|
+
visibilityKind: 'declarative',
|
|
1171
|
+
rule: {
|
|
1172
|
+
requireAuthenticated: true
|
|
1173
|
+
},
|
|
1174
|
+
effectiveReadOnly: true
|
|
1175
|
+
},
|
|
1176
|
+
staticWireEntry: buildStaticWireEntry({
|
|
1177
|
+
name: name,
|
|
1178
|
+
description: description,
|
|
1179
|
+
inputSchema: MODEL_INFO_INPUT_SCHEMA,
|
|
1180
|
+
outputSchema: MODEL_INFO_OUTPUT_SCHEMA
|
|
1181
|
+
})
|
|
1182
|
+
};
|
|
1183
|
+
}
|
|
1184
|
+
// MARK: Handler
|
|
1185
|
+
function modelInfoToolHandler(args, _ctx, deps) {
|
|
1186
|
+
var result;
|
|
1187
|
+
try {
|
|
1188
|
+
var input = parseModelInfoInput(args);
|
|
1189
|
+
var output = input.model == null ? buildListOutput(deps.manifest) : buildSingleOutput(input.model, deps.manifest);
|
|
1190
|
+
result = {
|
|
1191
|
+
content: [
|
|
1192
|
+
{
|
|
1193
|
+
type: 'text',
|
|
1194
|
+
text: JSON.stringify(output)
|
|
1195
|
+
}
|
|
1196
|
+
],
|
|
1197
|
+
structuredContent: output
|
|
1198
|
+
};
|
|
1199
|
+
} catch (error) {
|
|
1200
|
+
result = formatMcpToolErrorResponse(error);
|
|
1201
|
+
}
|
|
1202
|
+
return result;
|
|
1203
|
+
}
|
|
1204
|
+
function parseModelInfoInput(args) {
|
|
1205
|
+
var raw = args['model'];
|
|
1206
|
+
var model;
|
|
1207
|
+
if (raw == null) {
|
|
1208
|
+
model = undefined;
|
|
1209
|
+
} else if (typeof raw === 'string') {
|
|
1210
|
+
var trimmed = raw.trim();
|
|
1211
|
+
if (trimmed.length === 0) {
|
|
1212
|
+
throw new Error('model-info: "model" must be a non-empty string when provided.');
|
|
1213
|
+
}
|
|
1214
|
+
model = trimmed;
|
|
1215
|
+
} else {
|
|
1216
|
+
throw new TypeError('model-info: "model" must be a string when provided.');
|
|
1217
|
+
}
|
|
1218
|
+
return {
|
|
1219
|
+
model: model
|
|
1220
|
+
};
|
|
1221
|
+
}
|
|
1222
|
+
function buildListOutput(manifest) {
|
|
1223
|
+
var models = manifest.map(function(entry) {
|
|
1224
|
+
var row = _object_spread$2({
|
|
1225
|
+
modelType: entry.modelType,
|
|
1226
|
+
modelName: entry.modelName,
|
|
1227
|
+
identityConst: entry.identityConst,
|
|
1228
|
+
collectionPrefix: entry.collectionPrefix,
|
|
1229
|
+
sourcePackage: entry.sourcePackage,
|
|
1230
|
+
fieldCount: entry.fields.length
|
|
1231
|
+
}, entry.modelGroup == null ? {} : {
|
|
1232
|
+
modelGroup: entry.modelGroup
|
|
1233
|
+
}, entry.parentIdentityConst == null ? {} : {
|
|
1234
|
+
parentIdentityConst: entry.parentIdentityConst
|
|
1235
|
+
}, entry.description == null ? {} : {
|
|
1236
|
+
description: entry.description
|
|
1237
|
+
}, entry.read == null ? {} : {
|
|
1238
|
+
read: entry.read
|
|
1239
|
+
}, entry.serviceFactory == null ? {} : {
|
|
1240
|
+
serviceFactoryExport: entry.serviceFactory.exportName
|
|
1241
|
+
});
|
|
1242
|
+
return row;
|
|
1243
|
+
});
|
|
1244
|
+
return {
|
|
1245
|
+
mode: 'list',
|
|
1246
|
+
models: models
|
|
1247
|
+
};
|
|
1248
|
+
}
|
|
1249
|
+
function buildSingleOutput(query, manifest) {
|
|
1250
|
+
var entry = findModelEntry(query, manifest);
|
|
1251
|
+
if (entry == null) {
|
|
1252
|
+
throw new Error('model-info: no model matches "'.concat(query, '". Call `model-info` without arguments to list available models.'));
|
|
1253
|
+
}
|
|
1254
|
+
return {
|
|
1255
|
+
mode: 'single',
|
|
1256
|
+
model: entry
|
|
1257
|
+
};
|
|
1258
|
+
}
|
|
1259
|
+
/**
|
|
1260
|
+
* Resolves a manifest entry by `modelType`, `identityConst`, or `collectionPrefix`.
|
|
1261
|
+
*
|
|
1262
|
+
* @param query - Identifier to look up.
|
|
1263
|
+
* @param manifest - Model manifest to search.
|
|
1264
|
+
* @returns The matching entry or `undefined`.
|
|
1265
|
+
*
|
|
1266
|
+
* @__NO_SIDE_EFFECTS__
|
|
1267
|
+
*/ function findModelEntry(query, manifest) {
|
|
1268
|
+
var result;
|
|
1269
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
1270
|
+
try {
|
|
1271
|
+
for(var _iterator = manifest[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
1272
|
+
var entry = _step.value;
|
|
1273
|
+
if (entry.modelType === query || entry.identityConst === query || entry.collectionPrefix === query) {
|
|
1274
|
+
result = entry;
|
|
1275
|
+
break;
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
} catch (err) {
|
|
1279
|
+
_didIteratorError = true;
|
|
1280
|
+
_iteratorError = err;
|
|
1281
|
+
} finally{
|
|
1282
|
+
try {
|
|
1283
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
1284
|
+
_iterator.return();
|
|
1285
|
+
}
|
|
1286
|
+
} finally{
|
|
1287
|
+
if (_didIteratorError) {
|
|
1288
|
+
throw _iteratorError;
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
return result;
|
|
1293
|
+
}
|
|
1294
|
+
// MARK: Schemas
|
|
1295
|
+
var MODEL_INFO_INPUT_SCHEMA = {
|
|
1296
|
+
type: 'object',
|
|
1297
|
+
properties: {
|
|
1298
|
+
model: {
|
|
1299
|
+
type: 'string',
|
|
1300
|
+
minLength: 1,
|
|
1301
|
+
description: 'Model to inspect (modelType, identityConst, or collectionPrefix). Omit to list every model.'
|
|
1302
|
+
}
|
|
1303
|
+
},
|
|
1304
|
+
additionalProperties: false
|
|
1305
|
+
};
|
|
1306
|
+
// MCP's `tools/list` validator requires `outputSchema.type === 'object'` at the root
|
|
1307
|
+
// (see @modelcontextprotocol/sdk Zod schema), so the list/single union is expressed via a
|
|
1308
|
+
// top-level object whose nested `oneOf` discriminates by the `mode` literal.
|
|
1309
|
+
var MODEL_INFO_OUTPUT_SCHEMA = {
|
|
1310
|
+
type: 'object',
|
|
1311
|
+
required: [
|
|
1312
|
+
'mode'
|
|
1313
|
+
],
|
|
1314
|
+
description: 'Either lists every model in the manifest (when `model` is omitted) or returns a full entry for the requested model (when `model` is provided). The `mode` field discriminates the variant.',
|
|
1315
|
+
properties: {
|
|
1316
|
+
mode: {
|
|
1317
|
+
type: 'string',
|
|
1318
|
+
enum: [
|
|
1319
|
+
'list',
|
|
1320
|
+
'single'
|
|
1321
|
+
]
|
|
1322
|
+
},
|
|
1323
|
+
models: {
|
|
1324
|
+
type: 'array',
|
|
1325
|
+
description: 'Present when `mode` is `"list"`. One summary entry per registered model.',
|
|
1326
|
+
items: {
|
|
1327
|
+
type: 'object',
|
|
1328
|
+
required: [
|
|
1329
|
+
'modelType',
|
|
1330
|
+
'modelName',
|
|
1331
|
+
'identityConst',
|
|
1332
|
+
'collectionPrefix',
|
|
1333
|
+
'sourcePackage',
|
|
1334
|
+
'fieldCount'
|
|
1335
|
+
],
|
|
1336
|
+
properties: {
|
|
1337
|
+
modelType: {
|
|
1338
|
+
type: 'string'
|
|
1339
|
+
},
|
|
1340
|
+
modelName: {
|
|
1341
|
+
type: 'string'
|
|
1342
|
+
},
|
|
1343
|
+
modelGroup: {
|
|
1344
|
+
type: 'string'
|
|
1345
|
+
},
|
|
1346
|
+
identityConst: {
|
|
1347
|
+
type: 'string'
|
|
1348
|
+
},
|
|
1349
|
+
collectionPrefix: {
|
|
1350
|
+
type: 'string'
|
|
1351
|
+
},
|
|
1352
|
+
parentIdentityConst: {
|
|
1353
|
+
type: 'string'
|
|
1354
|
+
},
|
|
1355
|
+
sourcePackage: {
|
|
1356
|
+
type: 'string'
|
|
1357
|
+
},
|
|
1358
|
+
fieldCount: {
|
|
1359
|
+
type: 'integer'
|
|
1360
|
+
},
|
|
1361
|
+
description: {
|
|
1362
|
+
type: 'string'
|
|
1363
|
+
},
|
|
1364
|
+
read: {
|
|
1365
|
+
type: 'string',
|
|
1366
|
+
enum: [
|
|
1367
|
+
'system',
|
|
1368
|
+
'owner',
|
|
1369
|
+
'admin-only',
|
|
1370
|
+
'permissions'
|
|
1371
|
+
]
|
|
1372
|
+
},
|
|
1373
|
+
serviceFactoryExport: {
|
|
1374
|
+
type: 'string'
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
},
|
|
1379
|
+
model: {
|
|
1380
|
+
type: 'object',
|
|
1381
|
+
description: 'Present when `mode` is `"single"`. The full manifest entry for the requested model.',
|
|
1382
|
+
required: [
|
|
1383
|
+
'modelType',
|
|
1384
|
+
'modelName',
|
|
1385
|
+
'identityConst',
|
|
1386
|
+
'collectionPrefix',
|
|
1387
|
+
'sourcePackage',
|
|
1388
|
+
'sourceFile',
|
|
1389
|
+
'fields'
|
|
1390
|
+
],
|
|
1391
|
+
properties: {
|
|
1392
|
+
modelType: {
|
|
1393
|
+
type: 'string'
|
|
1394
|
+
},
|
|
1395
|
+
modelName: {
|
|
1396
|
+
type: 'string'
|
|
1397
|
+
},
|
|
1398
|
+
modelGroup: {
|
|
1399
|
+
type: 'string'
|
|
1400
|
+
},
|
|
1401
|
+
identityConst: {
|
|
1402
|
+
type: 'string'
|
|
1403
|
+
},
|
|
1404
|
+
collectionPrefix: {
|
|
1405
|
+
type: 'string'
|
|
1406
|
+
},
|
|
1407
|
+
parentIdentityConst: {
|
|
1408
|
+
type: 'string'
|
|
1409
|
+
},
|
|
1410
|
+
description: {
|
|
1411
|
+
type: 'string'
|
|
1412
|
+
},
|
|
1413
|
+
sourcePackage: {
|
|
1414
|
+
type: 'string'
|
|
1415
|
+
},
|
|
1416
|
+
sourceFile: {
|
|
1417
|
+
type: 'string'
|
|
1418
|
+
},
|
|
1419
|
+
fields: {
|
|
1420
|
+
type: 'array',
|
|
1421
|
+
items: {
|
|
1422
|
+
type: 'object'
|
|
1423
|
+
}
|
|
1424
|
+
},
|
|
1425
|
+
read: {
|
|
1426
|
+
type: 'string',
|
|
1427
|
+
enum: [
|
|
1428
|
+
'system',
|
|
1429
|
+
'owner',
|
|
1430
|
+
'admin-only',
|
|
1431
|
+
'permissions'
|
|
1432
|
+
]
|
|
1433
|
+
},
|
|
1434
|
+
serviceFactory: {
|
|
1435
|
+
type: 'object',
|
|
1436
|
+
required: [
|
|
1437
|
+
'exportName',
|
|
1438
|
+
'sourceFile'
|
|
1439
|
+
],
|
|
1440
|
+
properties: {
|
|
1441
|
+
exportName: {
|
|
1442
|
+
type: 'string'
|
|
1443
|
+
},
|
|
1444
|
+
sourceFile: {
|
|
1445
|
+
type: 'string'
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
},
|
|
1452
|
+
oneOf: [
|
|
1453
|
+
{
|
|
1454
|
+
required: [
|
|
1455
|
+
'models'
|
|
1456
|
+
],
|
|
1457
|
+
properties: {
|
|
1458
|
+
mode: {
|
|
1459
|
+
const: 'list'
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
},
|
|
1463
|
+
{
|
|
1464
|
+
required: [
|
|
1465
|
+
'model'
|
|
1466
|
+
],
|
|
1467
|
+
properties: {
|
|
1468
|
+
mode: {
|
|
1469
|
+
const: 'single'
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
]
|
|
1474
|
+
};
|
|
1475
|
+
|
|
1476
|
+
function _define_property$4(obj, key, value) {
|
|
1477
|
+
if (key in obj) {
|
|
1478
|
+
Object.defineProperty(obj, key, {
|
|
1479
|
+
value: value,
|
|
1480
|
+
enumerable: true,
|
|
1481
|
+
configurable: true,
|
|
1482
|
+
writable: true
|
|
1483
|
+
});
|
|
1484
|
+
} else {
|
|
1485
|
+
obj[key] = value;
|
|
1486
|
+
}
|
|
1487
|
+
return obj;
|
|
1488
|
+
}
|
|
1489
|
+
function _object_spread$1(target) {
|
|
1490
|
+
for(var i = 1; i < arguments.length; i++){
|
|
1491
|
+
var source = arguments[i] != null ? arguments[i] : {};
|
|
1492
|
+
var ownKeys = Object.keys(source);
|
|
1493
|
+
if (typeof Object.getOwnPropertySymbols === "function") {
|
|
1494
|
+
ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
|
|
1495
|
+
return Object.getOwnPropertyDescriptor(source, sym).enumerable;
|
|
1496
|
+
}));
|
|
1497
|
+
}
|
|
1498
|
+
ownKeys.forEach(function(key) {
|
|
1499
|
+
_define_property$4(target, key, source[key]);
|
|
1500
|
+
});
|
|
1501
|
+
}
|
|
1502
|
+
return target;
|
|
1503
|
+
}
|
|
1504
|
+
// MARK: Constants
|
|
1505
|
+
/**
|
|
1506
|
+
* Reserved tool name for the built-in `model-decode` static tool.
|
|
1507
|
+
*/ var MODEL_DECODE_TOOL_NAME = 'model-decode';
|
|
1508
|
+
/**
|
|
1509
|
+
* Synthetic call type used in the tool's dispatch identity. Aligns with the dbx-cli `model-decode`
|
|
1510
|
+
* command.
|
|
1511
|
+
*/ var MODEL_DECODE_DISPATCH_CALL = 'decode';
|
|
1512
|
+
/**
|
|
1513
|
+
* Synthetic model type used in the tool's dispatch identity. Mirrors the other built-in static
|
|
1514
|
+
* tools (`model-get`, `model-info`); apps avoiding collisions should not register a real model
|
|
1515
|
+
* literally named "model".
|
|
1516
|
+
*/ var MODEL_DECODE_DISPATCH_MODEL_TYPE = 'model';
|
|
1517
|
+
// MARK: Factory
|
|
1518
|
+
/**
|
|
1519
|
+
* Builds the built-in `model-decode` MCP tool definition.
|
|
1520
|
+
*
|
|
1521
|
+
* Mirrors dbx-cli's `model-decode <key>` command:
|
|
1522
|
+
* - Splits the supplied Firestore key on `/`, walks `[prefix, id]` pairs, and resolves each prefix
|
|
1523
|
+
* against the manifest.
|
|
1524
|
+
* - Supports subcollection paths (`parentPrefix/parentId/childPrefix/childId`).
|
|
1525
|
+
*
|
|
1526
|
+
* Output is delivered as both stringified JSON in `content[0].text` and `structuredContent` so
|
|
1527
|
+
* MCP clients can consume either form.
|
|
1528
|
+
*
|
|
1529
|
+
* @param deps - The frozen model manifest loaded at boot from the MCP manifest JSON.
|
|
1530
|
+
* @returns A statically-registered {@link McpToolDefinition} ready to be appended to the MCP
|
|
1531
|
+
* server factory's tool registry.
|
|
1532
|
+
*/ function createModelDecodeTool(deps) {
|
|
1533
|
+
var handler = function handler(args, ctx) {
|
|
1534
|
+
return Promise.resolve(modelDecodeToolHandler(args, ctx, deps));
|
|
1535
|
+
};
|
|
1536
|
+
var name = MODEL_DECODE_TOOL_NAME;
|
|
1537
|
+
var description = 'Decode a Firestore model key ("prefix/id", or subcollection path "parentPrefix/parentId/childPrefix/childId") into { leaf, ancestors, unresolvedPrefixes } using the registered manifest ('.concat(deps.manifest.length, " model").concat(deps.manifest.length === 1 ? '' : 's', ").");
|
|
1538
|
+
return {
|
|
1539
|
+
name: name,
|
|
1540
|
+
description: description,
|
|
1541
|
+
inputSchema: MODEL_DECODE_INPUT_SCHEMA,
|
|
1542
|
+
outputSchema: MODEL_DECODE_OUTPUT_SCHEMA,
|
|
1543
|
+
dispatch: {
|
|
1544
|
+
call: MODEL_DECODE_DISPATCH_CALL,
|
|
1545
|
+
modelType: MODEL_DECODE_DISPATCH_MODEL_TYPE
|
|
1546
|
+
},
|
|
1547
|
+
staticHandler: handler,
|
|
1548
|
+
filterMetadata: {
|
|
1549
|
+
visibilityKind: 'declarative',
|
|
1550
|
+
rule: {
|
|
1551
|
+
requireAuthenticated: true
|
|
1552
|
+
},
|
|
1553
|
+
effectiveReadOnly: true
|
|
1554
|
+
},
|
|
1555
|
+
staticWireEntry: buildStaticWireEntry({
|
|
1556
|
+
name: name,
|
|
1557
|
+
description: description,
|
|
1558
|
+
inputSchema: MODEL_DECODE_INPUT_SCHEMA,
|
|
1559
|
+
outputSchema: MODEL_DECODE_OUTPUT_SCHEMA
|
|
1560
|
+
})
|
|
1561
|
+
};
|
|
1562
|
+
}
|
|
1563
|
+
// MARK: Handler
|
|
1564
|
+
function modelDecodeToolHandler(args, _ctx, deps) {
|
|
1565
|
+
var result;
|
|
1566
|
+
try {
|
|
1567
|
+
var input = parseModelDecodeInput(args);
|
|
1568
|
+
var output = decodeFirestoreModelKey(input.key, deps.manifest);
|
|
1569
|
+
result = {
|
|
1570
|
+
content: [
|
|
1571
|
+
{
|
|
1572
|
+
type: 'text',
|
|
1573
|
+
text: JSON.stringify(output)
|
|
1574
|
+
}
|
|
1575
|
+
],
|
|
1576
|
+
structuredContent: output
|
|
1577
|
+
};
|
|
1578
|
+
} catch (error) {
|
|
1579
|
+
result = formatMcpToolErrorResponse(error);
|
|
1580
|
+
}
|
|
1581
|
+
return result;
|
|
1582
|
+
}
|
|
1583
|
+
function parseModelDecodeInput(args) {
|
|
1584
|
+
var raw = args['key'];
|
|
1585
|
+
if (typeof raw !== 'string' || raw.length === 0) {
|
|
1586
|
+
throw new Error('model-decode: "key" is required and must be a non-empty string.');
|
|
1587
|
+
}
|
|
1588
|
+
return {
|
|
1589
|
+
key: raw
|
|
1590
|
+
};
|
|
1591
|
+
}
|
|
1592
|
+
/**
|
|
1593
|
+
* Splits `rawKey` on `/`, resolves each `[prefix, id]` pair against the manifest, and
|
|
1594
|
+
* returns the leaf segment + ancestor chain.
|
|
1595
|
+
*
|
|
1596
|
+
* Mirrors the dbx-cli `decodeFirestoreModelKey` helper structurally; both implementations
|
|
1597
|
+
* must stay in lockstep on segment count and resolution order.
|
|
1598
|
+
*
|
|
1599
|
+
* @param rawKey - The Firestore key string.
|
|
1600
|
+
* @param manifest - The model manifest.
|
|
1601
|
+
* @returns The decoded key with leaf, ancestors, and any unresolved prefixes.
|
|
1602
|
+
* @throws {Error} When `rawKey` is empty or does not parse into an even number of `prefix/id` segments.
|
|
1603
|
+
*
|
|
1604
|
+
* @__NO_SIDE_EFFECTS__
|
|
1605
|
+
*/ function decodeFirestoreModelKey(rawKey, manifest) {
|
|
1606
|
+
var trimmed = rawKey.trim();
|
|
1607
|
+
if (trimmed.length === 0) {
|
|
1608
|
+
throw new Error('model-decode: key is empty. Provide a Firestore key like `sf/abc123`.');
|
|
1609
|
+
}
|
|
1610
|
+
var segments = trimmed.split('/').filter(function(s) {
|
|
1611
|
+
return s.length > 0;
|
|
1612
|
+
});
|
|
1613
|
+
if (segments.length < 2 || segments.length % 2 !== 0) {
|
|
1614
|
+
throw new Error('model-decode: invalid Firestore key "'.concat(trimmed, '". Expected an even number of segments (`prefix/id` pairs). Got ').concat(segments.length, " segment(s). Use the format `prefix/id` (or `parentPrefix/parentId/childPrefix/childId` for subcollections)."));
|
|
1615
|
+
}
|
|
1616
|
+
var decoded = [];
|
|
1617
|
+
var unresolved = [];
|
|
1618
|
+
for(var i = 0; i < segments.length; i += 2){
|
|
1619
|
+
var prefix = segments[i];
|
|
1620
|
+
var id = segments[i + 1];
|
|
1621
|
+
var entry = findModelEntry(prefix, manifest);
|
|
1622
|
+
if (entry == null) {
|
|
1623
|
+
unresolved.push(prefix);
|
|
1624
|
+
}
|
|
1625
|
+
decoded.push(toSegment(prefix, id, entry));
|
|
1626
|
+
}
|
|
1627
|
+
var leaf = decoded.at(-1);
|
|
1628
|
+
var ancestors = decoded.slice(0, -1);
|
|
1629
|
+
return {
|
|
1630
|
+
key: trimmed,
|
|
1631
|
+
leaf: leaf,
|
|
1632
|
+
ancestors: ancestors,
|
|
1633
|
+
unresolvedPrefixes: unresolved
|
|
1634
|
+
};
|
|
1635
|
+
}
|
|
1636
|
+
function toSegment(prefix, id, entry) {
|
|
1637
|
+
var segment = entry ? _object_spread$1({
|
|
1638
|
+
prefix: prefix,
|
|
1639
|
+
id: id,
|
|
1640
|
+
modelType: entry.modelType,
|
|
1641
|
+
modelName: entry.modelName,
|
|
1642
|
+
identityConst: entry.identityConst,
|
|
1643
|
+
sourcePackage: entry.sourcePackage,
|
|
1644
|
+
sourceFile: entry.sourceFile
|
|
1645
|
+
}, entry.modelGroup == null ? {} : {
|
|
1646
|
+
modelGroup: entry.modelGroup
|
|
1647
|
+
}, entry.parentIdentityConst == null ? {} : {
|
|
1648
|
+
parentIdentityConst: entry.parentIdentityConst
|
|
1649
|
+
}) : {
|
|
1650
|
+
prefix: prefix,
|
|
1651
|
+
id: id
|
|
1652
|
+
};
|
|
1653
|
+
return segment;
|
|
1654
|
+
}
|
|
1655
|
+
// MARK: Schemas
|
|
1656
|
+
var MODEL_DECODE_INPUT_SCHEMA = {
|
|
1657
|
+
type: 'object',
|
|
1658
|
+
required: [
|
|
1659
|
+
'key'
|
|
1660
|
+
],
|
|
1661
|
+
properties: {
|
|
1662
|
+
key: {
|
|
1663
|
+
type: 'string',
|
|
1664
|
+
minLength: 1,
|
|
1665
|
+
description: 'Firestore model key — prefix/id, or `parentPrefix/parentId/childPrefix/childId` for subcollections.'
|
|
1666
|
+
}
|
|
1667
|
+
},
|
|
1668
|
+
additionalProperties: false
|
|
1669
|
+
};
|
|
1670
|
+
var SEGMENT_SCHEMA = {
|
|
1671
|
+
type: 'object',
|
|
1672
|
+
required: [
|
|
1673
|
+
'prefix',
|
|
1674
|
+
'id'
|
|
1675
|
+
],
|
|
1676
|
+
properties: {
|
|
1677
|
+
prefix: {
|
|
1678
|
+
type: 'string'
|
|
1679
|
+
},
|
|
1680
|
+
id: {
|
|
1681
|
+
type: 'string'
|
|
1682
|
+
},
|
|
1683
|
+
modelType: {
|
|
1684
|
+
type: 'string'
|
|
1685
|
+
},
|
|
1686
|
+
modelName: {
|
|
1687
|
+
type: 'string'
|
|
1688
|
+
},
|
|
1689
|
+
modelGroup: {
|
|
1690
|
+
type: 'string'
|
|
1691
|
+
},
|
|
1692
|
+
identityConst: {
|
|
1693
|
+
type: 'string'
|
|
1694
|
+
},
|
|
1695
|
+
parentIdentityConst: {
|
|
1696
|
+
type: 'string'
|
|
1697
|
+
},
|
|
1698
|
+
sourcePackage: {
|
|
1699
|
+
type: 'string'
|
|
1700
|
+
},
|
|
1701
|
+
sourceFile: {
|
|
1702
|
+
type: 'string'
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
};
|
|
1706
|
+
var MODEL_DECODE_OUTPUT_SCHEMA = {
|
|
1707
|
+
type: 'object',
|
|
1708
|
+
required: [
|
|
1709
|
+
'key',
|
|
1710
|
+
'leaf',
|
|
1711
|
+
'ancestors',
|
|
1712
|
+
'unresolvedPrefixes'
|
|
1713
|
+
],
|
|
1714
|
+
properties: {
|
|
1715
|
+
key: {
|
|
1716
|
+
type: 'string',
|
|
1717
|
+
description: 'The trimmed input key.'
|
|
1718
|
+
},
|
|
1719
|
+
leaf: SEGMENT_SCHEMA,
|
|
1720
|
+
ancestors: {
|
|
1721
|
+
type: 'array',
|
|
1722
|
+
description: 'Parent segments, root-first.',
|
|
1723
|
+
items: SEGMENT_SCHEMA
|
|
1724
|
+
},
|
|
1725
|
+
unresolvedPrefixes: {
|
|
1726
|
+
type: 'array',
|
|
1727
|
+
description: 'Prefixes that did not match any model in the manifest.',
|
|
1728
|
+
items: {
|
|
1729
|
+
type: 'string'
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
};
|
|
1734
|
+
|
|
1735
|
+
function _array_like_to_array$2(arr, len) {
|
|
1736
|
+
if (len == null || len > arr.length) len = arr.length;
|
|
1737
|
+
for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
|
|
1738
|
+
return arr2;
|
|
1739
|
+
}
|
|
1740
|
+
function _array_with_holes$1(arr) {
|
|
1741
|
+
if (Array.isArray(arr)) return arr;
|
|
1742
|
+
}
|
|
1743
|
+
function _array_without_holes$2(arr) {
|
|
1744
|
+
if (Array.isArray(arr)) return _array_like_to_array$2(arr);
|
|
1745
|
+
}
|
|
1746
|
+
function _define_property$3(obj, key, value) {
|
|
1747
|
+
if (key in obj) {
|
|
1748
|
+
Object.defineProperty(obj, key, {
|
|
1749
|
+
value: value,
|
|
1750
|
+
enumerable: true,
|
|
1751
|
+
configurable: true,
|
|
1752
|
+
writable: true
|
|
1753
|
+
});
|
|
1754
|
+
} else {
|
|
1755
|
+
obj[key] = value;
|
|
1756
|
+
}
|
|
1757
|
+
return obj;
|
|
1758
|
+
}
|
|
1759
|
+
function _iterable_to_array$2(iter) {
|
|
1760
|
+
if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
|
|
1761
|
+
}
|
|
1762
|
+
function _iterable_to_array_limit$1(arr, i) {
|
|
1763
|
+
var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
|
|
1764
|
+
if (_i == null) return;
|
|
1765
|
+
var _arr = [];
|
|
1766
|
+
var _n = true;
|
|
1767
|
+
var _d = false;
|
|
1768
|
+
var _s, _e;
|
|
1769
|
+
try {
|
|
1770
|
+
for(_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true){
|
|
1771
|
+
_arr.push(_s.value);
|
|
1772
|
+
if (i && _arr.length === i) break;
|
|
1773
|
+
}
|
|
1774
|
+
} catch (err) {
|
|
1775
|
+
_d = true;
|
|
1776
|
+
_e = err;
|
|
1777
|
+
} finally{
|
|
1778
|
+
try {
|
|
1779
|
+
if (!_n && _i["return"] != null) _i["return"]();
|
|
1780
|
+
} finally{
|
|
1781
|
+
if (_d) throw _e;
|
|
1782
|
+
}
|
|
1783
|
+
}
|
|
1784
|
+
return _arr;
|
|
1785
|
+
}
|
|
1786
|
+
function _non_iterable_rest$1() {
|
|
1787
|
+
throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
1788
|
+
}
|
|
1789
|
+
function _non_iterable_spread$2() {
|
|
1790
|
+
throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
1791
|
+
}
|
|
1792
|
+
function _object_spread(target) {
|
|
1793
|
+
for(var i = 1; i < arguments.length; i++){
|
|
1794
|
+
var source = arguments[i] != null ? arguments[i] : {};
|
|
1795
|
+
var ownKeys = Object.keys(source);
|
|
1796
|
+
if (typeof Object.getOwnPropertySymbols === "function") {
|
|
1797
|
+
ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
|
|
1798
|
+
return Object.getOwnPropertyDescriptor(source, sym).enumerable;
|
|
1799
|
+
}));
|
|
1800
|
+
}
|
|
1801
|
+
ownKeys.forEach(function(key) {
|
|
1802
|
+
_define_property$3(target, key, source[key]);
|
|
1803
|
+
});
|
|
1804
|
+
}
|
|
1805
|
+
return target;
|
|
1806
|
+
}
|
|
1807
|
+
function _sliced_to_array$1(arr, i) {
|
|
1808
|
+
return _array_with_holes$1(arr) || _iterable_to_array_limit$1(arr, i) || _unsupported_iterable_to_array$2(arr, i) || _non_iterable_rest$1();
|
|
1809
|
+
}
|
|
1810
|
+
function _to_consumable_array$2(arr) {
|
|
1811
|
+
return _array_without_holes$2(arr) || _iterable_to_array$2(arr) || _unsupported_iterable_to_array$2(arr) || _non_iterable_spread$2();
|
|
1812
|
+
}
|
|
1813
|
+
function _unsupported_iterable_to_array$2(o, minLen) {
|
|
1814
|
+
if (!o) return;
|
|
1815
|
+
if (typeof o === "string") return _array_like_to_array$2(o, minLen);
|
|
1816
|
+
var n = Object.prototype.toString.call(o).slice(8, -1);
|
|
1817
|
+
if (n === "Object" && o.constructor) n = o.constructor.name;
|
|
1818
|
+
if (n === "Map" || n === "Set") return Array.from(n);
|
|
1819
|
+
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$2(o, minLen);
|
|
1820
|
+
}
|
|
1821
|
+
// MARK: Constants
|
|
1822
|
+
/**
|
|
1823
|
+
* Reserved tool name for the built-in `whoami` static tool.
|
|
1824
|
+
*/ var WHOAMI_TOOL_NAME = 'whoami';
|
|
1825
|
+
/**
|
|
1826
|
+
* Synthetic call type used in the tool's dispatch identity. Matches the
|
|
1827
|
+
* pattern set by `model-info` — `read` (not `info`) so the visibility
|
|
1828
|
+
* classifier treats the tool as a read.
|
|
1829
|
+
*/ var WHOAMI_DISPATCH_CALL = 'read';
|
|
1830
|
+
/**
|
|
1831
|
+
* Synthetic model type used in the tool's dispatch identity.
|
|
1832
|
+
*/ var WHOAMI_DISPATCH_MODEL_TYPE = 'auth';
|
|
1833
|
+
// MARK: Factory
|
|
1834
|
+
/**
|
|
1835
|
+
* Builds the built-in `whoami` MCP tool definition. Returns the calling
|
|
1836
|
+
* caller's `uid`, claims object, decoded role set, and rendered descriptions
|
|
1837
|
+
* for every claim key actually present on the live token.
|
|
1838
|
+
*
|
|
1839
|
+
* Unauthenticated callers receive a structured `authenticated: false` result
|
|
1840
|
+
* (no exception) so the tool can be used as a "do you see me?" probe.
|
|
1841
|
+
*
|
|
1842
|
+
* @param deps - The manifest's auth section + optional role reader.
|
|
1843
|
+
* @returns A statically-registered {@link McpToolDefinition} to append to the MCP server factory's tool registry.
|
|
1844
|
+
*/ function createWhoamiTool(deps) {
|
|
1845
|
+
var _ref;
|
|
1846
|
+
var _deps_auth_app;
|
|
1847
|
+
var handler = function handler(_args, ctx) {
|
|
1848
|
+
return Promise.resolve(whoamiToolHandler(ctx, deps));
|
|
1849
|
+
};
|
|
1850
|
+
var name = WHOAMI_TOOL_NAME;
|
|
1851
|
+
var appLabel = (_ref = (_deps_auth_app = deps.auth.app) === null || _deps_auth_app === void 0 ? void 0 : _deps_auth_app.app) !== null && _ref !== void 0 ? _ref : 'this server';
|
|
1852
|
+
var description = "Reports the authenticated caller's uid, raw custom claims, and decoded role set for `".concat(appLabel, "`. Returns `authenticated: false` for anonymous callers. Claim descriptions are drawn from the build-time auth manifest; only keys actually present on the live token are detailed.");
|
|
1853
|
+
return {
|
|
1854
|
+
name: name,
|
|
1855
|
+
description: description,
|
|
1856
|
+
inputSchema: WHOAMI_INPUT_SCHEMA,
|
|
1857
|
+
outputSchema: WHOAMI_OUTPUT_SCHEMA,
|
|
1858
|
+
dispatch: {
|
|
1859
|
+
call: WHOAMI_DISPATCH_CALL,
|
|
1860
|
+
modelType: WHOAMI_DISPATCH_MODEL_TYPE
|
|
1861
|
+
},
|
|
1862
|
+
staticHandler: handler,
|
|
1863
|
+
filterMetadata: {
|
|
1864
|
+
visibilityKind: 'declarative',
|
|
1865
|
+
rule: {},
|
|
1866
|
+
effectiveReadOnly: true
|
|
1867
|
+
},
|
|
1868
|
+
staticWireEntry: buildStaticWireEntry({
|
|
1869
|
+
name: name,
|
|
1870
|
+
description: description,
|
|
1871
|
+
inputSchema: WHOAMI_INPUT_SCHEMA,
|
|
1872
|
+
outputSchema: WHOAMI_OUTPUT_SCHEMA
|
|
1873
|
+
})
|
|
1874
|
+
};
|
|
1875
|
+
}
|
|
1876
|
+
// MARK: Handler
|
|
1877
|
+
function whoamiToolHandler(ctx, deps) {
|
|
1878
|
+
var _auth_token;
|
|
1879
|
+
var _deps_auth_app;
|
|
1880
|
+
var auth = ctx.auth;
|
|
1881
|
+
if ((auth === null || auth === void 0 ? void 0 : auth.uid) == null) {
|
|
1882
|
+
var output = {
|
|
1883
|
+
authenticated: false,
|
|
1884
|
+
claims: {},
|
|
1885
|
+
roles: [],
|
|
1886
|
+
claimDetails: [],
|
|
1887
|
+
unknownClaimKeys: []
|
|
1888
|
+
};
|
|
1889
|
+
return buildResult(output, '# whoami\n\nUnauthenticated.');
|
|
1890
|
+
}
|
|
1891
|
+
var token = (_auth_token = auth.token) !== null && _auth_token !== void 0 ? _auth_token : {};
|
|
1892
|
+
var presentKeys = Object.entries(token).filter(function(param) {
|
|
1893
|
+
var _param = _sliced_to_array$1(param, 2), value = _param[1];
|
|
1894
|
+
return value !== undefined;
|
|
1895
|
+
}).map(function(param) {
|
|
1896
|
+
var _param = _sliced_to_array$1(param, 1), key = _param[0];
|
|
1897
|
+
return key;
|
|
1898
|
+
});
|
|
1899
|
+
var roleSet = deps.roleReader == null ? new Set() : deps.roleReader(token);
|
|
1900
|
+
var roles = _to_consumable_array$2(roleSet).sort(function(a, b) {
|
|
1901
|
+
return a.localeCompare(b);
|
|
1902
|
+
});
|
|
1903
|
+
var claimsByKey = new Map();
|
|
1904
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
1905
|
+
try {
|
|
1906
|
+
for(var _iterator = deps.auth.claims[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
1907
|
+
var claim = _step.value;
|
|
1908
|
+
claimsByKey.set(claim.key, claim);
|
|
1909
|
+
}
|
|
1910
|
+
} catch (err) {
|
|
1911
|
+
_didIteratorError = true;
|
|
1912
|
+
_iteratorError = err;
|
|
1913
|
+
} finally{
|
|
1914
|
+
try {
|
|
1915
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
1916
|
+
_iterator.return();
|
|
1917
|
+
}
|
|
1918
|
+
} finally{
|
|
1919
|
+
if (_didIteratorError) {
|
|
1920
|
+
throw _iteratorError;
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
}
|
|
1924
|
+
var claimDetails = [];
|
|
1925
|
+
var unknownClaimKeys = [];
|
|
1926
|
+
var _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
|
|
1927
|
+
try {
|
|
1928
|
+
for(var _iterator1 = presentKeys[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
|
|
1929
|
+
var key = _step1.value;
|
|
1930
|
+
if (key === 'email' || key === 'email_verified') {
|
|
1931
|
+
continue;
|
|
1932
|
+
}
|
|
1933
|
+
var claim1 = claimsByKey.get(key);
|
|
1934
|
+
if (claim1 == null) {
|
|
1935
|
+
unknownClaimKeys.push(key);
|
|
1936
|
+
} else {
|
|
1937
|
+
claimDetails.push(_object_spread({
|
|
1938
|
+
key: claim1.key,
|
|
1939
|
+
description: claim1.description,
|
|
1940
|
+
grantedRoles: claim1.mapping.roles,
|
|
1941
|
+
inverse: claim1.mapping.inverse,
|
|
1942
|
+
tags: claim1.tags
|
|
1943
|
+
}, claim1.interfaceName == null ? {} : {
|
|
1944
|
+
interfaceName: claim1.interfaceName
|
|
1945
|
+
}));
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
} catch (err) {
|
|
1949
|
+
_didIteratorError1 = true;
|
|
1950
|
+
_iteratorError1 = err;
|
|
1951
|
+
} finally{
|
|
1952
|
+
try {
|
|
1953
|
+
if (!_iteratorNormalCompletion1 && _iterator1.return != null) {
|
|
1954
|
+
_iterator1.return();
|
|
1955
|
+
}
|
|
1956
|
+
} finally{
|
|
1957
|
+
if (_didIteratorError1) {
|
|
1958
|
+
throw _iteratorError1;
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
}
|
|
1962
|
+
var output1 = _object_spread({
|
|
1963
|
+
authenticated: true,
|
|
1964
|
+
uid: auth.uid,
|
|
1965
|
+
claims: _object_spread({}, token),
|
|
1966
|
+
roles: roles,
|
|
1967
|
+
claimDetails: claimDetails,
|
|
1968
|
+
unknownClaimKeys: unknownClaimKeys
|
|
1969
|
+
}, ((_deps_auth_app = deps.auth.app) === null || _deps_auth_app === void 0 ? void 0 : _deps_auth_app.app) == null ? {} : {
|
|
1970
|
+
app: deps.auth.app.app
|
|
1971
|
+
}, token.email == null ? {} : {
|
|
1972
|
+
email: token.email
|
|
1973
|
+
}, token.email_verified == null ? {} : {
|
|
1974
|
+
emailVerified: token.email_verified
|
|
1975
|
+
});
|
|
1976
|
+
var text = renderText(output1);
|
|
1977
|
+
return buildResult(output1, text);
|
|
1978
|
+
}
|
|
1979
|
+
function buildResult(output, text) {
|
|
1980
|
+
return {
|
|
1981
|
+
content: [
|
|
1982
|
+
{
|
|
1983
|
+
type: 'text',
|
|
1984
|
+
text: text
|
|
1985
|
+
}
|
|
1986
|
+
],
|
|
1987
|
+
structuredContent: output
|
|
1988
|
+
};
|
|
1989
|
+
}
|
|
1990
|
+
function renderText(output) {
|
|
1991
|
+
var lines = [
|
|
1992
|
+
'# whoami',
|
|
1993
|
+
''
|
|
1994
|
+
];
|
|
1995
|
+
appendHeaderLines(lines, output);
|
|
1996
|
+
appendClaimDetailLines(lines, output.claimDetails);
|
|
1997
|
+
appendUnknownClaimLines(lines, output.unknownClaimKeys);
|
|
1998
|
+
return lines.join('\n');
|
|
1999
|
+
}
|
|
2000
|
+
function appendHeaderLines(lines, output) {
|
|
2001
|
+
if (output.uid != null) {
|
|
2002
|
+
lines.push("- **uid:** `".concat(output.uid, "`"));
|
|
2003
|
+
}
|
|
2004
|
+
if (output.app != null) {
|
|
2005
|
+
lines.push("- **app:** `".concat(output.app, "`"));
|
|
2006
|
+
}
|
|
2007
|
+
if (output.email != null) {
|
|
2008
|
+
lines.push("- **email:** `".concat(output.email, "`"));
|
|
2009
|
+
}
|
|
2010
|
+
if (output.roles.length > 0) {
|
|
2011
|
+
var rolesText = output.roles.map(function(role) {
|
|
2012
|
+
return "`".concat(role, "`");
|
|
2013
|
+
}).join(', ');
|
|
2014
|
+
lines.push("- **roles:** ".concat(rolesText));
|
|
2015
|
+
} else {
|
|
2016
|
+
lines.push('- **roles:** _(none)_');
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
function appendClaimDetailLines(lines, claimDetails) {
|
|
2020
|
+
if (claimDetails.length === 0) {
|
|
2021
|
+
return;
|
|
2022
|
+
}
|
|
2023
|
+
lines.push('', '## Claims on this token');
|
|
2024
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
2025
|
+
try {
|
|
2026
|
+
for(var _iterator = claimDetails[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
2027
|
+
var detail = _step.value;
|
|
2028
|
+
var headerInterfacePart = detail.interfaceName == null ? '' : " on `".concat(detail.interfaceName, "`");
|
|
2029
|
+
var grants = detail.grantedRoles.length > 0 ? detail.grantedRoles.map(function(role) {
|
|
2030
|
+
return "`".concat(role, "`");
|
|
2031
|
+
}).join(', ') : '_(no roles)_';
|
|
2032
|
+
var action = detail.inverse ? 'revokes' : 'grants';
|
|
2033
|
+
lines.push("- `".concat(detail.key, "`").concat(headerInterfacePart, " — ").concat(action, " ").concat(grants));
|
|
2034
|
+
if (detail.description.length > 0) {
|
|
2035
|
+
lines.push(" ".concat(detail.description));
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
} catch (err) {
|
|
2039
|
+
_didIteratorError = true;
|
|
2040
|
+
_iteratorError = err;
|
|
2041
|
+
} finally{
|
|
2042
|
+
try {
|
|
2043
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
2044
|
+
_iterator.return();
|
|
2045
|
+
}
|
|
2046
|
+
} finally{
|
|
2047
|
+
if (_didIteratorError) {
|
|
2048
|
+
throw _iteratorError;
|
|
2049
|
+
}
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
2052
|
+
}
|
|
2053
|
+
function appendUnknownClaimLines(lines, unknownClaimKeys) {
|
|
2054
|
+
if (unknownClaimKeys.length === 0) {
|
|
2055
|
+
return;
|
|
2056
|
+
}
|
|
2057
|
+
lines.push('', '## Unknown claim keys');
|
|
2058
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
2059
|
+
try {
|
|
2060
|
+
for(var _iterator = unknownClaimKeys[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
2061
|
+
var key = _step.value;
|
|
2062
|
+
lines.push("- `".concat(key, "`"));
|
|
2063
|
+
}
|
|
2064
|
+
} catch (err) {
|
|
2065
|
+
_didIteratorError = true;
|
|
2066
|
+
_iteratorError = err;
|
|
2067
|
+
} finally{
|
|
2068
|
+
try {
|
|
2069
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
2070
|
+
_iterator.return();
|
|
2071
|
+
}
|
|
2072
|
+
} finally{
|
|
2073
|
+
if (_didIteratorError) {
|
|
2074
|
+
throw _iteratorError;
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
2078
|
+
}
|
|
2079
|
+
// MARK: Schemas
|
|
2080
|
+
var WHOAMI_INPUT_SCHEMA = {
|
|
2081
|
+
type: 'object',
|
|
2082
|
+
properties: {},
|
|
2083
|
+
additionalProperties: false
|
|
2084
|
+
};
|
|
2085
|
+
var WHOAMI_OUTPUT_SCHEMA = {
|
|
2086
|
+
type: 'object',
|
|
2087
|
+
required: [
|
|
2088
|
+
'authenticated',
|
|
2089
|
+
'claims',
|
|
2090
|
+
'roles',
|
|
2091
|
+
'claimDetails',
|
|
2092
|
+
'unknownClaimKeys'
|
|
2093
|
+
],
|
|
2094
|
+
description: 'Caller identity, raw token claims, decoded role set, and per-claim descriptions for every key present on the live token.',
|
|
2095
|
+
properties: {
|
|
2096
|
+
authenticated: {
|
|
2097
|
+
type: 'boolean'
|
|
2098
|
+
},
|
|
2099
|
+
uid: {
|
|
2100
|
+
type: 'string'
|
|
2101
|
+
},
|
|
2102
|
+
email: {
|
|
2103
|
+
type: 'string'
|
|
2104
|
+
},
|
|
2105
|
+
emailVerified: {
|
|
2106
|
+
type: 'boolean'
|
|
2107
|
+
},
|
|
2108
|
+
app: {
|
|
2109
|
+
type: 'string'
|
|
2110
|
+
},
|
|
2111
|
+
claims: {
|
|
2112
|
+
type: 'object',
|
|
2113
|
+
additionalProperties: true
|
|
2114
|
+
},
|
|
2115
|
+
roles: {
|
|
2116
|
+
type: 'array',
|
|
2117
|
+
items: {
|
|
2118
|
+
type: 'string'
|
|
2119
|
+
}
|
|
2120
|
+
},
|
|
2121
|
+
claimDetails: {
|
|
2122
|
+
type: 'array',
|
|
2123
|
+
items: {
|
|
2124
|
+
type: 'object',
|
|
2125
|
+
required: [
|
|
2126
|
+
'key',
|
|
2127
|
+
'description',
|
|
2128
|
+
'grantedRoles',
|
|
2129
|
+
'inverse',
|
|
2130
|
+
'tags'
|
|
2131
|
+
],
|
|
2132
|
+
properties: {
|
|
2133
|
+
key: {
|
|
2134
|
+
type: 'string'
|
|
2135
|
+
},
|
|
2136
|
+
interfaceName: {
|
|
2137
|
+
type: 'string'
|
|
2138
|
+
},
|
|
2139
|
+
description: {
|
|
2140
|
+
type: 'string'
|
|
2141
|
+
},
|
|
2142
|
+
grantedRoles: {
|
|
2143
|
+
type: 'array',
|
|
2144
|
+
items: {
|
|
2145
|
+
type: 'string'
|
|
2146
|
+
}
|
|
2147
|
+
},
|
|
2148
|
+
inverse: {
|
|
2149
|
+
type: 'boolean'
|
|
2150
|
+
},
|
|
2151
|
+
tags: {
|
|
2152
|
+
type: 'array',
|
|
2153
|
+
items: {
|
|
2154
|
+
type: 'string'
|
|
2155
|
+
}
|
|
2156
|
+
}
|
|
2157
|
+
}
|
|
2158
|
+
}
|
|
2159
|
+
},
|
|
2160
|
+
unknownClaimKeys: {
|
|
2161
|
+
type: 'array',
|
|
2162
|
+
items: {
|
|
2163
|
+
type: 'string'
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
}
|
|
2167
|
+
};
|
|
2168
|
+
|
|
2169
|
+
function _array_like_to_array$1(arr, len) {
|
|
2170
|
+
if (len == null || len > arr.length) len = arr.length;
|
|
2171
|
+
for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
|
|
2172
|
+
return arr2;
|
|
2173
|
+
}
|
|
2174
|
+
function _array_with_holes(arr) {
|
|
2175
|
+
if (Array.isArray(arr)) return arr;
|
|
2176
|
+
}
|
|
2177
|
+
function _array_without_holes$1(arr) {
|
|
2178
|
+
if (Array.isArray(arr)) return _array_like_to_array$1(arr);
|
|
2179
|
+
}
|
|
2180
|
+
function asyncGeneratorStep$2(gen, resolve, reject, _next, _throw, key, arg) {
|
|
2181
|
+
try {
|
|
2182
|
+
var info = gen[key](arg);
|
|
2183
|
+
var value = info.value;
|
|
2184
|
+
} catch (error) {
|
|
2185
|
+
reject(error);
|
|
2186
|
+
return;
|
|
2187
|
+
}
|
|
2188
|
+
if (info.done) {
|
|
2189
|
+
resolve(value);
|
|
2190
|
+
} else {
|
|
2191
|
+
Promise.resolve(value).then(_next, _throw);
|
|
2192
|
+
}
|
|
2193
|
+
}
|
|
2194
|
+
function _async_to_generator$2(fn) {
|
|
2195
|
+
return function() {
|
|
2196
|
+
var self = this, args = arguments;
|
|
2197
|
+
return new Promise(function(resolve, reject) {
|
|
2198
|
+
var gen = fn.apply(self, args);
|
|
2199
|
+
function _next(value) {
|
|
2200
|
+
asyncGeneratorStep$2(gen, resolve, reject, _next, _throw, "next", value);
|
|
2201
|
+
}
|
|
2202
|
+
function _throw(err) {
|
|
2203
|
+
asyncGeneratorStep$2(gen, resolve, reject, _next, _throw, "throw", err);
|
|
2204
|
+
}
|
|
2205
|
+
_next(undefined);
|
|
2206
|
+
});
|
|
2207
|
+
};
|
|
2208
|
+
}
|
|
2209
|
+
function _class_call_check$2(instance, Constructor) {
|
|
2210
|
+
if (!(instance instanceof Constructor)) {
|
|
2211
|
+
throw new TypeError("Cannot call a class as a function");
|
|
2212
|
+
}
|
|
2213
|
+
}
|
|
2214
|
+
function _defineProperties$2(target, props) {
|
|
2215
|
+
for(var i = 0; i < props.length; i++){
|
|
2216
|
+
var descriptor = props[i];
|
|
2217
|
+
descriptor.enumerable = descriptor.enumerable || false;
|
|
2218
|
+
descriptor.configurable = true;
|
|
2219
|
+
if ("value" in descriptor) descriptor.writable = true;
|
|
2220
|
+
Object.defineProperty(target, descriptor.key, descriptor);
|
|
2221
|
+
}
|
|
2222
|
+
}
|
|
2223
|
+
function _create_class$2(Constructor, protoProps, staticProps) {
|
|
2224
|
+
if (protoProps) _defineProperties$2(Constructor.prototype, protoProps);
|
|
2225
|
+
return Constructor;
|
|
2226
|
+
}
|
|
2227
|
+
function _define_property$2(obj, key, value) {
|
|
2228
|
+
if (key in obj) {
|
|
2229
|
+
Object.defineProperty(obj, key, {
|
|
2230
|
+
value: value,
|
|
2231
|
+
enumerable: true,
|
|
2232
|
+
configurable: true,
|
|
2233
|
+
writable: true
|
|
2234
|
+
});
|
|
2235
|
+
} else {
|
|
2236
|
+
obj[key] = value;
|
|
2237
|
+
}
|
|
2238
|
+
return obj;
|
|
2239
|
+
}
|
|
2240
|
+
function _iterable_to_array$1(iter) {
|
|
2241
|
+
if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
|
|
2242
|
+
}
|
|
2243
|
+
function _iterable_to_array_limit(arr, i) {
|
|
2244
|
+
var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
|
|
2245
|
+
if (_i == null) return;
|
|
2246
|
+
var _arr = [];
|
|
2247
|
+
var _n = true;
|
|
2248
|
+
var _d = false;
|
|
2249
|
+
var _s, _e;
|
|
2250
|
+
try {
|
|
2251
|
+
for(_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true){
|
|
2252
|
+
_arr.push(_s.value);
|
|
2253
|
+
if (i && _arr.length === i) break;
|
|
2254
|
+
}
|
|
2255
|
+
} catch (err) {
|
|
2256
|
+
_d = true;
|
|
2257
|
+
_e = err;
|
|
2258
|
+
} finally{
|
|
2259
|
+
try {
|
|
2260
|
+
if (!_n && _i["return"] != null) _i["return"]();
|
|
2261
|
+
} finally{
|
|
2262
|
+
if (_d) throw _e;
|
|
2263
|
+
}
|
|
2264
|
+
}
|
|
2265
|
+
return _arr;
|
|
2266
|
+
}
|
|
2267
|
+
function _non_iterable_rest() {
|
|
2268
|
+
throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
2269
|
+
}
|
|
2270
|
+
function _non_iterable_spread$1() {
|
|
2271
|
+
throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
2272
|
+
}
|
|
2273
|
+
function _sliced_to_array(arr, i) {
|
|
2274
|
+
return _array_with_holes(arr) || _iterable_to_array_limit(arr, i) || _unsupported_iterable_to_array$1(arr, i) || _non_iterable_rest();
|
|
2275
|
+
}
|
|
2276
|
+
function _to_consumable_array$1(arr) {
|
|
2277
|
+
return _array_without_holes$1(arr) || _iterable_to_array$1(arr) || _unsupported_iterable_to_array$1(arr) || _non_iterable_spread$1();
|
|
2278
|
+
}
|
|
2279
|
+
function _unsupported_iterable_to_array$1(o, minLen) {
|
|
2280
|
+
if (!o) return;
|
|
2281
|
+
if (typeof o === "string") return _array_like_to_array$1(o, minLen);
|
|
2282
|
+
var n = Object.prototype.toString.call(o).slice(8, -1);
|
|
2283
|
+
if (n === "Object" && o.constructor) n = o.constructor.name;
|
|
2284
|
+
if (n === "Map" || n === "Set") return Array.from(n);
|
|
2285
|
+
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$1(o, minLen);
|
|
2286
|
+
}
|
|
2287
|
+
function _ts_generator$2(thisArg, body) {
|
|
2288
|
+
var f, y, t, _ = {
|
|
2289
|
+
label: 0,
|
|
2290
|
+
sent: function() {
|
|
2291
|
+
if (t[0] & 1) throw t[1];
|
|
2292
|
+
return t[1];
|
|
2293
|
+
},
|
|
2294
|
+
trys: [],
|
|
2295
|
+
ops: []
|
|
2296
|
+
}, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype), d = Object.defineProperty;
|
|
2297
|
+
return d(g, "next", {
|
|
2298
|
+
value: verb(0)
|
|
2299
|
+
}), d(g, "throw", {
|
|
2300
|
+
value: verb(1)
|
|
2301
|
+
}), d(g, "return", {
|
|
2302
|
+
value: verb(2)
|
|
2303
|
+
}), typeof Symbol === "function" && d(g, Symbol.iterator, {
|
|
2304
|
+
value: function() {
|
|
2305
|
+
return this;
|
|
2306
|
+
}
|
|
2307
|
+
}), g;
|
|
2308
|
+
function verb(n) {
|
|
2309
|
+
return function(v) {
|
|
2310
|
+
return step([
|
|
2311
|
+
n,
|
|
2312
|
+
v
|
|
2313
|
+
]);
|
|
2314
|
+
};
|
|
2315
|
+
}
|
|
2316
|
+
function step(op) {
|
|
2317
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
2318
|
+
while(g && (g = 0, op[0] && (_ = 0)), _)try {
|
|
2319
|
+
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;
|
|
2320
|
+
if (y = 0, t) op = [
|
|
2321
|
+
op[0] & 2,
|
|
2322
|
+
t.value
|
|
2323
|
+
];
|
|
2324
|
+
switch(op[0]){
|
|
2325
|
+
case 0:
|
|
2326
|
+
case 1:
|
|
2327
|
+
t = op;
|
|
2328
|
+
break;
|
|
2329
|
+
case 4:
|
|
2330
|
+
_.label++;
|
|
2331
|
+
return {
|
|
2332
|
+
value: op[1],
|
|
2333
|
+
done: false
|
|
2334
|
+
};
|
|
2335
|
+
case 5:
|
|
2336
|
+
_.label++;
|
|
2337
|
+
y = op[1];
|
|
2338
|
+
op = [
|
|
2339
|
+
0
|
|
2340
|
+
];
|
|
2341
|
+
continue;
|
|
2342
|
+
case 7:
|
|
2343
|
+
op = _.ops.pop();
|
|
2344
|
+
_.trys.pop();
|
|
2345
|
+
continue;
|
|
2346
|
+
default:
|
|
2347
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
|
|
2348
|
+
_ = 0;
|
|
2349
|
+
continue;
|
|
2350
|
+
}
|
|
2351
|
+
if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) {
|
|
2352
|
+
_.label = op[1];
|
|
2353
|
+
break;
|
|
2354
|
+
}
|
|
2355
|
+
if (op[0] === 6 && _.label < t[1]) {
|
|
2356
|
+
_.label = t[1];
|
|
2357
|
+
t = op;
|
|
2358
|
+
break;
|
|
2359
|
+
}
|
|
2360
|
+
if (t && _.label < t[2]) {
|
|
2361
|
+
_.label = t[2];
|
|
2362
|
+
_.ops.push(op);
|
|
2363
|
+
break;
|
|
2364
|
+
}
|
|
2365
|
+
if (t[2]) _.ops.pop();
|
|
2366
|
+
_.trys.pop();
|
|
2367
|
+
continue;
|
|
2368
|
+
}
|
|
2369
|
+
op = body.call(thisArg, _);
|
|
2370
|
+
} catch (e) {
|
|
2371
|
+
op = [
|
|
2372
|
+
6,
|
|
2373
|
+
e
|
|
2374
|
+
];
|
|
2375
|
+
y = 0;
|
|
2376
|
+
} finally{
|
|
2377
|
+
f = t = 0;
|
|
2378
|
+
}
|
|
2379
|
+
if (op[0] & 5) throw op[1];
|
|
2380
|
+
return {
|
|
2381
|
+
value: op[0] ? op[1] : void 0,
|
|
2382
|
+
done: true
|
|
2383
|
+
};
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
2386
|
+
var McpServerFactoryService_1;
|
|
2387
|
+
/**
|
|
2388
|
+
* Injectable factory that builds {@link McpServer} instances pre-wired to the
|
|
2389
|
+
* call model dispatch chain.
|
|
2390
|
+
*
|
|
2391
|
+
* The factory is invoked per Streamable HTTP request — `@modelcontextprotocol/sdk`
|
|
2392
|
+
* recommends a fresh `McpServer` + transport pair per stateless JSON-RPC request,
|
|
2393
|
+
* which sidesteps session bookkeeping for the common Claude-connector case.
|
|
2394
|
+
*/ var McpServerFactoryService = McpServerFactoryService_1 = /*#__PURE__*/ function() {
|
|
2395
|
+
function McpServerFactoryService(mcpConfig, dispatchService, modelApiGetService, roleReader) {
|
|
2396
|
+
_class_call_check$2(this, McpServerFactoryService);
|
|
2397
|
+
_define_property$2(this, "mcpConfig", void 0);
|
|
2398
|
+
_define_property$2(this, "dispatchService", void 0);
|
|
2399
|
+
_define_property$2(this, "modelApiGetService", void 0);
|
|
2400
|
+
_define_property$2(this, "roleReader", void 0);
|
|
2401
|
+
_define_property$2(this, "_logger", new Logger(McpServerFactoryService_1.name));
|
|
2402
|
+
_define_property$2(this, "_cachedTools", void 0);
|
|
2403
|
+
_define_property$2(this, "_cachedStaticTools", void 0);
|
|
2404
|
+
_define_property$2(this, "_cachedManifest", void 0);
|
|
2405
|
+
_define_property$2(this, "_cachedManifestModels", void 0);
|
|
2406
|
+
_define_property$2(this, "_cachedManifestAuth", void 0);
|
|
2407
|
+
_define_property$2(this, "_manifestLoaded", false);
|
|
2408
|
+
_define_property$2(this, "_loggedSkips", false);
|
|
2409
|
+
_define_property$2(this, "_warnedMissingRoleReader", false);
|
|
2410
|
+
this.mcpConfig = mcpConfig;
|
|
2411
|
+
this.dispatchService = dispatchService;
|
|
2412
|
+
this.modelApiGetService = modelApiGetService;
|
|
2413
|
+
this.roleReader = roleReader;
|
|
2414
|
+
}
|
|
2415
|
+
_create_class$2(McpServerFactoryService, [
|
|
2416
|
+
{
|
|
2417
|
+
/**
|
|
2418
|
+
* Builds a configured MCP server with tool listing + dispatch handlers wired up.
|
|
2419
|
+
*
|
|
2420
|
+
* @param ctx - The per-request context (auth, raw request) used when forwarding tool calls.
|
|
2421
|
+
* @returns A configured MCP server ready to be `connect()`-ed to a transport.
|
|
2422
|
+
*/ key: "createServer",
|
|
2423
|
+
value: function createServer(ctx) {
|
|
2424
|
+
var _this = this;
|
|
2425
|
+
var _this_mcpConfig_serverName, _this_mcpConfig_serverVersion;
|
|
2426
|
+
var baseServerName = (_this_mcpConfig_serverName = this.mcpConfig.serverName) !== null && _this_mcpConfig_serverName !== void 0 ? _this_mcpConfig_serverName : DEFAULT_MCP_SERVER_NAME;
|
|
2427
|
+
var serverName = this.mcpConfig.readOnly === true ? "".concat(baseServerName, " (read-only)") : baseServerName;
|
|
2428
|
+
var server = new McpServer({
|
|
2429
|
+
name: serverName,
|
|
2430
|
+
version: (_this_mcpConfig_serverVersion = this.mcpConfig.serverVersion) !== null && _this_mcpConfig_serverVersion !== void 0 ? _this_mcpConfig_serverVersion : '0.0.0'
|
|
2431
|
+
}, {
|
|
2432
|
+
instructions: 'Model-bound RPC tools generated from the callModel _apiDetails tree.'
|
|
2433
|
+
});
|
|
2434
|
+
server.server.registerCapabilities({
|
|
2435
|
+
tools: {}
|
|
2436
|
+
});
|
|
2437
|
+
var toolGeneration = this._resolveToolDefinitions();
|
|
2438
|
+
var staticTools = this._resolveStaticTools();
|
|
2439
|
+
var scopes = this._resolveScopes(ctx);
|
|
2440
|
+
var authRoles = this._resolveAuthRoles(ctx);
|
|
2441
|
+
var combinedTools = _to_consumable_array$1(toolGeneration.tools).concat(_to_consumable_array$1(staticTools));
|
|
2442
|
+
var visibleTools = this._filterToolsForRequest(combinedTools, {
|
|
2443
|
+
ctx: ctx,
|
|
2444
|
+
scopes: scopes,
|
|
2445
|
+
authRoles: authRoles
|
|
2446
|
+
});
|
|
2447
|
+
var definitionsByName = new Map(visibleTools.map(function(tool) {
|
|
2448
|
+
return [
|
|
2449
|
+
tool.name,
|
|
2450
|
+
tool
|
|
2451
|
+
];
|
|
2452
|
+
}));
|
|
2453
|
+
server.server.setRequestHandler(ListToolsRequestSchema, function() {
|
|
2454
|
+
return _async_to_generator$2(function() {
|
|
2455
|
+
var _this;
|
|
2456
|
+
return _ts_generator$2(this, function(_state) {
|
|
2457
|
+
_this = this;
|
|
2458
|
+
return [
|
|
2459
|
+
2,
|
|
2460
|
+
{
|
|
2461
|
+
tools: visibleTools.map(function(tool) {
|
|
2462
|
+
return _this._buildToolListEntry(tool, ctx, scopes);
|
|
2463
|
+
})
|
|
2464
|
+
}
|
|
2465
|
+
];
|
|
2466
|
+
});
|
|
2467
|
+
}).call(_this);
|
|
2468
|
+
});
|
|
2469
|
+
server.server.setRequestHandler(CallToolRequestSchema, function(request) {
|
|
2470
|
+
return _async_to_generator$2(function() {
|
|
2471
|
+
return _ts_generator$2(this, function(_state) {
|
|
2472
|
+
return [
|
|
2473
|
+
2,
|
|
2474
|
+
this._handleToolCall(request, definitionsByName, ctx)
|
|
2475
|
+
];
|
|
2476
|
+
});
|
|
2477
|
+
}).call(_this);
|
|
2478
|
+
});
|
|
2479
|
+
return server;
|
|
2480
|
+
}
|
|
2481
|
+
},
|
|
2482
|
+
{
|
|
2483
|
+
/**
|
|
2484
|
+
* Reads cached tool definitions, regenerating + logging skips on first call.
|
|
2485
|
+
*
|
|
2486
|
+
* The underlying call model `_apiDetails` is built at boot and doesn't change at runtime,
|
|
2487
|
+
* so the generation result is safe to cache for the lifetime of the process.
|
|
2488
|
+
*
|
|
2489
|
+
* @returns The cached or freshly-generated tool generation result.
|
|
2490
|
+
*/ key: "_resolveToolDefinitions",
|
|
2491
|
+
value: function _resolveToolDefinitions() {
|
|
2492
|
+
var result = this._cachedTools;
|
|
2493
|
+
if (result == null) {
|
|
2494
|
+
var apiDetails = this.dispatchService.getApiDetails();
|
|
2495
|
+
var manifest = this._resolveManifest();
|
|
2496
|
+
if (apiDetails == null) {
|
|
2497
|
+
result = {
|
|
2498
|
+
tools: [],
|
|
2499
|
+
neverVisibleTools: [],
|
|
2500
|
+
skipped: []
|
|
2501
|
+
};
|
|
2502
|
+
} else {
|
|
2503
|
+
result = generateMcpToolDefinitions(apiDetails, undefined, manifest);
|
|
2504
|
+
}
|
|
2505
|
+
this._cachedTools = result;
|
|
2506
|
+
}
|
|
2507
|
+
if (!this._loggedSkips && result.skipped.length > 0) {
|
|
2508
|
+
this._loggedSkips = true;
|
|
2509
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
2510
|
+
try {
|
|
2511
|
+
for(var _iterator = result.skipped[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
2512
|
+
var skip = _step.value;
|
|
2513
|
+
var errorSuffix = skip.error ? ": ".concat(skip.error.message) : '';
|
|
2514
|
+
this._logger.warn("Skipped MCP tool ".concat(skip.toolName, " (").concat(skip.reason, ")").concat(errorSuffix));
|
|
2515
|
+
}
|
|
2516
|
+
} catch (err) {
|
|
2517
|
+
_didIteratorError = true;
|
|
2518
|
+
_iteratorError = err;
|
|
2519
|
+
} finally{
|
|
2520
|
+
try {
|
|
2521
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
2522
|
+
_iterator.return();
|
|
2523
|
+
}
|
|
2524
|
+
} finally{
|
|
2525
|
+
if (_didIteratorError) {
|
|
2526
|
+
throw _iteratorError;
|
|
2527
|
+
}
|
|
2528
|
+
}
|
|
2529
|
+
}
|
|
2530
|
+
}
|
|
2531
|
+
return result;
|
|
2532
|
+
}
|
|
2533
|
+
},
|
|
2534
|
+
{
|
|
2535
|
+
/**
|
|
2536
|
+
* Builds the list of statically-registered (non-callModel) MCP tools.
|
|
2537
|
+
*
|
|
2538
|
+
* Includes `model-get` whenever {@link ModelApiGetService} is available, plus
|
|
2539
|
+
* `model-info` and `model-decode` whenever the boot-time MCP manifest provided
|
|
2540
|
+
* a non-empty `models` catalog. The list is cached for the lifetime of the
|
|
2541
|
+
* process since static tools share the same boot-time inputs as the
|
|
2542
|
+
* auto-generated ones.
|
|
2543
|
+
*
|
|
2544
|
+
* @returns The cached array of static tool definitions, filtered for collisions with generated tools.
|
|
2545
|
+
*/ key: "_resolveStaticTools",
|
|
2546
|
+
value: function _resolveStaticTools() {
|
|
2547
|
+
var result = this._cachedStaticTools;
|
|
2548
|
+
if (result == null) {
|
|
2549
|
+
var _ref;
|
|
2550
|
+
var _this__cachedTools;
|
|
2551
|
+
// Ensure the manifest has been loaded so its `models` array is available for the catalog tools.
|
|
2552
|
+
this._resolveManifest();
|
|
2553
|
+
var staticTools = [];
|
|
2554
|
+
var getService = this.modelApiGetService;
|
|
2555
|
+
if (getService != null) {
|
|
2556
|
+
staticTools.push(createModelGetTool({
|
|
2557
|
+
readDocuments: function readDocuments(modelType, keys, auth) {
|
|
2558
|
+
return getService.readDocuments(modelType, keys, auth);
|
|
2559
|
+
},
|
|
2560
|
+
resolveIdentity: function resolveIdentity(modelType, auth) {
|
|
2561
|
+
return getService.getModelIdentity(modelType, auth);
|
|
2562
|
+
}
|
|
2563
|
+
}));
|
|
2564
|
+
}
|
|
2565
|
+
var modelManifest = this._cachedManifestModels;
|
|
2566
|
+
if (modelManifest != null && modelManifest.length > 0) {
|
|
2567
|
+
staticTools.push(createModelInfoTool({
|
|
2568
|
+
manifest: modelManifest
|
|
2569
|
+
}), createModelDecodeTool({
|
|
2570
|
+
manifest: modelManifest
|
|
2571
|
+
}));
|
|
2572
|
+
}
|
|
2573
|
+
var authManifest = this._cachedManifestAuth;
|
|
2574
|
+
if (authManifest != null) {
|
|
2575
|
+
staticTools.push(createWhoamiTool({
|
|
2576
|
+
auth: authManifest,
|
|
2577
|
+
roleReader: this.roleReader
|
|
2578
|
+
}));
|
|
2579
|
+
}
|
|
2580
|
+
// Guard against the auto-generated tools accidentally claiming a static tool name. The first
|
|
2581
|
+
// dispatch wins via Map.set order, so a collision would silently shadow one side.
|
|
2582
|
+
var generatedNames = new Set((_ref = (_this__cachedTools = this._cachedTools) === null || _this__cachedTools === void 0 ? void 0 : _this__cachedTools.tools.map(function(t) {
|
|
2583
|
+
return t.name;
|
|
2584
|
+
})) !== null && _ref !== void 0 ? _ref : []);
|
|
2585
|
+
var filtered = [];
|
|
2586
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
2587
|
+
try {
|
|
2588
|
+
for(var _iterator = staticTools[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
2589
|
+
var tool = _step.value;
|
|
2590
|
+
if (generatedNames.has(tool.name)) {
|
|
2591
|
+
this._logger.warn('Static MCP tool "'.concat(tool.name, '" collides with an auto-generated tool of the same name; the auto-generated tool will win.'));
|
|
2592
|
+
} else {
|
|
2593
|
+
filtered.push(tool);
|
|
2594
|
+
}
|
|
2595
|
+
}
|
|
2596
|
+
} catch (err) {
|
|
2597
|
+
_didIteratorError = true;
|
|
2598
|
+
_iteratorError = err;
|
|
2599
|
+
} finally{
|
|
2600
|
+
try {
|
|
2601
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
2602
|
+
_iterator.return();
|
|
2603
|
+
}
|
|
2604
|
+
} finally{
|
|
2605
|
+
if (_didIteratorError) {
|
|
2606
|
+
throw _iteratorError;
|
|
2607
|
+
}
|
|
2608
|
+
}
|
|
2609
|
+
}
|
|
2610
|
+
result = filtered;
|
|
2611
|
+
this._cachedStaticTools = result;
|
|
2612
|
+
}
|
|
2613
|
+
return result;
|
|
2614
|
+
}
|
|
2615
|
+
},
|
|
2616
|
+
{
|
|
2617
|
+
/**
|
|
2618
|
+
* Reads the pre-rendered MCP manifest JSON once, validates its version, and caches the
|
|
2619
|
+
* resulting `key → entry` map plus the optional `models` catalog for the process lifetime.
|
|
2620
|
+
*
|
|
2621
|
+
* Missing file or wrong version fall back to "no manifest" with a single boot warning;
|
|
2622
|
+
* the runtime still produces tools using the auto-generated descriptions and
|
|
2623
|
+
* ArkType-derived schemas.
|
|
2624
|
+
*
|
|
2625
|
+
* @returns The cached tool-entry map, or `undefined` when no manifest was loaded.
|
|
2626
|
+
*/ key: "_resolveManifest",
|
|
2627
|
+
value: function _resolveManifest() {
|
|
2628
|
+
if (!this._manifestLoaded) {
|
|
2629
|
+
this._manifestLoaded = true;
|
|
2630
|
+
var path = this.mcpConfig.mcpManifestPath;
|
|
2631
|
+
if (path == null) {
|
|
2632
|
+
this._resetManifestCache();
|
|
2633
|
+
} else if (existsSync(path)) {
|
|
2634
|
+
this._parseManifestFile(path);
|
|
2635
|
+
} else {
|
|
2636
|
+
this._logger.warn("MCP manifest path is set but the file is missing: ".concat(path, ". Falling back to runtime defaults."));
|
|
2637
|
+
this._resetManifestCache();
|
|
2638
|
+
}
|
|
2639
|
+
}
|
|
2640
|
+
return this._cachedManifest;
|
|
2641
|
+
}
|
|
2642
|
+
},
|
|
2643
|
+
{
|
|
2644
|
+
key: "_resetManifestCache",
|
|
2645
|
+
value: function _resetManifestCache() {
|
|
2646
|
+
this._cachedManifest = undefined;
|
|
2647
|
+
this._cachedManifestModels = undefined;
|
|
2648
|
+
this._cachedManifestAuth = undefined;
|
|
2649
|
+
}
|
|
2650
|
+
},
|
|
2651
|
+
{
|
|
2652
|
+
key: "_parseManifestFile",
|
|
2653
|
+
value: function _parseManifestFile(path) {
|
|
2654
|
+
try {
|
|
2655
|
+
var raw = readFileSync(path, 'utf8');
|
|
2656
|
+
var parsed = JSON.parse(raw);
|
|
2657
|
+
if (parsed.version === MCP_MANIFEST_VERSION) {
|
|
2658
|
+
this._applyParsedManifest(parsed, path);
|
|
2659
|
+
} else {
|
|
2660
|
+
this._logger.warn("MCP manifest version mismatch at ".concat(path, ": got ").concat(String(parsed.version), ", expected ").concat(MCP_MANIFEST_VERSION, ". Falling back to runtime defaults."));
|
|
2661
|
+
this._resetManifestCache();
|
|
2662
|
+
}
|
|
2663
|
+
} catch (error) {
|
|
2664
|
+
this._logger.warn("Failed to read MCP manifest at ".concat(path, ": ").concat(error.message, ". Falling back to runtime defaults."));
|
|
2665
|
+
this._resetManifestCache();
|
|
2666
|
+
}
|
|
2667
|
+
}
|
|
2668
|
+
},
|
|
2669
|
+
{
|
|
2670
|
+
key: "_applyParsedManifest",
|
|
2671
|
+
value: function _applyParsedManifest(parsed, path) {
|
|
2672
|
+
var map = new Map();
|
|
2673
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
2674
|
+
try {
|
|
2675
|
+
for(var _iterator = Object.entries(parsed.tools)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
2676
|
+
var _step_value = _sliced_to_array(_step.value, 2), key = _step_value[0], entry = _step_value[1];
|
|
2677
|
+
if (entry != null) {
|
|
2678
|
+
map.set(key, entry);
|
|
2679
|
+
}
|
|
2680
|
+
}
|
|
2681
|
+
} catch (err) {
|
|
2682
|
+
_didIteratorError = true;
|
|
2683
|
+
_iteratorError = err;
|
|
2684
|
+
} finally{
|
|
2685
|
+
try {
|
|
2686
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
2687
|
+
_iterator.return();
|
|
2688
|
+
}
|
|
2689
|
+
} finally{
|
|
2690
|
+
if (_didIteratorError) {
|
|
2691
|
+
throw _iteratorError;
|
|
2692
|
+
}
|
|
2693
|
+
}
|
|
2694
|
+
}
|
|
2695
|
+
var models = Array.isArray(parsed.models) && parsed.models.length > 0 ? parsed.models : undefined;
|
|
2696
|
+
var modelSuffix = models == null ? '' : ", ".concat(models.length, " model entries");
|
|
2697
|
+
var auth = parsed.auth != null && Array.isArray(parsed.auth.claims) ? parsed.auth : undefined;
|
|
2698
|
+
var authSuffix = auth == null ? '' : ", ".concat(auth.claims.length, " auth claim entries");
|
|
2699
|
+
this._logger.log("Loaded MCP manifest from ".concat(path, ": ").concat(map.size, " tool entries").concat(modelSuffix).concat(authSuffix, "."));
|
|
2700
|
+
this._cachedManifest = map;
|
|
2701
|
+
this._cachedManifestModels = models;
|
|
2702
|
+
this._cachedManifestAuth = auth;
|
|
2703
|
+
}
|
|
2704
|
+
},
|
|
2705
|
+
{
|
|
2706
|
+
/**
|
|
2707
|
+
* Reads the caller's OIDC scopes from the raw Express request via the auth context.
|
|
2708
|
+
*
|
|
2709
|
+
* Synthesizes the same `{ auth: { token } }` shape that `getOidcScopesFromRequest`
|
|
2710
|
+
* expects post-dispatch, so the upstream helper stays the single source of scope parsing.
|
|
2711
|
+
* Returns `undefined` for non-OIDC callers (no `oidcValidatedToken.scope`) — the filter
|
|
2712
|
+
* loop treats that as "skip scope enforcement", matching `oidcCallModelScopePreAssert`.
|
|
2713
|
+
*
|
|
2714
|
+
* @param ctx - The per-request context carrying the validated auth payload.
|
|
2715
|
+
* @returns The set of granted OIDC scopes, or `undefined` when scope enforcement should be skipped.
|
|
2716
|
+
*/ key: "_resolveScopes",
|
|
2717
|
+
value: function _resolveScopes(ctx) {
|
|
2718
|
+
var _ctx_auth;
|
|
2719
|
+
var oidcValidatedToken = (_ctx_auth = ctx.auth) === null || _ctx_auth === void 0 ? void 0 : _ctx_auth.oidcValidatedToken;
|
|
2720
|
+
return oidcValidatedToken == null ? undefined : getOidcScopesFromRequest({
|
|
2721
|
+
auth: {
|
|
2722
|
+
token: oidcValidatedToken
|
|
2723
|
+
}
|
|
2724
|
+
});
|
|
2725
|
+
}
|
|
2726
|
+
},
|
|
2727
|
+
{
|
|
2728
|
+
/**
|
|
2729
|
+
* Maps the caller's Firebase custom claims through the optional role reader.
|
|
2730
|
+
* Emits one boot-time warning when a declarative `requiredRoles` rule will be checked
|
|
2731
|
+
* but no reader is wired — that path will fail closed.
|
|
2732
|
+
*
|
|
2733
|
+
* @param ctx - The per-request context carrying the authenticated user's token.
|
|
2734
|
+
* @returns The resolved auth role set, or `undefined` when no auth or reader is available.
|
|
2735
|
+
*/ key: "_resolveAuthRoles",
|
|
2736
|
+
value: function _resolveAuthRoles(ctx) {
|
|
2737
|
+
var _ctx_auth;
|
|
2738
|
+
var result;
|
|
2739
|
+
if (((_ctx_auth = ctx.auth) === null || _ctx_auth === void 0 ? void 0 : _ctx_auth.token) != null && this.roleReader != null) {
|
|
2740
|
+
result = this.roleReader(ctx.auth.token);
|
|
2741
|
+
}
|
|
2742
|
+
return result;
|
|
2743
|
+
}
|
|
2744
|
+
},
|
|
2745
|
+
{
|
|
2746
|
+
key: "_filterToolsForRequest",
|
|
2747
|
+
value: function _filterToolsForRequest(tools, context) {
|
|
2748
|
+
var ctx = context.ctx, scopes = context.scopes, authRoles = context.authRoles;
|
|
2749
|
+
var readOnlyMode = this.mcpConfig.readOnly === true;
|
|
2750
|
+
var visible = [];
|
|
2751
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
2752
|
+
try {
|
|
2753
|
+
for(var _iterator = tools[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
2754
|
+
var tool = _step.value;
|
|
2755
|
+
var filterMetadata = tool.filterMetadata;
|
|
2756
|
+
if (scopes != null && filterMetadata.requiredScope != null && !scopes.has(filterMetadata.requiredScope)) {
|
|
2757
|
+
continue;
|
|
2758
|
+
}
|
|
2759
|
+
if (readOnlyMode && filterMetadata.effectiveReadOnly !== true) {
|
|
2760
|
+
continue;
|
|
2761
|
+
}
|
|
2762
|
+
if (!this._passesVisibility({
|
|
2763
|
+
tool: tool,
|
|
2764
|
+
ctx: ctx,
|
|
2765
|
+
scopes: scopes,
|
|
2766
|
+
authRoles: authRoles
|
|
2767
|
+
})) {
|
|
2768
|
+
continue;
|
|
2769
|
+
}
|
|
2770
|
+
visible.push(tool);
|
|
2771
|
+
}
|
|
2772
|
+
} catch (err) {
|
|
2773
|
+
_didIteratorError = true;
|
|
2774
|
+
_iteratorError = err;
|
|
2775
|
+
} finally{
|
|
2776
|
+
try {
|
|
2777
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
2778
|
+
_iterator.return();
|
|
2779
|
+
}
|
|
2780
|
+
} finally{
|
|
2781
|
+
if (_didIteratorError) {
|
|
2782
|
+
throw _iteratorError;
|
|
2783
|
+
}
|
|
2784
|
+
}
|
|
2785
|
+
}
|
|
2786
|
+
return visible;
|
|
2787
|
+
}
|
|
2788
|
+
},
|
|
2789
|
+
{
|
|
2790
|
+
key: "_passesVisibility",
|
|
2791
|
+
value: function _passesVisibility(input) {
|
|
2792
|
+
var tool = input.tool, ctx = input.ctx, scopes = input.scopes, authRoles = input.authRoles;
|
|
2793
|
+
var filterMetadata = tool.filterMetadata;
|
|
2794
|
+
var result;
|
|
2795
|
+
if (filterMetadata.visibilityKind === 'declarative') {
|
|
2796
|
+
result = this._checkDeclarativeVisibility(filterMetadata.rule, ctx, authRoles);
|
|
2797
|
+
} else if (filterMetadata.visibilityKind === 'dynamic') {
|
|
2798
|
+
try {
|
|
2799
|
+
result = filterMetadata.visibilityFn({
|
|
2800
|
+
auth: ctx.auth,
|
|
2801
|
+
scopes: scopes !== null && scopes !== void 0 ? scopes : undefined,
|
|
2802
|
+
tool: tool.dispatch
|
|
2803
|
+
}) === true;
|
|
2804
|
+
} catch (error) {
|
|
2805
|
+
this._logger.warn("MCP tool ".concat(tool.name, " visibility predicate threw; treating as not visible: ").concat(error.message));
|
|
2806
|
+
result = false;
|
|
2807
|
+
}
|
|
2808
|
+
} else {
|
|
2809
|
+
result = true;
|
|
2810
|
+
}
|
|
2811
|
+
return result;
|
|
2812
|
+
}
|
|
2813
|
+
},
|
|
2814
|
+
{
|
|
2815
|
+
key: "_checkDeclarativeVisibility",
|
|
2816
|
+
value: function _checkDeclarativeVisibility(rule, ctx, authRoles) {
|
|
2817
|
+
var result = true;
|
|
2818
|
+
if (rule.requireAuthenticated === true && ctx.auth == null) {
|
|
2819
|
+
result = false;
|
|
2820
|
+
} else if (rule.requiredRoles != null && rule.requiredRoles.length > 0) {
|
|
2821
|
+
if (authRoles == null) {
|
|
2822
|
+
if (this.roleReader == null && !this._warnedMissingRoleReader) {
|
|
2823
|
+
this._warnedMissingRoleReader = true;
|
|
2824
|
+
this._logger.warn('MCP tool visibility rule declared requiredRoles but no McpAuthRoleReader is provided. Tools gated by role will be hidden.');
|
|
2825
|
+
}
|
|
2826
|
+
result = false;
|
|
2827
|
+
} else {
|
|
2828
|
+
result = authRolesSetHasRoles(authRoles, rule.requiredRoles);
|
|
2829
|
+
}
|
|
2830
|
+
}
|
|
2831
|
+
return result;
|
|
2832
|
+
}
|
|
2833
|
+
},
|
|
2834
|
+
{
|
|
2835
|
+
/**
|
|
2836
|
+
* Resolves the wire-shape `tools/list` entry for a single tool.
|
|
2837
|
+
*
|
|
2838
|
+
* Hot-path short-circuit: tools without a `toolDetailsBuilder` reuse the precomputed,
|
|
2839
|
+
* frozen {@link McpToolDefinition.staticWireEntry} verbatim — zero allocations per
|
|
2840
|
+
* request for the common case.
|
|
2841
|
+
*
|
|
2842
|
+
* Tools that opted in to {@link McpToolDetailsBuilder} get a fresh wire entry built
|
|
2843
|
+
* from the builder's overrides. If the builder throws, the framework falls back to
|
|
2844
|
+
* the static defaults and logs a warning (fail-soft, matching `_passesVisibility`).
|
|
2845
|
+
*
|
|
2846
|
+
* @param tool - The tool definition whose wire entry is being resolved.
|
|
2847
|
+
* @param ctx - The per-request context forwarded to any dynamic details builder.
|
|
2848
|
+
* @param scopes - The caller's granted OIDC scopes forwarded to any dynamic details builder.
|
|
2849
|
+
* @returns The wire-shape entry to emit for this tool on `tools/list`.
|
|
2850
|
+
*/ key: "_buildToolListEntry",
|
|
2851
|
+
value: function _buildToolListEntry(tool, ctx, scopes) {
|
|
2852
|
+
var result;
|
|
2853
|
+
if (tool.toolDetailsBuilder == null) {
|
|
2854
|
+
result = tool.staticWireEntry;
|
|
2855
|
+
} else {
|
|
2856
|
+
var description = tool.description;
|
|
2857
|
+
var inputSchema = tool.inputSchema;
|
|
2858
|
+
try {
|
|
2859
|
+
var overrides = tool.toolDetailsBuilder({
|
|
2860
|
+
dispatch: tool.dispatch,
|
|
2861
|
+
defaultDescription: description,
|
|
2862
|
+
defaultInputSchema: inputSchema,
|
|
2863
|
+
auth: ctx.auth,
|
|
2864
|
+
scopes: scopes !== null && scopes !== void 0 ? scopes : undefined
|
|
2865
|
+
});
|
|
2866
|
+
if (overrides.description != null) {
|
|
2867
|
+
description = overrides.description;
|
|
2868
|
+
}
|
|
2869
|
+
if (overrides.inputSchema != null) {
|
|
2870
|
+
inputSchema = overrides.inputSchema;
|
|
2871
|
+
}
|
|
2872
|
+
} catch (error) {
|
|
2873
|
+
this._logger.warn("MCP tool ".concat(tool.name, " toolDetails builder threw; falling back to defaults: ").concat(error.message));
|
|
2874
|
+
}
|
|
2875
|
+
var wire = {
|
|
2876
|
+
name: tool.name,
|
|
2877
|
+
description: description,
|
|
2878
|
+
inputSchema: inputSchema !== null && inputSchema !== void 0 ? inputSchema : {
|
|
2879
|
+
type: 'object'
|
|
2880
|
+
}
|
|
2881
|
+
};
|
|
2882
|
+
if (tool.outputSchema != null) {
|
|
2883
|
+
wire.outputSchema = tool.outputSchema;
|
|
2884
|
+
}
|
|
2885
|
+
result = wire;
|
|
2886
|
+
}
|
|
2887
|
+
return result;
|
|
2888
|
+
}
|
|
2889
|
+
},
|
|
2890
|
+
{
|
|
2891
|
+
key: "_handleToolCall",
|
|
2892
|
+
value: function _handleToolCall(request, definitionsByName, ctx) {
|
|
2893
|
+
return _async_to_generator$2(function() {
|
|
2894
|
+
var _request_params_arguments, definition, args, response;
|
|
2895
|
+
return _ts_generator$2(this, function(_state) {
|
|
2896
|
+
switch(_state.label){
|
|
2897
|
+
case 0:
|
|
2898
|
+
definition = definitionsByName.get(request.params.name);
|
|
2899
|
+
args = (_request_params_arguments = request.params.arguments) !== null && _request_params_arguments !== void 0 ? _request_params_arguments : {};
|
|
2900
|
+
if (!(definition == null)) return [
|
|
2901
|
+
3,
|
|
2902
|
+
1
|
|
2903
|
+
];
|
|
2904
|
+
response = formatMcpToolErrorResponse(new Error("Unknown tool: ".concat(request.params.name)));
|
|
2905
|
+
return [
|
|
2906
|
+
3,
|
|
2907
|
+
5
|
|
2908
|
+
];
|
|
2909
|
+
case 1:
|
|
2910
|
+
if (!(definition.staticHandler == null)) return [
|
|
2911
|
+
3,
|
|
2912
|
+
3
|
|
2913
|
+
];
|
|
2914
|
+
return [
|
|
2915
|
+
4,
|
|
2916
|
+
this._handleCallModelToolCall(definition, args, ctx)
|
|
2917
|
+
];
|
|
2918
|
+
case 2:
|
|
2919
|
+
response = _state.sent();
|
|
2920
|
+
return [
|
|
2921
|
+
3,
|
|
2922
|
+
5
|
|
2923
|
+
];
|
|
2924
|
+
case 3:
|
|
2925
|
+
return [
|
|
2926
|
+
4,
|
|
2927
|
+
this._handleStaticToolCall(definition.staticHandler, args, ctx)
|
|
2928
|
+
];
|
|
2929
|
+
case 4:
|
|
2930
|
+
response = _state.sent();
|
|
2931
|
+
_state.label = 5;
|
|
2932
|
+
case 5:
|
|
2933
|
+
return [
|
|
2934
|
+
2,
|
|
2935
|
+
response
|
|
2936
|
+
];
|
|
2937
|
+
}
|
|
2938
|
+
});
|
|
2939
|
+
}).call(this);
|
|
2940
|
+
}
|
|
2941
|
+
},
|
|
2942
|
+
{
|
|
2943
|
+
key: "_handleStaticToolCall",
|
|
2944
|
+
value: function _handleStaticToolCall(staticHandler, args, ctx) {
|
|
2945
|
+
return _async_to_generator$2(function() {
|
|
2946
|
+
var response, error;
|
|
2947
|
+
return _ts_generator$2(this, function(_state) {
|
|
2948
|
+
switch(_state.label){
|
|
2949
|
+
case 0:
|
|
2950
|
+
_state.trys.push([
|
|
2951
|
+
0,
|
|
2952
|
+
2,
|
|
2953
|
+
,
|
|
2954
|
+
3
|
|
2955
|
+
]);
|
|
2956
|
+
return [
|
|
2957
|
+
4,
|
|
2958
|
+
staticHandler(args, ctx)
|
|
2959
|
+
];
|
|
2960
|
+
case 1:
|
|
2961
|
+
response = _state.sent();
|
|
2962
|
+
return [
|
|
2963
|
+
3,
|
|
2964
|
+
3
|
|
2965
|
+
];
|
|
2966
|
+
case 2:
|
|
2967
|
+
error = _state.sent();
|
|
2968
|
+
response = formatMcpToolErrorResponse(error);
|
|
2969
|
+
return [
|
|
2970
|
+
3,
|
|
2971
|
+
3
|
|
2972
|
+
];
|
|
2973
|
+
case 3:
|
|
2974
|
+
return [
|
|
2975
|
+
2,
|
|
2976
|
+
response
|
|
2977
|
+
];
|
|
2978
|
+
}
|
|
2979
|
+
});
|
|
2980
|
+
})();
|
|
2981
|
+
}
|
|
2982
|
+
},
|
|
2983
|
+
{
|
|
2984
|
+
key: "_handleCallModelToolCall",
|
|
2985
|
+
value: function _handleCallModelToolCall(definition, args, ctx) {
|
|
2986
|
+
return _async_to_generator$2(function() {
|
|
2987
|
+
var params, response, result, error;
|
|
2988
|
+
return _ts_generator$2(this, function(_state) {
|
|
2989
|
+
switch(_state.label){
|
|
2990
|
+
case 0:
|
|
2991
|
+
params = {
|
|
2992
|
+
call: definition.dispatch.call,
|
|
2993
|
+
modelType: definition.dispatch.modelType,
|
|
2994
|
+
specifier: definition.dispatch.specifier,
|
|
2995
|
+
data: args
|
|
2996
|
+
};
|
|
2997
|
+
_state.label = 1;
|
|
2998
|
+
case 1:
|
|
2999
|
+
_state.trys.push([
|
|
3000
|
+
1,
|
|
3001
|
+
3,
|
|
3002
|
+
,
|
|
3003
|
+
4
|
|
3004
|
+
]);
|
|
3005
|
+
return [
|
|
3006
|
+
4,
|
|
3007
|
+
this.dispatchService.dispatch(params, ctx.auth, ctx.rawRequest)
|
|
3008
|
+
];
|
|
3009
|
+
case 2:
|
|
3010
|
+
result = _state.sent();
|
|
3011
|
+
response = formatMcpToolResponse(result, params, definition.details);
|
|
3012
|
+
return [
|
|
3013
|
+
3,
|
|
3014
|
+
4
|
|
3015
|
+
];
|
|
3016
|
+
case 3:
|
|
3017
|
+
error = _state.sent();
|
|
3018
|
+
response = formatMcpToolErrorResponse(error);
|
|
3019
|
+
return [
|
|
3020
|
+
3,
|
|
3021
|
+
4
|
|
3022
|
+
];
|
|
3023
|
+
case 4:
|
|
3024
|
+
return [
|
|
3025
|
+
2,
|
|
3026
|
+
response
|
|
3027
|
+
];
|
|
3028
|
+
}
|
|
3029
|
+
});
|
|
3030
|
+
}).call(this);
|
|
3031
|
+
}
|
|
3032
|
+
}
|
|
3033
|
+
]);
|
|
3034
|
+
return McpServerFactoryService;
|
|
3035
|
+
}();
|
|
3036
|
+
McpServerFactoryService = McpServerFactoryService_1 = __decorate([
|
|
3037
|
+
Injectable(),
|
|
3038
|
+
__param(0, Inject(McpModuleConfig)),
|
|
3039
|
+
__param(1, Inject(ModelApiCallModelDispatchService)),
|
|
3040
|
+
__param(2, Optional()),
|
|
3041
|
+
__param(2, Inject(ModelApiGetService)),
|
|
3042
|
+
__param(3, Optional()),
|
|
3043
|
+
__param(3, Inject(MCP_AUTH_ROLE_READER))
|
|
3044
|
+
], McpServerFactoryService);
|
|
3045
|
+
|
|
3046
|
+
function asyncGeneratorStep$1(gen, resolve, reject, _next, _throw, key, arg) {
|
|
3047
|
+
try {
|
|
3048
|
+
var info = gen[key](arg);
|
|
3049
|
+
var value = info.value;
|
|
3050
|
+
} catch (error) {
|
|
3051
|
+
reject(error);
|
|
3052
|
+
return;
|
|
3053
|
+
}
|
|
3054
|
+
if (info.done) {
|
|
3055
|
+
resolve(value);
|
|
3056
|
+
} else {
|
|
3057
|
+
Promise.resolve(value).then(_next, _throw);
|
|
3058
|
+
}
|
|
3059
|
+
}
|
|
3060
|
+
function _async_to_generator$1(fn) {
|
|
3061
|
+
return function() {
|
|
3062
|
+
var self = this, args = arguments;
|
|
3063
|
+
return new Promise(function(resolve, reject) {
|
|
3064
|
+
var gen = fn.apply(self, args);
|
|
3065
|
+
function _next(value) {
|
|
3066
|
+
asyncGeneratorStep$1(gen, resolve, reject, _next, _throw, "next", value);
|
|
3067
|
+
}
|
|
3068
|
+
function _throw(err) {
|
|
3069
|
+
asyncGeneratorStep$1(gen, resolve, reject, _next, _throw, "throw", err);
|
|
3070
|
+
}
|
|
3071
|
+
_next(undefined);
|
|
3072
|
+
});
|
|
3073
|
+
};
|
|
3074
|
+
}
|
|
3075
|
+
function _ts_generator$1(thisArg, body) {
|
|
3076
|
+
var f, y, t, _ = {
|
|
3077
|
+
label: 0,
|
|
3078
|
+
sent: function() {
|
|
3079
|
+
if (t[0] & 1) throw t[1];
|
|
3080
|
+
return t[1];
|
|
3081
|
+
},
|
|
3082
|
+
trys: [],
|
|
3083
|
+
ops: []
|
|
3084
|
+
}, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype), d = Object.defineProperty;
|
|
3085
|
+
return d(g, "next", {
|
|
3086
|
+
value: verb(0)
|
|
3087
|
+
}), d(g, "throw", {
|
|
3088
|
+
value: verb(1)
|
|
3089
|
+
}), d(g, "return", {
|
|
3090
|
+
value: verb(2)
|
|
3091
|
+
}), typeof Symbol === "function" && d(g, Symbol.iterator, {
|
|
3092
|
+
value: function() {
|
|
3093
|
+
return this;
|
|
3094
|
+
}
|
|
3095
|
+
}), g;
|
|
3096
|
+
function verb(n) {
|
|
3097
|
+
return function(v) {
|
|
3098
|
+
return step([
|
|
3099
|
+
n,
|
|
3100
|
+
v
|
|
3101
|
+
]);
|
|
3102
|
+
};
|
|
3103
|
+
}
|
|
3104
|
+
function step(op) {
|
|
3105
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
3106
|
+
while(g && (g = 0, op[0] && (_ = 0)), _)try {
|
|
3107
|
+
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;
|
|
3108
|
+
if (y = 0, t) op = [
|
|
3109
|
+
op[0] & 2,
|
|
3110
|
+
t.value
|
|
3111
|
+
];
|
|
3112
|
+
switch(op[0]){
|
|
3113
|
+
case 0:
|
|
3114
|
+
case 1:
|
|
3115
|
+
t = op;
|
|
3116
|
+
break;
|
|
3117
|
+
case 4:
|
|
3118
|
+
_.label++;
|
|
3119
|
+
return {
|
|
3120
|
+
value: op[1],
|
|
3121
|
+
done: false
|
|
3122
|
+
};
|
|
3123
|
+
case 5:
|
|
3124
|
+
_.label++;
|
|
3125
|
+
y = op[1];
|
|
3126
|
+
op = [
|
|
3127
|
+
0
|
|
3128
|
+
];
|
|
3129
|
+
continue;
|
|
3130
|
+
case 7:
|
|
3131
|
+
op = _.ops.pop();
|
|
3132
|
+
_.trys.pop();
|
|
3133
|
+
continue;
|
|
3134
|
+
default:
|
|
3135
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
|
|
3136
|
+
_ = 0;
|
|
3137
|
+
continue;
|
|
3138
|
+
}
|
|
3139
|
+
if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) {
|
|
3140
|
+
_.label = op[1];
|
|
3141
|
+
break;
|
|
3142
|
+
}
|
|
3143
|
+
if (op[0] === 6 && _.label < t[1]) {
|
|
3144
|
+
_.label = t[1];
|
|
3145
|
+
t = op;
|
|
3146
|
+
break;
|
|
3147
|
+
}
|
|
3148
|
+
if (t && _.label < t[2]) {
|
|
3149
|
+
_.label = t[2];
|
|
3150
|
+
_.ops.push(op);
|
|
3151
|
+
break;
|
|
3152
|
+
}
|
|
3153
|
+
if (t[2]) _.ops.pop();
|
|
3154
|
+
_.trys.pop();
|
|
3155
|
+
continue;
|
|
3156
|
+
}
|
|
3157
|
+
op = body.call(thisArg, _);
|
|
3158
|
+
} catch (e) {
|
|
3159
|
+
op = [
|
|
3160
|
+
6,
|
|
3161
|
+
e
|
|
3162
|
+
];
|
|
3163
|
+
y = 0;
|
|
3164
|
+
} finally{
|
|
3165
|
+
f = t = 0;
|
|
3166
|
+
}
|
|
3167
|
+
if (op[0] & 5) throw op[1];
|
|
3168
|
+
return {
|
|
3169
|
+
value: op[0] ? op[1] : void 0,
|
|
3170
|
+
done: true
|
|
3171
|
+
};
|
|
3172
|
+
}
|
|
3173
|
+
}
|
|
3174
|
+
/**
|
|
3175
|
+
* Handles a single Streamable HTTP JSON-RPC request against the supplied MCP server.
|
|
3176
|
+
*
|
|
3177
|
+
* Each call creates a fresh `StreamableHTTPServerTransport` in stateless mode — the
|
|
3178
|
+
* transport binds the server, processes one request, and is discarded. Stateless
|
|
3179
|
+
* mode is adequate for Claude custom-connector usage and avoids the bookkeeping
|
|
3180
|
+
* cost of session-tracked transports for the common case.
|
|
3181
|
+
*
|
|
3182
|
+
* The caller is expected to have already validated the bearer token (via the
|
|
3183
|
+
* OIDC bearer middleware) before this function runs.
|
|
3184
|
+
*
|
|
3185
|
+
* @param req - The Express request carrying the JSON-RPC body.
|
|
3186
|
+
* @param res - The Express response.
|
|
3187
|
+
* @param server - The MCP server instance to back the transport.
|
|
3188
|
+
* @param server.connect - The MCP server's connect method that binds the transport before request handling.
|
|
3189
|
+
*/ function handleStreamableHttpMcpRequest(req, res, server) {
|
|
3190
|
+
return _async_to_generator$1(function() {
|
|
3191
|
+
var transport;
|
|
3192
|
+
return _ts_generator$1(this, function(_state) {
|
|
3193
|
+
switch(_state.label){
|
|
3194
|
+
case 0:
|
|
3195
|
+
// sessionIdGenerator: undefined → stateless mode.
|
|
3196
|
+
transport = new StreamableHTTPServerTransport({
|
|
3197
|
+
sessionIdGenerator: undefined
|
|
3198
|
+
});
|
|
3199
|
+
res.on('close', function() {
|
|
3200
|
+
void transport.close();
|
|
3201
|
+
});
|
|
3202
|
+
return [
|
|
3203
|
+
4,
|
|
3204
|
+
server.connect(transport)
|
|
3205
|
+
];
|
|
3206
|
+
case 1:
|
|
3207
|
+
_state.sent();
|
|
3208
|
+
return [
|
|
3209
|
+
4,
|
|
3210
|
+
transport.handleRequest(req, res, req.body)
|
|
3211
|
+
];
|
|
3212
|
+
case 2:
|
|
3213
|
+
_state.sent();
|
|
3214
|
+
return [
|
|
3215
|
+
2
|
|
3216
|
+
];
|
|
3217
|
+
}
|
|
3218
|
+
});
|
|
3219
|
+
})();
|
|
3220
|
+
}
|
|
3221
|
+
|
|
3222
|
+
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
|
|
3223
|
+
try {
|
|
3224
|
+
var info = gen[key](arg);
|
|
3225
|
+
var value = info.value;
|
|
3226
|
+
} catch (error) {
|
|
3227
|
+
reject(error);
|
|
3228
|
+
return;
|
|
3229
|
+
}
|
|
3230
|
+
if (info.done) {
|
|
3231
|
+
resolve(value);
|
|
3232
|
+
} else {
|
|
3233
|
+
Promise.resolve(value).then(_next, _throw);
|
|
3234
|
+
}
|
|
3235
|
+
}
|
|
3236
|
+
function _async_to_generator(fn) {
|
|
3237
|
+
return function() {
|
|
3238
|
+
var self = this, args = arguments;
|
|
3239
|
+
return new Promise(function(resolve, reject) {
|
|
3240
|
+
var gen = fn.apply(self, args);
|
|
3241
|
+
function _next(value) {
|
|
3242
|
+
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
|
|
3243
|
+
}
|
|
3244
|
+
function _throw(err) {
|
|
3245
|
+
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
|
|
3246
|
+
}
|
|
3247
|
+
_next(undefined);
|
|
3248
|
+
});
|
|
3249
|
+
};
|
|
3250
|
+
}
|
|
3251
|
+
function _class_call_check$1(instance, Constructor) {
|
|
3252
|
+
if (!(instance instanceof Constructor)) {
|
|
3253
|
+
throw new TypeError("Cannot call a class as a function");
|
|
3254
|
+
}
|
|
3255
|
+
}
|
|
3256
|
+
function _defineProperties$1(target, props) {
|
|
3257
|
+
for(var i = 0; i < props.length; i++){
|
|
3258
|
+
var descriptor = props[i];
|
|
3259
|
+
descriptor.enumerable = descriptor.enumerable || false;
|
|
3260
|
+
descriptor.configurable = true;
|
|
3261
|
+
if ("value" in descriptor) descriptor.writable = true;
|
|
3262
|
+
Object.defineProperty(target, descriptor.key, descriptor);
|
|
3263
|
+
}
|
|
3264
|
+
}
|
|
3265
|
+
function _create_class$1(Constructor, protoProps, staticProps) {
|
|
3266
|
+
if (protoProps) _defineProperties$1(Constructor.prototype, protoProps);
|
|
3267
|
+
return Constructor;
|
|
3268
|
+
}
|
|
3269
|
+
function _define_property$1(obj, key, value) {
|
|
3270
|
+
if (key in obj) {
|
|
3271
|
+
Object.defineProperty(obj, key, {
|
|
3272
|
+
value: value,
|
|
3273
|
+
enumerable: true,
|
|
3274
|
+
configurable: true,
|
|
3275
|
+
writable: true
|
|
3276
|
+
});
|
|
3277
|
+
} else {
|
|
3278
|
+
obj[key] = value;
|
|
3279
|
+
}
|
|
3280
|
+
return obj;
|
|
3281
|
+
}
|
|
3282
|
+
function _ts_generator(thisArg, body) {
|
|
3283
|
+
var f, y, t, _ = {
|
|
3284
|
+
label: 0,
|
|
3285
|
+
sent: function() {
|
|
3286
|
+
if (t[0] & 1) throw t[1];
|
|
3287
|
+
return t[1];
|
|
3288
|
+
},
|
|
3289
|
+
trys: [],
|
|
3290
|
+
ops: []
|
|
3291
|
+
}, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype), d = Object.defineProperty;
|
|
3292
|
+
return d(g, "next", {
|
|
3293
|
+
value: verb(0)
|
|
3294
|
+
}), d(g, "throw", {
|
|
3295
|
+
value: verb(1)
|
|
3296
|
+
}), d(g, "return", {
|
|
3297
|
+
value: verb(2)
|
|
3298
|
+
}), typeof Symbol === "function" && d(g, Symbol.iterator, {
|
|
3299
|
+
value: function() {
|
|
3300
|
+
return this;
|
|
3301
|
+
}
|
|
3302
|
+
}), g;
|
|
3303
|
+
function verb(n) {
|
|
3304
|
+
return function(v) {
|
|
3305
|
+
return step([
|
|
3306
|
+
n,
|
|
3307
|
+
v
|
|
3308
|
+
]);
|
|
3309
|
+
};
|
|
3310
|
+
}
|
|
3311
|
+
function step(op) {
|
|
3312
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
3313
|
+
while(g && (g = 0, op[0] && (_ = 0)), _)try {
|
|
3314
|
+
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;
|
|
3315
|
+
if (y = 0, t) op = [
|
|
3316
|
+
op[0] & 2,
|
|
3317
|
+
t.value
|
|
3318
|
+
];
|
|
3319
|
+
switch(op[0]){
|
|
3320
|
+
case 0:
|
|
3321
|
+
case 1:
|
|
3322
|
+
t = op;
|
|
3323
|
+
break;
|
|
3324
|
+
case 4:
|
|
3325
|
+
_.label++;
|
|
3326
|
+
return {
|
|
3327
|
+
value: op[1],
|
|
3328
|
+
done: false
|
|
3329
|
+
};
|
|
3330
|
+
case 5:
|
|
3331
|
+
_.label++;
|
|
3332
|
+
y = op[1];
|
|
3333
|
+
op = [
|
|
3334
|
+
0
|
|
3335
|
+
];
|
|
3336
|
+
continue;
|
|
3337
|
+
case 7:
|
|
3338
|
+
op = _.ops.pop();
|
|
3339
|
+
_.trys.pop();
|
|
3340
|
+
continue;
|
|
3341
|
+
default:
|
|
3342
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
|
|
3343
|
+
_ = 0;
|
|
3344
|
+
continue;
|
|
3345
|
+
}
|
|
3346
|
+
if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) {
|
|
3347
|
+
_.label = op[1];
|
|
3348
|
+
break;
|
|
3349
|
+
}
|
|
3350
|
+
if (op[0] === 6 && _.label < t[1]) {
|
|
3351
|
+
_.label = t[1];
|
|
3352
|
+
t = op;
|
|
3353
|
+
break;
|
|
3354
|
+
}
|
|
3355
|
+
if (t && _.label < t[2]) {
|
|
3356
|
+
_.label = t[2];
|
|
3357
|
+
_.ops.push(op);
|
|
3358
|
+
break;
|
|
3359
|
+
}
|
|
3360
|
+
if (t[2]) _.ops.pop();
|
|
3361
|
+
_.trys.pop();
|
|
3362
|
+
continue;
|
|
3363
|
+
}
|
|
3364
|
+
op = body.call(thisArg, _);
|
|
3365
|
+
} catch (e) {
|
|
3366
|
+
op = [
|
|
3367
|
+
6,
|
|
3368
|
+
e
|
|
3369
|
+
];
|
|
3370
|
+
y = 0;
|
|
3371
|
+
} finally{
|
|
3372
|
+
f = t = 0;
|
|
3373
|
+
}
|
|
3374
|
+
if (op[0] & 5) throw op[1];
|
|
3375
|
+
return {
|
|
3376
|
+
value: op[0] ? op[1] : void 0,
|
|
3377
|
+
done: true
|
|
3378
|
+
};
|
|
3379
|
+
}
|
|
3380
|
+
}
|
|
3381
|
+
var McpController_1;
|
|
3382
|
+
/**
|
|
3383
|
+
* NestJS controller mounting the MCP Streamable HTTP transport at `POST /mcp`.
|
|
3384
|
+
*
|
|
3385
|
+
* Auth is enforced by the global OIDC bearer middleware (`OidcAuthBearerTokenMiddleware`)
|
|
3386
|
+
* which must include `'/mcp'` in its `protectedPaths`. By the time the request reaches
|
|
3387
|
+
* this controller, `req.auth` is populated with the authenticated user's data.
|
|
3388
|
+
*
|
|
3389
|
+
* Each request gets a fresh transport + MCP server pair (stateless mode), which is
|
|
3390
|
+
* adequate for Claude custom-connector style usage. A session-tracked variant can
|
|
3391
|
+
* be layered on later if streaming tool output becomes a requirement.
|
|
3392
|
+
*/ var McpController = McpController_1 = /*#__PURE__*/ function() {
|
|
3393
|
+
function McpController(factory) {
|
|
3394
|
+
_class_call_check$1(this, McpController);
|
|
3395
|
+
_define_property$1(this, "factory", void 0);
|
|
3396
|
+
_define_property$1(this, "_logger", new Logger(McpController_1.name));
|
|
3397
|
+
this.factory = factory;
|
|
3398
|
+
}
|
|
3399
|
+
_create_class$1(McpController, [
|
|
3400
|
+
{
|
|
3401
|
+
key: "handleMcpRequest",
|
|
3402
|
+
value: function handleMcpRequest(req, res) {
|
|
3403
|
+
return _async_to_generator(function() {
|
|
3404
|
+
var auth, server, error;
|
|
3405
|
+
return _ts_generator(this, function(_state) {
|
|
3406
|
+
switch(_state.label){
|
|
3407
|
+
case 0:
|
|
3408
|
+
auth = req.auth;
|
|
3409
|
+
server = this.factory.createServer({
|
|
3410
|
+
auth: auth,
|
|
3411
|
+
rawRequest: req
|
|
3412
|
+
});
|
|
3413
|
+
_state.label = 1;
|
|
3414
|
+
case 1:
|
|
3415
|
+
_state.trys.push([
|
|
3416
|
+
1,
|
|
3417
|
+
3,
|
|
3418
|
+
,
|
|
3419
|
+
4
|
|
3420
|
+
]);
|
|
3421
|
+
return [
|
|
3422
|
+
4,
|
|
3423
|
+
handleStreamableHttpMcpRequest(req, res, server)
|
|
3424
|
+
];
|
|
3425
|
+
case 2:
|
|
3426
|
+
_state.sent();
|
|
3427
|
+
return [
|
|
3428
|
+
3,
|
|
3429
|
+
4
|
|
3430
|
+
];
|
|
3431
|
+
case 3:
|
|
3432
|
+
error = _state.sent();
|
|
3433
|
+
this._logger.error('MCP request handling failed', error);
|
|
3434
|
+
if (!res.headersSent) {
|
|
3435
|
+
res.status(500).json({
|
|
3436
|
+
statusCode: 500,
|
|
3437
|
+
message: 'MCP request handling failed'
|
|
3438
|
+
});
|
|
3439
|
+
}
|
|
3440
|
+
return [
|
|
3441
|
+
3,
|
|
3442
|
+
4
|
|
3443
|
+
];
|
|
3444
|
+
case 4:
|
|
3445
|
+
return [
|
|
3446
|
+
2
|
|
3447
|
+
];
|
|
3448
|
+
}
|
|
3449
|
+
});
|
|
3450
|
+
}).call(this);
|
|
3451
|
+
}
|
|
3452
|
+
}
|
|
3453
|
+
]);
|
|
3454
|
+
return McpController;
|
|
3455
|
+
}();
|
|
3456
|
+
__decorate([
|
|
3457
|
+
Post(),
|
|
3458
|
+
__param(0, Req()),
|
|
3459
|
+
__param(1, Res())
|
|
3460
|
+
], McpController.prototype, "handleMcpRequest", null);
|
|
3461
|
+
McpController = McpController_1 = __decorate([
|
|
3462
|
+
Controller('mcp'),
|
|
3463
|
+
__param(0, Inject(McpServerFactoryService))
|
|
3464
|
+
], McpController);
|
|
3465
|
+
|
|
3466
|
+
function _class_call_check(instance, Constructor) {
|
|
3467
|
+
if (!(instance instanceof Constructor)) {
|
|
3468
|
+
throw new TypeError("Cannot call a class as a function");
|
|
3469
|
+
}
|
|
3470
|
+
}
|
|
3471
|
+
function _defineProperties(target, props) {
|
|
3472
|
+
for(var i = 0; i < props.length; i++){
|
|
3473
|
+
var descriptor = props[i];
|
|
3474
|
+
descriptor.enumerable = descriptor.enumerable || false;
|
|
3475
|
+
descriptor.configurable = true;
|
|
3476
|
+
if ("value" in descriptor) descriptor.writable = true;
|
|
3477
|
+
Object.defineProperty(target, descriptor.key, descriptor);
|
|
3478
|
+
}
|
|
3479
|
+
}
|
|
3480
|
+
function _create_class(Constructor, protoProps, staticProps) {
|
|
3481
|
+
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
|
|
3482
|
+
return Constructor;
|
|
3483
|
+
}
|
|
3484
|
+
function _define_property(obj, key, value) {
|
|
3485
|
+
if (key in obj) {
|
|
3486
|
+
Object.defineProperty(obj, key, {
|
|
3487
|
+
value: value,
|
|
3488
|
+
enumerable: true,
|
|
3489
|
+
configurable: true,
|
|
3490
|
+
writable: true
|
|
3491
|
+
});
|
|
3492
|
+
} else {
|
|
3493
|
+
obj[key] = value;
|
|
3494
|
+
}
|
|
3495
|
+
return obj;
|
|
3496
|
+
}
|
|
3497
|
+
/**
|
|
3498
|
+
* Serves the `GET /.well-known/oauth-protected-resource` metadata document so
|
|
3499
|
+
* Claude (and other MCP clients) can discover which OIDC issuer guards this
|
|
3500
|
+
* MCP endpoint.
|
|
3501
|
+
*
|
|
3502
|
+
* The route is registered without a controller-level prefix because well-known
|
|
3503
|
+
* URIs must live at the host root. Apps need to exclude `.well-known/{*path}`
|
|
3504
|
+
* from any global API route prefix (see `FIREBASE_SERVER_OIDC_ROUTES_FOR_GLOBAL_ROUTE_EXCLUDE`
|
|
3505
|
+
* in `firebase-server/oidc` for the corresponding OIDC excludes).
|
|
3506
|
+
*/ var McpWellKnownController = /*#__PURE__*/ function() {
|
|
3507
|
+
function McpWellKnownController(mcpConfig) {
|
|
3508
|
+
_class_call_check(this, McpWellKnownController);
|
|
3509
|
+
_define_property(this, "mcpConfig", void 0);
|
|
3510
|
+
this.mcpConfig = mcpConfig;
|
|
3511
|
+
}
|
|
3512
|
+
_create_class(McpWellKnownController, [
|
|
3513
|
+
{
|
|
3514
|
+
key: "getProtectedResourceMetadata",
|
|
3515
|
+
value: function getProtectedResourceMetadata() {
|
|
3516
|
+
return {
|
|
3517
|
+
resource: this.mcpConfig.mcpUrl,
|
|
3518
|
+
authorization_servers: [
|
|
3519
|
+
this.mcpConfig.oidcIssuer
|
|
3520
|
+
]
|
|
3521
|
+
};
|
|
3522
|
+
}
|
|
3523
|
+
}
|
|
3524
|
+
]);
|
|
3525
|
+
return McpWellKnownController;
|
|
3526
|
+
}();
|
|
3527
|
+
__decorate([
|
|
3528
|
+
Get('oauth-protected-resource')
|
|
3529
|
+
], McpWellKnownController.prototype, "getProtectedResourceMetadata", null);
|
|
3530
|
+
McpWellKnownController = __decorate([
|
|
3531
|
+
Controller('.well-known'),
|
|
3532
|
+
__param(0, Inject(McpModuleConfig))
|
|
3533
|
+
], McpWellKnownController);
|
|
3534
|
+
|
|
3535
|
+
function _array_like_to_array(arr, len) {
|
|
3536
|
+
if (len == null || len > arr.length) len = arr.length;
|
|
3537
|
+
for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
|
|
3538
|
+
return arr2;
|
|
3539
|
+
}
|
|
3540
|
+
function _array_without_holes(arr) {
|
|
3541
|
+
if (Array.isArray(arr)) return _array_like_to_array(arr);
|
|
3542
|
+
}
|
|
3543
|
+
function _iterable_to_array(iter) {
|
|
3544
|
+
if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
|
|
3545
|
+
}
|
|
3546
|
+
function _non_iterable_spread() {
|
|
3547
|
+
throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
3548
|
+
}
|
|
3549
|
+
function _to_consumable_array(arr) {
|
|
3550
|
+
return _array_without_holes(arr) || _iterable_to_array(arr) || _unsupported_iterable_to_array(arr) || _non_iterable_spread();
|
|
3551
|
+
}
|
|
3552
|
+
function _unsupported_iterable_to_array(o, minLen) {
|
|
3553
|
+
if (!o) return;
|
|
3554
|
+
if (typeof o === "string") return _array_like_to_array(o, minLen);
|
|
3555
|
+
var n = Object.prototype.toString.call(o).slice(8, -1);
|
|
3556
|
+
if (n === "Object" && o.constructor) n = o.constructor.name;
|
|
3557
|
+
if (n === "Map" || n === "Set") return Array.from(n);
|
|
3558
|
+
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
|
|
3559
|
+
}
|
|
3560
|
+
/**
|
|
3561
|
+
* Routes the firebase-server/mcp module owns. Apps should exclude these from
|
|
3562
|
+
* any global API route prefix (`globalApiRoutePrefix.exclude`) so the canonical
|
|
3563
|
+
* URLs land at `/.well-known/...` and `/mcp` rather than `/api/.well-known/...`.
|
|
3564
|
+
*/ var FIREBASE_SERVER_MCP_ROUTES_FOR_GLOBAL_ROUTE_EXCLUDE = [
|
|
3565
|
+
'.well-known/{*path}',
|
|
3566
|
+
'mcp'
|
|
3567
|
+
];
|
|
3568
|
+
/**
|
|
3569
|
+
* Generates NestJS module metadata for the firebase-server/mcp module.
|
|
3570
|
+
*
|
|
3571
|
+
* Mirrors the convention used by {@link modelApiModuleMetadata}: the consumer provides
|
|
3572
|
+
* a dependency module that exposes the required tokens, and this factory wires up the
|
|
3573
|
+
* controllers + factory service.
|
|
3574
|
+
*
|
|
3575
|
+
* @param metadataConfig - Configuration including the dependency module.
|
|
3576
|
+
* @returns NestJS module metadata exposing the MCP transport + well-known controller.
|
|
3577
|
+
*
|
|
3578
|
+
* @Module ({
|
|
3579
|
+
* imports: [DemoModelApiModule],
|
|
3580
|
+
* providers: [{ provide: McpModuleConfig, useValue: { oidcIssuer, mcpUrl } }],
|
|
3581
|
+
* exports: [McpModuleConfig, ModelApiCallModelDispatchService, MODEL_API_NEST_APPLICATION_CONTEXT]
|
|
3582
|
+
* })
|
|
3583
|
+
* export class DemoMcpDependencyModule {}
|
|
3584
|
+
* @Module (mcpModuleMetadata({ dependencyModule: DemoMcpDependencyModule }))
|
|
3585
|
+
* export class DemoMcpModule {}
|
|
3586
|
+
* ```
|
|
3587
|
+
*
|
|
3588
|
+
* @example
|
|
3589
|
+
* ```typescript
|
|
3590
|
+
*/ function mcpModuleMetadata(metadataConfig) {
|
|
3591
|
+
var dependencyModule = metadataConfig.dependencyModule, imports = metadataConfig.imports, exports = metadataConfig.exports, providers = metadataConfig.providers;
|
|
3592
|
+
return {
|
|
3593
|
+
imports: [
|
|
3594
|
+
dependencyModule
|
|
3595
|
+
].concat(_to_consumable_array(imports !== null && imports !== void 0 ? imports : [])),
|
|
3596
|
+
controllers: [
|
|
3597
|
+
McpController,
|
|
3598
|
+
McpWellKnownController
|
|
3599
|
+
],
|
|
3600
|
+
exports: [
|
|
3601
|
+
McpServerFactoryService
|
|
3602
|
+
].concat(_to_consumable_array(exports !== null && exports !== void 0 ? exports : [])),
|
|
3603
|
+
providers: [
|
|
3604
|
+
McpServerFactoryService
|
|
3605
|
+
].concat(_to_consumable_array(providers !== null && providers !== void 0 ? providers : []))
|
|
3606
|
+
};
|
|
3607
|
+
}
|
|
3608
|
+
|
|
3609
|
+
export { DEFAULT_JSON_SCHEMA_GENERATION_OPTIONS, DEFAULT_MCP_PATH, DEFAULT_MCP_SERVER_NAME, DEFAULT_SPECIFIER_KEY, DEFAULT_VOID_MCP_SUCCESS_VALUE, FIREBASE_SERVER_MCP_ROUTES_FOR_GLOBAL_ROUTE_EXCLUDE, MCP_AUTH_ROLE_READER, MCP_MANIFEST_VERSION, MCP_MODEL_GET_BATCH_SIZE, MODEL_DECODE_DISPATCH_CALL, MODEL_DECODE_DISPATCH_MODEL_TYPE, MODEL_DECODE_TOOL_NAME, MODEL_GET_DISPATCH_CALL, MODEL_GET_DISPATCH_MODEL_TYPE, MODEL_GET_TOOL_NAME, MODEL_INFO_DISPATCH_CALL, MODEL_INFO_DISPATCH_MODEL_TYPE, MODEL_INFO_TOOL_NAME, McpController, McpModuleConfig, McpServerFactoryService, McpWellKnownController, buildDefaultMcpToolDescription, buildMcpToolName, buildStaticWireEntry, classifyVisibility, createModelDecodeTool, createModelGetTool, createModelInfoTool, decodeFirestoreModelKey, findModelEntry, formatMcpToolErrorResponse, formatMcpToolResponse, generateMcpToolDefinitions, handleStreamableHttpMcpRequest, mcpManifestKey, mcpModuleMetadata, resolveEffectiveReadOnly, resolveRequiredScope };
|