A Python CLI tool for sending Firebase Cloud Messaging (FCM) notifications
Project description
Firebase Cloud Messaging (FCM) Notification Sender
A Python CLI tool for sending Firebase Cloud Messaging (FCM) notifications using the Firebase Admin SDK.
Installation
From PyPI (Recommended)
pip install fcm-send
From Source
git clone https://github.com/mfdeveloper/firebase_cloud_messaging_cli.git
cd fcm-send
pip install -e .
Features
- Send push notifications with title, body, and optional custom data
- Send data-only messages (silent notifications for background processing)
- Display service account info and retrieve the access token
- Dry-run mode to validate messages without sending
- Image support for rich notifications
Prerequisites
1. Set Up Virtual Environment
# Navigate to the firebase-notifications directory
cd <project-root>
# Create virtual environment
python3 -m venv venv
# Activate virtual environment
source venv/bin/activate
# Install dependencies
pip install -r requirements.txt
# or
pip install -e ".[dev]"
Note: Always activate the virtual environment before running the script.
2. Firebase Service Account
To authenticate a service account and authorize it to access Firebase services, you must generate a
service-account.json private key file in JSON format.
To generate and obtain one: a private key file for your service account:
- Go to Firebase Console
- Select your project
- Open Project Settings > Service Accounts.
- Click Generate New Private Key, then confirm by clicking Generate Key.
- Securely store the
.jsonfile containing the key.
For more details, see the Firebase documentation page: Initialize the SDK in non-Google environments
Environment Setup
Set the credentials environment variable pointing to your service account JSON file:
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/your-service-account.json
Tip: Add this to your
~/.zshrcor~/.bashrcfor persistence.
External Variables Reference
| Variable | Description |
|---|---|
GOOGLE_APPLICATION_CREDENTIALS |
Environment variable pointing to the service account .json file |
ACCESS_TOKEN |
Temporary Firebase access token (retrieved automatically by the SDK) |
FCM_TOKEN |
Device FCM registration token (obtained from mobile app) |
SERVICE_ACCOUNT_EMAIL |
The client_email field from the credentials JSON |
PROJECT_ID |
The project_id field from the credentials JSON |
Usage
Activate Virtual Environment
Before running any commands, activate the virtual environment:
cd <project-root>
source venv/bin/activate
To deactivate when done:
deactivate
Note: Using a code editor (such as VSCode or Cursor) a Python Virtual Environment can be loaded automatically using .vscode/settings.json configurations. Open this folder as
${workspaceFolder}andsettings.jsonfile mentioned above would be loaded!
Pass Google Credentials .json file
# Using CLI argument (takes precedence)
fcm-send --credentials-key-file ~/my-service-account.json --info
# Using environment variable
export GOOGLE_APPLICATION_CREDENTIALS=~/my-service-account.json
fcm-send --info
# Combining with other commands
fcm-send --credentials-key-file ~/sa.json --token FCM_TOKEN --title "Hi" --body "Test"
Show Service Account Info & Access Token
Display project details and retrieve the current Firebase Admin SDK access token:
fcm-send --info
# Or use --access-token alias
fcm-send --access-token
Output:
============================================================
Firebase Service Account Information
============================================================
PROJECT_ID: your-project-id
SERVICE_ACCOUNT_EMAIL: firebase-adminsdk@your-project.iam.gserviceaccount.com
CREDENTIALS_FILE: /path/to/service-account.json
============================================================
ACCESS_TOKEN (first 50 chars): ya29.c.c0ASRK0GYQ...
ACCESS_TOKEN (full):
ya29.c.c0ASRK0GYQ...
============================================================
Alternatively, display project details with current Google OAuth2 access token for using with FCM HTTP API access token. You can use it to perform a curl or any other way for HTTP requests on FCM API:
fcm-send --info-http
# Or use --access-token-http alias
fcm-send --access-token-http
Send a Simple Notification
fcm-send --token YOUR_FCM_TOKEN --title "Hello" --body "World"
Send Notification with Custom Data Payload
fcm-send --token YOUR_FCM_TOKEN \
--title "Order Update" \
--body "Your order has been shipped!" \
--data '{"order_id": "12345", "action": "open_order"}'
Send Notification with Image
fcm-send --token YOUR_FCM_TOKEN \
--title "Check this out" \
--body "New feature available!" \
--image "https://example.com/image.png"
Send Data-Only Message (Silent/Background)
Data-only messages don't show a visible notification but are delivered to your app for background processing:
fcm-send --token YOUR_FCM_TOKEN \
--data-only '{"action": "sync", "resource_id": "123"}'
Validate Message Without Sending (Dry Run)
Test your message configuration without actually sending it:
fcm-send --token YOUR_FCM_TOKEN \
--title "Test" \
--body "This is a test" \
--dry-run
Using as a Python Library
You can also use fcm-send as a library in your Python code:
from fcm_send import FCMClient
# Initialize client with credentials file
client = FCMClient("/path/to/service-account.json")
# Send a notification
response = client.send_notification(
fcm_token="device_fcm_token",
title="Hello",
body="World",
data={"key": "value"} # optional
)
print(f"Message ID: {response}")
# Send a data-only message
response = client.send_data_message(
fcm_token="device_fcm_token",
data={"action": "sync", "id": "123"}
)
CLI Options Reference
| Option | Description |
|---|---|
--credentials-key-file |
Path to Firebase service account .json file (CLI argument, takes over $GOOGLE_APPLICATION_CREDENTIALS) |
--info |
Display service account info and access token |
--info-http |
Display service account info and Google OAuth2 access token for FCM HTTP API |
--access-token |
Alias for --info |
--access-token-http |
Alias for --info-http |
--token <FCM_TOKEN> |
FCM registration token of the target device |
--title TEXT |
Notification title |
--body TEXT |
Notification body |
--data <JSON> |
Custom data payload as JSON string (e.g --data '{"action": "sync" }') |
--data-only <JSON> |
Send data-only message (no visible notification) |
--image URL |
Image URL for rich notifications |
--dry-run |
Validate message without sending |
Getting the FCM Token from Your App
To send notifications, you need the FCM registration token from your mobile app. In Android:
FirebaseMessaging.getInstance().token.addOnCompleteListener { task ->
if (task.isSuccessful) {
val token = task.result
Log.d("FCM", "Token: $token")
}
}
Reference: FCM: Retrieve the current registration token
Troubleshooting
"GOOGLE_APPLICATION_CREDENTIALS environment variable not set"
Make sure you've exported the environment variable:
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json
Reference: Firebase Admin SDK: Initialize in non-Google environments
"The FCM token is not registered"
This error occurs when:
- The app has been uninstalled from the device
- The token has expired or been rotated
- The token was generated for a different Firebase project
"The FCM token does not match the sender ID"
The FCM token was generated for a different Firebase project. Make sure you're using:
- The correct service account JSON for your project
- An FCM token generated by an app configured with the same Firebase project
Class Structure
The script is organized into two main classes:
FCMClient
Handles all Firebase operations:
credentials_path- Get credentials file path from environmentcredentials_info- Load/cache credentials JSONproject_id- Get project ID from credentialsservice_account_email- Get service account emailinitialize()- Initialize Firebase Admin SDKget_access_token()- Retrieve current access tokenshow_info()- Display service account informationsend_notification()- Send FCM notificationsend_data_message()- Send data-only message
CLIHandler
Handles command-line interface:
create_parser()- Configure argument parserhandle_info()- Process--infocommandhandle_data_only()- Process--data-onlycommandhandle_notification()- Process notification commandrun()- Main CLI execution logic
Development
Package Structure
firebase-cloud-messaging/
├── src/
│ └── fcm_send/
│ ├── __init__.py # Package exports (FCMClient, main, etc.)
│ ├── __main__.py # Enables `python -m fcm_send`
│ ├── __version__.py # Version from pyproject.toml ✨
│ ├── client.py # FCMClient class
│ ├── cli.py # CLIHandler and main() entry point
│ └── py.typed # PEP 561 marker for type checking
├── tests/ # Test suite (58 tests)
├── pyproject.toml # Package configuration (uses hatchling)
├── LICENSE # MIT License
├── README.md # This file
└── requirements.txt # Legacy dependencies file
Installation for Development
git clone https://github.com/mfdeveloper/firebase_cloud_messaging_cli.git
cd <project-root>
# Activate virtual environment
source venv/bin/activate
# Install dependencies
pip install -e ".[dev]"
Unit Testing
The project includes a comprehensive test suite with 58 unit tests achieving 97% code coverage.
# Run all tests
pytest
# Run with coverage terminal report
pytest --cov=src/fcm_send --cov-report=term-missing
# Run tests with coverage (HTML report)
pytest --cov=src/fcm_send --cov-report=html
For detailed testing documentation, including test breakdown, fixtures, and mocking strategy, see:
Code Linting
This project uses Pylint for static code analysis. The configuration is defined in .pylintrc.
Running Pylint
# Activate virtual environment
source venv/bin/activate
# Lint source package only
pylint src/fcm_send/
# Lint source package and tests
pylint src/fcm_send/ tests/
# Lint tests only
pylint tests/
Configuration
The .pylintrc file includes project-specific settings:
- Max line length: 120 characters
- Extended test names: Supports long descriptive test method names
- Disabled rules: Common pytest patterns (unused fixtures, redefined outer names) and CLI patterns (broad exception catching)
To check your current score:
# Output: Your code has been rated at X.XX/10
pylint src/fcm_send/ tests/
Publishing to PyPI
1. Update Package Metadata
Before publishing, edit pyproject.toml to set your author information and repository URLs:
authors = [
{ name = "Your Name", email = "your.email@example.com" }
]
[project.urls]
Homepage = "https://github.com/mfdeveloper/firebase_cloud_messaging_cli"
Repository = "https://github.com/mfdeveloper/firebase_cloud_messaging_cli"
2. Build the Package
# Activate virtual environment
source venv/bin/activate
# Build both sdist and wheel
python -m build
This creates distribution files in the dist/ directory:
dist/fcm_send-0.1.0.tar.gz(source distribution)dist/fcm_send-0.1.0-py3-none-any.whl(wheel)
3. Upload to Test PyPI (Recommended First)
Test your package on Test PyPI before publishing to the real PyPI.
Option A: Using GitHub Actions (Recommended)
This project includes a GitHub Actions workflow for automated publishing to TestPyPI.
Triggers:
- Manual: Go to Actions → "Publish to TestPyPI" → "Run workflow"
- Automatic: Push a version tag (e.g.,
git tag v0.1.0 && git push --tags)
One-time Setup:
- Create a TestPyPI account
- Configure Trusted Publishing:
- Click "Add a new pending publisher"
- PyPI Project Name:
fcm-send - Owner: Your GitHub username
- Repository:
firebase_cloud_messaging_cli - Workflow name:
publish-testpypi.yml - Environment:
testpypi
Option B: Manual Upload
# Upload to Test PyPI
twine upload --repository testpypi dist/*
# Test installation from Test PyPI
pip install --index-url https://test.pypi.org/simple/ \
--extra-index-url https://pypi.org/simple/ \
fcm-send
Note: The
--extra-index-urlis needed because TestPyPI doesn't have all dependencies (likefirebase-admin), so it falls back to the real PyPI for those.
4. Upload to PyPI
Once verified on TestPyPI, publish to the official PyPI.
Option A: Using GitHub Actions (Recommended)
This project includes a GitHub Actions workflow for automated publishing to PyPI.
Trigger:
- Push a
pypi-*tag:git tag pypi-0.1.0 git push origin pypi-0.1.0
One-time Setup:
- Create a PyPI account
- Configure Trusted Publishing:
- Click "Add a new pending publisher"
- PyPI Project Name:
fcm-send - Owner: Your GitHub username
- Repository:
firebase_cloud_messaging_cli - Workflow name:
publish-pypi.yml - Environment:
pypi
Option B: Manual Upload
twine upload dist/*
Note: You'll need to create an account at pypi.org and generate an API token. Store your token in
~/.pypircor usetwine upload --username __token__ --password <your-token> dist/*
5. After Publishing
Users can install your package with:
pip install fcm-send
References
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fcm_send-0.1.0.tar.gz.
File metadata
- Download URL: fcm_send-0.1.0.tar.gz
- Upload date:
- Size: 21.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
50bfa932b358458dd7f77dd1a80a6c304225f7c42303c8124a2194459854803f
|
|
| MD5 |
62561b94e7f103925e057a9242f62116
|
|
| BLAKE2b-256 |
ea8bf3cfbe435172f7b7517d9182f26429ddd82b381ea4e579a96da44920490f
|
Provenance
The following attestation bundles were made for fcm_send-0.1.0.tar.gz:
Publisher:
publish-pypi.yml on mfdeveloper/firebase_cloud_messaging_cli
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fcm_send-0.1.0.tar.gz -
Subject digest:
50bfa932b358458dd7f77dd1a80a6c304225f7c42303c8124a2194459854803f - Sigstore transparency entry: 767403294
- Sigstore integration time:
-
Permalink:
mfdeveloper/firebase_cloud_messaging_cli@42c4ae1437c0bf798bcef3e0f52d0ece94105f4c -
Branch / Tag:
refs/tags/pypi-0.1.0 - Owner: https://github.com/mfdeveloper
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@42c4ae1437c0bf798bcef3e0f52d0ece94105f4c -
Trigger Event:
push
-
Statement type:
File details
Details for the file fcm_send-0.1.0-py3-none-any.whl.
File metadata
- Download URL: fcm_send-0.1.0-py3-none-any.whl
- Upload date:
- Size: 13.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cb9b418b91deed07a59bf9f9c304112b6fe66d525bc6aea936e4e126ff62fc30
|
|
| MD5 |
604333f9e88f7551f503415f7c86b4e6
|
|
| BLAKE2b-256 |
c8e7ca7576106c48837da1754a48e22d0a27a302dbd91faa03206511d282a26c
|
Provenance
The following attestation bundles were made for fcm_send-0.1.0-py3-none-any.whl:
Publisher:
publish-pypi.yml on mfdeveloper/firebase_cloud_messaging_cli
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fcm_send-0.1.0-py3-none-any.whl -
Subject digest:
cb9b418b91deed07a59bf9f9c304112b6fe66d525bc6aea936e4e126ff62fc30 - Sigstore transparency entry: 767403310
- Sigstore integration time:
-
Permalink:
mfdeveloper/firebase_cloud_messaging_cli@42c4ae1437c0bf798bcef3e0f52d0ece94105f4c -
Branch / Tag:
refs/tags/pypi-0.1.0 - Owner: https://github.com/mfdeveloper
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@42c4ae1437c0bf798bcef3e0f52d0ece94105f4c -
Trigger Event:
push
-
Statement type: