virtualsms-mcp 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +140 -0
- package/LICENSE +21 -0
- package/README.md +270 -0
- package/ROADMAP.md +48 -0
- package/dist/client.d.ts +49 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +109 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +98 -0
- package/dist/index.js.map +1 -0
- package/dist/tools.d.ts +285 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +602 -0
- package/dist/tools.js.map +1 -0
- package/package.json +56 -0
package/ARCHITECTURE.md
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# VirtualSMS MCP Server — Architecture
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The VirtualSMS MCP server is a **stdio-transport MCP server** written in TypeScript. It wraps the VirtualSMS REST API and WebSocket gateway, exposing them as 11 MCP tools that AI agents can call directly.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## How MCP stdio Transport Works
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
Claude Desktop / Cursor
|
|
13
|
+
│
|
|
14
|
+
│ stdin/stdout (JSON-RPC 2.0)
|
|
15
|
+
▼
|
|
16
|
+
virtualsms-mcp process (Node.js)
|
|
17
|
+
│
|
|
18
|
+
├──► REST API calls (axios)
|
|
19
|
+
│ https://virtualsms.io/api/v1/
|
|
20
|
+
│
|
|
21
|
+
└──► WebSocket connection (ws)
|
|
22
|
+
wss://virtualsms.io/ws/orders
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The MCP client (Claude Desktop, Cursor) spawns `virtualsms-mcp` as a child process and communicates via stdin/stdout using JSON-RPC 2.0. The server is persistent — it stays alive for the lifetime of the AI session, which is why it can hold WebSocket connections.
|
|
26
|
+
|
|
27
|
+
**Important:** Never write to stdout except via the MCP SDK. Any `console.log()` would corrupt the JSON-RPC stream. Use `process.stderr` for debug output.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Client → API Flow
|
|
32
|
+
|
|
33
|
+
All REST calls go through `VirtualSMSClient` (src/client.ts):
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
Tool Handler → VirtualSMSClient → axios → https://virtualsms.io/api/v1/
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The client:
|
|
40
|
+
- Attaches `Authorization: Bearer {apiKey}` to all authenticated requests
|
|
41
|
+
- Normalizes errors into human-readable messages
|
|
42
|
+
- Handles 401/403 with clear "set VIRTUALSMS_API_KEY" guidance
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Auth Flow
|
|
47
|
+
|
|
48
|
+
1. User sets `VIRTUALSMS_API_KEY=vms_xxxxx` in their MCP config
|
|
49
|
+
2. On startup, the MCP server reads `process.env.VIRTUALSMS_API_KEY`
|
|
50
|
+
3. Every API call includes `Authorization: Bearer vms_xxxxx`
|
|
51
|
+
4. The backend validates the key by SHA-256 hash lookup in `api_keys` table
|
|
52
|
+
5. For WebSocket, the key is passed as `?api_key=vms_xxxxx` query param
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## WebSocket Connection Lifecycle (`wait_for_code`)
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
1. buy_number() → REST POST /api/v1/order → {order_id, phone_number}
|
|
60
|
+
|
|
61
|
+
2. Connect WebSocket:
|
|
62
|
+
wss://virtualsms.io/ws/orders?order_id={id}&api_key={key}
|
|
63
|
+
|
|
64
|
+
3. Server sends: {type: "connected", order_id: "...", phone_number: "...", status: "pending"}
|
|
65
|
+
|
|
66
|
+
4. When SMS arrives, server pushes:
|
|
67
|
+
{type: "sms", code: "12345", full_text: "Your code is 12345", sender: "..."}
|
|
68
|
+
|
|
69
|
+
5. MCP server resolves with the code, closes WebSocket.
|
|
70
|
+
|
|
71
|
+
6. If WebSocket fails (network error, server unreachable):
|
|
72
|
+
→ Fallback to polling GET /api/v1/order/{id} every 5s
|
|
73
|
+
|
|
74
|
+
7. If timeout:
|
|
75
|
+
→ Return {success: false, order_id: ...} so user can recover
|
|
76
|
+
→ Do NOT cancel automatically (user may still want the code)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**Reconnect logic:** If the WebSocket disconnects unexpectedly (not on error/timeout), the client reconnects once after 1 second before falling back to polling.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Error Handling Strategy
|
|
84
|
+
|
|
85
|
+
All tool errors are wrapped as `McpError` to prevent server crashes:
|
|
86
|
+
|
|
87
|
+
| Error type | McpError code |
|
|
88
|
+
|------------|---------------|
|
|
89
|
+
| Zod validation | `InvalidParams` |
|
|
90
|
+
| Missing API key | `InvalidRequest` |
|
|
91
|
+
| Network/API errors | `InternalError` |
|
|
92
|
+
| Unknown tool name | `MethodNotFound` |
|
|
93
|
+
|
|
94
|
+
This means the AI agent always gets a structured error it can act on, rather than a crash.
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## File Structure
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
virtualsms-mcp/
|
|
102
|
+
├── src/
|
|
103
|
+
│ ├── index.ts # MCP server setup, tool routing, startup
|
|
104
|
+
│ ├── client.ts # VirtualSMSClient — REST API wrapper
|
|
105
|
+
│ └── tools.ts # Tool definitions (schemas) + handlers
|
|
106
|
+
├── dist/ # Compiled output (from tsc)
|
|
107
|
+
│ ├── index.js # Has #!/usr/bin/env node shebang
|
|
108
|
+
│ ├── client.js
|
|
109
|
+
│ └── tools.js
|
|
110
|
+
├── package.json
|
|
111
|
+
├── tsconfig.json
|
|
112
|
+
├── README.md
|
|
113
|
+
├── ROADMAP.md
|
|
114
|
+
└── ARCHITECTURE.md
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Adding a New Tool
|
|
120
|
+
|
|
121
|
+
1. Add input schema (zod) to `src/tools.ts`
|
|
122
|
+
2. Add tool definition to `TOOL_DEFINITIONS` array
|
|
123
|
+
3. Add handler function `handleXxx()` to `src/tools.ts`
|
|
124
|
+
4. Add case to switch in `src/index.ts`
|
|
125
|
+
5. Export schema + handler from `src/tools.ts`
|
|
126
|
+
6. `npm run build`
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Local Development
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
npm install
|
|
134
|
+
npm run build # compile TypeScript
|
|
135
|
+
npm run dev # watch mode (tsc --watch)
|
|
136
|
+
node dist/index.js # test startup (exits immediately — needs MCP client)
|
|
137
|
+
|
|
138
|
+
# Test with MCP Inspector:
|
|
139
|
+
npx @modelcontextprotocol/inspector node dist/index.js
|
|
140
|
+
```
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 VirtualSMS
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
# VirtualSMS MCP Server
|
|
2
|
+
|
|
3
|
+
Receive SMS verification codes directly inside your AI agent workflows. Supports Claude Desktop, Cursor, and any MCP-compatible client.
|
|
4
|
+
|
|
5
|
+
**Powered by [VirtualSMS.io](https://virtualsms.io)** — virtual phone numbers for SMS verification.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Quick Start
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx virtualsms-mcp
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Or install globally:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install -g virtualsms-mcp
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Get your API key at [virtualsms.io](https://virtualsms.io).
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Configuration
|
|
26
|
+
|
|
27
|
+
### Claude Desktop
|
|
28
|
+
|
|
29
|
+
Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"mcpServers": {
|
|
34
|
+
"virtualsms": {
|
|
35
|
+
"command": "npx",
|
|
36
|
+
"args": ["virtualsms-mcp"],
|
|
37
|
+
"env": {
|
|
38
|
+
"VIRTUALSMS_API_KEY": "vms_your_api_key_here"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Cursor
|
|
46
|
+
|
|
47
|
+
Edit `~/.cursor/mcp.json`:
|
|
48
|
+
|
|
49
|
+
```json
|
|
50
|
+
{
|
|
51
|
+
"mcpServers": {
|
|
52
|
+
"virtualsms": {
|
|
53
|
+
"command": "npx",
|
|
54
|
+
"args": ["virtualsms-mcp"],
|
|
55
|
+
"env": {
|
|
56
|
+
"VIRTUALSMS_API_KEY": "vms_your_api_key_here"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Environment Variables
|
|
64
|
+
|
|
65
|
+
| Variable | Required | Default | Description |
|
|
66
|
+
|----------|----------|---------|-------------|
|
|
67
|
+
| `VIRTUALSMS_API_KEY` | Yes (for auth tools) | — | Your VirtualSMS API key |
|
|
68
|
+
| `VIRTUALSMS_BASE_URL` | No | `https://virtualsms.io` | API base URL |
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Tools (11 total)
|
|
73
|
+
|
|
74
|
+
### Discovery Tools (no auth required)
|
|
75
|
+
|
|
76
|
+
#### `list_services`
|
|
77
|
+
Get all available SMS verification services.
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
list_services()
|
|
81
|
+
→ [{code: "telegram", name: "Telegram"}, ...]
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
#### `list_countries`
|
|
85
|
+
Get all available countries for SMS verification.
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
list_countries()
|
|
89
|
+
→ [{iso: "US", name: "United States"}, ...]
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
#### `check_price`
|
|
93
|
+
Check price and availability for a service + country combination.
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
check_price(service: "telegram", country: "US")
|
|
97
|
+
→ {price_usd: 0.15, available: true}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
#### `find_cheapest`
|
|
101
|
+
Find cheapest countries for a service, sorted by price.
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
find_cheapest(service: "telegram", limit: 5)
|
|
105
|
+
→ {cheapest_options: [{country: "PK", price_usd: 0.05, ...}], total_available_countries: 23}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
#### `search_service`
|
|
109
|
+
Find the right service code using natural language.
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
search_service(query: "uber")
|
|
113
|
+
→ {matches: [{code: "uber", name: "Uber", match_score: 1.0}]}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Account Tools (API key required)
|
|
117
|
+
|
|
118
|
+
#### `get_balance`
|
|
119
|
+
Check your account balance.
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
get_balance()
|
|
123
|
+
→ {balance_usd: 5.00}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
#### `active_orders`
|
|
127
|
+
List your active orders. **Essential for crash recovery.**
|
|
128
|
+
|
|
129
|
+
```
|
|
130
|
+
active_orders()
|
|
131
|
+
active_orders(status: "pending")
|
|
132
|
+
→ {count: 2, orders: [{order_id: "abc123", phone_number: "+14155552671", status: "pending", ...}]}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Optional `status` filter: `"pending"`, `"sms_received"`, `"cancelled"`, `"completed"`
|
|
136
|
+
|
|
137
|
+
### Order Management Tools (API key required)
|
|
138
|
+
|
|
139
|
+
#### `buy_number`
|
|
140
|
+
Purchase a virtual phone number.
|
|
141
|
+
|
|
142
|
+
```
|
|
143
|
+
buy_number(service: "telegram", country: "US")
|
|
144
|
+
→ {order_id: "abc123", phone_number: "+14155552671", expires_at: "...", status: "pending"}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
#### `check_sms`
|
|
148
|
+
Check if an SMS code has arrived for an order.
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
check_sms(order_id: "abc123")
|
|
152
|
+
→ {status: "sms_received", phone_number: "+14155552671", sms_code: "12345", sms_text: "Your code is 12345"}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
#### `cancel_order`
|
|
156
|
+
Cancel an order and request a refund (only if no SMS received yet).
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
cancel_order(order_id: "abc123")
|
|
160
|
+
→ {success: true, refunded: true}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
#### `wait_for_code` ⭐ Recommended
|
|
164
|
+
One-step tool: buys a number AND waits for the SMS code. Uses WebSocket for instant delivery with automatic polling fallback.
|
|
165
|
+
|
|
166
|
+
```
|
|
167
|
+
wait_for_code(service: "telegram", country: "US")
|
|
168
|
+
wait_for_code(service: "whatsapp", country: "PK", timeout_seconds: 180)
|
|
169
|
+
→ {
|
|
170
|
+
success: true,
|
|
171
|
+
phone_number: "+14155552671",
|
|
172
|
+
sms_code: "12345",
|
|
173
|
+
sms_text: "Your Telegram code: 12345",
|
|
174
|
+
order_id: "abc123",
|
|
175
|
+
delivery_method: "websocket",
|
|
176
|
+
elapsed_seconds: 8
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
On timeout, returns `order_id` for recovery:
|
|
181
|
+
```
|
|
182
|
+
→ {success: false, error: "timeout", order_id: "abc123", phone_number: "...", tip: "Use check_sms..."}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## How It Works
|
|
188
|
+
|
|
189
|
+
### WebSocket vs Polling
|
|
190
|
+
|
|
191
|
+
`wait_for_code` uses a two-tier delivery system:
|
|
192
|
+
|
|
193
|
+
1. **WebSocket (instant)** — connects to `wss://virtualsms.io/ws/orders?order_id=xxx` immediately after buying the number. When the SMS arrives, the server pushes it in real-time. Typical delivery: 2-15 seconds.
|
|
194
|
+
|
|
195
|
+
2. **Polling fallback** — if WebSocket fails to connect or disconnects, automatically falls back to polling `GET /api/v1/order/{id}` every 5 seconds. Same result, slightly slower.
|
|
196
|
+
|
|
197
|
+
The `delivery_method` field in the response tells you which was used.
|
|
198
|
+
|
|
199
|
+
### Architecture
|
|
200
|
+
|
|
201
|
+
```
|
|
202
|
+
AI Agent (Claude/Cursor)
|
|
203
|
+
│
|
|
204
|
+
▼ MCP stdio protocol
|
|
205
|
+
VirtualSMS MCP Server (this package)
|
|
206
|
+
│
|
|
207
|
+
├──► REST API: https://virtualsms.io/api/v1/
|
|
208
|
+
│ buy_number, check_sms, cancel_order, etc.
|
|
209
|
+
│
|
|
210
|
+
└──► WebSocket: wss://virtualsms.io/ws/orders
|
|
211
|
+
real-time SMS push delivery
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## Typical Workflows
|
|
217
|
+
|
|
218
|
+
### Simple: Get a Telegram code
|
|
219
|
+
```
|
|
220
|
+
wait_for_code(service: "telegram", country: "US")
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Budget: Find cheapest option first
|
|
224
|
+
```
|
|
225
|
+
find_cheapest(service: "telegram", limit: 3)
|
|
226
|
+
# → picks cheapest country
|
|
227
|
+
wait_for_code(service: "telegram", country: "PK")
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Manual: Step by step
|
|
231
|
+
```
|
|
232
|
+
buy_number(service: "google", country: "GB")
|
|
233
|
+
# → order_id: "abc123", phone: "+447911123456"
|
|
234
|
+
|
|
235
|
+
# Use the phone number to trigger the SMS...
|
|
236
|
+
|
|
237
|
+
check_sms(order_id: "abc123")
|
|
238
|
+
# Poll until sms_code appears, or:
|
|
239
|
+
cancel_order(order_id: "abc123") # if taking too long
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## Crash Recovery
|
|
245
|
+
|
|
246
|
+
If your MCP server crashes or the AI session is interrupted mid-verification:
|
|
247
|
+
|
|
248
|
+
1. **Restart the MCP server** (it reconnects automatically)
|
|
249
|
+
2. **List active orders:**
|
|
250
|
+
```
|
|
251
|
+
active_orders(status: "pending")
|
|
252
|
+
```
|
|
253
|
+
3. **Check for codes:**
|
|
254
|
+
```
|
|
255
|
+
check_sms(order_id: "abc123")
|
|
256
|
+
```
|
|
257
|
+
4. **Cancel if no longer needed:**
|
|
258
|
+
```
|
|
259
|
+
cancel_order(order_id: "abc123")
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
The `wait_for_code` tool always returns `order_id` even on timeout — this is your recovery handle.
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
## License
|
|
267
|
+
|
|
268
|
+
MIT — See [LICENSE](./LICENSE)
|
|
269
|
+
|
|
270
|
+
Built with ❤️ by [VirtualSMS.io](https://virtualsms.io)
|
package/ROADMAP.md
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# VirtualSMS MCP Server — Roadmap
|
|
2
|
+
|
|
3
|
+
Current version: **v1.0.0** — Core tools, WebSocket delivery, crash recovery.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## v1.1 — Smart Features
|
|
8
|
+
|
|
9
|
+
- **`smart_verify`** — Auto-retry with different countries on failure. Finds cheapest country, buys number, waits for SMS, and if it times out, cancels and tries next country automatically. (Held from v1.0 pending refund mechanics verification.)
|
|
10
|
+
- **`recommend_country`** — ML-based country recommendation based on historical success rate analytics per service.
|
|
11
|
+
- **`price_compare`** — Show VirtualSMS price vs competitors for a given service/country.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## v1.2 — Batch & Automation
|
|
16
|
+
|
|
17
|
+
- **`batch_verify`** — Buy multiple numbers in parallel for bulk verification workflows.
|
|
18
|
+
- **`webhook_mode`** — Register a callback URL to receive SMS codes instead of waiting synchronously.
|
|
19
|
+
- Notification preferences: get notified via email or Telegram when a code arrives.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## v1.3 — Multi-Provider Aggregation
|
|
24
|
+
|
|
25
|
+
- Aggregate numbers across multiple upstream SMS providers.
|
|
26
|
+
- Auto-route to cheapest/fastest provider based on real-time availability.
|
|
27
|
+
- Unified service code mapping across providers (different providers use different codes for the same service).
|
|
28
|
+
- Provider health monitoring — skip providers with high failure rates.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## v2.0 — Remote Transport
|
|
33
|
+
|
|
34
|
+
- **SSE/Streamable HTTP transport** — zero-install, just add a URL to your MCP config.
|
|
35
|
+
- Hosted at `https://mcp.virtualsms.io`.
|
|
36
|
+
- OAuth2 auth flow for MCP clients (no more copying API keys).
|
|
37
|
+
- Usage dashboard for MCP users — see spend, history, success rates.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Future Ideas
|
|
42
|
+
|
|
43
|
+
- **Number reputation scoring** — flag numbers with low verification success rates.
|
|
44
|
+
- **Success rate analytics** — per-service, per-country historical success rates exposed via API.
|
|
45
|
+
- **Rate limiting per API key tier** — different quotas for free, pro, enterprise.
|
|
46
|
+
- **Rental numbers** — keep a number for days or weeks (not just one-time verification).
|
|
47
|
+
- **Number recycling preferences** — opt out of recycled numbers for higher success rates.
|
|
48
|
+
- **Operator filtering** — prefer specific carriers per country for higher deliverability.
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export interface Service {
|
|
2
|
+
code: string;
|
|
3
|
+
name: string;
|
|
4
|
+
icon?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface Country {
|
|
7
|
+
iso: string;
|
|
8
|
+
name: string;
|
|
9
|
+
flag?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface Price {
|
|
12
|
+
price_usd: number;
|
|
13
|
+
currency: string;
|
|
14
|
+
available: boolean;
|
|
15
|
+
}
|
|
16
|
+
export interface Balance {
|
|
17
|
+
balance_usd: number;
|
|
18
|
+
}
|
|
19
|
+
export interface Order {
|
|
20
|
+
order_id: string;
|
|
21
|
+
phone_number: string;
|
|
22
|
+
expires_at?: string;
|
|
23
|
+
status: string;
|
|
24
|
+
sms_code?: string;
|
|
25
|
+
sms_text?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface CancelResult {
|
|
28
|
+
success: boolean;
|
|
29
|
+
refunded: boolean;
|
|
30
|
+
}
|
|
31
|
+
export declare class VirtualSMSClient {
|
|
32
|
+
private http;
|
|
33
|
+
private apiKey?;
|
|
34
|
+
private baseUrl;
|
|
35
|
+
constructor(baseUrl: string, apiKey?: string);
|
|
36
|
+
requireApiKey(): void;
|
|
37
|
+
listServices(): Promise<Service[]>;
|
|
38
|
+
listCountries(): Promise<Country[]>;
|
|
39
|
+
checkPrice(service: string, country: string): Promise<Price>;
|
|
40
|
+
getBalance(): Promise<Balance>;
|
|
41
|
+
createOrder(service: string, country: string): Promise<Order>;
|
|
42
|
+
getOrder(orderId: string): Promise<Order>;
|
|
43
|
+
cancelOrder(orderId: string): Promise<CancelResult>;
|
|
44
|
+
completeOrder(orderId: string): Promise<Order>;
|
|
45
|
+
listOrders(status?: string): Promise<Order[]>;
|
|
46
|
+
getApiKey(): string | undefined;
|
|
47
|
+
getBaseUrl(): string;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,OAAO;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,KAAK;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,OAAO;IACtB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,KAAK;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,IAAI,CAAgB;IAC5B,OAAO,CAAC,MAAM,CAAC,CAAS;IACxB,OAAO,CAAC,OAAO,CAAS;gBAEZ,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;IA4C5C,aAAa,IAAI,IAAI;IASf,YAAY,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;IAKlC,aAAa,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;IAKnC,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;IAO5D,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC;IAM9B,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;IAM7D,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;IAMzC,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAQnD,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;IAQ9C,UAAU,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;IAOnD,SAAS,IAAI,MAAM,GAAG,SAAS;IAI/B,UAAU,IAAI,MAAM;CAGrB"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
export class VirtualSMSClient {
|
|
3
|
+
http;
|
|
4
|
+
apiKey;
|
|
5
|
+
baseUrl;
|
|
6
|
+
constructor(baseUrl, apiKey) {
|
|
7
|
+
this.apiKey = apiKey;
|
|
8
|
+
this.baseUrl = baseUrl;
|
|
9
|
+
this.http = axios.create({
|
|
10
|
+
baseURL: baseUrl,
|
|
11
|
+
timeout: 30000,
|
|
12
|
+
headers: {
|
|
13
|
+
'Content-Type': 'application/json',
|
|
14
|
+
'Accept': 'application/json',
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
// Add auth header if API key is set
|
|
18
|
+
this.http.interceptors.request.use((config) => {
|
|
19
|
+
if (this.apiKey) {
|
|
20
|
+
config.headers['Authorization'] = `Bearer ${this.apiKey}`;
|
|
21
|
+
}
|
|
22
|
+
return config;
|
|
23
|
+
});
|
|
24
|
+
// Handle errors gracefully
|
|
25
|
+
this.http.interceptors.response.use((res) => res, (err) => {
|
|
26
|
+
const status = err.response?.status;
|
|
27
|
+
const data = err.response?.data;
|
|
28
|
+
const message = data?.message || data?.error || err.message;
|
|
29
|
+
if (status === 401) {
|
|
30
|
+
throw new Error('Invalid API key. Get one at https://virtualsms.io');
|
|
31
|
+
}
|
|
32
|
+
else if (status === 402) {
|
|
33
|
+
throw new Error('Insufficient balance. Top up at https://virtualsms.io');
|
|
34
|
+
}
|
|
35
|
+
else if (status === 404) {
|
|
36
|
+
throw new Error(`Not found: ${message}`);
|
|
37
|
+
}
|
|
38
|
+
else if (status === 429) {
|
|
39
|
+
throw new Error('Rate limit exceeded. Please slow down requests.');
|
|
40
|
+
}
|
|
41
|
+
else if (status && status >= 500) {
|
|
42
|
+
throw new Error(`VirtualSMS server error (${status}). Please try again.`);
|
|
43
|
+
}
|
|
44
|
+
throw new Error(`API error: ${message}`);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
requireApiKey() {
|
|
48
|
+
if (!this.apiKey) {
|
|
49
|
+
throw new Error('VIRTUALSMS_API_KEY is required for this operation. ' +
|
|
50
|
+
'Get your API key at https://virtualsms.io');
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async listServices() {
|
|
54
|
+
const res = await this.http.get('/api/v1/services');
|
|
55
|
+
return res.data;
|
|
56
|
+
}
|
|
57
|
+
async listCountries() {
|
|
58
|
+
const res = await this.http.get('/api/v1/countries');
|
|
59
|
+
return res.data;
|
|
60
|
+
}
|
|
61
|
+
async checkPrice(service, country) {
|
|
62
|
+
const res = await this.http.get('/api/v1/price', {
|
|
63
|
+
params: { service, country },
|
|
64
|
+
});
|
|
65
|
+
return res.data;
|
|
66
|
+
}
|
|
67
|
+
async getBalance() {
|
|
68
|
+
this.requireApiKey();
|
|
69
|
+
const res = await this.http.get('/api/v1/balance');
|
|
70
|
+
return res.data;
|
|
71
|
+
}
|
|
72
|
+
async createOrder(service, country) {
|
|
73
|
+
this.requireApiKey();
|
|
74
|
+
const res = await this.http.post('/api/v1/order', { service, country });
|
|
75
|
+
return res.data;
|
|
76
|
+
}
|
|
77
|
+
async getOrder(orderId) {
|
|
78
|
+
this.requireApiKey();
|
|
79
|
+
const res = await this.http.get(`/api/v1/order/${orderId}`);
|
|
80
|
+
return res.data;
|
|
81
|
+
}
|
|
82
|
+
async cancelOrder(orderId) {
|
|
83
|
+
this.requireApiKey();
|
|
84
|
+
const res = await this.http.put(`/api/v1/order/${orderId}`, {
|
|
85
|
+
action: 'cancel',
|
|
86
|
+
});
|
|
87
|
+
return res.data;
|
|
88
|
+
}
|
|
89
|
+
async completeOrder(orderId) {
|
|
90
|
+
this.requireApiKey();
|
|
91
|
+
const res = await this.http.put(`/api/v1/order/${orderId}`, {
|
|
92
|
+
action: 'complete',
|
|
93
|
+
});
|
|
94
|
+
return res.data;
|
|
95
|
+
}
|
|
96
|
+
async listOrders(status) {
|
|
97
|
+
this.requireApiKey();
|
|
98
|
+
const params = status ? { status } : {};
|
|
99
|
+
const res = await this.http.get('/api/v1/orders', { params });
|
|
100
|
+
return res.data;
|
|
101
|
+
}
|
|
102
|
+
getApiKey() {
|
|
103
|
+
return this.apiKey;
|
|
104
|
+
}
|
|
105
|
+
getBaseUrl() {
|
|
106
|
+
return this.baseUrl;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;GAIG"}
|