@abhinavyadav/bolna-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/LICENSE +21 -0
- package/README.md +293 -0
- package/dist/client.js +52 -0
- package/dist/index.js +56 -0
- package/dist/tools/agents.js +225 -0
- package/dist/tools/batches.js +192 -0
- package/dist/tools/calls.js +87 -0
- package/dist/tools/dispositions.js +234 -0
- package/dist/tools/executions.js +72 -0
- package/dist/tools/inbound.js +56 -0
- package/dist/tools/knowledgebases.js +129 -0
- package/dist/tools/phone_numbers.js +95 -0
- package/dist/tools/providers.js +67 -0
- package/dist/tools/sip_trunks.js +191 -0
- package/dist/tools/sub_accounts.js +132 -0
- package/dist/tools/user.js +55 -0
- package/dist/tools/violations.js +79 -0
- package/dist/tools/voice.js +37 -0
- package/package.json +58 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Abhinav Yadav
|
|
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,293 @@
|
|
|
1
|
+
# Bolna MCP Server
|
|
2
|
+
|
|
3
|
+
A production-ready [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server that exposes the full [Bolna Voice AI](https://api.bolna.ai) platform as MCP tools. Connect any MCP-compatible AI client (Claude Desktop, Cursor, etc.) to Bolna so you can create and manage voice agents, make and monitor calls, run batch campaigns, manage knowledgebases, and handle phone numbers all through natural language.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Prerequisites
|
|
8
|
+
|
|
9
|
+
- **Node.js 18+**
|
|
10
|
+
- A **Bolna API key** — sign up at [bolna.ai](https://bolna.ai) and find your key in the dashboard
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# 1. Clone or download the repository
|
|
18
|
+
git clone https://github.com/your-org/bolna-mcp.git
|
|
19
|
+
cd bolna-mcp
|
|
20
|
+
|
|
21
|
+
# 2. Install dependencies
|
|
22
|
+
npm install
|
|
23
|
+
|
|
24
|
+
# 3. Build the TypeScript source
|
|
25
|
+
npm run build
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
The compiled server will be available at `dist/index.js`.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## MCP Client Configuration
|
|
33
|
+
|
|
34
|
+
The Bolna MCP server runs as a background process. Here is how you can add it to your favorite MCP clients:
|
|
35
|
+
|
|
36
|
+
> [!TIP]
|
|
37
|
+
> **How to get the absolute path to `dist/index.js` on your machine:**
|
|
38
|
+
>
|
|
39
|
+
> First, ensure you have navigated (using `cd`) into the root of the cloned `bolna-mcp` directory. Then run:
|
|
40
|
+
> - **macOS / Linux**: Run:
|
|
41
|
+
> ```bash
|
|
42
|
+
> echo "$(pwd)/dist/index.js"
|
|
43
|
+
> ```
|
|
44
|
+
> - **Windows (PowerShell)**: Run:
|
|
45
|
+
> ```powershell
|
|
46
|
+
> (Get-Item .).FullName + "\dist\index.js"
|
|
47
|
+
> ```
|
|
48
|
+
> - **Windows (Command Prompt)**: Run:
|
|
49
|
+
> ```cmd
|
|
50
|
+
> echo %cd%\dist\index.js
|
|
51
|
+
> ```
|
|
52
|
+
> Use the resulting path (e.g., `/Users/username/Developer/bolna-mcp/dist/index.js`) as the replacement for `/path/to/bolna-mcp/dist/index.js` in the configs below.
|
|
53
|
+
|
|
54
|
+
### 1. Claude Desktop App
|
|
55
|
+
Add the following to your Claude Desktop configuration file:
|
|
56
|
+
- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
57
|
+
- **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"mcpServers": {
|
|
62
|
+
"bolna": {
|
|
63
|
+
"command": "node",
|
|
64
|
+
"args": ["/path/to/bolna-mcp/dist/index.js"],
|
|
65
|
+
"env": {
|
|
66
|
+
"BOLNA_API_KEY": "your_api_key_here"
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 2. Claude Code (CLI)
|
|
74
|
+
You can add the Bolna MCP server to Claude Code by running:
|
|
75
|
+
```bash
|
|
76
|
+
claude mcp add node /path/to/bolna-mcp/dist/index.js --env BOLNA_API_KEY=your_api_key_here
|
|
77
|
+
```
|
|
78
|
+
Or manually configure it in your `~/.claude.json` configuration file:
|
|
79
|
+
```json
|
|
80
|
+
{
|
|
81
|
+
"mcpServers": {
|
|
82
|
+
"bolna": {
|
|
83
|
+
"command": "node",
|
|
84
|
+
"args": ["/path/to/bolna-mcp/dist/index.js"],
|
|
85
|
+
"env": {
|
|
86
|
+
"BOLNA_API_KEY": "your_api_key_here"
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### 3. Antigravity / Codex (Antigravity CLI / IDE)
|
|
94
|
+
Antigravity (Codex) loads its configuration from:
|
|
95
|
+
- **Path**: `~/.gemini/config/mcp_config.json`
|
|
96
|
+
|
|
97
|
+
Add the server configuration under `mcpServers`:
|
|
98
|
+
```json
|
|
99
|
+
{
|
|
100
|
+
"mcpServers": {
|
|
101
|
+
"bolna": {
|
|
102
|
+
"command": "node",
|
|
103
|
+
"args": ["/path/to/bolna-mcp/dist/index.js"],
|
|
104
|
+
"env": {
|
|
105
|
+
"BOLNA_API_KEY": "your_api_key_here"
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### 4. Cursor
|
|
113
|
+
1. Open Cursor and navigate to **Settings** (Gear icon in top right) -> **Features** -> **MCP**.
|
|
114
|
+
2. Click **+ Add New MCP Server**.
|
|
115
|
+
3. Fill out the fields:
|
|
116
|
+
- **Name**: `bolna`
|
|
117
|
+
- **Type**: `command`
|
|
118
|
+
- **Command**: `env BOLNA_API_KEY=your_api_key_here node /path/to/bolna-mcp/dist/index.js`
|
|
119
|
+
|
|
120
|
+
### 5. Windsurf
|
|
121
|
+
Windsurf reads its configuration from:
|
|
122
|
+
- **Path**: `~/.codeium/windsurf/mcp_config.json`
|
|
123
|
+
|
|
124
|
+
Add the server under `mcpServers`:
|
|
125
|
+
```json
|
|
126
|
+
{
|
|
127
|
+
"mcpServers": {
|
|
128
|
+
"bolna": {
|
|
129
|
+
"command": "node",
|
|
130
|
+
"args": ["/path/to/bolna-mcp/dist/index.js"],
|
|
131
|
+
"env": {
|
|
132
|
+
"BOLNA_API_KEY": "your_api_key_here"
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### 6. VS Code (via extensions like Cline / Roo Code)
|
|
140
|
+
If you use Cline or Roo Code inside VS Code, they read their MCP configurations from:
|
|
141
|
+
- **macOS**: `~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`
|
|
142
|
+
- **Windows**: `%APPDATA%\Code\User\globalStorage\saoudrizwan.claude-dev\settings\cline_mcp_settings.json`
|
|
143
|
+
|
|
144
|
+
Add the configuration block:
|
|
145
|
+
```json
|
|
146
|
+
{
|
|
147
|
+
"mcpServers": {
|
|
148
|
+
"bolna": {
|
|
149
|
+
"command": "node",
|
|
150
|
+
"args": ["/path/to/bolna-mcp/dist/index.js"],
|
|
151
|
+
"env": {
|
|
152
|
+
"BOLNA_API_KEY": "your_api_key_here"
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
> [!IMPORTANT]
|
|
160
|
+
> Make sure to replace `/path/to/bolna-mcp` with the absolute path where the repository is located on your local machine, and set your real Bolna API key.
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Available Tools
|
|
165
|
+
|
|
166
|
+
### 🤖 Agents
|
|
167
|
+
| Tool | Description |
|
|
168
|
+
|------|-------------|
|
|
169
|
+
| `bolna_create_agent` | Create a new voice AI agent with system prompt and task config |
|
|
170
|
+
| `bolna_get_agent` | Retrieve full configuration of an agent by ID |
|
|
171
|
+
| `bolna_list_agents` | List all agents in your account (paginated) |
|
|
172
|
+
| `bolna_update_agent` | Update an existing agent's config or system prompt |
|
|
173
|
+
| `bolna_delete_agent` | Permanently delete an agent |
|
|
174
|
+
|
|
175
|
+
### 📞 Calls
|
|
176
|
+
| Tool | Description |
|
|
177
|
+
|------|-------------|
|
|
178
|
+
| `bolna_make_call` | Initiate an outbound call with optional scheduling and retry logic |
|
|
179
|
+
| `bolna_stop_call` | Immediately terminate an active call |
|
|
180
|
+
|
|
181
|
+
### 📋 Batch Campaigns
|
|
182
|
+
| Tool | Description |
|
|
183
|
+
|------|-------------|
|
|
184
|
+
| `bolna_create_batch` | Upload a CSV of phone numbers to create a batch campaign |
|
|
185
|
+
| `bolna_schedule_batch` | Schedule a batch to run at a specific datetime |
|
|
186
|
+
| `bolna_stop_batch` | Stop a running batch and cancel pending calls |
|
|
187
|
+
| `bolna_get_batch` | Get status and call counts for a batch |
|
|
188
|
+
| `bolna_list_batches` | List all batches for an agent (paginated) |
|
|
189
|
+
| `bolna_get_batch_executions` | List all call executions within a batch |
|
|
190
|
+
|
|
191
|
+
### 📊 Call History (Executions)
|
|
192
|
+
| Tool | Description |
|
|
193
|
+
|------|-------------|
|
|
194
|
+
| `bolna_get_execution` | Get full call details: transcript, recording URL, telephony data |
|
|
195
|
+
| `bolna_list_agent_executions` | List all call executions for an agent (paginated) |
|
|
196
|
+
|
|
197
|
+
### 📚 Knowledgebases
|
|
198
|
+
| Tool | Description |
|
|
199
|
+
|------|-------------|
|
|
200
|
+
| `bolna_create_knowledgebase` | Create a knowledgebase from a PDF file (base64) or a web URL |
|
|
201
|
+
| `bolna_list_knowledgebases` | List all knowledgebases with status and metadata |
|
|
202
|
+
| `bolna_delete_knowledgebase` | Delete a knowledgebase |
|
|
203
|
+
|
|
204
|
+
### 📱 Phone Numbers
|
|
205
|
+
| Tool | Description |
|
|
206
|
+
|------|-------------|
|
|
207
|
+
| `bolna_search_phone_numbers` | Search available numbers by country, area code, or pattern |
|
|
208
|
+
| `bolna_buy_phone_number` | Purchase a phone number for your account |
|
|
209
|
+
| `bolna_list_phone_numbers` | List all phone numbers on your account |
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## Example Claude Prompts
|
|
214
|
+
|
|
215
|
+
### 1. Create an agent and make a test call
|
|
216
|
+
```
|
|
217
|
+
Create a Hindi appointment booking agent for a hospital.
|
|
218
|
+
The agent should greet callers warmly, collect their name and preferred appointment time,
|
|
219
|
+
and confirm the booking. Then make a test call to +919999999999 using that agent.
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### 2. Review batch call results
|
|
223
|
+
```
|
|
224
|
+
Show me all call executions for agent <agent_id> from the last batch.
|
|
225
|
+
Include the transcript and recording URL for any calls that lasted more than 2 minutes.
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### 3. Purchase an Indian phone number
|
|
229
|
+
```
|
|
230
|
+
Search for available Indian phone numbers with area code 080 (Bangalore).
|
|
231
|
+
Show me the top 3 options with their prices, then buy the cheapest one using Twilio as the provider.
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## Environment Variables
|
|
237
|
+
|
|
238
|
+
| Variable | Required | Description |
|
|
239
|
+
|----------|----------|-------------|
|
|
240
|
+
| `BOLNA_API_KEY` | ✅ Yes | Your Bolna platform API key |
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## Development
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
# Run in development mode (no build step)
|
|
248
|
+
BOLNA_API_KEY=your_key npm run dev
|
|
249
|
+
|
|
250
|
+
# Build for production
|
|
251
|
+
npm run build
|
|
252
|
+
|
|
253
|
+
# Run built server
|
|
254
|
+
BOLNA_API_KEY=your_key npm start
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## Testing
|
|
260
|
+
|
|
261
|
+
The project ships with a Jest + ts-jest test suite located in `src/__tests__/`. Tests are entirely offline — no real API calls are made; the Bolna client is fully mocked.
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
# Run all tests
|
|
265
|
+
BOLNA_API_KEY=any-value npm test
|
|
266
|
+
|
|
267
|
+
# Run with coverage report
|
|
268
|
+
BOLNA_API_KEY=any-value npm run test:coverage
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### Test Coverage
|
|
272
|
+
|
|
273
|
+
| File | Statements | Branches | Functions | Lines |
|
|
274
|
+
|------|-----------|----------|-----------|-------|
|
|
275
|
+
| `client.ts` | 95% | 78% | 100% | 95% |
|
|
276
|
+
| `calls.ts` | 100% | 96% | 100% | 100% |
|
|
277
|
+
| `agents.ts` | 33% | 6% | 50% | 33% |
|
|
278
|
+
|
|
279
|
+
> **Note**: `agents.ts` coverage is intentionally lower — the test suite focuses on `list`, `get`, and `delete` endpoints (most commonly used). More tool coverage can be added to `src/__tests__/agents.test.ts`.
|
|
280
|
+
|
|
281
|
+
### Test Files
|
|
282
|
+
|
|
283
|
+
| File | What's tested |
|
|
284
|
+
|------|---------------|
|
|
285
|
+
| `src/__tests__/calls.test.ts` | `bolna_make_call` (6 cases) + `bolna_stop_call` (2 cases) |
|
|
286
|
+
| `src/__tests__/agents.test.ts` | `bolna_list_agents`, `bolna_get_agent`, `bolna_delete_agent` |
|
|
287
|
+
| `src/__tests__/client.test.ts` | `handleAxiosError` (4 cases) + `bolnaClient` factory (3 cases) |
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## License
|
|
292
|
+
|
|
293
|
+
MIT
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
function _export(target, all) {
|
|
6
|
+
for(var name in all)Object.defineProperty(target, name, {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: Object.getOwnPropertyDescriptor(all, name).get
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
_export(exports, {
|
|
12
|
+
get bolnaClient () {
|
|
13
|
+
return bolnaClient;
|
|
14
|
+
},
|
|
15
|
+
get handleAxiosError () {
|
|
16
|
+
return handleAxiosError;
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
const _axios = /*#__PURE__*/ _interop_require_default(require("axios"));
|
|
20
|
+
const _types = require("@modelcontextprotocol/sdk/types.js");
|
|
21
|
+
function _interop_require_default(obj) {
|
|
22
|
+
return obj && obj.__esModule ? obj : {
|
|
23
|
+
default: obj
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const BOLNA_BASE_URL = "https://api.bolna.ai";
|
|
27
|
+
function createBolnaClient() {
|
|
28
|
+
const apiKey = process.env.BOLNA_API_KEY;
|
|
29
|
+
if (!apiKey) {
|
|
30
|
+
throw new Error("BOLNA_API_KEY environment variable is required");
|
|
31
|
+
}
|
|
32
|
+
const client = _axios.default.create({
|
|
33
|
+
baseURL: BOLNA_BASE_URL,
|
|
34
|
+
headers: {
|
|
35
|
+
Authorization: `Bearer ${apiKey}`,
|
|
36
|
+
"Content-Type": "application/json"
|
|
37
|
+
},
|
|
38
|
+
timeout: 30000
|
|
39
|
+
});
|
|
40
|
+
return client;
|
|
41
|
+
}
|
|
42
|
+
const bolnaClient = createBolnaClient();
|
|
43
|
+
function handleAxiosError(error, context) {
|
|
44
|
+
if (_axios.default.isAxiosError(error)) {
|
|
45
|
+
const axiosError = error;
|
|
46
|
+
const status = axiosError.response?.status ?? "unknown";
|
|
47
|
+
const data = axiosError.response?.data;
|
|
48
|
+
const message = data && (data.message || data.detail) || axiosError.message || "Unknown error";
|
|
49
|
+
throw new _types.McpError(_types.ErrorCode.InternalError, `${context} failed (HTTP ${status}): ${message}`);
|
|
50
|
+
}
|
|
51
|
+
throw new _types.McpError(_types.ErrorCode.InternalError, `${context} failed: ${String(error)}`);
|
|
52
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
const _mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
7
|
+
const _stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
8
|
+
const _agents = require("./tools/agents.js");
|
|
9
|
+
const _calls = require("./tools/calls.js");
|
|
10
|
+
const _batches = require("./tools/batches.js");
|
|
11
|
+
const _executions = require("./tools/executions.js");
|
|
12
|
+
const _knowledgebases = require("./tools/knowledgebases.js");
|
|
13
|
+
const _phone_numbers = require("./tools/phone_numbers.js");
|
|
14
|
+
const _inbound = require("./tools/inbound.js");
|
|
15
|
+
const _dispositions = require("./tools/dispositions.js");
|
|
16
|
+
const _providers = require("./tools/providers.js");
|
|
17
|
+
const _sub_accounts = require("./tools/sub_accounts.js");
|
|
18
|
+
const _voice = require("./tools/voice.js");
|
|
19
|
+
const _user = require("./tools/user.js");
|
|
20
|
+
const _sip_trunks = require("./tools/sip_trunks.js");
|
|
21
|
+
const _violations = require("./tools/violations.js");
|
|
22
|
+
// Validate required environment variables at startup
|
|
23
|
+
if (!process.env.BOLNA_API_KEY) {
|
|
24
|
+
process.stderr.write("Error: BOLNA_API_KEY environment variable is required\n");
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
async function main() {
|
|
28
|
+
const server = new _mcp.McpServer({
|
|
29
|
+
name: "bolna-mcp",
|
|
30
|
+
version: "1.0.0"
|
|
31
|
+
});
|
|
32
|
+
// ── Current APIs ──────────────────────────────────────────────────────────
|
|
33
|
+
(0, _agents.registerAgentTools)(server); // Agent CRUD + patch + stop queued
|
|
34
|
+
(0, _calls.registerCallTools)(server); // Make call + stop call
|
|
35
|
+
(0, _batches.registerBatchTools)(server); // Batch CRUD + schedule/stop/executions
|
|
36
|
+
(0, _executions.registerExecutionTools)(server); // Get execution + list + raw logs
|
|
37
|
+
(0, _knowledgebases.registerKnowledgebaseTools)(server); // KB create/get/list/delete
|
|
38
|
+
(0, _phone_numbers.registerPhoneNumberTools)(server); // Search/buy/list/delete phone numbers
|
|
39
|
+
(0, _inbound.registerInboundTools)(server); // Set + unlink inbound agent
|
|
40
|
+
(0, _dispositions.registerDispositionTools)(server); // Dispositions CRUD + bulk + test
|
|
41
|
+
(0, _providers.registerProviderTools)(server); // Add/list/remove providers
|
|
42
|
+
(0, _sub_accounts.registerSubAccountTools)(server); // Sub-account CRUD + usage
|
|
43
|
+
(0, _voice.registerVoiceTools)(server); // List voices
|
|
44
|
+
(0, _user.registerUserTools)(server); // User info + add custom LLM
|
|
45
|
+
(0, _sip_trunks.registerSipTrunkTools)(server); // SIP trunk CRUD + numbers
|
|
46
|
+
(0, _violations.registerViolationTools)(server); // List + submit violations
|
|
47
|
+
// Use stdio transport (standard MCP pattern)
|
|
48
|
+
const transport = new _stdio.StdioServerTransport();
|
|
49
|
+
await server.connect(transport);
|
|
50
|
+
// Log to stderr only — stdout is reserved for MCP protocol messages
|
|
51
|
+
process.stderr.write("Bolna MCP Server running\n");
|
|
52
|
+
}
|
|
53
|
+
main().catch((error)=>{
|
|
54
|
+
process.stderr.write(`Fatal error: ${error.message}\n`);
|
|
55
|
+
process.exit(1);
|
|
56
|
+
});
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "registerAgentTools", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return registerAgentTools;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _zod = require("zod");
|
|
12
|
+
const _client = require("../client.js");
|
|
13
|
+
function registerAgentTools(server) {
|
|
14
|
+
server.tool("bolna_create_agent", "Create a new Bolna voice AI agent with full configuration and system prompt", {
|
|
15
|
+
agent_name: _zod.z.string().describe("Unique name for the agent"),
|
|
16
|
+
agent_welcome_message: _zod.z.string().optional().describe("First message the agent speaks when a call connects"),
|
|
17
|
+
agent_type: _zod.z.string().optional().default("other").describe('Agent type, e.g. "other"'),
|
|
18
|
+
webhook_url: _zod.z.string().url().nullable().optional().describe("Webhook URL to receive call events"),
|
|
19
|
+
hangup_after_silence: _zod.z.number().optional().default(10).describe("Seconds of silence before hanging up"),
|
|
20
|
+
incremental_delay: _zod.z.number().optional().default(400).describe("Delay in ms between incremental responses"),
|
|
21
|
+
number_of_words_for_interruption: _zod.z.number().optional().default(2).describe("Number of words the caller must speak to interrupt the agent"),
|
|
22
|
+
call_terminate: _zod.z.number().optional().describe("Maximum call duration in seconds"),
|
|
23
|
+
ambient_noise_track: _zod.z.string().optional().describe('Ambient noise track, e.g. "coffee-shop"'),
|
|
24
|
+
system_prompt: _zod.z.string().describe("LLM system prompt that governs agent behaviour")
|
|
25
|
+
}, async (args)=>{
|
|
26
|
+
try {
|
|
27
|
+
const body = {
|
|
28
|
+
agent_config: {
|
|
29
|
+
agent_name: args.agent_name,
|
|
30
|
+
...args.agent_welcome_message !== undefined && {
|
|
31
|
+
agent_welcome_message: args.agent_welcome_message
|
|
32
|
+
},
|
|
33
|
+
...args.agent_type !== undefined && {
|
|
34
|
+
agent_type: args.agent_type
|
|
35
|
+
},
|
|
36
|
+
webhook_url: args.webhook_url ?? null,
|
|
37
|
+
tasks: [
|
|
38
|
+
{
|
|
39
|
+
task_type: "conversation",
|
|
40
|
+
task_config: {
|
|
41
|
+
hangup_after_silence: args.hangup_after_silence ?? 10,
|
|
42
|
+
incremental_delay: args.incremental_delay ?? 400,
|
|
43
|
+
number_of_words_for_interruption: args.number_of_words_for_interruption ?? 2,
|
|
44
|
+
...args.call_terminate !== undefined && {
|
|
45
|
+
call_terminate: args.call_terminate
|
|
46
|
+
},
|
|
47
|
+
...args.ambient_noise_track !== undefined && {
|
|
48
|
+
ambient_noise_track: args.ambient_noise_track
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
},
|
|
54
|
+
agent_prompts: {
|
|
55
|
+
task_1: {
|
|
56
|
+
system_prompt: args.system_prompt
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
const response = await _client.bolnaClient.post("/v2/agent", body);
|
|
61
|
+
return {
|
|
62
|
+
content: [
|
|
63
|
+
{
|
|
64
|
+
type: "text",
|
|
65
|
+
text: JSON.stringify(response.data, null, 2)
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
};
|
|
69
|
+
} catch (error) {
|
|
70
|
+
(0, _client.handleAxiosError)(error, "bolna_create_agent");
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
server.tool("bolna_get_agent", "Retrieve the full configuration of a specific Bolna agent by its ID", {
|
|
74
|
+
agent_id: _zod.z.string().describe("The unique ID of the agent to retrieve")
|
|
75
|
+
}, async (args)=>{
|
|
76
|
+
try {
|
|
77
|
+
const response = await _client.bolnaClient.get(`/v2/agent/${args.agent_id}`);
|
|
78
|
+
return {
|
|
79
|
+
content: [
|
|
80
|
+
{
|
|
81
|
+
type: "text",
|
|
82
|
+
text: JSON.stringify(response.data, null, 2)
|
|
83
|
+
}
|
|
84
|
+
]
|
|
85
|
+
};
|
|
86
|
+
} catch (error) {
|
|
87
|
+
(0, _client.handleAxiosError)(error, "bolna_get_agent");
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
server.tool("bolna_list_agents", "List all Bolna agents in your account with optional pagination", {
|
|
91
|
+
page_number: _zod.z.number().int().positive().optional().default(1).describe("Page number (1-indexed)"),
|
|
92
|
+
page_size: _zod.z.number().int().positive().optional().default(10).describe("Number of agents per page")
|
|
93
|
+
}, async (args)=>{
|
|
94
|
+
try {
|
|
95
|
+
const response = await _client.bolnaClient.get("/v2/agent/all", {
|
|
96
|
+
params: {
|
|
97
|
+
page_number: args.page_number ?? 1,
|
|
98
|
+
page_size: args.page_size ?? 10
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
return {
|
|
102
|
+
content: [
|
|
103
|
+
{
|
|
104
|
+
type: "text",
|
|
105
|
+
text: JSON.stringify(response.data, null, 2)
|
|
106
|
+
}
|
|
107
|
+
]
|
|
108
|
+
};
|
|
109
|
+
} catch (error) {
|
|
110
|
+
(0, _client.handleAxiosError)(error, "bolna_list_agents");
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
server.tool("bolna_update_agent", "Update an existing Bolna agent's configuration and/or system prompt", {
|
|
114
|
+
agent_id: _zod.z.string().describe("The unique ID of the agent to update"),
|
|
115
|
+
agent_name: _zod.z.string().optional().describe("New name for the agent"),
|
|
116
|
+
agent_welcome_message: _zod.z.string().optional().describe("New welcome message"),
|
|
117
|
+
agent_type: _zod.z.string().optional().describe("Agent type"),
|
|
118
|
+
webhook_url: _zod.z.string().url().nullable().optional().describe("New webhook URL"),
|
|
119
|
+
hangup_after_silence: _zod.z.number().optional().describe("Seconds of silence before hanging up"),
|
|
120
|
+
incremental_delay: _zod.z.number().optional().describe("Delay in ms between incremental responses"),
|
|
121
|
+
number_of_words_for_interruption: _zod.z.number().optional().describe("Words to interrupt"),
|
|
122
|
+
call_terminate: _zod.z.number().optional().describe("Max call duration in seconds"),
|
|
123
|
+
ambient_noise_track: _zod.z.string().optional().describe("Ambient noise track"),
|
|
124
|
+
system_prompt: _zod.z.string().optional().describe("New LLM system prompt")
|
|
125
|
+
}, async (args)=>{
|
|
126
|
+
try {
|
|
127
|
+
const { agent_id, system_prompt, ...rest } = args;
|
|
128
|
+
const taskConfig = {};
|
|
129
|
+
if (rest.hangup_after_silence !== undefined) taskConfig.hangup_after_silence = rest.hangup_after_silence;
|
|
130
|
+
if (rest.incremental_delay !== undefined) taskConfig.incremental_delay = rest.incremental_delay;
|
|
131
|
+
if (rest.number_of_words_for_interruption !== undefined) taskConfig.number_of_words_for_interruption = rest.number_of_words_for_interruption;
|
|
132
|
+
if (rest.call_terminate !== undefined) taskConfig.call_terminate = rest.call_terminate;
|
|
133
|
+
if (rest.ambient_noise_track !== undefined) taskConfig.ambient_noise_track = rest.ambient_noise_track;
|
|
134
|
+
const agentConfig = {};
|
|
135
|
+
if (rest.agent_name !== undefined) agentConfig.agent_name = rest.agent_name;
|
|
136
|
+
if (rest.agent_welcome_message !== undefined) agentConfig.agent_welcome_message = rest.agent_welcome_message;
|
|
137
|
+
if (rest.agent_type !== undefined) agentConfig.agent_type = rest.agent_type;
|
|
138
|
+
if (rest.webhook_url !== undefined) agentConfig.webhook_url = rest.webhook_url;
|
|
139
|
+
if (Object.keys(taskConfig).length > 0) {
|
|
140
|
+
agentConfig.tasks = [
|
|
141
|
+
{
|
|
142
|
+
task_type: "conversation",
|
|
143
|
+
task_config: taskConfig
|
|
144
|
+
}
|
|
145
|
+
];
|
|
146
|
+
}
|
|
147
|
+
const body = {};
|
|
148
|
+
if (Object.keys(agentConfig).length > 0) body.agent_config = agentConfig;
|
|
149
|
+
if (system_prompt !== undefined) {
|
|
150
|
+
body.agent_prompts = {
|
|
151
|
+
task_1: {
|
|
152
|
+
system_prompt
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
const response = await _client.bolnaClient.put(`/v2/agent/${agent_id}`, body);
|
|
157
|
+
return {
|
|
158
|
+
content: [
|
|
159
|
+
{
|
|
160
|
+
type: "text",
|
|
161
|
+
text: JSON.stringify(response.data, null, 2)
|
|
162
|
+
}
|
|
163
|
+
]
|
|
164
|
+
};
|
|
165
|
+
} catch (error) {
|
|
166
|
+
(0, _client.handleAxiosError)(error, "bolna_update_agent");
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
server.tool("bolna_delete_agent", "Permanently delete a Bolna agent by its ID", {
|
|
170
|
+
agent_id: _zod.z.string().describe("The unique ID of the agent to delete")
|
|
171
|
+
}, async (args)=>{
|
|
172
|
+
try {
|
|
173
|
+
const response = await _client.bolnaClient.delete(`/v2/agent/${args.agent_id}`);
|
|
174
|
+
return {
|
|
175
|
+
content: [
|
|
176
|
+
{
|
|
177
|
+
type: "text",
|
|
178
|
+
text: JSON.stringify(response.data, null, 2) || `Agent ${args.agent_id} deleted successfully.`
|
|
179
|
+
}
|
|
180
|
+
]
|
|
181
|
+
};
|
|
182
|
+
} catch (error) {
|
|
183
|
+
(0, _client.handleAxiosError)(error, "bolna_delete_agent");
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
server.tool("bolna_patch_agent", "Partially update specific fields of a Bolna agent (name, welcome message, webhook URL, etc.)", {
|
|
187
|
+
agent_id: _zod.z.string().describe("The unique ID of the agent to patch"),
|
|
188
|
+
agent_name: _zod.z.string().optional().describe("New agent name"),
|
|
189
|
+
agent_welcome_message: _zod.z.string().optional().describe("New welcome message"),
|
|
190
|
+
webhook_url: _zod.z.string().url().nullable().optional().describe("New webhook URL or null to remove"),
|
|
191
|
+
system_prompt: _zod.z.string().optional().describe("New system prompt")
|
|
192
|
+
}, async (args)=>{
|
|
193
|
+
try {
|
|
194
|
+
const { agent_id, ...fields } = args;
|
|
195
|
+
const response = await _client.bolnaClient.patch(`/v2/agent/${agent_id}`, fields);
|
|
196
|
+
return {
|
|
197
|
+
content: [
|
|
198
|
+
{
|
|
199
|
+
type: "text",
|
|
200
|
+
text: JSON.stringify(response.data, null, 2)
|
|
201
|
+
}
|
|
202
|
+
]
|
|
203
|
+
};
|
|
204
|
+
} catch (error) {
|
|
205
|
+
(0, _client.handleAxiosError)(error, "bolna_patch_agent");
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
server.tool("bolna_stop_agent_queued_calls", "Stop all queued (pending) calls for a specific Bolna agent, preventing them from executing", {
|
|
209
|
+
agent_id: _zod.z.string().describe("The unique ID of the agent whose queued calls to cancel")
|
|
210
|
+
}, async (args)=>{
|
|
211
|
+
try {
|
|
212
|
+
const response = await _client.bolnaClient.post(`/v2/agent/${args.agent_id}/stop`);
|
|
213
|
+
return {
|
|
214
|
+
content: [
|
|
215
|
+
{
|
|
216
|
+
type: "text",
|
|
217
|
+
text: JSON.stringify(response.data, null, 2) || `All queued calls for agent ${args.agent_id} stopped.`
|
|
218
|
+
}
|
|
219
|
+
]
|
|
220
|
+
};
|
|
221
|
+
} catch (error) {
|
|
222
|
+
(0, _client.handleAxiosError)(error, "bolna_stop_agent_queued_calls");
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
}
|