@theihtisham/mcp-server-firebase 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +362 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +79 -0
- package/dist/services/firebase.d.ts +14 -0
- package/dist/services/firebase.js +163 -0
- package/dist/tools/auth.d.ts +3 -0
- package/dist/tools/auth.js +346 -0
- package/dist/tools/firestore.d.ts +3 -0
- package/dist/tools/firestore.js +802 -0
- package/dist/tools/functions.d.ts +3 -0
- package/dist/tools/functions.js +168 -0
- package/dist/tools/index.d.ts +10 -0
- package/dist/tools/index.js +30 -0
- package/dist/tools/messaging.d.ts +3 -0
- package/dist/tools/messaging.js +296 -0
- package/dist/tools/realtime-db.d.ts +4 -0
- package/dist/tools/realtime-db.js +271 -0
- package/dist/tools/storage.d.ts +3 -0
- package/dist/tools/storage.js +279 -0
- package/dist/tools/types.d.ts +11 -0
- package/dist/tools/types.js +3 -0
- package/dist/utils/cache.d.ts +16 -0
- package/dist/utils/cache.js +75 -0
- package/dist/utils/errors.d.ts +15 -0
- package/dist/utils/errors.js +94 -0
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.js +37 -0
- package/dist/utils/pagination.d.ts +28 -0
- package/dist/utils/pagination.js +75 -0
- package/dist/utils/validation.d.ts +22 -0
- package/dist/utils/validation.js +172 -0
- package/package.json +53 -0
- package/src/index.ts +94 -0
- package/src/services/firebase.ts +140 -0
- package/src/tools/auth.ts +375 -0
- package/src/tools/firestore.ts +931 -0
- package/src/tools/functions.ts +189 -0
- package/src/tools/index.ts +24 -0
- package/src/tools/messaging.ts +324 -0
- package/src/tools/realtime-db.ts +307 -0
- package/src/tools/storage.ts +314 -0
- package/src/tools/types.ts +10 -0
- package/src/utils/cache.ts +82 -0
- package/src/utils/errors.ts +110 -0
- package/src/utils/index.ts +4 -0
- package/src/utils/pagination.ts +105 -0
- package/src/utils/validation.ts +212 -0
- package/tests/cache.test.ts +139 -0
- package/tests/errors.test.ts +132 -0
- package/tests/firebase-service.test.ts +46 -0
- package/tests/pagination.test.ts +26 -0
- package/tests/tools.test.ts +226 -0
- package/tests/validation.test.ts +216 -0
- package/tsconfig.json +26 -0
- package/vitest.config.ts +15 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
# MCP Server Firebase
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/mcp-server-firebase)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://nodejs.org/)
|
|
6
|
+
[](https://www.typescriptlang.org/)
|
|
7
|
+
[](https://vitest.dev/)
|
|
8
|
+
|
|
9
|
+
> **Give AI assistants full Firebase superpowers -- manage Firestore, Auth, Storage, and more through natural language.**
|
|
10
|
+
|
|
11
|
+
An MCP (Model Context Protocol) server that provides AI assistants like Claude with complete access to Firebase services. Query Firestore, manage users, upload files, send notifications, and more -- all through conversational AI.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Features
|
|
16
|
+
|
|
17
|
+
- **37 tools** across 6 Firebase services
|
|
18
|
+
- **Firestore**: Query, CRUD, batch writes, transactions, aggregation, real-time listeners, schema inference
|
|
19
|
+
- **Auth**: Create, read, update, delete users, verify tokens, manage custom claims
|
|
20
|
+
- **Storage**: Upload, download, list, delete files, generate signed URLs
|
|
21
|
+
- **Realtime DB**: Full CRUD with advanced querying (orderBy, filters, limits)
|
|
22
|
+
- **Cloud Functions**: List, trigger, read logs
|
|
23
|
+
- **Messaging (FCM)**: Send notifications, multicast, topic subscriptions
|
|
24
|
+
- **Secure by default**: Input validation, NoSQL injection prevention, path traversal protection
|
|
25
|
+
- **Production-ready**: LRU caching, automatic pagination, connection pooling, structured errors with fix suggestions
|
|
26
|
+
- **Type-safe**: TypeScript strict mode with comprehensive Zod-style validation
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Demo Conversation
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
User: Show me all users who signed up this week
|
|
34
|
+
|
|
35
|
+
AI: I'll query the users collection, filtering by creation date.
|
|
36
|
+
|
|
37
|
+
[calls firestore_query with:
|
|
38
|
+
collection: "users"
|
|
39
|
+
where: [{ field: "createdAt", operator: ">=", value: "2026-04-04" }]
|
|
40
|
+
orderBy: [{ field: "createdAt", direction: "desc" }]
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
Found 23 users who signed up since April 4th. Here are the most recent:
|
|
44
|
+
|
|
45
|
+
| Name | Email | Joined |
|
|
46
|
+
|---------------|----------------------|---------------|
|
|
47
|
+
| Jane Smith | jane@example.com | Apr 9, 18:42 |
|
|
48
|
+
| Bob Wilson | bob@example.com | Apr 9, 15:11 |
|
|
49
|
+
| Alice Chen | alice@example.com | Apr 8, 22:03 |
|
|
50
|
+
... and 20 more.
|
|
51
|
+
|
|
52
|
+
User: Send a welcome notification to Jane
|
|
53
|
+
|
|
54
|
+
AI: I'll send a push notification to Jane's devices.
|
|
55
|
+
|
|
56
|
+
[calls auth_get_user with email: "jane@example.com"]
|
|
57
|
+
[calls messaging_send with:
|
|
58
|
+
topic: "user_jane_smith"
|
|
59
|
+
notification: { title: "Welcome!", body: "Thanks for joining us, Jane!" }
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
Notification sent successfully! Message ID: projects/myapp/messages/123456
|
|
63
|
+
|
|
64
|
+
User: What's the schema of the orders collection?
|
|
65
|
+
|
|
66
|
+
AI: Let me infer the schema by sampling documents.
|
|
67
|
+
|
|
68
|
+
[calls firestore_infer_schema with:
|
|
69
|
+
collection: "orders"
|
|
70
|
+
sampleSize: 20
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
Here's the inferred schema for `orders`:
|
|
74
|
+
|
|
75
|
+
| Field | Type | Presence | Nullable |
|
|
76
|
+
|-------------|----------|----------|----------|
|
|
77
|
+
| customerId | string | 100% | No |
|
|
78
|
+
| items | array | 100% | No |
|
|
79
|
+
| total | number | 100% | No |
|
|
80
|
+
| status | string | 95% | No |
|
|
81
|
+
| notes | string | 40% | Yes |
|
|
82
|
+
| shippedAt | timestamp| 60% | Yes |
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Installation
|
|
88
|
+
|
|
89
|
+
### Quick Start (Claude Desktop)
|
|
90
|
+
|
|
91
|
+
1. Install the package globally:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
npm install -g mcp-server-firebase
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
2. Add to your Claude Desktop config (`~/AppData/Roaming/Claude/claude_desktop_config.json` on Windows or `~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):
|
|
98
|
+
|
|
99
|
+
```json
|
|
100
|
+
{
|
|
101
|
+
"mcpServers": {
|
|
102
|
+
"firebase": {
|
|
103
|
+
"command": "mcp-server-firebase",
|
|
104
|
+
"env": {
|
|
105
|
+
"FIREBASE_SERVICE_ACCOUNT_PATH": "/path/to/service-account-key.json"
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### From Source
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
git clone https://github.com/your-username/mcp-server-firebase.git
|
|
116
|
+
cd mcp-server-firebase
|
|
117
|
+
npm install
|
|
118
|
+
npm run build
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Then configure Claude Desktop to use the built version:
|
|
122
|
+
|
|
123
|
+
```json
|
|
124
|
+
{
|
|
125
|
+
"mcpServers": {
|
|
126
|
+
"firebase": {
|
|
127
|
+
"command": "node",
|
|
128
|
+
"args": ["/path/to/mcp-server-firebase/dist/index.js"],
|
|
129
|
+
"env": {
|
|
130
|
+
"FIREBASE_SERVICE_ACCOUNT_PATH": "/path/to/service-account-key.json"
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Authentication Options
|
|
138
|
+
|
|
139
|
+
The server supports multiple ways to authenticate with Firebase:
|
|
140
|
+
|
|
141
|
+
| Method | Environment Variable | Description |
|
|
142
|
+
|--------|---------------------|-------------|
|
|
143
|
+
| Service Account JSON string | `FIREBASE_SERVICE_ACCOUNT_KEY` | Full JSON as a single-line string |
|
|
144
|
+
| Service Account file path | `FIREBASE_SERVICE_ACCOUNT_PATH` | Path to JSON key file |
|
|
145
|
+
| Individual fields | `FIREBASE_PROJECT_ID`, `FIREBASE_CLIENT_EMAIL`, `FIREBASE_PRIVATE_KEY` | Separate env vars |
|
|
146
|
+
| Application Default Credentials | _(none needed)_ | Works on GCP, with `gcloud auth` |
|
|
147
|
+
|
|
148
|
+
Optional configuration:
|
|
149
|
+
|
|
150
|
+
| Variable | Description | Default |
|
|
151
|
+
|----------|-------------|---------|
|
|
152
|
+
| `FIREBASE_STORAGE_BUCKET` | Cloud Storage bucket name | Auto-detected from project |
|
|
153
|
+
| `FIREBASE_DATABASE_URL` | Realtime Database URL | Auto-detected from project |
|
|
154
|
+
| `FIREBASE_FUNCTIONS_REGION` | Cloud Functions region | `us-central1` |
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## Tools Reference
|
|
159
|
+
|
|
160
|
+
### Firestore (13 tools)
|
|
161
|
+
|
|
162
|
+
| Tool | Description |
|
|
163
|
+
|------|-------------|
|
|
164
|
+
| `firestore_query` | Query collections with where, orderBy, limit, and pagination |
|
|
165
|
+
| `firestore_get_document` | Get a single document by path |
|
|
166
|
+
| `firestore_add_document` | Add a document with auto-generated ID |
|
|
167
|
+
| `firestore_set_document` | Create or overwrite a document (with optional merge) |
|
|
168
|
+
| `firestore_update_document` | Update specific fields (supports dot notation) |
|
|
169
|
+
| `firestore_delete_document` | Delete a document (optionally recursive) |
|
|
170
|
+
| `firestore_batch_write` | Atomic batch of up to 500 write operations |
|
|
171
|
+
| `firestore_transaction` | Read-then-write transaction for conditional updates |
|
|
172
|
+
| `firestore_list_collections` | List root collections or subcollections of a document |
|
|
173
|
+
| `firestore_list_subcollections` | List subcollections of a specific document |
|
|
174
|
+
| `firestore_aggregate_query` | Count, sum, average with optional filters |
|
|
175
|
+
| `firestore_listen_changes` | Listen for real-time changes (time-windowed) |
|
|
176
|
+
| `firestore_infer_schema` | Infer collection schema by sampling documents |
|
|
177
|
+
|
|
178
|
+
### Auth (7 tools)
|
|
179
|
+
|
|
180
|
+
| Tool | Description |
|
|
181
|
+
|------|-------------|
|
|
182
|
+
| `auth_create_user` | Create a new user (email, phone, password, etc.) |
|
|
183
|
+
| `auth_get_user` | Get user by UID, email, or phone number |
|
|
184
|
+
| `auth_list_users` | List users with pagination |
|
|
185
|
+
| `auth_update_user` | Update user properties |
|
|
186
|
+
| `auth_delete_user` | Delete a user |
|
|
187
|
+
| `auth_verify_token` | Verify and decode a Firebase ID token |
|
|
188
|
+
| `auth_set_custom_claims` | Set RBAC claims on a user |
|
|
189
|
+
|
|
190
|
+
### Storage (6 tools)
|
|
191
|
+
|
|
192
|
+
| Tool | Description |
|
|
193
|
+
|------|-------------|
|
|
194
|
+
| `storage_upload_file` | Upload a base64-encoded file |
|
|
195
|
+
| `storage_download_file` | Download a file (returns base64) |
|
|
196
|
+
| `storage_list_files` | List files with prefix filtering and pagination |
|
|
197
|
+
| `storage_delete_file` | Delete a file |
|
|
198
|
+
| `storage_get_signed_url` | Generate a time-limited signed URL |
|
|
199
|
+
| `storage_get_metadata` | Get file metadata (size, type, hashes) |
|
|
200
|
+
|
|
201
|
+
### Realtime Database (6 tools)
|
|
202
|
+
|
|
203
|
+
| Tool | Description |
|
|
204
|
+
|------|-------------|
|
|
205
|
+
| `rtdb_get_data` | Read data at a path |
|
|
206
|
+
| `rtdb_set_data` | Overwrite data at a path |
|
|
207
|
+
| `rtdb_push_data` | Push to a list with auto-generated key |
|
|
208
|
+
| `rtdb_update_data` | Update specific fields without full overwrite |
|
|
209
|
+
| `rtdb_remove_data` | Remove data at a path |
|
|
210
|
+
| `rtdb_query_data` | Query with orderBy, filters, and limits |
|
|
211
|
+
|
|
212
|
+
### Cloud Functions (3 tools)
|
|
213
|
+
|
|
214
|
+
| Tool | Description |
|
|
215
|
+
|------|-------------|
|
|
216
|
+
| `functions_list` | List deployed Cloud Functions |
|
|
217
|
+
| `functions_trigger` | Trigger an HTTP Cloud Function |
|
|
218
|
+
| `functions_get_logs` | Get recent logs for a function |
|
|
219
|
+
|
|
220
|
+
### Messaging (4 tools)
|
|
221
|
+
|
|
222
|
+
| Tool | Description |
|
|
223
|
+
|------|-------------|
|
|
224
|
+
| `messaging_send` | Send a notification to a device, topic, or condition |
|
|
225
|
+
| `messaging_send_multicast` | Send to up to 500 devices at once |
|
|
226
|
+
| `messaging_subscribe_topic` | Subscribe tokens to a topic |
|
|
227
|
+
| `messaging_unsubscribe_topic` | Unsubscribe tokens from a topic |
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## Query Builder Examples
|
|
232
|
+
|
|
233
|
+
### Firestore Query with Multiple Filters
|
|
234
|
+
|
|
235
|
+
```json
|
|
236
|
+
{
|
|
237
|
+
"collection": "orders",
|
|
238
|
+
"where": [
|
|
239
|
+
{ "field": "status", "operator": "==", "value": "active" },
|
|
240
|
+
{ "field": "total", "operator": ">", "value": 100 }
|
|
241
|
+
],
|
|
242
|
+
"orderBy": [{ "field": "total", "direction": "desc" }],
|
|
243
|
+
"limit": 50
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Pagination
|
|
248
|
+
|
|
249
|
+
```json
|
|
250
|
+
{
|
|
251
|
+
"collection": "products",
|
|
252
|
+
"limit": 100,
|
|
253
|
+
"pageToken": "cHJvZHVjdHMvcHJvZDEyMw=="
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Aggregation
|
|
258
|
+
|
|
259
|
+
```json
|
|
260
|
+
{
|
|
261
|
+
"collection": "orders",
|
|
262
|
+
"aggregations": [
|
|
263
|
+
{ "type": "count", "alias": "totalOrders" },
|
|
264
|
+
{ "type": "sum", "field": "total", "alias": "revenue" },
|
|
265
|
+
{ "type": "avg", "field": "total", "alias": "averageOrder" }
|
|
266
|
+
],
|
|
267
|
+
"where": [
|
|
268
|
+
{ "field": "status", "operator": "==", "value": "completed" }
|
|
269
|
+
]
|
|
270
|
+
}
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### Realtime DB Query
|
|
274
|
+
|
|
275
|
+
```json
|
|
276
|
+
{
|
|
277
|
+
"path": "/scores",
|
|
278
|
+
"orderBy": { "child": "score" },
|
|
279
|
+
"limitToLast": 10
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
## Security
|
|
286
|
+
|
|
287
|
+
This server is designed with security as a priority:
|
|
288
|
+
|
|
289
|
+
- **Service account key via environment variable** -- never hardcoded or committed
|
|
290
|
+
- **Input validation** on every parameter (paths, emails, UIDs, limits)
|
|
291
|
+
- **NoSQL injection prevention** -- field names starting with `$` or containing `.` are rejected
|
|
292
|
+
- **Path traversal prevention** -- storage paths with `..` or leading `/` are blocked
|
|
293
|
+
- **Field name restrictions** -- where clause fields starting with `__` are rejected
|
|
294
|
+
- **Custom claims size limit** -- enforces Firebase's 1000-byte limit
|
|
295
|
+
- **Reserved claim keys** -- prevents overwriting standard JWT claims
|
|
296
|
+
- **Batch operation limits** -- caps at 500 operations per batch
|
|
297
|
+
- **Signed URL limits** -- minimum 60s, maximum 7 days validity
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
## Development
|
|
302
|
+
|
|
303
|
+
```bash
|
|
304
|
+
# Install dependencies
|
|
305
|
+
npm install
|
|
306
|
+
|
|
307
|
+
# Build
|
|
308
|
+
npm run build
|
|
309
|
+
|
|
310
|
+
# Run tests
|
|
311
|
+
npm test
|
|
312
|
+
|
|
313
|
+
# Run tests in watch mode
|
|
314
|
+
npm run test:watch
|
|
315
|
+
|
|
316
|
+
# Run with coverage
|
|
317
|
+
npm run test:coverage
|
|
318
|
+
|
|
319
|
+
# Type check
|
|
320
|
+
npm run lint
|
|
321
|
+
|
|
322
|
+
# Run locally
|
|
323
|
+
npm run dev
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
### Project Structure
|
|
327
|
+
|
|
328
|
+
```
|
|
329
|
+
src/
|
|
330
|
+
index.ts # MCP server entry point
|
|
331
|
+
services/
|
|
332
|
+
firebase.ts # Firebase Admin SDK initialization
|
|
333
|
+
tools/
|
|
334
|
+
types.ts # ToolDefinition interface
|
|
335
|
+
index.ts # Tool aggregation and export
|
|
336
|
+
firestore.ts # 13 Firestore tools
|
|
337
|
+
auth.ts # 7 Auth tools
|
|
338
|
+
storage.ts # 6 Storage tools
|
|
339
|
+
realtime-db.ts # 6 Realtime DB tools
|
|
340
|
+
functions.ts # 3 Cloud Functions tools
|
|
341
|
+
messaging.ts # 4 FCM Messaging tools
|
|
342
|
+
utils/
|
|
343
|
+
index.ts # Re-exports
|
|
344
|
+
validation.ts # Input validation and sanitization
|
|
345
|
+
errors.ts # Error handling with fix suggestions
|
|
346
|
+
cache.ts # LRU cache with TTL and prefix invalidation
|
|
347
|
+
pagination.ts # Cursor-based pagination utilities
|
|
348
|
+
tests/
|
|
349
|
+
validation.test.ts # Validation tests (16 tests)
|
|
350
|
+
errors.test.ts # Error handling tests (10 tests)
|
|
351
|
+
cache.test.ts # Cache tests (12 tests)
|
|
352
|
+
pagination.test.ts # Pagination tests (4 tests)
|
|
353
|
+
tools.test.ts # Tool registration and validation tests (30+ tests)
|
|
354
|
+
firebase-service.test.ts # RTDB path validation tests
|
|
355
|
+
vitest.config.ts
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
---
|
|
359
|
+
|
|
360
|
+
## License
|
|
361
|
+
|
|
362
|
+
MIT
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
|
|
5
|
+
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
6
|
+
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
7
|
+
const firebase_js_1 = require("./services/firebase.js");
|
|
8
|
+
const index_js_2 = require("./tools/index.js");
|
|
9
|
+
const SERVER_NAME = 'mcp-server-firebase';
|
|
10
|
+
const SERVER_VERSION = '1.0.0';
|
|
11
|
+
async function main() {
|
|
12
|
+
// Validate Firebase configuration on startup
|
|
13
|
+
try {
|
|
14
|
+
(0, firebase_js_1.initializeFirebase)();
|
|
15
|
+
}
|
|
16
|
+
catch (err) {
|
|
17
|
+
console.error(`Failed to initialize Firebase: ${err.message}\n` +
|
|
18
|
+
'Ensure one of the following is set:\n' +
|
|
19
|
+
' - FIREBASE_SERVICE_ACCOUNT_KEY (JSON string)\n' +
|
|
20
|
+
' - FIREBASE_SERVICE_ACCOUNT_PATH (file path)\n' +
|
|
21
|
+
' - FIREBASE_PROJECT_ID + FIREBASE_CLIENT_EMAIL + FIREBASE_PRIVATE_KEY\n' +
|
|
22
|
+
' - Application Default Credentials (gcloud auth application-default login)');
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
// Create MCP server using the low-level Server class
|
|
26
|
+
// This allows us to register tools with raw JSON Schema input schemas
|
|
27
|
+
const server = new index_js_1.Server({ name: SERVER_NAME, version: SERVER_VERSION }, {
|
|
28
|
+
capabilities: {
|
|
29
|
+
tools: {},
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
// Build a map for quick tool lookup
|
|
33
|
+
const toolMap = new Map(index_js_2.allTools.map((t) => [t.name, t]));
|
|
34
|
+
// Handle tools/list requests
|
|
35
|
+
server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => {
|
|
36
|
+
return {
|
|
37
|
+
tools: index_js_2.allTools.map((tool) => ({
|
|
38
|
+
name: tool.name,
|
|
39
|
+
description: tool.description,
|
|
40
|
+
inputSchema: tool.inputSchema,
|
|
41
|
+
})),
|
|
42
|
+
};
|
|
43
|
+
});
|
|
44
|
+
// Handle tools/call requests
|
|
45
|
+
server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
46
|
+
const toolName = request.params.name;
|
|
47
|
+
const tool = toolMap.get(toolName);
|
|
48
|
+
if (!tool) {
|
|
49
|
+
return {
|
|
50
|
+
content: [{ type: 'text', text: `Error: Unknown tool "${toolName}".` }],
|
|
51
|
+
isError: true,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
const args = request.params.arguments ?? {};
|
|
56
|
+
const result = await tool.handler(args);
|
|
57
|
+
return {
|
|
58
|
+
content: [{ type: 'text', text: result }],
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
63
|
+
return {
|
|
64
|
+
content: [{ type: 'text', text: `Error: ${message}` }],
|
|
65
|
+
isError: true,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
// Connect using stdio transport
|
|
70
|
+
const transport = new stdio_js_1.StdioServerTransport();
|
|
71
|
+
await server.connect(transport);
|
|
72
|
+
console.error(`${SERVER_NAME} v${SERVER_VERSION} running on stdio`);
|
|
73
|
+
console.error(`Registered ${index_js_2.allTools.length} tools`);
|
|
74
|
+
}
|
|
75
|
+
main().catch((err) => {
|
|
76
|
+
console.error('Fatal error:', err);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
});
|
|
79
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as admin from 'firebase-admin';
|
|
2
|
+
export interface FirebaseConfig {
|
|
3
|
+
projectId?: string;
|
|
4
|
+
storageBucket?: string;
|
|
5
|
+
databaseURL?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function initializeFirebase(config?: FirebaseConfig): admin.app.App;
|
|
8
|
+
export declare function getFirestore(): admin.firestore.Firestore;
|
|
9
|
+
export declare function getAuth(): admin.auth.Auth;
|
|
10
|
+
export declare function getStorage(): admin.storage.Storage;
|
|
11
|
+
export declare function getRealtimeDb(): admin.database.Database;
|
|
12
|
+
export declare function getMessaging(): admin.messaging.Messaging;
|
|
13
|
+
export declare function getApp(): admin.app.App;
|
|
14
|
+
//# sourceMappingURL=firebase.d.ts.map
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.initializeFirebase = initializeFirebase;
|
|
40
|
+
exports.getFirestore = getFirestore;
|
|
41
|
+
exports.getAuth = getAuth;
|
|
42
|
+
exports.getStorage = getStorage;
|
|
43
|
+
exports.getRealtimeDb = getRealtimeDb;
|
|
44
|
+
exports.getMessaging = getMessaging;
|
|
45
|
+
exports.getApp = getApp;
|
|
46
|
+
const admin = __importStar(require("firebase-admin"));
|
|
47
|
+
const fs_1 = __importDefault(require("fs"));
|
|
48
|
+
let app = null;
|
|
49
|
+
function getServiceAccount() {
|
|
50
|
+
// 1. Try FIREBASE_SERVICE_ACCOUNT_KEY (JSON string)
|
|
51
|
+
const keyEnv = process.env['FIREBASE_SERVICE_ACCOUNT_KEY'];
|
|
52
|
+
if (keyEnv) {
|
|
53
|
+
try {
|
|
54
|
+
return JSON.parse(keyEnv);
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
throw new Error('FIREBASE_SERVICE_ACCOUNT_KEY is set but contains invalid JSON. ' +
|
|
58
|
+
'Provide the full service account JSON as a single-line string.');
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// 2. Try FIREBASE_SERVICE_ACCOUNT_PATH (file path)
|
|
62
|
+
const pathEnv = process.env['FIREBASE_SERVICE_ACCOUNT_PATH'];
|
|
63
|
+
if (pathEnv) {
|
|
64
|
+
try {
|
|
65
|
+
const content = fs_1.default.readFileSync(pathEnv, 'utf-8');
|
|
66
|
+
return JSON.parse(content);
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
throw new Error(`Failed to read service account file at "${pathEnv}": ${err.message}. ` +
|
|
70
|
+
'Ensure the path is correct and the file is valid JSON.');
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// 3. Try individual env vars
|
|
74
|
+
const projectId = process.env['FIREBASE_PROJECT_ID'];
|
|
75
|
+
const clientEmail = process.env['FIREBASE_CLIENT_EMAIL'];
|
|
76
|
+
const privateKey = process.env['FIREBASE_PRIVATE_KEY']?.replace(/\\n/g, '\n');
|
|
77
|
+
if (projectId && clientEmail && privateKey) {
|
|
78
|
+
return { projectId, clientEmail, privateKey };
|
|
79
|
+
}
|
|
80
|
+
// 4. Return undefined — rely on Application Default Credentials (ADC)
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
function initializeFirebase(config) {
|
|
84
|
+
if (app) {
|
|
85
|
+
return app;
|
|
86
|
+
}
|
|
87
|
+
const serviceAccount = getServiceAccount();
|
|
88
|
+
const initOptions = {};
|
|
89
|
+
if (serviceAccount) {
|
|
90
|
+
initOptions.credential = admin.credential.cert(serviceAccount);
|
|
91
|
+
}
|
|
92
|
+
// If no service account, ADC will be used (works on GCP, with gcloud CLI, etc.)
|
|
93
|
+
if (config?.projectId) {
|
|
94
|
+
initOptions.projectId = config.projectId;
|
|
95
|
+
}
|
|
96
|
+
else if (serviceAccount?.projectId) {
|
|
97
|
+
initOptions.projectId = serviceAccount.projectId;
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
const envProject = process.env['FIREBASE_PROJECT_ID'];
|
|
101
|
+
if (envProject) {
|
|
102
|
+
initOptions.projectId = envProject;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (config?.storageBucket) {
|
|
106
|
+
initOptions.storageBucket = config.storageBucket;
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
const envBucket = process.env['FIREBASE_STORAGE_BUCKET'];
|
|
110
|
+
if (envBucket) {
|
|
111
|
+
initOptions.storageBucket = envBucket;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (config?.databaseURL) {
|
|
115
|
+
initOptions.databaseURL = config.databaseURL;
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
const envDbUrl = process.env['FIREBASE_DATABASE_URL'];
|
|
119
|
+
if (envDbUrl) {
|
|
120
|
+
initOptions.databaseURL = envDbUrl;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
try {
|
|
124
|
+
app = admin.initializeApp(initOptions, 'mcp-firebase-server');
|
|
125
|
+
}
|
|
126
|
+
catch (err) {
|
|
127
|
+
// If app already exists, get it
|
|
128
|
+
if (err instanceof Error && err.message.includes('already exists')) {
|
|
129
|
+
app = admin.app('mcp-firebase-server');
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
throw err;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return app;
|
|
136
|
+
}
|
|
137
|
+
function getFirestore() {
|
|
138
|
+
const firebaseApp = initializeFirebase();
|
|
139
|
+
return firebaseApp.firestore();
|
|
140
|
+
}
|
|
141
|
+
function getAuth() {
|
|
142
|
+
const firebaseApp = initializeFirebase();
|
|
143
|
+
return firebaseApp.auth();
|
|
144
|
+
}
|
|
145
|
+
function getStorage() {
|
|
146
|
+
const firebaseApp = initializeFirebase();
|
|
147
|
+
return firebaseApp.storage();
|
|
148
|
+
}
|
|
149
|
+
function getRealtimeDb() {
|
|
150
|
+
const firebaseApp = initializeFirebase();
|
|
151
|
+
return firebaseApp.database();
|
|
152
|
+
}
|
|
153
|
+
function getMessaging() {
|
|
154
|
+
const firebaseApp = initializeFirebase();
|
|
155
|
+
return firebaseApp.messaging();
|
|
156
|
+
}
|
|
157
|
+
function getApp() {
|
|
158
|
+
if (!app) {
|
|
159
|
+
return initializeFirebase();
|
|
160
|
+
}
|
|
161
|
+
return app;
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=firebase.js.map
|