@dereekb/firebase-server 13.36.0 → 13.38.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/calcom/package.json +10 -10
- package/discord/package.json +10 -10
- package/index.cjs.js +1732 -510
- package/index.esm.js +1717 -513
- package/mailgun/package.json +9 -9
- package/mcp/index.cjs.js +913 -167
- package/mcp/index.esm.js +906 -166
- package/mcp/package.json +13 -12
- package/mcp/src/lib/controller/mcp.controller.d.ts +28 -3
- package/mcp/src/lib/controller/mcp.wellknown.controller.d.ts +21 -1
- package/mcp/src/lib/mcp.config.d.ts +31 -8
- package/mcp/src/lib/mcp.module.d.ts +2 -1
- package/mcp/src/lib/service/index.d.ts +1 -0
- package/mcp/src/lib/service/mcp.server.factory.d.ts +8 -6
- package/mcp/src/lib/service/mcp.tool-generator.d.ts +1 -1
- package/mcp/src/lib/service/mcp.visibility.d.ts +1 -1
- package/mcp/src/lib/service/tools/mcp.tool.model-roles.d.ts +95 -0
- package/mcp/src/lib/transport/streamable-http.transport.d.ts +16 -11
- package/model/index.cjs.js +1 -1
- package/model/index.esm.js +1 -1
- package/model/package.json +9 -9
- package/oidc/index.cjs.js +257 -67
- package/oidc/index.esm.js +256 -69
- package/oidc/package.json +10 -10
- package/oidc/src/lib/profile.d.ts +21 -0
- package/oidc/src/lib/service/index.d.ts +1 -0
- package/oidc/src/lib/service/oidc.config.service.d.ts +14 -0
- package/oidc/src/lib/service/oidc.interaction-policy.d.ts +35 -0
- package/oidc/src/lib/service/oidc.service.d.ts +29 -0
- package/package.json +14 -13
- package/src/lib/env/env.service.d.ts +27 -3
- package/src/lib/nest/controller/api.scope.d.ts +63 -0
- package/src/lib/nest/controller/index.d.ts +2 -0
- package/src/lib/nest/controller/model/model.api.get.service.d.ts +146 -1
- package/src/lib/nest/controller/model/model.api.scope.d.ts +3 -2
- package/src/lib/nest/controller/session/index.d.ts +4 -0
- package/src/lib/nest/controller/session/session.api.config.d.ts +104 -0
- package/src/lib/nest/controller/session/session.api.controller.d.ts +26 -0
- package/src/lib/nest/controller/session/session.api.module.d.ts +49 -0
- package/src/lib/nest/controller/session/session.api.service.d.ts +71 -0
- package/test/index.cjs.js +8 -8
- package/test/index.esm.js +8 -8
- package/test/package.json +11 -11
- package/twilio/package.json +8 -8
- package/zoho/package.json +10 -10
package/mcp/index.cjs.js
CHANGED
|
@@ -2,22 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
var common = require('@nestjs/common');
|
|
4
4
|
var node_fs = require('node:fs');
|
|
5
|
-
var
|
|
6
|
-
var types_js = require('@modelcontextprotocol/sdk/types.js');
|
|
5
|
+
var server = require('@modelcontextprotocol/server');
|
|
7
6
|
var firebase = require('@dereekb/firebase');
|
|
8
7
|
var oidc = require('@dereekb/firebase-server/oidc');
|
|
9
8
|
var util = require('@dereekb/util');
|
|
10
9
|
var firebaseServer = require('@dereekb/firebase-server');
|
|
11
10
|
var node_util = require('node:util');
|
|
12
11
|
var model = require('@dereekb/model');
|
|
13
|
-
var
|
|
12
|
+
var node = require('@modelcontextprotocol/node');
|
|
14
13
|
|
|
15
14
|
function _class_call_check$5(instance, Constructor) {
|
|
16
15
|
if (!(instance instanceof Constructor)) {
|
|
17
16
|
throw new TypeError("Cannot call a class as a function");
|
|
18
17
|
}
|
|
19
18
|
}
|
|
20
|
-
function _define_property$
|
|
19
|
+
function _define_property$a(obj, key, value) {
|
|
21
20
|
if (key in obj) {
|
|
22
21
|
Object.defineProperty(obj, key, {
|
|
23
22
|
value: value,
|
|
@@ -88,7 +87,7 @@ function _define_property$9(obj, key, value) {
|
|
|
88
87
|
* discovery document. Claude custom-connector reads this to discover the OIDC issuer.
|
|
89
88
|
*
|
|
90
89
|
* @example 'https://api.example.com/oidc'
|
|
91
|
-
*/ _define_property$
|
|
90
|
+
*/ _define_property$a(this, "oidcIssuer", void 0);
|
|
92
91
|
/**
|
|
93
92
|
* The canonical resource URL of the MCP endpoint.
|
|
94
93
|
*
|
|
@@ -96,17 +95,18 @@ function _define_property$9(obj, key, value) {
|
|
|
96
95
|
* and used as the audience the access token's `aud` claim should match.
|
|
97
96
|
*
|
|
98
97
|
* @example 'https://api.example.com/mcp'
|
|
99
|
-
*/ _define_property$
|
|
98
|
+
*/ _define_property$a(this, "mcpUrl", void 0);
|
|
100
99
|
/**
|
|
101
100
|
* Optional filter applied to the OIDC provider's scope list when building the
|
|
102
101
|
* protected-resource discovery document's `scopes_supported` (RFC 9728 §2).
|
|
103
102
|
*
|
|
104
103
|
* The base list is pulled automatically from the OIDC provider config via the
|
|
105
|
-
* injected {@link OidcProviderConfigService.
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
104
|
+
* injected {@link OidcProviderConfigService.clientRequestableScopesSupported}, so
|
|
105
|
+
* the MCP resource advertises the scopes the issuer grants an arbitrary client
|
|
106
|
+
* without the app restating them. Scopes only an admin-assigned provider profile
|
|
107
|
+
* unlocks are already excluded — requesting one is fatal at the consent unlock
|
|
108
|
+
* gate. Provide this only to narrow the set further; it returns the subset to
|
|
109
|
+
* advertise. When unset, the whole base list is advertised.
|
|
110
110
|
*
|
|
111
111
|
* Advertising these matters because dynamic-registration MCP clients (the Claude
|
|
112
112
|
* Code CLI) read `scopes_supported` to decide which scopes to request on the
|
|
@@ -114,19 +114,19 @@ function _define_property$9(obj, key, value) {
|
|
|
114
114
|
* the OIDC provider nothing to grant and ending the interaction in `access_denied`.
|
|
115
115
|
*
|
|
116
116
|
* @example (allScopes) => allScopes.filter((scope) => scope === 'openid' || scope.startsWith('model.'))
|
|
117
|
-
*/ _define_property$
|
|
117
|
+
*/ _define_property$a(this, "scopesSupported", void 0);
|
|
118
118
|
/**
|
|
119
119
|
* Optional name advertised on the MCP `initialize` handshake. Defaults to
|
|
120
120
|
* {@link DEFAULT_MCP_SERVER_NAME}.
|
|
121
|
-
*/ _define_property$
|
|
121
|
+
*/ _define_property$a(this, "serverName", void 0);
|
|
122
122
|
/**
|
|
123
123
|
* Optional version advertised on the MCP `initialize` handshake.
|
|
124
|
-
*/ _define_property$
|
|
124
|
+
*/ _define_property$a(this, "serverVersion", void 0);
|
|
125
125
|
/**
|
|
126
126
|
* Optional override for the `instructions` string passed to `new McpServer(...)` and
|
|
127
127
|
* advertised on the JSON-RPC `initialize` handshake. Defaults to
|
|
128
128
|
* {@link DEFAULT_MCP_SERVER_INSTRUCTIONS}.
|
|
129
|
-
*/ _define_property$
|
|
129
|
+
*/ _define_property$a(this, "serverInstructions", void 0);
|
|
130
130
|
/**
|
|
131
131
|
* Absolute path to a pre-rendered MCP manifest JSON file produced by
|
|
132
132
|
* `dbx-cli-generate-mcp-manifest`. When set, the runtime reads it once at
|
|
@@ -136,7 +136,7 @@ function _define_property$9(obj, key, value) {
|
|
|
136
136
|
* Optional. When unset or the file is missing, the runtime falls back to
|
|
137
137
|
* today's behavior (auto-generated descriptions, ArkType-derived input
|
|
138
138
|
* schemas, no output schemas) and emits a single boot warning.
|
|
139
|
-
*/ _define_property$
|
|
139
|
+
*/ _define_property$a(this, "mcpManifestPath", void 0);
|
|
140
140
|
/**
|
|
141
141
|
* Absolute path to a pre-rendered route manifest JSON file produced by
|
|
142
142
|
* `dbx-cli-generate-route-manifest`. When set, the runtime reads it once at
|
|
@@ -146,7 +146,7 @@ function _define_property$9(obj, key, value) {
|
|
|
146
146
|
* Optional. When unset or the file is missing, the runtime skips registering
|
|
147
147
|
* `url-models` and emits a single boot warning when the path was set but
|
|
148
148
|
* unreadable.
|
|
149
|
-
*/ _define_property$
|
|
149
|
+
*/ _define_property$a(this, "mcpRouteManifestPath", void 0);
|
|
150
150
|
/**
|
|
151
151
|
* When `true`, the MCP server only advertises tools whose effective read-only classification is `true`.
|
|
152
152
|
*
|
|
@@ -156,7 +156,7 @@ function _define_property$9(obj, key, value) {
|
|
|
156
156
|
*
|
|
157
157
|
* The advertised `serverName` on the JSON-RPC `initialize` handshake is suffixed with
|
|
158
158
|
* ` (read-only)` so the client surface reflects the mode.
|
|
159
|
-
*/ _define_property$
|
|
159
|
+
*/ _define_property$a(this, "readOnly", void 0);
|
|
160
160
|
/**
|
|
161
161
|
* Controls the auto-injected `reason` parameter added to every advertised tool's input schema.
|
|
162
162
|
*
|
|
@@ -166,7 +166,7 @@ function _define_property$9(obj, key, value) {
|
|
|
166
166
|
* When enabled, every advertised tool's `inputSchema` carries a required `reason` string the model
|
|
167
167
|
* fills with a short justification for the call. The value is forwarded to analytics and stripped
|
|
168
168
|
* from the dispatched handler body. See {@link McpReasonParameterConfig}.
|
|
169
|
-
*/ _define_property$
|
|
169
|
+
*/ _define_property$a(this, "reasonParameter", void 0);
|
|
170
170
|
/**
|
|
171
171
|
* Default OIDC scope group term required on EVERY `callModel` op, mirrored into MCP tool-list
|
|
172
172
|
* visibility, unless a finer term overrides it (a per-function `requiredScope`, then a
|
|
@@ -176,17 +176,20 @@ function _define_property$9(obj, key, value) {
|
|
|
176
176
|
*
|
|
177
177
|
* Set this to the SAME value passed to `ModelApiDispatchConfig.defaultRequiredScope` to
|
|
178
178
|
* keep tool visibility and enforcement in lockstep.
|
|
179
|
-
*/ _define_property$
|
|
179
|
+
*/ _define_property$a(this, "defaultRequiredScope", void 0);
|
|
180
180
|
/**
|
|
181
181
|
* Per-model OIDC scope group-term overrides, keyed by {@link FirestoreModelType}, mirrored into MCP
|
|
182
182
|
* tool-list visibility. Set this to the SAME value passed to
|
|
183
183
|
* `ModelApiDispatchConfig.modelRequiredScopes` so per-model / verb-keyed restrictions that
|
|
184
184
|
* confine a subset client apply identically to the advertised tool list.
|
|
185
|
-
*/ _define_property$
|
|
185
|
+
*/ _define_property$a(this, "modelRequiredScopes", void 0);
|
|
186
186
|
};
|
|
187
187
|
/**
|
|
188
188
|
* NestJS injection token for the optional {@link McpAuthRoleReader} provider.
|
|
189
189
|
*/ var MCP_AUTH_ROLE_READER = 'MCP_AUTH_ROLE_READER';
|
|
190
|
+
/**
|
|
191
|
+
* NestJS injection token for the optional {@link McpModelRolesTargetUidPredicate} provider.
|
|
192
|
+
*/ var MCP_MODEL_ROLES_TARGET_UID_PREDICATE = 'MCP_MODEL_ROLES_TARGET_UID_PREDICATE';
|
|
190
193
|
|
|
191
194
|
function _type_of$5(obj) {
|
|
192
195
|
"@swc/helpers - typeof";
|
|
@@ -216,7 +219,7 @@ function _array_like_to_array$b(arr, len) {
|
|
|
216
219
|
function _array_without_holes$8(arr) {
|
|
217
220
|
if (Array.isArray(arr)) return _array_like_to_array$b(arr);
|
|
218
221
|
}
|
|
219
|
-
function _define_property$
|
|
222
|
+
function _define_property$9(obj, key, value) {
|
|
220
223
|
if (key in obj) {
|
|
221
224
|
Object.defineProperty(obj, key, {
|
|
222
225
|
value: value,
|
|
@@ -235,7 +238,7 @@ function _iterable_to_array$8(iter) {
|
|
|
235
238
|
function _non_iterable_spread$8() {
|
|
236
239
|
throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
237
240
|
}
|
|
238
|
-
function _object_spread$
|
|
241
|
+
function _object_spread$7(target) {
|
|
239
242
|
for(var i = 1; i < arguments.length; i++){
|
|
240
243
|
var source = arguments[i] != null ? arguments[i] : {};
|
|
241
244
|
var ownKeys = Object.keys(source);
|
|
@@ -245,7 +248,7 @@ function _object_spread$6(target) {
|
|
|
245
248
|
}));
|
|
246
249
|
}
|
|
247
250
|
ownKeys.forEach(function(key) {
|
|
248
|
-
_define_property$
|
|
251
|
+
_define_property$9(target, key, source[key]);
|
|
249
252
|
});
|
|
250
253
|
}
|
|
251
254
|
return target;
|
|
@@ -355,9 +358,9 @@ function _unsupported_iterable_to_array$b(o, minLen) {
|
|
|
355
358
|
var _base_type;
|
|
356
359
|
var base = (typeof inputSchema === "undefined" ? "undefined" : _type_of$4(inputSchema)) === 'object' && inputSchema != null ? inputSchema : {};
|
|
357
360
|
var existingProperties = _type_of$4(base.properties) === 'object' && base.properties != null ? base.properties : {};
|
|
358
|
-
var next = _object_spread_props$3(_object_spread$
|
|
361
|
+
var next = _object_spread_props$3(_object_spread$7({}, base), {
|
|
359
362
|
type: (_base_type = base.type) !== null && _base_type !== void 0 ? _base_type : 'object',
|
|
360
|
-
properties: _object_spread_props$3(_object_spread$
|
|
363
|
+
properties: _object_spread_props$3(_object_spread$7({}, existingProperties), _define_property$9({}, resolved.parameterName, {
|
|
361
364
|
type: 'string',
|
|
362
365
|
maxLength: resolved.maxLength,
|
|
363
366
|
description: resolved.description
|
|
@@ -391,7 +394,7 @@ function _unsupported_iterable_to_array$b(o, minLen) {
|
|
|
391
394
|
var result;
|
|
392
395
|
if (resolved.enabled && !toolDeclaresOwnReason && Object.prototype.hasOwnProperty.call(args, resolved.parameterName)) {
|
|
393
396
|
var rawValue = args[resolved.parameterName];
|
|
394
|
-
var nextArgs = _object_spread$
|
|
397
|
+
var nextArgs = _object_spread$7({}, args);
|
|
395
398
|
delete nextArgs[resolved.parameterName];
|
|
396
399
|
var stringValue = rawValue == null ? '' : String(rawValue);
|
|
397
400
|
var clamped = stringValue.length > resolved.maxLength ? stringValue.slice(0, resolved.maxLength) : stringValue;
|
|
@@ -746,7 +749,7 @@ function scoreCandidates(states, pathname) {
|
|
|
746
749
|
});
|
|
747
750
|
}
|
|
748
751
|
|
|
749
|
-
function asyncGeneratorStep$
|
|
752
|
+
function asyncGeneratorStep$7(gen, resolve, reject, _next, _throw, key, arg) {
|
|
750
753
|
try {
|
|
751
754
|
var info = gen[key](arg);
|
|
752
755
|
var value = info.value;
|
|
@@ -760,16 +763,16 @@ function asyncGeneratorStep$6(gen, resolve, reject, _next, _throw, key, arg) {
|
|
|
760
763
|
Promise.resolve(value).then(_next, _throw);
|
|
761
764
|
}
|
|
762
765
|
}
|
|
763
|
-
function _async_to_generator$
|
|
766
|
+
function _async_to_generator$7(fn) {
|
|
764
767
|
return function() {
|
|
765
768
|
var self = this, args = arguments;
|
|
766
769
|
return new Promise(function(resolve, reject) {
|
|
767
770
|
var gen = fn.apply(self, args);
|
|
768
771
|
function _next(value) {
|
|
769
|
-
asyncGeneratorStep$
|
|
772
|
+
asyncGeneratorStep$7(gen, resolve, reject, _next, _throw, "next", value);
|
|
770
773
|
}
|
|
771
774
|
function _throw(err) {
|
|
772
|
-
asyncGeneratorStep$
|
|
775
|
+
asyncGeneratorStep$7(gen, resolve, reject, _next, _throw, "throw", err);
|
|
773
776
|
}
|
|
774
777
|
_next(undefined);
|
|
775
778
|
});
|
|
@@ -783,7 +786,7 @@ function _instanceof$1(left, right) {
|
|
|
783
786
|
return left instanceof right;
|
|
784
787
|
}
|
|
785
788
|
}
|
|
786
|
-
function _ts_generator$
|
|
789
|
+
function _ts_generator$7(thisArg, body) {
|
|
787
790
|
var f, y, t, _ = {
|
|
788
791
|
label: 0,
|
|
789
792
|
sent: function() {
|
|
@@ -911,9 +914,9 @@ function _ts_generator$6(thisArg, body) {
|
|
|
911
914
|
* @param details - The handler-level API details (carries the mapper + Tier 2/3 formatters).
|
|
912
915
|
* @returns The MCP tool response content.
|
|
913
916
|
*/ function formatMcpToolResponse(result, params, details) {
|
|
914
|
-
return _async_to_generator$
|
|
917
|
+
return _async_to_generator$7(function() {
|
|
915
918
|
var mcp, value, _tmp, context, response, summary;
|
|
916
|
-
return _ts_generator$
|
|
919
|
+
return _ts_generator$7(this, function(_state) {
|
|
917
920
|
switch(_state.label){
|
|
918
921
|
case 0:
|
|
919
922
|
mcp = details === null || details === void 0 ? void 0 : details.mcp;
|
|
@@ -1773,7 +1776,7 @@ function _array_like_to_array$8(arr, len) {
|
|
|
1773
1776
|
function _array_without_holes$7(arr) {
|
|
1774
1777
|
if (Array.isArray(arr)) return _array_like_to_array$8(arr);
|
|
1775
1778
|
}
|
|
1776
|
-
function asyncGeneratorStep$
|
|
1779
|
+
function asyncGeneratorStep$6(gen, resolve, reject, _next, _throw, key, arg) {
|
|
1777
1780
|
try {
|
|
1778
1781
|
var info = gen[key](arg);
|
|
1779
1782
|
var value = info.value;
|
|
@@ -1787,16 +1790,16 @@ function asyncGeneratorStep$5(gen, resolve, reject, _next, _throw, key, arg) {
|
|
|
1787
1790
|
Promise.resolve(value).then(_next, _throw);
|
|
1788
1791
|
}
|
|
1789
1792
|
}
|
|
1790
|
-
function _async_to_generator$
|
|
1793
|
+
function _async_to_generator$6(fn) {
|
|
1791
1794
|
return function() {
|
|
1792
1795
|
var self = this, args = arguments;
|
|
1793
1796
|
return new Promise(function(resolve, reject) {
|
|
1794
1797
|
var gen = fn.apply(self, args);
|
|
1795
1798
|
function _next(value) {
|
|
1796
|
-
asyncGeneratorStep$
|
|
1799
|
+
asyncGeneratorStep$6(gen, resolve, reject, _next, _throw, "next", value);
|
|
1797
1800
|
}
|
|
1798
1801
|
function _throw(err) {
|
|
1799
|
-
asyncGeneratorStep$
|
|
1802
|
+
asyncGeneratorStep$6(gen, resolve, reject, _next, _throw, "throw", err);
|
|
1800
1803
|
}
|
|
1801
1804
|
_next(undefined);
|
|
1802
1805
|
});
|
|
@@ -1819,7 +1822,7 @@ function _unsupported_iterable_to_array$8(o, minLen) {
|
|
|
1819
1822
|
if (n === "Map" || n === "Set") return Array.from(n);
|
|
1820
1823
|
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array$8(o, minLen);
|
|
1821
1824
|
}
|
|
1822
|
-
function _ts_generator$
|
|
1825
|
+
function _ts_generator$6(thisArg, body) {
|
|
1823
1826
|
var f, y, t, _ = {
|
|
1824
1827
|
label: 0,
|
|
1825
1828
|
sent: function() {
|
|
@@ -1980,55 +1983,564 @@ function _ts_generator$5(thisArg, body) {
|
|
|
1980
1983
|
}
|
|
1981
1984
|
// MARK: Handler
|
|
1982
1985
|
function modelGetToolHandler(args, ctx, deps) {
|
|
1986
|
+
return _async_to_generator$6(function() {
|
|
1987
|
+
var result, input, identity, resolvedKeys, merged, error;
|
|
1988
|
+
return _ts_generator$6(this, function(_state) {
|
|
1989
|
+
switch(_state.label){
|
|
1990
|
+
case 0:
|
|
1991
|
+
_state.trys.push([
|
|
1992
|
+
0,
|
|
1993
|
+
2,
|
|
1994
|
+
,
|
|
1995
|
+
3
|
|
1996
|
+
]);
|
|
1997
|
+
input = parseModelGetInput(args);
|
|
1998
|
+
identity = deps.resolveIdentity(input.modelType, ctx.auth);
|
|
1999
|
+
if (identity == null) {
|
|
2000
|
+
throw new Error("Unknown modelType: ".concat(input.modelType));
|
|
2001
|
+
}
|
|
2002
|
+
resolvedKeys = resolveKeys$1(input.keys, identity);
|
|
2003
|
+
return [
|
|
2004
|
+
4,
|
|
2005
|
+
readInChunks$1({
|
|
2006
|
+
modelType: input.modelType,
|
|
2007
|
+
keys: resolvedKeys,
|
|
2008
|
+
auth: ctx.auth,
|
|
2009
|
+
readDocuments: deps.readDocuments
|
|
2010
|
+
})
|
|
2011
|
+
];
|
|
2012
|
+
case 1:
|
|
2013
|
+
merged = _state.sent();
|
|
2014
|
+
result = {
|
|
2015
|
+
content: [
|
|
2016
|
+
{
|
|
2017
|
+
type: 'text',
|
|
2018
|
+
text: JSON.stringify(merged)
|
|
2019
|
+
}
|
|
2020
|
+
],
|
|
2021
|
+
structuredContent: merged
|
|
2022
|
+
};
|
|
2023
|
+
return [
|
|
2024
|
+
3,
|
|
2025
|
+
3
|
|
2026
|
+
];
|
|
2027
|
+
case 2:
|
|
2028
|
+
error = _state.sent();
|
|
2029
|
+
result = formatMcpToolErrorResponse(error);
|
|
2030
|
+
return [
|
|
2031
|
+
3,
|
|
2032
|
+
3
|
|
2033
|
+
];
|
|
2034
|
+
case 3:
|
|
2035
|
+
return [
|
|
2036
|
+
2,
|
|
2037
|
+
result
|
|
2038
|
+
];
|
|
2039
|
+
}
|
|
2040
|
+
});
|
|
2041
|
+
})();
|
|
2042
|
+
}
|
|
2043
|
+
function parseModelGetInput(args) {
|
|
2044
|
+
var modelType = args['modelType'];
|
|
2045
|
+
var keys = args['keys'];
|
|
2046
|
+
if (typeof modelType !== 'string' || modelType.length === 0) {
|
|
2047
|
+
throw new Error('model-get: "modelType" is required and must be a non-empty string.');
|
|
2048
|
+
}
|
|
2049
|
+
if (!Array.isArray(keys) || keys.length === 0) {
|
|
2050
|
+
throw new Error('model-get: "keys" is required and must be a non-empty array.');
|
|
2051
|
+
}
|
|
2052
|
+
var normalizedKeys = keys.map(function(value, index) {
|
|
2053
|
+
if (typeof value !== 'string' || value.length === 0) {
|
|
2054
|
+
throw new Error("model-get: keys[".concat(index, "] must be a non-empty string."));
|
|
2055
|
+
}
|
|
2056
|
+
return value;
|
|
2057
|
+
});
|
|
2058
|
+
return {
|
|
2059
|
+
modelType: modelType,
|
|
2060
|
+
keys: normalizedKeys
|
|
2061
|
+
};
|
|
2062
|
+
}
|
|
2063
|
+
function resolveKeys$1(keys, identity) {
|
|
2064
|
+
var isRoot = identity.type === 'root';
|
|
2065
|
+
var resolved = [];
|
|
2066
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
2067
|
+
try {
|
|
2068
|
+
for(var _iterator = keys[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
2069
|
+
var value = _step.value;
|
|
2070
|
+
if (value.includes('/')) {
|
|
2071
|
+
resolved.push(value);
|
|
2072
|
+
} else if (isRoot) {
|
|
2073
|
+
resolved.push("".concat(identity.collectionName, "/").concat(value));
|
|
2074
|
+
} else {
|
|
2075
|
+
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, '").'));
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
2078
|
+
} catch (err) {
|
|
2079
|
+
_didIteratorError = true;
|
|
2080
|
+
_iteratorError = err;
|
|
2081
|
+
} finally{
|
|
2082
|
+
try {
|
|
2083
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
2084
|
+
_iterator.return();
|
|
2085
|
+
}
|
|
2086
|
+
} finally{
|
|
2087
|
+
if (_didIteratorError) {
|
|
2088
|
+
throw _iteratorError;
|
|
2089
|
+
}
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
return resolved;
|
|
2093
|
+
}
|
|
2094
|
+
function readInChunks$1(input) {
|
|
2095
|
+
return _async_to_generator$6(function() {
|
|
2096
|
+
var modelType, keys, auth, readDocuments, merged, offset, _merged_results, _merged_errors, batch, partial;
|
|
2097
|
+
return _ts_generator$6(this, function(_state) {
|
|
2098
|
+
switch(_state.label){
|
|
2099
|
+
case 0:
|
|
2100
|
+
modelType = input.modelType, keys = input.keys, auth = input.auth, readDocuments = input.readDocuments;
|
|
2101
|
+
merged = {
|
|
2102
|
+
results: [],
|
|
2103
|
+
errors: []
|
|
2104
|
+
};
|
|
2105
|
+
offset = 0;
|
|
2106
|
+
_state.label = 1;
|
|
2107
|
+
case 1:
|
|
2108
|
+
if (!(offset < keys.length)) return [
|
|
2109
|
+
3,
|
|
2110
|
+
4
|
|
2111
|
+
];
|
|
2112
|
+
batch = keys.slice(offset, offset + MCP_MODEL_GET_BATCH_SIZE);
|
|
2113
|
+
return [
|
|
2114
|
+
4,
|
|
2115
|
+
readDocuments(modelType, batch, auth)
|
|
2116
|
+
];
|
|
2117
|
+
case 2:
|
|
2118
|
+
partial = _state.sent();
|
|
2119
|
+
(_merged_results = merged.results).push.apply(_merged_results, _to_consumable_array$7(partial.results));
|
|
2120
|
+
(_merged_errors = merged.errors).push.apply(_merged_errors, _to_consumable_array$7(partial.errors));
|
|
2121
|
+
_state.label = 3;
|
|
2122
|
+
case 3:
|
|
2123
|
+
offset += MCP_MODEL_GET_BATCH_SIZE;
|
|
2124
|
+
return [
|
|
2125
|
+
3,
|
|
2126
|
+
1
|
|
2127
|
+
];
|
|
2128
|
+
case 4:
|
|
2129
|
+
return [
|
|
2130
|
+
2,
|
|
2131
|
+
merged
|
|
2132
|
+
];
|
|
2133
|
+
}
|
|
2134
|
+
});
|
|
2135
|
+
})();
|
|
2136
|
+
}
|
|
2137
|
+
// MARK: Schemas
|
|
2138
|
+
var MODEL_GET_INPUT_SCHEMA = {
|
|
2139
|
+
type: 'object',
|
|
2140
|
+
required: [
|
|
2141
|
+
'modelType',
|
|
2142
|
+
'keys'
|
|
2143
|
+
],
|
|
2144
|
+
properties: {
|
|
2145
|
+
modelType: {
|
|
2146
|
+
type: 'string',
|
|
2147
|
+
minLength: 1,
|
|
2148
|
+
description: 'Firestore model type to fetch (e.g. "guestbook", "profile").'
|
|
2149
|
+
},
|
|
2150
|
+
keys: {
|
|
2151
|
+
type: 'array',
|
|
2152
|
+
minItems: 1,
|
|
2153
|
+
description: 'Full keys ("prefix/id") or bare ids (root models only).',
|
|
2154
|
+
items: {
|
|
2155
|
+
type: 'string',
|
|
2156
|
+
minLength: 1
|
|
2157
|
+
}
|
|
2158
|
+
}
|
|
2159
|
+
},
|
|
2160
|
+
additionalProperties: false
|
|
2161
|
+
};
|
|
2162
|
+
var MODEL_GET_OUTPUT_SCHEMA = {
|
|
2163
|
+
type: 'object',
|
|
2164
|
+
required: [
|
|
2165
|
+
'results',
|
|
2166
|
+
'errors'
|
|
2167
|
+
],
|
|
2168
|
+
properties: {
|
|
2169
|
+
results: {
|
|
2170
|
+
type: 'array',
|
|
2171
|
+
description: 'Per-key successful reads.',
|
|
2172
|
+
items: {
|
|
2173
|
+
type: 'object',
|
|
2174
|
+
required: [
|
|
2175
|
+
'key',
|
|
2176
|
+
'data'
|
|
2177
|
+
],
|
|
2178
|
+
properties: {
|
|
2179
|
+
key: {
|
|
2180
|
+
type: 'string'
|
|
2181
|
+
},
|
|
2182
|
+
data: {}
|
|
2183
|
+
}
|
|
2184
|
+
}
|
|
2185
|
+
},
|
|
2186
|
+
errors: {
|
|
2187
|
+
type: 'array',
|
|
2188
|
+
description: 'Per-key failures (not-found, forbidden, etc.).',
|
|
2189
|
+
items: {
|
|
2190
|
+
type: 'object',
|
|
2191
|
+
required: [
|
|
2192
|
+
'key',
|
|
2193
|
+
'message'
|
|
2194
|
+
],
|
|
2195
|
+
properties: {
|
|
2196
|
+
key: {
|
|
2197
|
+
type: 'string'
|
|
2198
|
+
},
|
|
2199
|
+
message: {
|
|
2200
|
+
type: 'string'
|
|
2201
|
+
},
|
|
2202
|
+
code: {
|
|
2203
|
+
type: 'string'
|
|
2204
|
+
}
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
}
|
|
2209
|
+
};
|
|
2210
|
+
|
|
2211
|
+
function asyncGeneratorStep$5(gen, resolve, reject, _next, _throw, key, arg) {
|
|
2212
|
+
try {
|
|
2213
|
+
var info = gen[key](arg);
|
|
2214
|
+
var value = info.value;
|
|
2215
|
+
} catch (error) {
|
|
2216
|
+
reject(error);
|
|
2217
|
+
return;
|
|
2218
|
+
}
|
|
2219
|
+
if (info.done) {
|
|
2220
|
+
resolve(value);
|
|
2221
|
+
} else {
|
|
2222
|
+
Promise.resolve(value).then(_next, _throw);
|
|
2223
|
+
}
|
|
2224
|
+
}
|
|
2225
|
+
function _async_to_generator$5(fn) {
|
|
2226
|
+
return function() {
|
|
2227
|
+
var self = this, args = arguments;
|
|
2228
|
+
return new Promise(function(resolve, reject) {
|
|
2229
|
+
var gen = fn.apply(self, args);
|
|
2230
|
+
function _next(value) {
|
|
2231
|
+
asyncGeneratorStep$5(gen, resolve, reject, _next, _throw, "next", value);
|
|
2232
|
+
}
|
|
2233
|
+
function _throw(err) {
|
|
2234
|
+
asyncGeneratorStep$5(gen, resolve, reject, _next, _throw, "throw", err);
|
|
2235
|
+
}
|
|
2236
|
+
_next(undefined);
|
|
2237
|
+
});
|
|
2238
|
+
};
|
|
2239
|
+
}
|
|
2240
|
+
function _define_property$8(obj, key, value) {
|
|
2241
|
+
if (key in obj) {
|
|
2242
|
+
Object.defineProperty(obj, key, {
|
|
2243
|
+
value: value,
|
|
2244
|
+
enumerable: true,
|
|
2245
|
+
configurable: true,
|
|
2246
|
+
writable: true
|
|
2247
|
+
});
|
|
2248
|
+
} else {
|
|
2249
|
+
obj[key] = value;
|
|
2250
|
+
}
|
|
2251
|
+
return obj;
|
|
2252
|
+
}
|
|
2253
|
+
function _object_spread$6(target) {
|
|
2254
|
+
for(var i = 1; i < arguments.length; i++){
|
|
2255
|
+
var source = arguments[i] != null ? arguments[i] : {};
|
|
2256
|
+
var ownKeys = Object.keys(source);
|
|
2257
|
+
if (typeof Object.getOwnPropertySymbols === "function") {
|
|
2258
|
+
ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {
|
|
2259
|
+
return Object.getOwnPropertyDescriptor(source, sym).enumerable;
|
|
2260
|
+
}));
|
|
2261
|
+
}
|
|
2262
|
+
ownKeys.forEach(function(key) {
|
|
2263
|
+
_define_property$8(target, key, source[key]);
|
|
2264
|
+
});
|
|
2265
|
+
}
|
|
2266
|
+
return target;
|
|
2267
|
+
}
|
|
2268
|
+
function _ts_generator$5(thisArg, body) {
|
|
2269
|
+
var f, y, t, _ = {
|
|
2270
|
+
label: 0,
|
|
2271
|
+
sent: function() {
|
|
2272
|
+
if (t[0] & 1) throw t[1];
|
|
2273
|
+
return t[1];
|
|
2274
|
+
},
|
|
2275
|
+
trys: [],
|
|
2276
|
+
ops: []
|
|
2277
|
+
}, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype), d = Object.defineProperty;
|
|
2278
|
+
return d(g, "next", {
|
|
2279
|
+
value: verb(0)
|
|
2280
|
+
}), d(g, "throw", {
|
|
2281
|
+
value: verb(1)
|
|
2282
|
+
}), d(g, "return", {
|
|
2283
|
+
value: verb(2)
|
|
2284
|
+
}), typeof Symbol === "function" && d(g, Symbol.iterator, {
|
|
2285
|
+
value: function() {
|
|
2286
|
+
return this;
|
|
2287
|
+
}
|
|
2288
|
+
}), g;
|
|
2289
|
+
function verb(n) {
|
|
2290
|
+
return function(v) {
|
|
2291
|
+
return step([
|
|
2292
|
+
n,
|
|
2293
|
+
v
|
|
2294
|
+
]);
|
|
2295
|
+
};
|
|
2296
|
+
}
|
|
2297
|
+
function step(op) {
|
|
2298
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
2299
|
+
while(g && (g = 0, op[0] && (_ = 0)), _)try {
|
|
2300
|
+
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;
|
|
2301
|
+
if (y = 0, t) op = [
|
|
2302
|
+
op[0] & 2,
|
|
2303
|
+
t.value
|
|
2304
|
+
];
|
|
2305
|
+
switch(op[0]){
|
|
2306
|
+
case 0:
|
|
2307
|
+
case 1:
|
|
2308
|
+
t = op;
|
|
2309
|
+
break;
|
|
2310
|
+
case 4:
|
|
2311
|
+
_.label++;
|
|
2312
|
+
return {
|
|
2313
|
+
value: op[1],
|
|
2314
|
+
done: false
|
|
2315
|
+
};
|
|
2316
|
+
case 5:
|
|
2317
|
+
_.label++;
|
|
2318
|
+
y = op[1];
|
|
2319
|
+
op = [
|
|
2320
|
+
0
|
|
2321
|
+
];
|
|
2322
|
+
continue;
|
|
2323
|
+
case 7:
|
|
2324
|
+
op = _.ops.pop();
|
|
2325
|
+
_.trys.pop();
|
|
2326
|
+
continue;
|
|
2327
|
+
default:
|
|
2328
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
|
|
2329
|
+
_ = 0;
|
|
2330
|
+
continue;
|
|
2331
|
+
}
|
|
2332
|
+
if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) {
|
|
2333
|
+
_.label = op[1];
|
|
2334
|
+
break;
|
|
2335
|
+
}
|
|
2336
|
+
if (op[0] === 6 && _.label < t[1]) {
|
|
2337
|
+
_.label = t[1];
|
|
2338
|
+
t = op;
|
|
2339
|
+
break;
|
|
2340
|
+
}
|
|
2341
|
+
if (t && _.label < t[2]) {
|
|
2342
|
+
_.label = t[2];
|
|
2343
|
+
_.ops.push(op);
|
|
2344
|
+
break;
|
|
2345
|
+
}
|
|
2346
|
+
if (t[2]) _.ops.pop();
|
|
2347
|
+
_.trys.pop();
|
|
2348
|
+
continue;
|
|
2349
|
+
}
|
|
2350
|
+
op = body.call(thisArg, _);
|
|
2351
|
+
} catch (e) {
|
|
2352
|
+
op = [
|
|
2353
|
+
6,
|
|
2354
|
+
e
|
|
2355
|
+
];
|
|
2356
|
+
y = 0;
|
|
2357
|
+
} finally{
|
|
2358
|
+
f = t = 0;
|
|
2359
|
+
}
|
|
2360
|
+
if (op[0] & 5) throw op[1];
|
|
2361
|
+
return {
|
|
2362
|
+
value: op[0] ? op[1] : void 0,
|
|
2363
|
+
done: true
|
|
2364
|
+
};
|
|
2365
|
+
}
|
|
2366
|
+
}
|
|
2367
|
+
// MARK: Constants
|
|
2368
|
+
/**
|
|
2369
|
+
* Reserved tool name for the built-in `model-roles` static tool.
|
|
2370
|
+
*/ var MODEL_ROLES_TOOL_NAME = 'model-roles';
|
|
2371
|
+
/**
|
|
2372
|
+
* Synthetic call type used in the tool's dispatch identity. Distinct from `model-get`'s `get` so
|
|
2373
|
+
* visibility predicates can target the two independently.
|
|
2374
|
+
*/ var MODEL_ROLES_DISPATCH_CALL = 'roles';
|
|
2375
|
+
/**
|
|
2376
|
+
* Synthetic model type used in the tool's dispatch identity. Mirrors {@link MODEL_GET_DISPATCH_MODEL_TYPE}
|
|
2377
|
+
* — the tool isn't bound to one model type, so the literal "model" stands in.
|
|
2378
|
+
*/ var MODEL_ROLES_DISPATCH_MODEL_TYPE = 'model';
|
|
2379
|
+
/**
|
|
2380
|
+
* Maximum number of keys accepted per `model-roles` call. Role resolution runs the model's real
|
|
2381
|
+
* permission delegate per key (which may itself read parent documents), so this is deliberately
|
|
2382
|
+
* lower than the `model-get` batch size.
|
|
2383
|
+
*/ var MCP_MODEL_ROLES_MAX_KEYS = 25;
|
|
2384
|
+
// MARK: Factory
|
|
2385
|
+
/**
|
|
2386
|
+
* Builds the built-in `model-roles` MCP tool definition.
|
|
2387
|
+
*
|
|
2388
|
+
* Answers "what is this user actually allowed to do with this document?" by running the same
|
|
2389
|
+
* `roleMapForModel()` delegate the permission-checked read/write paths use, and returning the
|
|
2390
|
+
* resolved roles instead of consuming them. Because the model's real delegate runs, derived and
|
|
2391
|
+
* cascading roles appear exactly as the API grants them — there is no second implementation of the
|
|
2392
|
+
* rules to drift.
|
|
2393
|
+
*
|
|
2394
|
+
* Two things it disambiguates that a plain read cannot:
|
|
2395
|
+
* - **Missing vs. forbidden.** A key that resolves to a missing document returns
|
|
2396
|
+
* `exists: false, roles: []` rather than an error, so "the document is not there" reads
|
|
2397
|
+
* differently from "it is there and you have no access" (`exists: true, roles: []`).
|
|
2398
|
+
* - **Full access.** Admin short-circuits that grant the full-access marker come back as
|
|
2399
|
+
* `fullAccess: true` rather than as an opaque enumerated set.
|
|
2400
|
+
*
|
|
2401
|
+
* By default roles are resolved for the calling user. Passing `uid` resolves them for another user
|
|
2402
|
+
* and is gated behind {@link CreateModelRolesToolDeps.canTargetOtherUids}.
|
|
2403
|
+
*
|
|
2404
|
+
* @param deps - Role-map reader, identity resolver, and the target-uid authorization predicate.
|
|
2405
|
+
* @returns A statically-registered {@link McpToolDefinition} ready to be appended to the MCP
|
|
2406
|
+
* server factory's tool registry.
|
|
2407
|
+
*/ function createModelRolesTool(deps) {
|
|
2408
|
+
var handler = function handler(args, ctx) {
|
|
2409
|
+
return modelRolesToolHandler(args, ctx, deps);
|
|
2410
|
+
};
|
|
2411
|
+
var name = MODEL_ROLES_TOOL_NAME;
|
|
2412
|
+
var description = 'Resolve which roles the current user (or, with "uid", another user) is granted on one or more model keys — the same permission computation the API runs, returned instead of enforced. Use to answer "why was this forbidden?" and to tell a missing document (exists:false) apart from one you cannot access (exists:true, roles:[]). Admin full-access short-circuits report fullAccess:true. Targeting another uid requires elevated access.';
|
|
2413
|
+
return buildStaticToolDefinition({
|
|
2414
|
+
name: name,
|
|
2415
|
+
description: description,
|
|
2416
|
+
inputSchema: MODEL_ROLES_INPUT_SCHEMA,
|
|
2417
|
+
outputSchema: MODEL_ROLES_OUTPUT_SCHEMA,
|
|
2418
|
+
dispatch: {
|
|
2419
|
+
call: MODEL_ROLES_DISPATCH_CALL,
|
|
2420
|
+
modelType: MODEL_ROLES_DISPATCH_MODEL_TYPE
|
|
2421
|
+
},
|
|
2422
|
+
staticHandler: handler,
|
|
2423
|
+
effectiveReadOnly: true,
|
|
2424
|
+
rule: {
|
|
2425
|
+
requireAuthenticated: true
|
|
2426
|
+
}
|
|
2427
|
+
});
|
|
2428
|
+
}
|
|
2429
|
+
// MARK: Handler
|
|
2430
|
+
function modelRolesToolHandler(args, ctx, deps) {
|
|
1983
2431
|
return _async_to_generator$5(function() {
|
|
1984
|
-
var result, input, identity,
|
|
2432
|
+
var result, input, identity, targetUid, keys, resolved, error;
|
|
1985
2433
|
return _ts_generator$5(this, function(_state) {
|
|
1986
2434
|
switch(_state.label){
|
|
1987
2435
|
case 0:
|
|
1988
2436
|
_state.trys.push([
|
|
1989
2437
|
0,
|
|
1990
|
-
|
|
2438
|
+
3,
|
|
1991
2439
|
,
|
|
1992
|
-
|
|
2440
|
+
4
|
|
1993
2441
|
]);
|
|
1994
|
-
input =
|
|
2442
|
+
input = parseModelRolesInput(args);
|
|
1995
2443
|
identity = deps.resolveIdentity(input.modelType, ctx.auth);
|
|
1996
2444
|
if (identity == null) {
|
|
1997
2445
|
throw new Error("Unknown modelType: ".concat(input.modelType));
|
|
1998
2446
|
}
|
|
1999
|
-
resolvedKeys = resolveKeys(input.keys, identity);
|
|
2000
2447
|
return [
|
|
2001
2448
|
4,
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
keys: resolvedKeys,
|
|
2449
|
+
resolveTargetUid({
|
|
2450
|
+
requestedUid: input.uid,
|
|
2005
2451
|
auth: ctx.auth,
|
|
2006
|
-
|
|
2452
|
+
canTargetOtherUids: deps.canTargetOtherUids
|
|
2007
2453
|
})
|
|
2008
2454
|
];
|
|
2009
2455
|
case 1:
|
|
2010
|
-
|
|
2456
|
+
targetUid = _state.sent();
|
|
2457
|
+
keys = resolveKeys(input.keys, identity);
|
|
2458
|
+
return [
|
|
2459
|
+
4,
|
|
2460
|
+
deps.readRoleMaps(_object_spread$6({
|
|
2461
|
+
modelType: input.modelType,
|
|
2462
|
+
keys: keys,
|
|
2463
|
+
auth: ctx.auth
|
|
2464
|
+
}, targetUid == null ? {} : {
|
|
2465
|
+
targetUid: targetUid
|
|
2466
|
+
}))
|
|
2467
|
+
];
|
|
2468
|
+
case 2:
|
|
2469
|
+
resolved = _state.sent();
|
|
2011
2470
|
result = {
|
|
2012
2471
|
content: [
|
|
2013
2472
|
{
|
|
2014
2473
|
type: 'text',
|
|
2015
|
-
text: JSON.stringify(
|
|
2474
|
+
text: JSON.stringify(resolved)
|
|
2016
2475
|
}
|
|
2017
2476
|
],
|
|
2018
|
-
structuredContent:
|
|
2477
|
+
structuredContent: resolved
|
|
2019
2478
|
};
|
|
2020
2479
|
return [
|
|
2021
2480
|
3,
|
|
2022
|
-
|
|
2481
|
+
4
|
|
2023
2482
|
];
|
|
2024
|
-
case
|
|
2483
|
+
case 3:
|
|
2025
2484
|
error = _state.sent();
|
|
2026
2485
|
result = formatMcpToolErrorResponse(error);
|
|
2486
|
+
return [
|
|
2487
|
+
3,
|
|
2488
|
+
4
|
|
2489
|
+
];
|
|
2490
|
+
case 4:
|
|
2491
|
+
return [
|
|
2492
|
+
2,
|
|
2493
|
+
result
|
|
2494
|
+
];
|
|
2495
|
+
}
|
|
2496
|
+
});
|
|
2497
|
+
})();
|
|
2498
|
+
}
|
|
2499
|
+
/**
|
|
2500
|
+
* Resolves the effective target uid for a call, enforcing the elevated-access gate.
|
|
2501
|
+
*
|
|
2502
|
+
* Requesting your own uid is always allowed and is treated as no target at all, so a caller that
|
|
2503
|
+
* passes their own uid never trips the predicate.
|
|
2504
|
+
*
|
|
2505
|
+
* @param input - The requested uid, the caller's auth, and the authorization predicate.
|
|
2506
|
+
* @returns The uid to resolve as, or `undefined` to resolve as the caller.
|
|
2507
|
+
* @throws {Error} When another user's uid is requested and the predicate denies (or is absent).
|
|
2508
|
+
*/ function resolveTargetUid(input) {
|
|
2509
|
+
return _async_to_generator$5(function() {
|
|
2510
|
+
var requestedUid, auth, canTargetOtherUids, result, allowed, _tmp;
|
|
2511
|
+
return _ts_generator$5(this, function(_state) {
|
|
2512
|
+
switch(_state.label){
|
|
2513
|
+
case 0:
|
|
2514
|
+
requestedUid = input.requestedUid, auth = input.auth, canTargetOtherUids = input.canTargetOtherUids;
|
|
2515
|
+
if (!(requestedUid != null && requestedUid !== (auth === null || auth === void 0 ? void 0 : auth.uid))) return [
|
|
2516
|
+
3,
|
|
2517
|
+
4
|
|
2518
|
+
];
|
|
2519
|
+
if (!(canTargetOtherUids == null)) return [
|
|
2520
|
+
3,
|
|
2521
|
+
1
|
|
2522
|
+
];
|
|
2523
|
+
_tmp = false;
|
|
2027
2524
|
return [
|
|
2028
2525
|
3,
|
|
2029
2526
|
3
|
|
2030
2527
|
];
|
|
2528
|
+
case 1:
|
|
2529
|
+
return [
|
|
2530
|
+
4,
|
|
2531
|
+
canTargetOtherUids(auth)
|
|
2532
|
+
];
|
|
2533
|
+
case 2:
|
|
2534
|
+
_tmp = _state.sent();
|
|
2535
|
+
_state.label = 3;
|
|
2031
2536
|
case 3:
|
|
2537
|
+
allowed = _tmp;
|
|
2538
|
+
if (!allowed) {
|
|
2539
|
+
throw new Error("model-roles: resolving roles for another user's uid requires elevated access.");
|
|
2540
|
+
}
|
|
2541
|
+
result = requestedUid;
|
|
2542
|
+
_state.label = 4;
|
|
2543
|
+
case 4:
|
|
2032
2544
|
return [
|
|
2033
2545
|
2,
|
|
2034
2546
|
result
|
|
@@ -2037,25 +2549,34 @@ function modelGetToolHandler(args, ctx, deps) {
|
|
|
2037
2549
|
});
|
|
2038
2550
|
})();
|
|
2039
2551
|
}
|
|
2040
|
-
function
|
|
2041
|
-
var modelType = args
|
|
2042
|
-
var keys = args
|
|
2552
|
+
function parseModelRolesInput(args) {
|
|
2553
|
+
var modelType = args['modelType'];
|
|
2554
|
+
var keys = args['keys'];
|
|
2555
|
+
var uid = args['uid'];
|
|
2043
2556
|
if (typeof modelType !== 'string' || modelType.length === 0) {
|
|
2044
|
-
throw new Error('model-
|
|
2557
|
+
throw new Error('model-roles: "modelType" is required and must be a non-empty string.');
|
|
2045
2558
|
}
|
|
2046
2559
|
if (!Array.isArray(keys) || keys.length === 0) {
|
|
2047
|
-
throw new Error('model-
|
|
2560
|
+
throw new Error('model-roles: "keys" is required and must be a non-empty array.');
|
|
2561
|
+
}
|
|
2562
|
+
if (keys.length > MCP_MODEL_ROLES_MAX_KEYS) {
|
|
2563
|
+
throw new Error("model-roles: at most ".concat(MCP_MODEL_ROLES_MAX_KEYS, " keys may be resolved per call (received ").concat(keys.length, ")."));
|
|
2564
|
+
}
|
|
2565
|
+
if (uid !== undefined && (typeof uid !== 'string' || uid.length === 0)) {
|
|
2566
|
+
throw new Error('model-roles: "uid" must be a non-empty string when provided.');
|
|
2048
2567
|
}
|
|
2049
2568
|
var normalizedKeys = keys.map(function(value, index) {
|
|
2050
2569
|
if (typeof value !== 'string' || value.length === 0) {
|
|
2051
|
-
throw new Error("model-
|
|
2570
|
+
throw new Error("model-roles: keys[".concat(index, "] must be a non-empty string."));
|
|
2052
2571
|
}
|
|
2053
2572
|
return value;
|
|
2054
2573
|
});
|
|
2055
|
-
return {
|
|
2574
|
+
return _object_spread$6({
|
|
2056
2575
|
modelType: modelType,
|
|
2057
2576
|
keys: normalizedKeys
|
|
2058
|
-
}
|
|
2577
|
+
}, uid === undefined ? {} : {
|
|
2578
|
+
uid: uid
|
|
2579
|
+
});
|
|
2059
2580
|
}
|
|
2060
2581
|
function resolveKeys(keys, identity) {
|
|
2061
2582
|
var isRoot = identity.type === 'root';
|
|
@@ -2069,7 +2590,7 @@ function resolveKeys(keys, identity) {
|
|
|
2069
2590
|
} else if (isRoot) {
|
|
2070
2591
|
resolved.push("".concat(identity.collectionName, "/").concat(value));
|
|
2071
2592
|
} else {
|
|
2072
|
-
throw new Error('model-
|
|
2593
|
+
throw new Error('model-roles: modelType "'.concat(identity.modelType, '" is a subcollection; bare ids are not allowed. Provide full keys (e.g. "parentPrefix/parentId/').concat(identity.collectionName, "/").concat(value, '").'));
|
|
2073
2594
|
}
|
|
2074
2595
|
}
|
|
2075
2596
|
} catch (err) {
|
|
@@ -2088,51 +2609,8 @@ function resolveKeys(keys, identity) {
|
|
|
2088
2609
|
}
|
|
2089
2610
|
return resolved;
|
|
2090
2611
|
}
|
|
2091
|
-
function readInChunks$1(input) {
|
|
2092
|
-
return _async_to_generator$5(function() {
|
|
2093
|
-
var modelType, keys, auth, readDocuments, merged, offset, _merged_results, _merged_errors, batch, partial;
|
|
2094
|
-
return _ts_generator$5(this, function(_state) {
|
|
2095
|
-
switch(_state.label){
|
|
2096
|
-
case 0:
|
|
2097
|
-
modelType = input.modelType, keys = input.keys, auth = input.auth, readDocuments = input.readDocuments;
|
|
2098
|
-
merged = {
|
|
2099
|
-
results: [],
|
|
2100
|
-
errors: []
|
|
2101
|
-
};
|
|
2102
|
-
offset = 0;
|
|
2103
|
-
_state.label = 1;
|
|
2104
|
-
case 1:
|
|
2105
|
-
if (!(offset < keys.length)) return [
|
|
2106
|
-
3,
|
|
2107
|
-
4
|
|
2108
|
-
];
|
|
2109
|
-
batch = keys.slice(offset, offset + MCP_MODEL_GET_BATCH_SIZE);
|
|
2110
|
-
return [
|
|
2111
|
-
4,
|
|
2112
|
-
readDocuments(modelType, batch, auth)
|
|
2113
|
-
];
|
|
2114
|
-
case 2:
|
|
2115
|
-
partial = _state.sent();
|
|
2116
|
-
(_merged_results = merged.results).push.apply(_merged_results, _to_consumable_array$7(partial.results));
|
|
2117
|
-
(_merged_errors = merged.errors).push.apply(_merged_errors, _to_consumable_array$7(partial.errors));
|
|
2118
|
-
_state.label = 3;
|
|
2119
|
-
case 3:
|
|
2120
|
-
offset += MCP_MODEL_GET_BATCH_SIZE;
|
|
2121
|
-
return [
|
|
2122
|
-
3,
|
|
2123
|
-
1
|
|
2124
|
-
];
|
|
2125
|
-
case 4:
|
|
2126
|
-
return [
|
|
2127
|
-
2,
|
|
2128
|
-
merged
|
|
2129
|
-
];
|
|
2130
|
-
}
|
|
2131
|
-
});
|
|
2132
|
-
})();
|
|
2133
|
-
}
|
|
2134
2612
|
// MARK: Schemas
|
|
2135
|
-
var
|
|
2613
|
+
var MODEL_ROLES_INPUT_SCHEMA = {
|
|
2136
2614
|
type: 'object',
|
|
2137
2615
|
required: [
|
|
2138
2616
|
'modelType',
|
|
@@ -2142,47 +2620,78 @@ var MODEL_GET_INPUT_SCHEMA = {
|
|
|
2142
2620
|
modelType: {
|
|
2143
2621
|
type: 'string',
|
|
2144
2622
|
minLength: 1,
|
|
2145
|
-
description: 'Firestore model type to
|
|
2623
|
+
description: 'Firestore model type to resolve roles on (e.g. "guestbook", "profile").'
|
|
2146
2624
|
},
|
|
2147
2625
|
keys: {
|
|
2148
2626
|
type: 'array',
|
|
2149
2627
|
minItems: 1,
|
|
2628
|
+
maxItems: MCP_MODEL_ROLES_MAX_KEYS,
|
|
2150
2629
|
description: 'Full keys ("prefix/id") or bare ids (root models only).',
|
|
2151
2630
|
items: {
|
|
2152
2631
|
type: 'string',
|
|
2153
2632
|
minLength: 1
|
|
2154
2633
|
}
|
|
2634
|
+
},
|
|
2635
|
+
uid: {
|
|
2636
|
+
type: 'string',
|
|
2637
|
+
minLength: 1,
|
|
2638
|
+
description: 'Resolve roles as this user instead of the caller. Requires elevated access; passing your own uid is always allowed.'
|
|
2155
2639
|
}
|
|
2156
2640
|
},
|
|
2157
2641
|
additionalProperties: false
|
|
2158
2642
|
};
|
|
2159
|
-
var
|
|
2643
|
+
var MODEL_ROLES_OUTPUT_SCHEMA = {
|
|
2160
2644
|
type: 'object',
|
|
2161
2645
|
required: [
|
|
2646
|
+
'targeted',
|
|
2162
2647
|
'results',
|
|
2163
2648
|
'errors'
|
|
2164
2649
|
],
|
|
2165
2650
|
properties: {
|
|
2651
|
+
uid: {
|
|
2652
|
+
type: 'string',
|
|
2653
|
+
description: 'The uid the roles were resolved for.'
|
|
2654
|
+
},
|
|
2655
|
+
targeted: {
|
|
2656
|
+
type: 'boolean',
|
|
2657
|
+
description: 'True when the roles were resolved for a user other than the caller.'
|
|
2658
|
+
},
|
|
2166
2659
|
results: {
|
|
2167
2660
|
type: 'array',
|
|
2168
|
-
description: 'Per-key
|
|
2661
|
+
description: 'Per-key resolved permission state.',
|
|
2169
2662
|
items: {
|
|
2170
2663
|
type: 'object',
|
|
2171
2664
|
required: [
|
|
2172
2665
|
'key',
|
|
2173
|
-
'
|
|
2666
|
+
'exists',
|
|
2667
|
+
'fullAccess',
|
|
2668
|
+
'roles'
|
|
2174
2669
|
],
|
|
2175
2670
|
properties: {
|
|
2176
2671
|
key: {
|
|
2177
2672
|
type: 'string'
|
|
2178
2673
|
},
|
|
2179
|
-
|
|
2674
|
+
exists: {
|
|
2675
|
+
type: 'boolean',
|
|
2676
|
+
description: 'Whether the document exists. Roles are only computed for documents that exist.'
|
|
2677
|
+
},
|
|
2678
|
+
fullAccess: {
|
|
2679
|
+
type: 'boolean',
|
|
2680
|
+
description: 'True when the full-access marker was granted; every role is implicitly held and "roles" is empty.'
|
|
2681
|
+
},
|
|
2682
|
+
roles: {
|
|
2683
|
+
type: 'array',
|
|
2684
|
+
items: {
|
|
2685
|
+
type: 'string'
|
|
2686
|
+
},
|
|
2687
|
+
description: 'Granted role names, sorted.'
|
|
2688
|
+
}
|
|
2180
2689
|
}
|
|
2181
2690
|
}
|
|
2182
2691
|
},
|
|
2183
2692
|
errors: {
|
|
2184
2693
|
type: 'array',
|
|
2185
|
-
description: 'Per-key failures
|
|
2694
|
+
description: 'Per-key failures. A missing document is NOT an error — it appears in results with exists:false.',
|
|
2186
2695
|
items: {
|
|
2187
2696
|
type: 'object',
|
|
2188
2697
|
required: [
|
|
@@ -2940,7 +3449,7 @@ var MODEL_ROW_SCHEMA = {
|
|
|
2940
3449
|
}
|
|
2941
3450
|
};
|
|
2942
3451
|
// MCP's `tools/list` validator requires `outputSchema.type === 'object'` at the root
|
|
2943
|
-
// (see
|
|
3452
|
+
// (see the MCP SDK's `ToolSchema`), so the variants are expressed via a top-level
|
|
2944
3453
|
// object whose nested `oneOf` discriminates by the `mode` literal.
|
|
2945
3454
|
var MODEL_INFO_OUTPUT_SCHEMA = {
|
|
2946
3455
|
type: 'object',
|
|
@@ -3742,7 +4251,7 @@ function whoamiToolHandler(args, ctx, deps) {
|
|
|
3742
4251
|
}
|
|
3743
4252
|
// Coerces the `detail` input into a WhoamiDetailLevel, defaulting unknown or absent values to `summary`.
|
|
3744
4253
|
function parseDetailLevel(args) {
|
|
3745
|
-
var value = args
|
|
4254
|
+
var value = args['detail'];
|
|
3746
4255
|
return value === 'claims' || value === 'full' ? value : 'summary';
|
|
3747
4256
|
}
|
|
3748
4257
|
function buildResult(output, text) {
|
|
@@ -4337,30 +4846,30 @@ function urlModelsToolHandler(args, ctx, deps) {
|
|
|
4337
4846
|
})();
|
|
4338
4847
|
}
|
|
4339
4848
|
function parseUrlModelsInput(args) {
|
|
4340
|
-
var url = args
|
|
4849
|
+
var url = args['url'];
|
|
4341
4850
|
if (typeof url !== 'string' || url.length === 0) {
|
|
4342
4851
|
throw new Error('url-models: "url" is required and must be a non-empty string.');
|
|
4343
4852
|
}
|
|
4344
|
-
var keysOnly = args
|
|
4345
|
-
var load = args
|
|
4853
|
+
var keysOnly = args['keysOnly'] === true;
|
|
4854
|
+
var load = args['load'] === true;
|
|
4346
4855
|
if (keysOnly && load) {
|
|
4347
4856
|
throw new Error('url-models: "keysOnly" and "load" cannot be combined — "keysOnly" returns keys without loading documents.');
|
|
4348
4857
|
}
|
|
4349
4858
|
var models;
|
|
4350
|
-
if (args
|
|
4351
|
-
if (!Array.isArray(args
|
|
4859
|
+
if (args['models'] !== undefined) {
|
|
4860
|
+
if (!Array.isArray(args['models']) || args['models'].some(function(m) {
|
|
4352
4861
|
return typeof m !== 'string';
|
|
4353
4862
|
})) {
|
|
4354
4863
|
throw new Error('url-models: "models" must be an array of model-type strings.');
|
|
4355
4864
|
}
|
|
4356
|
-
models = args
|
|
4865
|
+
models = args['models'];
|
|
4357
4866
|
}
|
|
4358
4867
|
var currentUserUid;
|
|
4359
|
-
if (args
|
|
4360
|
-
if (typeof args
|
|
4868
|
+
if (args['currentUserUid'] !== undefined) {
|
|
4869
|
+
if (typeof args['currentUserUid'] !== 'string' || args['currentUserUid'].length === 0) {
|
|
4361
4870
|
throw new Error('url-models: "currentUserUid" must be a non-empty string.');
|
|
4362
4871
|
}
|
|
4363
|
-
currentUserUid = args
|
|
4872
|
+
currentUserUid = args['currentUserUid'];
|
|
4364
4873
|
}
|
|
4365
4874
|
return _object_spread$2(_object_spread_props$1(_object_spread$2({
|
|
4366
4875
|
url: url
|
|
@@ -5308,19 +5817,19 @@ function batchExecuteToolHandler(args, ctx, deps) {
|
|
|
5308
5817
|
// MARK: Input parsing
|
|
5309
5818
|
function parseBatchExecuteInput(args) {
|
|
5310
5819
|
var _args_format, _args_maxParallel;
|
|
5311
|
-
var uploadPath = args
|
|
5820
|
+
var uploadPath = args['uploadPath'];
|
|
5312
5821
|
if (typeof uploadPath !== 'string' || uploadPath.length === 0) {
|
|
5313
5822
|
throw new Error('batch-execute: "uploadPath" is required and must be a non-empty string.');
|
|
5314
5823
|
}
|
|
5315
|
-
var bucketId = args
|
|
5824
|
+
var bucketId = args['bucketId'];
|
|
5316
5825
|
if (bucketId != null && (typeof bucketId !== 'string' || bucketId.length === 0)) {
|
|
5317
5826
|
throw new Error('batch-execute: "bucketId" must be a non-empty string when provided.');
|
|
5318
5827
|
}
|
|
5319
|
-
var format = (_args_format = args
|
|
5828
|
+
var format = (_args_format = args['format']) !== null && _args_format !== void 0 ? _args_format : 'ndjson';
|
|
5320
5829
|
if (format !== 'json' && format !== 'ndjson') {
|
|
5321
5830
|
throw new Error('batch-execute: "format" must be "json" or "ndjson".');
|
|
5322
5831
|
}
|
|
5323
|
-
var maxParallel = (_args_maxParallel = args
|
|
5832
|
+
var maxParallel = (_args_maxParallel = args['maxParallel']) !== null && _args_maxParallel !== void 0 ? _args_maxParallel : DEFAULT_BATCH_EXECUTE_MAX_PARALLEL;
|
|
5324
5833
|
if (typeof maxParallel !== 'number' || !Number.isInteger(maxParallel) || maxParallel < 1) {
|
|
5325
5834
|
throw new Error('batch-execute: "maxParallel" must be a positive integer.');
|
|
5326
5835
|
}
|
|
@@ -5329,8 +5838,8 @@ function parseBatchExecuteInput(args) {
|
|
|
5329
5838
|
bucketId: bucketId !== null && bucketId !== void 0 ? bucketId : undefined,
|
|
5330
5839
|
format: format,
|
|
5331
5840
|
maxParallel: maxParallel,
|
|
5332
|
-
stopOnError: args
|
|
5333
|
-
deleteUploadOnSuccess: args
|
|
5841
|
+
stopOnError: args['stopOnError'] === true,
|
|
5842
|
+
deleteUploadOnSuccess: args['deleteUploadOnSuccess'] !== false
|
|
5334
5843
|
};
|
|
5335
5844
|
}
|
|
5336
5845
|
function readOperationsFile(input) {
|
|
@@ -5411,9 +5920,9 @@ function validateOperation(value, index) {
|
|
|
5411
5920
|
throw new Error("batch-execute: operation[".concat(index, "] must be an object."));
|
|
5412
5921
|
}
|
|
5413
5922
|
var candidate = value;
|
|
5414
|
-
var modelType = candidate
|
|
5415
|
-
var call = candidate
|
|
5416
|
-
var specifier = candidate
|
|
5923
|
+
var modelType = candidate['modelType'];
|
|
5924
|
+
var call = candidate['call'];
|
|
5925
|
+
var specifier = candidate['specifier'];
|
|
5417
5926
|
if (typeof modelType !== 'string' || modelType.length === 0) {
|
|
5418
5927
|
throw new Error("batch-execute: operation[".concat(index, '] is missing a non-empty "modelType".'));
|
|
5419
5928
|
}
|
|
@@ -5427,7 +5936,7 @@ function validateOperation(value, index) {
|
|
|
5427
5936
|
call: call,
|
|
5428
5937
|
modelType: modelType,
|
|
5429
5938
|
specifier: specifier !== null && specifier !== void 0 ? specifier : undefined,
|
|
5430
|
-
data: candidate
|
|
5939
|
+
data: candidate['data']
|
|
5431
5940
|
};
|
|
5432
5941
|
}
|
|
5433
5942
|
function collectPreflightErrors(operations, authorizeOperation) {
|
|
@@ -6055,17 +6564,19 @@ var McpServerFactoryService_1;
|
|
|
6055
6564
|
* Injectable factory that builds {@link McpServer} instances pre-wired to the
|
|
6056
6565
|
* call model dispatch chain.
|
|
6057
6566
|
*
|
|
6058
|
-
* The factory is invoked per Streamable HTTP request
|
|
6059
|
-
*
|
|
6060
|
-
*
|
|
6567
|
+
* The factory is invoked per Streamable HTTP request. That is the shape the MCP
|
|
6568
|
+
* SDK's 2026-07-28 entry (`createMcpHandler`) requires — it builds a fresh server
|
|
6569
|
+
* per request and has no session concept at all — and it is equally what the SDK
|
|
6570
|
+
* recommends for stateless 2025-era serving, so one factory backs both eras.
|
|
6061
6571
|
*/ exports.McpServerFactoryService = McpServerFactoryService_1 = /*#__PURE__*/ function() {
|
|
6062
|
-
function McpServerFactoryService(mcpConfig, dispatchService, modelApiGetService, roleReader, analyticsService, storageService) {
|
|
6572
|
+
function McpServerFactoryService(mcpConfig, dispatchService, modelApiGetService, roleReader, analyticsService, storageService, modelRolesTargetUidPredicate) {
|
|
6063
6573
|
_class_call_check$3(this, McpServerFactoryService);
|
|
6064
6574
|
_define_property$3(this, "mcpConfig", void 0);
|
|
6065
6575
|
_define_property$3(this, "dispatchService", void 0);
|
|
6066
6576
|
_define_property$3(this, "modelApiGetService", void 0);
|
|
6067
6577
|
_define_property$3(this, "roleReader", void 0);
|
|
6068
6578
|
_define_property$3(this, "storageService", void 0);
|
|
6579
|
+
_define_property$3(this, "modelRolesTargetUidPredicate", void 0);
|
|
6069
6580
|
_define_property$3(this, "_logger", new common.Logger(McpServerFactoryService_1.name));
|
|
6070
6581
|
_define_property$3(this, "_cachedTools", void 0);
|
|
6071
6582
|
_define_property$3(this, "_cachedStaticTools", void 0);
|
|
@@ -6085,6 +6596,7 @@ var McpServerFactoryService_1;
|
|
|
6085
6596
|
this.modelApiGetService = modelApiGetService;
|
|
6086
6597
|
this.roleReader = roleReader;
|
|
6087
6598
|
this.storageService = storageService;
|
|
6599
|
+
this.modelRolesTargetUidPredicate = modelRolesTargetUidPredicate;
|
|
6088
6600
|
this._analyticsService = analyticsService !== null && analyticsService !== void 0 ? analyticsService : noopMcpAnalyticsService();
|
|
6089
6601
|
}
|
|
6090
6602
|
_create_class$3(McpServerFactoryService, [
|
|
@@ -6100,13 +6612,13 @@ var McpServerFactoryService_1;
|
|
|
6100
6612
|
var _this_mcpConfig_serverName, _this_mcpConfig_serverVersion, _this_mcpConfig_serverInstructions;
|
|
6101
6613
|
var baseServerName = (_this_mcpConfig_serverName = this.mcpConfig.serverName) !== null && _this_mcpConfig_serverName !== void 0 ? _this_mcpConfig_serverName : DEFAULT_MCP_SERVER_NAME;
|
|
6102
6614
|
var serverName = this.mcpConfig.readOnly === true ? "".concat(baseServerName, " (read-only)") : baseServerName;
|
|
6103
|
-
var server = new
|
|
6615
|
+
var server$1 = new server.McpServer({
|
|
6104
6616
|
name: serverName,
|
|
6105
6617
|
version: (_this_mcpConfig_serverVersion = this.mcpConfig.serverVersion) !== null && _this_mcpConfig_serverVersion !== void 0 ? _this_mcpConfig_serverVersion : '0.0.0'
|
|
6106
6618
|
}, {
|
|
6107
6619
|
instructions: (_this_mcpConfig_serverInstructions = this.mcpConfig.serverInstructions) !== null && _this_mcpConfig_serverInstructions !== void 0 ? _this_mcpConfig_serverInstructions : DEFAULT_MCP_SERVER_INSTRUCTIONS
|
|
6108
6620
|
});
|
|
6109
|
-
server.server.registerCapabilities({
|
|
6621
|
+
server$1.server.registerCapabilities({
|
|
6110
6622
|
tools: {}
|
|
6111
6623
|
});
|
|
6112
6624
|
var toolGeneration = this._resolveToolDefinitions();
|
|
@@ -6135,7 +6647,7 @@ var McpServerFactoryService_1;
|
|
|
6135
6647
|
batchTool
|
|
6136
6648
|
]);
|
|
6137
6649
|
}
|
|
6138
|
-
server.server.setRequestHandler(
|
|
6650
|
+
server$1.server.setRequestHandler('tools/list', function() {
|
|
6139
6651
|
return _async_to_generator$2(function() {
|
|
6140
6652
|
var _this;
|
|
6141
6653
|
return _ts_generator$2(this, function(_state) {
|
|
@@ -6143,6 +6655,10 @@ var McpServerFactoryService_1;
|
|
|
6143
6655
|
return [
|
|
6144
6656
|
2,
|
|
6145
6657
|
{
|
|
6658
|
+
// McpToolListEntry types its schemas as opaque `object`: they are read from a manifest file
|
|
6659
|
+
// or produced by arktype's JSON Schema export, so the `type: 'object'` root the SDK's wire
|
|
6660
|
+
// type mandates holds at runtime but cannot be proven statically. Every schema reaching here
|
|
6661
|
+
// either came from arktypeToJsonSchemaForExport or defaulted to `{ type: 'object' }`.
|
|
6146
6662
|
tools: listedTools.map(function(tool) {
|
|
6147
6663
|
return _this._buildToolListEntry(tool, ctx, scopes);
|
|
6148
6664
|
})
|
|
@@ -6151,7 +6667,7 @@ var McpServerFactoryService_1;
|
|
|
6151
6667
|
});
|
|
6152
6668
|
}).call(_this);
|
|
6153
6669
|
});
|
|
6154
|
-
server.server.setRequestHandler(
|
|
6670
|
+
server$1.server.setRequestHandler('tools/call', function(request) {
|
|
6155
6671
|
return _async_to_generator$2(function() {
|
|
6156
6672
|
return _ts_generator$2(this, function(_state) {
|
|
6157
6673
|
return [
|
|
@@ -6161,7 +6677,7 @@ var McpServerFactoryService_1;
|
|
|
6161
6677
|
});
|
|
6162
6678
|
}).call(_this);
|
|
6163
6679
|
});
|
|
6164
|
-
return server;
|
|
6680
|
+
return server$1;
|
|
6165
6681
|
}
|
|
6166
6682
|
},
|
|
6167
6683
|
{
|
|
@@ -6351,6 +6867,14 @@ var McpServerFactoryService_1;
|
|
|
6351
6867
|
resolveIdentity: function resolveIdentity(modelType, auth) {
|
|
6352
6868
|
return getService.getModelIdentity(modelType, auth);
|
|
6353
6869
|
}
|
|
6870
|
+
}), createModelRolesTool({
|
|
6871
|
+
readRoleMaps: function readRoleMaps(params) {
|
|
6872
|
+
return getService.readRoleMaps(params);
|
|
6873
|
+
},
|
|
6874
|
+
resolveIdentity: function resolveIdentity(modelType, auth) {
|
|
6875
|
+
return getService.getModelIdentity(modelType, auth);
|
|
6876
|
+
},
|
|
6877
|
+
canTargetOtherUids: this.modelRolesTargetUidPredicate
|
|
6354
6878
|
}));
|
|
6355
6879
|
// `url-models` reuses the model-get read/identity path, so it is only offered when the get
|
|
6356
6880
|
// service is wired AND a route manifest with at least one state was loaded.
|
|
@@ -7104,7 +7628,9 @@ exports.McpServerFactoryService = McpServerFactoryService_1 = __decorate([
|
|
|
7104
7628
|
__param(4, common.Optional()),
|
|
7105
7629
|
__param(4, common.Inject(MCP_ANALYTICS_SERVICE)),
|
|
7106
7630
|
__param(5, common.Optional()),
|
|
7107
|
-
__param(5, common.Inject(firebaseServer.FirebaseServerStorageService))
|
|
7631
|
+
__param(5, common.Inject(firebaseServer.FirebaseServerStorageService)),
|
|
7632
|
+
__param(6, common.Optional()),
|
|
7633
|
+
__param(6, common.Inject(MCP_MODEL_ROLES_TARGET_UID_PREDICATE))
|
|
7108
7634
|
], exports.McpServerFactoryService);
|
|
7109
7635
|
|
|
7110
7636
|
function asyncGeneratorStep$1(gen, resolve, reject, _next, _throw, key, arg) {
|
|
@@ -7236,29 +7762,168 @@ function _ts_generator$1(thisArg, body) {
|
|
|
7236
7762
|
}
|
|
7237
7763
|
}
|
|
7238
7764
|
/**
|
|
7239
|
-
* Handles a single Streamable HTTP JSON-RPC request
|
|
7765
|
+
* Handles a single Streamable HTTP JSON-RPC request, building a fresh MCP server for it.
|
|
7766
|
+
*
|
|
7767
|
+
* Both protocol eras are served, each by its own leg:
|
|
7240
7768
|
*
|
|
7241
|
-
*
|
|
7242
|
-
*
|
|
7243
|
-
*
|
|
7244
|
-
*
|
|
7769
|
+
* - **2026-07-28 (modern)** — `createMcpHandler`, which is per-request and stateless by
|
|
7770
|
+
* construction: there is no `Mcp-Session-Id` and no transport bookkeeping.
|
|
7771
|
+
* - **2025-era (legacy)** — a `NodeStreamableHTTPServerTransport` in stateless mode, which is
|
|
7772
|
+
* byte-for-byte what this endpoint served before the SDK v2 upgrade.
|
|
7773
|
+
*
|
|
7774
|
+
* The legacy leg is wired by hand (the SDK's documented `isLegacyRequest` composition) rather
|
|
7775
|
+
* than left to `createMcpHandler`'s built-in `legacy: 'stateless'` fallback, because that
|
|
7776
|
+
* fallback builds its transport with `sessionIdGenerator` alone and therefore answers in
|
|
7777
|
+
* `text/event-stream`. See {@link handleLegacyMcpRequest} for why that framing is not viable
|
|
7778
|
+
* here.
|
|
7245
7779
|
*
|
|
7246
7780
|
* The caller is expected to have already validated the bearer token (via the
|
|
7247
7781
|
* OIDC bearer middleware) before this function runs.
|
|
7248
7782
|
*
|
|
7249
7783
|
* @param req - The Express request carrying the JSON-RPC body.
|
|
7250
7784
|
* @param res - The Express response.
|
|
7785
|
+
* @param buildServer - Factory producing the MCP server that backs this request.
|
|
7786
|
+
*/ function handleStreamableHttpMcpRequest(req, res, buildServer) {
|
|
7787
|
+
return _async_to_generator$1(function() {
|
|
7788
|
+
var probe;
|
|
7789
|
+
return _ts_generator$1(this, function(_state) {
|
|
7790
|
+
switch(_state.label){
|
|
7791
|
+
case 0:
|
|
7792
|
+
// Hand-wired compositions must reject non-JSON POST bodies themselves — neither the modern
|
|
7793
|
+
// entry's routing nor the legacy transport does it on our behalf once we classify up front.
|
|
7794
|
+
if (!server.isJsonContentType(req.headers['content-type'])) {
|
|
7795
|
+
res.status(415).json({
|
|
7796
|
+
statusCode: 415,
|
|
7797
|
+
message: 'Unsupported Media Type'
|
|
7798
|
+
});
|
|
7799
|
+
return [
|
|
7800
|
+
2
|
|
7801
|
+
];
|
|
7802
|
+
}
|
|
7803
|
+
return [
|
|
7804
|
+
4,
|
|
7805
|
+
node.toWebRequest(req, req.body)
|
|
7806
|
+
];
|
|
7807
|
+
case 1:
|
|
7808
|
+
probe = _state.sent();
|
|
7809
|
+
return [
|
|
7810
|
+
4,
|
|
7811
|
+
server.isLegacyRequest(probe)
|
|
7812
|
+
];
|
|
7813
|
+
case 2:
|
|
7814
|
+
if (!_state.sent()) return [
|
|
7815
|
+
3,
|
|
7816
|
+
4
|
|
7817
|
+
];
|
|
7818
|
+
return [
|
|
7819
|
+
4,
|
|
7820
|
+
handleLegacyMcpRequest(req, res, buildServer())
|
|
7821
|
+
];
|
|
7822
|
+
case 3:
|
|
7823
|
+
_state.sent();
|
|
7824
|
+
return [
|
|
7825
|
+
3,
|
|
7826
|
+
6
|
|
7827
|
+
];
|
|
7828
|
+
case 4:
|
|
7829
|
+
return [
|
|
7830
|
+
4,
|
|
7831
|
+
handleModernMcpRequest(req, res, buildServer)
|
|
7832
|
+
];
|
|
7833
|
+
case 5:
|
|
7834
|
+
_state.sent();
|
|
7835
|
+
_state.label = 6;
|
|
7836
|
+
case 6:
|
|
7837
|
+
return [
|
|
7838
|
+
2
|
|
7839
|
+
];
|
|
7840
|
+
}
|
|
7841
|
+
});
|
|
7842
|
+
})();
|
|
7843
|
+
}
|
|
7844
|
+
/**
|
|
7845
|
+
* Serves one 2026-07-28 request through `createMcpHandler`.
|
|
7846
|
+
*
|
|
7847
|
+
* `legacy: 'reject'` keeps this leg modern-only — 2025-era traffic never reaches it, having
|
|
7848
|
+
* already been routed to {@link handleLegacyMcpRequest}.
|
|
7849
|
+
*
|
|
7850
|
+
* `responseMode: 'json'` is the modern-leg equivalent of the legacy transport's
|
|
7851
|
+
* `enableJsonResponse`, keeping the endpoint on a single `application/json` body. Safe here
|
|
7852
|
+
* because no tool handler emits mid-call notifications, which are the only thing `'json'` drops.
|
|
7853
|
+
*
|
|
7854
|
+
* @param req - The Express request carrying the JSON-RPC body.
|
|
7855
|
+
* @param res - The Express response.
|
|
7856
|
+
* @param buildServer - Factory producing the MCP server that backs this request.
|
|
7857
|
+
*/ function handleModernMcpRequest(req, res, buildServer) {
|
|
7858
|
+
return _async_to_generator$1(function() {
|
|
7859
|
+
var handler;
|
|
7860
|
+
return _ts_generator$1(this, function(_state) {
|
|
7861
|
+
switch(_state.label){
|
|
7862
|
+
case 0:
|
|
7863
|
+
handler = server.createMcpHandler(buildServer, {
|
|
7864
|
+
legacy: 'reject',
|
|
7865
|
+
responseMode: 'json'
|
|
7866
|
+
});
|
|
7867
|
+
_state.label = 1;
|
|
7868
|
+
case 1:
|
|
7869
|
+
_state.trys.push([
|
|
7870
|
+
1,
|
|
7871
|
+
,
|
|
7872
|
+
3,
|
|
7873
|
+
5
|
|
7874
|
+
]);
|
|
7875
|
+
return [
|
|
7876
|
+
4,
|
|
7877
|
+
node.toNodeHandler(handler)(req, res, req.body)
|
|
7878
|
+
];
|
|
7879
|
+
case 2:
|
|
7880
|
+
_state.sent();
|
|
7881
|
+
return [
|
|
7882
|
+
3,
|
|
7883
|
+
5
|
|
7884
|
+
];
|
|
7885
|
+
case 3:
|
|
7886
|
+
return [
|
|
7887
|
+
4,
|
|
7888
|
+
handler.close()
|
|
7889
|
+
];
|
|
7890
|
+
case 4:
|
|
7891
|
+
_state.sent();
|
|
7892
|
+
return [
|
|
7893
|
+
7
|
|
7894
|
+
];
|
|
7895
|
+
case 5:
|
|
7896
|
+
return [
|
|
7897
|
+
2
|
|
7898
|
+
];
|
|
7899
|
+
}
|
|
7900
|
+
});
|
|
7901
|
+
})();
|
|
7902
|
+
}
|
|
7903
|
+
/**
|
|
7904
|
+
* Serves one 2025-era request through a stateless `NodeStreamableHTTPServerTransport`.
|
|
7905
|
+
*
|
|
7906
|
+
* `sessionIdGenerator: undefined` selects stateless mode: the transport binds the server,
|
|
7907
|
+
* processes one request, and is discarded.
|
|
7908
|
+
*
|
|
7909
|
+
* `enableJsonResponse: true` replies with a single `application/json` body instead of framing it
|
|
7910
|
+
* as `text/event-stream`. Stateless mode answers exactly one JSON-RPC message per request and the
|
|
7911
|
+
* controller only implements POST (there is no standalone SSE stream to keep open), so SSE framing
|
|
7912
|
+
* adds nothing while making the endpoint fragile behind proxies that buffer or rewrite streamed
|
|
7913
|
+
* responses — the failure that originally pushed the demo app off its dev proxy.
|
|
7914
|
+
*
|
|
7915
|
+
* @param req - The Express request carrying the JSON-RPC body.
|
|
7916
|
+
* @param res - The Express response.
|
|
7251
7917
|
* @param server - The MCP server instance to back the transport.
|
|
7252
|
-
|
|
7253
|
-
*/ function handleStreamableHttpMcpRequest(req, res, server) {
|
|
7918
|
+
*/ function handleLegacyMcpRequest(req, res, server) {
|
|
7254
7919
|
return _async_to_generator$1(function() {
|
|
7255
7920
|
var transport;
|
|
7256
7921
|
return _ts_generator$1(this, function(_state) {
|
|
7257
7922
|
switch(_state.label){
|
|
7258
7923
|
case 0:
|
|
7259
|
-
|
|
7260
|
-
|
|
7261
|
-
|
|
7924
|
+
transport = new node.NodeStreamableHTTPServerTransport({
|
|
7925
|
+
sessionIdGenerator: undefined,
|
|
7926
|
+
enableJsonResponse: true
|
|
7262
7927
|
});
|
|
7263
7928
|
res.on('close', function() {
|
|
7264
7929
|
void transport.close();
|
|
@@ -7450,9 +8115,11 @@ var McpController_1;
|
|
|
7450
8115
|
* which must include `'/mcp'` in its `protectedPaths`. By the time the request reaches
|
|
7451
8116
|
* this controller, `req.auth` is populated with the authenticated user's data.
|
|
7452
8117
|
*
|
|
7453
|
-
* Each request gets a fresh
|
|
7454
|
-
*
|
|
7455
|
-
*
|
|
8118
|
+
* Each request gets a fresh MCP server (stateless mode), which is adequate for Claude
|
|
8119
|
+
* custom-connector style usage. Both protocol eras are served — 2026-07-28 through the
|
|
8120
|
+
* SDK's per-request `createMcpHandler` entry, and 2025-era through a stateless Streamable
|
|
8121
|
+
* HTTP transport. A session-tracked variant can be layered on later if streaming tool
|
|
8122
|
+
* output becomes a requirement.
|
|
7456
8123
|
*/ exports.McpController = McpController_1 = /*#__PURE__*/ function() {
|
|
7457
8124
|
function McpController(factory) {
|
|
7458
8125
|
_class_call_check$2(this, McpController);
|
|
@@ -7465,15 +8132,12 @@ var McpController_1;
|
|
|
7465
8132
|
key: "handleMcpRequest",
|
|
7466
8133
|
value: function handleMcpRequest(req, res) {
|
|
7467
8134
|
return _async_to_generator(function() {
|
|
7468
|
-
var
|
|
8135
|
+
var _this, auth, error;
|
|
7469
8136
|
return _ts_generator(this, function(_state) {
|
|
7470
8137
|
switch(_state.label){
|
|
7471
8138
|
case 0:
|
|
8139
|
+
_this = this;
|
|
7472
8140
|
auth = req.auth;
|
|
7473
|
-
server = this.factory.createServer({
|
|
7474
|
-
auth: auth,
|
|
7475
|
-
rawRequest: req
|
|
7476
|
-
});
|
|
7477
8141
|
_state.label = 1;
|
|
7478
8142
|
case 1:
|
|
7479
8143
|
_state.trys.push([
|
|
@@ -7484,7 +8148,12 @@ var McpController_1;
|
|
|
7484
8148
|
]);
|
|
7485
8149
|
return [
|
|
7486
8150
|
4,
|
|
7487
|
-
handleStreamableHttpMcpRequest(req, res,
|
|
8151
|
+
handleStreamableHttpMcpRequest(req, res, function() {
|
|
8152
|
+
return _this.factory.createServer({
|
|
8153
|
+
auth: auth,
|
|
8154
|
+
rawRequest: req
|
|
8155
|
+
});
|
|
8156
|
+
})
|
|
7488
8157
|
];
|
|
7489
8158
|
case 2:
|
|
7490
8159
|
_state.sent();
|
|
@@ -7513,6 +8182,45 @@ var McpController_1;
|
|
|
7513
8182
|
});
|
|
7514
8183
|
}).call(this);
|
|
7515
8184
|
}
|
|
8185
|
+
},
|
|
8186
|
+
{
|
|
8187
|
+
/**
|
|
8188
|
+
* Rejects the Streamable HTTP transport's optional `GET` method, which opens a standalone SSE
|
|
8189
|
+
* stream. Not applicable in stateless mode. Separate from {@link handleUnsupportedDelete} because
|
|
8190
|
+
* NestJS binds one HTTP method per handler — stacking route decorators would silently drop one.
|
|
8191
|
+
*
|
|
8192
|
+
* @param res - The Express response to write the rejection to.
|
|
8193
|
+
*/ key: "handleUnsupportedGet",
|
|
8194
|
+
value: function handleUnsupportedGet(res) {
|
|
8195
|
+
this._rejectUnsupportedMethod(res);
|
|
8196
|
+
}
|
|
8197
|
+
},
|
|
8198
|
+
{
|
|
8199
|
+
/**
|
|
8200
|
+
* Rejects the Streamable HTTP transport's optional `DELETE` method, which tears down a session.
|
|
8201
|
+
* Not applicable in stateless mode — no session is ever issued.
|
|
8202
|
+
*
|
|
8203
|
+
* @param res - The Express response to write the rejection to.
|
|
8204
|
+
*/ key: "handleUnsupportedDelete",
|
|
8205
|
+
value: function handleUnsupportedDelete(res) {
|
|
8206
|
+
this._rejectUnsupportedMethod(res);
|
|
8207
|
+
}
|
|
8208
|
+
},
|
|
8209
|
+
{
|
|
8210
|
+
/**
|
|
8211
|
+
* Answers with a spec-conformant `405 Method Not Allowed` + `Allow` header. Without these
|
|
8212
|
+
* handlers NestJS answers `404`, which some clients treat as a hard failure rather than
|
|
8213
|
+
* "the server doesn't offer this".
|
|
8214
|
+
*
|
|
8215
|
+
* @param res - The Express response to write the rejection to.
|
|
8216
|
+
*/ key: "_rejectUnsupportedMethod",
|
|
8217
|
+
value: function _rejectUnsupportedMethod(res) {
|
|
8218
|
+
res.setHeader('Allow', 'POST');
|
|
8219
|
+
res.status(common.HttpStatus.METHOD_NOT_ALLOWED).json({
|
|
8220
|
+
statusCode: common.HttpStatus.METHOD_NOT_ALLOWED,
|
|
8221
|
+
message: 'Method Not Allowed'
|
|
8222
|
+
});
|
|
8223
|
+
}
|
|
7516
8224
|
}
|
|
7517
8225
|
]);
|
|
7518
8226
|
return McpController;
|
|
@@ -7522,6 +8230,14 @@ __decorate([
|
|
|
7522
8230
|
__param(0, common.Req()),
|
|
7523
8231
|
__param(1, common.Res())
|
|
7524
8232
|
], exports.McpController.prototype, "handleMcpRequest", null);
|
|
8233
|
+
__decorate([
|
|
8234
|
+
common.Get(),
|
|
8235
|
+
__param(0, common.Res())
|
|
8236
|
+
], exports.McpController.prototype, "handleUnsupportedGet", null);
|
|
8237
|
+
__decorate([
|
|
8238
|
+
common.Delete(),
|
|
8239
|
+
__param(0, common.Res())
|
|
8240
|
+
], exports.McpController.prototype, "handleUnsupportedDelete", null);
|
|
7525
8241
|
exports.McpController = McpController_1 = __decorate([
|
|
7526
8242
|
common.Controller('mcp'),
|
|
7527
8243
|
__param(0, common.Inject(exports.McpServerFactoryService))
|
|
@@ -7607,6 +8323,13 @@ function _unsupported_iterable_to_array$2(o, minLen) {
|
|
|
7607
8323
|
* URIs must live at the host root. Apps need to exclude `.well-known/{*path}`
|
|
7608
8324
|
* from any global API route prefix (see `FIREBASE_SERVER_OIDC_ROUTES_FOR_GLOBAL_ROUTE_EXCLUDE`
|
|
7609
8325
|
* in `firebase-server/oidc` for the corresponding OIDC excludes).
|
|
8326
|
+
*
|
|
8327
|
+
* Because the document must be reachable at the origin root, the MCP endpoint has to be
|
|
8328
|
+
* exposed through a host that serves the app at `/` — Firebase Hosting, or the hosting
|
|
8329
|
+
* emulator in development. Pointing a client straight at the Functions emulator origin
|
|
8330
|
+
* (`http://localhost:<port>/<project>/<region>/<function>/mcp`) breaks discovery: that
|
|
8331
|
+
* runtime only routes `/<project>/<region>/<function>/…`, so nothing answers at the root
|
|
8332
|
+
* and a client that has not yet seen a 401 challenge cannot find the issuer.
|
|
7610
8333
|
*/ exports.McpWellKnownController = /*#__PURE__*/ function() {
|
|
7611
8334
|
function McpWellKnownController(mcpConfig, oidcProviderConfigService) {
|
|
7612
8335
|
_class_call_check$1(this, McpWellKnownController);
|
|
@@ -7617,12 +8340,26 @@ function _unsupported_iterable_to_array$2(o, minLen) {
|
|
|
7617
8340
|
}
|
|
7618
8341
|
_create_class$1(McpWellKnownController, [
|
|
7619
8342
|
{
|
|
7620
|
-
|
|
8343
|
+
/**
|
|
8344
|
+
* Serves both RFC 9728 discovery URLs — the primary form (§3.1), which inserts the resource's
|
|
8345
|
+
* path after the well-known prefix (`/.well-known/oauth-protected-resource/mcp`), and the bare
|
|
8346
|
+
* form (`/.well-known/oauth-protected-resource`) that clients probe as a fallback.
|
|
8347
|
+
*
|
|
8348
|
+
* The document is identical either way; the resource identity comes from
|
|
8349
|
+
* {@link McpModuleConfig.mcpUrl}, not from the request path. Serving the primary form means
|
|
8350
|
+
* discovery succeeds on a client's first probe rather than depending on it implementing the
|
|
8351
|
+
* fallback.
|
|
8352
|
+
*
|
|
8353
|
+
* @returns The protected-resource metadata document.
|
|
8354
|
+
*/ key: "getProtectedResourceMetadata",
|
|
7621
8355
|
value: function getProtectedResourceMetadata() {
|
|
7622
8356
|
var _this_mcpConfig = this.mcpConfig, mcpUrl = _this_mcpConfig.mcpUrl, oidcIssuer = _this_mcpConfig.oidcIssuer, scopesFilter = _this_mcpConfig.scopesSupported;
|
|
7623
|
-
// Base scope list is the
|
|
7624
|
-
//
|
|
7625
|
-
|
|
8357
|
+
// Base scope list is what the issuer grants an arbitrary client, NOT its full
|
|
8358
|
+
// `scopes_supported`: a dynamic-registration client copies this list verbatim onto
|
|
8359
|
+
// `/authorize`, and a provider-profile-gated scope in it would be hard-rejected at the
|
|
8360
|
+
// consent unlock gate (`access_denied`) for every client lacking that profile assignment.
|
|
8361
|
+
// The optional filter narrows the list further.
|
|
8362
|
+
var providerScopes = this.oidcProviderConfigService.clientRequestableScopesSupported;
|
|
7626
8363
|
var scopes = scopesFilter ? scopesFilter(providerScopes) : providerScopes;
|
|
7627
8364
|
return _object_spread({
|
|
7628
8365
|
resource: mcpUrl,
|
|
@@ -7638,7 +8375,10 @@ function _unsupported_iterable_to_array$2(o, minLen) {
|
|
|
7638
8375
|
return McpWellKnownController;
|
|
7639
8376
|
}();
|
|
7640
8377
|
__decorate([
|
|
7641
|
-
common.Get(
|
|
8378
|
+
common.Get([
|
|
8379
|
+
'oauth-protected-resource',
|
|
8380
|
+
'oauth-protected-resource/{*path}'
|
|
8381
|
+
])
|
|
7642
8382
|
], exports.McpWellKnownController.prototype, "getProtectedResourceMetadata", null);
|
|
7643
8383
|
exports.McpWellKnownController = __decorate([
|
|
7644
8384
|
common.Controller('.well-known'),
|
|
@@ -7976,6 +8716,8 @@ exports.MCP_AUTH_ROLE_READER = MCP_AUTH_ROLE_READER;
|
|
|
7976
8716
|
exports.MCP_CALL_TYPE_ABBREVIATIONS = MCP_CALL_TYPE_ABBREVIATIONS;
|
|
7977
8717
|
exports.MCP_MANIFEST_VERSION = MCP_MANIFEST_VERSION;
|
|
7978
8718
|
exports.MCP_MODEL_GET_BATCH_SIZE = MCP_MODEL_GET_BATCH_SIZE;
|
|
8719
|
+
exports.MCP_MODEL_ROLES_MAX_KEYS = MCP_MODEL_ROLES_MAX_KEYS;
|
|
8720
|
+
exports.MCP_MODEL_ROLES_TARGET_UID_PREDICATE = MCP_MODEL_ROLES_TARGET_UID_PREDICATE;
|
|
7979
8721
|
exports.MCP_TOOL_NAME_MAX_LENGTH = MCP_TOOL_NAME_MAX_LENGTH;
|
|
7980
8722
|
exports.MCP_TOOL_NAME_WARN_LENGTH = MCP_TOOL_NAME_WARN_LENGTH;
|
|
7981
8723
|
exports.MCP_WRITE_TOOL_DESCRIPTION_PREFIX = MCP_WRITE_TOOL_DESCRIPTION_PREFIX;
|
|
@@ -7990,6 +8732,9 @@ exports.MODEL_INFO_DISPATCH_MODEL_TYPE = MODEL_INFO_DISPATCH_MODEL_TYPE;
|
|
|
7990
8732
|
exports.MODEL_INFO_GROUPS_HINT = MODEL_INFO_GROUPS_HINT;
|
|
7991
8733
|
exports.MODEL_INFO_TOOL_NAME = MODEL_INFO_TOOL_NAME;
|
|
7992
8734
|
exports.MODEL_INFO_UNGROUPED_LABEL = MODEL_INFO_UNGROUPED_LABEL;
|
|
8735
|
+
exports.MODEL_ROLES_DISPATCH_CALL = MODEL_ROLES_DISPATCH_CALL;
|
|
8736
|
+
exports.MODEL_ROLES_DISPATCH_MODEL_TYPE = MODEL_ROLES_DISPATCH_MODEL_TYPE;
|
|
8737
|
+
exports.MODEL_ROLES_TOOL_NAME = MODEL_ROLES_TOOL_NAME;
|
|
7993
8738
|
exports.McpAnalyticsService = McpAnalyticsService;
|
|
7994
8739
|
exports.McpModuleConfig = McpModuleConfig;
|
|
7995
8740
|
exports.ROUTE_MANIFEST_VERSION = ROUTE_MANIFEST_VERSION;
|
|
@@ -8012,6 +8757,7 @@ exports.createEnumInfoTool = createEnumInfoTool;
|
|
|
8012
8757
|
exports.createModelDecodeTool = createModelDecodeTool;
|
|
8013
8758
|
exports.createModelGetTool = createModelGetTool;
|
|
8014
8759
|
exports.createModelInfoTool = createModelInfoTool;
|
|
8760
|
+
exports.createModelRolesTool = createModelRolesTool;
|
|
8015
8761
|
exports.createUrlModelsTool = createUrlModelsTool;
|
|
8016
8762
|
exports.decodeFirestoreModelKey = decodeFirestoreModelKey;
|
|
8017
8763
|
exports.extractMcpReasonFromArgs = extractMcpReasonFromArgs;
|