@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.
Files changed (45) hide show
  1. package/calcom/package.json +10 -10
  2. package/discord/package.json +10 -10
  3. package/index.cjs.js +1732 -510
  4. package/index.esm.js +1717 -513
  5. package/mailgun/package.json +9 -9
  6. package/mcp/index.cjs.js +913 -167
  7. package/mcp/index.esm.js +906 -166
  8. package/mcp/package.json +13 -12
  9. package/mcp/src/lib/controller/mcp.controller.d.ts +28 -3
  10. package/mcp/src/lib/controller/mcp.wellknown.controller.d.ts +21 -1
  11. package/mcp/src/lib/mcp.config.d.ts +31 -8
  12. package/mcp/src/lib/mcp.module.d.ts +2 -1
  13. package/mcp/src/lib/service/index.d.ts +1 -0
  14. package/mcp/src/lib/service/mcp.server.factory.d.ts +8 -6
  15. package/mcp/src/lib/service/mcp.tool-generator.d.ts +1 -1
  16. package/mcp/src/lib/service/mcp.visibility.d.ts +1 -1
  17. package/mcp/src/lib/service/tools/mcp.tool.model-roles.d.ts +95 -0
  18. package/mcp/src/lib/transport/streamable-http.transport.d.ts +16 -11
  19. package/model/index.cjs.js +1 -1
  20. package/model/index.esm.js +1 -1
  21. package/model/package.json +9 -9
  22. package/oidc/index.cjs.js +257 -67
  23. package/oidc/index.esm.js +256 -69
  24. package/oidc/package.json +10 -10
  25. package/oidc/src/lib/profile.d.ts +21 -0
  26. package/oidc/src/lib/service/index.d.ts +1 -0
  27. package/oidc/src/lib/service/oidc.config.service.d.ts +14 -0
  28. package/oidc/src/lib/service/oidc.interaction-policy.d.ts +35 -0
  29. package/oidc/src/lib/service/oidc.service.d.ts +29 -0
  30. package/package.json +14 -13
  31. package/src/lib/env/env.service.d.ts +27 -3
  32. package/src/lib/nest/controller/api.scope.d.ts +63 -0
  33. package/src/lib/nest/controller/index.d.ts +2 -0
  34. package/src/lib/nest/controller/model/model.api.get.service.d.ts +146 -1
  35. package/src/lib/nest/controller/model/model.api.scope.d.ts +3 -2
  36. package/src/lib/nest/controller/session/index.d.ts +4 -0
  37. package/src/lib/nest/controller/session/session.api.config.d.ts +104 -0
  38. package/src/lib/nest/controller/session/session.api.controller.d.ts +26 -0
  39. package/src/lib/nest/controller/session/session.api.module.d.ts +49 -0
  40. package/src/lib/nest/controller/session/session.api.service.d.ts +71 -0
  41. package/test/index.cjs.js +8 -8
  42. package/test/index.esm.js +8 -8
  43. package/test/package.json +11 -11
  44. package/twilio/package.json +8 -8
  45. 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.36.0",
3
+ "version": "13.38.0",
4
4
  "peerDependencies": {
5
- "@dereekb/analytics": "13.36.0",
6
- "@dereekb/date": "13.36.0",
7
- "@dereekb/firebase": "13.36.0",
8
- "@dereekb/firebase-server": "13.36.0",
9
- "@dereekb/firebase-server/oidc": "13.36.0",
10
- "@dereekb/model": "13.36.0",
11
- "@dereekb/nestjs": "13.36.0",
12
- "@dereekb/rxjs": "13.36.0",
13
- "@dereekb/util": "13.36.0",
14
- "@dereekb/zoho": "13.36.0",
15
- "@modelcontextprotocol/sdk": "1.29.0",
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 transport + MCP server pair (stateless mode), which is
11
- * adequate for Claude custom-connector style usage. A session-tracked variant can
12
- * be layered on later if streaming tool output becomes a requirement.
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. Omitted when {@link McpModuleConfig.scopesSupported} is unset.
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.scopesSupported`) and returns the subset to
87
- * advertise. Supplied via {@link McpModuleConfig.scopesSupported}; when unset, all are advertised.
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.scopesSupported} (i.e.
131
- * `Object.keys(providerConfig.claims)`), so the MCP resource advertises exactly
132
- * the scopes the issuer supports without the app restating them. Provide this
133
- * only to narrow that set — it receives every scope the provider issues and
134
- * returns the subset to advertise. When unset, all of them are advertised.
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 `scopesSupported` to advertise them on the protected-resource document.
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/sdk/server/mcp.js';
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 — `@modelcontextprotocol/sdk`
21
- * recommends a fresh `McpServer` + transport pair per stateless JSON-RPC request,
22
- * which sidesteps session bookkeeping for the common Claude-connector case.
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 { type CallToolResult, type ToolAnnotations } from '@modelcontextprotocol/sdk/types.js';
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 { type ToolAnnotations } from '@modelcontextprotocol/sdk/types.js';
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 { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
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 against the supplied MCP server.
4
+ * Handles a single Streamable HTTP JSON-RPC request, building a fresh MCP server for it.
5
5
  *
6
- * Each call creates a fresh `StreamableHTTPServerTransport` in stateless mode — the
7
- * transport binds the server, processes one request, and is discarded. Stateless
8
- * mode is adequate for Claude custom-connector usage and avoids the bookkeeping
9
- * cost of session-tracked transports for the common case.
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 server - The MCP server instance to back the transport.
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, server: {
20
- connect: (transport: StreamableHTTPServerTransport) => Promise<void>;
21
- }): Promise<void>;
26
+ export declare function handleStreamableHttpMcpRequest(req: Request, res: Response, buildServer: () => McpServer): Promise<void>;
@@ -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.purpose;
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, '"');
@@ -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.purpose;
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, '"');
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@dereekb/firebase-server/model",
3
- "version": "13.36.0",
3
+ "version": "13.38.0",
4
4
  "peerDependencies": {
5
- "@dereekb/analytics": "13.36.0",
6
- "@dereekb/date": "13.36.0",
7
- "@dereekb/firebase": "13.36.0",
8
- "@dereekb/firebase-server": "13.36.0",
9
- "@dereekb/model": "13.36.0",
10
- "@dereekb/nestjs": "13.36.0",
11
- "@dereekb/rxjs": "13.36.0",
12
- "@dereekb/util": "13.36.0",
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",