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
Using Poetry (Recommended)
This project uses Poetry for dependency management and packaging.
Add to your project:
poetry add fcm-send
Install globally:
pipx install fcm-send
Using pip
pip install fcm-send
Using pipx (Isolated CLI)
pipx installs the CLI in an isolated environment, avoiding dependency conflicts:
# Install pipx if you don't have it
pip install pipx
# macOS: Using brew
brew install pipx
pipx ensurepath
# Install fcm-send
pipx install fcm-send
From Source (Development)
git clone https://github.com/mfdeveloper/firebase_cloud_messaging_cli.git
cd firebase_cloud_messaging_cli
# Install with Poetry (recommended)
poetry install
# Run the CLI
poetry run fcm-send --version
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. Install Poetry
If you don't have Poetry installed:
# macOS / Linux
curl -sSL https://install.python-poetry.org | python3 -
# Alternatively, create and activate virtual environment
python3 -m venv .venv
source .venv/bin/activate
# And using pipx
pipx install poetry
Note: After installation, ensure Poetry is in your PATH. See Poetry Installation.
2. Set Up the Project
cd <project-root>
# Install dependencies (creates .venv automatically)
poetry install
# Activate the virtual environment
poetry shell
Note: Poetry automatically creates a
.venvfolder in your project (configured inpoetry.toml).
3. 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
Running with Poetry
Before running commands, ensure you're in the Poetry environment:
cd <project-root>
# Option 1: Activate shell
poetry shell
fcm-send --info
# Option 2: Run directly with "poetry run"
poetry run fcm-send --info
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 # Poetry configuration
├── poetry.toml # Poetry settings (in-project venv)
├── poetry.lock # Locked dependencies
├── LICENSE # MIT License
└── README.md # This file
Installation for Development
Using Poetry (Recommended)
git clone https://github.com/mfdeveloper/firebase_cloud_messaging_cli.git
cd firebase_cloud_messaging_cli
# Install all dependencies (creates .venv automatically)
poetry install
# Activate the virtual environment
poetry shell
# Or run commands directly
poetry run fcm-send --version
poetry run pytest
Using pip (Alternative)
git clone https://github.com/mfdeveloper/firebase_cloud_messaging_cli.git
cd firebase_cloud_messaging_cli
# Create and activate virtual environment
python3 -m venv .venv
source .venv/bin/activate
# Install from requirements.txt
pip install -r requirements.txt
# Or install the package in editable mode
pip install -e .
Unit Testing
The project includes a comprehensive test suite with 58 unit tests achieving 97% code coverage.
# Run all tests
poetry run pytest
# Run with coverage terminal report
poetry run pytest --cov=src/fcm_send --cov-report=term-missing
# Run tests with coverage (HTML report)
poetry run 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
# Lint source package only
poetry run pylint src/fcm_send/
# Lint source package and tests
poetry run pylint src/fcm_send/ tests/
# Lint tests only
poetry run 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
poetry run pylint src/fcm_send/ tests/
Publishing to PyPI
This project uses Poetry for building and publishing packages.
1. Update Version
Update the version in pyproject.toml:
[tool.poetry]
version = "0.2.0"
Or use Poetry's version command:
# Bump patch version (0.1.0 → 0.1.1)
poetry version patch
# Bump minor version (0.1.0 → 0.2.0)
poetry version minor
# Bump major version (0.1.0 → 1.0.0)
poetry version major
2. Build the Package
# Build both sdist and wheel
poetry 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
- Generate an API token at TestPyPI API Tokens
- Add the token as a GitHub secret:
- Go to your repository Settings → Secrets and variables → Actions
- Create a new secret named
TEST_PYPI_API_TOKENwith your token
Option B: Manual Upload with Poetry
# Configure TestPyPI repository
poetry config repositories.testpypi https://test.pypi.org/legacy/
# Set your TestPyPI token
poetry config pypi-token.testpypi pypi-XXXXXXXXXXXX
# Publish to TestPyPI
poetry publish --repository testpypi
# Alternatively, you can use regular "Twine" for publishing
twine upload --repository testpypi dist/*
# Test installation from TestPyPI
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 -a pypi-0.1.0 -m "Release version 0.1.0" git push origin pypi-0.1.0
One-time Setup:
- Create a PyPI account
- Generate an API token at PyPI API Tokens
- Add the token as a GitHub secret:
- Go to your repository Settings → Secrets and variables → Actions
- Create a new secret named
PYPI_API_TOKENwith your token
Option B: Manual Upload with Poetry
# Set your PyPI token
poetry config pypi-token.pypi pypi-XXXXXXXXXXXX
# Publish to PyPI
poetry publish
# Alternatively, you can use regular "Twine" for publishing
twine upload dist/*
5. After Publishing
Users can install your package with:
poetry add fcm-send
# or
pip install fcm-send
Poetry Commands Reference
| Command | Description |
|---|---|
poetry install |
Install all dependencies |
poetry install --extras dev |
Install with dev dependencies |
poetry shell |
Activate the virtual environment |
poetry run <cmd> |
Run a command in the virtual environment |
poetry add <pkg> |
Add a dependency |
poetry add --group dev <pkg> |
Add a dev dependency |
poetry remove <pkg> |
Remove a dependency |
poetry update |
Update dependencies to latest versions |
poetry lock |
Update poetry.lock without installing |
poetry build |
Build sdist and wheel |
poetry publish |
Publish to PyPI |
poetry version <rule> |
Bump version (patch, minor, major) |
poetry show |
Show installed packages |
poetry env info |
Show virtualenv info |
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.1.tar.gz.
File metadata
- Download URL: fcm_send-0.1.1.tar.gz
- Upload date:
- Size: 16.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cee605f26c7a54fffa38030aa089cd11a31b81479c39040118431827b260a55d
|
|
| MD5 |
15718b76ef1f0aaf165379db992ca696
|
|
| BLAKE2b-256 |
60be42b966b6e2c3860c308073f9ffe3016dadf8c713abe027dd96396b4dd56e
|
Provenance
The following attestation bundles were made for fcm_send-0.1.1.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.1.tar.gz -
Subject digest:
cee605f26c7a54fffa38030aa089cd11a31b81479c39040118431827b260a55d - Sigstore transparency entry: 769072696
- Sigstore integration time:
-
Permalink:
mfdeveloper/firebase_cloud_messaging_cli@204a300cf9e3f8b6c40b6adb288419b3afbee5af -
Branch / Tag:
refs/tags/pypi-0.1.1 - Owner: https://github.com/mfdeveloper
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@204a300cf9e3f8b6c40b6adb288419b3afbee5af -
Trigger Event:
push
-
Statement type:
File details
Details for the file fcm_send-0.1.1-py3-none-any.whl.
File metadata
- Download URL: fcm_send-0.1.1-py3-none-any.whl
- Upload date:
- Size: 14.3 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 |
78f39310dbb780e3bce3d7b7c11ecd067e836013e3c4b2237feeb73402291e9e
|
|
| MD5 |
9e66cf9336071d191fafc9748d99e351
|
|
| BLAKE2b-256 |
6c15a226cb311e694f4915ac4030a119ff0049c58ef2e5e08b94c0b9fff34807
|
Provenance
The following attestation bundles were made for fcm_send-0.1.1-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.1-py3-none-any.whl -
Subject digest:
78f39310dbb780e3bce3d7b7c11ecd067e836013e3c4b2237feeb73402291e9e - Sigstore transparency entry: 769072725
- Sigstore integration time:
-
Permalink:
mfdeveloper/firebase_cloud_messaging_cli@204a300cf9e3f8b6c40b6adb288419b3afbee5af -
Branch / Tag:
refs/tags/pypi-0.1.1 - Owner: https://github.com/mfdeveloper
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@204a300cf9e3f8b6c40b6adb288419b3afbee5af -
Trigger Event:
push
-
Statement type: