Skip to main content
Glama
Tahsine
by Tahsine

momo-mcp

FR — Un serveur MCP pour Mobile Money en Afrique de l'Ouest francophone. Une seule intégration pour permettre à un agent IA de faire des paiements Mobile Money — sans jongler entre les API de MTN MoMo, Moov Africa et Orange Money.

EN — An MCP server for Mobile Money in Francophone West Africa. One integration that lets an AI agent make Mobile Money payments — without juggling the MTN MoMo, Moov Africa, and Orange Money APIs.

FR — Aujourd'hui : MTN MoMo, multi-pays, sandbox et production. L'architecture est prête à accueillir Moov Africa et Orange Money sans réécrire le serveur.

EN — Today: MTN MoMo, multi-country, sandbox and production. The architecture is ready to host Moov Africa and Orange Money without rewriting the server.

⚠️ FR — Statut : à valider avant tout usage réel. Le serveur est testé (35 tests) et couvre la gestion d'erreurs, le retry et le multi-pays. Il n'a pas encore été audité ni utilisé avec de vrais fonds. En production, valide de bout en bout sur un compte marchand MTN avant de l'exposer.

⚠️ EN — Status: validate before any real use. The server is tested (35 tests) and covers error handling, retries, and multi-country routing. It has not been audited or used with real funds yet. In production, validate end-to-end on an MTN merchant account before exposing it.


Pourquoi / Why

FR — La plupart des outils IA génériques (Stripe, PayPal...) ne couvrent pas le Mobile Money, qui reste le moyen de paiement dominant en Afrique de l'Ouest. Les rares intégrations existantes ciblent surtout l'Afrique anglophone/de l'Est. momo-mcp comble ce vide pour les développeurs qui construisent des agents IA (assistants d'achat, bots de paiement, outils de facturation) sur ce marché.

EN — Most generic AI payment tools (Stripe, PayPal...) don't cover Mobile Money, still the dominant payment method in West Africa. The few existing integrations mostly target anglophone/East Africa. momo-mcp fills that gap for developers building AI agents (shopping assistants, payment bots, invoicing tools) in this market.

Related MCP server: M-Pesa MCP Server

Outils exposés / Tools

FR/EN — Chaque outil renvoie un dict JSON. Les succès contiennent ok: true ; les échecs ok: false + un objet error (voir Erreurs).

Outil / Tool

FR

EN

request_payment

Demande un paiement à un client (Request-to-Pay). Asynchrone : renvoie PENDING, à suivre avec check_payment_status.

Request a payment from a customer (Request-to-Pay). Async: returns PENDING, follow up with check_payment_status.

disburse_payment

Envoie de l'argent à un utilisateur (remboursement, salaire, payout). Sortie de fonds — annoté destructiveHint.

Send money to a user (refund, salary, payout). Fund outflow — annotated destructiveHint.

check_payment_status

Statut final d'un paiement ou transfert. Utilise kind : collection, disbursement ou auto.

Final status of a payment or transfer. Uses kind: collection, disbursement, or auto.

validate_account

Vérifie qu'un numéro a un compte Mobile Money actif avant d'y envoyer des fonds.

Check a number has an active Mobile Money account before sending funds.

Cycle d'un paiement / Payment flow

flowchart TD
    A["request_payment(phone_number, amount, currency, external_id, country)"] --> B["status: PENDING + reference_id"]
    B --> C["Customer confirms with PIN on their phone"]
    C --> D["check_payment_status(reference_id, kind=collection, country)"]
    D --> E{"status ?"}
    E -->|SUCCESSFUL| F["Settled - financial_transaction_id"]
    E -->|FAILED| G["Failed - reason"]
    E -->|PENDING| D

FR — request_payment est asynchrone : le statut reste PENDING tant que le client n'a pas confirmé. Interroge check_payment_status périodiquement (quelques secondes à quelques minutes).

EN — request_payment is async: status stays PENDING until the customer confirms. Poll check_payment_status (seconds to minutes).

Pays supportés / Supported countries

FR — Le paramètre country de chaque outil sélectionne la config MTN (header X-Target-Environment, devise, format de numéro). En sandbox, utilise benin (ou n'importe quel pays avec la devise EUR). En production, la devise locale du pays est requise.

EN — The country parameter on every tool selects the MTN config (X-Target-Environment header, currency, number format). In sandbox use benin (or any country with EUR). In production the country's local currency is required.

country

X-Target-Environment

Devise / Currency

Indicatif / Code

Exemple / Example

benin (défaut)

mtnbenin

XOF

229

2290197000000

ivory_coast

mtnivorycoast

XOF

225

2250700000000

cameroon

mtncameroon

XAF

237

237600000000

congo

mtncongo

XAF

242

242060000000

guinea

mtnguineaconakry

GNF

224

224620000000

ghana

mtnghana

GHS

233

233240000000

liberia

mtnliberia

LRD

231

231880000000

uganda

mtnuganda

UGX

256

256770000000

zambia

mtnzambia

ZMW

260

260970000000

rwanda

mtnrwanda

RWF

250

250788000000

south_africa

mtnsouthafrica

ZAR

27

27820000000

eswatini

mtnswaziland

SZL

268

26876000000

Alias acceptés / Accepted aliases : bj→benin, ci/cote_d_ivoire/côte d'ivoire→ivory_coast, cm→cameroon, congo_brazzaville→congo, guinea_conakry→guinea, swaziland→eswatini. Insensible à la casse, aux accents et aux espaces.

Installation

git clone https://github.com/Tahsine/momo-mcp.git
cd momo-mcp
python -m venv .venv && source .venv/bin/activate
pip install -e .

Configuration (sandbox MTN MoMo)

FR

  1. Crée un compte gratuit sur momodeveloper.mtn.com et abonne-toi aux produits Collection et Disbursement → récupère les deux clés d'abonnement.

  2. Provisionne ton API user + API key (une seule fois) :

    python scripts/provision_mtn.py --subscription-key TA_CLE_COLLECTION
  3. Copie .env.example vers .env et renseigne les valeurs. Voir le détail des variables dans .env.example.

EN

  1. Create a free account at momodeveloper.mtn.com, subscribe to Collection and Disbursement, and grab both subscription keys.

  2. Provision your API user + API key (once):

    python scripts/provision_mtn.py --subscription-key YOUR_COLLECTION_KEY
  3. Copy .env.example to .env and fill in the values. See .env.example for every variable.

Variable

Rôle / Purpose

MTN_SUBSCRIPTION_KEY_COLLECTION

Clé produit Collection (paiements entrants).

MTN_SUBSCRIPTION_KEY_DISBURSEMENT

Clé produit Disbursement (paiements sortants).

MTN_API_USER / MTN_API_KEY

Identifiants API (sandbox : via scripts/provision_mtn.py).

MTN_TARGET_ENVIRONMENT

sandbox pour tester ; toute autre valeur = production (résolu par pays).

MTN_BASE_URL

https://sandbox.momodeveloper.mtn.com ou https://proxy.momoapi.mtn.com.

MOMO_PAYER_MESSAGES

(Optionnel) Messages prédéfinis autorisés comme payer_message, séparés par `

MOMO_PAYER_MESSAGE_MAX_LEN

Longueur max d'un payer_message (défaut 60).

MOMO_MAX_DISBURSEMENT

Plafond par appel de disburse_payment. Vide/invalide/0 = absent. Obligatoire en production (sauf échappatoire).

MOMO_MAX_DAILY_DISBURSEMENT

Plafond quotidien (UTC) des sorties de fonds, persisté en SQLite. Vide/invalide/0 = absent. Obligatoire en production (sauf échappatoire).

MOMO_STATE_DB

Fichier du compteur quotidien (défaut $XDG_STATE_HOME/momo-mcp/state.db, repli ~/.local/state/momo-mcp/state.db).

MOMO_ALLOW_UNCAPPED_DISBURSEMENT

Échappatoire : accepte des sorties sans plafond en production. Valeur exacte true.

Utilisation avec un client MCP / Using an MCP client

Claude Code

claude mcp add momo-mcp -- python -m momo_mcp.server

Inspecteur MCP / MCP Inspector

npx @modelcontextprotocol/inspector python -m momo_mcp.server

Docker — le serveur parle le protocole MCP sur stdin/stdout :

docker build -t momo-mcp .
docker run -i --env-file .env momo-mcp

Référence des outils / Tool reference

request_payment

Param

Requis

Description

phone_number

oui

MSISDN du payeur, format international sans +.

amount

oui

Montant en chaîne (ex : "1000").

currency

oui

Devise du pays (XOF, XAF...) ou EUR en sandbox.

external_id

oui

Référence interne à ton système (ex : n° de commande).

payer_message

non

Message affiché au payeur.

payee_note

non

Note interne liée à la transaction.

provider

non

mtn_momo (seul disponible).

country

non

Pays MTN (défaut benin).

disburse_payment

Mêmes paramètres (message au lieu de payer_message/payee_note) — envoie des fonds sans confirmation PIN.

check_payment_status

Param

Requis

Description

reference_id

oui

Identifiant retourné par request_payment / disburse_payment.

kind

non

collection, disbursement ou auto (défaut).

country

non

Pays d'origine de la transaction (requis en production).

validate_account

Param

Requis

Description

phone_number

oui

MSISDN à vérifier.

country

non

Pays MTN (défaut benin).

Erreurs / Errors

FR — Aucun outil ne lève d'exception vers l'agent. Toute erreur devient :

{
  "ok": false,
  "error": {
    "code": "devise_invalide",
    "message": "Devise non supportée pour ce compte...",
    "message_en": "Currency not supported for this account.",
    "retryable": false,
    "http_status": 500,
    "detail": "INVALID_CURRENCY"
  }
}

EN — No tool raises an exception to the agent. Every error becomes the dict above. Check retryable: if true, retrying the same call may succeed.

Codes principaux / Main codes (extrait — voir src/momo_mcp/errors.py)

code

Retryable

Cause

validation

non

Paramètre invalide (numéro, montant, devise, external_id).

reference_dupliquee

non

external_id déjà utilisé — doit être unique par transaction.

payeur_introuvable / beneficiaire_introuvable

non

Pas de compte Mobile Money actif pour ce numéro.

fonds_insuffisants

non

Solde payeur insuffisant.

devise_invalide

non

Devise non supportée (utilise la devise du pays).

environnement_non_autorise

non

country/credentials ne correspondent pas à l'environnement.

authentification_echouee

non

Clé d'abonnement ou identifiants API invalides.

trop_de_requetes

oui

Limite de débit MTN atteinte.

timeout_reseau / erreur_reseau

oui

MTN injoignable / délai dépassé.

service_indisponible / erreur_serveur_mtn

oui

Erreur temporaire côté MTN.

erreur_interne

non

Erreur inattendue du serveur momo-mcp.

Comportement de retry / Retry behaviour — Le client réessaie automatiquement une fois les erreurs réseau, 5xx et les tokens expirés (401), en réutilisant le même X-Reference-Id (idempotent côté MTN). Les erreurs métier (4xx, code MTN) ne sont jamais réessayées automatiquement.

Sécurité / Security

FR — Un serveur MCP qui déplace de l'argent expose des primitives à un LLM. Garde-fous intégrés :

  • Sortie de fonds signalée. disburse_payment porte l'annotation MCP destructiveHint: true (et openWorldHint: true) ; les clients qui le gèrent peuvent exiger une confirmation humaine. check_payment_status et validate_account sont readOnlyHint: true.

  • Plafonds + barrière de production. MOMO_MAX_DISBURSEMENT (par appel) et MOMO_MAX_DAILY_DISBURSEMENT (par jour UTC) bornent les sorties ; dépassement → erreur plafond_depasse (non réessayable). Si MTN_TARGET_ENVIRONMENT != "sandbox" et qu'aucun plafond n'est défini, le serveur refuse de démarrer — sauf échappatoire explicite MOMO_ALLOW_UNCAPPED_DISBURSEMENT=true. Le compteur quotidien est persisté en SQLite (réservation atomique) : il survit aux redémarrages et se partage entre instances ; une réservation est libérée si l'appel MTN échoue.

  • Canal payer_message restreint. Le message affiché sur le téléphone du payeur n'est jamais du texte libre : il doit être vide ou l'un des messages prédéfinis (MOMO_PAYER_MESSAGES), sous plafond de longueur. Cela ferme un vecteur de phishing (un agent rédigeant le texte que la victime lit avant de saisir son PIN).

  • Erreurs structurées. Aucune stack trace vers le modèle ; toute erreur est un objet {code, message, retryable} exploitable sans fuite d'information.

  • Pas de fuite d'identifiants. Les clés vivent dans .env (non commité) ; rien n'est journalisé en clair.

EN — A money-moving MCP server hands primitives to an LLM. Built-in guardrails:

  • Outflow is flagged. disburse_payment carries the MCP destructiveHint: true annotation (plus openWorldHint: true); clients that honour it can require human confirmation. check_payment_status and validate_account are readOnlyHint: true.

  • Caps + production barrier. MOMO_MAX_DISBURSEMENT (per call) and MOMO_MAX_DAILY_DISBURSEMENT (per UTC day) bound outflows; exceeding them returns a plafond_depasse error (non-retryable). If MTN_TARGET_ENVIRONMENT != "sandbox" and no cap is set, the server refuses to start — unless the explicit escape hatch MOMO_ALLOW_UNCAPPED_DISBURSEMENT=true is set. The daily counter is persisted in SQLite (atomic reservation): it survives restarts and is shared across instances; a reservation is released if the MTN call fails.

  • Restricted payer_message channel. The message shown on the payer's phone is never free text: it must be empty or one of the configured MOMO_PAYER_MESSAGES, under a length cap. This closes a phishing vector (an agent writing what the victim reads before entering their PIN).

  • Structured errors. No stack traces to the model; every error is a {code, message, retryable} object.

  • No credential leakage. Keys live in .env (not committed); nothing sensitive is logged.

Voir / see SECURITY.md.

Mise en production / Production checklist

FR

  • Passer par le Partner Portal MTN de ton pays : KYC + compte marchand local requis pour la production.

  • Récupérer les clés d'abonnement production sur momoapi.mtn.com et les identifiants API du pays.

  • MTN_TARGET_ENVIRONMENT ≠ sandbox, MTN_BASE_URL=https://proxy.momoapi.mtn.com.

  • Définir MOMO_MAX_DISBURSEMENT et MOMO_MAX_DAILY_DISBURSEMENT (obligatoires en production), et monter un volume sur MOMO_STATE_DB en Docker.

  • Renseigner un providerCallbackHost réel : scripts/provision_mtn.py utilise https://example.com par défaut (à adapter).

  • Passer external_id unique par transaction (sinon reference_dupliquee).

  • Valider le parcours complet sur un petit montant réel avant d'exposer l'agent.

EN

  • Go through the country MTN Partner Portal: KYC + local merchant account are required for production.

  • Get production subscription keys on momoapi.mtn.com and the country API credentials.

  • Set MTN_TARGET_ENVIRONMENT ≠ sandbox and MTN_BASE_URL=https://proxy.momoapi.mtn.com.

  • Set MOMO_MAX_DISBURSEMENT and MOMO_MAX_DAILY_DISBURSEMENT (mandatory in production), and mount a volume on MOMO_STATE_DB in Docker.

  • Provide a real providerCallbackHost: scripts/provision_mtn.py defaults to https://example.com (change it).

  • Use a unique external_id per transaction (otherwise reference_dupliquee).

  • Run the full flow on a small real amount before exposing the agent.

Développement / Development

pip install -e ".[dev]"
ruff check .        # lint
pytest              # 66 tests, aucun réseau réel (respx mocke MTN)

FR — Les tests n'appellent jamais MTN : respx intercepte les requêtes HTTP. Le build Docker est validé en CI (Python 3.10 / 3.12 + build image). Un test « comme un agent » (vrai client MCP, appels sandbox réels) vit dans docs/agent-test/ — hors CI.

EN — Tests never call MTN: respx intercepts HTTP. The Docker build is validated in CI (Python 3.10 / 3.12 + image build). An agent smoke test (real MCP client, real sandbox calls) lives in docs/agent-test/ — not run in CI.

Feuille de route / Roadmap

  • MTN MoMo (Collection + Disbursement) — sandbox, multi-pays

  • Gestion d'erreurs structurée + retry idempotent

  • Tests, CI, image Docker

  • MTN MoMo — validation production

  • Migration mcp v2 (FastMCP → MCPServer)

  • Moov Africa

  • Orange Money

  • Webhook optionnel (éviter le polling de check_payment_status)

Contact

FR — Construit par Tahsine (Borrelle Dev) à Cotonou, Bénin. Questions, retours, intégration sur-mesure (Moov, Orange, autre marché) : ouvre une issue ou écris-moi sur LinkedIn / GitHub.

EN — Built by Tahsine (Borrelle Dev) in Cotonou, Benin. Questions, feedback, custom integrations (Moov, Orange, other markets): open an issue or reach me on LinkedIn / GitHub.

Licence / License

MIT — voir / see LICENSE.

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    Open-source MCP server that streamlines payment integration for AI agents and financial apps in Africa, providing unified tools for providers like M-Pesa.
    1
    -
  • A
    license
    A
    quality
    D
    maintenance
    MCP server that connects AI assistants to the CinetPay API for mobile money payments across Africa, enabling balance checks, payment initialization, transfers, and more.
    7
    7 npm
    MIT