VirtualSMS
    VirtualSMS
    Ask AI:
    ← Back to Blog
    Technical

    Published: April 28, 2026 | 12 min read

    Already using Claude API for production workloads and want to add SMS verification as a capability? This post ships four production patterns built on the VirtualSMS MCP server — single-shot verification, parallel batch, multi-step reasoning over failures, and a verify-then-persist pipeline. All four share the same MCP connection and differ only in prompt structure.

    Anthropic Claude + VirtualSMS MCP — Production Workflow Patterns (2026)

    Anthropic Claude + VirtualSMS MCP workflow — production phone verification patterns

    Why Claude + MCP for Phone Verification

    For most production phone-verification workloads, the integration code is the bottleneck — you write a tool wrapper, you write a dispatcher, you write retry logic, you write JSON-schema specs, and every change to the underlying API breaks something downstream. Anthropic Claude with MCP collapses all of that into "connect a server URL, prompt Claude." Once the VirtualSMS MCP server is wired into a Claude API call (via mcp_servers), Claude Desktop, or Claude Code, the 18 verification tools become available without any client-side glue.

    This post is the production deep-dive for devs already using Claude API for real workloads. The Claude MCP setup guide covers install + tool catalog; this one covers the patterns that emerge once the connection is live: single-shot, batch, multi-step retry, and the verify-then-persist pipeline. All four patterns share the same MCP connection and only differ in prompt structure.

    • One MCP connection, four production patterns. No code change between patterns — only the system + user prompt evolve.
    • Same 18-tool catalog as Claude Desktop. Whatever you tested in the desktop UI ports verbatim to the API.
    • Auto-refund on no-SMS means cost paranoia in retry loops disappears — Claude can branch over failures without tipping budget.
    • Bearer-token auth, no KYC, no platform fee. Same key as the REST API; switching paths within a single org is free.

    Pattern 1: Single-Shot Verification (Claude API + MCP)

    The smallest production unit: one prompt, one number verified, one return. Useful for ad-hoc verifications, lead-capture flows that need a verified contact, or as a building block inside a larger non-Claude pipeline.

    import anthropic
    client = anthropic.Anthropic()
    
    resp = client.messages.create(
        model="claude-opus-4-7",
        max_tokens=2048,
        mcp_servers=[{
            "type": "url",
            "url": "https://mcp.virtualsms.io/mcp",
            "name": "virtualsms",
            "authorization_token": os.environ["VIRTUALSMS_API_KEY"],
        }],
        messages=[{
            "role": "user",
            "content": "Buy a UK Telegram number, wait for the SMS, return phone + code as JSON.",
        }],
    )
    # Single-turn: Claude orchestrates buy_number → wait_for_code → return.
    # No retry boilerplate; if the activation fails, Claude reasons about it and
    # either swap_number or cancel_order from the same MCP tool catalog.

    Notes:

    • Single API call, single Claude turn — Claude internally orchestrates buy_number → wait_for_code → return.
    • The authorization_token field is passed straight through to the MCP server as a Bearer token. Get a key in 30 seconds at the API page.
    • If buy_number succeeds but wait_for_code times out, Claude has the choice (within this single turn) of calling swap_number or cancel_order — depending on prompt phrasing.
    • Same shape works for any country/service combo VirtualSMS supports — including high-trust pairs like WhatsApp from Germany or Telegram from the UK.

    Pattern 2: Parallel Batch Verifications

    For multi-account ops, A/B testing, or bulk QA flows, Claude can fan out tool calls within a single API turn. Prompt structure does most of the work; the MCP connection is identical to Pattern 1.

    # Pattern 2 — parallel batch via Claude tool-use API.
    # Claude can fan out tool calls across an N-element list when prompted
    # with a structured request and the right system prompt.
    
    resp = client.messages.create(
        model="claude-opus-4-7",
        max_tokens=4096,
        mcp_servers=[{"type": "url", "url": "https://mcp.virtualsms.io/mcp",
                      "name": "virtualsms", "authorization_token": KEY}],
        system=("You verify accounts in parallel. For each requested service+country, "
                "buy_number then wait_for_code (timeout 300s). Return a JSON array "
                "of {service, country, phone, code} or {service, country, error}."),
        messages=[{
            "role": "user",
            "content": "Verify these in parallel: whatsapp/uk, telegram/uk, "
                       "discord/germany, instagram/france. Return JSON array.",
        }],
    )
    # In practice Claude pipelines 4 buys, then collects 4 codes.
    # Activation auto-refunds via cancel_order if any timeout, so cost is bounded.

    At higher throughput (50+ activations per turn), the REST API is usually a better fit because you can control parallelism explicitly. MCP shines at the 4–20 element batch where the reasoning overhead is worth the lower glue-code cost. The activation pricing is identical; only the orchestration shifts.

    💡 Pre-flight tip: call find_cheapest in the system prompt before buy_number on a large batch. Claude pipelines the discovery call, picks the cheapest country per service, and only then commits to activations. Discovery tools are unauthenticated and free — no rate limit pressure.

    Pattern 3: Multi-Step Reasoning Over Failures

    The pattern that makes Claude + MCP qualitatively different from a REST loop: when verifications fail, Claude reasons about the next move using the full tool catalog rather than a hardcoded retry policy. Useful for strict-detection platforms (WhatsApp, Tinder, banking) where country choice meaningfully affects success rate.

    # Pattern 3 — multi-step with Claude reasoning over failure.
    # When wait_for_code returns "no SMS", Claude can decide: retry on a different
    # country, swap the phone via swap_number, or cancel + report. The tool catalog
    # exposes all three; the right move is reasoning, not retry-with-jitter.
    
    system = """You are an account-provisioning agent. For each verification:
    1. buy_number(service, country)
    2. wait_for_code(order_id, timeout=300)
    3. If no code: try swap_number once. If still no code: cancel and try a
       different country from the user's preferred list.
    4. If 3 distinct countries fail: cancel all, return error with last 3 attempts.
    Always cancel orphan orders before returning. Never leave open activations."""
    
    resp = client.messages.create(
        model="claude-opus-4-7",
        max_tokens=4096,
        mcp_servers=[{"type": "url", "url": "https://mcp.virtualsms.io/mcp",
                      "name": "virtualsms", "authorization_token": KEY}],
        system=system,
        messages=[{
            "role": "user",
            "content": "Verify a fresh Tinder account. Try UK, Germany, then France.",
        }],
    )

    The system prompt encodes a small policy: try swap_number once on no-SMS, then move to a different country. Claude's value here is not the retry itself — REST can do that — but the ability to incorporate context (last 3 attempts' outcomes, the user's preferred country list, hints from order_history) into the next decision. This is the pattern that fixes the failure modes unpacked in why AI agents need real phone numbers — the agent stops looping on dead ranges and pivots intelligently.

    Pattern 4: Account-Creation Pipeline (Verify → Store → Reuse)

    Once you have reliable single-shot verification, the next layer is persistence: store the verified phone + code so it can be reused for password resets, MFA, or 2FA back-fills. The MCP server handles verification; the pipeline handles state.

    # Pattern 4 — account-creation pipeline.
    # Verify → store → reuse. The MCP server holds the verification half;
    # the pipeline owns persistence.
    
    import json, sqlite3
    from datetime import datetime
    
    def provision_account(service: str, country: str, db_path="accounts.db") -> dict:
        """Single-call provisioning via Claude + MCP. Stores result for reuse."""
        resp = client.messages.create(
            model="claude-opus-4-7",
            max_tokens=2048,
            mcp_servers=[{"type": "url", "url": "https://mcp.virtualsms.io/mcp",
                          "name": "virtualsms", "authorization_token": KEY}],
            messages=[{"role": "user",
                       "content": f"Buy {country} {service} number, wait for SMS, "
                                  f"return JSON {{phone, code, order_id}}."}],
        )
        payload = json.loads(_extract_json(resp))  # parse the final assistant turn
        conn = sqlite3.connect(db_path)
        conn.execute("INSERT INTO accounts(service, country, phone, code, ts) "
                     "VALUES(?, ?, ?, ?, ?)",
                     (service, country, payload["phone"], payload["code"],
                      datetime.utcnow().isoformat()))
        conn.commit(); conn.close()
        return payload

    Two architectural notes:

    • The number stays alive on VirtualSMS via the rental endpoints (separate from activations) if you need to re-receive SMS later. Activation = single SMS; rental = ongoing inbox. Both are exposed via MCP.
    • Don't store the order ID alone. If you only persist order_id and need the phone back later, the order has already been finalised. Persist {phone, code, country, service, ts} at minimum — the rest is recoverable from the dashboard.

    Error Handling: Timeouts, Rate Limits, Cancelled Orders

    Three error classes show up in production. Each has a clean MCP path:

    ErrorHow it surfacesThe Claude + MCP fix
    SMS timeout (no code arrives)wait_for_code returns "no code"Claude calls swap_number or cancel_order; activation auto-refunds
    Rate limit (120 rpm)MCP returns 429 with retry_afterClaude back-off + retry, or contact support for higher limits on production volume
    Cancelled / orphan orderDisconnected MCP run leaves an order opencancel_all_orders at the start of the next run; cost is zero on truly-orphaned orders
    Insufficient balancebuy_number returns "balance too low"Claude checks get_balance before committing; surfaces a refill request to the user

    The pattern across all four: the MCP catalog already exposes the tool that fixes the error. The system prompt's job is to wire them together with the right policy. Don't write external retry loops — they fight the MCP layer rather than work with it.

    Cost Optimization: HTTP vs stdio Transport

    VirtualSMS MCP ships in two transport flavors. Both expose the identical tool catalog; the difference is process model and latency:

    • StreamableHTTP — https://mcp.virtualsms.io/mcp. Zero install, hosted by VirtualSMS, points-of-presence in EU + US for low latency. The right default for Claude API workloads, hosted Claude Desktop, and any caller that doesn't want to manage a Node subprocess. Per-call latency: 30-80ms.
    • stdio — npx -y virtualsms-mcp. Local Node subprocess, talks via stdin/stdout. Right when you need the MCP server in an air-gapped environment, or when you want to bind to a specific Claude Code session and have observability over every call. Per-call latency: 5-15ms (local).

    Activation cost is identical between transports — both call the same upstream API. The optimization is about latency-vs-ops-overhead, not unit cost. Most Claude API users land on StreamableHTTP because the per-call latency is dominated by Claude's own thinking time, not the MCP hop.

    Claude Tool-Use vs MCP — Which When?

    Both work. The choice is about how much glue code you want to maintain.

    DimensionRaw Claude tool-useClaude + MCP
    Glue codeTool spec + dispatcher (~50-100 lines/tool)None — connection URL + auth token
    API key residenceYour environmentYour environment (passed via authorization_token)
    Tool surface evolutionYou update specs when VirtualSMS evolvesAuto-picks-up server-side changes
    Best forCustom orchestration outside ClaudeClaude-first orchestration
    Per-call latency~5ms (your function call)~30-80ms (HTTP) / ~5-15ms (stdio)
    Setup time30-90 min for a clean spec5 minutes for first call

    Net: if Claude is doing the orchestration, MCP wins. If Claude is one node in a non-Claude DAG, raw tool-use can be a cleaner fit. The two paths interoperate — nothing stops you from connecting MCP and defining a few extra raw tools in the same Claude API call. The 18-tool catalog covers the verification surface end-to-end; raw tool-use is for the bits that don't.

    ✅ Production starter checklist: hit get_balance from a Claude prompt to confirm the MCP connection works, then run Pattern 1 against a low-cost service (e.g., Telegram in your home country). Once that lands, you can ship Patterns 2-4 by changing prompt structure alone.

    Ask AI about this article

    Get a summary or follow-up answer in your favourite AI assistant.

    Frequently Asked Questions

    How do I use Anthropic Claude with phone verification?

    Connect the VirtualSMS MCP server to your Claude API call via the mcp_servers parameter (or to Claude Desktop / Claude Code via the standard MCP install). Once connected, Claude can invoke the 18 SMS-verification tools (buy_number, wait_for_code, swap_number, cancel_order, etc.) directly from natural-language prompts. Single-shot verification ("buy a UK Telegram number") works in one API call; multi-step flows ("retry on different country if first fails") use the same tool catalog with a slightly different system prompt.

    Does Claude API support tool use for SMS?

    Yes — Claude has tool-use as a first-class API feature, and MCP support extends that with a hosted-server model. The mcp_servers parameter lets you point any Claude API call at https://mcp.virtualsms.io/mcp with a Bearer token, and the 18 verification tools become available to Claude in that conversation. No manual function-spec wrangling, no schema upkeep — when VirtualSMS adds tools or evolves them, Claude picks up the change without redeploying your code.

    What's the rate limit for VirtualSMS MCP?

    The MCP server inherits the same 120 requests/min default rate limit as the REST API per API key. Production accounts requesting higher throughput (5-figure activations/month) can email [email protected] with expected QPS — limits are lifted on request after a quick conversation. Discovery tools (list_services, list_countries, find_cheapest, check_price) are unauthenticated and don't count against the rate limit, so Claude can plan freely before committing budget.

    Can Claude handle bulk phone verification?

    Yes — Claude tool-use supports parallel tool calls within a single API turn, and MCP-backed tool calls fan out the same way. For a batch of N verifications, Claude pipelines N buy_number calls, then collects N codes via wait_for_code. The architecture handles 50-element batches cleanly; for 5-figure-per-day workloads, the REST API is usually a better fit because you control parallelism explicitly. The cost difference per activation is zero; the difference is which side does the orchestration.

    When should I use Claude tool-use vs MCP for verification?

    Use raw Claude tool-use when you have custom orchestration logic that lives outside the verification layer — e.g., feeding verified accounts into a non-Claude downstream pipeline. Use MCP when Claude is the orchestrator and you want zero glue code. The MCP path is dramatically less to maintain (no function-spec JSON, no dispatcher, no schema sync), and the cost delta is zero. Production workloads on Claude Desktop or Claude Code should always use MCP; production workloads on Claude API can go either way, with MCP usually winning on simplicity.

    What happens if a verification times out in a Claude workflow?

    Claude detects the timeout via the wait_for_code tool returning a "no code received" result, and can branch on it — retry on a different country, swap the phone via swap_number, or cancel and report. The activation auto-refunds if no SMS arrived, so you do not pay for failed verifications. The right pattern (in our experience) is to give Claude a short list of fallback countries in the system prompt and let it reason about which to try next; this beats hardcoded retry loops because Claude can incorporate context (target platform, recent failure patterns, time of day).

    Published:
    VirtualSMS
    Engineering

    VirtualSMS

    Maintained by the VirtualSMS team. We've been shipping real-SIM SMS verification infrastructure since 2025 — 2000+ services across 145+ countries, MCP server v1.2.0 listed on Smithery and the official MCP registry. Open source, MIT licensed.

    Last updated:

    Related Articles

    Wire Up Claude + MCP in 5 Minutes

    Hosted MCP at mcp.virtualsms.io/mcp · 18 SMS-verification tools · Real physical SIM cards · 145+ countries · 120 rpm/key · Activations from $0.05