Send SMS
sms_sendSend SMS messages to up to 1,000 recipients across 20+ African markets via Africa's Talking. Supports Unicode, returns per-recipient status and cost.
Instructions
Send SMS to one or many recipients via Africa's Talking. Supports up to 1,000 recipients per call. Works across Kenya, Nigeria, Ghana, Tanzania, Uganda, and 15+ African markets. Returns per-recipient status and cost.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| message | Yes | SMS message text. Unicode supported (Kiswahili, etc.) | |
| recipients | Yes | List of phone numbers in E.164 format e.g. ['+254712345678'] | |
| sender_id | No | Optional pre-registered alphanumeric sender ID |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Implementation Reference
- src/mpesa_mcp/server.py:273-308 (handler)The sms_send tool handler function. Accepts message text, a list of recipient phone numbers (E.164 format), and an optional sender_id. Calls Africa's Talking SMS API and returns per-recipient status, cost, and a summary.
def sms_send( message: Annotated[str, "SMS message text. Unicode supported (Kiswahili, etc.)"], recipients: Annotated[list[str], "List of phone numbers in E.164 format e.g. ['+254712345678']"], sender_id: Annotated[str, "Optional pre-registered alphanumeric sender ID"] = "", ) -> dict: """ Send SMS to one or many recipients via Africa's Talking. Supports up to 1,000 recipients per call. Works across Kenya, Nigeria, Ghana, Tanzania, Uganda, and 15+ African markets. Returns per-recipient status and cost. """ sms = _at_sms() kwargs: dict = {"message": message, "recipients": recipients} if sender_id: kwargs["sender_id"] = sender_id response = sms.send(**kwargs) data = response["SMSMessageData"] results = data["Recipients"] success_count = sum(1 for r in results if r["status"] == "Success") failed = [ {"number": r["number"], "status": r["status"]} for r in results if r["status"] != "Success" ] return { "sent": success_count, "failed": len(failed), "failures": failed, "summary": data.get("Message", ""), "results": [ {"number": r["number"], "status": r["status"], "cost": r.get("cost"), "id": r.get("messageId")} for r in results ], } - src/mpesa_mcp/server.py:266-272 (registration)Registration of sms_send as an MCP tool via @mcp.tool decorator with annotations (title='Send SMS', destructiveHint=True, etc.).
@mcp.tool(annotations={ 'title': 'Send SMS', 'readOnlyHint': False, 'destructiveHint': True, 'idempotentHint': False, 'openWorldHint': True, }) - src/mpesa_mcp/server.py:84-89 (helper)The _at_sms helper function that initializes the Africa's Talking SDK (with AT_USERNAME and AT_API_KEY env vars) and returns the SMS service object used by sms_send.
def _at_sms(): africastalking.initialize( username=os.environ["AT_USERNAME"], api_key=os.environ["AT_API_KEY"], ) return africastalking.SMS - tests/test_smoke.py:18-28 (registration)Smoke test that verifies 'sms_send' is registered among the MCP tools (asserts its name is in the list of tool names).
tools = asyncio.run(mcp.list_tools()) names = [t.name for t in tools] expected = [ "mpesa_stk_push", "mpesa_stk_query", "mpesa_transaction_status", "sms_send", "airtime_send", ] for name in expected: assert name in names, f"Tool '{name}' not registered. Found: {names}"