@dereekb/firebase-server 13.11.18 → 13.12.1

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 (67) hide show
  1. package/index.cjs.js +218 -31
  2. package/index.esm.js +217 -34
  3. package/mailgun/package.json +9 -9
  4. package/mcp/index.cjs.default.js +1 -0
  5. package/mcp/index.cjs.js +3646 -0
  6. package/mcp/index.cjs.mjs +2 -0
  7. package/mcp/index.d.ts +1 -0
  8. package/mcp/index.esm.js +3609 -0
  9. package/mcp/package.json +33 -0
  10. package/mcp/src/index.d.ts +1 -0
  11. package/mcp/src/lib/controller/index.d.ts +2 -0
  12. package/mcp/src/lib/controller/mcp.controller.d.ts +19 -0
  13. package/mcp/src/lib/controller/mcp.wellknown.controller.d.ts +25 -0
  14. package/mcp/src/lib/index.d.ts +5 -0
  15. package/mcp/src/lib/mcp.config.d.ts +100 -0
  16. package/mcp/src/lib/mcp.module.d.ts +49 -0
  17. package/mcp/src/lib/service/index.d.ts +8 -0
  18. package/mcp/src/lib/service/mcp.manifest.d.ts +150 -0
  19. package/mcp/src/lib/service/mcp.response-formatter.d.ts +36 -0
  20. package/mcp/src/lib/service/mcp.server.factory.d.ts +124 -0
  21. package/mcp/src/lib/service/mcp.tool-generator.d.ts +235 -0
  22. package/mcp/src/lib/service/mcp.visibility.d.ts +99 -0
  23. package/mcp/src/lib/service/tools/mcp.tool.model-decode.d.ts +87 -0
  24. package/mcp/src/lib/service/tools/mcp.tool.model-get.d.ts +81 -0
  25. package/mcp/src/lib/service/tools/mcp.tool.model-info.d.ts +84 -0
  26. package/mcp/src/lib/service/tools/mcp.tool.whoami.d.ts +72 -0
  27. package/mcp/src/lib/transport/index.d.ts +1 -0
  28. package/mcp/src/lib/transport/streamable-http.transport.d.ts +21 -0
  29. package/model/index.cjs.js +639 -266
  30. package/model/index.esm.js +640 -270
  31. package/model/package.json +9 -9
  32. package/model/src/lib/storagefile/extension/compress.pdf.d.ts +29 -1
  33. package/model/src/lib/storagefile/index.d.ts +1 -0
  34. package/model/src/lib/storagefile/storagefile.action.server.d.ts +29 -2
  35. package/model/src/lib/storagefile/storagefile.mcp.d.ts +32 -0
  36. package/model/src/lib/storagefile/storagefile.module.d.ts +20 -3
  37. package/oidc/index.cjs.js +322 -147
  38. package/oidc/index.esm.js +321 -148
  39. package/oidc/package.json +10 -10
  40. package/oidc/src/lib/controller/oidc.wellknown.controller.d.ts +6 -15
  41. package/oidc/src/lib/middleware/oauth-auth.middleware.d.ts +22 -2
  42. package/oidc/src/lib/middleware/oauth-auth.module.d.ts +57 -0
  43. package/oidc/src/lib/oidc.config.d.ts +73 -0
  44. package/oidc/src/lib/oidc.module.d.ts +44 -2
  45. package/package.json +22 -10
  46. package/src/lib/auth/auth.service.d.ts +16 -1
  47. package/src/lib/auth/auth.service.error.util.d.ts +24 -5
  48. package/src/lib/env/env.config.d.ts +16 -0
  49. package/src/lib/env/env.service.d.ts +9 -0
  50. package/src/lib/nest/controller/model/model.api.get.service.d.ts +35 -1
  51. package/src/lib/nest/env/env.service.d.ts +1 -0
  52. package/src/lib/nest/model/api.details.d.ts +116 -5
  53. package/src/lib/nest/model/crud.assert.function.d.ts +1 -1
  54. package/src/lib/nest/model/index.d.ts +1 -0
  55. package/src/lib/nest/model/invoke.model.function.d.ts +89 -0
  56. package/test/package.json +11 -11
  57. package/twilio/LICENSE +21 -0
  58. package/twilio/index.cjs.default.js +1 -0
  59. package/twilio/index.cjs.js +404 -0
  60. package/twilio/index.cjs.mjs +2 -0
  61. package/twilio/index.d.ts +1 -0
  62. package/twilio/index.esm.js +398 -0
  63. package/twilio/package.json +25 -0
  64. package/twilio/src/index.d.ts +1 -0
  65. package/twilio/src/lib/index.d.ts +1 -0
  66. package/twilio/src/lib/notification.send.service.twilio.d.ts +148 -0
  67. package/zoho/package.json +9 -9
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@dereekb/firebase-server/mcp",
3
+ "version": "13.12.1",
4
+ "peerDependencies": {
5
+ "@dereekb/analytics": "13.12.1",
6
+ "@dereekb/date": "13.12.1",
7
+ "@dereekb/firebase": "13.12.1",
8
+ "@dereekb/firebase-server": "13.12.1",
9
+ "@dereekb/firebase-server/oidc": "13.12.1",
10
+ "@dereekb/model": "13.12.1",
11
+ "@dereekb/nestjs": "13.12.1",
12
+ "@dereekb/rxjs": "13.12.1",
13
+ "@dereekb/util": "13.12.1",
14
+ "@dereekb/zoho": "13.12.1",
15
+ "@modelcontextprotocol/sdk": "1.29.0",
16
+ "@nestjs/common": "^11.1.19",
17
+ "@nestjs/core": "^11.1.19",
18
+ "express": "^5.2.1",
19
+ "firebase-admin": "^13.8.0"
20
+ },
21
+ "exports": {
22
+ "./package.json": "./package.json",
23
+ ".": {
24
+ "module": "./index.esm.js",
25
+ "types": "./index.d.ts",
26
+ "import": "./index.cjs.mjs",
27
+ "default": "./index.cjs.js"
28
+ }
29
+ },
30
+ "module": "./index.esm.js",
31
+ "main": "./index.cjs.js",
32
+ "types": "./index.d.ts"
33
+ }
@@ -0,0 +1 @@
1
+ export * from './lib';
@@ -0,0 +1,2 @@
1
+ export * from './mcp.controller';
2
+ export * from './mcp.wellknown.controller';
@@ -0,0 +1,19 @@
1
+ import { type Request, type Response } from 'express';
2
+ import { McpServerFactoryService } from '../service/mcp.server.factory';
3
+ /**
4
+ * NestJS controller mounting the MCP Streamable HTTP transport at `POST /mcp`.
5
+ *
6
+ * Auth is enforced by the global OIDC bearer middleware (`OidcAuthBearerTokenMiddleware`)
7
+ * which must include `'/mcp'` in its `protectedPaths`. By the time the request reaches
8
+ * this controller, `req.auth` is populated with the authenticated user's data.
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.
13
+ */
14
+ export declare class McpController {
15
+ private readonly factory;
16
+ private readonly _logger;
17
+ constructor(factory: McpServerFactoryService);
18
+ handleMcpRequest(req: Request, res: Response): Promise<void>;
19
+ }
@@ -0,0 +1,25 @@
1
+ import { McpModuleConfig } from '../mcp.config';
2
+ /**
3
+ * Discovery document body for the OAuth protected-resource indicator.
4
+ *
5
+ * Format defined by RFC 9728 (OAuth 2.0 Protected Resource Metadata).
6
+ */
7
+ export interface OAuthProtectedResourceMetadata {
8
+ readonly resource: string;
9
+ readonly authorization_servers: ReadonlyArray<string>;
10
+ }
11
+ /**
12
+ * Serves the `GET /.well-known/oauth-protected-resource` metadata document so
13
+ * Claude (and other MCP clients) can discover which OIDC issuer guards this
14
+ * MCP endpoint.
15
+ *
16
+ * The route is registered without a controller-level prefix because well-known
17
+ * URIs must live at the host root. Apps need to exclude `.well-known/{*path}`
18
+ * from any global API route prefix (see `FIREBASE_SERVER_OIDC_ROUTES_FOR_GLOBAL_ROUTE_EXCLUDE`
19
+ * in `firebase-server/oidc` for the corresponding OIDC excludes).
20
+ */
21
+ export declare class McpWellKnownController {
22
+ private readonly mcpConfig;
23
+ constructor(mcpConfig: McpModuleConfig);
24
+ getProtectedResourceMetadata(): OAuthProtectedResourceMetadata;
25
+ }
@@ -0,0 +1,5 @@
1
+ export * from './mcp.config';
2
+ export * from './mcp.module';
3
+ export * from './controller';
4
+ export * from './service';
5
+ export * from './transport';
@@ -0,0 +1,100 @@
1
+ import { type AuthClaims, type AuthRoleSet } from '@dereekb/util';
2
+ /**
3
+ * Default path the MCP Streamable HTTP transport is mounted at.
4
+ *
5
+ * Combined with the app origin to form the canonical MCP resource URL
6
+ * (e.g., `https://api.example.com/mcp`) advertised in the protected-resource
7
+ * discovery document at `/.well-known/oauth-protected-resource`.
8
+ */
9
+ export declare const DEFAULT_MCP_PATH = "/mcp";
10
+ /**
11
+ * Default name advertised by the MCP server on the JSON-RPC `initialize` handshake.
12
+ *
13
+ * Apps may override this via {@link McpModuleConfig.serverName} to identify their
14
+ * deployment (e.g., `demo-api-mcp`, `hellosubs-api-mcp`).
15
+ */
16
+ export declare const DEFAULT_MCP_SERVER_NAME = "dbx-firebase-server-mcp";
17
+ /**
18
+ * Configuration for the firebase-server/mcp module.
19
+ *
20
+ * Apps construct this in their `*McpModule` provider and pass it through
21
+ * {@link mcpModuleMetadata}. It carries:
22
+ *
23
+ * - The canonical issuer URL for the OIDC provider that gates this MCP endpoint
24
+ * (used in the protected-resource discovery document).
25
+ * - The canonical resource URL where the MCP transport is mounted (also part of
26
+ * the discovery document; doubles as the `resource` claim consumers should
27
+ * verify their access tokens against).
28
+ * - Optional server identity (name, version) advertised on the MCP `initialize` handshake.
29
+ *
30
+ * The OAuth bearer middleware that authenticates `/mcp` is configured on
31
+ * {@link OidcModuleConfig.protectedPaths} — that's outside this config's scope.
32
+ */
33
+ export declare abstract class McpModuleConfig {
34
+ /**
35
+ * The canonical issuer URL of the OIDC provider that gates this MCP endpoint.
36
+ *
37
+ * Surfaced in the `authorization_servers` field of the protected-resource
38
+ * discovery document. Claude custom-connector reads this to discover the OIDC issuer.
39
+ *
40
+ * @example 'https://api.example.com/oidc'
41
+ */
42
+ readonly oidcIssuer: string;
43
+ /**
44
+ * The canonical resource URL of the MCP endpoint.
45
+ *
46
+ * Surfaced as the `resource` field of the protected-resource discovery document,
47
+ * and used as the audience the access token's `aud` claim should match.
48
+ *
49
+ * @example 'https://api.example.com/mcp'
50
+ */
51
+ readonly mcpUrl: string;
52
+ /**
53
+ * Optional name advertised on the MCP `initialize` handshake. Defaults to
54
+ * {@link DEFAULT_MCP_SERVER_NAME}.
55
+ */
56
+ readonly serverName?: string;
57
+ /**
58
+ * Optional version advertised on the MCP `initialize` handshake.
59
+ */
60
+ readonly serverVersion?: string;
61
+ /**
62
+ * Absolute path to a pre-rendered MCP manifest JSON file produced by
63
+ * `dbx-cli-generate-mcp-manifest`. When set, the runtime reads it once at
64
+ * boot and uses each tool's `description`, `inputSchema`, and `outputSchema`
65
+ * during tool generation — no per-request file I/O.
66
+ *
67
+ * Optional. When unset or the file is missing, the runtime falls back to
68
+ * today's behavior (auto-generated descriptions, ArkType-derived input
69
+ * schemas, no output schemas) and emits a single boot warning.
70
+ */
71
+ readonly mcpManifestPath?: string;
72
+ /**
73
+ * When `true`, the MCP server only advertises tools whose effective read-only classification is `true`.
74
+ *
75
+ * Write tools (`create`/`update`/`delete`) and tools with unknown classification (e.g., `invoke`
76
+ * with no explicit `mcp.readOnly` override) are dropped from `tools/list` — fail-safe under the
77
+ * principle that anything not provably read-only is treated as a write.
78
+ *
79
+ * The advertised `serverName` on the JSON-RPC `initialize` handshake is suffixed with
80
+ * ` (read-only)` so the client surface reflects the mode.
81
+ */
82
+ readonly readOnly?: boolean;
83
+ }
84
+ /**
85
+ * Signature for the optional role reader the MCP module uses when evaluating
86
+ * declarative {@link McpVisibilityRule.requiredRoles} on `tools/list`.
87
+ *
88
+ * The MCP factory does not have access to the constructed `FirebaseServerAuthContext`
89
+ * (that's built later by the dispatch chain), so apps wire a thin function that
90
+ * maps the caller's Firebase custom claims to the corresponding role set —
91
+ * typically `authRoleClaimsService(...).toRoles` from `@dereekb/util`.
92
+ *
93
+ * When no reader is provided, declarative role checks fail closed (treated as
94
+ * "missing role"), and the factory emits a single boot-time warning.
95
+ */
96
+ export type McpAuthRoleReader = (claims: AuthClaims) => AuthRoleSet;
97
+ /**
98
+ * NestJS injection token for the optional {@link McpAuthRoleReader} provider.
99
+ */
100
+ export declare const MCP_AUTH_ROLE_READER = "MCP_AUTH_ROLE_READER";
@@ -0,0 +1,49 @@
1
+ import { type ModuleMetadata } from '@nestjs/common';
2
+ import { type ClassType } from '@dereekb/util';
3
+ /**
4
+ * Routes the firebase-server/mcp module owns. Apps should exclude these from
5
+ * any global API route prefix (`globalApiRoutePrefix.exclude`) so the canonical
6
+ * URLs land at `/.well-known/...` and `/mcp` rather than `/api/.well-known/...`.
7
+ */
8
+ export declare const FIREBASE_SERVER_MCP_ROUTES_FOR_GLOBAL_ROUTE_EXCLUDE: string[];
9
+ /**
10
+ * Configuration for {@link mcpModuleMetadata}.
11
+ */
12
+ export interface McpModuleMetadataConfig extends Pick<ModuleMetadata, 'imports' | 'exports' | 'providers'> {
13
+ /**
14
+ * Module that exports the required dependencies.
15
+ *
16
+ * Must provide:
17
+ * - {@link ModelApiDispatchConfig} — so the MCP server can reuse the call model dispatch chain.
18
+ * - {@link McpModuleConfig} — issuer + resource URLs for protected-resource discovery.
19
+ *
20
+ * In practice, downstream apps typically import their own `*ModelApiModule` first (which provides
21
+ * `ModelApiCallModelDispatchService` + `MODEL_API_NEST_APPLICATION_CONTEXT`) and add the
22
+ * `McpModuleConfig` provider in the dependency module passed here.
23
+ */
24
+ readonly dependencyModule: ClassType;
25
+ }
26
+ /**
27
+ * Generates NestJS module metadata for the firebase-server/mcp module.
28
+ *
29
+ * Mirrors the convention used by {@link modelApiModuleMetadata}: the consumer provides
30
+ * a dependency module that exposes the required tokens, and this factory wires up the
31
+ * controllers + factory service.
32
+ *
33
+ * @param metadataConfig - Configuration including the dependency module.
34
+ * @returns NestJS module metadata exposing the MCP transport + well-known controller.
35
+ *
36
+ * @Module ({
37
+ * imports: [DemoModelApiModule],
38
+ * providers: [{ provide: McpModuleConfig, useValue: { oidcIssuer, mcpUrl } }],
39
+ * exports: [McpModuleConfig, ModelApiCallModelDispatchService, MODEL_API_NEST_APPLICATION_CONTEXT]
40
+ * })
41
+ * export class DemoMcpDependencyModule {}
42
+ * @Module (mcpModuleMetadata({ dependencyModule: DemoMcpDependencyModule }))
43
+ * export class DemoMcpModule {}
44
+ * ```
45
+ *
46
+ * @example
47
+ * ```typescript
48
+ */
49
+ export declare function mcpModuleMetadata(metadataConfig: McpModuleMetadataConfig): ModuleMetadata;
@@ -0,0 +1,8 @@
1
+ export * from './mcp.manifest';
2
+ export * from './mcp.response-formatter';
3
+ export * from './mcp.server.factory';
4
+ export * from './mcp.tool-generator';
5
+ export * from './mcp.visibility';
6
+ export * from './tools/mcp.tool.model-get';
7
+ export * from './tools/mcp.tool.model-info';
8
+ export * from './tools/mcp.tool.model-decode';
@@ -0,0 +1,150 @@
1
+ import { type Maybe } from '@dereekb/util';
2
+ /**
3
+ * Version stamp embedded in the build-time MCP manifest JSON. Runtime loaders
4
+ * refuse manifests whose `version` does not match this constant.
5
+ *
6
+ * Mirrors {@link MCP_MANIFEST_VERSION} in `@dereekb/dbx-cli` — both packages
7
+ * version-bump together when the manifest shape changes.
8
+ */
9
+ export declare const MCP_MANIFEST_VERSION: 1;
10
+ /**
11
+ * One tool entry inside the pre-rendered MCP manifest JSON.
12
+ *
13
+ * Structural mirror of `@dereekb/dbx-cli`'s `McpManifestToolEntry` so the
14
+ * firebase-server/mcp runtime can consume the build output without taking a
15
+ * runtime dependency on the build-time CLI package.
16
+ */
17
+ export interface McpManifestToolEntry {
18
+ readonly description?: string;
19
+ readonly inputSchema?: object;
20
+ readonly outputSchema?: object;
21
+ }
22
+ /**
23
+ * One persisted field on a {@link McpManifestModelEntry}.
24
+ *
25
+ * Structural mirror of `@dereekb/dbx-cli`'s `CliModelField` minus the
26
+ * converter-text fields (CLI-only). The runtime keeps the payload narrow so the
27
+ * built-in `model-info` / `model-decode` tools can answer catalog queries without
28
+ * round-tripping back through the source packages.
29
+ */
30
+ export interface McpManifestModelField {
31
+ readonly name: string;
32
+ readonly longName: string;
33
+ readonly tsType?: string;
34
+ readonly optional: boolean;
35
+ readonly description?: string;
36
+ readonly enumRef?: string;
37
+ readonly syncFlag?: string;
38
+ readonly nestedFields?: readonly McpManifestModelField[];
39
+ readonly nestedIsArray?: boolean;
40
+ }
41
+ /**
42
+ * One Firestore model entry in the pre-rendered MCP manifest JSON.
43
+ *
44
+ * Structural mirror of `@dereekb/dbx-cli`'s `CliModelManifestEntry`. Drives the
45
+ * built-in `model-info` and `model-decode` MCP tools.
46
+ */
47
+ export interface McpManifestModelEntry {
48
+ readonly modelType: string;
49
+ readonly modelName: string;
50
+ readonly modelGroup?: string;
51
+ readonly identityConst: string;
52
+ readonly collectionPrefix: string;
53
+ readonly parentIdentityConst?: string;
54
+ readonly description?: string;
55
+ readonly sourcePackage: string;
56
+ readonly sourceFile: string;
57
+ readonly fields: readonly McpManifestModelField[];
58
+ /**
59
+ * Read posture declared by `@dbxModelRead <level>` on the model interface (`system` /
60
+ * `owner` / `admin-only` / `permissions`). Absent when the source model omits the tag.
61
+ */
62
+ readonly read?: 'system' | 'owner' | 'admin-only' | 'permissions';
63
+ /**
64
+ * Resolved `@dbxModelServiceFactory`-tagged export that implements this model, joined onto
65
+ * the model entry by `modelType`. Absent when no factory was found in the same scan.
66
+ */
67
+ readonly serviceFactory?: {
68
+ readonly exportName: string;
69
+ readonly sourceFile: string;
70
+ };
71
+ }
72
+ /**
73
+ * One auth claim entry in the pre-rendered MCP manifest JSON.
74
+ *
75
+ * Source paths and line numbers are deliberately stripped — the `whoami`
76
+ * runtime tool only needs the claim key, the interface it belongs to, the
77
+ * roles it grants, and the human-readable description.
78
+ */
79
+ export interface McpManifestAuthClaim {
80
+ readonly key: string;
81
+ readonly description: string;
82
+ readonly type: string;
83
+ readonly app?: string;
84
+ readonly interfaceName?: string;
85
+ readonly source: 'system' | 'app';
86
+ readonly mapping: {
87
+ readonly roles: readonly string[];
88
+ readonly inverse: boolean;
89
+ readonly inverseMode?: 'any' | 'all';
90
+ readonly claimValue?: string | number | boolean;
91
+ readonly customEncodeDecode: boolean;
92
+ };
93
+ readonly tags: readonly string[];
94
+ }
95
+ /**
96
+ * One auth app entry in the pre-rendered MCP manifest JSON.
97
+ *
98
+ * `auth.app` denotes the manifest's primary app (the host that emitted the
99
+ * manifest). `auth.apps` carries the full list, which may include the primary
100
+ * plus inherited apps (e.g. `storageFile-upload-user`).
101
+ */
102
+ export interface McpManifestAuthApp {
103
+ readonly app: string;
104
+ readonly claimsInterfaceName: string;
105
+ readonly serviceConstName: string;
106
+ readonly claimKeys: readonly string[];
107
+ readonly scopes: readonly string[];
108
+ readonly description?: string;
109
+ }
110
+ /**
111
+ * Auth section of the pre-rendered MCP manifest JSON. Drives the built-in
112
+ * `whoami` static tool. Optional — runtimes that pre-date this section skip
113
+ * registering whoami.
114
+ */
115
+ export interface McpManifestAuth {
116
+ readonly app?: McpManifestAuthApp;
117
+ readonly apps: readonly McpManifestAuthApp[];
118
+ readonly claims: readonly McpManifestAuthClaim[];
119
+ }
120
+ /**
121
+ * Full MCP manifest JSON shape consumed at boot.
122
+ *
123
+ * The optional `models` array carries the Firestore model catalog used by the
124
+ * built-in `model-info` / `model-decode` static tools. When absent (e.g., legacy
125
+ * manifests rendered before model catalog support landed), the runtime skips
126
+ * registering those tools instead of failing the boot.
127
+ *
128
+ * The optional `auth` section drives the built-in `whoami` static tool.
129
+ */
130
+ export interface McpManifest {
131
+ readonly version: typeof MCP_MANIFEST_VERSION;
132
+ readonly generatedAt: string;
133
+ readonly tools: {
134
+ readonly [key: string]: McpManifestToolEntry | undefined;
135
+ };
136
+ readonly models?: readonly McpManifestModelEntry[];
137
+ readonly auth?: McpManifestAuth;
138
+ }
139
+ /**
140
+ * Builds the canonical MCP manifest key for a (modelType, callType, specifier) triple.
141
+ *
142
+ * Default-specifier entries collapse to `_`. Must stay in sync with the build-time
143
+ * helper of the same name in `@dereekb/dbx-cli`.
144
+ *
145
+ * @param modelType - The Firestore model type segment of the key.
146
+ * @param call - The call type segment of the key.
147
+ * @param specifier - The specifier segment, or `_` / undefined for the default entry.
148
+ * @returns The canonical `modelType.call.specifier` manifest key.
149
+ */
150
+ export declare function mcpManifestKey(modelType: string, call: string, specifier?: Maybe<string>): string;
@@ -0,0 +1,36 @@
1
+ import { type OnCallTypedModelParams } from '@dereekb/firebase';
2
+ import { type McpToolResponseContent, type OnCallModelFunctionApiDetails } from '@dereekb/firebase-server';
3
+ /**
4
+ * Default structured value emitted by MCP when a handler returns `undefined`.
5
+ *
6
+ * Keeps the response usable by clients that key off either the text content or the
7
+ * structured/object body. Tests and any future overrides should compare against this
8
+ * constant rather than re-literalling `{ ok: true }`.
9
+ */
10
+ export declare const DEFAULT_VOID_MCP_SUCCESS_VALUE: {
11
+ readonly ok: true;
12
+ };
13
+ /**
14
+ * Resolves a dispatch result + handler API details into the MCP `CallToolResult` shape.
15
+ *
16
+ * Three-tier resolution as documented on {@link OnCallModelFunctionApiDetails.mcp}:
17
+ *
18
+ * - **Tier 3** — when `mcp.formatResponse` is set, its return value is used verbatim.
19
+ * - **Tier 2** — when `mcp.summarizeResponse` is set, the summary string is wrapped into a
20
+ * single text content block with the raw `result` exposed as `structuredContent`.
21
+ * - **Tier 1** — default: JSON-stringify `result` as a single text content block, also
22
+ * exposing the raw value as `structuredContent`.
23
+ *
24
+ * @param result - The handler's return value.
25
+ * @param params - The {@link OnCallTypedModelParams} that were dispatched.
26
+ * @param details - The handler-level API details (carries Tier 2/3 formatters).
27
+ * @returns The MCP tool response content.
28
+ */
29
+ export declare function formatMcpToolResponse(result: unknown, params: OnCallTypedModelParams, details: OnCallModelFunctionApiDetails | undefined): McpToolResponseContent;
30
+ /**
31
+ * Converts an error thrown from the dispatch chain into the MCP error response shape.
32
+ *
33
+ * @param error - The thrown error.
34
+ * @returns An MCP tool response with `isError: true` and the error message as a text block.
35
+ */
36
+ export declare function formatMcpToolErrorResponse(error: unknown): McpToolResponseContent;
@@ -0,0 +1,124 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { type Request } from 'express';
3
+ import { ModelApiCallModelDispatchService, ModelApiGetService, type FirebaseServerAuthData } from '@dereekb/firebase-server';
4
+ import { McpModuleConfig, type McpAuthRoleReader } from '../mcp.config';
5
+ /**
6
+ * Optional per-request context passed when invoking the MCP server through a
7
+ * Streamable HTTP transport. Carries the authenticated user (already extracted
8
+ * by the OIDC bearer middleware) and the raw Express request so the dispatch
9
+ * chain can satisfy `nestApplication`, `auth`, and `rawRequest` consumers.
10
+ */
11
+ export interface McpRequestContext {
12
+ readonly auth?: FirebaseServerAuthData;
13
+ readonly rawRequest: Request;
14
+ }
15
+ /**
16
+ * Injectable factory that builds {@link McpServer} instances pre-wired to the
17
+ * call model dispatch chain.
18
+ *
19
+ * The factory is invoked per Streamable HTTP request — `@modelcontextprotocol/sdk`
20
+ * recommends a fresh `McpServer` + transport pair per stateless JSON-RPC request,
21
+ * which sidesteps session bookkeeping for the common Claude-connector case.
22
+ */
23
+ export declare class McpServerFactoryService {
24
+ private readonly mcpConfig;
25
+ private readonly dispatchService;
26
+ private readonly modelApiGetService?;
27
+ private readonly roleReader?;
28
+ private readonly _logger;
29
+ private _cachedTools;
30
+ private _cachedStaticTools;
31
+ private _cachedManifest;
32
+ private _cachedManifestModels;
33
+ private _cachedManifestAuth;
34
+ private _manifestLoaded;
35
+ private _loggedSkips;
36
+ private _warnedMissingRoleReader;
37
+ constructor(mcpConfig: McpModuleConfig, dispatchService: ModelApiCallModelDispatchService, modelApiGetService?: ModelApiGetService | undefined, roleReader?: McpAuthRoleReader | undefined);
38
+ /**
39
+ * Builds a configured MCP server with tool listing + dispatch handlers wired up.
40
+ *
41
+ * @param ctx - The per-request context (auth, raw request) used when forwarding tool calls.
42
+ * @returns A configured MCP server ready to be `connect()`-ed to a transport.
43
+ */
44
+ createServer(ctx: McpRequestContext): McpServer;
45
+ /**
46
+ * Reads cached tool definitions, regenerating + logging skips on first call.
47
+ *
48
+ * The underlying call model `_apiDetails` is built at boot and doesn't change at runtime,
49
+ * so the generation result is safe to cache for the lifetime of the process.
50
+ *
51
+ * @returns The cached or freshly-generated tool generation result.
52
+ */
53
+ private _resolveToolDefinitions;
54
+ /**
55
+ * Builds the list of statically-registered (non-callModel) MCP tools.
56
+ *
57
+ * Includes `model-get` whenever {@link ModelApiGetService} is available, plus
58
+ * `model-info` and `model-decode` whenever the boot-time MCP manifest provided
59
+ * a non-empty `models` catalog. The list is cached for the lifetime of the
60
+ * process since static tools share the same boot-time inputs as the
61
+ * auto-generated ones.
62
+ *
63
+ * @returns The cached array of static tool definitions, filtered for collisions with generated tools.
64
+ */
65
+ private _resolveStaticTools;
66
+ /**
67
+ * Reads the pre-rendered MCP manifest JSON once, validates its version, and caches the
68
+ * resulting `key → entry` map plus the optional `models` catalog for the process lifetime.
69
+ *
70
+ * Missing file or wrong version fall back to "no manifest" with a single boot warning;
71
+ * the runtime still produces tools using the auto-generated descriptions and
72
+ * ArkType-derived schemas.
73
+ *
74
+ * @returns The cached tool-entry map, or `undefined` when no manifest was loaded.
75
+ */
76
+ private _resolveManifest;
77
+ private _resetManifestCache;
78
+ private _parseManifestFile;
79
+ private _applyParsedManifest;
80
+ /**
81
+ * Reads the caller's OIDC scopes from the raw Express request via the auth context.
82
+ *
83
+ * Synthesizes the same `{ auth: { token } }` shape that `getOidcScopesFromRequest`
84
+ * expects post-dispatch, so the upstream helper stays the single source of scope parsing.
85
+ * Returns `undefined` for non-OIDC callers (no `oidcValidatedToken.scope`) — the filter
86
+ * loop treats that as "skip scope enforcement", matching `oidcCallModelScopePreAssert`.
87
+ *
88
+ * @param ctx - The per-request context carrying the validated auth payload.
89
+ * @returns The set of granted OIDC scopes, or `undefined` when scope enforcement should be skipped.
90
+ */
91
+ private _resolveScopes;
92
+ /**
93
+ * Maps the caller's Firebase custom claims through the optional role reader.
94
+ * Emits one boot-time warning when a declarative `requiredRoles` rule will be checked
95
+ * but no reader is wired — that path will fail closed.
96
+ *
97
+ * @param ctx - The per-request context carrying the authenticated user's token.
98
+ * @returns The resolved auth role set, or `undefined` when no auth or reader is available.
99
+ */
100
+ private _resolveAuthRoles;
101
+ private _filterToolsForRequest;
102
+ private _passesVisibility;
103
+ private _checkDeclarativeVisibility;
104
+ /**
105
+ * Resolves the wire-shape `tools/list` entry for a single tool.
106
+ *
107
+ * Hot-path short-circuit: tools without a `toolDetailsBuilder` reuse the precomputed,
108
+ * frozen {@link McpToolDefinition.staticWireEntry} verbatim — zero allocations per
109
+ * request for the common case.
110
+ *
111
+ * Tools that opted in to {@link McpToolDetailsBuilder} get a fresh wire entry built
112
+ * from the builder's overrides. If the builder throws, the framework falls back to
113
+ * the static defaults and logs a warning (fail-soft, matching `_passesVisibility`).
114
+ *
115
+ * @param tool - The tool definition whose wire entry is being resolved.
116
+ * @param ctx - The per-request context forwarded to any dynamic details builder.
117
+ * @param scopes - The caller's granted OIDC scopes forwarded to any dynamic details builder.
118
+ * @returns The wire-shape entry to emit for this tool on `tools/list`.
119
+ */
120
+ private _buildToolListEntry;
121
+ private _handleToolCall;
122
+ private _handleStaticToolCall;
123
+ private _handleCallModelToolCall;
124
+ }