mpesa-daraja-mcp: M-Pesa Daraja MCP server
An MCP server that exposes Safaricom's Daraja APIs as tools, so an agent (Claude Code, Claude Desktop, any MCP client) can test and operate M-Pesa integrations. It pairs with the skills in Agent Skills in this repository: skills teach how to integrate, this server lets the agent call Daraja.
Tools
| Tool | Daraja API | Moves money |
|---|---|---|
mpesa_explain_code |
— offline error/result code lookup | |
mpesa_normalize_phone |
— offline 07… → 2547… |
|
mpesa_generate_stk_password |
— offline Password + EAT Timestamp | |
mpesa_parse_callback |
— offline parse of any callback payload | |
mpesa_setup_status |
— offline: what's configured and missing per toolset (no values) | |
mpesa_check_credentials |
OAuth token (never returned) | |
mpesa_get_result |
Final status by idempotency key or any Daraja ID (from captured callbacks; STK falls back to STK Query) | |
mpesa_stk_push_and_wait |
M-Pesa Express: push, then wait for callback / poll query; returns SUCCESS, FAILED or UNKNOWN | ✅ |
mpesa_stk_push / mpesa_stk_query |
M-Pesa Express, fire-and-forget / status | ✅ / |
mpesa_c2b_register_urls / mpesa_c2b_simulate |
C2B v2 (simulate = sandbox only) | |
mpesa_pull_transactions |
Pull Transactions query | |
mpesa_b2c_payment |
B2C v3, B2Pochi | ✅ |
mpesa_b2b_payment |
BusinessPayBill, BusinessBuyGoods, BusinessPayToBulk (B2C top-up), PayTaxToKRA | ✅ |
mpesa_b2b_express_checkout |
B2B USSD push | ✅ |
mpesa_transaction_status / mpesa_account_balance |
Status / Balance (async result) | |
mpesa_reverse_transaction |
Reversal | ✅ |
mpesa_generate_dynamic_qr |
Dynamic QR, saved as a PNG file (path returned, not the image) | |
mpesa_ratiba_create_standing_order |
M-Pesa Ratiba | ✅ |
mpesa_bill_manager_optin / mpesa_bill_manager_send_invoice / mpesa_bill_manager_cancel_invoice |
Bill Manager |
Tool annotations mark money-moving tools as destructiveHint, so MCP clients ask for approval.
Safety
- Defaults to the sandbox.
- In production, money-moving tools refuse to run unless
MPESA_ALLOW_LIVE_MONEY_MOVEMENT=true. - Callback URLs must be HTTPS; C2B URLs with
mpesa/safaricomin them are rejected up front. - Tokens and secrets are never returned in tool output.
- Responses with
ResponseCode "0"are tagged_note: ACCEPTED for processing only, so the agent does not report them as completed payments. - Idempotent money tools. Each request is stored under an idempotency key. Repeating the key returns the stored request (
duplicate: true) instead of paying again.- Only a definitively FAILED request is re-sent.
- ACCEPTED or UNKNOWN requests (queue timeouts, network errors) are never re-sent until their result is known.
- B2C, B2B and B2B Express require an explicit
idempotency_key, e.g.refund-order-1042. Reversal, tax and Ratiba derive one from the transaction ID, the PRN and phone + name respectively. For STK the key is optional.
Configuration
| Variable | Required | Notes |
|---|---|---|
MPESA_ENV |
sandbox (default) or production |
|
MPESA_CONSUMER_KEY, MPESA_CONSUMER_SECRET |
✅ | From your Daraja app |
MPESA_SHORTCODE |
most tools | Paybill / store number (174379 in sandbox) |
MPESA_PASSKEY |
STK | Lipa na M-Pesa passkey |
MPESA_INITIATOR_NAME |
B2C/B2B/status/balance/reversal | API operator username |
MPESA_SECURITY_CREDENTIAL |
same | Pre-encrypted credential, or set the next two |
MPESA_INITIATOR_PASSWORD + MPESA_CERT_PATH |
Server encrypts the password with the cert at startup | |
MPESA_CALLBACK_BASE_URL |
e.g. https://abc.ngrok-free.app; tools append /mpesa/stk/callback, /mpesa/b2c/result, … |
|
MPESA_ALLOW_LIVE_MONEY_MOVEMENT |
true to allow production charges/payouts |
|
MPESA_CALLBACK_LISTEN |
e.g. 127.0.0.1:8787: starts the built-in callback receiver |
|
MPESA_CALLBACK_TOKEN |
with receiver | 16+ random characters. Callback URLs become <base>/cb/<token>/… |
MPESA_STATE_DB |
SQLite file for requests and results (default ~/.mpesa-mcp/state.db) |
|
MPESA_TOOLSETS |
Comma list of stk,c2b,payouts,ops,qr,ratiba,billmanager (core is always on). Default: all |
|
MPESA_OUTPUT_DIR |
Where QR PNGs are saved (default ~/.mpesa-mcp/qr) |
Install & run
uvx mpesa-daraja-mcp setup # guided: keys (hidden input), live check, save, register with your agents
uvx mpesa-daraja-mcp status # what's configured (masked) + a live key check
uvx mpesa-daraja-mcp # the MCP server itself, on stdio: what your agent runs
The server needs no flags or environment variables in your agent's config. Settings are read in this order, highest first:
- Environment variables, e.g. from the client's
envblock. - A project
.envin the folder the agent starts the server in, or the file named byMPESA_DOTENV. OnlyMPESA_*lines are read. - The user config file that
setupwrites:~/.config/mpesa-daraja-mcp/config.env(Windows:%APPDATA%\mpesa-daraja-mcp\config.env; override the folder withMPESA_CONFIG_DIR). It is readable only by you.
The server starts even when unconfigured. Tools then explain what's missing, and mpesa_setup_status lists the gaps for each toolset without revealing any values.
To register by hand: claude mcp add --scope user mpesa -- uvx mpesa-daraja-mcp. Every other agent is covered in docs/clients.md. The older mpesa-mcp command still works.
Results and callbacks
Every money or async tool returns one shape:
{"status": "ACCEPTED | SUCCESS | FAILED | UNKNOWN", "message": "...", "idempotency_key": "...",
"duplicate": false, "ids": {"checkout_request_id": "..."}, "receipt": "...", "result_code": "...",
"explanation": "...", "raw": {}}
To let the agent see final results, enable the built-in receiver:
export MPESA_CALLBACK_LISTEN=127.0.0.1:8787
export MPESA_CALLBACK_TOKEN=$(python -c "import secrets; print(secrets.token_urlsafe(24))")
ngrok http 8787 # sandbox; use a reverse proxy with TLS in production
export MPESA_CALLBACK_BASE_URL=https://<tunnel-host>
Callbacks are stored in MPESA_STATE_DB:
mpesa_get_result(id)reads them.mpesa_stk_push_and_waitreturns the final outcome in one call.- Queue-timeout callbacks are recorded as UNKNOWN.
- C2B validation requests are always accepted. Use your own receiver if you need to reject payments.
Without the receiver, mpesa_stk_push_and_wait still works through STK Query. For other APIs, paste a received payload into mpesa_parse_callback.
Context cost
| Toolsets | Tools | Schema tokens (approx.) |
|---|---|---|
| all (default) | 24 | ~6,300 |
stk |
9 | ~2,100 |
stk,c2b |
12 | ~2,600 |
Pick the smallest set you need with MPESA_TOOLSETS.
Development
pip install -e ".[dev]"
pytest
Tests use respx to mock Daraja. Nothing hits the network.
License
MIT. Not affiliated with or endorsed by Safaricom PLC; M-Pesa and Daraja are trademarks of Safaricom PLC.
Metadata
Release files for mpesa-daraja-mcp 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| mpesa_daraja_mcp-0.1.1.tar.gz | 50.6 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mpesa_daraja_mcp-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 88.6 kB
Release files / mpesa_daraja_mcp-0.1.1.tar.gz
| Download URL | mpesa_daraja_mcp-0.1.1.tar.gz |
|---|---|
| Size | 50.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
234d5dcd455b865249e10b27d0ccc62971cbfa0d5b105b28363ad9204bdaedca
|
|
BLAKE2b-256 checksum How to use checksums |
96d6e5b5ba9df9d4893ddd503567eed197a6aac17d2c400eb5baaaf6749838e7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logRelease files / mpesa_daraja_mcp-0.1.1-py3-none-any.whl
| Download URL | mpesa_daraja_mcp-0.1.1-py3-none-any.whl |
|---|---|
| Size | 37.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9133d18b0d9fe3f0b45abe055d9dead9b9032ca5c1724b0fdbaf843151949be7
|
|
BLAKE2b-256 checksum How to use checksums |
5cb1d8e09016e4b6360ce2b9a4a1908d6cb3241a8a693819f320e84e420a728e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logSecurity for mpesa-daraja-mcp 0.1.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for mpesa_daraja_mcp-0.1.1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.