Skip to content

Commit 9de5ce0

Browse files
authored
Merge pull request #45 from gannonh/File-Upload-Support-for-Firebase-Storage-#37
2 parents b4017f6 + c970517 commit 9de5ce0

File tree

13 files changed

+4057
-827
lines changed

13 files changed

+4057
-827
lines changed

β€Ž.github/workflows/firebase-tests.ymlβ€Ž renamed to β€Ž.github/workflows/tests.ymlβ€Ž

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,21 @@ jobs:
2424
with:
2525
node-version: ${{ matrix.node-version }}
2626
cache: 'npm'
27-
- run: npm ci
27+
- name: Install dependencies
28+
run: |
29+
# Set environment variable to skip native Rollup modules
30+
echo "Setting ROLLUP_SKIP_LOAD_NATIVE_PLUGIN=true"
31+
export ROLLUP_SKIP_LOAD_NATIVE_PLUGIN=true
32+
33+
# Clean install with environment variable set
34+
ROLLUP_SKIP_LOAD_NATIVE_PLUGIN=true npm ci
35+
36+
# Verify Rollup installation
37+
echo "Checking Rollup installation..."
38+
ls -la node_modules/rollup/dist/
39+
40+
# Install Vitest explicitly to ensure it's properly installed
41+
ROLLUP_SKIP_LOAD_NATIVE_PLUGIN=true npm install -D vitest @vitest/coverage-v8
2842
2943
- name: Add format:check script if needed
3044
run: |
@@ -197,18 +211,47 @@ jobs:
197211
# Run build
198212
npm run build --if-present
199213
200-
# Run all tests with Vitest using emulator mode explicitly
201-
npm run test:emulator
202-
203-
# Store the test result
204-
TEST_RESULT=$?
214+
# Run all tests with coverage in emulator mode
215+
# Set environment variable to skip native Rollup modules
216+
export ROLLUP_SKIP_LOAD_NATIVE_PLUGIN=true
217+
export NODE_OPTIONS="--max-old-space-size=4096"
218+
219+
# Run tests with coverage and capture the output
220+
npm run test:coverage:emulator | tee test-output.log
221+
222+
# Extract thresholds from vitest.config.ts
223+
echo "Extracting coverage thresholds from vitest.config.ts..."
224+
BRANCH_THRESHOLD=$(grep -A 5 "thresholds:" vitest.config.ts | grep "branches:" | awk '{print $2}' | tr -d ',')
225+
FUNCTION_THRESHOLD=$(grep -A 5 "thresholds:" vitest.config.ts | grep "functions:" | awk '{print $2}' | tr -d ',')
226+
LINE_THRESHOLD=$(grep -A 5 "thresholds:" vitest.config.ts | grep "lines:" | awk '{print $2}' | tr -d ',')
227+
STATEMENT_THRESHOLD=$(grep -A 5 "thresholds:" vitest.config.ts | grep "statements:" | awk '{print $2}' | tr -d ',')
228+
229+
echo "Thresholds from vitest.config.ts:"
230+
echo "Branch coverage threshold: ${BRANCH_THRESHOLD}%"
231+
echo "Function coverage threshold: ${FUNCTION_THRESHOLD}%"
232+
echo "Line coverage threshold: ${LINE_THRESHOLD}%"
233+
echo "Statement coverage threshold: ${STATEMENT_THRESHOLD}%"
234+
235+
# Check if coverage thresholds are met
236+
if grep -q "ERROR: Coverage for branches" test-output.log; then
237+
echo "❌ Branch coverage does not meet threshold of ${BRANCH_THRESHOLD}%"
238+
exit 1
239+
elif grep -q "ERROR: Coverage for functions" test-output.log; then
240+
echo "❌ Function coverage does not meet threshold of ${FUNCTION_THRESHOLD}%"
241+
exit 1
242+
elif grep -q "ERROR: Coverage for lines" test-output.log; then
243+
echo "❌ Line coverage does not meet threshold of ${LINE_THRESHOLD}%"
244+
exit 1
245+
elif grep -q "ERROR: Coverage for statements" test-output.log; then
246+
echo "❌ Statement coverage does not meet threshold of ${STATEMENT_THRESHOLD}%"
247+
exit 1
248+
else
249+
echo "βœ… Coverage meets all thresholds"
250+
fi
205251
206252
# Kill the emulator process
207253
kill $EMULATOR_PID
208254
209-
# Return the test result
210-
exit $TEST_RESULT
211-
212255
publish:
213256
name: Publish to npm
214257
needs: test

β€ŽCHANGELOG.mdβ€Ž

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,28 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [1.3.3] - 2025-04-10
9+
10+
### Added
11+
12+
- New storage upload capabilities:
13+
- `storage_upload`: Upload files directly to Firebase Storage from text or base64 content
14+
- `storage_upload_from_url`: Upload files to Firebase Storage from external URLs
15+
- **Permanent public URLs** for uploaded files that don't expire and work with public storage rules
16+
- Support for direct local file path uploads - no need for Base64 conversion
17+
- Improved guidance for all MCP clients on file upload best practices
18+
- Automatic filename sanitization for better URL compatibility
19+
- Response formatting metadata for MCP clients to display user-friendly file upload information
20+
- Improved error handling for storage operations
21+
- Automatic content type detection for uploaded files
22+
23+
### Fixed
24+
25+
- Fixed response format issues with storage tools to comply with MCP protocol standards
26+
- Fixed image encoding issues to ensure uploaded images display correctly
27+
- Improved error handling for invalid base64 data
28+
- Enhanced MIME type detection for files uploaded from URLs
29+
830
## [1.3.2] - 2024-04-10
931

1032
### Added

β€ŽREADME.mdβ€Ž

Lines changed: 158 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,82 @@ The [Model Context Protocol (MCP)](https://github.com/modelcontextprotocol) is a
1616
- **Firestore**: Document database operations
1717
- **Storage**: File storage and retrieval
1818

19-
The server exposes Firebase services through MCP tools, making them accessible to LLM clients including [Claude Desktop](https://claude.ai/download), [Cursor](https://www.cursor.com/), [Roo Code](https://github.com/RooVetGit/Roo-Code), and [Cline](https://cline.bot/), while handling authentication and connection management.
19+
The server exposes Firebase services through MCP tools, making them accessible to LLM clients including [Claude Desktop](https://claude.ai/download), [Augment](https://docs.augmentcode.com/setup-augment/mcp#about-model-context-protocol-servers), [VS Code](https://code.visualstudio.com/docs/copilot/chat/mcp-servers), [Cursor](https://www.cursor.com/), and others, while handling authentication and connection management.
2020

21-
## πŸ”₯ New in v1.3.0: Collection Group Queries
21+
## πŸ”₯ New in v1.3.3: Storage Upload Features
2222

23-
Firebase MCP now supports querying sub-collections (collection groups) in Firestore! This allows you to query across all sub-collections with the same name, regardless of their parent document - making it easy to search across your entire database hierarchy with a single query. Perfect for cross-document searches, activity feeds, and unified dashboards.
23+
Firebase MCP now supports direct file uploads to Firebase Storage! Version 1.3.3 introduces two new tools:
2424

25-
## Setup
25+
- **`storage_upload`**: Upload files directly from text or base64 content with automatic content type detection
26+
- **`storage_upload_from_url`**: Import files from external URLs with a single command
27+
28+
Both tools provide **permanent public URLs** that don't expire, making it easier to share and access your uploaded files. They also include user-friendly response formatting to display file information and download links in a clean, readable format.
29+
30+
#### File Upload Options for MCP Clients
31+
32+
MCP clients can upload files to Firebase Storage in three ways:
33+
34+
1. **Direct Local File Path** (RECOMMENDED for binary files)
35+
```ts
36+
{
37+
`filePath`: `my-image.png`,
38+
`content`: `/path/to/local/image.png`
39+
}
40+
```
41+
The server will read the file, detect its content type, and upload it to Firebase Storage.
42+
43+
> ‼️ **Note for MCP Clients**: This method is strongly recommended for all file types, especially binary files like PDFs and images. Path-based uploads are faster and more reliable than base64 encoding, which often fails with large files. MCP clients are made aware of this recommendation via the tool description. ‼️
44+
45+
2. **Base64 Data URL** (For binary data)
46+
```ts
47+
{
48+
`filePath`: `my-image.png`,
49+
`content`: `data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...`
50+
}
51+
```
52+
The server will automatically detect the content type from the data URL prefix. This method works (most of the time) for small files but **clients may struggle with larger files due to the base64 encoding string length**.
53+
54+
3. **Plain Text** (For text files)
55+
```ts
56+
{
57+
`filePath`: `readme.md`,
58+
`content`: `# My README\n\nThis is a markdown file.`
59+
}
60+
```
61+
62+
The server handles all the necessary conversion and content type detection, making it easy for MCP clients to upload files without complex preprocessing.
63+
64+
#### Best Practices for File Uploads
65+
66+
When using this server with any MCP client, follow these best practices for file uploads:
67+
68+
1. **Use Direct File Paths**: Always use the full path to files on your system
69+
```ts
70+
{
71+
`filePath`: `financial_report.pdf`,
72+
`content`: `/Users/username/Downloads/report.pdf`
73+
}
74+
```
75+
76+
2. **URL-Based Uploads**: For files available online, use the `storage_upload_from_url` tool
77+
```ts
78+
{
79+
`filePath`: `financial_report.pdf`,
80+
`url`: `https://example.com/report.pdf`
81+
}
82+
```
83+
84+
3. **Text Extraction**: For text-based files, Claude can extract and upload the content directly
85+
```ts
86+
{
87+
`filePath`: `report_summary.txt`,
88+
`content`: `This quarterly report shows a 15% increase in revenue...`
89+
}
90+
```
91+
92+
> ‼️ **Important**: Document references (like `/document/123` or internal references) are not directly accessible by external tools. Always use the actual file path or URL for reliable uploads. ‼️
2693
27-
> The easiest way to install the Firebase MCP server is to simply feed your LLM client (like Cline) the [llms-install.md](./llms-install.md) file.
94+
## Setup
2895

2996
### 1. Firebase Configuration
3097

@@ -46,9 +113,8 @@ The server requires the following environment variables:
46113
Add the server configuration to your MCP settings file:
47114

48115
- Claude Desktop: `~/Library/Application Support/Claude/claude_desktop_config.json`
116+
- Augment: `~/Library/Application Support/Code/User/settings.json`
49117
- Cursor: `[project root]/.cursor/mcp.json`
50-
- Roo Code (VS Code Extension): (`~/Library/Application Support/Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/cline_mcp_settings.json`)
51-
- Cline (VS Code Extension): `~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`
52118

53119
MCP Servers can be installed manually or at runtime via npx (recommended). How you install determines your configuration:
54120

@@ -113,7 +179,7 @@ To make sure everything is working, simply prompt your client: `Please run throu
113179

114180
- `auth_get_user`: Get user details by ID or email
115181

116-
```typescript
182+
```ts
117183
{
118184
identifier: string // User ID or email address
119185
}
@@ -123,7 +189,7 @@ To make sure everything is working, simply prompt your client: `Please run throu
123189

124190
- `firestore_add_document`: Add a document to a collection
125191

126-
```typescript
192+
```ts
127193
{
128194
collection: string,
129195
data: object
@@ -132,7 +198,7 @@ To make sure everything is working, simply prompt your client: `Please run throu
132198

133199
- `firestore_list_collections`: List available collections
134200

135-
```typescript
201+
```ts
136202
{
137203
documentPath?: string, // Optional parent document path
138204
limit?: number, // Default: 20
@@ -142,7 +208,7 @@ To make sure everything is working, simply prompt your client: `Please run throu
142208

143209
- `firestore_list_documents`: List documents with optional filtering
144210

145-
```typescript
211+
```ts
146212
{
147213
collection: string,
148214
filters?: Array<{
@@ -157,7 +223,7 @@ To make sure everything is working, simply prompt your client: `Please run throu
157223

158224
- `firestore_get_document`: Get a specific document
159225

160-
```typescript
226+
```ts
161227
{
162228
collection: string,
163229
id: string
@@ -166,7 +232,7 @@ To make sure everything is working, simply prompt your client: `Please run throu
166232

167233
- `firestore_update_document`: Update an existing document
168234

169-
```typescript
235+
```ts
170236
{
171237
collection: string,
172238
id: string,
@@ -176,7 +242,7 @@ To make sure everything is working, simply prompt your client: `Please run throu
176242

177243
- `firestore_delete_document`: Delete a document
178244

179-
```typescript
245+
```ts
180246
{
181247
collection: string,
182248
id: string
@@ -185,7 +251,7 @@ To make sure everything is working, simply prompt your client: `Please run throu
185251

186252
- `firestore_query_collection_group`: Query documents across all sub-collections πŸ†•
187253

188-
```typescript
254+
```ts
189255
{
190256
collectionId: string, // The collection ID to query across all documents
191257
filters?: Array<{ // Optional filters
@@ -206,7 +272,7 @@ To make sure everything is working, simply prompt your client: `Please run throu
206272

207273
- `storage_list_files`: List files in a directory
208274

209-
```typescript
275+
```ts
210276
{
211277
directoryPath?: string, // Optional path, defaults to root
212278
pageSize?: number, // Number of items per page, defaults to 10
@@ -216,12 +282,72 @@ To make sure everything is working, simply prompt your client: `Please run throu
216282

217283
- `storage_get_file_info`: Get file metadata and download URL
218284

219-
```typescript
285+
```ts
220286
{
221287
filePath: string // Path to the file in storage
222288
}
223289
```
224290

291+
- `storage_upload`: Upload a file to Firebase Storage from content (text, base64, etc.)
292+
293+
```ts
294+
{
295+
filePath: string, // The destination path in Firebase Storage
296+
content: string, // The file content (text or base64 encoded data)
297+
contentType?: string, // Optional MIME type. If not provided, it will be inferred
298+
metadata?: Record<string, any> // Optional additional metadata
299+
}
300+
```
301+
302+
- `storage_upload_from_url`: Upload a file to Firebase Storage from an external URL
303+
304+
```ts
305+
{
306+
filePath: string, // The destination path in Firebase Storage
307+
url: string, // The source URL to download from
308+
contentType?: string, // Optional MIME type. If not provided, it will be inferred from response headers
309+
metadata?: Record<string, any> // Optional additional metadata
310+
}
311+
```
312+
313+
### Response Formatting for MCP Clients
314+
315+
When displaying responses from the Firebase MCP server, clients should format the responses in a user-friendly way. This is especially important for storage operations where users benefit from seeing file information in a structured format with clickable links.
316+
317+
#### Example: Formatting Storage Upload Responses
318+
319+
Raw response from `storage_upload` or `storage_upload_from_url`:
320+
```json
321+
{
322+
"name": "example.txt",
323+
"size": "1024",
324+
"contentType": "text/plain",
325+
"updated": "2025-04-10T22:37:10.290Z",
326+
"downloadUrl": "https://storage.googleapis.com/bucket/example.txt?token..."
327+
}
328+
```
329+
330+
Recommended client formatting:
331+
```markdown
332+
## File Successfully Uploaded! πŸ“
333+
334+
Your file has been uploaded to Firebase Storage:
335+
336+
**File Details:**
337+
- **Name:** example.txt
338+
- **Size:** 1024 bytes
339+
- **Type:** text/plain
340+
- **Last Updated:** April 10, 2025 at 22:37:10 UTC
341+
342+
**[Click here to download your file](https://storage.googleapis.com/bucket/example.txt?token...)**
343+
```
344+
345+
This formatting provides a better user experience by:
346+
1. Clearly indicating success with a descriptive heading
347+
2. Organizing file details in an easy-to-read format
348+
3. Providing a clickable download link
349+
4. Using appropriate formatting and emoji for visual appeal
350+
225351
## Development
226352

227353
### Building
@@ -250,22 +376,33 @@ The project uses Vitest for testing. Tests can be run against Firebase emulators
250376
3. **Run Tests**
251377

252378
```bash
379+
# Run tests with emulator
253380
npm run test:emulator
381+
382+
# Run tests with coverage report
383+
npm run test:coverage
384+
385+
# Run tests with coverage report in emulator mode
386+
npm run test:coverage:emulator
254387
```
255388

389+
The coverage reports will be generated in the `coverage` directory. The project aims to maintain at least 90% code coverage across all metrics (lines, statements, functions, and branches).
390+
256391
### Architecture
257392

258393
The server is structured into three main components:
259394

260395
```
261396
src/
262-
β”œβ”€β”€ index.ts # Server entry point
397+
β”œβ”€β”€ .github/workflows/ # CI workflows
398+
β”œβ”€β”€ index.ts # Server entry point
399+
β”œβ”€β”€ __tests__ # Tests
263400
└── lib/
264401
└── firebase/
265-
β”œβ”€β”€ authClient.ts # Authentication operations
402+
β”œβ”€β”€ authClient.ts # Authentication operations
266403
β”œβ”€β”€ firebaseConfig.ts # Firebase configuration
267-
β”œβ”€β”€ firestoreClient.ts # Firestore operations
268-
└── storageClient.ts # Storage operations
404+
β”œβ”€β”€ firestoreClient.ts # Firestore operations
405+
└── storageClient.ts # Storage operations
269406
```
270407

271408
Each client module implements specific Firebase service operations and exposes them as MCP tools.

0 commit comments

Comments
Β (0)