@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/package.json
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dereekb/firebase-server/mcp",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.38.0",
|
|
4
4
|
"peerDependencies": {
|
|
5
|
-
"@dereekb/analytics": "13.
|
|
6
|
-
"@dereekb/date": "13.
|
|
7
|
-
"@dereekb/firebase": "13.
|
|
8
|
-
"@dereekb/firebase-server": "13.
|
|
9
|
-
"@dereekb/firebase-server/oidc": "13.
|
|
10
|
-
"@dereekb/model": "13.
|
|
11
|
-
"@dereekb/nestjs": "13.
|
|
12
|
-
"@dereekb/rxjs": "13.
|
|
13
|
-
"@dereekb/util": "13.
|
|
14
|
-
"@dereekb/zoho": "13.
|
|
15
|
-
"@modelcontextprotocol/
|
|
5
|
+
"@dereekb/analytics": "13.38.0",
|
|
6
|
+
"@dereekb/date": "13.38.0",
|
|
7
|
+
"@dereekb/firebase": "13.38.0",
|
|
8
|
+
"@dereekb/firebase-server": "13.38.0",
|
|
9
|
+
"@dereekb/firebase-server/oidc": "13.38.0",
|
|
10
|
+
"@dereekb/model": "13.38.0",
|
|
11
|
+
"@dereekb/nestjs": "13.38.0",
|
|
12
|
+
"@dereekb/rxjs": "13.38.0",
|
|
13
|
+
"@dereekb/util": "13.38.0",
|
|
14
|
+
"@dereekb/zoho": "13.38.0",
|
|
15
|
+
"@modelcontextprotocol/node": "2.0.0",
|
|
16
|
+
"@modelcontextprotocol/server": "2.0.0",
|
|
16
17
|
"@nestjs/common": "^11.1.19",
|
|
17
18
|
"@nestjs/core": "^11.1.19",
|
|
18
19
|
"arktype": "^2.2.0",
|
|
@@ -7,13 +7,38 @@ import { McpServerFactoryService } from '../service/mcp.server.factory';
|
|
|
7
7
|
* which must include `'/mcp'` in its `protectedPaths`. By the time the request reaches
|
|
8
8
|
* this controller, `req.auth` is populated with the authenticated user's data.
|
|
9
9
|
*
|
|
10
|
-
* Each request gets a fresh
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* Each request gets a fresh MCP server (stateless mode), which is adequate for Claude
|
|
11
|
+
* custom-connector style usage. Both protocol eras are served — 2026-07-28 through the
|
|
12
|
+
* SDK's per-request `createMcpHandler` entry, and 2025-era through a stateless Streamable
|
|
13
|
+
* HTTP transport. A session-tracked variant can be layered on later if streaming tool
|
|
14
|
+
* output becomes a requirement.
|
|
13
15
|
*/
|
|
14
16
|
export declare class McpController {
|
|
15
17
|
private readonly factory;
|
|
16
18
|
private readonly _logger;
|
|
17
19
|
constructor(factory: McpServerFactoryService);
|
|
18
20
|
handleMcpRequest(req: Request, res: Response): Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* Rejects the Streamable HTTP transport's optional `GET` method, which opens a standalone SSE
|
|
23
|
+
* stream. Not applicable in stateless mode. Separate from {@link handleUnsupportedDelete} because
|
|
24
|
+
* NestJS binds one HTTP method per handler — stacking route decorators would silently drop one.
|
|
25
|
+
*
|
|
26
|
+
* @param res - The Express response to write the rejection to.
|
|
27
|
+
*/
|
|
28
|
+
handleUnsupportedGet(res: Response): void;
|
|
29
|
+
/**
|
|
30
|
+
* Rejects the Streamable HTTP transport's optional `DELETE` method, which tears down a session.
|
|
31
|
+
* Not applicable in stateless mode — no session is ever issued.
|
|
32
|
+
*
|
|
33
|
+
* @param res - The Express response to write the rejection to.
|
|
34
|
+
*/
|
|
35
|
+
handleUnsupportedDelete(res: Response): void;
|
|
36
|
+
/**
|
|
37
|
+
* Answers with a spec-conformant `405 Method Not Allowed` + `Allow` header. Without these
|
|
38
|
+
* handlers NestJS answers `404`, which some clients treat as a hard failure rather than
|
|
39
|
+
* "the server doesn't offer this".
|
|
40
|
+
*
|
|
41
|
+
* @param res - The Express response to write the rejection to.
|
|
42
|
+
*/
|
|
43
|
+
private _rejectUnsupportedMethod;
|
|
19
44
|
}
|
|
@@ -12,7 +12,8 @@ export interface OAuthProtectedResourceMetadata {
|
|
|
12
12
|
/**
|
|
13
13
|
* Scopes the resource accepts (RFC 9728 §2). Advertised so dynamic-registration
|
|
14
14
|
* MCP clients (e.g. the Claude Code CLI) know which scopes to request on the
|
|
15
|
-
* authorization call
|
|
15
|
+
* authorization call — they request this list verbatim, so it carries only scopes
|
|
16
|
+
* such a client can actually be granted. Omitted when the resolved list is empty.
|
|
16
17
|
*/
|
|
17
18
|
readonly scopes_supported?: ReadonlyArray<OidcScope>;
|
|
18
19
|
}
|
|
@@ -25,10 +26,29 @@ export interface OAuthProtectedResourceMetadata {
|
|
|
25
26
|
* URIs must live at the host root. Apps need to exclude `.well-known/{*path}`
|
|
26
27
|
* from any global API route prefix (see `FIREBASE_SERVER_OIDC_ROUTES_FOR_GLOBAL_ROUTE_EXCLUDE`
|
|
27
28
|
* in `firebase-server/oidc` for the corresponding OIDC excludes).
|
|
29
|
+
*
|
|
30
|
+
* Because the document must be reachable at the origin root, the MCP endpoint has to be
|
|
31
|
+
* exposed through a host that serves the app at `/` — Firebase Hosting, or the hosting
|
|
32
|
+
* emulator in development. Pointing a client straight at the Functions emulator origin
|
|
33
|
+
* (`http://localhost:<port>/<project>/<region>/<function>/mcp`) breaks discovery: that
|
|
34
|
+
* runtime only routes `/<project>/<region>/<function>/…`, so nothing answers at the root
|
|
35
|
+
* and a client that has not yet seen a 401 challenge cannot find the issuer.
|
|
28
36
|
*/
|
|
29
37
|
export declare class McpWellKnownController {
|
|
30
38
|
private readonly mcpConfig;
|
|
31
39
|
private readonly oidcProviderConfigService;
|
|
32
40
|
constructor(mcpConfig: McpModuleConfig, oidcProviderConfigService: OidcProviderConfigService);
|
|
41
|
+
/**
|
|
42
|
+
* Serves both RFC 9728 discovery URLs — the primary form (§3.1), which inserts the resource's
|
|
43
|
+
* path after the well-known prefix (`/.well-known/oauth-protected-resource/mcp`), and the bare
|
|
44
|
+
* form (`/.well-known/oauth-protected-resource`) that clients probe as a fallback.
|
|
45
|
+
*
|
|
46
|
+
* The document is identical either way; the resource identity comes from
|
|
47
|
+
* {@link McpModuleConfig.mcpUrl}, not from the request path. Serving the primary form means
|
|
48
|
+
* discovery succeeds on a client's first probe rather than depending on it implementing the
|
|
49
|
+
* fallback.
|
|
50
|
+
*
|
|
51
|
+
* @returns The protected-resource metadata document.
|
|
52
|
+
*/
|
|
33
53
|
getProtectedResourceMetadata(): OAuthProtectedResourceMetadata;
|
|
34
54
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { type AuthClaims, type AuthRoleSet } from '@dereekb/util';
|
|
1
|
+
import { type AuthClaims, type AuthRoleSet, type Maybe, type PromiseOrValue } from '@dereekb/util';
|
|
2
|
+
import { type FirebaseServerAuthData } from '@dereekb/firebase-server';
|
|
2
3
|
import { type FirestoreModelType, type OidcModelScopeRequirement, type OidcScope, type OidcScopeTerm } from '@dereekb/firebase';
|
|
3
4
|
/**
|
|
4
5
|
* Default path the MCP Streamable HTTP transport is mounted at.
|
|
@@ -83,8 +84,9 @@ export interface McpReasonParameterConfig {
|
|
|
83
84
|
/**
|
|
84
85
|
* Filter applied to the OIDC provider's advertised scope list when building the MCP
|
|
85
86
|
* protected-resource metadata's `scopes_supported`. Receives every scope the provider
|
|
86
|
-
* issues (from `OidcProviderConfigService.
|
|
87
|
-
* advertise. Supplied via {@link McpModuleConfig.scopesSupported};
|
|
87
|
+
* issues to an arbitrary client (from `OidcProviderConfigService.clientRequestableScopesSupported`)
|
|
88
|
+
* and returns the subset to advertise. Supplied via {@link McpModuleConfig.scopesSupported};
|
|
89
|
+
* when unset, all of them are advertised.
|
|
88
90
|
*/
|
|
89
91
|
export type McpScopesSupportedFilter = (allScopes: readonly OidcScope[]) => readonly OidcScope[];
|
|
90
92
|
/**
|
|
@@ -127,11 +129,12 @@ export declare abstract class McpModuleConfig {
|
|
|
127
129
|
* protected-resource discovery document's `scopes_supported` (RFC 9728 §2).
|
|
128
130
|
*
|
|
129
131
|
* The base list is pulled automatically from the OIDC provider config via the
|
|
130
|
-
* injected {@link OidcProviderConfigService.
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
132
|
+
* injected {@link OidcProviderConfigService.clientRequestableScopesSupported}, so
|
|
133
|
+
* the MCP resource advertises the scopes the issuer grants an arbitrary client
|
|
134
|
+
* without the app restating them. Scopes only an admin-assigned provider profile
|
|
135
|
+
* unlocks are already excluded — requesting one is fatal at the consent unlock
|
|
136
|
+
* gate. Provide this only to narrow the set further; it returns the subset to
|
|
137
|
+
* advertise. When unset, the whole base list is advertised.
|
|
135
138
|
*
|
|
136
139
|
* Advertising these matters because dynamic-registration MCP clients (the Claude
|
|
137
140
|
* Code CLI) read `scopes_supported` to decide which scopes to request on the
|
|
@@ -236,3 +239,23 @@ export type McpAuthRoleReader = (claims: AuthClaims) => AuthRoleSet;
|
|
|
236
239
|
* NestJS injection token for the optional {@link McpAuthRoleReader} provider.
|
|
237
240
|
*/
|
|
238
241
|
export declare const MCP_AUTH_ROLE_READER = "MCP_AUTH_ROLE_READER";
|
|
242
|
+
/**
|
|
243
|
+
* Signature for the optional predicate that authorizes `model-roles` calls which target another
|
|
244
|
+
* user's uid.
|
|
245
|
+
*
|
|
246
|
+
* `model-roles` resolves permissions for the calling user by default, which is always safe. Passing
|
|
247
|
+
* a `uid` asks the server "what can *that* user do here?" — an answer that leaks the target's
|
|
248
|
+
* effective access, so it is gated behind this app-supplied predicate rather than being open.
|
|
249
|
+
*
|
|
250
|
+
* Receives the calling request's auth data (`undefined` for an unauthenticated request) and returns
|
|
251
|
+
* true if that caller may resolve roles for arbitrary uids. Typically an admin check, e.g.
|
|
252
|
+
* `(auth) => authRoleClaimsService.toRoles(auth?.token ?? {}).has('admin')`.
|
|
253
|
+
*
|
|
254
|
+
* When no predicate is provided the `uid` parameter fails closed for every caller — the tool is
|
|
255
|
+
* still registered and still answers for the caller themselves.
|
|
256
|
+
*/
|
|
257
|
+
export type McpModelRolesTargetUidPredicate = (auth: Maybe<FirebaseServerAuthData>) => PromiseOrValue<boolean>;
|
|
258
|
+
/**
|
|
259
|
+
* NestJS injection token for the optional {@link McpModelRolesTargetUidPredicate} provider.
|
|
260
|
+
*/
|
|
261
|
+
export declare const MCP_MODEL_ROLES_TARGET_UID_PREDICATE = "MCP_MODEL_ROLES_TARGET_UID_PREDICATE";
|
|
@@ -17,7 +17,8 @@ export interface McpModuleMetadataConfig extends Pick<ModuleMetadata, 'imports'
|
|
|
17
17
|
* - {@link ModelApiDispatchConfig} — so the MCP server can reuse the call model dispatch chain.
|
|
18
18
|
* - {@link McpModuleConfig} — issuer + resource URLs for protected-resource discovery.
|
|
19
19
|
* - `OidcProviderConfigService` (from `@dereekb/firebase-server/oidc`) — the `McpWellKnownController`
|
|
20
|
-
* reads the provider's `
|
|
20
|
+
* reads the provider's `clientRequestableScopesSupported` to advertise them on the
|
|
21
|
+
* protected-resource document.
|
|
21
22
|
*
|
|
22
23
|
* In practice, downstream apps typically import their own `*ModelApiModule` first (which provides
|
|
23
24
|
* `ModelApiCallModelDispatchService` + `MODEL_API_NEST_APPLICATION_CONTEXT`) and add the
|
|
@@ -7,6 +7,7 @@ export * from './mcp.server.factory';
|
|
|
7
7
|
export * from './mcp.tool-generator';
|
|
8
8
|
export * from './mcp.visibility';
|
|
9
9
|
export * from './tools/mcp.tool.model-get';
|
|
10
|
+
export * from './tools/mcp.tool.model-roles';
|
|
10
11
|
export * from './tools/mcp.tool.model-info';
|
|
11
12
|
export * from './tools/mcp.tool.model-decode';
|
|
12
13
|
export * from './tools/mcp.tool.enum-info';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { McpServer } from '@modelcontextprotocol/
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/server';
|
|
2
2
|
import { type Request } from 'express';
|
|
3
3
|
import { ModelApiCallModelDispatchService, ModelApiGetService, FirebaseServerStorageService, type FirebaseServerAuthData } from '@dereekb/firebase-server';
|
|
4
|
-
import { McpModuleConfig, type McpAuthRoleReader } from '../mcp.config';
|
|
4
|
+
import { McpModuleConfig, type McpAuthRoleReader, type McpModelRolesTargetUidPredicate } from '../mcp.config';
|
|
5
5
|
import { type McpAnalyticsService } from './analytics/mcp.analytics.handler';
|
|
6
6
|
/**
|
|
7
7
|
* Optional per-request context passed when invoking the MCP server through a
|
|
@@ -17,9 +17,10 @@ export interface McpRequestContext {
|
|
|
17
17
|
* Injectable factory that builds {@link McpServer} instances pre-wired to the
|
|
18
18
|
* call model dispatch chain.
|
|
19
19
|
*
|
|
20
|
-
* The factory is invoked per Streamable HTTP request
|
|
21
|
-
*
|
|
22
|
-
*
|
|
20
|
+
* The factory is invoked per Streamable HTTP request. That is the shape the MCP
|
|
21
|
+
* SDK's 2026-07-28 entry (`createMcpHandler`) requires — it builds a fresh server
|
|
22
|
+
* per request and has no session concept at all — and it is equally what the SDK
|
|
23
|
+
* recommends for stateless 2025-era serving, so one factory backs both eras.
|
|
23
24
|
*/
|
|
24
25
|
export declare class McpServerFactoryService {
|
|
25
26
|
private readonly mcpConfig;
|
|
@@ -27,6 +28,7 @@ export declare class McpServerFactoryService {
|
|
|
27
28
|
private readonly modelApiGetService?;
|
|
28
29
|
private readonly roleReader?;
|
|
29
30
|
private readonly storageService?;
|
|
31
|
+
private readonly modelRolesTargetUidPredicate?;
|
|
30
32
|
private readonly _logger;
|
|
31
33
|
private _cachedTools;
|
|
32
34
|
private _cachedStaticTools;
|
|
@@ -41,7 +43,7 @@ export declare class McpServerFactoryService {
|
|
|
41
43
|
private _warnedMissingRoleReader;
|
|
42
44
|
private _resolvedReasonConfig?;
|
|
43
45
|
private readonly _analyticsService;
|
|
44
|
-
constructor(mcpConfig: McpModuleConfig, dispatchService: ModelApiCallModelDispatchService, modelApiGetService?: ModelApiGetService | undefined, roleReader?: McpAuthRoleReader | undefined, analyticsService?: McpAnalyticsService, storageService?: FirebaseServerStorageService | undefined);
|
|
46
|
+
constructor(mcpConfig: McpModuleConfig, dispatchService: ModelApiCallModelDispatchService, modelApiGetService?: ModelApiGetService | undefined, roleReader?: McpAuthRoleReader | undefined, analyticsService?: McpAnalyticsService, storageService?: FirebaseServerStorageService | undefined, modelRolesTargetUidPredicate?: McpModelRolesTargetUidPredicate | undefined);
|
|
45
47
|
/**
|
|
46
48
|
* Builds a configured MCP server with tool listing + dispatch handlers wired up.
|
|
47
49
|
*
|
|
@@ -2,7 +2,7 @@ import { type Maybe } from '@dereekb/util';
|
|
|
2
2
|
import { type FirestoreModelType, type KnownOnCallFunctionType, type OidcModelScopeRequirement, type OidcScopeTerm } from '@dereekb/firebase';
|
|
3
3
|
import { type ModelApiDetailsResult, type OnCallModelFunctionApiDetails, type FirebaseServerAuthData, type McpToolDetailsBuilder, type McpVisibilityRule } from '@dereekb/firebase-server';
|
|
4
4
|
import { type Request } from 'express';
|
|
5
|
-
import {
|
|
5
|
+
import type { CallToolResult, ToolAnnotations } from '@modelcontextprotocol/server';
|
|
6
6
|
import { type McpManifestToolEntry } from './mcp.manifest';
|
|
7
7
|
import { type McpToolFilterMetadata } from './mcp.visibility';
|
|
8
8
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type Maybe } from '@dereekb/util';
|
|
2
2
|
import { type CallModelOidcScope, type OidcScopeTerm } from '@dereekb/firebase';
|
|
3
3
|
import { type McpToolVisibility, type McpVisibilityContext, type McpVisibilityRule } from '@dereekb/firebase-server';
|
|
4
|
-
import {
|
|
4
|
+
import type { ToolAnnotations } from '@modelcontextprotocol/server';
|
|
5
5
|
/**
|
|
6
6
|
* Normalized classification of a {@link McpToolVisibility} value computed at boot.
|
|
7
7
|
*
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { type Maybe, type PromiseOrValue } from '@dereekb/util';
|
|
2
|
+
import { type FirebaseAuthUserId, type FirestoreModelIdentity, type FirestoreModelKey, type FirestoreModelType } from '@dereekb/firebase';
|
|
3
|
+
import { type ModelAccessMultiRoleMapResult, type FirebaseServerAuthData } from '@dereekb/firebase-server';
|
|
4
|
+
import { type McpToolDefinition } from '../mcp.tool-generator';
|
|
5
|
+
/**
|
|
6
|
+
* Reserved tool name for the built-in `model-roles` static tool.
|
|
7
|
+
*/
|
|
8
|
+
export declare const MODEL_ROLES_TOOL_NAME = "model-roles";
|
|
9
|
+
/**
|
|
10
|
+
* Synthetic call type used in the tool's dispatch identity. Distinct from `model-get`'s `get` so
|
|
11
|
+
* visibility predicates can target the two independently.
|
|
12
|
+
*/
|
|
13
|
+
export declare const MODEL_ROLES_DISPATCH_CALL = "roles";
|
|
14
|
+
/**
|
|
15
|
+
* Synthetic model type used in the tool's dispatch identity. Mirrors {@link MODEL_GET_DISPATCH_MODEL_TYPE}
|
|
16
|
+
* — the tool isn't bound to one model type, so the literal "model" stands in.
|
|
17
|
+
*/
|
|
18
|
+
export declare const MODEL_ROLES_DISPATCH_MODEL_TYPE = "model";
|
|
19
|
+
/**
|
|
20
|
+
* Maximum number of keys accepted per `model-roles` call. Role resolution runs the model's real
|
|
21
|
+
* permission delegate per key (which may itself read parent documents), so this is deliberately
|
|
22
|
+
* lower than the `model-get` batch size.
|
|
23
|
+
*/
|
|
24
|
+
export declare const MCP_MODEL_ROLES_MAX_KEYS = 25;
|
|
25
|
+
/**
|
|
26
|
+
* Resolves granted role maps for a batch of keys. Signature matches
|
|
27
|
+
* `ModelApiGetService.readRoleMaps` so the service method can be passed directly.
|
|
28
|
+
*/
|
|
29
|
+
export type McpModelRolesReadRoleMaps = (params: {
|
|
30
|
+
readonly modelType: FirestoreModelType;
|
|
31
|
+
readonly keys: FirestoreModelKey[];
|
|
32
|
+
readonly auth: Maybe<FirebaseServerAuthData>;
|
|
33
|
+
readonly targetUid?: Maybe<FirebaseAuthUserId>;
|
|
34
|
+
}) => Promise<ModelAccessMultiRoleMapResult>;
|
|
35
|
+
/**
|
|
36
|
+
* Lookup that resolves a `modelType` to its registered {@link FirestoreModelIdentity}, so bare ids
|
|
37
|
+
* can be promoted to full keys. Mirrors `McpModelGetResolveIdentity`.
|
|
38
|
+
*/
|
|
39
|
+
export type McpModelRolesResolveIdentity = (modelType: FirestoreModelType, auth: Maybe<FirebaseServerAuthData>) => Maybe<FirestoreModelIdentity>;
|
|
40
|
+
/**
|
|
41
|
+
* Predicate authorizing a caller to resolve roles for a uid other than their own. Mirrors
|
|
42
|
+
* `McpModelRolesTargetUidPredicate` from the module config; redeclared here so this module does not
|
|
43
|
+
* depend on the config module.
|
|
44
|
+
*/
|
|
45
|
+
export type McpModelRolesTargetUidCheck = (auth: Maybe<FirebaseServerAuthData>) => PromiseOrValue<boolean>;
|
|
46
|
+
/**
|
|
47
|
+
* Constructor dependencies for {@link createModelRolesTool}.
|
|
48
|
+
*/
|
|
49
|
+
export interface CreateModelRolesToolDeps {
|
|
50
|
+
/**
|
|
51
|
+
* Resolves the granted role maps for a batch of keys.
|
|
52
|
+
*/
|
|
53
|
+
readonly readRoleMaps: McpModelRolesReadRoleMaps;
|
|
54
|
+
/**
|
|
55
|
+
* Resolves the registered identity for a model type so bare ids can be promoted into full keys.
|
|
56
|
+
*/
|
|
57
|
+
readonly resolveIdentity: McpModelRolesResolveIdentity;
|
|
58
|
+
/**
|
|
59
|
+
* Authorizes use of the `uid` parameter. Omitted means the parameter fails closed for everyone;
|
|
60
|
+
* the tool still answers for the calling user.
|
|
61
|
+
*/
|
|
62
|
+
readonly canTargetOtherUids?: Maybe<McpModelRolesTargetUidCheck>;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Shape of the `model-roles` tool input.
|
|
66
|
+
*/
|
|
67
|
+
export interface ModelRolesToolInput {
|
|
68
|
+
readonly modelType: string;
|
|
69
|
+
readonly keys: ReadonlyArray<string>;
|
|
70
|
+
readonly uid?: string;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Builds the built-in `model-roles` MCP tool definition.
|
|
74
|
+
*
|
|
75
|
+
* Answers "what is this user actually allowed to do with this document?" by running the same
|
|
76
|
+
* `roleMapForModel()` delegate the permission-checked read/write paths use, and returning the
|
|
77
|
+
* resolved roles instead of consuming them. Because the model's real delegate runs, derived and
|
|
78
|
+
* cascading roles appear exactly as the API grants them — there is no second implementation of the
|
|
79
|
+
* rules to drift.
|
|
80
|
+
*
|
|
81
|
+
* Two things it disambiguates that a plain read cannot:
|
|
82
|
+
* - **Missing vs. forbidden.** A key that resolves to a missing document returns
|
|
83
|
+
* `exists: false, roles: []` rather than an error, so "the document is not there" reads
|
|
84
|
+
* differently from "it is there and you have no access" (`exists: true, roles: []`).
|
|
85
|
+
* - **Full access.** Admin short-circuits that grant the full-access marker come back as
|
|
86
|
+
* `fullAccess: true` rather than as an opaque enumerated set.
|
|
87
|
+
*
|
|
88
|
+
* By default roles are resolved for the calling user. Passing `uid` resolves them for another user
|
|
89
|
+
* and is gated behind {@link CreateModelRolesToolDeps.canTargetOtherUids}.
|
|
90
|
+
*
|
|
91
|
+
* @param deps - Role-map reader, identity resolver, and the target-uid authorization predicate.
|
|
92
|
+
* @returns A statically-registered {@link McpToolDefinition} ready to be appended to the MCP
|
|
93
|
+
* server factory's tool registry.
|
|
94
|
+
*/
|
|
95
|
+
export declare function createModelRolesTool(deps: CreateModelRolesToolDeps): McpToolDefinition;
|
|
@@ -1,21 +1,26 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type McpServer } from '@modelcontextprotocol/server';
|
|
2
2
|
import { type Request, type Response } from 'express';
|
|
3
3
|
/**
|
|
4
|
-
* Handles a single Streamable HTTP JSON-RPC request
|
|
4
|
+
* Handles a single Streamable HTTP JSON-RPC request, building a fresh MCP server for it.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
6
|
+
* Both protocol eras are served, each by its own leg:
|
|
7
|
+
*
|
|
8
|
+
* - **2026-07-28 (modern)** — `createMcpHandler`, which is per-request and stateless by
|
|
9
|
+
* construction: there is no `Mcp-Session-Id` and no transport bookkeeping.
|
|
10
|
+
* - **2025-era (legacy)** — a `NodeStreamableHTTPServerTransport` in stateless mode, which is
|
|
11
|
+
* byte-for-byte what this endpoint served before the SDK v2 upgrade.
|
|
12
|
+
*
|
|
13
|
+
* The legacy leg is wired by hand (the SDK's documented `isLegacyRequest` composition) rather
|
|
14
|
+
* than left to `createMcpHandler`'s built-in `legacy: 'stateless'` fallback, because that
|
|
15
|
+
* fallback builds its transport with `sessionIdGenerator` alone and therefore answers in
|
|
16
|
+
* `text/event-stream`. See {@link handleLegacyMcpRequest} for why that framing is not viable
|
|
17
|
+
* here.
|
|
10
18
|
*
|
|
11
19
|
* The caller is expected to have already validated the bearer token (via the
|
|
12
20
|
* OIDC bearer middleware) before this function runs.
|
|
13
21
|
*
|
|
14
22
|
* @param req - The Express request carrying the JSON-RPC body.
|
|
15
23
|
* @param res - The Express response.
|
|
16
|
-
* @param
|
|
17
|
-
* @param server.connect - The MCP server's connect method that binds the transport before request handling.
|
|
24
|
+
* @param buildServer - Factory producing the MCP server that backs this request.
|
|
18
25
|
*/
|
|
19
|
-
export declare function handleStreamableHttpMcpRequest(req: Request, res: Response,
|
|
20
|
-
connect: (transport: StreamableHTTPServerTransport) => Promise<void>;
|
|
21
|
-
}): Promise<void>;
|
|
26
|
+
export declare function handleStreamableHttpMcpRequest(req: Request, res: Response, buildServer: () => McpServer): Promise<void>;
|
package/model/index.cjs.js
CHANGED
|
@@ -14770,7 +14770,7 @@ function enrichSignedUploadUrlInputSchema(defaultInputSchema, purposeKeys) {
|
|
|
14770
14770
|
if (defaultInputSchema != null && (typeof defaultInputSchema === "undefined" ? "undefined" : _type_of$3(defaultInputSchema)) === 'object') {
|
|
14771
14771
|
var _base_properties;
|
|
14772
14772
|
var base = defaultInputSchema;
|
|
14773
|
-
var purposeProperty = (_base_properties = base.properties) === null || _base_properties === void 0 ? void 0 : _base_properties
|
|
14773
|
+
var purposeProperty = (_base_properties = base.properties) === null || _base_properties === void 0 ? void 0 : _base_properties['purpose'];
|
|
14774
14774
|
if (purposeProperty != null && (typeof purposeProperty === "undefined" ? "undefined" : _type_of$3(purposeProperty)) === 'object') {
|
|
14775
14775
|
var quotedPurposes = purposeKeys.map(function(purpose) {
|
|
14776
14776
|
return '"'.concat(purpose, '"');
|
package/model/index.esm.js
CHANGED
|
@@ -14768,7 +14768,7 @@ function enrichSignedUploadUrlInputSchema(defaultInputSchema, purposeKeys) {
|
|
|
14768
14768
|
if (defaultInputSchema != null && (typeof defaultInputSchema === "undefined" ? "undefined" : _type_of$3(defaultInputSchema)) === 'object') {
|
|
14769
14769
|
var _base_properties;
|
|
14770
14770
|
var base = defaultInputSchema;
|
|
14771
|
-
var purposeProperty = (_base_properties = base.properties) === null || _base_properties === void 0 ? void 0 : _base_properties
|
|
14771
|
+
var purposeProperty = (_base_properties = base.properties) === null || _base_properties === void 0 ? void 0 : _base_properties['purpose'];
|
|
14772
14772
|
if (purposeProperty != null && (typeof purposeProperty === "undefined" ? "undefined" : _type_of$3(purposeProperty)) === 'object') {
|
|
14773
14773
|
var quotedPurposes = purposeKeys.map(function(purpose) {
|
|
14774
14774
|
return '"'.concat(purpose, '"');
|
package/model/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dereekb/firebase-server/model",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.38.0",
|
|
4
4
|
"peerDependencies": {
|
|
5
|
-
"@dereekb/analytics": "13.
|
|
6
|
-
"@dereekb/date": "13.
|
|
7
|
-
"@dereekb/firebase": "13.
|
|
8
|
-
"@dereekb/firebase-server": "13.
|
|
9
|
-
"@dereekb/model": "13.
|
|
10
|
-
"@dereekb/nestjs": "13.
|
|
11
|
-
"@dereekb/rxjs": "13.
|
|
12
|
-
"@dereekb/util": "13.
|
|
5
|
+
"@dereekb/analytics": "13.38.0",
|
|
6
|
+
"@dereekb/date": "13.38.0",
|
|
7
|
+
"@dereekb/firebase": "13.38.0",
|
|
8
|
+
"@dereekb/firebase-server": "13.38.0",
|
|
9
|
+
"@dereekb/model": "13.38.0",
|
|
10
|
+
"@dereekb/nestjs": "13.38.0",
|
|
11
|
+
"@dereekb/rxjs": "13.38.0",
|
|
12
|
+
"@dereekb/util": "13.38.0",
|
|
13
13
|
"@nestjs/common": "^11.1.19",
|
|
14
14
|
"@nestjs/config": "^4.0.4",
|
|
15
15
|
"archiver": "^7.0.1",
|