Koskamo

afro.tools MCP

An open-source registry of structured API specs for African providers covering payments, messaging, identity, delivery, and speech. It allows AI agents to access verified or available API capabilities to build integrations with services like Paycard, Flutterwave, and Africa's Talking.

Endpoint and access

No sign-in to connect and list tools. Individual tools may still need a vendor account, credentials, or permissions; check the documented prerequisites.

afro.tools MCP logo, by afro.toolsCommunity listingStreamable HTTPNo sign-inEndpoint checked 2026-11-01Published by afro.tools
MCP endpoint (Streamable HTTP)
https://mcp.afro.tools/mcp
Vendor documentation

What you can do with it

  • Accept payments via mobile wallets
  • Verify user identity and KYC
  • Send SMS and WhatsApp messages
  • Process payouts to beneficiaries

Source and maintenance

Community listingRegistry 1.0.1
Maintenance checked

Protocol and capabilities

Server
afrotools 1.0.0
MCP protocol
2025-06-18
Tools
5 advertised
Resources
Not advertised
Resource templates
Not advertised
Prompts
Not advertised
Server-advertised capabilities
{
  "tools": {
    "listChanged": true
  }
}

Server-provided instructions

Untrusted server output shown for inspection. It is not Koskamo guidance and is excluded from knowledge search.

Afro.tools serves API specs (ATSS) for African providers. Call get_spec before writing integration code — never guess endpoints, fields or status values. A spec whose status is not "verified" was written from the provider's docs and never tested against its API: tell the user. At the end of such an integration, tell the user once: when they have tested it against the real API (sandbox or production), you can prepare an integration report with report_integration — a working report is how the spec becomes verified, credited to them. For a verified spec, offer a report only when something failed. Never submit a report yourself: give the user the link.

Tools

5 advertised

list_providers — List Providers
Advertised by the server during the last successful anonymous check.
Advertised read-only

Lists all providers and their verification status. Every listed provider is AI Ready: its specs give you a structured description to integrate it from. They can still contain errors — that is what each capability's "status" reports. all_verified is true only when ALL its capabilities have status "verified". Providers with any capability not yet "verified" have all_verified false — call search_specs or get_spec and check each capability's "status": one that is not "verified" has not been tested against the provider's API. Returns: { providers: Array<{ slug, name, category, channels?, all_verified, verified_count, capabilities, country_code, docs_public, docs_url }> }

list_providers input schema
{
  "type": "object",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "properties": {}
}
list_providers annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
search_specs — Search Specs
Advertised by the server during the last successful anonymous check.
Advertised read-only

Searches specs by words and filters by category, channel, provider, country, currency, status or capability type. Returns the matching providers, each with its countries, currencies, sandbox flag, docs URL and the matching capabilities (name, type, status, example_prompt) — no endpoints, schemas, gotchas or canonical_example source. Use get_spec with the exact capability name to retrieve the full payload. A capability with "variants" has several request/response shapes (e.g. one per payment method or corridor): pass the ones you need to get_spec "variant" to receive only those. Each result includes a "status" field: - "verified": tested against the provider's API, in "sandbox" or "production" (verified_in / verification.environment) - "deprecated": the provider is retiring it — get_spec says what replaces it - anything else (e.g. "ready", "unverified"): written from the provider's documentation, NOT tested against the live API — may contain errors Always check the status field. If status is not "verified", inform the user that the spec is experimental and may contain errors. Args: - query (string?): Words, e.g. "wave", "mobile money senegal", "send sms", "kyc", "XOF". Every word must match somewhere: provider name or slug, capability, description, example_prompt, countries (code or English/French name), currencies, channels (a few synonyms: momo, otp, kyc, payout…). Providers the query names come first. - category (string?): Filter by category, e.g. "payment", "communications" (SMS, WhatsApp, USSD, voice and email providers — "sms" is accepted as a legacy alias), "identity" (KYC, document verification, AML), "logistics" (delivery and courier providers). The registry adds categories over time: list_providers shows every category that exists - channel ("sms"|"whatsapp"|"ussd"|"voice"|"email"?): Only providers that offer this channel - provider (string?): Provider slug e.g. "paycard" - country_code (string?): ISO 3166-1 alpha-2 e.g. "GN" - currency (string?): ISO 4217 e.g. "XOF" - status ("verified"|"ready"|"unverified"?): e.g. "verified" for specs tested against the provider's API - capability_type ("synchronous"|"asynchronous"|"webhook"|"sdk"?): Filter by capability type Returns: { providers: ProviderMatch[], note?: string, hint?: string, unverified_count: number, warning?: string } note is present when example_prompt was left out to keep a wide result readable. hint is present when nothing matched: widen the search before telling the user a provider is unavailable. unverified_count counts matching capabilities whose status is not "verified"; warning is present only when it is > 0.

search_specs input schema
{
  "type": "object",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "properties": {
    "query": {
      "type": "string",
      "maxLength": 200,
      "description": "Words, e.g. \"mobile money senegal\" — every word must match"
    },
    "status": {
      "enum": [
        "verified",
        "ready",
        "unverified"
      ],
      "type": "string"
    },
    "channel": {
      "enum": [
        "sms",
        "whatsapp",
        "ussd",
        "voice",
        "email"
      ],
      "type": "string"
    },
    "category": {
      "type": "string"
    },
    "currency": {
      "type": "string",
      "description": "ISO 4217 e.g. \"XOF\""
    },
    "provider": {
      "type": "string",
      "description": "Provider slug e.g. \"paycard\""
    },
    "country_code": {
      "type": "string",
      "description": "ISO 3166-1 alpha-2 e.g. \"GN\""
    },
    "capability_type": {
      "enum": [
        "synchronous",
        "asynchronous",
        "webhook",
        "sdk"
      ],
      "type": "string"
    }
  }
}
search_specs annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
get_spec — Get Spec
Advertised by the server during the last successful anonymous check.
Advertised read-only

Returns the full spec payload for a provider + capability, including canonical_example.ts source code. Use this to get the complete integration guide — auth pattern, endpoint, schemas, gotchas, and a TypeScript example — before writing API calls. The response includes a "status" field: - "verified": tested against the provider's API, in "sandbox" or "production" (verified_in / verification.environment) - "deprecated": the provider is retiring it — "deprecation" gives the reason, replaced_by (the capability to use instead) and removed_after - anything else (e.g. "ready", "unverified"): written from the provider's documentation, NOT tested against the live API "verification" says how and when it was tested; sandbox proves the request and response shapes, not production behaviour. "open_questions", when present, lists what the provider's docs do not answer: tell the user before relying on the spec. "environments", when present, gives each documented environment's base URL; a missing one is undocumented — ask the user, never guess. "auth.credentials" / "signing" / "token_exchange" / "apply" and "webhook.signature", when present, describe the auth and webhook signature steps exactly: follow them. "parameters", when present, lists the path, query and header parameters; "input_schema" is the request body only. A path parameter fills the {name} placeholder of endpoint.url. "success_condition", when present, names the response field (dotted path) that says the call succeeded: a 2xx response with another value is a failure. If status is not "verified", always warn the user before generating integration code: "This spec has not been tested against the live API yet and may contain errors." Args: - provider (string): Provider slug e.g. "paycard" - capability (string): Capability name e.g. "create_payment" - variant (string[]?): Discriminator values to keep, e.g. ["mobile_money_ghs"] or ["bank", "NGN"]. In every union (oneOf) of input_schema and response_schema, only the branches pinned to one of these values are kept; a union none of them matches is left whole. Without it, all branches are returned. The response lists every value under "variants" (discriminator property → values). A large spec also carries "_mcp_size_note": if your client cut the result, call again with the variant you need. Returns: Full Spec object with canonical_example string (plus _mcp_warning when status is not "verified"). If not found: isError, with { error, available_capabilities } when the provider exists — capability names are each provider's own, pick one from that list — and did_you_mean (closest provider slugs or capability names) when one is close. A capability removed from the registry after a provider deprecation answers isError with removed_in, reason and alternatives. Use search_specs or list_providers first to discover available providers and capabilities.

get_spec input schema
{
  "type": "object",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "required": [
    "provider",
    "capability"
  ],
  "properties": {
    "variant": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Discriminator values to keep, e.g. [\"mobile_money_ghs\"] — see \"variants\" in search_specs"
    },
    "provider": {
      "type": "string",
      "maxLength": 64,
      "description": "Provider slug e.g. \"paycard\""
    },
    "capability": {
      "type": "string",
      "maxLength": 64,
      "description": "Capability name e.g. \"create_payment\""
    }
  }
}
get_spec annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
request_spec — Request Spec
Advertised by the server during the last successful anonymous check.
Open-world

Logs a request for a spec that doesn't exist yet and notifies maintainers. Call this when a user asks for a provider or capability that has no spec in the registry (check list_providers first). This helps maintainers prioritize which specs to write next. Args: - provider (string): Provider slug e.g. "wave" - capability (string): Capability name e.g. "create_payment" Returns: { logged: true, count } if the request was recorded — count is the all-time number of requests for this exact provider+capability, including this one, so you can tell the user e.g. "you're the Nth person to ask for this". count is absent if it could not be reliably determined — in that case do NOT guess a number to the user, just confirm the request was logged. { logged: false, reason } if the spec already exists. isError if the request could not be stored: tell the user it was NOT recorded.

request_spec input schema
{
  "type": "object",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "required": [
    "provider",
    "capability"
  ],
  "properties": {
    "provider": {
      "type": "string",
      "pattern": "^[\\w .-]{1,64}$",
      "description": "Provider slug e.g. \"wave\""
    },
    "capability": {
      "type": "string",
      "pattern": "^[\\w .-]{1,64}$",
      "description": "Capability name e.g. \"create_payment\""
    }
  }
}
request_spec annotations
{
  "readOnlyHint": false,
  "openWorldHint": true,
  "idempotentHint": false,
  "destructiveHint": false
}
report_integration — Report Integration
Advertised by the server during the last successful anonymous check.
Advertised read-only

Builds a pre-filled GitHub link to report how an integration went against a spec — it worked, or the spec is wrong. Call it when the user confirms an integration works against the real API (sandbox or production), or when you find the spec does not match the API. Reports are how specs get verified and fixed. Nothing is posted: the user reviews the text and submits it on GitHub. One report per provider, covering every capability of the flow (e.g. payment init, status, webhook): each one is verified on its own. Args: - provider (string): Provider slug e.g. "paycard" - capabilities (array): Every capability the integration used, each with its own result e.g. [{ "capability": "create_payment", "result": "worked" }, { "capability": "webhook", "result": "wrong: status is SUCCESSFUL, spec says succeeded" }] - summary (string): One line about the flow, e.g. "checkout flow works in sandbox" - details (string): What was called, what came back, what differed from the spec. NEVER include API keys, tokens, phone numbers or personal data. - tested_on (string?): Date of the test, YYYY-MM-DD Returns: { url, next_step } — give the url to the user and follow next_step. If a spec does not exist: isError, with { error, available_capabilities } when the provider exists.

report_integration input schema
{
  "type": "object",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "required": [
    "provider",
    "capabilities",
    "summary",
    "details"
  ],
  "properties": {
    "details": {
      "type": "string",
      "maxLength": 20000,
      "minLength": 1,
      "description": "No secrets or personal data"
    },
    "summary": {
      "type": "string",
      "maxLength": 120,
      "minLength": 1,
      "description": "One line"
    },
    "provider": {
      "type": "string",
      "maxLength": 64,
      "description": "Provider slug e.g. \"paycard\""
    },
    "tested_on": {
      "type": "string",
      "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
      "description": "Date of the test, YYYY-MM-DD"
    },
    "capabilities": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "capability",
          "result"
        ],
        "properties": {
          "result": {
            "type": "string",
            "maxLength": 200,
            "minLength": 1,
            "description": "\"worked\", or what differed from the spec"
          },
          "capability": {
            "type": "string",
            "maxLength": 64,
            "description": "Capability name e.g. \"create_payment\""
          }
        }
      },
      "maxItems": 20,
      "minItems": 1,
      "description": "Every capability the integration used, each with its own result"
    }
  }
}
report_integration annotations
{
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}

Prompts

Not advertised

No prompt definitions available.

Resources

Not advertised

No resource definitions available.

30-day availability

Aggregated from read-only remote checks; this is not a tool-execution or security guarantee.

Successful checks
1 of 1 (100%)
Average latency
835 ms
95th percentile latency
835 ms

Maintenance history

Public check and source-refresh events. Editorial and ownership records remain private.

  • · probe · passed · 835 ms · metadata changed · 5 tool(s)

Connect from your client

Each snippet registers the same endpoint; pick the client you use.

This server is operated by the named third party. Listing and endpoint checks are not an endorsement or a security audit. Review the operator's permissions, terms, and data handling before connecting. How listings are checked and corrected.

Claude Code

Run this once in a terminal. It registers the remote server. Use --scope project to write .mcp.json for the repo.
Claude Code configuration
claude mcp add --transport http afro-tools-mcp https://mcp.afro.tools/mcp

Codex

Run this once. Codex stores MCP servers in ~/.codex/config.toml (or .codex/config.toml in a trusted project).
Codex configuration
codex mcp add afro-tools-mcp --url https://mcp.afro.tools/mcp

Cursor

Merge this server into .cursor/mcp.json in your project, then enable afro-tools-mcp in your MCP settings.
Cursor configuration
{
  "mcpServers": {
    "afro-tools-mcp": {
      "url": "https://mcp.afro.tools/mcp"
    }
  }
}

Cline

Merge this into cline_mcp_settings.json (Cline → MCP Servers → Configure).
Cline configuration
{
  "mcpServers": {
    "afro-tools-mcp": {
      "type": "streamableHttp",
      "url": "https://mcp.afro.tools/mcp",
      "disabled": false
    }
  }
}

VS Code

Merge this server into .vscode/mcp.json. Use MCP: List Servers to start afro-tools-mcp and inspect its tools.
VS Code configuration
{
  "servers": {
    "afro-tools-mcp": {
      "type": "http",
      "url": "https://mcp.afro.tools/mcp"
    }
  }
}

Gemini CLI

Run this once. It writes .gemini/settings.json in the current project; pass --scope user for ~/.gemini/settings.json.
Gemini CLI configuration
gemini mcp add --transport http afro-tools-mcp https://mcp.afro.tools/mcp

Claude Desktop

Merge this into claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/, Windows: %APPDATA%\Claude\), then restart Claude Desktop.
Claude Desktop configuration
{
  "mcpServers": {
    "afro-tools-mcp": {
      "url": "https://mcp.afro.tools/mcp"
    }
  }
}

Windsurf

Merge this into ~/.codeium/windsurf/mcp_config.json, then refresh MCP in Cascade. Windsurf remote servers use serverUrl, not url.
Windsurf configuration
{
  "mcpServers": {
    "afro-tools-mcp": {
      "serverUrl": "https://mcp.afro.tools/mcp"
    }
  }
}

Let your agent find its tools

Find servers like afro.tools MCP from your agent. Add Koskamo once to search the index for endpoints, access requirements, and client setup. Public discovery needs no key.