Skip to content

feat: v1.3.0 — 6 new tools (batch ops, smart pick, x402, webhooks)#5

Open
ashuran111 wants to merge 14 commits into
virtualsms-io:mainfrom
ashuran111:feature/v1.3.0-audit
Open

feat: v1.3.0 — 6 new tools (batch ops, smart pick, x402, webhooks)#5
ashuran111 wants to merge 14 commits into
virtualsms-io:mainfrom
ashuran111:feature/v1.3.0-audit

Conversation

@ashuran111
Copy link
Copy Markdown
Contributor

@ashuran111 ashuran111 commented Apr 30, 2026

Status: implementation complete, ready for review

v1.3.0 of the VirtualSMS MCP server. Six new tools + two additive enhancements, locked behind a v1.2.x backward-compat snapshot test.

This PR went from design-only stubs → fully wired implementation in 12 atomic commits following the 13-task plan in docs/v1.3.0-plan.md.

What shipped

6 new tools (18 → 24 total):

Tool What it does Backend route
virtualsms_buy_batch Buy 1-20 numbers in one call Promise.allSettled over POST /api/v1/customer/purchase
virtualsms_wait_for_sms_batch Collect SMS for N orders in parallel Per-order WS race vs polling, shared deadline
virtualsms_find_best_pick Single-shot decision with country pool/exclude + reasoning GET /api/v1/price iteration with 0.7 × price_inverse + 0.3 × stock_signal for prefer='balanced'
virtualsms_x402_info Money-path capability discovery (no payment, no api_key) GET /api/v1/x402/info (BSC/BNB defensively stripped — Permit2 not yet shipped)
virtualsms_pay_and_buy x402 deposit-first one-shot, with optional bundled buy POST /api/v1/x402/topup (two-step: manifest → settle) + bundled create_order
virtualsms_subscribe_webhook Outbound event subscription (5 events) POST /api/v1/customer/webhooks
virtualsms_manage_webhooks Combined list / delete / test / deliveries via action enum CRUD on /api/v1/customer/webhooks/:id

2 additive enhancements (zero break):

  • virtualsms_get_balance → adds topup_url + x402_topup_available (and a low-balance tip when balance < $1)
  • virtualsms_create_order → adds webhook_subscribe_hint for long-running agents (suppressed when one already exists; cached 30s)

Backward compatibility — guaranteed

tests/v1_2_3_schema_snapshot.test.ts snapshots the name + inputSchema + annotations + description prefix of every v1.2.x tool. The snapshot stays GREEN through every commit in this PR — proving zero breaking change for existing 1.2.x clients.

Internal layout: TOOL_DEFINITIONS_V1_2_X (locked) + V1_3_TOOL_DEFS (additive) → public TOOL_DEFINITIONS = [...V1_2_X, ...V1_3].

Testing

  • 59 tests passing across 11 test files
  • Vitest 2.x added as devDep, new npm test + npm run test:watch scripts
  • Production build (tsc) excludes tests/ so dist/ stays clean
  • Each new tool has at minimum: 1 happy-path, 1 error-path, plus the v1.2.x backward-compat snapshot
$ npm run build && npm test
> virtualsms-mcp@1.3.0 build
> tsc

> virtualsms-mcp@1.3.0 test
> vitest run

 ✓ tests/find-best-pick.test.ts        (6 tests)
 ✓ tests/x402-info.test.ts             (4 tests)
 ✓ tests/buy-batch.test.ts             (4 tests)
 ✓ tests/subscribe-webhook.test.ts     (6 tests)
 ✓ tests/manage-webhooks.test.ts       (5 tests)
 ✓ tests/client-v1_3-methods.test.ts   (12 tests)
 ✓ tests/pay-and-buy.test.ts           (5 tests)
 ✓ tests/buy-hint.test.ts              (5 tests)
 ✓ tests/balance-hint.test.ts          (4 tests)
 ✓ tests/v1_2_3_schema_snapshot.test.ts (4 tests)
 ✓ tests/wait-batch.test.ts            (4 tests)

 Test Files  11 passed (11)
      Tests  59 passed (59)

Commits in this PR (12 atomic, in order)

test: snapshot v1.2.x tool schemas (lock backward compat)
feat(client): add x402 + webhooks + batch methods
feat(tools): buy_batch — purchase 1-20 numbers in one call
feat(tools): wait_for_sms_batch — collect SMS for N orders in parallel
feat(tools): find_best_pick — single-shot decision with country pool/exclude
feat(tools): x402_info — discover money-path capabilities
feat(tools): pay_and_buy — x402 deposit-first one-shot
feat(tools): subscribe_webhook — outbound event delivery
feat(tools): manage_webhooks — list/delete/test/deliveries combined
feat(tools): add x402 + webhook hints to existing tools (additive)
chore(release): 1.2.3 -> 1.3.0
docs(examples): batch buy + webhook end-to-end demo (04)

Wiring — both transports

The 7 new tools are dispatched on both stdio (src/index.ts) and StreamableHTTP (src/http-server.ts) — guards against the v1.2.2 dispatch bug (commit 1da145b) where 6 tools were defined but only registered on stdio.

What's also new

  • examples/04-batch-buy-with-webhook/ — runnable end-to-end demo of the canonical v1.3.0 batch agentic pattern (subscribe → buy_batch → wait_for_sms_batch → manage_webhooks deliveries audit). Degrades gracefully when WEBHOOK_URL is unset.
  • README updated: tagline + Why-VirtualSMS bullet + footer all show "24 tools". New "What's new in v1.3.0" callout right under the feat(v1.1.0): add 6 new tools — 12 → 18 total #1 ranking banner.
  • CHANGELOG.md — full [1.3.0] - 2026-05-01 section.
  • server.json + package.json — both bumped 1.2.3 → 1.3.0.

What's NOT in this PR (parking lot — design doc §10)

  • Long-term rental tools (no backend support yet)
  • Streaming MCP responses (spec churn)
  • Backend-side /purchase/batch (would let MCP drop the loop)
  • WebSocket multiplexing per agent (would let wait_for_sms_batch use 1 socket)

After merge — registry republish

Per existing reference_mcp_registry_publish runbook:

  • mcp-publisher against registry.modelcontextprotocol.io
  • npm publish from CI

Test plan

  • npm run build clean — exit 0
  • npm test — 59/59 green
  • Schema snapshot test — v1.2.x contract still locked
  • All 7 new tools wired on both stdio + StreamableHTTP transports
  • x402_info defensively strips BNB/BSC (verified via test)
  • buy_batch budget guard refuses overspend (verified via test)
  • webhook lookup cached 30s on bursty create_order calls (verified via test)
  • Maintainer review
  • Merge → registry republish (mcp-publisher) + npm publish

🤖 Generated with Claude Code

virtualsms-io and others added 14 commits April 30, 2026 16:43
Adds v1.3.0 audit + design pass for the next minor of the MCP server.
No implementation logic — stubs only. All v1.2.x tools untouched.

Proposed new tools (6 net-new + 2 additive hints):
- virtualsms_buy_batch          — purchase 1-20 numbers in one call
- virtualsms_wait_for_sms_batch — collect SMS for N orders in parallel
- virtualsms_find_best_pick     — single-shot decision with country pool/exclude
- virtualsms_pay_and_buy        — x402 deposit-first one-shot (Pattern B)
- virtualsms_x402_info          — money-path capability discovery
- virtualsms_subscribe_webhook  — outbound event delivery
- virtualsms_manage_webhooks    — list/delete/test/deliveries combined

Backward compat: zero breaking changes for 1.2.x clients (locked via
schema snapshot test in Task 1 of the plan).

See docs/v1.3.0-design.md for full design + alternatives considered.
See docs/v1.3.0-plan.md for the bite-sized 13-task implementation plan.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds Vitest + a backward-compat snapshot test that pins the name +
inputSchema + annotations + description prefix of every tool that
shipped in v1.2.x.

Any change to one of these fields breaks this test, which means the
change broke a 1.2.x client contract. v1.3.x must be additive — new
tools only, never edit the 18 v1.2.x ones.

Setup:
- vitest 2.x as devDep
- npm scripts: test, test:watch
- vitest.config.ts (include tests/**/*.test.ts)
- tsconfig excludes tests/ from production build (dist/ stays clean)

Snapshots committed at tests/__snapshots__/.

Per docs/v1.3.0-plan.md Task 1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the backend client methods that v1.3.0 tools dispatch through.
No MCP wiring yet — that lands in Tasks 3-9.

Methods:
- getX402Info()                           — discovery, no api key required
- topup({amount_usd, payment_method,
         payment_proof?})                 — two-step x402 settlement
- listWebhooks()
- createWebhook({url, events, ...})
- deleteWebhook(id)
- testWebhook(id)
- getDeliveries(id)
- createOrderBatch(service, country, n)   — Promise.allSettled fan-out

Types added: X402Info, X402Manifest, X402TopupResult, Webhook,
WebhookDelivery, BatchPurchaseResult.

The 402-on-manifest-path is handled by catching the AxiosError and
returning the manifest body — same pattern x402-fetch uses on the
client side.

Per docs/v1.3.0-plan.md Task 2.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the v1.3.0 stub with real impl. Wires into both stdio
(src/index.ts) and StreamableHTTP (src/http-server.ts) transports.

Behavior:
- Pre-flight balance × cheapest_price guard. If count × price > 80%
  of balance → returns budget_guard error WITHOUT charging anything.
- Backend dispatches via client.createOrderBatch (Promise.allSettled
  fan-out). Partial failures aggregate into failed[].
- Returns succeeded[] + failed[] + total_charged_usd +
  remaining_balance_usd + tip pointing at wait_for_sms_batch.
- stop_on_failure flag is annotated in the response when set; actual
  early-stop requires a backend batch endpoint (parking-lot item).

TOOL_DEFINITIONS layout: split into TOOL_DEFINITIONS_V1_2_X (locked,
snapshot test enforces) + V1_3_TOOL_DEFS (additive). Public surface
TOOL_DEFINITIONS = concat of both.

Schema snapshot test still GREEN — proves the v1.2.x contract is
unchanged.

Per docs/v1.3.0-plan.md Task 3.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the v1.3.0 stub. Wires into both stdio + StreamableHTTP
transports.

Behavior:
- Per-order WebSocket race vs polling (5s interval) with shared
  per-order deadline. WS tries first if api key present; on close
  or error, falls back to polling for the remaining time.
- Aggregates into received[] / timed_out[] / errors[] via
  Promise.allSettled. No partial failure throws.
- return_partial=false → top-level error if anything missed,
  with the same arrays for inspection.
- Each received[] entry: order_id, code, sms_text,
  delivery_method (instant|websocket|polling), elapsed_ms.

Schema snapshot test still GREEN.

Per docs/v1.3.0-plan.md Task 4.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…exclude

Replaces the v1.3.0 stub. Wires into both stdio + StreamableHTTP transports.

Behavior:
- listCountries → filter by country_pool whitelist + country_exclude blacklist
- checkPrice each (batched 10 at a time, Promise.allSettled, skip OOS)
- Score per `prefer`:
  - cheapest:   normalized 1 - (price - min) / range
  - most_stock: stock binary (0/1)
  - balanced:   0.7 × price_inverse + 0.3 × stock_signal (default)
- Plain-English `reasoning` string explains why pick beat runner-ups
  (cheapest among pool / X% above cheapest but better stock / etc.)
- runner_ups: top 3 alternatives
- error: "no_pick" with tip when nothing in stock

Schema snapshot still GREEN.

Per docs/v1.3.0-plan.md Task 5.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the v1.3.0 stub. Wires into both transports.

Behavior:
- Wraps client.getX402Info()
- Maps backend networks[] → agent-friendly accepts[] with
  {network, asset, min_usd, max_usd, pay_to}
- Solana network → solana_relayer pay_to; everything else → evm_relayer
- patterns[] derived: "topup" if topup_endpoint present, "sms-verify"
  if resource present (deprecated server-side but surface if leaked)
- Top-level: enabled, x402_version, accepts, min/max/default_topup_usd

CRITICAL — defends against BNB/BSC leakage:
DISABLED_NETWORKS = {bsc, binance, bnb}. Even if a self-hosted backend
returns these, accepts[] strips them. Backed by Vault memory
project_x402_wallet_setup: BNB disabled until Permit2 ships in settler.

Backend 503/missing endpoint → unsupported_on_this_backend error,
no throw.

Schema snapshot still GREEN.

Per docs/v1.3.0-plan.md Task 6.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the v1.3.0 stub. Wires into both transports.

Two-step protocol:
- First call (no payment_proof) → returns 402 manifest from
  client.topup({ amount_usd, payment_method }). Tip points to
  x402-fetch / wallet to sign.
- Second call (with payment_proof) → topup succeeds, returns
  api_key + credited balance. If service+country provided in
  the same call, the handler instantiates a fresh client with
  the new api_key and bundles createOrder.

Failure isolation: if the bundled buy fails after a successful
topup, response is paid_buy_failed with the api_key still surfaced
so the caller never loses the deposit.

Service/country validation runs BEFORE topup — refuses one-of-pair
input as input_error without charging.

Schema snapshot still GREEN.

Per docs/v1.3.0-plan.md Task 7.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the v1.3.0 stub. (Wiring lands with manage_webhooks in the
next commit so transports get both webhook tools at once.)

Behavior:
- Wraps client.createWebhook
- Pre-flight: balance.low requires positive threshold_usd. Refused
  client-side as `threshold_required` — saves a backend 4xx.
- Returns webhook_id, secret (HMAC-SHA256), active flag, created_at
- tip points caller at the manage_webhooks(action:"test") flow to
  fire a synthetic event and verify the endpoint

Schema snapshot still GREEN.

Per docs/v1.3.0-plan.md Task 8.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the v1.3.0 stub. Wires both subscribe_webhook AND
manage_webhooks into stdio + StreamableHTTP transports.

Behavior:
- action enum dispatches to the right backend route:
  - list       → GET    /api/v1/customer/webhooks
  - delete     → DELETE /api/v1/customer/webhooks/:id
  - test       → POST   /api/v1/customer/webhooks/:id/test
  - deliveries → GET    /api/v1/customer/webhooks/:id/deliveries
- webhook_id required for delete/test/deliveries — enforced at the
  zod refine layer
- Each branch returns a tip explaining the next move (e.g. test failed
  → check deliveries; no deliveries yet → fire test event)

Schema snapshot still GREEN.

Per docs/v1.3.0-plan.md Task 9.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ZERO breaking changes — schema-snapshot test still GREEN.

handleGetBalance now returns:
- balance_usd          (unchanged, original field)
- topup_url            (NEW — points at dashboard topup)
- x402_topup_available (NEW — boolean from getX402Info)
- tip                  (NEW, conditional — only when balance < $1)

handleBuyNumber now returns:
- order_id, phone_number, expires_at, status, tip (unchanged)
- webhook_subscribe_hint (NEW, conditional — only when no
  sms.received webhook exists for this api key)

Webhook lookup is cached for 30s in a module-level singleton.
Bursty agents calling create_order in tight loops only pay one
listWebhooks() round-trip per minute regardless of QPS.

Test-only export _resetWebhookCacheForTests() for hermetic tests.

Per docs/v1.3.0-plan.md Task 10.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- package.json + server.json: 1.3.0
- src/index.ts (main + sandbox) + src/http-server.ts (per-request +
  server-card payload): version bumped to 1.3.0
- CHANGELOG.md: new [1.3.0] section listing 6 new tools, 2 additive
  enhancements, backward-compat note, snapshot-test reference, vitest
  tooling note
- README.md: tagline + Why-VirtualSMS bullet + footer updated to
  "24 tools". New "What's new in v1.3.0" callout after the virtualsms-io#1 ranking
  banner

Schema-snapshot test still GREEN — proves zero breaking changes.

Per docs/v1.3.0-plan.md Task 11.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New runnable example demonstrating the canonical v1.3.0 batch agentic
pattern: subscribe_webhook → buy_batch → wait_for_sms_batch →
manage_webhooks(action:"deliveries").

- examples/04-batch-buy-with-webhook/run.mjs — 4-step orchestrator,
  degrades gracefully when WEBHOOK_URL unset (skips webhook steps,
  still demos buy_batch + wait_for_sms_batch).
- examples/04-batch-buy-with-webhook/README.md — env-var contract,
  expected console output, HMAC verification pseudocode for the
  receiver.

Per docs/v1.3.0-plan.md Task 12.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ashuran111 ashuran111 marked this pull request as ready for review April 30, 2026 17:13
@ashuran111 ashuran111 changed the title WIP: v1.3.0 design — 6 new tools + backward-compat plan feat: v1.3.0 — 6 new tools (batch ops, smart pick, x402, webhooks) Apr 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants