Skip to main content

A Python CLI tool for sending Firebase Cloud Messaging (FCM) notifications

Project description

Firebase Cloud Messaging (FCM) Notification Sender

fcm-icon

A Python CLI tool for sending Firebase Cloud Messaging (FCM) notifications using the Firebase Admin SDK.

PyPI version Python 3.9+ License: MIT

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:

  1. Go to Firebase Console
  2. Select your project
  3. Open Project Settings > Service Accounts.
  4. Click Generate New Private Key, then confirm by clicking Generate Key.
  5. Securely store the .json file 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 ~/.zshrc or ~/.bashrc for 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} and settings.json file 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 environment
  • credentials_info - Load/cache credentials JSON
  • project_id - Get project ID from credentials
  • service_account_email - Get service account email
  • initialize() - Initialize Firebase Admin SDK
  • get_access_token() - Retrieve current access token
  • show_info() - Display service account information
  • send_notification() - Send FCM notification
  • send_data_message() - Send data-only message

CLIHandler

Handles command-line interface:

  • create_parser() - Configure argument parser
  • handle_info() - Process --info command
  • handle_data_only() - Process --data-only command
  • handle_notification() - Process notification command
  • run() - 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:

📄 tests/README.md

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:

  1. Create a TestPyPI account
  2. 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-url is needed because TestPyPI doesn't have all dependencies (like firebase-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:

  1. Create a PyPI account
  2. 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 ~/.pypirc or use twine upload --username __token__ --password <your-token> dist/*

5. After Publishing

Users can install your package with:

pip install fcm-send

References

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fcm_send-0.1.0.tar.gz (21.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fcm_send-0.1.0-py3-none-any.whl (13.6 kB view details)

Uploaded Python 3

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

Hashes for fcm_send-0.1.0.tar.gz
Algorithm Hash digest
SHA256 50bfa932b358458dd7f77dd1a80a6c304225f7c42303c8124a2194459854803f
MD5 62561b94e7f103925e057a9242f62116
BLAKE2b-256 ea8bf3cfbe435172f7b7517d9182f26429ddd82b381ea4e579a96da44920490f

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for fcm_send-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 cb9b418b91deed07a59bf9f9c304112b6fe66d525bc6aea936e4e126ff62fc30
MD5 604333f9e88f7551f503415f7c86b4e6
BLAKE2b-256 c8e7ca7576106c48837da1754a48e22d0a27a302dbd91faa03206511d282a26c

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page