voipbin-mcp 0.1.0__tar.gz
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.
- voipbin_mcp-0.1.0/.github/workflows/ci.yml +20 -0
- voipbin_mcp-0.1.0/.github/workflows/publish.yml +16 -0
- voipbin_mcp-0.1.0/.gitignore +15 -0
- voipbin_mcp-0.1.0/LICENSE +21 -0
- voipbin_mcp-0.1.0/PKG-INFO +159 -0
- voipbin_mcp-0.1.0/README.md +130 -0
- voipbin_mcp-0.1.0/pyproject.toml +52 -0
- voipbin_mcp-0.1.0/src/voipbin_mcp/__init__.py +1 -0
- voipbin_mcp-0.1.0/src/voipbin_mcp/client.py +106 -0
- voipbin_mcp-0.1.0/src/voipbin_mcp/server.py +47 -0
- voipbin_mcp-0.1.0/src/voipbin_mcp/tools/__init__.py +20 -0
- voipbin_mcp-0.1.0/src/voipbin_mcp/tools/activeflows.py +45 -0
- voipbin_mcp-0.1.0/src/voipbin_mcp/tools/agents.py +33 -0
- voipbin_mcp-0.1.0/src/voipbin_mcp/tools/ais.py +72 -0
- voipbin_mcp-0.1.0/src/voipbin_mcp/tools/billings.py +31 -0
- voipbin_mcp-0.1.0/src/voipbin_mcp/tools/calls.py +77 -0
- voipbin_mcp-0.1.0/src/voipbin_mcp/tools/campaigns.py +91 -0
- voipbin_mcp-0.1.0/src/voipbin_mcp/tools/conferences.py +76 -0
- voipbin_mcp-0.1.0/src/voipbin_mcp/tools/contacts.py +109 -0
- voipbin_mcp-0.1.0/src/voipbin_mcp/tools/conversations.py +33 -0
- voipbin_mcp-0.1.0/src/voipbin_mcp/tools/customer.py +14 -0
- voipbin_mcp-0.1.0/src/voipbin_mcp/tools/emails.py +53 -0
- voipbin_mcp-0.1.0/src/voipbin_mcp/tools/extensions.py +33 -0
- voipbin_mcp-0.1.0/src/voipbin_mcp/tools/flows.py +93 -0
- voipbin_mcp-0.1.0/src/voipbin_mcp/tools/messages.py +53 -0
- voipbin_mcp-0.1.0/src/voipbin_mcp/tools/numbers.py +31 -0
- voipbin_mcp-0.1.0/src/voipbin_mcp/tools/queues.py +33 -0
- voipbin_mcp-0.1.0/src/voipbin_mcp/tools/routes.py +33 -0
- voipbin_mcp-0.1.0/src/voipbin_mcp/tools/tags.py +33 -0
- voipbin_mcp-0.1.0/tests/test_client.py +116 -0
- voipbin_mcp-0.1.0/tests/test_server.py +52 -0
- voipbin_mcp-0.1.0/tests/test_tools_calls.py +130 -0
- voipbin_mcp-0.1.0/tests/test_tools_flows.py +163 -0
- voipbin_mcp-0.1.0/uv.lock +905 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.10", "3.11", "3.12"]
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
- uses: astral-sh/setup-uv@v4
|
|
18
|
+
- run: uv venv --python ${{ matrix.python-version }}
|
|
19
|
+
- run: uv pip install -e ".[dev]"
|
|
20
|
+
- run: uv run pytest tests/ -v
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
publish:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
permissions:
|
|
11
|
+
id-token: write
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- uses: astral-sh/setup-uv@v4
|
|
15
|
+
- run: uv build
|
|
16
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 VoIPbin
|
|
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.
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: voipbin-mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: MCP server for the VoIPbin CPaaS platform
|
|
5
|
+
Project-URL: Homepage, https://voipbin.net
|
|
6
|
+
Project-URL: Repository, https://github.com/voipbin/mcp
|
|
7
|
+
Project-URL: Bug Tracker, https://github.com/voipbin/mcp/issues
|
|
8
|
+
Author-email: VoIPbin <support@voipbin.net>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: ai,cpaas,mcp,model-context-protocol,telephony,voip,voipbin
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Communications :: Telephony
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Requires-Dist: httpx>=0.27.0
|
|
23
|
+
Requires-Dist: mcp>=1.0.0
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
26
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
27
|
+
Requires-Dist: respx>=0.21; extra == 'dev'
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# VoIPbin MCP Server
|
|
31
|
+
|
|
32
|
+
An MCP (Model Context Protocol) server that enables AI assistants to interact with the VoIPbin CPaaS platform. It exposes VoIPbin's communication APIs -- calls, flows, messaging, conferencing, AI, and more -- as tools that any MCP-compatible client can use to manage and automate cloud communications.
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pip install voipbin-mcp
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Or run directly without installing:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
uvx voipbin-mcp
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Configuration
|
|
47
|
+
|
|
48
|
+
### Claude Code
|
|
49
|
+
|
|
50
|
+
Add to `~/.claude.json`:
|
|
51
|
+
|
|
52
|
+
```json
|
|
53
|
+
{
|
|
54
|
+
"mcpServers": {
|
|
55
|
+
"voipbin": {
|
|
56
|
+
"command": "uvx",
|
|
57
|
+
"args": ["voipbin-mcp"],
|
|
58
|
+
"env": {
|
|
59
|
+
"VOIPBIN_API_KEY": "your-access-key"
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Cursor
|
|
67
|
+
|
|
68
|
+
Add to `.cursor/mcp.json` in your project directory:
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{
|
|
72
|
+
"mcpServers": {
|
|
73
|
+
"voipbin": {
|
|
74
|
+
"command": "uvx",
|
|
75
|
+
"args": ["voipbin-mcp"],
|
|
76
|
+
"env": {
|
|
77
|
+
"VOIPBIN_API_KEY": "your-access-key"
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Generic MCP Client
|
|
85
|
+
|
|
86
|
+
Any MCP-compatible client can connect by running the `voipbin-mcp` command with the `VOIPBIN_API_KEY` environment variable set:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
VOIPBIN_API_KEY=your-access-key voipbin-mcp
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Available Tools
|
|
93
|
+
|
|
94
|
+
| Resource | Tools |
|
|
95
|
+
|---|---|
|
|
96
|
+
| Calls | `list_calls`, `get_call`, `create_call`, `hangup_call` |
|
|
97
|
+
| Flows | `list_flows`, `get_flow`, `create_flow`, `update_flow`, `delete_flow` |
|
|
98
|
+
| Active Flows | `list_activeflows`, `get_activeflow`, `stop_activeflow` |
|
|
99
|
+
| Agents | `list_agents`, `get_agent` |
|
|
100
|
+
| Numbers | `list_numbers`, `get_number` |
|
|
101
|
+
| Contacts | `list_contacts`, `get_contact`, `create_contact`, `update_contact`, `delete_contact` |
|
|
102
|
+
| Messages | `list_messages`, `get_message`, `send_message` |
|
|
103
|
+
| Emails | `list_emails`, `get_email`, `send_email` |
|
|
104
|
+
| Conversations | `list_conversations`, `get_conversation` |
|
|
105
|
+
| Conferences | `list_conferences`, `get_conference`, `create_conference`, `delete_conference` |
|
|
106
|
+
| Campaigns | `list_campaigns`, `get_campaign`, `create_campaign`, `update_campaign`, `delete_campaign` |
|
|
107
|
+
| Queues | `list_queues`, `get_queue` |
|
|
108
|
+
| Routes | `list_routes`, `get_route` |
|
|
109
|
+
| Billings | `list_billings`, `get_billing` |
|
|
110
|
+
| AIs | `list_ais`, `get_ai`, `create_ai` |
|
|
111
|
+
| Customer | `get_customer` |
|
|
112
|
+
| Tags | `list_tags`, `get_tag` |
|
|
113
|
+
| Extensions | `list_extensions`, `get_extension` |
|
|
114
|
+
|
|
115
|
+
## Example Usage
|
|
116
|
+
|
|
117
|
+
Once configured, you can ask your AI assistant to interact with VoIPbin directly:
|
|
118
|
+
|
|
119
|
+
**List active calls:**
|
|
120
|
+
> "Show me all my active calls"
|
|
121
|
+
|
|
122
|
+
The AI uses `list_calls` to fetch and display your current calls.
|
|
123
|
+
|
|
124
|
+
**Create a flow:**
|
|
125
|
+
> "Create a flow that answers and plays a greeting"
|
|
126
|
+
|
|
127
|
+
The AI uses `create_flow` to build a call flow with answer and play actions.
|
|
128
|
+
|
|
129
|
+
**Check billing:**
|
|
130
|
+
> "What are my recent billing charges?"
|
|
131
|
+
|
|
132
|
+
The AI uses `list_billings` to retrieve your billing history.
|
|
133
|
+
|
|
134
|
+
**Manage contacts:**
|
|
135
|
+
> "Add a new contact named John with phone number +1234567890"
|
|
136
|
+
|
|
137
|
+
The AI uses `create_contact` to create the contact in your account.
|
|
138
|
+
|
|
139
|
+
## Security Note
|
|
140
|
+
|
|
141
|
+
Your VoIPbin API key is sent as a URL query parameter (`accesskey=`) on every request. The connection uses HTTPS, so the key is encrypted in transit. However, be aware that URL parameters may be recorded in server access logs and proxy logs. Avoid sharing unredacted debug output, and rotate your key if you suspect it has been exposed.
|
|
142
|
+
|
|
143
|
+
## Getting an API Key
|
|
144
|
+
|
|
145
|
+
Sign up at [voipbin.net](https://voipbin.net) and create an access key through the API or the admin console at [admin.voipbin.net](https://admin.voipbin.net).
|
|
146
|
+
|
|
147
|
+
## Development
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
git clone https://github.com/voipbin/mcp.git
|
|
151
|
+
cd mcp
|
|
152
|
+
uv venv
|
|
153
|
+
uv pip install -e ".[dev]"
|
|
154
|
+
uv run pytest tests/ -v
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## License
|
|
158
|
+
|
|
159
|
+
MIT
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# VoIPbin MCP Server
|
|
2
|
+
|
|
3
|
+
An MCP (Model Context Protocol) server that enables AI assistants to interact with the VoIPbin CPaaS platform. It exposes VoIPbin's communication APIs -- calls, flows, messaging, conferencing, AI, and more -- as tools that any MCP-compatible client can use to manage and automate cloud communications.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install voipbin-mcp
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or run directly without installing:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
uvx voipbin-mcp
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Configuration
|
|
18
|
+
|
|
19
|
+
### Claude Code
|
|
20
|
+
|
|
21
|
+
Add to `~/.claude.json`:
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"mcpServers": {
|
|
26
|
+
"voipbin": {
|
|
27
|
+
"command": "uvx",
|
|
28
|
+
"args": ["voipbin-mcp"],
|
|
29
|
+
"env": {
|
|
30
|
+
"VOIPBIN_API_KEY": "your-access-key"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Cursor
|
|
38
|
+
|
|
39
|
+
Add to `.cursor/mcp.json` in your project directory:
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"mcpServers": {
|
|
44
|
+
"voipbin": {
|
|
45
|
+
"command": "uvx",
|
|
46
|
+
"args": ["voipbin-mcp"],
|
|
47
|
+
"env": {
|
|
48
|
+
"VOIPBIN_API_KEY": "your-access-key"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Generic MCP Client
|
|
56
|
+
|
|
57
|
+
Any MCP-compatible client can connect by running the `voipbin-mcp` command with the `VOIPBIN_API_KEY` environment variable set:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
VOIPBIN_API_KEY=your-access-key voipbin-mcp
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Available Tools
|
|
64
|
+
|
|
65
|
+
| Resource | Tools |
|
|
66
|
+
|---|---|
|
|
67
|
+
| Calls | `list_calls`, `get_call`, `create_call`, `hangup_call` |
|
|
68
|
+
| Flows | `list_flows`, `get_flow`, `create_flow`, `update_flow`, `delete_flow` |
|
|
69
|
+
| Active Flows | `list_activeflows`, `get_activeflow`, `stop_activeflow` |
|
|
70
|
+
| Agents | `list_agents`, `get_agent` |
|
|
71
|
+
| Numbers | `list_numbers`, `get_number` |
|
|
72
|
+
| Contacts | `list_contacts`, `get_contact`, `create_contact`, `update_contact`, `delete_contact` |
|
|
73
|
+
| Messages | `list_messages`, `get_message`, `send_message` |
|
|
74
|
+
| Emails | `list_emails`, `get_email`, `send_email` |
|
|
75
|
+
| Conversations | `list_conversations`, `get_conversation` |
|
|
76
|
+
| Conferences | `list_conferences`, `get_conference`, `create_conference`, `delete_conference` |
|
|
77
|
+
| Campaigns | `list_campaigns`, `get_campaign`, `create_campaign`, `update_campaign`, `delete_campaign` |
|
|
78
|
+
| Queues | `list_queues`, `get_queue` |
|
|
79
|
+
| Routes | `list_routes`, `get_route` |
|
|
80
|
+
| Billings | `list_billings`, `get_billing` |
|
|
81
|
+
| AIs | `list_ais`, `get_ai`, `create_ai` |
|
|
82
|
+
| Customer | `get_customer` |
|
|
83
|
+
| Tags | `list_tags`, `get_tag` |
|
|
84
|
+
| Extensions | `list_extensions`, `get_extension` |
|
|
85
|
+
|
|
86
|
+
## Example Usage
|
|
87
|
+
|
|
88
|
+
Once configured, you can ask your AI assistant to interact with VoIPbin directly:
|
|
89
|
+
|
|
90
|
+
**List active calls:**
|
|
91
|
+
> "Show me all my active calls"
|
|
92
|
+
|
|
93
|
+
The AI uses `list_calls` to fetch and display your current calls.
|
|
94
|
+
|
|
95
|
+
**Create a flow:**
|
|
96
|
+
> "Create a flow that answers and plays a greeting"
|
|
97
|
+
|
|
98
|
+
The AI uses `create_flow` to build a call flow with answer and play actions.
|
|
99
|
+
|
|
100
|
+
**Check billing:**
|
|
101
|
+
> "What are my recent billing charges?"
|
|
102
|
+
|
|
103
|
+
The AI uses `list_billings` to retrieve your billing history.
|
|
104
|
+
|
|
105
|
+
**Manage contacts:**
|
|
106
|
+
> "Add a new contact named John with phone number +1234567890"
|
|
107
|
+
|
|
108
|
+
The AI uses `create_contact` to create the contact in your account.
|
|
109
|
+
|
|
110
|
+
## Security Note
|
|
111
|
+
|
|
112
|
+
Your VoIPbin API key is sent as a URL query parameter (`accesskey=`) on every request. The connection uses HTTPS, so the key is encrypted in transit. However, be aware that URL parameters may be recorded in server access logs and proxy logs. Avoid sharing unredacted debug output, and rotate your key if you suspect it has been exposed.
|
|
113
|
+
|
|
114
|
+
## Getting an API Key
|
|
115
|
+
|
|
116
|
+
Sign up at [voipbin.net](https://voipbin.net) and create an access key through the API or the admin console at [admin.voipbin.net](https://admin.voipbin.net).
|
|
117
|
+
|
|
118
|
+
## Development
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
git clone https://github.com/voipbin/mcp.git
|
|
122
|
+
cd mcp
|
|
123
|
+
uv venv
|
|
124
|
+
uv pip install -e ".[dev]"
|
|
125
|
+
uv run pytest tests/ -v
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## License
|
|
129
|
+
|
|
130
|
+
MIT
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "voipbin-mcp"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "MCP server for the VoIPbin CPaaS platform"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
requires-python = ">=3.10"
|
|
8
|
+
authors = [
|
|
9
|
+
{name = "VoIPbin", email = "support@voipbin.net"},
|
|
10
|
+
]
|
|
11
|
+
keywords = ["mcp", "voipbin", "cpaas", "voip", "telephony", "ai", "model-context-protocol"]
|
|
12
|
+
classifiers = [
|
|
13
|
+
"Development Status :: 4 - Beta",
|
|
14
|
+
"Intended Audience :: Developers",
|
|
15
|
+
"License :: OSI Approved :: MIT License",
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"Programming Language :: Python :: 3.10",
|
|
18
|
+
"Programming Language :: Python :: 3.11",
|
|
19
|
+
"Programming Language :: Python :: 3.12",
|
|
20
|
+
"Topic :: Communications :: Telephony",
|
|
21
|
+
"Topic :: Software Development :: Libraries",
|
|
22
|
+
]
|
|
23
|
+
dependencies = [
|
|
24
|
+
"mcp>=1.0.0",
|
|
25
|
+
"httpx>=0.27.0",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[project.urls]
|
|
29
|
+
Homepage = "https://voipbin.net"
|
|
30
|
+
Repository = "https://github.com/voipbin/mcp"
|
|
31
|
+
"Bug Tracker" = "https://github.com/voipbin/mcp/issues"
|
|
32
|
+
|
|
33
|
+
[project.scripts]
|
|
34
|
+
voipbin-mcp = "voipbin_mcp.server:main"
|
|
35
|
+
|
|
36
|
+
[build-system]
|
|
37
|
+
requires = ["hatchling"]
|
|
38
|
+
build-backend = "hatchling.build"
|
|
39
|
+
|
|
40
|
+
[tool.hatch.build.targets.wheel]
|
|
41
|
+
packages = ["src/voipbin_mcp"]
|
|
42
|
+
|
|
43
|
+
[tool.pytest.ini_options]
|
|
44
|
+
asyncio_mode = "auto"
|
|
45
|
+
testpaths = ["tests"]
|
|
46
|
+
|
|
47
|
+
[project.optional-dependencies]
|
|
48
|
+
dev = [
|
|
49
|
+
"pytest>=8.0",
|
|
50
|
+
"pytest-asyncio>=0.23",
|
|
51
|
+
"respx>=0.21",
|
|
52
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""VoIPbin MCP Server - MCP tools for the VoIPbin CPaaS platform."""
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"""HTTP client for the VoIPbin REST API."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
import httpx
|
|
8
|
+
|
|
9
|
+
BASE_URL = "https://api.voipbin.net/v1.0"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class VoIPbinAPIError(Exception):
|
|
13
|
+
"""Raised when a VoIPbin API call fails."""
|
|
14
|
+
|
|
15
|
+
def __init__(self, status_code: int, message: str):
|
|
16
|
+
self.status_code = status_code
|
|
17
|
+
self.message = message
|
|
18
|
+
super().__init__(message)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class VoIPbinClient:
|
|
22
|
+
"""Async HTTP client for the VoIPbin REST API."""
|
|
23
|
+
|
|
24
|
+
def __init__(self):
|
|
25
|
+
self.api_key = os.environ.get("VOIPBIN_API_KEY")
|
|
26
|
+
if not self.api_key:
|
|
27
|
+
raise ValueError(
|
|
28
|
+
"VOIPBIN_API_KEY environment variable is required. "
|
|
29
|
+
"Set it to your VoIPbin access key."
|
|
30
|
+
)
|
|
31
|
+
self.base_url = BASE_URL
|
|
32
|
+
self._client = httpx.AsyncClient(timeout=30.0)
|
|
33
|
+
|
|
34
|
+
def __repr__(self) -> str:
|
|
35
|
+
base_url = getattr(self, "base_url", "<not initialised>")
|
|
36
|
+
return f"VoIPbinClient(base_url={base_url!r})"
|
|
37
|
+
|
|
38
|
+
def _params_with_auth(self, params: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
39
|
+
"""Add accesskey to query parameters."""
|
|
40
|
+
result = dict(params) if params else {}
|
|
41
|
+
result["accesskey"] = self.api_key
|
|
42
|
+
return result
|
|
43
|
+
|
|
44
|
+
def _handle_error(self, response: httpx.Response) -> None:
|
|
45
|
+
"""Raise descriptive errors for non-2xx responses."""
|
|
46
|
+
if response.is_success:
|
|
47
|
+
return
|
|
48
|
+
|
|
49
|
+
try:
|
|
50
|
+
body = response.json()
|
|
51
|
+
detail = body.get("message", "")
|
|
52
|
+
except Exception:
|
|
53
|
+
detail = response.text
|
|
54
|
+
|
|
55
|
+
error_map = {
|
|
56
|
+
401: "Invalid or expired API key",
|
|
57
|
+
402: "Insufficient credits",
|
|
58
|
+
403: "Permission denied",
|
|
59
|
+
404: f"Resource not found: {detail}",
|
|
60
|
+
409: f"Conflict: {detail}",
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
message = error_map.get(response.status_code, f"API error {response.status_code}: {detail}")
|
|
64
|
+
raise VoIPbinAPIError(response.status_code, message)
|
|
65
|
+
|
|
66
|
+
async def get(self, path: str, params: dict[str, Any] | None = None) -> dict:
|
|
67
|
+
"""Send a GET request."""
|
|
68
|
+
response = await self._client.get(
|
|
69
|
+
f"{self.base_url}{path}",
|
|
70
|
+
params=self._params_with_auth(params),
|
|
71
|
+
)
|
|
72
|
+
self._handle_error(response)
|
|
73
|
+
return response.json()
|
|
74
|
+
|
|
75
|
+
async def post(self, path: str, json: dict[str, Any] | None = None) -> dict:
|
|
76
|
+
"""Send a POST request."""
|
|
77
|
+
response = await self._client.post(
|
|
78
|
+
f"{self.base_url}{path}",
|
|
79
|
+
params=self._params_with_auth(),
|
|
80
|
+
json=json,
|
|
81
|
+
)
|
|
82
|
+
self._handle_error(response)
|
|
83
|
+
return response.json()
|
|
84
|
+
|
|
85
|
+
async def put(self, path: str, json: dict[str, Any] | None = None) -> dict:
|
|
86
|
+
"""Send a PUT request."""
|
|
87
|
+
response = await self._client.put(
|
|
88
|
+
f"{self.base_url}{path}",
|
|
89
|
+
params=self._params_with_auth(),
|
|
90
|
+
json=json,
|
|
91
|
+
)
|
|
92
|
+
self._handle_error(response)
|
|
93
|
+
return response.json()
|
|
94
|
+
|
|
95
|
+
async def delete(self, path: str) -> dict:
|
|
96
|
+
"""Send a DELETE request."""
|
|
97
|
+
response = await self._client.delete(
|
|
98
|
+
f"{self.base_url}{path}",
|
|
99
|
+
params=self._params_with_auth(),
|
|
100
|
+
)
|
|
101
|
+
self._handle_error(response)
|
|
102
|
+
return response.json()
|
|
103
|
+
|
|
104
|
+
async def close(self):
|
|
105
|
+
"""Close the underlying HTTP client."""
|
|
106
|
+
await self._client.aclose()
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""VoIPbin MCP server entry point."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
|
|
5
|
+
from mcp.server.fastmcp import FastMCP
|
|
6
|
+
|
|
7
|
+
from voipbin_mcp.client import VoIPbinClient
|
|
8
|
+
|
|
9
|
+
mcp = FastMCP("voipbin")
|
|
10
|
+
|
|
11
|
+
# Lazy-initialized client (created on first tool call)
|
|
12
|
+
_client: VoIPbinClient | None = None
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def get_client() -> VoIPbinClient:
|
|
16
|
+
"""Get or create the VoIPbin HTTP client."""
|
|
17
|
+
global _client
|
|
18
|
+
if _client is None:
|
|
19
|
+
_client = VoIPbinClient()
|
|
20
|
+
return _client
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def format_response(data: dict) -> str:
|
|
24
|
+
"""Format API response as readable JSON string."""
|
|
25
|
+
return json.dumps(data, indent=2, default=str)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def validate_page_size(page_size: int) -> int:
|
|
29
|
+
"""Clamp page_size to a safe integer range (1–100)."""
|
|
30
|
+
try:
|
|
31
|
+
page_size = int(page_size)
|
|
32
|
+
except (TypeError, ValueError):
|
|
33
|
+
page_size = 10
|
|
34
|
+
return max(1, min(page_size, 100))
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# Import tools to register them on the mcp instance
|
|
38
|
+
import voipbin_mcp.tools # noqa: E402, F401
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def main():
|
|
42
|
+
"""Run the VoIPbin MCP server over stdio."""
|
|
43
|
+
mcp.run(transport="stdio")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
if __name__ == "__main__":
|
|
47
|
+
main()
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""VoIPbin MCP tools — importing registers them on the server."""
|
|
2
|
+
|
|
3
|
+
from voipbin_mcp.tools import calls # noqa: F401
|
|
4
|
+
from voipbin_mcp.tools import flows # noqa: F401
|
|
5
|
+
from voipbin_mcp.tools import activeflows # noqa: F401
|
|
6
|
+
from voipbin_mcp.tools import agents # noqa: F401
|
|
7
|
+
from voipbin_mcp.tools import numbers # noqa: F401
|
|
8
|
+
from voipbin_mcp.tools import contacts # noqa: F401
|
|
9
|
+
from voipbin_mcp.tools import messages # noqa: F401
|
|
10
|
+
from voipbin_mcp.tools import emails # noqa: F401
|
|
11
|
+
from voipbin_mcp.tools import conversations # noqa: F401
|
|
12
|
+
from voipbin_mcp.tools import conferences # noqa: F401
|
|
13
|
+
from voipbin_mcp.tools import campaigns # noqa: F401
|
|
14
|
+
from voipbin_mcp.tools import queues # noqa: F401
|
|
15
|
+
from voipbin_mcp.tools import routes # noqa: F401
|
|
16
|
+
from voipbin_mcp.tools import billings # noqa: F401
|
|
17
|
+
from voipbin_mcp.tools import ais # noqa: F401
|
|
18
|
+
from voipbin_mcp.tools import customer # noqa: F401
|
|
19
|
+
from voipbin_mcp.tools import tags # noqa: F401
|
|
20
|
+
from voipbin_mcp.tools import extensions # noqa: F401
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""Active flow tools."""
|
|
2
|
+
|
|
3
|
+
from voipbin_mcp.server import mcp, get_client, format_response, validate_page_size
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@mcp.tool()
|
|
7
|
+
async def list_activeflows(page_size: int = 10, page_token: str = "") -> str:
|
|
8
|
+
"""List all currently running active flows.
|
|
9
|
+
|
|
10
|
+
Active flows are flow instances currently executing on calls.
|
|
11
|
+
|
|
12
|
+
Args:
|
|
13
|
+
page_size: Number of results per page (default 10).
|
|
14
|
+
page_token: Pagination cursor from a previous response.
|
|
15
|
+
"""
|
|
16
|
+
client = get_client()
|
|
17
|
+
params = {"page_size": validate_page_size(page_size)}
|
|
18
|
+
if page_token:
|
|
19
|
+
params["page_token"] = page_token
|
|
20
|
+
result = await client.get("/activeflows", params=params)
|
|
21
|
+
return format_response(result)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@mcp.tool()
|
|
25
|
+
async def get_activeflow(activeflow_id: str) -> str:
|
|
26
|
+
"""Get details of a running active flow.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
activeflow_id: The UUID of the active flow.
|
|
30
|
+
"""
|
|
31
|
+
client = get_client()
|
|
32
|
+
result = await client.get(f"/activeflows/{activeflow_id}")
|
|
33
|
+
return format_response(result)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@mcp.tool()
|
|
37
|
+
async def stop_activeflow(activeflow_id: str) -> str:
|
|
38
|
+
"""Stop a running active flow.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
activeflow_id: The UUID of the active flow to stop.
|
|
42
|
+
"""
|
|
43
|
+
client = get_client()
|
|
44
|
+
result = await client.post(f"/activeflows/{activeflow_id}/stop")
|
|
45
|
+
return format_response(result)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""Agent tools."""
|
|
2
|
+
|
|
3
|
+
from voipbin_mcp.server import mcp, get_client, format_response, validate_page_size
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@mcp.tool()
|
|
7
|
+
async def list_agents(page_size: int = 10, page_token: str = "") -> str:
|
|
8
|
+
"""List all agents in your VoIPbin account.
|
|
9
|
+
|
|
10
|
+
Agents are users who can handle calls, messages, and other interactions.
|
|
11
|
+
|
|
12
|
+
Args:
|
|
13
|
+
page_size: Number of results per page (default 10).
|
|
14
|
+
page_token: Pagination cursor from a previous response.
|
|
15
|
+
"""
|
|
16
|
+
client = get_client()
|
|
17
|
+
params = {"page_size": validate_page_size(page_size)}
|
|
18
|
+
if page_token:
|
|
19
|
+
params["page_token"] = page_token
|
|
20
|
+
result = await client.get("/agents", params=params)
|
|
21
|
+
return format_response(result)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@mcp.tool()
|
|
25
|
+
async def get_agent(agent_id: str) -> str:
|
|
26
|
+
"""Get details of a specific agent.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
agent_id: The UUID of the agent.
|
|
30
|
+
"""
|
|
31
|
+
client = get_client()
|
|
32
|
+
result = await client.get(f"/agents/{agent_id}")
|
|
33
|
+
return format_response(result)
|