A Model Context Protocol (MCP) server that integrates Safaricom's M-PESA Daraja API with Claude, enabling natural language payment processing and real-time transaction notifications.
- STK Push Payments: Initiate M-PESA payment requests through natural language
- Real-time Callbacks: Automatic payment notification handling with Flask server
- Payment Tracking: Store and query payment history with read/unread status
- Natural Language Interface: Interact with M-PESA through Claude conversations
- Sandbox Testing: Full support for Daraja sandbox environment
- Automated Testing: Comprehensive test suite for validation
- Prerequisites
- Installation
- Configuration
- Getting Daraja Credentials
- Usage
- Testing
- Integrating with Claude Desktop
- Available Tools
- Callback Setup
- Troubleshooting
- Security Best Practices
- Production Deployment
- API Reference
- Contributing
- License
- Python 3.10+ installed on your system
- Daraja API Account - Register at developer.safaricom.co.ke
- ngrok (optional, for testing callbacks) - Download from ngrok.com
- Claude Desktop (optional, for MCP integration)
# Clone the repository
git clone https://github.com/mboya/daraja-mcp.git
cd daraja-mcp
# Or if you already have the repository, navigate to it
cd daraja-mcp# Create virtual environment
python3 -m venv venv
# Activate virtual environment
# macOS/Linux:
source venv/bin/activate
# Windows:
venv\Scripts\activateYou should see (venv) in your terminal prompt.
# Install all required packages from requirements.txt
pip install -r requirements.txtThis will install:
mcp- Model Context Protocol serverrequests- HTTP library for API callsflask- Web framework for callback serverpython-dotenv- Environment variable managementgunicorn- WSGI HTTP server (for production deployment)
Create a .env file in the project root directory. The repository includes a .env.example file as a template.
Quick Setup:
# Copy the example file
cp .env.example .env
# Edit the .env file with your actual credentials
# Use your preferred text editor (nano, vim, code, etc.)
nano .envThen replace the placeholder values with your actual Daraja API credentials (see Getting Daraja Credentials section below).
Note: See the "Choosing Between server.py and server_http.py" section below for guidance on which server file to use.
Once you've completed the installation and configuration steps above, follow these steps to run the server:
# Make sure you're in the project directory
cd daraja-mcp
# Activate virtual environment
source venv/bin/activate # macOS/Linux
# OR
venv\Scripts\activate # Windows
# Verify .env file exists
ls -la .env # macOS/Linux
# OR
dir .env # WindowsFor Local Development with Claude Desktop:
# Activate virtual environment (if not already activated)
source venv/bin/activate
# Run the server
python server.pyFor Production/Cloud Deployment:
# Activate virtual environment
source venv/bin/activate
# Run with Python (for testing)
python server_http.py
# OR run with gunicorn (for production)
gunicorn server_http:app --bind 0.0.0.0:3000 --workers 2Check the output: You should see:
🚀 Daraja MCP Server starting...
📡 Callback server running on 0.0.0.0:3000
🌐 Public callback URL: http://localhost:3000/mpesa/callback
🔧 Environment: sandbox
Test the health endpoint: Open a new terminal and run:
curl http://localhost:3000/healthExpected response:
{
"status": "healthy",
"callback_url": "http://localhost:3000/mpesa/callback",
"unread_payments": 0
}If you need to receive real M-PESA callbacks during local development:
# In a new terminal, start ngrok
ngrok http 3000
# Copy the HTTPS URL (e.g., https://abc123.ngrok.io)
# Update your .env file:
PUBLIC_URL=https://abc123.ngrok.io
# Restart the server
python server.py- Open Claude Desktop settings
- Add the MCP server configuration (see Integrating with Claude Desktop section)
- Restart Claude Desktop
- Start chatting and use natural language to process payments!
source venv/bin/activate
python server.py- Use for testing MCP tools locally
- Callbacks won't work (localhost not accessible from internet)
- Good for development and debugging
# Terminal 1: Start ngrok
ngrok http 3000
# Terminal 2: Update .env with ngrok URL, then start server
source venv/bin/activate
python server.py- Use for testing full payment flow with real callbacks
- ngrok provides public HTTPS URL
- Safaricom can reach your callback endpoint
# Deploy to Railway (automatic from git push)
git push origin main
# Or run locally with production settings
source venv/bin/activate
gunicorn server_http:app --bind 0.0.0.0:3000 --workers 2- Use
server_http.pyfor production - Railway automatically provides HTTPS
- No ngrok needed
"Can't assign requested address":
This often happens on macOS when binding to 0.0.0.0. Use 127.0.0.1 for local development:
# In your .env file, set:
CALLBACK_HOST=127.0.0.1If CALLBACK_HOST is already in .env with 0.0.0.0, change it to 127.0.0.1 or remove the line to use the default. The server will still work with ngrok (ngrok forwards to localhost).
Port already in use:
# Check what's using port 3000
lsof -i :3000 # macOS/Linux
netstat -ano | findstr :3000 # Windows
# Kill the process or change CALLBACK_PORT in .envMissing dependencies:
# Reinstall dependencies
pip install -r requirements.txtEnvironment variables not loading:
# Verify .env file exists and has correct values
cat .env
# Test loading
python -c "from dotenv import load_dotenv; import os; load_dotenv(); print(os.getenv('DARAJA_CONSUMER_KEY'))"Server starts but health check fails:
# Check if Flask callback server is running
curl http://localhost:3000/health
# Check server logs for errors
# Look for error messages in the terminal running server.pyThe repository includes a .env.example file with all required environment variables. To set up your environment:
-
Copy the example file:
cp .env.example .env
-
Edit the
.envfile with your actual credentials:# Using nano nano .env # Or using your preferred editor code .env # VS Code vim .env # Vim
-
Replace the placeholder values with your actual Daraja API credentials:
DARAJA_CONSUMER_KEY- Your consumer key from Daraja portalDARAJA_CONSUMER_SECRET- Your consumer secret from Daraja portalDARAJA_SHORTCODE- Your business shortcode (174379 for sandbox)DARAJA_PASSKEY- Your passkey from Daraja portalPUBLIC_URL- Your public callback URL (use ngrok URL for local testing)
Example .env file structure:
# Daraja API Credentials
DARAJA_CONSUMER_KEY=your_consumer_key_here
DARAJA_CONSUMER_SECRET=your_consumer_secret_here
DARAJA_SHORTCODE=174379
DARAJA_PASSKEY=your_passkey_here
# Environment (sandbox or production)
DARAJA_ENV=sandbox
# Callback Server Configuration
CALLBACK_PORT=3000
CALLBACK_HOST=127.0.0.1
PUBLIC_URL=http://localhost:3000Important:
- Use
CALLBACK_HOST=127.0.0.1for local dev to avoid "Can't assign requested address" on macOS. - Never commit
.envto version control! (It's already in.gitignore) - The
.env.examplefile is safe to commit and serves as a template - For production deployments, set environment variables in your hosting platform (Railway, Heroku, etc.)
The repository already includes all necessary files:
server.py- MCP server for local Claude Desktop integration (stdio)server_http.py- MCP server for cloud/production deployment (HTTP)test_daraja.py- Comprehensive test suitequick_test.py- Quick validation scriptrequirements.txt- Python dependencies (already configured).gitignore- Git ignore rules (already configured).env.example- Environment variables templateProcfile- Railway deployment configurationrailway.json- Railway platform settingsREADME.md- This documentation
You only need to create the .env file by copying .env.example:
cp .env.example .envThen edit .env with your actual Daraja API credentials (see Configuration section above).
- Visit developer.safaricom.co.ke
- Create an account
- Verify your email
- Navigate to "My Apps" → "Create New App"
- Select APIs:
- Lipa Na M-PESA Online
- M-PESA Express (STK Push)
- Submit your app
- Get your credentials:
- Consumer Key
- Consumer Secret
- Passkey (in app details)
For testing, use these sandbox values:
- Business Short Code: 174379 (default sandbox)
- Passkey: Check your app details on Daraja portal
- Test Phone Numbers: 254708374149 (check Daraja docs for updated test numbers)
- Test PIN: Varies by sandbox version (usually simulated automatically)
- Test thoroughly in sandbox
- Apply for production access through Daraja portal
- Complete KYC and business verification
- Receive production credentials
- Update
.envwith production values and setDARAJA_ENV=production
# Activate virtual environment
source venv/bin/activate
# Run the stdio server for Claude Desktop
python server.pyExpected output:
🚀 Daraja MCP Server starting...
📡 Callback server running on 0.0.0.0:3000
🌐 Public callback URL: http://localhost:3000/mpesa/callback
🔧 Environment: sandbox
# Activate virtual environment
source venv/bin/activate
# Run the HTTP server (for Railway, Heroku, etc.)
python server_http.py
# Or use gunicorn for production (as configured in Procfile)
gunicorn server_http:app --bind 0.0.0.0:$PORT --workers 2Expected output:
🚀 Daraja MCP Server (HTTP Mode)
📡 Listening on port 3000
🌐 Public URL: http://localhost:3000
🔧 Environment: sandbox
The MCP server runs two components simultaneously:
- MCP Protocol Server - Communicates with Claude via stdio
- Flask Callback Server - Receives M-PESA payment notifications on port 3000
This project includes two server implementations for different use cases:
Use this when:
- Running the MCP server locally on your machine
- Integrating with Claude Desktop application
- Developing and testing locally
- Using stdio (standard input/output) for MCP communication
Features:
- Communicates with Claude Desktop via stdio protocol
- Runs Flask callback server in a background thread
- Full MCP tool implementation with all features
- Best for local development and testing
Usage:
python server.pyUse this when:
- Deploying to cloud platforms (Railway, Heroku, AWS, etc.)
- Running in production environments
- Need HTTP-based MCP endpoints
- Using gunicorn or similar WSGI servers
Features:
- Single Flask app combining MCP HTTP endpoints and callbacks
- Exposes
/mcp/toolsand/mcp/call_toolendpoints - Works with gunicorn for production deployment
- Compatible with Railway's Procfile configuration
Usage:
# For production with gunicorn (as configured in Procfile)
gunicorn server_http:app --bind 0.0.0.0:$PORT --workers 2
# For local testing
python server_http.pyRailway Deployment:
The Procfile is configured to use server_http.py with gunicorn:
web: gunicorn server_http:app --bind 0.0.0.0:$PORT --workers 2
Summary:
- Local development with Claude Desktop → Use
server.py - Cloud/production deployment → Use
server_http.py
# Terminal 1: Start server
source venv/bin/activate
python server.py
# Terminal 2: Run quick test
source venv/bin/activate
python quick_test.pyExpected output:
🔐 Testing Daraja Authentication...
✅ Authentication successful! (sandbox environment)
🌐 Testing Callback Server...
✅ Callback server is running!
📨 Testing Callback Endpoint...
✅ Callback endpoint working!
Tests passed: 3/3
🎉 All tests passed!
python test_daraja.pyThis runs 8 test phases:
- Environment variable validation
- Python dependency checks
- Daraja API authentication
- MCP server startup
- Callback server health
- Callback endpoint processing
- ngrok availability
- STK push format validation
curl -X GET "https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials" \
-H "Authorization: Basic $(echo -n 'KEY:SECRET' | base64)"curl http://localhost:3000/healthcurl -X POST http://localhost:3000/mpesa/callback \
-H "Content-Type: application/json" \
-d '{
"Body": {
"stkCallback": {
"ResultCode": 0,
"ResultDesc": "Success"
}
}
}'- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"daraja": {
"command": "/absolute/path/to/daraja-mcp/venv/bin/python",
"args": ["/absolute/path/to/daraja-mcp/server.py"],
"env": {
"DARAJA_CONSUMER_KEY": "your_consumer_key",
"DARAJA_CONSUMER_SECRET": "your_consumer_secret",
"DARAJA_SHORTCODE": "174379",
"DARAJA_PASSKEY": "your_passkey",
"DARAJA_ENV": "sandbox",
"CALLBACK_PORT": "3000",
"PUBLIC_URL": "https://your-ngrok-url.ngrok.io"
}
}
}
}Important:
- Use absolute paths (not relative)
- Use virtual environment's Python:
venv/bin/python - Update PUBLIC_URL with your ngrok HTTPS URL
Completely quit and reopen Claude Desktop to load the MCP server.
In Claude Desktop, ask:
"Is the Daraja callback server working?"
Claude should respond with server status information.
Once configured, Claude can use these tools:
Initiate an STK Push payment request.
Example:
"Send a payment request for 500 KES to 0712345678 for order #INV-001"
Parameters:
phone_number- Customer phone (254XXXXXXXXX or 07XXXXXXXX)amount- Amount in KES (minimum 1)account_reference- Reference like invoice/order numbertransaction_desc- Description of transaction
Check the status of a payment request.
Example:
"Check the status of checkout request ws_CO_12345"
Parameters:
checkout_request_id- ID returned from STK push
View recent payment notifications.
Example:
"Show me the last 10 payments"
Parameters:
limit- Number of payments to retrieve (default: 10, max: 50)
Get details of a specific payment.
Example:
"Show me details for receipt QAR7I8K3LM"
Parameters:
checkout_request_id- Or -mpesa_receipt- M-PESA receipt number
Mark a notification as read.
Example:
"Mark payment ws_CO_12345 as read"
Get summary of all notifications.
Example:
"How many unread payments do I have?"
Check if callback server is running.
Example:
"Is the callback server working?"
The Problem:
- M-PESA Daraja API requires HTTPS callbacks (not HTTP)
- Safaricom's servers need to reach your callback endpoint from the internet
- Your local development server (
localhost:3000) is not accessible from the internet - Firewalls and NAT prevent external access to your local machine
The Solution: ngrok creates a secure tunnel that:
- ✅ Exposes your local server to the internet via HTTPS
- ✅ Provides a public URL that Safaricom can reach
- ✅ Automatically handles SSL/TLS encryption
- ✅ Allows real-time testing without deploying to production
- ✅ Shows all incoming requests in a web interface for debugging
How It Works:
Safaricom Servers → ngrok HTTPS URL → ngrok Tunnel → Your Local Server (localhost:3000)
# macOS
brew install ngrok
# Linux (using snap)
sudo snap install ngrok
# Windows
# Download from https://ngrok.com/download
# Or use Chocolatey: choco install ngrok
# Or download directly from https://ngrok.com/downloadSign up for free: Visit ngrok.com and create an account to get your authtoken.
ngrok config add-authtoken YOUR_AUTHTOKEN_HERE# Forward HTTPS traffic to your local port 3000
ngrok http 3000Output:
Session Status online
Account Your Name (Plan: Free)
Version 3.x.x
Region United States (us)
Latency 45ms
Web Interface http://127.0.0.1:4040
Forwarding https://abc123.ngrok.io -> http://localhost:3000
Connections ttl opn rt1 rt5 p50 p90
0 0 0.00 0.00 0.00 0.00
Important: Copy the Forwarding HTTPS URL (e.g., https://abc123.ngrok.io)
Update PUBLIC_URL in your .env file:
PUBLIC_URL=https://abc123.ngrok.ioOr update Claude Desktop config with the ngrok URL.
Note: Free ngrok URLs change each time you restart ngrok. For a static URL, upgrade to a paid plan or use ngrok's reserved domains feature.
# Stop the server (Ctrl+C)
# Restart with new PUBLIC_URL
python server.pyCheck ngrok web interface:
- Open http://localhost:4040 in your browser
- You'll see all requests being forwarded through ngrok
- Useful for debugging callback issues
Test the tunnel:
# Test health endpoint through ngrok
curl https://abc123.ngrok.io/health
# Should return:
# {"status":"healthy","callback_url":"https://abc123.ngrok.io/mpesa/callback",...}Important: Keep the ngrok terminal window open while testing. If you close it, the tunnel stops and Safaricom won't be able to reach your callback endpoint.
Pro Tip: Run ngrok in a separate terminal or use a process manager like tmux or screen:
# Using tmux
tmux new -s ngrok
ngrok http 3000
# Press Ctrl+B then D to detach (keeps running in background)If you prefer other tunneling services:
-
Cloudflare Tunnel (cloudflared) - Free, no account needed for basic use
cloudflared tunnel --url http://localhost:3000
-
localtunnel - Simple npm-based tunnel
npx localtunnel --port 3000
-
serveo - SSH-based tunnel (no installation)
ssh -R 80:localhost:3000 serveo.net
However, ngrok is recommended because:
- Most reliable and stable
- Best documentation and community support
- Web interface for request inspection
- Easy to use and configure
For production, deploy to a server with:
- Public HTTPS endpoint (SSL certificate required)
- Static IP or domain name
- Firewall rules allowing incoming HTTPS traffic
- Monitoring and logging
Popular options:
- AWS EC2 with Elastic IP
- DigitalOcean Droplet
- Heroku with SSL
- Google Cloud Run
- Railway (recommended - see deployment guide below)
Example nginx configuration:
server {
listen 443 ssl;
server_name api.yourdomain.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location /mpesa/ {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}Railway is an excellent choice for deploying this MCP server because it:
- ✅ Provides HTTPS endpoints automatically
- ✅ Handles SSL certificates
- ✅ Easy environment variable configuration
- ✅ Automatic deployments from Git
- ✅ Free tier available for testing
-
Create Railway Account
- Visit railway.app
- Sign up with GitHub/GitLab
-
Create New Project
- Click "New Project"
- Select "Deploy from GitHub repo" (or upload code)
-
Configure Environment Variables In Railway dashboard, add these environment variables:
DARAJA_CONSUMER_KEY=your_consumer_key DARAJA_CONSUMER_SECRET=your_consumer_secret DARAJA_SHORTCODE=174379 DARAJA_PASSKEY=your_passkey DARAJA_ENV=sandbox CALLBACK_PORT=3000 PUBLIC_URL=https://your-app-name.railway.appNote: On Railway, the app is served by gunicorn which binds to
0.0.0.0:$PORTautomatically; you don't need to setCALLBACK_HOST. -
Deploy
- Railway will automatically detect
Procfileandrailway.json - The
Procfileusesserver_http.pywith gunicorn - Railway will build and deploy automatically
- Railway will automatically detect
-
Get Your Public URL
- Railway provides a public HTTPS URL (e.g.,
https://your-app.railway.app) - Update
PUBLIC_URLenvironment variable with this URL - Railway will restart the service automatically
- Railway provides a public HTTPS URL (e.g.,
-
Verify Deployment
# Test health endpoint curl https://your-app.railway.app/health # Should return: # {"status":"healthy","callback_url":"https://your-app.railway.app/mpesa/callback",...}
Important Notes:
- Railway automatically provides HTTPS, so no ngrok needed in production
- The
PUBLIC_URLmust match your Railway app URL exactly - Use
server_http.py(configured inProcfile) for Railway deployments - Railway handles port binding automatically via
$PORTenvironment variable
Causes:
- Invalid Consumer Key or Secret
- Wrong environment (sandbox vs production)
- Network connectivity issues
Solutions:
# Test authentication manually
python -c "
from dotenv import load_dotenv
import os, base64, requests
load_dotenv()
key = os.getenv('DARAJA_CONSUMER_KEY')
secret = os.getenv('DARAJA_CONSUMER_SECRET')
auth = base64.b64encode(f'{key}:{secret}'.encode()).decode()
r = requests.get('https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials',
headers={'Authorization': f'Basic {auth}'})
print(r.json())
"Solutions:
# Check if port 3000 is available
lsof -i :3000
# Kill any process using the port
kill -9 <PID>
# Restart server
python server.pySolutions:
- Verify config file path is correct
- Use absolute paths in configuration
- Ensure virtual environment Python path is correct
- Check Claude Desktop logs: Help → View Logs
- Restart Claude Desktop completely
Solutions:
- Verify ngrok is running:
curl https://your-url.ngrok.io/health - Check PUBLIC_URL environment variable
- Ensure ngrok URL is HTTPS (required by Safaricom)
- View ngrok request logs: http://localhost:4040
- Check firewall settings
Solutions:
- Use format: 254XXXXXXXXX (not +254 or 07XX)
- Sandbox: Use test numbers from Daraja portal
- Remove spaces, dashes, or special characters
# Check server process
ps aux | grep server.py
# Test callback health
curl http://localhost:3000/health
# Test ngrok forwarding
curl https://your-ngrok-url.ngrok.io/health
# View Python errors
tail -f server.log
# Check Claude logs
# macOS: ~/Library/Logs/Claude/
# Windows: %APPDATA%\Claude\logs\- Check Daraja API documentation: developer.safaricom.co.ke/Documentation
- Review ngrok request inspector: http://localhost:4040
- Check Claude Desktop logs
- Verify all environment variables are set correctly
- Test each component independently
- ✅ Never commit credentials to version control
- ✅ Use
.envfiles with.gitignore - ✅ Rotate credentials regularly
- ✅ Use different credentials for sandbox and production
- ✅ Store production secrets in secure vaults (AWS Secrets Manager, etc.)
- ✅ Use HTTPS for all callbacks (required by Safaricom)
- ✅ Implement webhook signature verification
- ✅ Restrict callback endpoint to Safaricom IPs
- ✅ Use firewall rules to limit access
- ✅ Enable rate limiting
- ✅ Validate all input data
- ✅ Sanitize phone numbers and amounts
- ✅ Implement request logging
- ✅ Add authentication for sensitive operations
- ✅ Use environment-specific configurations
- ✅ Don't log sensitive data (PINs, full card numbers)
- ✅ Mask phone numbers in logs
- ✅ Implement data retention policies
- ✅ Comply with data protection regulations
- ✅ Encrypt data at rest and in transit
- ✅ Set up error alerting
- ✅ Monitor callback success rates
- ✅ Track failed transactions
- ✅ Log all API calls
- ✅ Implement health checks
- Thoroughly tested in sandbox environment
- Obtained production credentials from Daraja
- Set up production server with SSL/TLS
- Configured firewall and security groups
- Implemented proper logging and monitoring
- Set up error alerting
- Documented deployment process
- Created backup and recovery plan
- Tested with small amounts first
- Configured auto-restart on failure
# Update system
sudo apt update && sudo apt upgrade -y
# Install Python
sudo apt install python3.10 python3.10-venv -y
# Install nginx (for reverse proxy)
sudo apt install nginx -y
# Install supervisor (for process management)
sudo apt install supervisor -y# Create application directory
sudo mkdir -p /opt/daraja-mcp
sudo chown $USER:$USER /opt/daraja-mcp
cd /opt/daraja-mcp
# Clone or copy application files
# Set up virtual environment
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
# Create production .env
nano .env
# Add production credentialsCreate /etc/supervisor/conf.d/daraja-mcp.conf:
[program:daraja-mcp]
command=/opt/daraja-mcp/venv/bin/python /opt/daraja-mcp/server.py
directory=/opt/daraja-mcp
user=www-data
autostart=true
autorestart=true
stderr_logfile=/var/log/daraja-mcp/error.log
stdout_logfile=/var/log/daraja-mcp/access.log
environment=PRODUCTION="true"Create /etc/nginx/sites-available/daraja-mcp:
server {
listen 80;
server_name api.yourdomain.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name api.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/api.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.yourdomain.com/privkey.pem;
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}# Reload supervisor
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start daraja-mcp
# Enable and restart nginx
sudo ln -s /etc/nginx/sites-available/daraja-mcp /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx
# Check status
sudo supervisorctl status daraja-mcp
curl https://api.yourdomain.com/health# View logs
sudo tail -f /var/log/daraja-mcp/error.log
# Restart service
sudo supervisorctl restart daraja-mcp
# Check resource usage
htop
# Monitor nginx access
sudo tail -f /var/log/nginx/access.logGET https://api.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials
Authorization: Basic <base64(consumer_key:consumer_secret)>
POST https://api.safaricom.co.ke/mpesa/stkpush/v1/processrequest
Authorization: Bearer <access_token>
Content-Type: application/json
{
"BusinessShortCode": "174379",
"Password": "<base64(shortcode+passkey+timestamp)>",
"Timestamp": "20240108143022",
"TransactionType": "CustomerPayBillOnline",
"Amount": 100,
"PartyA": "254712345678",
"PartyB": "174379",
"PhoneNumber": "254712345678",
"CallBackURL": "https://your-domain.com/callback",
"AccountReference": "Order123",
"TransactionDesc": "Payment for Order123"
}
POST https://api.safaricom.co.ke/mpesa/stkpushquery/v1/query
Authorization: Bearer <access_token>
Content-Type: application/json
{
"BusinessShortCode": "174379",
"Password": "<base64(shortcode+passkey+timestamp)>",
"Timestamp": "20240108143022",
"CheckoutRequestID": "ws_CO_08012024123456789"
}
GET http://localhost:3000/health
Response:
{
"status": "healthy",
"callback_url": "http://localhost:3000/mpesa/callback",
"unread_payments": 0
}
POST http://localhost:3000/mpesa/callback
Content-Type: application/json
{
"Body": {
"stkCallback": {
"MerchantRequestID": "29115-34620561-1",
"CheckoutRequestID": "ws_CO_08012024123456789",
"ResultCode": 0,
"ResultDesc": "The service request is processed successfully.",
"CallbackMetadata": {
"Item": [
{"Name": "Amount", "Value": 100},
{"Name": "MpesaReceiptNumber", "Value": "QAR7I8K3LM"},
{"Name": "TransactionDate", "Value": 20240108143022},
{"Name": "PhoneNumber", "Value": 254712345678}
]
}
}
}
}
daraja-mcp/
├── venv/ # Virtual environment (not in git)
├── server.py # MCP server for local Claude Desktop (stdio)
├── server_http.py # MCP server for cloud deployment (HTTP)
├── test_daraja.py # Comprehensive test suite
├── quick_test.py # Quick validation script
├── Procfile # Railway deployment configuration
├── railway.json # Railway platform configuration
├── .env # Environment variables (not in git)
├── .gitignore # Git ignore rules
├── requirements.txt # Python dependencies
├── README.md # This file
└── docs/ # Additional documentation
├── DEPLOYMENT.md # Deployment guide
├── API.md # API documentation
└── TROUBLESHOOTING.md # Extended troubleshooting
Key Files:
server.py- Use for local development with Claude Desktop (stdio protocol)server_http.py- Use for cloud deployments like Railway (HTTP endpoints)Procfile- Defines how Railway runs the application (usesserver_http.py)railway.json- Railway platform configuration (builder, replicas, restart policy)
Contributions are welcome! Please follow these guidelines:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
# Clone repo
git clone https://github.com/mboya/daraja-mcp.git
cd daraja-mcp
# Set up environment
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
# Run tests
python test_daraja.pyThis project is licensed under the MIT License - see the LICENSE file for details.
- Safaricom Daraja API - M-PESA API platform
- Anthropic MCP - Model Context Protocol
- Flask - Web framework for callbacks
- ngrok - Secure tunneling for local development
- Documentation: developer.safaricom.co.ke/Documentation
- Daraja Support: support@safaricom.co.ke
- MCP Documentation: modelcontextprotocol.io
- Issues: GitHub Issues
- Initial release
- STK Push implementation
- Real-time callback handling
- Payment notification storage
- Automated testing suite
- Claude Desktop integration
- Comprehensive documentation
Made with ❤️ for the M-PESA ecosystem
For questions or support, please open an issue on GitHub or contact the maintainers.