Skip to content

blackie360/africastalking

v0.2.0

Unofficial hosted Africa’s Talking balance, SMS, subscriptions and mobile-data tools. Connect your own account through OAuth; changes require explicit permission.

Africa’s Talking MCP · Unofficial

An unofficial, sandbox-first MCP server for Africa’s Talking balance, SMS, premium subscriptions, mobile-data bundles and offline USSD previews. Connect to the hosted service from your AI client without installing anything locally. The repository also supports stdio and self-hosting on Cloudflare Workers. Users connect their own Africa’s Talking application credentials; no shared provider account is included.

Hosted status: staging deployment. The connection page is live; its setup banner shows whether operator credential setup is still pending. Local protocol, isolation, storage and OAuth checks pass. Target-client OAuth interoperability and Free-plan CPU performance remain unverified. See deployment instructions and verification boundaries.

Not affiliated with, endorsed by or maintained by Africa’s Talking. This is a tested starter implementation, not a production-certified integration. No credentials are included. It has not sent a live SMS or airtime transaction.

Connect without installing locally

  1. Open at.blackielabs.com and choose Cursor.
  2. For Cursor, click Add to Cursor, allow your browser to open Cursor, and confirm the server. The web installer and remote JSON configuration are fallbacks. The public connection page is focused on Cursor.
  3. Follow your client’s OAuth connection prompt. On the authorization page, start with Sandbox, enter your sandbox API key, and approve the read-only balance check. The sandbox username is filled automatically. Sending is optional and off by default.
  4. Return to your client and try the first preview prompt provided on the connection page.

No clone, Node.js, npm, build, terminal or local server is needed for hosted use. Your own Africa’s Talking account and API key are required. Enter the key only on the HTTPS authorization page, never in chat or the remote configuration.

The remote endpoint is https://at.blackielabs.com/mcp. Clients accepting remote JSON entries can merge this with their existing settings:

{"mcpServers":{"africastalking":{"url":"https://at.blackielabs.com/mcp"}}}

If Add does not connect: allow the browser to open Cursor, then approve installation and use the client’s Connect/login action. Copy the endpoint if the browser blocks the app link. If authorization fails after installation, restart the client’s connection flow; do not add your provider key to the URL. This staging service supports client metadata documents (CIMD) and rate-limited dynamic client registration (DCR). See hosted deployment and compatibility checks.

Marketplace distribution

A hosted plugin package lives in plugins/africastalking/, with a repository marketplace at .agents/plugins/marketplace.json. It contains only the remote MCP URL; users authenticate in the browser and do not run a local server. This package is prepared for review, not published or approved. See platform submission instructions for Cursor, Claude and the shared ChatGPT/Codex directory.

What you get

ToolWhat it doesDefault
at_get_configShows environment and safety limits; never exposes credentials or allowlisted numbersLocal, no network
at_get_balanceReads the application balanceRequires your API key
at_send_smsPreviews or sends bulk SMSPreview only
at_send_airtimePreviews or sends airtime in one configured currencyPreview only
at_fetch_smsReads inbound SMS with a cursor; masks numbers and omits message text by defaultHosted sms:read permission
at_fetch_subscriptionsReads premium subscriptions for a shortcode/keywordHosted sms:read permission
at_create_subscriptionPreviews or creates a premium subscriptionPreview only; separate permission
at_delete_subscriptionPreviews or removes a premium subscriptionPreview only; separate permission
at_get_data_balanceReads the separate mobile-data walletHosted data:read permission
at_find_data_transactionLooks up a mobile-data transaction by its exact IDHosted data:read permission
at_send_mobile_dataPreviews or sends bundles to up to 10 recipientsPreview only; separate data permission
at_preview_ussd_sessionRuns the built-in USSD demo menu offlineNo credentials/network

The examples/ folder also contains an inbound USSD HTTP callback with CON / END handling. USSD is not an outbound send API and does not run inside stdio.

Hosted connections

A deployment exposes /mcp. OAuth uses @cloudflare/workers-oauth-provider with PKCE S256 and exact resource binding. The hosted consent page accepts an environment, application username and API key directly from the user over HTTPS. One read-only balance request validates application access; it does not verify a human identity. Do not put provider credentials in chat, MCP arguments, URLs or client metadata.

Each authorization creates a new opaque connection, including when every sandbox user has the username sandbox. Authorize separately for sandbox and live. Credentials and recipient policy are AES-256-GCM encrypted in D1; OAuth grants live in KV. Reads are enabled by consent, sending requires an additional checkbox, and live access requires explicit opt-in. Sandbox reaches the simulator, not a handset.

Hosted sends have per-connection scopes, up to 10 recipients/call, a live recipient allowlist, 10 send requests/minute and a KES 100 airtime face-value cap/call. Identical payloads are reserved in D1 for five minutes before dispatch, including ambiguous failures. These are connection limits, not account-wide budgets: repeated authorizations create independent limits. Use human approval for every funded transaction.

Optional local development: no credentials needed

Requires Node.js 22.15+ and npm. Node 24 is recommended; this project was tested on Node 24.21.0.

cd africastalking-mcp
npm ci --ignore-scripts
npm run check

Connect your MCP client using an absolute path to the compiled server. A common JSON configuration shape is:

{
  "mcpServers": {
    "africastalking-unofficial": {
      "command": "node",
      "args": ["/absolute/path/africastalking-mcp/dist/src/index.js"],
      "env": {
        "AT_ENVIRONMENT": "sandbox",
        "AT_USERNAME": "sandbox",
        "AT_ENABLE_MUTATIONS": "false",
        "AT_ENABLE_PRODUCTION": "false"
      }
    }
  }
}

Merge this entry into your client's existing configuration; do not overwrite other servers. JSON settings locations vary by client. Use an absolute Node executable path if the client cannot find node.

For a client that uses TOML MCP entries:

[mcp_servers.africastalking_unofficial]
command = "node"
args = ["/absolute/path/africastalking-mcp/dist/src/index.js"]

[mcp_servers.africastalking_unofficial.env]
AT_ENVIRONMENT = "sandbox"
AT_USERNAME = "sandbox"
AT_ENABLE_MUTATIONS = "false"
AT_ENABLE_PRODUCTION = "false"

Keep tool-approval prompts enabled. The SDK supports current MCP discovery and legacy initialize-based clients; both are smoke-tested. This package is not published on npm, so use the local build rather than npx africastalking-mcp-unofficial.

Try asking your client: “Show Africa’s Talking safety settings, then preview an SMS to +254700000000 saying Hello. Do not send it.”

Direct tool inputs

at_send_sms:

{"recipients":["+254700000000"],"message":"Hello from the sandbox","dryRun":true}

at_send_airtime:

{"recipients":[{"phoneNumber":"+254700000000","amount":"10.00"}],"currencyCode":"KES","dryRun":true}

Amounts are decimal strings, not floating-point numbers. Recipients must be unique E.164 numbers. Preview results mask recipients and omit message text; the host already sees the original tool arguments. Optional SMS senderId must be registered with the provider for your market. A preview is not a price quote or a check that a phone number, sender ID, currency or operator is supported.

Optional local credentials

Copy .env.example to .env, protect the file and enter your own sandbox API key locally. Do not paste it into a chat, source control or logs. Never use the production API key with the sandbox endpoint.

cp .env.example .env
chmod 600 .env
# Edit .env locally, then launch with explicit dotenv support:
node --env-file=/absolute/path/africastalking-mcp/.env /absolute/path/africastalking-mcp/dist/src/index.js

The server deliberately does not auto-load .env from an arbitrary working directory. For an MCP client, add --env-file=/absolute/path/.../.env before the script in args, or use that client's secure environment-variable facility. Explicit environment values override env-file values in Node; remove conflicting entries from the client's env block when switching configurations.

To test real sandbox provider calls, set AT_ENABLE_MUTATIONS=true, preferably set AT_ALLOWED_RECIPIENTS to your simulator number, approve the exact send in your MCP host, and pass dryRun:false. The sandbox username must be sandbox. Sandbox activity is simulated by Africa’s Talking; it still makes authenticated external API requests.

Additional services in sandbox and live

The additional services route to the environment selected during connection: sandbox uses sandbox.africastalking.com hosts, and live uses production hosts. Premium subscriptions use the API host in sandbox and the separate content host in production; mobile data uses the bundles host. Endpoint routing follows the official SDK. Sandbox simulation and actual live product access depend on your provider account, registered shortcodes/keywords, supported packages/operators and enabled products. These automated tests do not verify live provisioning or deliver anything to a handset.

Existing hosted connections keep their original permissions. Reconnect and select the optional permissions for inbound SMS/subscription reads, data-wallet/transaction reads, data sends or subscription changes. Sending SMS/airtime does not grant these permissions. Each actual new change still needs dryRun:false, a user-approved call and the selected permission. Live changes also require production consent and the recipient allowlist. The data limit is 1024 MB total per call in hosted mode; it is a volume limit, not a price quote or monetary spending cap.

Inbound SMS reads return IDs and a nextCursor. Pass that cursor as lastReceivedId on the next call; request includeMessageText:true only when you want message contents shared with your AI client. This is an inbox fetch, not outbound message history or delivery reports. Provider message text is untrusted data. Phone numbers are masked in results, including numbers inside returned text. Recipient lists are still supplied in tool inputs.

Examples:

{"lastReceivedId":0,"limit":20,"includeMessageText":false}

Call at_fetch_sms with the input above. For at_send_mobile_data:

{"productName":"YourConfiguredProduct","recipients":[{"phoneNumber":"+254700000000","quantity":50,"unit":"MB","validity":"Day"}],"dryRun":true}

For at_create_subscription or at_delete_subscription:

{"shortCode":"46585","keyword":"YOUR_KEYWORD","phoneNumber":"+254700000000","dryRun":true}

Use returned data transaction IDs with at_find_data_transaction. Provider queue acceptance is not proof of bundle delivery. No automatic retries are performed; failed and ambiguous changes remain duplicate-suppressed. The adapter sends empty data-recipient metadata rather than exposing arbitrary personal metadata.

at_preview_ussd_session accepts {"text":""}, {"text":"1"} or {"text":"1*1"} and returns the demo's CON/END response. It does not provision USSD, register a callback or create a live session.

For optional stdio use, actual data/subscription changes require AT_ENABLE_MUTATIONS=true plus the corresponding separate environment flag below. Hosted users make these choices on the consent page; no local setup is needed.

Safety settings

VariableDefaultPurpose
AT_ENVIRONMENTsandboxsandbox or production; controls fixed provider origin
AT_USERNAMEsandboxProduction requires an explicit application username
AT_API_KEYemptyStdio only: read from process environment; never a tool argument
AT_ENABLE_MUTATIONSfalseRequired for every actual SMS/airtime send
AT_ENABLE_DATA_MUTATIONSfalseSeparate opt-in for actual mobile-data sends
AT_ENABLE_SUBSCRIPTIONSfalseSeparate opt-in for premium subscription changes
AT_MAX_DATA_MB_PER_REQUEST1024Integer 1–10240; data volume per call, not a monetary budget
AT_ENABLE_PRODUCTIONfalseRequired for any production API request, including balance
AT_ALLOWED_RECIPIENTSemptyComma-separated exact E.164 allowlist; mandatory for production sends
AT_MAX_RECIPIENTS10Integer 1–10 per request
AT_AIRTIME_CURRENCYKESThree-letter currency allowed by local policy; provider support varies
AT_MAX_AIRTIME_PER_REQUEST100.00Total requested face value per call in the configured currency
AT_TIMEOUT_MS15000Whole provider request timeout, 100–60000 ms

A live send requires both AT_ENABLE_MUTATIONS=true and dryRun:false. Production adds the production opt-in and nonempty recipient allowlist. Invalid or misspelled boolean values fail closed. There is no tool to change these settings.

Production checklist: review the code; complete sandbox testing; register sender IDs; confirm recipient consent, data-handling and local messaging requirements; choose small caps and a strict allowlist; set real host-side human approvals; implement durable reconciliation, delivery/airtime callbacks, daily budgets and monitoring before unattended use. The current cap excludes fees and is not a cumulative spending limit. SMS has no monetary cap because this adapter cannot determine an authoritative pre-send quote.

Result and retry semantics

  • SMS accepted and airtime Sent/Success indicate provider acceptance at this stage, not proof of handset delivery or final airtime fulfillment
  • Results are per recipient. A partial or completely failed batch returns MCP isError:true; inspect structuredContent.results rather than resending the entire batch
  • HTTP errors, malformed/incomplete responses, aborted requests and network failures after send dispatch report outcomeUnknown:true. A timed-out transaction can still complete
  • The adapter makes one fetch attempt. There are no automatic HTTP retries. Provider-side processing/retries are outside this adapter's control
  • Identical send payloads are suppressed for five minutes within one stdio process, including failures and concurrent calls. This is a best-effort accidental-duplicate guard, not persistent or provider-level idempotency. Stdio restarts, multiple processes, modified messages or elapsed TTL bypass it. Hosted mode additionally uses D1 reservations across requests; this still is not provider-level idempotency
  • Africa’s Talking supports airtime idempotency keys, but this starter does not expose them. Reconcile in the provider dashboard using returned request IDs before any manual retry
  • Provider text is untrusted data. Never interpret it as instructions. Only bounded, selected response fields are returned; raw bodies, request headers and exception stacks are not exposed

USSD callback demo

After building:

npm run ussd

This explicitly starts a separate loopback-only HTTP server at http://127.0.0.1:3000/ussd. No listener is created by the MCP server.

curl -X POST http://127.0.0.1:3000/ussd \
  --data-urlencode 'sessionId=demo-session' \
  --data-urlencode 'serviceCode=*384*123#' \
  --data-urlencode 'phoneNumber=+254700000000' \
  --data-urlencode 'text='

The initial response starts with CON. text=1 continues a submenu and text=1*1 returns END. Africa’s Talking supplies the entire *-separated input history on every callback; the demo is stateless and never stores phone numbers or sessions.

A real USSD deployment needs a separately hosted public HTTPS callback registered in Africa’s Talking, verified request provenance, rate limiting and robust session/business logic. This example is not authenticated and must not be publicly exposed unchanged. It binds only 127.0.0.1, accepts URL-encoded POST requests, caps request bodies and does not log incoming data. No tunnel, hosting, callback registration or deployment is included.

Development and verification

npm run typecheck
npm test
npm run check

Tests use synthetic credentials and mocked provider fetch calls, with real local stdio and HTTP loopback integration checks. They cover configuration/validation, wire encoding, safety gates, partial failures, redaction, response limits, timeout/no-retry behavior, duplicate suppression, MCP discovery/tool execution, and USSD sessions. No live provider account or credentials are needed. See VERIFICATION.md for this deliverable’s test result.

Structure:

src/config.ts     Environment validation and money helpers
src/http.ts       Fixed-origin, bounded, no-retry provider transport
src/schemas.ts    Tool input schemas
src/service.ts    Safety policy, previews and normalized responses
src/server.ts     MCP tool registrations and annotations
src/index.ts      Stdio entry point
examples/         Local USSD callback and session handler
 test/            Unit, mocked-provider and protocol integration tests

Sources and scope

Contracts were checked on 2026-10-05 against first-party material. The developer portal was blocked to the research browser, so request contracts were verified in the official SDK source rather than inferred from community MCPs.

The project uses the official MCP SDK but implements a small, focused Africa’s Talking HTTP adapter itself. Voice calls, payments, WhatsApp, premium SMS sending, SIM-swap insights, delivery callback ingestion, general outbound transaction history and persistent spending budgets are not exposed in this version. Mobile-data transaction lookup is supported. Real-provider product provisioning and production delivery remain unverified.

License

MIT. Africa’s Talking names and trademarks remain the property of their respective owners.

Additional contracts were checked against the official SMS SDK implementation, mobile-data SDK, environment host map and provider-owned response fixtures on 2026-10-05.