firebase-mcp 0.1.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 (82) hide show
  1. package/README.md +119 -0
  2. package/dist/access/index.d.ts +23 -0
  3. package/dist/access/index.d.ts.map +1 -0
  4. package/dist/access/index.js +35 -0
  5. package/dist/access/index.js.map +1 -0
  6. package/dist/cli/index.d.ts +3 -0
  7. package/dist/cli/index.d.ts.map +1 -0
  8. package/dist/cli/index.js +14 -0
  9. package/dist/cli/index.js.map +1 -0
  10. package/dist/config/index.d.ts +51 -0
  11. package/dist/config/index.d.ts.map +1 -0
  12. package/dist/config/index.js +56 -0
  13. package/dist/config/index.js.map +1 -0
  14. package/dist/firebase/index.d.ts +21 -0
  15. package/dist/firebase/index.d.ts.map +1 -0
  16. package/dist/firebase/index.js +42 -0
  17. package/dist/firebase/index.js.map +1 -0
  18. package/dist/server/index.d.ts +23 -0
  19. package/dist/server/index.d.ts.map +1 -0
  20. package/dist/server/index.js +60 -0
  21. package/dist/server/index.js.map +1 -0
  22. package/dist/tools/firestore/aggregate_collection.d.ts +34 -0
  23. package/dist/tools/firestore/aggregate_collection.d.ts.map +1 -0
  24. package/dist/tools/firestore/aggregate_collection.js +86 -0
  25. package/dist/tools/firestore/aggregate_collection.js.map +1 -0
  26. package/dist/tools/firestore/count_documents.d.ts +25 -0
  27. package/dist/tools/firestore/count_documents.d.ts.map +1 -0
  28. package/dist/tools/firestore/count_documents.js +54 -0
  29. package/dist/tools/firestore/count_documents.js.map +1 -0
  30. package/dist/tools/firestore/get_collection_schema.d.ts +30 -0
  31. package/dist/tools/firestore/get_collection_schema.d.ts.map +1 -0
  32. package/dist/tools/firestore/get_collection_schema.js +129 -0
  33. package/dist/tools/firestore/get_collection_schema.js.map +1 -0
  34. package/dist/tools/firestore/get_document.d.ts +32 -0
  35. package/dist/tools/firestore/get_document.d.ts.map +1 -0
  36. package/dist/tools/firestore/get_document.js +62 -0
  37. package/dist/tools/firestore/get_document.js.map +1 -0
  38. package/dist/tools/firestore/get_many_documents.d.ts +33 -0
  39. package/dist/tools/firestore/get_many_documents.d.ts.map +1 -0
  40. package/dist/tools/firestore/get_many_documents.js +86 -0
  41. package/dist/tools/firestore/get_many_documents.js.map +1 -0
  42. package/dist/tools/firestore/index.d.ts +12 -0
  43. package/dist/tools/firestore/index.d.ts.map +1 -0
  44. package/dist/tools/firestore/index.js +28 -0
  45. package/dist/tools/firestore/index.js.map +1 -0
  46. package/dist/tools/firestore/list_collections.d.ts +24 -0
  47. package/dist/tools/firestore/list_collections.d.ts.map +1 -0
  48. package/dist/tools/firestore/list_collections.js +61 -0
  49. package/dist/tools/firestore/list_collections.js.map +1 -0
  50. package/dist/tools/firestore/list_documents.d.ts +24 -0
  51. package/dist/tools/firestore/list_documents.d.ts.map +1 -0
  52. package/dist/tools/firestore/list_documents.js +60 -0
  53. package/dist/tools/firestore/list_documents.js.map +1 -0
  54. package/dist/tools/firestore/list_indexes.d.ts +32 -0
  55. package/dist/tools/firestore/list_indexes.d.ts.map +1 -0
  56. package/dist/tools/firestore/list_indexes.js +99 -0
  57. package/dist/tools/firestore/list_indexes.js.map +1 -0
  58. package/dist/tools/firestore/normalize.d.ts +8 -0
  59. package/dist/tools/firestore/normalize.d.ts.map +1 -0
  60. package/dist/tools/firestore/normalize.js +36 -0
  61. package/dist/tools/firestore/normalize.js.map +1 -0
  62. package/dist/tools/firestore/query_collection.d.ts +34 -0
  63. package/dist/tools/firestore/query_collection.d.ts.map +1 -0
  64. package/dist/tools/firestore/query_collection.js +96 -0
  65. package/dist/tools/firestore/query_collection.js.map +1 -0
  66. package/dist/tools/firestore/query_collection_group.d.ts +34 -0
  67. package/dist/tools/firestore/query_collection_group.d.ts.map +1 -0
  68. package/dist/tools/firestore/query_collection_group.js +94 -0
  69. package/dist/tools/firestore/query_collection_group.js.map +1 -0
  70. package/dist/tools/firestore/read_collections.d.ts +44 -0
  71. package/dist/tools/firestore/read_collections.d.ts.map +1 -0
  72. package/dist/tools/firestore/read_collections.js +95 -0
  73. package/dist/tools/firestore/read_collections.js.map +1 -0
  74. package/dist/tools/firestore/types.d.ts +52 -0
  75. package/dist/tools/firestore/types.d.ts.map +1 -0
  76. package/dist/tools/firestore/types.js +75 -0
  77. package/dist/tools/firestore/types.js.map +1 -0
  78. package/dist/tools/index.d.ts +104 -0
  79. package/dist/tools/index.d.ts.map +1 -0
  80. package/dist/tools/index.js +121 -0
  81. package/dist/tools/index.js.map +1 -0
  82. package/package.json +48 -0
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.listCollections = exports.listCollectionsDefinition = exports.LIST_COLLECTIONS = exports.FirestoreListCollectionsError = void 0;
4
+ const effect_1 = require("effect");
5
+ const access_1 = require("../../access");
6
+ const firebase_1 = require("../../firebase");
7
+ class FirestoreListCollectionsError extends effect_1.Data.TaggedError('FirestoreListCollectionsError') {
8
+ }
9
+ exports.FirestoreListCollectionsError = FirestoreListCollectionsError;
10
+ exports.LIST_COLLECTIONS = 'list_collections';
11
+ exports.listCollectionsDefinition = {
12
+ name: exports.LIST_COLLECTIONS,
13
+ description: 'List Firestore collections. Omit path to list all root-level collections, or provide a document path to list its subcollections.',
14
+ inputSchema: {
15
+ type: 'object',
16
+ properties: {
17
+ path: {
18
+ type: 'string',
19
+ description: "Optional document path whose subcollections to list, e.g. 'users/123'. Omit to list root-level collections.",
20
+ },
21
+ includeCounts: {
22
+ type: 'boolean',
23
+ description: 'If true, includes the document count for each collection. Useful for quickly understanding the scale of each collection without separate count_documents calls.',
24
+ },
25
+ },
26
+ },
27
+ };
28
+ const listCollections = (input) => effect_1.Effect.gen(function* () {
29
+ const { firestore } = yield* firebase_1.FirebaseService;
30
+ if (input.path) {
31
+ const access = yield* access_1.AccessService;
32
+ yield* access.check(input.path);
33
+ }
34
+ const collections = yield* effect_1.Effect.tryPromise({
35
+ try: () => input.path
36
+ ? firestore().doc(input.path).listCollections()
37
+ : firestore().listCollections(),
38
+ catch: (cause) => new FirestoreListCollectionsError({
39
+ message: input.path
40
+ ? `Failed to list subcollections of: ${input.path}`
41
+ : 'Failed to list root collections',
42
+ cause,
43
+ }),
44
+ });
45
+ if (input.includeCounts) {
46
+ const results = yield* effect_1.Effect.tryPromise({
47
+ try: () => Promise.all(collections.map(async (col) => {
48
+ const snap = await col.count().get();
49
+ return { id: col.id, path: col.path, count: snap.data().count };
50
+ })),
51
+ catch: (cause) => new FirestoreListCollectionsError({
52
+ message: 'Failed to get counts for collections',
53
+ cause,
54
+ }),
55
+ });
56
+ return results;
57
+ }
58
+ return collections.map((col) => ({ id: col.id, path: col.path }));
59
+ });
60
+ exports.listCollections = listCollections;
61
+ //# sourceMappingURL=list_collections.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list_collections.js","sourceRoot":"","sources":["../../../src/tools/firestore/list_collections.ts"],"names":[],"mappings":";;;AACA,mCAAsC;AAEtC,yCAA6C;AAC7C,6CAAiD;AAEjD,MAAa,6BAA8B,SAAQ,aAAI,CAAC,WAAW,CACjE,+BAA+B,CAI/B;CAAG;AALL,sEAKK;AAEQ,QAAA,gBAAgB,GAAG,kBAA2B,CAAC;AAO/C,QAAA,yBAAyB,GAAS;IAC7C,IAAI,EAAE,wBAAgB;IACtB,WAAW,EACT,kIAAkI;IACpI,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,IAAI,EAAE;gBACJ,IAAI,EAAE,QAAQ;gBACd,WAAW,EACT,6GAA6G;aAChH;YACD,aAAa,EAAE;gBACb,IAAI,EAAE,SAAS;gBACf,WAAW,EACT,iKAAiK;aACpK;SACF;KACF;CACF,CAAC;AAEK,MAAM,eAAe,GAAG,CAAC,KAA0B,EAAE,EAAE,CAC5D,eAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC,CAAC,0BAAe,CAAC;IAE7C,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;QACf,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,sBAAa,CAAC;QACpC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClC,CAAC;IAED,MAAM,WAAW,GAAG,KAAK,CAAC,CAAC,eAAM,CAAC,UAAU,CAAC;QAC3C,GAAG,EAAE,GAAG,EAAE,CACR,KAAK,CAAC,IAAI;YACR,CAAC,CAAC,SAAS,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,eAAe,EAAE;YAC/C,CAAC,CAAC,SAAS,EAAE,CAAC,eAAe,EAAE;QACnC,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CACf,IAAI,6BAA6B,CAAC;YAChC,OAAO,EAAE,KAAK,CAAC,IAAI;gBACjB,CAAC,CAAC,qCAAqC,KAAK,CAAC,IAAI,EAAE;gBACnD,CAAC,CAAC,iCAAiC;YACrC,KAAK;SACN,CAAC;KACL,CAAC,CAAC;IAEH,IAAI,KAAK,CAAC,aAAa,EAAE,CAAC;QACxB,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,eAAM,CAAC,UAAU,CAAC;YACvC,GAAG,EAAE,GAAG,EAAE,CACR,OAAO,CAAC,GAAG,CACT,WAAW,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;gBAC5B,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,EAAE,CAAC;gBACrC,OAAO,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,CAAC;YAClE,CAAC,CAAC,CACH;YACH,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CACf,IAAI,6BAA6B,CAAC;gBAChC,OAAO,EAAE,sCAAsC;gBAC/C,KAAK;aACN,CAAC;SACL,CAAC,CAAC;QACH,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;AACpE,CAAC,CAAC,CAAC;AA1CQ,QAAA,eAAe,mBA0CvB"}
@@ -0,0 +1,24 @@
1
+ import { Tool } from '@modelcontextprotocol/sdk/types.js';
2
+ import { Effect } from 'effect';
3
+ import { AccessService } from '../../access';
4
+ import { FirebaseService } from '../../firebase';
5
+ declare const FirestoreListDocumentsError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
6
+ readonly _tag: "FirestoreListDocumentsError";
7
+ } & Readonly<A>;
8
+ export declare class FirestoreListDocumentsError extends FirestoreListDocumentsError_base<{
9
+ readonly message: string;
10
+ readonly cause?: unknown;
11
+ }> {
12
+ }
13
+ export declare const LIST_DOCUMENTS: "list_documents";
14
+ export interface ListDocumentsArgs {
15
+ collection: string;
16
+ includeCollections?: boolean;
17
+ }
18
+ export declare const listDocumentsDefinition: Tool;
19
+ export declare const listDocuments: (input: ListDocumentsArgs) => Effect.Effect<{
20
+ id: string;
21
+ path: string;
22
+ }[], import("../../access").AccessDeniedError | FirestoreListDocumentsError, AccessService | FirebaseService>;
23
+ export {};
24
+ //# sourceMappingURL=list_documents.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list_documents.d.ts","sourceRoot":"","sources":["../../../src/tools/firestore/list_documents.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AAC1D,OAAO,EAAQ,MAAM,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;;;;AAEjD,qBAAa,2BAA4B,SAAQ,iCAE/C;IACA,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC;CAAG;AAEL,eAAO,MAAM,cAAc,EAAG,gBAAyB,CAAC;AAExD,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,eAAO,MAAM,uBAAuB,EAAE,IAoBrC,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,OAAO,iBAAiB;;;6GAuClD,CAAC"}
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.listDocuments = exports.listDocumentsDefinition = exports.LIST_DOCUMENTS = exports.FirestoreListDocumentsError = void 0;
4
+ const effect_1 = require("effect");
5
+ const access_1 = require("../../access");
6
+ const firebase_1 = require("../../firebase");
7
+ class FirestoreListDocumentsError extends effect_1.Data.TaggedError('FirestoreListDocumentsError') {
8
+ }
9
+ exports.FirestoreListDocumentsError = FirestoreListDocumentsError;
10
+ exports.LIST_DOCUMENTS = 'list_documents';
11
+ exports.listDocumentsDefinition = {
12
+ name: exports.LIST_DOCUMENTS,
13
+ description: 'List all document IDs in a Firestore collection, including phantom documents (documents with no fields that exist only as parents of subcollections). Use this when read_collection returns empty but subcollections are known to exist.',
14
+ inputSchema: {
15
+ type: 'object',
16
+ properties: {
17
+ collection: {
18
+ type: 'string',
19
+ description: "Collection path, e.g. 'users' or 'shared/stores_data/ABC123'",
20
+ },
21
+ includeCollections: {
22
+ type: 'boolean',
23
+ description: 'If true, also lists the subcollections of each document alongside its ID. Useful when exploring unknown structure to avoid a follow-up list_collections call per document.',
24
+ },
25
+ },
26
+ required: ['collection'],
27
+ },
28
+ };
29
+ const listDocuments = (input) => effect_1.Effect.gen(function* () {
30
+ const access = yield* access_1.AccessService;
31
+ yield* access.check(input.collection);
32
+ const { firestore } = yield* firebase_1.FirebaseService;
33
+ const refs = yield* effect_1.Effect.tryPromise({
34
+ try: () => firestore().collection(input.collection).listDocuments(),
35
+ catch: (cause) => new FirestoreListDocumentsError({
36
+ message: `Failed to list documents in: ${input.collection}`,
37
+ cause,
38
+ }),
39
+ });
40
+ if (input.includeCollections) {
41
+ const results = yield* effect_1.Effect.tryPromise({
42
+ try: () => Promise.all(refs.map(async (ref) => {
43
+ const collections = await ref.listCollections();
44
+ return {
45
+ id: ref.id,
46
+ path: ref.path,
47
+ collections: collections.map((c) => c.id),
48
+ };
49
+ })),
50
+ catch: (cause) => new FirestoreListDocumentsError({
51
+ message: `Failed to list subcollections in: ${input.collection}`,
52
+ cause,
53
+ }),
54
+ });
55
+ return results;
56
+ }
57
+ return refs.map((ref) => ({ id: ref.id, path: ref.path }));
58
+ });
59
+ exports.listDocuments = listDocuments;
60
+ //# sourceMappingURL=list_documents.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list_documents.js","sourceRoot":"","sources":["../../../src/tools/firestore/list_documents.ts"],"names":[],"mappings":";;;AACA,mCAAsC;AACtC,yCAA6C;AAC7C,6CAAiD;AAEjD,MAAa,2BAA4B,SAAQ,aAAI,CAAC,WAAW,CAC/D,6BAA6B,CAI7B;CAAG;AALL,kEAKK;AAEQ,QAAA,cAAc,GAAG,gBAAyB,CAAC;AAO3C,QAAA,uBAAuB,GAAS;IAC3C,IAAI,EAAE,sBAAc;IACpB,WAAW,EACT,0OAA0O;IAC5O,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,UAAU,EAAE;gBACV,IAAI,EAAE,QAAQ;gBACd,WAAW,EACT,8DAA8D;aACjE;YACD,kBAAkB,EAAE;gBAClB,IAAI,EAAE,SAAS;gBACf,WAAW,EACT,4KAA4K;aAC/K;SACF;QACD,QAAQ,EAAE,CAAC,YAAY,CAAC;KACzB;CACF,CAAC;AAEK,MAAM,aAAa,GAAG,CAAC,KAAwB,EAAE,EAAE,CACxD,eAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,sBAAa,CAAC;IACpC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAEtC,MAAM,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC,CAAC,0BAAe,CAAC;IAE7C,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,eAAM,CAAC,UAAU,CAAC;QACpC,GAAG,EAAE,GAAG,EAAE,CAAC,SAAS,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,aAAa,EAAE;QACnE,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CACf,IAAI,2BAA2B,CAAC;YAC9B,OAAO,EAAE,gCAAgC,KAAK,CAAC,UAAU,EAAE;YAC3D,KAAK;SACN,CAAC;KACL,CAAC,CAAC;IAEH,IAAI,KAAK,CAAC,kBAAkB,EAAE,CAAC;QAC7B,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,eAAM,CAAC,UAAU,CAAC;YACvC,GAAG,EAAE,GAAG,EAAE,CACR,OAAO,CAAC,GAAG,CACT,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;gBACrB,MAAM,WAAW,GAAG,MAAM,GAAG,CAAC,eAAe,EAAE,CAAC;gBAChD,OAAO;oBACL,EAAE,EAAE,GAAG,CAAC,EAAE;oBACV,IAAI,EAAE,GAAG,CAAC,IAAI;oBACd,WAAW,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBAC1C,CAAC;YACJ,CAAC,CAAC,CACH;YACH,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CACf,IAAI,2BAA2B,CAAC;gBAC9B,OAAO,EAAE,qCAAqC,KAAK,CAAC,UAAU,EAAE;gBAChE,KAAK;aACN,CAAC;SACL,CAAC,CAAC;QACH,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;AAC7D,CAAC,CAAC,CAAC;AAvCQ,QAAA,aAAa,iBAuCrB"}
@@ -0,0 +1,32 @@
1
+ import { Tool } from '@modelcontextprotocol/sdk/types.js';
2
+ import { Effect } from 'effect';
3
+ import { ConfigService } from '../../config';
4
+ declare const FirestoreListIndexesError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
5
+ readonly _tag: "FirestoreListIndexesError";
6
+ } & Readonly<A>;
7
+ export declare class FirestoreListIndexesError extends FirestoreListIndexesError_base<{
8
+ readonly message: string;
9
+ readonly cause?: unknown;
10
+ }> {
11
+ }
12
+ export declare const LIST_INDEXES: "list_indexes";
13
+ export interface ListIndexesArgs {
14
+ collectionGroup?: string;
15
+ includeNotReady?: boolean;
16
+ }
17
+ export declare const listIndexesDefinition: Tool;
18
+ export declare const listIndexes: (input: ListIndexesArgs) => Effect.Effect<{
19
+ total: number;
20
+ indexes: Record<string, {
21
+ collectionGroup: string;
22
+ queryScope: "COLLECTION" | "COLLECTION_GROUP";
23
+ fields: {
24
+ arrayConfig?: "CONTAINS" | undefined;
25
+ order?: "ASCENDING" | "DESCENDING" | undefined;
26
+ field: string;
27
+ }[];
28
+ state: "READY" | "CREATING" | "NEEDS_REPAIR";
29
+ }[]>;
30
+ }, FirestoreListIndexesError, ConfigService>;
31
+ export {};
32
+ //# sourceMappingURL=list_indexes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list_indexes.d.ts","sourceRoot":"","sources":["../../../src/tools/firestore/list_indexes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AAC1D,OAAO,EAAQ,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGtC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;;;;AAE7C,qBAAa,yBAA0B,SAAQ,+BAE7C;IACA,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC;CAAG;AAEL,eAAO,MAAM,YAAY,EAAG,cAAuB,CAAC;AAEpD,MAAM,WAAW,eAAe;IAC9B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAoBD,eAAO,MAAM,qBAAqB,EAAE,IAmBnC,CAAC;AAEF,eAAO,MAAM,WAAW,GAAI,OAAO,eAAe;;;;;;;;;;;;4CAgF9C,CAAC"}
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.listIndexes = exports.listIndexesDefinition = exports.LIST_INDEXES = exports.FirestoreListIndexesError = void 0;
7
+ const effect_1 = require("effect");
8
+ const firebase_admin_1 = __importDefault(require("firebase-admin"));
9
+ const config_1 = require("../../config");
10
+ class FirestoreListIndexesError extends effect_1.Data.TaggedError('FirestoreListIndexesError') {
11
+ }
12
+ exports.FirestoreListIndexesError = FirestoreListIndexesError;
13
+ exports.LIST_INDEXES = 'list_indexes';
14
+ exports.listIndexesDefinition = {
15
+ name: exports.LIST_INDEXES,
16
+ description: 'List Firestore composite indexes. Use this before running complex queries or collection group queries to check whether the required indexes exist. Returns index fields, query scope (COLLECTION or COLLECTION_GROUP), and state.',
17
+ inputSchema: {
18
+ type: 'object',
19
+ properties: {
20
+ collectionGroup: {
21
+ type: 'string',
22
+ description: 'Filter to a specific collection group name, e.g. "stock" or "purchase_orders". Omit to return all indexes.',
23
+ },
24
+ includeNotReady: {
25
+ type: 'boolean',
26
+ description: 'If true, includes indexes that are still CREATING or NEEDS_REPAIR. Defaults to false (only READY indexes).',
27
+ },
28
+ },
29
+ },
30
+ };
31
+ const listIndexes = (input) => effect_1.Effect.gen(function* () {
32
+ const { config } = yield* config_1.ConfigService;
33
+ const projectId = config.firebase.projectId;
34
+ const token = yield* effect_1.Effect.tryPromise({
35
+ try: () => firebase_admin_1.default.app().options.credential.getAccessToken(),
36
+ catch: (cause) => new FirestoreListIndexesError({
37
+ message: 'Failed to get access token',
38
+ cause,
39
+ }),
40
+ });
41
+ // Fetch all pages of indexes
42
+ const allIndexes = [];
43
+ let pageToken;
44
+ do {
45
+ const url = new URL(`https://firestore.googleapis.com/v1/projects/${projectId}/databases/(default)/collectionGroups/-/indexes`);
46
+ if (pageToken)
47
+ url.searchParams.set('pageToken', pageToken);
48
+ const page = yield* effect_1.Effect.tryPromise({
49
+ try: () => fetch(url.toString(), {
50
+ headers: { Authorization: `Bearer ${token.access_token}` },
51
+ }).then((r) => r.json()),
52
+ catch: (cause) => new FirestoreListIndexesError({
53
+ message: 'Failed to fetch indexes from Firestore Management API',
54
+ cause,
55
+ }),
56
+ });
57
+ allIndexes.push(...(page.indexes ?? []));
58
+ pageToken = page.nextPageToken;
59
+ } while (pageToken);
60
+ // Extract collection group from index name:
61
+ // projects/{projectId}/databases/(default)/collectionGroups/{group}/indexes/{id}
62
+ const parsed = allIndexes
63
+ .map((idx) => {
64
+ const parts = idx.name.split('/');
65
+ const groupIdx = parts.indexOf('collectionGroups');
66
+ const collectionGroup = groupIdx !== -1 ? parts[groupIdx + 1] : 'unknown';
67
+ return {
68
+ collectionGroup,
69
+ queryScope: idx.queryScope,
70
+ fields: idx.fields.map((f) => ({
71
+ field: f.fieldPath,
72
+ ...(f.order ? { order: f.order } : {}),
73
+ ...(f.arrayConfig ? { arrayConfig: f.arrayConfig } : {}),
74
+ })),
75
+ state: idx.state,
76
+ };
77
+ })
78
+ .filter((idx) => {
79
+ if (!input.includeNotReady && idx.state !== 'READY')
80
+ return false;
81
+ if (input.collectionGroup &&
82
+ idx.collectionGroup !== input.collectionGroup)
83
+ return false;
84
+ return true;
85
+ });
86
+ // Group by collection group for easier reading
87
+ const grouped = {};
88
+ for (const idx of parsed) {
89
+ if (!grouped[idx.collectionGroup])
90
+ grouped[idx.collectionGroup] = [];
91
+ grouped[idx.collectionGroup].push(idx);
92
+ }
93
+ return {
94
+ total: parsed.length,
95
+ indexes: grouped,
96
+ };
97
+ });
98
+ exports.listIndexes = listIndexes;
99
+ //# sourceMappingURL=list_indexes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list_indexes.js","sourceRoot":"","sources":["../../../src/tools/firestore/list_indexes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import admin from 'firebase-admin';
2
+ export declare const normalizeValue: (value: unknown) => unknown;
3
+ export declare const normalizeDocument: (doc: admin.firestore.QueryDocumentSnapshot | admin.firestore.DocumentSnapshot) => {
4
+ id: string;
5
+ path: string;
6
+ data: unknown;
7
+ };
8
+ //# sourceMappingURL=normalize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../../../src/tools/firestore/normalize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,gBAAgB,CAAC;AAEnC,eAAO,MAAM,cAAc,GAAI,OAAO,OAAO,KAAG,OAmB/C,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,KAAK,KAAK,CAAC,SAAS,CAAC,qBAAqB,GAAG,KAAK,CAAC,SAAS,CAAC,gBAAgB;;;;CAK7E,CAAC"}
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.normalizeDocument = exports.normalizeValue = void 0;
7
+ const firebase_admin_1 = __importDefault(require("firebase-admin"));
8
+ const normalizeValue = (value) => {
9
+ if (value === null || value === undefined)
10
+ return value;
11
+ if (value instanceof firebase_admin_1.default.firestore.Timestamp) {
12
+ return value.toDate().toISOString();
13
+ }
14
+ if (value instanceof firebase_admin_1.default.firestore.GeoPoint) {
15
+ return { latitude: value.latitude, longitude: value.longitude };
16
+ }
17
+ if (value instanceof firebase_admin_1.default.firestore.DocumentReference)
18
+ return value.path;
19
+ if (Array.isArray(value))
20
+ return value.map(exports.normalizeValue);
21
+ if (typeof value === 'object') {
22
+ return Object.fromEntries(Object.entries(value).map(([k, v]) => [
23
+ k,
24
+ (0, exports.normalizeValue)(v),
25
+ ]));
26
+ }
27
+ return value;
28
+ };
29
+ exports.normalizeValue = normalizeValue;
30
+ const normalizeDocument = (doc) => ({
31
+ id: doc.id,
32
+ path: doc.ref.path,
33
+ data: (0, exports.normalizeValue)(doc.data()),
34
+ });
35
+ exports.normalizeDocument = normalizeDocument;
36
+ //# sourceMappingURL=normalize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize.js","sourceRoot":"","sources":["../../../src/tools/firestore/normalize.ts"],"names":[],"mappings":";;;;;;AAAA,oEAAmC;AAE5B,MAAM,cAAc,GAAG,CAAC,KAAc,EAAW,EAAE;IACxD,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IACxD,IAAI,KAAK,YAAY,wBAAK,CAAC,SAAS,CAAC,SAAS,EAAE,CAAC;QAC/C,OAAO,KAAK,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE,CAAC;IACtC,CAAC;IACD,IAAI,KAAK,YAAY,wBAAK,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC;QAC9C,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,CAAC;IAClE,CAAC;IACD,IAAI,KAAK,YAAY,wBAAK,CAAC,SAAS,CAAC,iBAAiB;QAAE,OAAO,KAAK,CAAC,IAAI,CAAC;IAC1E,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC,sBAAc,CAAC,CAAC;IAC3D,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,MAAM,CAAC,WAAW,CACvB,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAqB,EAAE,CAAC;YACvD,CAAC;YACD,IAAA,sBAAc,EAAC,CAAC,CAAC;SAClB,CAAC,CACH,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AAnBW,QAAA,cAAc,kBAmBzB;AAEK,MAAM,iBAAiB,GAAG,CAC/B,GAA6E,EAC7E,EAAE,CAAC,CAAC;IACJ,EAAE,EAAE,GAAG,CAAC,EAAE;IACV,IAAI,EAAE,GAAG,CAAC,GAAG,CAAC,IAAI;IAClB,IAAI,EAAE,IAAA,sBAAc,EAAC,GAAG,CAAC,IAAI,EAAE,CAAC;CACjC,CAAC,CAAC;AANU,QAAA,iBAAiB,qBAM3B"}
@@ -0,0 +1,34 @@
1
+ import { Tool } from '@modelcontextprotocol/sdk/types.js';
2
+ import { Effect } from 'effect';
3
+ import { AccessService } from '../../access';
4
+ import { ConfigService } from '../../config';
5
+ import { FirebaseService } from '../../firebase';
6
+ import { QueryFilter, QueryOrderBy } from './types';
7
+ declare const FirestoreQueryError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
8
+ readonly _tag: "FirestoreQueryError";
9
+ } & Readonly<A>;
10
+ export declare class FirestoreQueryError extends FirestoreQueryError_base<{
11
+ readonly message: string;
12
+ readonly cause?: unknown;
13
+ }> {
14
+ }
15
+ export declare const QUERY_COLLECTION: "query_collection";
16
+ export interface QueryCollectionArgs {
17
+ collection: string;
18
+ filters?: QueryFilter[];
19
+ orderBy?: QueryOrderBy[];
20
+ limit?: number;
21
+ select?: string[];
22
+ startAfter?: string;
23
+ }
24
+ export declare const queryCollectionDefinition: Tool;
25
+ export declare const queryCollection: (input: QueryCollectionArgs) => Effect.Effect<{
26
+ documents: {
27
+ id: string;
28
+ path: string;
29
+ data: unknown;
30
+ }[];
31
+ nextPageCursor: string | null;
32
+ }, import("../../access").AccessDeniedError | FirestoreQueryError, ConfigService | AccessService | FirebaseService>;
33
+ export {};
34
+ //# sourceMappingURL=query_collection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query_collection.d.ts","sourceRoot":"","sources":["../../../src/tools/firestore/query_collection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AAC1D,OAAO,EAAQ,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEtC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAIL,WAAW,EACX,YAAY,EACb,MAAM,SAAS,CAAC;;;;AAEjB,qBAAa,mBAAoB,SAAQ,yBAEvC;IACA,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC;CAAG;AAEL,eAAO,MAAM,gBAAgB,EAAG,kBAA2B,CAAC;AAE5D,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC;IACxB,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,yBAAyB,EAAE,IAuCvC,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,OAAO,mBAAmB;;;;;;;mHA8DtD,CAAC"}
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.queryCollection = exports.queryCollectionDefinition = exports.QUERY_COLLECTION = exports.FirestoreQueryError = void 0;
4
+ const effect_1 = require("effect");
5
+ const access_1 = require("../../access");
6
+ const config_1 = require("../../config");
7
+ const firebase_1 = require("../../firebase");
8
+ const types_1 = require("./types");
9
+ class FirestoreQueryError extends effect_1.Data.TaggedError('FirestoreQueryError') {
10
+ }
11
+ exports.FirestoreQueryError = FirestoreQueryError;
12
+ exports.QUERY_COLLECTION = 'query_collection';
13
+ exports.queryCollectionDefinition = {
14
+ name: exports.QUERY_COLLECTION,
15
+ description: 'Query a Firestore collection with filters, ordering, and a limit. Supports cursor-based pagination via startAfter.',
16
+ inputSchema: {
17
+ type: 'object',
18
+ properties: {
19
+ collection: {
20
+ type: 'string',
21
+ description: "Collection path, e.g. 'users' or 'users/123/posts'",
22
+ },
23
+ filters: {
24
+ type: 'array',
25
+ description: 'Optional list of where-clause filters',
26
+ items: types_1.FILTER_SCHEMA_ITEM,
27
+ },
28
+ orderBy: {
29
+ type: 'array',
30
+ description: 'Optional ordering of results',
31
+ items: types_1.ORDER_BY_SCHEMA_ITEM,
32
+ },
33
+ limit: {
34
+ type: 'number',
35
+ description: 'Max number of documents to return',
36
+ },
37
+ select: {
38
+ type: 'array',
39
+ items: { type: 'string' },
40
+ description: 'Optional list of field paths to return. Omit to return all fields.',
41
+ },
42
+ startAfter: {
43
+ type: 'string',
44
+ description: 'Document ID to start after for pagination. Use the nextPageCursor value returned from a previous call.',
45
+ },
46
+ },
47
+ required: ['collection'],
48
+ },
49
+ };
50
+ const queryCollection = (input) => effect_1.Effect.gen(function* () {
51
+ const access = yield* access_1.AccessService;
52
+ yield* access.check(input.collection);
53
+ const { config } = yield* config_1.ConfigService;
54
+ const { firestore } = yield* firebase_1.FirebaseService;
55
+ const maxLimit = config.firestore.maxCollectionReadSize;
56
+ const limit = Math.min(input.limit ?? maxLimit, maxLimit);
57
+ const cursorSnap = input.startAfter
58
+ ? yield* effect_1.Effect.tryPromise({
59
+ try: () => firestore()
60
+ .collection(input.collection)
61
+ .doc(input.startAfter)
62
+ .get(),
63
+ catch: (cause) => new FirestoreQueryError({
64
+ message: `Failed to fetch cursor document: ${input.startAfter}`,
65
+ cause,
66
+ }),
67
+ })
68
+ : null;
69
+ const snapshot = yield* effect_1.Effect.tryPromise({
70
+ try: () => {
71
+ let query = firestore().collection(input.collection);
72
+ if (input.select?.length) {
73
+ query = query.select(...input.select);
74
+ }
75
+ for (const filter of input.filters ?? []) {
76
+ query = query.where(filter.field, filter.operator, filter.value);
77
+ }
78
+ for (const order of input.orderBy ?? []) {
79
+ query = query.orderBy(order.field, order.direction ?? 'asc');
80
+ }
81
+ if (cursorSnap) {
82
+ query = query.startAfter(cursorSnap);
83
+ }
84
+ return query.limit(limit).get();
85
+ },
86
+ catch: (cause) => new FirestoreQueryError({
87
+ message: `Failed to query collection: ${input.collection}`,
88
+ cause,
89
+ }),
90
+ });
91
+ const documents = snapshot.docs.map(types_1.normalizeDocument);
92
+ const nextPageCursor = documents.length === limit ? documents[documents.length - 1].id : null;
93
+ return { documents, nextPageCursor };
94
+ });
95
+ exports.queryCollection = queryCollection;
96
+ //# sourceMappingURL=query_collection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query_collection.js","sourceRoot":"","sources":["../../../src/tools/firestore/query_collection.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import { Tool } from '@modelcontextprotocol/sdk/types.js';
2
+ import { Effect } from 'effect';
3
+ import { AccessService } from '../../access';
4
+ import { ConfigService } from '../../config';
5
+ import { FirebaseService } from '../../firebase';
6
+ import { QueryFilter, QueryOrderBy } from './types';
7
+ declare const FirestoreCollectionGroupQueryError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
8
+ readonly _tag: "FirestoreCollectionGroupQueryError";
9
+ } & Readonly<A>;
10
+ export declare class FirestoreCollectionGroupQueryError extends FirestoreCollectionGroupQueryError_base<{
11
+ readonly message: string;
12
+ readonly cause?: unknown;
13
+ }> {
14
+ }
15
+ export declare const QUERY_COLLECTION_GROUP: "query_collection_group";
16
+ export interface QueryCollectionGroupArgs {
17
+ collectionId: string;
18
+ filters?: QueryFilter[];
19
+ orderBy?: QueryOrderBy[];
20
+ limit?: number;
21
+ select?: string[];
22
+ startAfter?: string;
23
+ }
24
+ export declare const queryCollectionGroupDefinition: Tool;
25
+ export declare const queryCollectionGroup: (input: QueryCollectionGroupArgs) => Effect.Effect<{
26
+ documents: {
27
+ id: string;
28
+ path: string;
29
+ data: unknown;
30
+ }[];
31
+ nextPageCursor: string | null;
32
+ }, import("../../access").AccessDeniedError | FirestoreCollectionGroupQueryError, ConfigService | AccessService | FirebaseService>;
33
+ export {};
34
+ //# sourceMappingURL=query_collection_group.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query_collection_group.d.ts","sourceRoot":"","sources":["../../../src/tools/firestore/query_collection_group.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AAC1D,OAAO,EAAQ,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEtC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAIL,WAAW,EACX,YAAY,EACb,MAAM,SAAS,CAAC;;;;AAEjB,qBAAa,kCAAmC,SAAQ,wCAEtD;IACA,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC;CAAG;AAEL,eAAO,MAAM,sBAAsB,EAAG,wBAAiC,CAAC;AAExE,MAAM,WAAW,wBAAwB;IACvC,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC;IACxB,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,8BAA8B,EAAE,IAyC5C,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAI,OAAO,wBAAwB;;;;;;;kIA2DhE,CAAC"}
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.queryCollectionGroup = exports.queryCollectionGroupDefinition = exports.QUERY_COLLECTION_GROUP = exports.FirestoreCollectionGroupQueryError = void 0;
4
+ const effect_1 = require("effect");
5
+ const access_1 = require("../../access");
6
+ const config_1 = require("../../config");
7
+ const firebase_1 = require("../../firebase");
8
+ const types_1 = require("./types");
9
+ class FirestoreCollectionGroupQueryError extends effect_1.Data.TaggedError('FirestoreCollectionGroupQueryError') {
10
+ }
11
+ exports.FirestoreCollectionGroupQueryError = FirestoreCollectionGroupQueryError;
12
+ exports.QUERY_COLLECTION_GROUP = 'query_collection_group';
13
+ exports.queryCollectionGroupDefinition = {
14
+ name: exports.QUERY_COLLECTION_GROUP,
15
+ description: 'Query across all Firestore collections with the same name, regardless of their parent path. Use this to query data across multiple stores or parent documents at once. Check list_indexes first to confirm a collection-group-scoped index exists for any filters or ordering you plan to use.',
16
+ inputSchema: {
17
+ type: 'object',
18
+ properties: {
19
+ collectionId: {
20
+ type: 'string',
21
+ description: "The collection name to query across all parents, e.g. 'purchase_orders' or 'stock'.",
22
+ },
23
+ filters: {
24
+ type: 'array',
25
+ description: 'Optional list of where-clause filters',
26
+ items: types_1.FILTER_SCHEMA_ITEM,
27
+ },
28
+ orderBy: {
29
+ type: 'array',
30
+ description: 'Optional ordering of results. Requires a collection-group index.',
31
+ items: types_1.ORDER_BY_SCHEMA_ITEM,
32
+ },
33
+ limit: {
34
+ type: 'number',
35
+ description: 'Max number of documents to return',
36
+ },
37
+ select: {
38
+ type: 'array',
39
+ items: { type: 'string' },
40
+ description: 'Optional list of field paths to return. Omit to return all fields.',
41
+ },
42
+ startAfter: {
43
+ type: 'string',
44
+ description: 'Full document path to start after for pagination, e.g. "shared/stores_data/ABC/data/purchase_orders/51721". Use the path from the last document in the previous page.',
45
+ },
46
+ },
47
+ required: ['collectionId'],
48
+ },
49
+ };
50
+ const queryCollectionGroup = (input) => effect_1.Effect.gen(function* () {
51
+ const access = yield* access_1.AccessService;
52
+ yield* access.check(input.collectionId);
53
+ const { config } = yield* config_1.ConfigService;
54
+ const { firestore } = yield* firebase_1.FirebaseService;
55
+ const maxLimit = config.firestore.maxCollectionReadSize;
56
+ const limit = Math.min(input.limit ?? maxLimit, maxLimit);
57
+ // For collection group pagination, startAfter must be a full path
58
+ const cursorSnap = input.startAfter
59
+ ? yield* effect_1.Effect.tryPromise({
60
+ try: () => firestore().doc(input.startAfter).get(),
61
+ catch: (cause) => new FirestoreCollectionGroupQueryError({
62
+ message: `Failed to fetch cursor document: ${input.startAfter}`,
63
+ cause,
64
+ }),
65
+ })
66
+ : null;
67
+ const snapshot = yield* effect_1.Effect.tryPromise({
68
+ try: () => {
69
+ let query = firestore().collectionGroup(input.collectionId);
70
+ if (input.select?.length) {
71
+ query = query.select(...input.select);
72
+ }
73
+ for (const filter of input.filters ?? []) {
74
+ query = query.where(filter.field, filter.operator, filter.value);
75
+ }
76
+ for (const order of input.orderBy ?? []) {
77
+ query = query.orderBy(order.field, order.direction ?? 'asc');
78
+ }
79
+ if (cursorSnap) {
80
+ query = query.startAfter(cursorSnap);
81
+ }
82
+ return query.limit(limit).get();
83
+ },
84
+ catch: (cause) => new FirestoreCollectionGroupQueryError({
85
+ message: `Failed to query collection group: ${input.collectionId}`,
86
+ cause,
87
+ }),
88
+ });
89
+ const documents = snapshot.docs.map(types_1.normalizeDocument);
90
+ const nextPageCursor = documents.length === limit ? documents[documents.length - 1].path : null;
91
+ return { documents, nextPageCursor };
92
+ });
93
+ exports.queryCollectionGroup = queryCollectionGroup;
94
+ //# sourceMappingURL=query_collection_group.js.map