@exposify/firebase 0.0.0-stage → 0.0.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.
- package/LICENSE +21 -0
- package/README.md +173 -2
- package/dist/cli.js +230 -0
- package/dist/index.cjs +868 -0
- package/dist/index.d.cts +178 -0
- package/dist/index.d.ts +178 -0
- package/dist/index.js +846 -0
- package/package.json +52 -3
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { AuthResolver, WebMcpHandler } from '@exposify/core';
|
|
2
|
+
export { HostedAuthResolverConfig, completeMcpLogin, createHostedAuthResolver, hostedBroker } from '@exposify/core';
|
|
3
|
+
import { IncomingMessage, ServerResponse } from 'node:http';
|
|
4
|
+
|
|
5
|
+
/** What `onRequest` hands its handler: Express's req/res, with the body already read. */
|
|
6
|
+
type FirebaseRequest = IncomingMessage & {
|
|
7
|
+
body?: unknown;
|
|
8
|
+
rawBody?: Buffer;
|
|
9
|
+
originalUrl?: string;
|
|
10
|
+
};
|
|
11
|
+
type NodeHandler = (req: FirebaseRequest, res: ServerResponse) => Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* Adapts a web-standard handler to Node's `(req, res)`. Cloud Functions has
|
|
14
|
+
* already drained the request stream by the time the handler runs (it sets
|
|
15
|
+
* `rawBody` and a parsed `body`), so the body comes from those; a plain Node
|
|
16
|
+
* server's untouched stream is read directly. The response body is streamed
|
|
17
|
+
* chunk by chunk, since MCP answers over SSE.
|
|
18
|
+
*/
|
|
19
|
+
declare function toNodeHandler(handle: (req: Request) => Promise<Response>): NodeHandler;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The JSON Schema subset accepted for `fields` and `functions[].input`
|
|
23
|
+
* (same as `@exposify/supabase`'s), plus `format: "date-time"`, which makes
|
|
24
|
+
* a string field a Firestore timestamp on writes and in filters.
|
|
25
|
+
*/
|
|
26
|
+
interface JsonSchemaProperty {
|
|
27
|
+
type: "string" | "number" | "integer" | "boolean" | "array" | "object";
|
|
28
|
+
description?: string;
|
|
29
|
+
format?: "date-time";
|
|
30
|
+
enum?: unknown[];
|
|
31
|
+
items?: JsonSchemaProperty;
|
|
32
|
+
properties?: Record<string, JsonSchemaProperty>;
|
|
33
|
+
required?: string[];
|
|
34
|
+
}
|
|
35
|
+
interface JsonSchemaObject {
|
|
36
|
+
type?: "object";
|
|
37
|
+
properties?: Record<string, JsonSchemaProperty>;
|
|
38
|
+
required?: string[];
|
|
39
|
+
}
|
|
40
|
+
interface CollectionConfig {
|
|
41
|
+
/** Becomes the tools' description. Say what a document is, for the agent. */
|
|
42
|
+
description?: string;
|
|
43
|
+
/**
|
|
44
|
+
* The document's fields. Optional: without it, filters accept any field
|
|
45
|
+
* name and writes accept any object. With it, filters and `select` are
|
|
46
|
+
* limited to these names and writes are typed.
|
|
47
|
+
*/
|
|
48
|
+
fields?: Record<string, JsonSchemaProperty>;
|
|
49
|
+
/** Field names a `create_*` call must include. */
|
|
50
|
+
required?: string[];
|
|
51
|
+
/**
|
|
52
|
+
* The field holding the owning user's uid, for the common
|
|
53
|
+
* `resource.data.<owner> == request.auth.uid` rule: `list_*` filters on
|
|
54
|
+
* it, `create_*` sets it, `update_*` refuses to change it. A convenience,
|
|
55
|
+
* not a security boundary: your rules still decide.
|
|
56
|
+
*/
|
|
57
|
+
owner?: string;
|
|
58
|
+
}
|
|
59
|
+
/** A registered callable function (`onCall`), invoked with the caller's ID token. */
|
|
60
|
+
interface FunctionToolConfig {
|
|
61
|
+
description: string;
|
|
62
|
+
input?: JsonSchemaObject;
|
|
63
|
+
/** Full URL override, e.g. a custom domain or a run.app URL. */
|
|
64
|
+
url?: string;
|
|
65
|
+
}
|
|
66
|
+
type WriteOp = "create" | "update" | "delete";
|
|
67
|
+
interface ExposifyFirebaseOptions {
|
|
68
|
+
/**
|
|
69
|
+
* The Firestore collections agents can use, keyed by collection path.
|
|
70
|
+
* Subcollections use `{param}` placeholders: `"users/{userId}/notes"`.
|
|
71
|
+
* A plain array is shorthand for `{ name: {} }` each.
|
|
72
|
+
*/
|
|
73
|
+
collections?: Record<string, CollectionConfig> | string[];
|
|
74
|
+
/** `"<collection>.create"` / `.update` / `.delete` / `.*`. Reads need no opt-in. */
|
|
75
|
+
writes?: string[];
|
|
76
|
+
/** Callable functions (`onCall`) exposed as `call_<name>` tools. */
|
|
77
|
+
functions?: Record<string, FunctionToolConfig>;
|
|
78
|
+
/** Doc globs, relative to the functions source directory. Default: `["docs/**\/*.md"]`. */
|
|
79
|
+
docs?: string[];
|
|
80
|
+
/** Default `"public"`. `"required"` hides `search_docs` from callers with no token. */
|
|
81
|
+
docsAuth?: "public" | "required";
|
|
82
|
+
/** Cap on documents per `list_*` call regardless of the caller's `limit`. Default 100. */
|
|
83
|
+
maxDocs?: number;
|
|
84
|
+
/** Default: `GCLOUD_PROJECT` / `GOOGLE_CLOUD_PROJECT` / `FIREBASE_CONFIG.projectId`, set in every deployed function. */
|
|
85
|
+
projectId?: string;
|
|
86
|
+
/** Firestore database id. Default `"(default)"`. */
|
|
87
|
+
databaseId?: string;
|
|
88
|
+
/** Region of the callable functions in `functions`. Default `"us-central1"`. */
|
|
89
|
+
functionsRegion?: string;
|
|
90
|
+
/**
|
|
91
|
+
* Resolves the caller's headers into the headers used for Firestore and
|
|
92
|
+
* callable calls, e.g. `hostedBroker()` (Exposify's hosted broker, from `EXPOSIFY_KEY`).
|
|
93
|
+
* Only `authorization` from the result is used.
|
|
94
|
+
*/
|
|
95
|
+
auth?: AuthResolver;
|
|
96
|
+
/**
|
|
97
|
+
* With `auth` set: 401 when the resolver returns no `authorization`, instead
|
|
98
|
+
* of running unauthenticated. Default true with the hosted broker (`EXPOSIFY_KEY`
|
|
99
|
+
* set, or `auth: hostedBroker()`), which also answers tokenless requests with
|
|
100
|
+
* Connect's sign-in challenge; false otherwise.
|
|
101
|
+
*/
|
|
102
|
+
authRequired?: boolean;
|
|
103
|
+
/** The cold-start "MCP ready" line. Default: on, except under `NODE_ENV=test`. Failures always log. */
|
|
104
|
+
startupLog?: boolean;
|
|
105
|
+
/** Injectable for tests; defaults to global `fetch`. */
|
|
106
|
+
fetchImpl?: typeof fetch;
|
|
107
|
+
/** Overrides `process.env` (tests). */
|
|
108
|
+
env?: Record<string, string | undefined>;
|
|
109
|
+
/** Directory doc globs resolve against. Default `process.cwd()` (the functions source directory when deployed). */
|
|
110
|
+
docsRoot?: string;
|
|
111
|
+
rateLimit?: number;
|
|
112
|
+
rateWindowMs?: number;
|
|
113
|
+
}
|
|
114
|
+
interface ResolvedCollection {
|
|
115
|
+
/** The configured key, e.g. `"users/{userId}/notes"`. */
|
|
116
|
+
key: string;
|
|
117
|
+
/** Path segments; odd positions are collection ids, even positions are `{param}` or literal doc ids. */
|
|
118
|
+
segments: string[];
|
|
119
|
+
/** `{param}` names in order. */
|
|
120
|
+
params: string[];
|
|
121
|
+
/** Tool-name suffix, e.g. `"users_notes"`. */
|
|
122
|
+
suffix: string;
|
|
123
|
+
config: CollectionConfig;
|
|
124
|
+
writes: Set<WriteOp>;
|
|
125
|
+
}
|
|
126
|
+
interface ResolvedFirebaseConfig {
|
|
127
|
+
collections: ResolvedCollection[];
|
|
128
|
+
functions: Record<string, FunctionToolConfig>;
|
|
129
|
+
docs: string[];
|
|
130
|
+
docsAuth: "public" | "required";
|
|
131
|
+
maxDocs: number;
|
|
132
|
+
databaseId: string;
|
|
133
|
+
functionsRegion: string;
|
|
134
|
+
rateLimit?: number;
|
|
135
|
+
rateWindowMs?: number;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Collection keys are Firestore collection paths with `{param}` placeholders
|
|
140
|
+
* in document positions: `"todos"`, `"users/{userId}/notes"`. A key has an
|
|
141
|
+
* odd number of segments (it names a collection, not a document); collection
|
|
142
|
+
* ids (even indexes) must be literal.
|
|
143
|
+
*/
|
|
144
|
+
interface ParsedKey {
|
|
145
|
+
segments: string[];
|
|
146
|
+
params: string[];
|
|
147
|
+
suffix: string;
|
|
148
|
+
}
|
|
149
|
+
/** Throws with a message naming the problem; config.ts turns that into a warning. */
|
|
150
|
+
declare function parseCollectionKey(key: string): ParsedKey;
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* The MCP endpoint for `onRequest`:
|
|
154
|
+
*
|
|
155
|
+
* ```ts
|
|
156
|
+
* export const mcp = onRequest(aiReady({ collections: { todos: { owner: "uid" } } }));
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
159
|
+
declare function aiReady(options?: ExposifyFirebaseOptions): NodeHandler;
|
|
160
|
+
/**
|
|
161
|
+
* The same endpoint as a web-standard `Request → Response` handler, for
|
|
162
|
+
* runtimes other than `onRequest` and for tests. Docs are read and indexed
|
|
163
|
+
* on the first request and cached for the instance's lifetime.
|
|
164
|
+
*/
|
|
165
|
+
declare function createFirebaseMcpHandler(options?: ExposifyFirebaseOptions): WebMcpHandler;
|
|
166
|
+
/**
|
|
167
|
+
* The function's public URL from the URL a request arrived at. Behind
|
|
168
|
+
* `<region>-<project>.cloudfunctions.net/<name>` the function sees the path
|
|
169
|
+
* without its own name, so the name goes back in; a `run.app` URL already is
|
|
170
|
+
* the function's root.
|
|
171
|
+
*/
|
|
172
|
+
declare function functionPublicUrl(own: string, functionName: string | undefined): string;
|
|
173
|
+
/** `https://<region>-<project>.cloudfunctions.net/<name>`, or the Functions emulator's URL. */
|
|
174
|
+
declare function callableUrl(name: string, projectId: string, region: string, env: Record<string, string | undefined>): string;
|
|
175
|
+
/** "7 tools (2 list, 2 get, 2 write, 1 callable)": only the kinds present. */
|
|
176
|
+
declare function firebaseToolsPhrase(cfg: ResolvedFirebaseConfig, hasDocs: boolean): string;
|
|
177
|
+
|
|
178
|
+
export { type CollectionConfig, type ExposifyFirebaseOptions, type FirebaseRequest, type FunctionToolConfig, type JsonSchemaObject, type JsonSchemaProperty, type NodeHandler, aiReady, callableUrl, createFirebaseMcpHandler, firebaseToolsPhrase, functionPublicUrl, parseCollectionKey, toNodeHandler };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { AuthResolver, WebMcpHandler } from '@exposify/core';
|
|
2
|
+
export { HostedAuthResolverConfig, completeMcpLogin, createHostedAuthResolver, hostedBroker } from '@exposify/core';
|
|
3
|
+
import { IncomingMessage, ServerResponse } from 'node:http';
|
|
4
|
+
|
|
5
|
+
/** What `onRequest` hands its handler: Express's req/res, with the body already read. */
|
|
6
|
+
type FirebaseRequest = IncomingMessage & {
|
|
7
|
+
body?: unknown;
|
|
8
|
+
rawBody?: Buffer;
|
|
9
|
+
originalUrl?: string;
|
|
10
|
+
};
|
|
11
|
+
type NodeHandler = (req: FirebaseRequest, res: ServerResponse) => Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* Adapts a web-standard handler to Node's `(req, res)`. Cloud Functions has
|
|
14
|
+
* already drained the request stream by the time the handler runs (it sets
|
|
15
|
+
* `rawBody` and a parsed `body`), so the body comes from those; a plain Node
|
|
16
|
+
* server's untouched stream is read directly. The response body is streamed
|
|
17
|
+
* chunk by chunk, since MCP answers over SSE.
|
|
18
|
+
*/
|
|
19
|
+
declare function toNodeHandler(handle: (req: Request) => Promise<Response>): NodeHandler;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The JSON Schema subset accepted for `fields` and `functions[].input`
|
|
23
|
+
* (same as `@exposify/supabase`'s), plus `format: "date-time"`, which makes
|
|
24
|
+
* a string field a Firestore timestamp on writes and in filters.
|
|
25
|
+
*/
|
|
26
|
+
interface JsonSchemaProperty {
|
|
27
|
+
type: "string" | "number" | "integer" | "boolean" | "array" | "object";
|
|
28
|
+
description?: string;
|
|
29
|
+
format?: "date-time";
|
|
30
|
+
enum?: unknown[];
|
|
31
|
+
items?: JsonSchemaProperty;
|
|
32
|
+
properties?: Record<string, JsonSchemaProperty>;
|
|
33
|
+
required?: string[];
|
|
34
|
+
}
|
|
35
|
+
interface JsonSchemaObject {
|
|
36
|
+
type?: "object";
|
|
37
|
+
properties?: Record<string, JsonSchemaProperty>;
|
|
38
|
+
required?: string[];
|
|
39
|
+
}
|
|
40
|
+
interface CollectionConfig {
|
|
41
|
+
/** Becomes the tools' description. Say what a document is, for the agent. */
|
|
42
|
+
description?: string;
|
|
43
|
+
/**
|
|
44
|
+
* The document's fields. Optional: without it, filters accept any field
|
|
45
|
+
* name and writes accept any object. With it, filters and `select` are
|
|
46
|
+
* limited to these names and writes are typed.
|
|
47
|
+
*/
|
|
48
|
+
fields?: Record<string, JsonSchemaProperty>;
|
|
49
|
+
/** Field names a `create_*` call must include. */
|
|
50
|
+
required?: string[];
|
|
51
|
+
/**
|
|
52
|
+
* The field holding the owning user's uid, for the common
|
|
53
|
+
* `resource.data.<owner> == request.auth.uid` rule: `list_*` filters on
|
|
54
|
+
* it, `create_*` sets it, `update_*` refuses to change it. A convenience,
|
|
55
|
+
* not a security boundary: your rules still decide.
|
|
56
|
+
*/
|
|
57
|
+
owner?: string;
|
|
58
|
+
}
|
|
59
|
+
/** A registered callable function (`onCall`), invoked with the caller's ID token. */
|
|
60
|
+
interface FunctionToolConfig {
|
|
61
|
+
description: string;
|
|
62
|
+
input?: JsonSchemaObject;
|
|
63
|
+
/** Full URL override, e.g. a custom domain or a run.app URL. */
|
|
64
|
+
url?: string;
|
|
65
|
+
}
|
|
66
|
+
type WriteOp = "create" | "update" | "delete";
|
|
67
|
+
interface ExposifyFirebaseOptions {
|
|
68
|
+
/**
|
|
69
|
+
* The Firestore collections agents can use, keyed by collection path.
|
|
70
|
+
* Subcollections use `{param}` placeholders: `"users/{userId}/notes"`.
|
|
71
|
+
* A plain array is shorthand for `{ name: {} }` each.
|
|
72
|
+
*/
|
|
73
|
+
collections?: Record<string, CollectionConfig> | string[];
|
|
74
|
+
/** `"<collection>.create"` / `.update` / `.delete` / `.*`. Reads need no opt-in. */
|
|
75
|
+
writes?: string[];
|
|
76
|
+
/** Callable functions (`onCall`) exposed as `call_<name>` tools. */
|
|
77
|
+
functions?: Record<string, FunctionToolConfig>;
|
|
78
|
+
/** Doc globs, relative to the functions source directory. Default: `["docs/**\/*.md"]`. */
|
|
79
|
+
docs?: string[];
|
|
80
|
+
/** Default `"public"`. `"required"` hides `search_docs` from callers with no token. */
|
|
81
|
+
docsAuth?: "public" | "required";
|
|
82
|
+
/** Cap on documents per `list_*` call regardless of the caller's `limit`. Default 100. */
|
|
83
|
+
maxDocs?: number;
|
|
84
|
+
/** Default: `GCLOUD_PROJECT` / `GOOGLE_CLOUD_PROJECT` / `FIREBASE_CONFIG.projectId`, set in every deployed function. */
|
|
85
|
+
projectId?: string;
|
|
86
|
+
/** Firestore database id. Default `"(default)"`. */
|
|
87
|
+
databaseId?: string;
|
|
88
|
+
/** Region of the callable functions in `functions`. Default `"us-central1"`. */
|
|
89
|
+
functionsRegion?: string;
|
|
90
|
+
/**
|
|
91
|
+
* Resolves the caller's headers into the headers used for Firestore and
|
|
92
|
+
* callable calls, e.g. `hostedBroker()` (Exposify's hosted broker, from `EXPOSIFY_KEY`).
|
|
93
|
+
* Only `authorization` from the result is used.
|
|
94
|
+
*/
|
|
95
|
+
auth?: AuthResolver;
|
|
96
|
+
/**
|
|
97
|
+
* With `auth` set: 401 when the resolver returns no `authorization`, instead
|
|
98
|
+
* of running unauthenticated. Default true with the hosted broker (`EXPOSIFY_KEY`
|
|
99
|
+
* set, or `auth: hostedBroker()`), which also answers tokenless requests with
|
|
100
|
+
* Connect's sign-in challenge; false otherwise.
|
|
101
|
+
*/
|
|
102
|
+
authRequired?: boolean;
|
|
103
|
+
/** The cold-start "MCP ready" line. Default: on, except under `NODE_ENV=test`. Failures always log. */
|
|
104
|
+
startupLog?: boolean;
|
|
105
|
+
/** Injectable for tests; defaults to global `fetch`. */
|
|
106
|
+
fetchImpl?: typeof fetch;
|
|
107
|
+
/** Overrides `process.env` (tests). */
|
|
108
|
+
env?: Record<string, string | undefined>;
|
|
109
|
+
/** Directory doc globs resolve against. Default `process.cwd()` (the functions source directory when deployed). */
|
|
110
|
+
docsRoot?: string;
|
|
111
|
+
rateLimit?: number;
|
|
112
|
+
rateWindowMs?: number;
|
|
113
|
+
}
|
|
114
|
+
interface ResolvedCollection {
|
|
115
|
+
/** The configured key, e.g. `"users/{userId}/notes"`. */
|
|
116
|
+
key: string;
|
|
117
|
+
/** Path segments; odd positions are collection ids, even positions are `{param}` or literal doc ids. */
|
|
118
|
+
segments: string[];
|
|
119
|
+
/** `{param}` names in order. */
|
|
120
|
+
params: string[];
|
|
121
|
+
/** Tool-name suffix, e.g. `"users_notes"`. */
|
|
122
|
+
suffix: string;
|
|
123
|
+
config: CollectionConfig;
|
|
124
|
+
writes: Set<WriteOp>;
|
|
125
|
+
}
|
|
126
|
+
interface ResolvedFirebaseConfig {
|
|
127
|
+
collections: ResolvedCollection[];
|
|
128
|
+
functions: Record<string, FunctionToolConfig>;
|
|
129
|
+
docs: string[];
|
|
130
|
+
docsAuth: "public" | "required";
|
|
131
|
+
maxDocs: number;
|
|
132
|
+
databaseId: string;
|
|
133
|
+
functionsRegion: string;
|
|
134
|
+
rateLimit?: number;
|
|
135
|
+
rateWindowMs?: number;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Collection keys are Firestore collection paths with `{param}` placeholders
|
|
140
|
+
* in document positions: `"todos"`, `"users/{userId}/notes"`. A key has an
|
|
141
|
+
* odd number of segments (it names a collection, not a document); collection
|
|
142
|
+
* ids (even indexes) must be literal.
|
|
143
|
+
*/
|
|
144
|
+
interface ParsedKey {
|
|
145
|
+
segments: string[];
|
|
146
|
+
params: string[];
|
|
147
|
+
suffix: string;
|
|
148
|
+
}
|
|
149
|
+
/** Throws with a message naming the problem; config.ts turns that into a warning. */
|
|
150
|
+
declare function parseCollectionKey(key: string): ParsedKey;
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* The MCP endpoint for `onRequest`:
|
|
154
|
+
*
|
|
155
|
+
* ```ts
|
|
156
|
+
* export const mcp = onRequest(aiReady({ collections: { todos: { owner: "uid" } } }));
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
159
|
+
declare function aiReady(options?: ExposifyFirebaseOptions): NodeHandler;
|
|
160
|
+
/**
|
|
161
|
+
* The same endpoint as a web-standard `Request → Response` handler, for
|
|
162
|
+
* runtimes other than `onRequest` and for tests. Docs are read and indexed
|
|
163
|
+
* on the first request and cached for the instance's lifetime.
|
|
164
|
+
*/
|
|
165
|
+
declare function createFirebaseMcpHandler(options?: ExposifyFirebaseOptions): WebMcpHandler;
|
|
166
|
+
/**
|
|
167
|
+
* The function's public URL from the URL a request arrived at. Behind
|
|
168
|
+
* `<region>-<project>.cloudfunctions.net/<name>` the function sees the path
|
|
169
|
+
* without its own name, so the name goes back in; a `run.app` URL already is
|
|
170
|
+
* the function's root.
|
|
171
|
+
*/
|
|
172
|
+
declare function functionPublicUrl(own: string, functionName: string | undefined): string;
|
|
173
|
+
/** `https://<region>-<project>.cloudfunctions.net/<name>`, or the Functions emulator's URL. */
|
|
174
|
+
declare function callableUrl(name: string, projectId: string, region: string, env: Record<string, string | undefined>): string;
|
|
175
|
+
/** "7 tools (2 list, 2 get, 2 write, 1 callable)": only the kinds present. */
|
|
176
|
+
declare function firebaseToolsPhrase(cfg: ResolvedFirebaseConfig, hasDocs: boolean): string;
|
|
177
|
+
|
|
178
|
+
export { type CollectionConfig, type ExposifyFirebaseOptions, type FirebaseRequest, type FunctionToolConfig, type JsonSchemaObject, type JsonSchemaProperty, type NodeHandler, aiReady, callableUrl, createFirebaseMcpHandler, firebaseToolsPhrase, functionPublicUrl, parseCollectionKey, toNodeHandler };
|