Skip to main content
Glama
SMS.ir MCP server

SMS.ir MCP server

by ali-toghiani

SMS.ir MCP server

A local Model Context Protocol server that exposes a curated, safety-gated set of tools for the SMS.ir Panel V2 API. Built with Python + FastMCP.

  • stdio transport for Codex / Claude Desktop / Claude Code

  • streamable HTTP transport for local development and testing

  • Read operations work out of the box; every send is billable and blocked by default behind a confirmation flag and a server-side kill switch.

  • Phone numbers, message text, API keys and OTP codes are masked in logs.

Built from the SMS.ir Panel V2 Postman collection (not included in this repo — it embeds a real API key). The normalized API description lives in docs/API.md and docs/openapi.yaml.


1. Setup

Requires Python 3.10+ (developed and tested on CPython 3.12).

cd C:\Users\Kasra\Documents\sms.ir-mcp

# create the project-local virtual environment
py -3.12 -m venv .venv

# install runtime deps (pinned)
.\.venv\Scripts\python.exe -m pip install -r requirements.txt

# ...or install with the package + dev/test extras
.\.venv\Scripts\python.exe -m pip install -e ".[dev]"

Related MCP server: meta-mcp-py

2. Configuration

All configuration comes from environment variables. For local use, copy the example env file and fill it in — it is git-ignored and never committed:

copy .env.example .env
notepad .env

Variable

Required

Default

Purpose

SMSIR_API_KEY

yes

–

SMS.ir Panel API key, sent as the X-API-KEY header

SMSIR_DEFAULT_LINE_NUMBER

no

–

Fallback sender line for send tools

SMSIR_ALLOW_SEND

no

false

Kill switch. Must be true for any real send to leave the process

SMSIR_BASE_URL

no

https://api.sms.ir

API base URL (host-allowlisted)

SMSIR_ALLOW_CUSTOM_BASE_URL

no

false

Allow a non-api.sms.ir host (local mocks only)

SMSIR_TIMEOUT_SECONDS

no

15

Per-request timeout

SMSIR_MAX_RETRIES

no

2

Retries for transient failures (429 / 5xx / network)

SMSIR_RATE_LIMIT_PER_MINUTE

no

60

Client-side rate limit

SMSIR_MAX_PAGE_SIZE

no

200

Upper bound accepted for page_size

SMSIR_LOG_LEVEL

no

INFO

DEBUG / INFO / WARNING / ERROR

SMSIR_ENV_FILE

no

./.env

Path to the env file to auto-load

Real environment variables always override values from the env file.

3. Running

# stdio (what MCP clients launch)
.\.venv\Scripts\python.exe -m sms_ir_mcp --transport stdio

# streamable HTTP for local testing (http://127.0.0.1:8000/mcp)
.\.venv\Scripts\python.exe -m sms_ir_mcp --transport http --host 127.0.0.1 --port 8000

For a quick connectivity + auth check that never spends credit, call the health_check tool (or get_balance) from any connected client — both are GET /v1/credit under the hood.

4. Tools

Read-only tools are always available. Write tools require confirm=true and SMSIR_ALLOW_SEND=true; the destructive tool requires confirm=true.

Tool

Kind

API

Description

get_balance

read

GET /v1/credit

Remaining SMS credit

list_lines

read

GET /v1/line

Sender line numbers / virtual numbers

get_message_report

read

GET /v1/send/{id}

Delivery report/status for one sent message

get_pack_report

read

GET /v1/send/pack/{packId}

Per-recipient results for a bulk pack (paginated)

list_sent_messages

read

GET /v1/send/live · /archive

Sent messages, scope=today|archive

list_sent_packs

read

GET /v1/send/pack · /archive/pack

Bulk packs, scope=today|archive

list_inbound_messages

read

GET /v1/receive/latest · /live · /archive

Inbound messages, scope=latest|today|archive

extract_latest_otp

read

GET /v1/receive/latest

Newest inbound message containing a parseable one-time code (heuristic)

health_check

read

GET /v1/credit

Reachability + auth check, never billable; also returns effective config

reload_config

admin

–

Re-read .env / env vars without restarting the server (e.g. after flipping SMSIR_ALLOW_SEND); sends nothing

send_sms

billable

POST /v1/send/bulk

One text to one or more recipients

send_verification_code

billable

POST /v1/send/verify

Templated OTP/verification message

send_personalized_sms

billable

POST /v1/send/likeToLike

A distinct text per recipient

cancel_scheduled_send

destructive

DELETE /v1/send/scheduled/{packId}

Cancel a not-yet-sent scheduled pack

Every tool returns {"ok": true, "data": …, …} on success or {"ok": false, "error": {"code": …, "message": …}} on failure. Error codes: config_error, validation_error, confirmation_required, send_disabled, auth_error, rate_limited, transient_error, api_error, internal_error.

Examples

// check balance
get_balance() -> {"ok": true, "data": {"credit": 45210}}

// read the latest OTP received on a given number
extract_latest_otp({"mobile": "9821000"})
  -> {"ok": true, "data": {"otp": "834122", "matched": true, "from": "*****1000", ...}}

// attempt a send without confirming -> refused, nothing sent
send_sms({"message_text": "Hi", "mobiles": ["09121234567"]})
  -> {"ok": false, "error": {"code": "confirmation_required", ...}}

// confirmed send, but kill switch still off -> refused, nothing sent
send_sms({"message_text": "Hi", "mobiles": ["09121234567"], "confirm": true})
  -> {"ok": false, "error": {"code": "send_disabled", ...}}

// edited .env to set SMSIR_ALLOW_SEND=true -> apply it without restarting
reload_config()
  -> {"ok": true, "data": {"config": {"allow_send": true, ...}, "changed": ["allow_send"]}}

// with SMSIR_ALLOW_SEND=true AND confirm=true -> actually sends (billable)
send_sms({"message_text": "Hi", "mobiles": ["09121234567"],
          "line_number": "30007732000000", "confirm": true})
  -> {"ok": true, "data": {"packId": "…", "messageIds": [123], "cost": 1.0}, "recipients": 1}

5. Safety model

  • Billable operations (send_sms, send_verification_code, send_personalized_sms) need both:

    1. confirm=true in the tool call, and

    2. SMSIR_ALLOW_SEND=true in the server environment. With the kill switch off, a confirmed call still sends nothing.

  • Destructive operation (cancel_scheduled_send) needs confirm=true.

  • No arbitrary base URLs: only api.sms.ir is accepted unless SMSIR_ALLOW_CUSTOM_BASE_URL=true. HTTPS is enforced.

  • No header injection: callers cannot set request headers; only typed, validated fields are forwarded.

  • Timeouts + bounded retries + client-side rate limiting on every request.

  • Masking: API keys, phone numbers, message bodies and OTP values are masked in log output.

  • No admin endpoints: only the operations in the Postman collection are exposed; nothing for account/config management.

6. Client registration

Your real API key goes in .env in this folder — never in a client config file. Each config below only points the client at this server and its .env.

Codex CLI (installed)

codex mcp add sms-ir `
  --env SMSIR_ENV_FILE=C:\Users\Kasra\Documents\sms.ir-mcp\.env `
  -- C:\Users\Kasra\Documents\sms.ir-mcp\.venv\Scripts\python.exe -m sms_ir_mcp --transport stdio

codex mcp list          # sms-ir should appear
codex mcp get sms-ir

Manual equivalent: docs/codex_config.example.toml.

Claude Code (installed)

This repo ships a project-scoped .mcp.json. Open Claude Code in this directory and approve the sms-ir server when prompted:

cd C:\Users\Kasra\Documents\sms.ir-mcp
claude
/mcp                    # shows sms-ir and its tools

To register it at user scope instead:

claude mcp add sms-ir --scope user `
  --env SMSIR_ENV_FILE=C:\Users\Kasra\Documents\sms.ir-mcp\.env `
  -- C:\Users\Kasra\Documents\sms.ir-mcp\.venv\Scripts\python.exe -m sms_ir_mcp --transport stdio

Claude Desktop (not installed)

When installed, merge docs/claude_desktop_config.example.json into %APPDATA%\Claude\claude_desktop_config.json (back it up first; keep other servers).

7. Development

.\.venv\Scripts\python.exe -m ruff check src tests
.\.venv\Scripts\python.exe -m ruff format --check src tests
.\.venv\Scripts\python.exe -m pytest

Tests cover request construction, auth header, envelope parsing, error normalization, retries/rate-limiting, argument validation, masking, OTP extraction, mocked integration for every tool (using the Postman example payloads), OpenAPI-vs-collection consistency, and MCP tool discovery.

8. First live test (after you provide a credential)

Nothing in this repo has made a billable call. To run the first real send, which you explicitly authorize:

  1. Put your key in .env:

    SMSIR_API_KEY=<your real key>
    SMSIR_DEFAULT_LINE_NUMBER=<your approved line>
    SMSIR_ALLOW_SEND=true
  2. Verify connectivity without spending anything — from a connected client call health_check (or get_balance).

  3. Then, and only then, make the first billable call. Exact tool call:

    send_sms({
      "message_text": "SMS.ir MCP test",
      "mobiles": ["<your own mobile>"],
      "line_number": "<your approved line>",
      "confirm": true
    })

    Codex phrasing: "Use the sms-ir server's send_sms tool to send 'SMS.ir MCP test' to from line , with confirm true."

9. Troubleshooting

Symptom

Cause / fix

config_error: SMSIR_API_KEY is not set

No key in env or .env; check SMSIR_ENV_FILE path the client passes

auth_error on every call

Wrong/rotated key, or key lacks Panel API access

send_disabled

SMSIR_ALLOW_SEND is not true in the server environment

Edited .env but nothing changed

The server reads config once at startup. Call reload_config, or restart the MCP client so it respawns the server

confirmation_required

Re-call the tool with "confirm": true

validation_error: Invalid mobile number

Use 10–15 digits, optional leading +

rate_limited

Client-side limiter tripped; raise SMSIR_RATE_LIMIT_PER_MINUTE or slow down

transient_error

Network/5xx after retries; check connectivity and SMS.ir status

Client shows no tools

Wrong command path in the client config; point at .venv\Scripts\python.exe

api_error with api_status

SMS.ir rejected the request; message carries their reason

Related MCP Connectors

Related MCP Servers

  • A
    license
    C
    quality
    C
    maintenance
    Enables reading analytics and publishing content to Facebook Pages, Instagram, and Threads via Meta's Graph API, with a safety toggle for write operations.
    56
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables sending and managing SMS, voice messages, contacts, templates, blacklists, and account reports through the przypominamy.com REST API, with key-scoped authorization and safety confirmations for destructive actions.
    MIT