betatel-mcp-server
v0.2.1
Published
Betatel Model Context Protocol (MCP) server — 15 tools for phone verification: 4 free no-key discovery tools (country/operator coverage, verification pricing, cost estimate) plus 11 send/verify tools (SMS, Flash Call, WhatsApp OTP, Telegram OTP + sandbox
Maintainers
Readme
betatel-mcp-server
Model Context Protocol (MCP) server for Betatel phone verification.
Try it free — no API key. Ask which channels and operators cover phone verification in any country, and what it costs. Then add a key to send SMS / Flash Call / WhatsApp / Telegram OTPs. One MCP server, four channels.
It exposes 15 tools to any MCP-compatible AI assistant (Claude Desktop, Cursor, Smithery's hosted runner, …): 4 free discovery tools (no credentials) and 11 send / OTP-verify tools (API key required).
Discovery (no API key required)
These read-only tools answer coverage, pricing, and cost questions without any credentials. Data comes from a bundled snapshot regenerated from Betatel's source of truth at release time; each response carries a generatedAt timestamp, and the binding price is always the one the API returns at send time.
| Tool | What it does |
|--------------------------------------|---------------------------------------------------------------------------|
| betatel_list_supported_countries | Which countries Betatel covers and which channels are available in each. |
| betatel_get_verification_pricing | Per-channel verification price (with currency, usually EUR) for a country, by ISO code or name. |
| betatel_check_operator_coverage | Mobile operators (MCC-MNC) in a country + whether Flash Call is supported. |
| betatel_estimate_cost | Total cost of N verifications in a country, ranked cheapest-channel-first. |
Send
| Tool | What it does |
|---------------------------------|-------------------------------------------------------------------|
| betatel_send_sms | Send an SMS through the Betatel SMS gateway. |
| betatel_flash_call | Trigger a flash call (silent ring-and-hangup) for verification. |
| betatel_send_telegram_otp | Deliver an OTP via the Telegram Gateway. |
| betatel_send_whatsapp_otp | Deliver an OTP-style WhatsApp message via 360dialog. |
Sandbox
Same request shape as the matching send tool, but the Betatel API returns a mocked response without dialing the carrier or billing the account. Use for integration tests or to confirm credentials are wired before going live.
betatel_sms_sandbox, betatel_flash_call_sandbox, betatel_telegram_sandbox, betatel_whatsapp_sandbox.
Telegram OTP lifecycle
| Tool | What it does |
|-----------------------------------------------|-----------------------------------------------------------------------|
| betatel_telegram_check_send_ability | Preflight: can Telegram Gateway deliver to this number? |
| betatel_telegram_check_verification_status | Verify the code the user typed against the OTP we sent. |
| betatel_telegram_revoke_verification | Cancel a pending Telegram OTP. |
Out of scope (per issue #610)
Reports / CDR / SDR / TDR / WDR queries, billing, stats, carrier config (CID / NCheck), SNA, WhatsApp channel management / onboarding, webhooks, access-hub admin. These can ship in a follow-up package or expansion later.
Install
The server runs on Node.js 22+ and is published to npm.
Claude Desktop / Cursor / any stdio MCP client
Add this entry to your MCP client config (claude_desktop_config.json on macOS lives in ~/Library/Application Support/Claude/):
For discovery only (no account needed), the minimal config is:
{
"mcpServers": {
"betatel": {
"command": "npx",
"args": ["-y", "betatel-mcp-server"]
}
}
}To also enable the send tools, add your credentials:
{
"mcpServers": {
"betatel": {
"command": "npx",
"args": ["-y", "betatel-mcp-server"],
"env": {
"BETATEL_API_KEY": "<your Betatel API key>",
"BETATEL_ACCOUNT_ID": "<your Betatel account ID>"
}
}
}
}Restart the client and the tools will appear in its tool picker. Without credentials the 4 discovery tools work; the send tools return a friendly "needs an API key" message.
Streamable HTTP (remote / hosted)
Run the HTTP transport as a long-lived process:
BETATEL_API_KEY=... \
BETATEL_ACCOUNT_ID=... \
PORT=3000 \
npx -y -p betatel-mcp-server betatel-mcp-server-httpThe MCP endpoint is mounted at POST /mcp. There is a GET /health for liveness checks.
Configuration
| Env var | Required | Default | Notes |
|--------------------------|----------|-----------------------------|----------------------------------------------------------------------------------------|
| BETATEL_API_KEY | send only| "" | Sent as x-api-key on every upstream call. Leave empty for discovery-only mode. |
| BETATEL_ACCOUNT_ID | send only| "" | Sent as x-account-id on every upstream call. Leave empty for discovery-only mode. |
| BETATEL_API_BASE_URL | no | https://api.betatel.com | Override for staging (https://api-dev.betatel.com) or local development. |
| PORT | no | 3000 | HTTP transport listen port. Ignored by the stdio bin. |
Agent Skills
Two Anthropic Agent Skills ship in the package under skills/:
betatel-messaging— umbrella how-to covering all four channels.betatel-otp— focused playbook for OTP / phone verification flows.
To install them into Claude Code, copy the directories into ~/.claude/skills/ (or a project-local .claude/skills/):
cp -r "$(npm root -g)/betatel-mcp-server/skills/"* ~/.claude/skills/(If you installed the package locally instead of globally, substitute the local path.)
Programmatic use
import { createServer } from "betatel-mcp-server";
const server = createServer({
apiKey: process.env.BETATEL_API_KEY!,
accountId: process.env.BETATEL_ACCOUNT_ID!,
baseUrl: "https://api.betatel.com"
});createServer returns an McpServer instance from @modelcontextprotocol/sdk that you can wire to any transport.
Tool reference
All recipient phone numbers MUST be E.164 (e.g. +38161444555). Generate OTP codes in your application — the server never invents them.
Discovery tools (no auth)
betatel_list_supported_countries({ channel?: "sms" | "flash-call" | "telegram" | "whatsapp" })
betatel_get_verification_pricing({ country: string /* ISO or name */ })
betatel_check_operator_coverage({ country: string })
betatel_estimate_cost({ country: string; verifications: number; channel?: DiscoveryChannel; compare?: boolean })Each returns structuredContent including generatedAt (snapshot date) and source. Pricing is an indicative standard rate; the binding price is what the API returns at send time. An unknown country returns a graceful { found: false } result rather than an error.
betatel_send_sms
{ to: string; text: string; from?: string }Returns { messageId, from, to }.
betatel_flash_call
{ callee: string; caller?: string; max_ring_time?: number; callback_url?: string }Returns { uuid, caller, callee }.
betatel_send_telegram_otp
{ to: string; code: string; ttl?: number; sender_username?: string; callback_url?: string }code is 4–8 digits. ttl is 30–3600 seconds. Returns { uuid, request_id, to, status, ... }.
betatel_send_whatsapp_otp
{ to: string; text: string }Returns { uuid, to }.
Development (inside the Betatel monorepo)
pnpm --filter betatel-mcp-server build
pnpm --filter betatel-mcp-server test
pnpm --filter betatel-mcp-server check-types
pnpm --filter betatel-mcp-server lintSmoke-test the stdio build against a local MCP inspector:
npx @modelcontextprotocol/inspector node packages/mcp-server/dist/transports/stdio.jsLicense
ISC. © Betatel LTD.
