DEV Community

Gabriel Mahia
Gabriel Mahia

Posted on

Building Secure AI Infrastructure for Africa: OWASP MCP Top 10 in Practice

The OWASP foundation just published the OWASP MCP Top 10 (2025) — the first dedicated security taxonomy for Model Context Protocol servers. For anyone building MCP tools that handle real-world data (payments, health records, legal documents), this is the most important security document to read right now.

Here's how we implemented every control in mpesa-mcp — Kenya's M-PESA Daraja API as an MCP server.

Why MCP Security Is Different

Traditional API security is about protecting your server from requests. MCP security is about protecting your tools from the AI agent using them.

The core vulnerability in MCP systems (arXiv:2603.21642, 2026) is that an AI agent decides which tools to call and with what parameters — based on natural language. An attacker who can inject text into what the agent reads (a document, a web page, a returned API response) can redirect tool calls without ever touching application code. The MCP-38 threat taxonomy calls this the tool description poisoning and indirect prompt injection class of attacks.

This is structurally new. It has no direct equivalent in classical software security.

How mpesa-mcp Addresses Each Risk

MCP01: Token Mismanagement & Secret Exposure

# Wrong — never do this
consumer_key = "hXvqiOG2yy0PfvxkFHd3..."  # hardcoded

# Right — all credentials via environment variables
consumer_key = os.environ["MPESA_CONSUMER_KEY"]
Enter fullscreen mode Exit fullscreen mode

OAuth tokens are cached in-memory only (1-hour expiry, auto-refreshed). Never written to disk. Never logged. All phone numbers in audit logs are SHA-256 hashed:

def _audit(tool, params, outcome):
    safe = {
        k: hashlib.sha256(str(v).encode()).hexdigest()[:8] + "..."
        if k in {"phone", "party_a", "party_b"}
        else str(v)
        for k, v in params.items()
    }
    _log.info("TOOL=%s PARAMS=%s OUTCOME=%s", tool, safe, outcome)
Enter fullscreen mode Exit fullscreen mode

MCP02: Tool Poisoning & Prompt Injection

Tool descriptions in mpesa-mcp are static, versioned in source, and published via PyPI Trusted Publisher (OIDC). An attacker cannot modify them without compromising GitHub Actions. All inputs are validated by type before any API call:

def mpesa_stk_push(
    phone: Annotated[str, "Customer phone (any Kenyan format)"],
    amount: Annotated[int, "Amount in KES (whole number, min 1, max 150000)"],
    account_ref: Annotated[str, "Account reference (max 12 chars)"],
) -> dict:
    if not 1 <= amount <= 150_000:
        return {"error": f"Amount {amount} out of bounds [1, 150000]"}
    # normalize phone to E.164 before API call
    phone = _normalize_phone(phone)
Enter fullscreen mode Exit fullscreen mode

MCP03: Excessive Agency — Tool Annotations

All 22 tools in mpesa-mcp declare MCP tool annotations so clients can gate calls correctly:

@mcp.tool(annotations={
    "readOnlyHint": True,      # query tools: can't modify state
    "destructiveHint": False,  # or False for write tools: requires confirmation
    "idempotentHint": True     # safe to retry
})
def mpesa_stk_query(checkout_request_id: str) -> dict: ...

@mcp.tool(annotations={
    "readOnlyHint": False,
    "destructiveHint": True,   # Claude Desktop will show confirmation dialog
    "idempotentHint": False
})
def mpesa_stk_push(...) -> dict: ...
Enter fullscreen mode Exit fullscreen mode

This is the single most important control. A well-implemented MCP client won't call mpesa_stk_push without showing the user a confirmation — because destructiveHint: true signals that the action cannot be undone.

MCP06: Audit Logging

Every tool call is logged with structured metadata, PII removed:

TOOL=mpesa_stk_push PARAMS={'phone': 'a3f2b1c4...', 'amount': '500'} OUTCOME=INITIATED
Enter fullscreen mode Exit fullscreen mode

No credentials, no full phone numbers, no transaction receipts in logs.

The NSA/CISA Alignment

The joint NSA/CISA guidance U/OO/143395-24 (April 2024) and the newer AI Data Security guidance (May 2025) both emphasize six principles:

  1. Govern the AI deployment (versioned releases, CHANGELOG, threat model)
  2. Understand the model and environment (documented data flows in README)
  3. Validate before deployment (CI/CD: lint + test gates)
  4. Secure infrastructure (HTTPS only, env vars, no credential in code)
  5. Secure at application layer (input validation, structured outputs)
  6. Operationalize security (audit logging, VDP at contact@aikungfu.dev)

mpesa-mcp implements all six. The formal SECURITY.md documents each control with specific code references.

Why This Matters for Africa

AI infrastructure in Africa handles genuinely high-stakes operations. M-PESA processes more transactions per day than PayPal in Africa. Drought alerts dispatched via wapimaji-mcp may be the first notification a community leader receives that their county is entering Phase 4 Emergency.

The OWASP MCP Top 10 was written primarily with Western fintech and enterprise use cases in mind. The controls apply just as well — and arguably more critically — to financial infrastructure serving 35 million people who have fewer remediation options when things go wrong.

Security is not a checkbox. It's the condition that makes everything else meaningful.


Repos:

Security docs:

Portfolio: gabrielmahia.github.io

Top comments (0)