@vapi-ai/mcp-server 0.0.9 → 0.0.11
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/README.md +130 -112
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/dist/schemas/index.d.ts +18 -18
- package/dist/tools/assistant.d.ts.map +1 -1
- package/dist/tools/assistant.js +4 -1
- package/dist/tools/call.d.ts.map +1 -1
- package/dist/tools/call.js +4 -1
- package/dist/tools/phone-number.d.ts.map +1 -1
- package/dist/tools/phone-number.js +4 -1
- package/dist/tools/tool.d.ts.map +1 -1
- package/dist/tools/tool.js +4 -1
- package/package.json +22 -11
- package/dist/__tests__/mcp-server-e2e.test.d.ts +0 -2
- package/dist/__tests__/mcp-server-e2e.test.d.ts.map +0 -1
- package/dist/__tests__/mcp-server-e2e.test.js +0 -239
- package/dist/__tests__/mcp-server-mock.test.d.ts +0 -2
- package/dist/__tests__/mcp-server-mock.test.d.ts.map +0 -1
- package/dist/__tests__/mcp-server-mock.test.js +0 -34
- package/dist/__tests__/mocks/vapi-client.mock.d.ts +0 -9
- package/dist/__tests__/mocks/vapi-client.mock.d.ts.map +0 -1
- package/dist/__tests__/mocks/vapi-client.mock.js +0 -43
package/README.md
CHANGED
|
@@ -2,33 +2,32 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://smithery.ai/server/@VapiAI/vapi-mcp-server)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Build AI voice assistants and phone agents with [Vapi](https://vapi.ai) using the [Model Context Protocol](https://modelcontextprotocol.com/).
|
|
6
6
|
|
|
7
7
|
<a href="https://glama.ai/mcp/servers/@VapiAI/mcp-server">
|
|
8
8
|
<img width="380" height="200" src="https://glama.ai/mcp/servers/@VapiAI/mcp-server/badge" alt="Vapi Server MCP server" />
|
|
9
9
|
</a>
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## Setup
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
2. Click on the `Developer` tab.
|
|
15
|
-
3. Click on the `Edit Config` button.
|
|
16
|
-
4. This will open the `claude_desktop_config.json` file in your file explorer.
|
|
17
|
-
5. Get your Vapi API key from the Vapi dashboard (<https://dashboard.vapi.ai/org/api-keys>).
|
|
18
|
-
6. Add the following to your `claude_desktop_config.json` file. See [here](https://modelcontextprotocol.io/quickstart/user) for more details.
|
|
19
|
-
7. Restart the Claude Desktop after editing the config file.
|
|
13
|
+
The MCP server requires a Vapi API key. Get one from the [Vapi dashboard](https://dashboard.vapi.ai/org/api-keys).
|
|
20
14
|
|
|
21
|
-
### Local
|
|
15
|
+
### Local Server (stdio)
|
|
16
|
+
|
|
17
|
+
Configure any MCP client that supports local stdio servers to run:
|
|
18
|
+
|
|
19
|
+
```text
|
|
20
|
+
npx -y @vapi-ai/mcp-server
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Set `VAPI_TOKEN` in the server environment. MCP client configuration formats vary, but the server definition generally looks like this:
|
|
22
24
|
|
|
23
25
|
```json
|
|
24
26
|
{
|
|
25
27
|
"mcpServers": {
|
|
26
|
-
"vapi
|
|
28
|
+
"vapi": {
|
|
27
29
|
"command": "npx",
|
|
28
|
-
"args": [
|
|
29
|
-
"-y",
|
|
30
|
-
"@vapi-ai/mcp-server"
|
|
31
|
-
],
|
|
30
|
+
"args": ["-y", "@vapi-ai/mcp-server"],
|
|
32
31
|
"env": {
|
|
33
32
|
"VAPI_TOKEN": "<your_vapi_token>"
|
|
34
33
|
}
|
|
@@ -37,12 +36,29 @@ The Vapi [Model Context Protocol](https://modelcontextprotocol.com/) server allo
|
|
|
37
36
|
}
|
|
38
37
|
```
|
|
39
38
|
|
|
40
|
-
### Remote
|
|
39
|
+
### Remote Server (Streamable HTTP)
|
|
40
|
+
|
|
41
|
+
Clients that support remote MCP servers can connect directly:
|
|
42
|
+
|
|
43
|
+
- URL: `https://mcp.vapi.ai/mcp`
|
|
44
|
+
- Header: `Authorization: Bearer your_vapi_api_key_here`
|
|
45
|
+
|
|
46
|
+
### Client-Specific Examples
|
|
47
|
+
|
|
48
|
+
#### Claude Code
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
claude mcp add -e VAPI_TOKEN=your_vapi_token vapi -- npx -y @vapi-ai/mcp-server
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
#### Claude Desktop
|
|
55
|
+
|
|
56
|
+
Use the local server configuration above in the Claude Desktop configuration file. To connect to the hosted server through an stdio bridge instead:
|
|
41
57
|
|
|
42
58
|
```json
|
|
43
59
|
{
|
|
44
60
|
"mcpServers": {
|
|
45
|
-
"vapi
|
|
61
|
+
"vapi": {
|
|
46
62
|
"command": "npx",
|
|
47
63
|
"args": [
|
|
48
64
|
"mcp-remote",
|
|
@@ -58,40 +74,50 @@ The Vapi [Model Context Protocol](https://modelcontextprotocol.com/) server allo
|
|
|
58
74
|
}
|
|
59
75
|
```
|
|
60
76
|
|
|
61
|
-
###
|
|
77
|
+
### Optional Agent Skill
|
|
62
78
|
|
|
63
|
-
|
|
64
|
-
2. Create a new assistant using the existing 'Appointment Scheduler' template in the Vapi dashboard (<https://dashboard.vapi.ai/assistants>).
|
|
65
|
-
3. Make sure to configure Claude Desktop to use the Vapi MCP server and restart the Claude Desktop app.
|
|
66
|
-
4. Ask Claude to initiate or schedule a call. See examples below:
|
|
79
|
+
The [`skill`](./skill) directory contains reusable instructions for AI coding agents that support Agent Skills. Install it using your host's skill installation process.
|
|
67
80
|
|
|
68
|
-
|
|
81
|
+
For Claude Code:
|
|
69
82
|
|
|
70
|
-
```
|
|
71
|
-
|
|
83
|
+
```bash
|
|
84
|
+
mkdir -p ~/.claude/skills/vapi
|
|
85
|
+
curl -o ~/.claude/skills/vapi/SKILL.md https://raw.githubusercontent.com/VapiAI/mcp-server/main/skill/SKILL.md
|
|
72
86
|
```
|
|
73
87
|
|
|
74
|
-
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Example Usage
|
|
75
91
|
|
|
76
|
-
|
|
77
|
-
|
|
92
|
+
### Create a Voice Assistant
|
|
93
|
+
|
|
94
|
+
Ask your MCP-enabled agent:
|
|
95
|
+
```
|
|
96
|
+
I want to build a voice assistant that can schedule appointments
|
|
78
97
|
```
|
|
79
98
|
|
|
80
|
-
|
|
99
|
+
### Make an Outbound Call
|
|
81
100
|
|
|
82
|
-
```
|
|
83
|
-
|
|
101
|
+
```
|
|
102
|
+
Call +1234567890 using my appointment reminder assistant with these details:
|
|
84
103
|
- Customer name: Sarah Johnson
|
|
85
104
|
- Appointment date: March 25th
|
|
86
105
|
- Appointment time: 2:30 PM
|
|
87
|
-
- Doctor name: Dr. Smith
|
|
88
106
|
```
|
|
89
107
|
|
|
108
|
+
### Schedule a Future Call
|
|
109
|
+
|
|
110
|
+
```
|
|
111
|
+
Schedule a call with my support assistant for next Tuesday at 3:00 PM to +1555123456
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
90
116
|
## Using Variable Values in Assistant Prompts
|
|
91
117
|
|
|
92
|
-
The `create_call` action supports passing dynamic variables through `assistantOverrides.variableValues`.
|
|
118
|
+
The `create_call` action supports passing dynamic variables through `assistantOverrides.variableValues`. Use double curly braces in your assistant's prompts: `{{variableName}}`.
|
|
93
119
|
|
|
94
|
-
### Example
|
|
120
|
+
### Example Prompt with Variables
|
|
95
121
|
|
|
96
122
|
```
|
|
97
123
|
Hello {{customerName}}, this is a reminder about your appointment on {{appointmentDate}} at {{appointmentTime}} with {{doctorName}}.
|
|
@@ -99,7 +125,7 @@ Hello {{customerName}}, this is a reminder about your appointment on {{appointme
|
|
|
99
125
|
|
|
100
126
|
### Default Variables
|
|
101
127
|
|
|
102
|
-
|
|
128
|
+
These are automatically available (no need to pass):
|
|
103
129
|
|
|
104
130
|
- `{{now}}` - Current date and time (UTC)
|
|
105
131
|
- `{{date}}` - Current date (UTC)
|
|
@@ -109,29 +135,58 @@ The following variables are automatically available (no need to pass in variable
|
|
|
109
135
|
- `{{year}}` - Current year (UTC)
|
|
110
136
|
- `{{customer.number}}` - Customer's phone number
|
|
111
137
|
|
|
112
|
-
|
|
138
|
+
See [Vapi documentation](https://docs.vapi.ai/assistants/dynamic-variables#default-variables) for advanced date/time formatting.
|
|
113
139
|
|
|
114
|
-
|
|
140
|
+
---
|
|
115
141
|
|
|
116
|
-
|
|
142
|
+
## Remote MCP Server
|
|
117
143
|
|
|
118
|
-
|
|
144
|
+
Connect to Vapi's hosted MCP server from any MCP client:
|
|
119
145
|
|
|
120
|
-
|
|
146
|
+
### Streamable HTTP (Recommended)
|
|
121
147
|
|
|
122
|
-
-
|
|
123
|
-
-
|
|
124
|
-
- Example header: `Authorization: Bearer your_vapi_api_key_here`
|
|
148
|
+
- URL: `https://mcp.vapi.ai/mcp`
|
|
149
|
+
- Header: `Authorization: Bearer your_vapi_api_key_here`
|
|
125
150
|
|
|
126
151
|
### SSE (Deprecated)
|
|
127
152
|
|
|
128
|
-
|
|
153
|
+
- URL: `https://mcp.vapi.ai/sse`
|
|
154
|
+
- Header: `Authorization: Bearer your_vapi_api_key_here`
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## Available Tools
|
|
159
|
+
|
|
160
|
+
### Assistants
|
|
161
|
+
| Tool | Description |
|
|
162
|
+
|------|-------------|
|
|
163
|
+
| `list_assistants` | List all assistants |
|
|
164
|
+
| `get_assistant` | Get assistant by ID |
|
|
165
|
+
| `create_assistant` | Create new assistant |
|
|
166
|
+
| `update_assistant` | Update assistant |
|
|
129
167
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
168
|
+
### Calls
|
|
169
|
+
| Tool | Description |
|
|
170
|
+
|------|-------------|
|
|
171
|
+
| `list_calls` | List call history |
|
|
172
|
+
| `get_call` | Get call details |
|
|
173
|
+
| `create_call` | Start outbound call (immediate or scheduled) |
|
|
133
174
|
|
|
134
|
-
|
|
175
|
+
### Phone Numbers
|
|
176
|
+
| Tool | Description |
|
|
177
|
+
|------|-------------|
|
|
178
|
+
| `list_phone_numbers` | List phone numbers |
|
|
179
|
+
| `get_phone_number` | Get phone number details |
|
|
180
|
+
|
|
181
|
+
### Tools (Function Calling)
|
|
182
|
+
| Tool | Description |
|
|
183
|
+
|------|-------------|
|
|
184
|
+
| `list_tools` | List custom tools |
|
|
185
|
+
| `get_tool` | Get tool details |
|
|
186
|
+
| `create_tool` | Create tool for API integration |
|
|
187
|
+
| `update_tool` | Update tool |
|
|
188
|
+
|
|
189
|
+
---
|
|
135
190
|
|
|
136
191
|
## Development
|
|
137
192
|
|
|
@@ -139,101 +194,64 @@ This connection allows you to access Vapi's functionality remotely without runni
|
|
|
139
194
|
# Install dependencies
|
|
140
195
|
npm install
|
|
141
196
|
|
|
142
|
-
# Build
|
|
197
|
+
# Build
|
|
143
198
|
npm run build
|
|
144
199
|
|
|
145
|
-
#
|
|
200
|
+
# Test with MCP inspector
|
|
146
201
|
npm run inspector
|
|
147
202
|
```
|
|
148
203
|
|
|
149
|
-
|
|
204
|
+
### Local Development Config
|
|
150
205
|
|
|
151
206
|
```json
|
|
152
207
|
{
|
|
153
208
|
"mcpServers": {
|
|
154
209
|
"vapi-local": {
|
|
155
210
|
"command": "node",
|
|
156
|
-
"args": [
|
|
157
|
-
"<path_to_vapi_mcp_server>/dist/index.js"
|
|
158
|
-
],
|
|
211
|
+
"args": ["<path>/dist/index.js"],
|
|
159
212
|
"env": {
|
|
160
213
|
"VAPI_TOKEN": "<your_vapi_token>"
|
|
161
214
|
}
|
|
162
|
-
}
|
|
215
|
+
}
|
|
163
216
|
}
|
|
164
217
|
}
|
|
165
218
|
```
|
|
166
219
|
|
|
167
220
|
### Testing
|
|
168
221
|
|
|
169
|
-
|
|
222
|
+
```bash
|
|
223
|
+
# Safe default suite (build, mocked execution, contract, and stdio compatibility)
|
|
224
|
+
npm test
|
|
170
225
|
|
|
171
|
-
|
|
226
|
+
# Unit tests (mocked public tool execution and catalog contract)
|
|
227
|
+
npm run test:unit
|
|
172
228
|
|
|
173
|
-
|
|
229
|
+
# Documentation/tool-catalog consistency lint
|
|
230
|
+
npm run test:docs
|
|
174
231
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
npm run test:unit
|
|
178
|
-
```
|
|
232
|
+
# Full TypeScript check (production and test code)
|
|
233
|
+
npm run typecheck
|
|
179
234
|
|
|
180
|
-
|
|
235
|
+
# Build and run local stdio protocol compatibility tests (no Vapi API calls)
|
|
236
|
+
npm run test:stdio
|
|
181
237
|
|
|
182
|
-
|
|
238
|
+
# Pack, install, and launch the package from a clean temporary consumer
|
|
239
|
+
npm run test:package
|
|
183
240
|
|
|
184
|
-
|
|
185
|
-
# Set your Vapi API token
|
|
241
|
+
# Live read-only API tests
|
|
186
242
|
export VAPI_TOKEN=your_token_here
|
|
243
|
+
npm run test:live:readonly
|
|
187
244
|
|
|
188
|
-
#
|
|
189
|
-
|
|
245
|
+
# Live mutating tests (creates and cleans up disposable assistants and tools)
|
|
246
|
+
export VAPI_TOKEN=your_test_org_token_here
|
|
247
|
+
npm run test:live:mutating
|
|
190
248
|
```
|
|
191
249
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
#### Running All Tests
|
|
195
|
-
|
|
196
|
-
To run all tests at once:
|
|
197
|
-
|
|
198
|
-
```bash
|
|
199
|
-
npm test
|
|
200
|
-
```
|
|
250
|
+
---
|
|
201
251
|
|
|
202
252
|
## References
|
|
203
253
|
|
|
204
|
-
- [
|
|
205
|
-
- [
|
|
206
|
-
- [
|
|
254
|
+
- [Vapi Documentation](https://docs.vapi.ai)
|
|
255
|
+
- [Vapi Dashboard](https://dashboard.vapi.ai)
|
|
256
|
+
- [Vapi Remote MCP Server](https://mcp.vapi.ai/)
|
|
207
257
|
- [Model Context Protocol](https://modelcontextprotocol.com/)
|
|
208
|
-
- [Claude Desktop](https://modelcontextprotocol.io/quickstart/user)
|
|
209
|
-
|
|
210
|
-
## Supported Actions
|
|
211
|
-
|
|
212
|
-
The Vapi MCP Server provides the following tools for integration:
|
|
213
|
-
|
|
214
|
-
### Assistant Tools
|
|
215
|
-
|
|
216
|
-
- `list_assistants`: Lists all Vapi assistants
|
|
217
|
-
- `create_assistant`: Creates a new Vapi assistant
|
|
218
|
-
- `update_assistant`: Updates an existing Vapi assistant
|
|
219
|
-
- `get_assistant`: Gets a Vapi assistant by ID
|
|
220
|
-
|
|
221
|
-
### Call Tools
|
|
222
|
-
|
|
223
|
-
- `list_calls`: Lists all Vapi calls
|
|
224
|
-
- `create_call`: Creates an outbound call with support for:
|
|
225
|
-
- Immediate or scheduled calls
|
|
226
|
-
- Dynamic variable values through `assistantOverrides`
|
|
227
|
-
- `get_call`: Gets details of a specific call
|
|
228
|
-
|
|
229
|
-
> **Note:** The `create_call` action supports scheduling calls for immediate execution or for a future time. You can also pass dynamic variables using `assistantOverrides.variableValues` to personalize assistant messages.
|
|
230
|
-
|
|
231
|
-
### Phone Number Tools
|
|
232
|
-
|
|
233
|
-
- `list_phone_numbers`: Lists all Vapi phone numbers
|
|
234
|
-
- `get_phone_number`: Gets details of a specific phone number
|
|
235
|
-
|
|
236
|
-
### Vapi Tools
|
|
237
|
-
|
|
238
|
-
- `list_tools`: Lists all Vapi tools
|
|
239
|
-
- `get_tool`: Gets details of a specific tool
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAGA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAYpE,iBAAS,eAAe,cAgBvB;AAgCD,OAAO,EAAE,eAAe,EAAE,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
2
3
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
4
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
5
|
import { registerAllTools } from './tools/index.js';
|
|
5
6
|
import { createVapiClient } from './client.js';
|
|
6
7
|
import dotenv from 'dotenv';
|
|
7
8
|
dotenv.config();
|
|
9
|
+
const packageMetadata = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
|
|
8
10
|
function createMcpServer() {
|
|
9
11
|
const vapiToken = process.env.VAPI_TOKEN;
|
|
10
12
|
if (!vapiToken) {
|
|
@@ -13,8 +15,7 @@ function createMcpServer() {
|
|
|
13
15
|
const vapiClient = createVapiClient(vapiToken);
|
|
14
16
|
const mcpServer = new McpServer({
|
|
15
17
|
name: 'Vapi MCP',
|
|
16
|
-
version:
|
|
17
|
-
capabilities: [],
|
|
18
|
+
version: packageMetadata.version,
|
|
18
19
|
});
|
|
19
20
|
registerAllTools(mcpServer, vapiClient);
|
|
20
21
|
return mcpServer;
|
|
@@ -27,6 +28,7 @@ async function main() {
|
|
|
27
28
|
setupShutdownHandler(mcpServer);
|
|
28
29
|
}
|
|
29
30
|
catch (err) {
|
|
31
|
+
console.error('Failed to start MCP server:', err);
|
|
30
32
|
process.exit(1);
|
|
31
33
|
}
|
|
32
34
|
}
|
|
@@ -42,6 +44,7 @@ function setupShutdownHandler(mcpServer) {
|
|
|
42
44
|
});
|
|
43
45
|
}
|
|
44
46
|
main().catch((err) => {
|
|
47
|
+
console.error('Fatal error:', err);
|
|
45
48
|
process.exit(1);
|
|
46
49
|
});
|
|
47
50
|
export { createMcpServer };
|
package/dist/schemas/index.d.ts
CHANGED
|
@@ -167,11 +167,11 @@ export declare const CreateAssistantInputSchema: z.ZodObject<{
|
|
|
167
167
|
firstMessage?: string | undefined;
|
|
168
168
|
firstMessageMode?: "assistant-speaks-first" | "assistant-waits-for-user" | "assistant-speaks-first-with-model-generated-message" | undefined;
|
|
169
169
|
}>;
|
|
170
|
-
export declare const AssistantOutputSchema: z.ZodObject<
|
|
170
|
+
export declare const AssistantOutputSchema: z.ZodObject<{
|
|
171
171
|
id: z.ZodString;
|
|
172
172
|
createdAt: z.ZodString;
|
|
173
173
|
updatedAt: z.ZodString;
|
|
174
|
-
}
|
|
174
|
+
} & {
|
|
175
175
|
name: z.ZodString;
|
|
176
176
|
llm: z.ZodObject<{
|
|
177
177
|
provider: z.ZodString;
|
|
@@ -207,7 +207,7 @@ export declare const AssistantOutputSchema: z.ZodObject<z.objectUtil.extendShape
|
|
|
207
207
|
model: string;
|
|
208
208
|
}>;
|
|
209
209
|
toolIds: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
210
|
-
}
|
|
210
|
+
}, "strip", z.ZodTypeAny, {
|
|
211
211
|
id: string;
|
|
212
212
|
createdAt: string;
|
|
213
213
|
updatedAt: string;
|
|
@@ -406,11 +406,11 @@ export declare const CallInputSchema: z.ZodObject<{
|
|
|
406
406
|
variableValues?: Record<string, any> | undefined;
|
|
407
407
|
} | undefined;
|
|
408
408
|
}>;
|
|
409
|
-
export declare const CallOutputSchema: z.ZodObject<
|
|
409
|
+
export declare const CallOutputSchema: z.ZodObject<{
|
|
410
410
|
id: z.ZodString;
|
|
411
411
|
createdAt: z.ZodString;
|
|
412
412
|
updatedAt: z.ZodString;
|
|
413
|
-
}
|
|
413
|
+
} & {
|
|
414
414
|
status: z.ZodString;
|
|
415
415
|
endedReason: z.ZodOptional<z.ZodString>;
|
|
416
416
|
assistantId: z.ZodOptional<z.ZodString>;
|
|
@@ -423,7 +423,7 @@ export declare const CallOutputSchema: z.ZodObject<z.objectUtil.extendShape<{
|
|
|
423
423
|
number: string;
|
|
424
424
|
}>>;
|
|
425
425
|
scheduledAt: z.ZodOptional<z.ZodString>;
|
|
426
|
-
}
|
|
426
|
+
}, "strip", z.ZodTypeAny, {
|
|
427
427
|
status: string;
|
|
428
428
|
id: string;
|
|
429
429
|
createdAt: string;
|
|
@@ -462,11 +462,11 @@ export declare const GetPhoneNumberInputSchema: z.ZodObject<{
|
|
|
462
462
|
}, {
|
|
463
463
|
phoneNumberId: string;
|
|
464
464
|
}>;
|
|
465
|
-
export declare const PhoneNumberOutputSchema: z.ZodObject<
|
|
465
|
+
export declare const PhoneNumberOutputSchema: z.ZodObject<{
|
|
466
466
|
id: z.ZodString;
|
|
467
467
|
createdAt: z.ZodString;
|
|
468
468
|
updatedAt: z.ZodString;
|
|
469
|
-
}
|
|
469
|
+
} & {
|
|
470
470
|
name: z.ZodOptional<z.ZodString>;
|
|
471
471
|
phoneNumber: z.ZodString;
|
|
472
472
|
status: z.ZodString;
|
|
@@ -480,7 +480,7 @@ export declare const PhoneNumberOutputSchema: z.ZodObject<z.objectUtil.extendSha
|
|
|
480
480
|
voice?: boolean | undefined;
|
|
481
481
|
sms?: boolean | undefined;
|
|
482
482
|
}>>;
|
|
483
|
-
}
|
|
483
|
+
}, "strip", z.ZodTypeAny, {
|
|
484
484
|
status: string;
|
|
485
485
|
id: string;
|
|
486
486
|
createdAt: string;
|
|
@@ -510,7 +510,7 @@ export declare const GetToolInputSchema: z.ZodObject<{
|
|
|
510
510
|
}, {
|
|
511
511
|
toolId: string;
|
|
512
512
|
}>;
|
|
513
|
-
export declare const CreateToolInputSchema: z.ZodObject<
|
|
513
|
+
export declare const CreateToolInputSchema: z.ZodObject<{
|
|
514
514
|
name: z.ZodOptional<z.ZodString>;
|
|
515
515
|
description: z.ZodOptional<z.ZodString>;
|
|
516
516
|
sms: z.ZodOptional<z.ZodObject<{
|
|
@@ -772,9 +772,9 @@ export declare const CreateToolInputSchema: z.ZodObject<z.objectUtil.extendShape
|
|
|
772
772
|
} | undefined;
|
|
773
773
|
timeoutSeconds?: number | undefined;
|
|
774
774
|
}>>;
|
|
775
|
-
}
|
|
775
|
+
} & {
|
|
776
776
|
type: z.ZodEnum<["sms", "transferCall", "function", "apiRequest"]>;
|
|
777
|
-
}
|
|
777
|
+
}, "strip", z.ZodTypeAny, {
|
|
778
778
|
type: "function" | "sms" | "transferCall" | "apiRequest";
|
|
779
779
|
function?: {
|
|
780
780
|
parameters: {
|
|
@@ -893,7 +893,7 @@ export declare const CreateToolInputSchema: z.ZodObject<z.objectUtil.extendShape
|
|
|
893
893
|
timeoutSeconds?: number | undefined;
|
|
894
894
|
} | undefined;
|
|
895
895
|
}>;
|
|
896
|
-
export declare const UpdateToolInputSchema: z.ZodObject<
|
|
896
|
+
export declare const UpdateToolInputSchema: z.ZodObject<{
|
|
897
897
|
name: z.ZodOptional<z.ZodString>;
|
|
898
898
|
description: z.ZodOptional<z.ZodString>;
|
|
899
899
|
sms: z.ZodOptional<z.ZodObject<{
|
|
@@ -1155,9 +1155,9 @@ export declare const UpdateToolInputSchema: z.ZodObject<z.objectUtil.extendShape
|
|
|
1155
1155
|
} | undefined;
|
|
1156
1156
|
timeoutSeconds?: number | undefined;
|
|
1157
1157
|
}>>;
|
|
1158
|
-
}
|
|
1158
|
+
} & {
|
|
1159
1159
|
toolId: z.ZodString;
|
|
1160
|
-
}
|
|
1160
|
+
}, "strip", z.ZodTypeAny, {
|
|
1161
1161
|
toolId: string;
|
|
1162
1162
|
function?: {
|
|
1163
1163
|
parameters: {
|
|
@@ -1276,11 +1276,11 @@ export declare const UpdateToolInputSchema: z.ZodObject<z.objectUtil.extendShape
|
|
|
1276
1276
|
timeoutSeconds?: number | undefined;
|
|
1277
1277
|
} | undefined;
|
|
1278
1278
|
}>;
|
|
1279
|
-
export declare const ToolOutputSchema: z.ZodObject<
|
|
1279
|
+
export declare const ToolOutputSchema: z.ZodObject<{
|
|
1280
1280
|
id: z.ZodString;
|
|
1281
1281
|
createdAt: z.ZodString;
|
|
1282
1282
|
updatedAt: z.ZodString;
|
|
1283
|
-
}
|
|
1283
|
+
} & {
|
|
1284
1284
|
type: z.ZodString;
|
|
1285
1285
|
name: z.ZodString;
|
|
1286
1286
|
description: z.ZodString;
|
|
@@ -1295,7 +1295,7 @@ export declare const ToolOutputSchema: z.ZodObject<z.objectUtil.extendShape<{
|
|
|
1295
1295
|
url: string;
|
|
1296
1296
|
headers?: Record<string, string> | undefined;
|
|
1297
1297
|
}>;
|
|
1298
|
-
}
|
|
1298
|
+
}, "strip", z.ZodTypeAny, {
|
|
1299
1299
|
type: string;
|
|
1300
1300
|
id: string;
|
|
1301
1301
|
createdAt: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assistant.d.ts","sourceRoot":"","sources":["../../src/tools/assistant.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,UAAU,EAAQ,MAAM,qBAAqB,CAAC;AAavD,eAAO,MAAM,sBAAsB,GACjC,QAAQ,SAAS,EACjB,YAAY,UAAU,
|
|
1
|
+
{"version":3,"file":"assistant.d.ts","sourceRoot":"","sources":["../../src/tools/assistant.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,UAAU,EAAQ,MAAM,qBAAqB,CAAC;AAavD,eAAO,MAAM,sBAAsB,GACjC,QAAQ,SAAS,EACjB,YAAY,UAAU,SA6EvB,CAAC"}
|
package/dist/tools/assistant.js
CHANGED
|
@@ -2,7 +2,10 @@ import { CreateAssistantInputSchema, GetAssistantInputSchema, UpdateAssistantInp
|
|
|
2
2
|
import { transformAssistantInput, transformAssistantOutput, transformUpdateAssistantInput, } from '../transformers/index.js';
|
|
3
3
|
import { createToolHandler } from './utils.js';
|
|
4
4
|
export const registerAssistantTools = (server, vapiClient) => {
|
|
5
|
-
server.
|
|
5
|
+
server.registerTool('list_assistants', {
|
|
6
|
+
description: 'Lists all Vapi assistants',
|
|
7
|
+
inputSchema: {},
|
|
8
|
+
}, createToolHandler(async () => {
|
|
6
9
|
// console.log('list_assistants');
|
|
7
10
|
const assistants = await vapiClient.assistants.list({ limit: 10 });
|
|
8
11
|
// console.log('assistants', assistants);
|
package/dist/tools/call.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"call.d.ts","sourceRoot":"","sources":["../../src/tools/call.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,UAAU,EAAQ,MAAM,qBAAqB,CAAC;AASvD,eAAO,MAAM,iBAAiB,GAC5B,QAAQ,SAAS,EACjB,YAAY,UAAU,
|
|
1
|
+
{"version":3,"file":"call.d.ts","sourceRoot":"","sources":["../../src/tools/call.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,UAAU,EAAQ,MAAM,qBAAqB,CAAC;AASvD,eAAO,MAAM,iBAAiB,GAC5B,QAAQ,SAAS,EACjB,YAAY,UAAU,SAkCvB,CAAC"}
|
package/dist/tools/call.js
CHANGED
|
@@ -2,7 +2,10 @@ import { CallInputSchema, GetCallInputSchema } from '../schemas/index.js';
|
|
|
2
2
|
import { transformCallInput, transformCallOutput, } from '../transformers/index.js';
|
|
3
3
|
import { createToolHandler } from './utils.js';
|
|
4
4
|
export const registerCallTools = (server, vapiClient) => {
|
|
5
|
-
server.
|
|
5
|
+
server.registerTool('list_calls', {
|
|
6
|
+
description: 'Lists all Vapi calls',
|
|
7
|
+
inputSchema: {},
|
|
8
|
+
}, createToolHandler(async () => {
|
|
6
9
|
const calls = await vapiClient.calls.list({ limit: 10 });
|
|
7
10
|
return calls.map(transformCallOutput);
|
|
8
11
|
}));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"phone-number.d.ts","sourceRoot":"","sources":["../../src/tools/phone-number.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAMjD,eAAO,MAAM,wBAAwB,GACnC,QAAQ,SAAS,EACjB,YAAY,UAAU,
|
|
1
|
+
{"version":3,"file":"phone-number.d.ts","sourceRoot":"","sources":["../../src/tools/phone-number.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAMjD,eAAO,MAAM,wBAAwB,GACnC,QAAQ,SAAS,EACjB,YAAY,UAAU,SAwBvB,CAAC"}
|
|
@@ -2,7 +2,10 @@ import { transformPhoneNumberOutput } from '../transformers/index.js';
|
|
|
2
2
|
import { createToolHandler } from './utils.js';
|
|
3
3
|
import { GetPhoneNumberInputSchema } from '../schemas/index.js';
|
|
4
4
|
export const registerPhoneNumberTools = (server, vapiClient) => {
|
|
5
|
-
server.
|
|
5
|
+
server.registerTool('list_phone_numbers', {
|
|
6
|
+
description: 'Lists all Vapi phone numbers',
|
|
7
|
+
inputSchema: {},
|
|
8
|
+
}, createToolHandler(async () => {
|
|
6
9
|
const phoneNumbers = await vapiClient.phoneNumbers.list({ limit: 10 });
|
|
7
10
|
return phoneNumbers.map(transformPhoneNumberOutput);
|
|
8
11
|
}));
|
package/dist/tools/tool.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/tools/tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,UAAU,EAAQ,MAAM,qBAAqB,CAAC;AAMvD,eAAO,MAAM,iBAAiB,GAC5B,QAAQ,SAAS,EACjB,YAAY,UAAU,
|
|
1
|
+
{"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/tools/tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,UAAU,EAAQ,MAAM,qBAAqB,CAAC;AAMvD,eAAO,MAAM,iBAAiB,GAC5B,QAAQ,SAAS,EACjB,YAAY,UAAU,SA6CvB,CAAC"}
|
package/dist/tools/tool.js
CHANGED
|
@@ -2,7 +2,10 @@ import { GetToolInputSchema, CreateToolInputSchema, UpdateToolInputSchema } from
|
|
|
2
2
|
import { transformToolInput, transformUpdateToolInput, transformToolOutput } from '../transformers/index.js';
|
|
3
3
|
import { createToolHandler } from './utils.js';
|
|
4
4
|
export const registerToolTools = (server, vapiClient) => {
|
|
5
|
-
server.
|
|
5
|
+
server.registerTool('list_tools', {
|
|
6
|
+
description: 'Lists all Vapi tools',
|
|
7
|
+
inputSchema: {},
|
|
8
|
+
}, createToolHandler(async () => {
|
|
6
9
|
const tools = await vapiClient.tools.list({ limit: 10 });
|
|
7
10
|
return tools.map(transformToolOutput);
|
|
8
11
|
}));
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vapi-ai/mcp-server",
|
|
3
|
-
"description": "Vapi
|
|
4
|
-
"version": "0.0.
|
|
3
|
+
"description": "Build AI voice assistants and phone agents with Vapi through the Model Context Protocol",
|
|
4
|
+
"version": "0.0.11",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"type": "module",
|
|
@@ -24,26 +24,37 @@
|
|
|
24
24
|
"vapi",
|
|
25
25
|
"mcp",
|
|
26
26
|
"model-context-protocol",
|
|
27
|
+
"mcp-server",
|
|
28
|
+
"mcp-client",
|
|
27
29
|
"ai",
|
|
28
|
-
"
|
|
30
|
+
"voice-agents",
|
|
29
31
|
"voice-api",
|
|
30
32
|
"tool-calling"
|
|
31
33
|
],
|
|
32
34
|
"bin": {
|
|
33
|
-
"
|
|
35
|
+
"mcp-server": "dist/index.js"
|
|
34
36
|
},
|
|
35
37
|
"scripts": {
|
|
36
38
|
"prepare": "npm run build",
|
|
37
|
-
"
|
|
39
|
+
"clean": "shx rm -rf dist",
|
|
40
|
+
"build": "npm run clean && tsc -p tsconfig.build.json && shx chmod +x dist/*.js",
|
|
41
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
38
42
|
"start": "node dist/index.js",
|
|
39
43
|
"dev": "tsx watch src/index.ts",
|
|
40
44
|
"dev:stdio-example": "tsx examples/stdio-client.ts",
|
|
41
45
|
"dev:sse-example": "tsx examples/sse-client.ts",
|
|
42
46
|
"dev:shttp-example": "tsx examples/shttp-client.ts",
|
|
43
47
|
"inspector": "mcp-inspector",
|
|
44
|
-
"
|
|
45
|
-
"test
|
|
46
|
-
"test:
|
|
48
|
+
"pretest": "npm run build",
|
|
49
|
+
"test": "VAPI_TOKEN=test-mock-token NODE_OPTIONS=--experimental-vm-modules jest --runInBand --testPathIgnorePatterns='.*-live\\.test\\.ts$'",
|
|
50
|
+
"test:unit": "VAPI_TOKEN=test-mock-token NODE_OPTIONS=--experimental-vm-modules jest --runInBand src/__tests__/mcp-tool-execution.test.ts src/__tests__/mcp-tools-list.test.ts",
|
|
51
|
+
"test:docs": "NODE_OPTIONS=--experimental-vm-modules jest --runInBand src/__tests__/documentation-tools.test.ts",
|
|
52
|
+
"pretest:stdio": "npm run build",
|
|
53
|
+
"test:stdio": "VAPI_TOKEN=test-mock-token NODE_OPTIONS=--experimental-vm-modules jest --runInBand src/__tests__/mcp-stdio-compat.test.ts",
|
|
54
|
+
"pretest:package": "npm run build",
|
|
55
|
+
"test:package": "node scripts/package-smoke.mjs",
|
|
56
|
+
"test:live:readonly": "npm run build && RUN_VAPI_READONLY_TESTS=1 NODE_OPTIONS=--experimental-vm-modules jest --runInBand src/__tests__/mcp-server-readonly-live.test.ts",
|
|
57
|
+
"test:live:mutating": "npm run build && RUN_VAPI_MUTATING_TESTS=1 NODE_OPTIONS=--experimental-vm-modules jest --runInBand src/__tests__/mcp-server-mutating-live.test.ts"
|
|
47
58
|
},
|
|
48
59
|
"files": [
|
|
49
60
|
"dist",
|
|
@@ -51,14 +62,14 @@
|
|
|
51
62
|
"dist/**/*.d.ts.map"
|
|
52
63
|
],
|
|
53
64
|
"dependencies": {
|
|
54
|
-
"@modelcontextprotocol/sdk": "
|
|
65
|
+
"@modelcontextprotocol/sdk": "1.26.0",
|
|
55
66
|
"@vapi-ai/server-sdk": "^0.5.2",
|
|
56
67
|
"dotenv": "^16.4.7",
|
|
57
|
-
"zod": "^3.
|
|
68
|
+
"zod": "^3.25.76",
|
|
58
69
|
"zod-to-json-schema": "^3.24.5"
|
|
59
70
|
},
|
|
60
71
|
"devDependencies": {
|
|
61
|
-
"@modelcontextprotocol/inspector": "
|
|
72
|
+
"@modelcontextprotocol/inspector": "1.0.1",
|
|
62
73
|
"@types/jest": "^29.5.14",
|
|
63
74
|
"@types/node": "^22.14.0",
|
|
64
75
|
"jest": "^29.7.0",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-server-e2e.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/mcp-server-e2e.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,239 +0,0 @@
|
|
|
1
|
-
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
2
|
-
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
3
|
-
import { join } from 'path';
|
|
4
|
-
import { jest } from '@jest/globals';
|
|
5
|
-
import dotenv from 'dotenv';
|
|
6
|
-
import { parseToolResponse } from '../utils/response.js';
|
|
7
|
-
dotenv.config();
|
|
8
|
-
jest.setTimeout(15000);
|
|
9
|
-
describe('MCP Server E2E Test', () => {
|
|
10
|
-
let mcpClient;
|
|
11
|
-
let transport;
|
|
12
|
-
beforeAll(async () => {
|
|
13
|
-
const serverScriptPath = join(process.cwd(), 'src', 'index.ts');
|
|
14
|
-
console.log(`Using server source at: ${serverScriptPath}`);
|
|
15
|
-
mcpClient = new Client({ name: 'vapi-e2e-test-client', version: '1.0.0' });
|
|
16
|
-
transport = new StdioClientTransport({
|
|
17
|
-
command: 'npx',
|
|
18
|
-
args: ['tsx', serverScriptPath],
|
|
19
|
-
env: {
|
|
20
|
-
...process.env,
|
|
21
|
-
VAPI_TOKEN: process.env.VAPI_TOKEN || 'test-token',
|
|
22
|
-
},
|
|
23
|
-
});
|
|
24
|
-
console.log('Connecting to MCP server...');
|
|
25
|
-
await mcpClient.connect(transport);
|
|
26
|
-
console.log('Connected to server successfully');
|
|
27
|
-
});
|
|
28
|
-
afterAll(async () => {
|
|
29
|
-
console.log('Disconnecting from server...');
|
|
30
|
-
await mcpClient.close();
|
|
31
|
-
console.log('Disconnected from server');
|
|
32
|
-
});
|
|
33
|
-
test('should list available tools', async () => {
|
|
34
|
-
console.log('Requesting available tools...');
|
|
35
|
-
const toolsResult = await mcpClient.listTools();
|
|
36
|
-
expect(toolsResult.tools).toBeDefined();
|
|
37
|
-
expect(Array.isArray(toolsResult.tools)).toBe(true);
|
|
38
|
-
const toolNames = toolsResult.tools.map((tool) => tool.name);
|
|
39
|
-
console.log('Available tools:', toolNames);
|
|
40
|
-
expect(toolNames).toContain('list_assistants');
|
|
41
|
-
expect(toolNames).toContain('create_assistant');
|
|
42
|
-
expect(toolNames).toContain('get_assistant');
|
|
43
|
-
expect(toolNames).toContain('list_phone_numbers');
|
|
44
|
-
expect(toolNames).toContain('get_phone_number');
|
|
45
|
-
expect(toolNames).toContain('list_calls');
|
|
46
|
-
expect(toolNames).toContain('create_call');
|
|
47
|
-
expect(toolNames).toContain('get_call');
|
|
48
|
-
});
|
|
49
|
-
describe('Assistant Tools', () => {
|
|
50
|
-
test('should list all assistants', async () => {
|
|
51
|
-
console.log('Calling list_assistants tool...');
|
|
52
|
-
const rawResult = await mcpClient.callTool({
|
|
53
|
-
name: 'list_assistants',
|
|
54
|
-
arguments: {},
|
|
55
|
-
});
|
|
56
|
-
const result = parseToolResponse(rawResult);
|
|
57
|
-
expect(result).toBeDefined();
|
|
58
|
-
if (Array.isArray(result)) {
|
|
59
|
-
console.log(`Received ${result.length} assistants in array form`);
|
|
60
|
-
}
|
|
61
|
-
else {
|
|
62
|
-
console.log(`Received assistants in non-array form:`, result);
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
test('should create a new assistant', async () => {
|
|
66
|
-
console.log('Calling create_assistant tool...');
|
|
67
|
-
const assistantData = {
|
|
68
|
-
name: `Test Assistant ${Date.now()}`,
|
|
69
|
-
instructions: 'You are a helpful test assistant',
|
|
70
|
-
llm: {
|
|
71
|
-
provider: 'openai',
|
|
72
|
-
model: 'gpt-4o',
|
|
73
|
-
},
|
|
74
|
-
transcriber: {
|
|
75
|
-
provider: 'deepgram',
|
|
76
|
-
model: 'nova-3',
|
|
77
|
-
},
|
|
78
|
-
voice: {
|
|
79
|
-
provider: '11labs',
|
|
80
|
-
model: 'eleven_turbo_v2_5',
|
|
81
|
-
voiceId: 'sarah',
|
|
82
|
-
},
|
|
83
|
-
firstMessage: "Hello, I'm your vapi assistant.",
|
|
84
|
-
};
|
|
85
|
-
const rawResult = await mcpClient.callTool({
|
|
86
|
-
name: 'create_assistant',
|
|
87
|
-
arguments: assistantData,
|
|
88
|
-
});
|
|
89
|
-
const result = parseToolResponse(rawResult);
|
|
90
|
-
expect(result).toBeDefined();
|
|
91
|
-
expect(result.id).toBeDefined();
|
|
92
|
-
expect(result.name).toBe(assistantData.name);
|
|
93
|
-
expect(result.llm.provider).toBe(assistantData.llm.provider);
|
|
94
|
-
expect(result.llm.model).toBe(assistantData.llm.model);
|
|
95
|
-
console.log(`Successfully created assistant with ID: ${result.id}`);
|
|
96
|
-
});
|
|
97
|
-
test('should get an assistant by ID', async () => {
|
|
98
|
-
console.log('Listing assistants to get the first one...');
|
|
99
|
-
const rawListResult = await mcpClient.callTool({
|
|
100
|
-
name: 'list_assistants',
|
|
101
|
-
arguments: {},
|
|
102
|
-
});
|
|
103
|
-
const listResult = parseToolResponse(rawListResult);
|
|
104
|
-
expect(listResult).toBeDefined();
|
|
105
|
-
expect(Array.isArray(listResult)).toBe(true);
|
|
106
|
-
if (listResult.length === 0) {
|
|
107
|
-
console.log('No assistants found, skipping get_assistant test.');
|
|
108
|
-
// Optionally, you could fail the test here or create an assistant first
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
const firstAssistant = listResult[0];
|
|
112
|
-
expect(firstAssistant.id).toBeDefined();
|
|
113
|
-
const assistantId = firstAssistant.id;
|
|
114
|
-
const assistantName = firstAssistant.name;
|
|
115
|
-
console.log(`Attempting to fetch assistant with ID: ${assistantId} (Name: ${assistantName})`);
|
|
116
|
-
const rawGetResult = await mcpClient.callTool({
|
|
117
|
-
name: 'get_assistant',
|
|
118
|
-
arguments: { assistantId: assistantId },
|
|
119
|
-
});
|
|
120
|
-
const getResult = parseToolResponse(rawGetResult);
|
|
121
|
-
expect(getResult).toBeDefined();
|
|
122
|
-
expect(getResult.id).toBe(assistantId);
|
|
123
|
-
expect(getResult.name).toBe(assistantName); // Verify name matches too
|
|
124
|
-
console.log(`Successfully fetched assistant with ID: ${getResult.id}`);
|
|
125
|
-
});
|
|
126
|
-
test('should handle invalid assistant ID', async () => {
|
|
127
|
-
const invalidId = 'non-existent-assistant-id-' + Date.now();
|
|
128
|
-
console.log(`Testing error handling with invalid assistant ID: ${invalidId}`);
|
|
129
|
-
try {
|
|
130
|
-
const rawResult = await mcpClient.callTool({
|
|
131
|
-
name: 'get_assistant',
|
|
132
|
-
arguments: { assistantId: invalidId },
|
|
133
|
-
});
|
|
134
|
-
const result = parseToolResponse(rawResult);
|
|
135
|
-
// Check for error in the result
|
|
136
|
-
if (result.error ||
|
|
137
|
-
(typeof result === 'string' &&
|
|
138
|
-
(result.includes('Error') || result.includes('error')))) {
|
|
139
|
-
// Test passed - we got an error response
|
|
140
|
-
expect(true).toBe(true);
|
|
141
|
-
}
|
|
142
|
-
else {
|
|
143
|
-
// If we somehow got a successful result, fail the test
|
|
144
|
-
expect(result).toContain('error');
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
catch (error) {
|
|
148
|
-
// This is also acceptable - the API might throw instead of returning error
|
|
149
|
-
expect(error).toBeDefined();
|
|
150
|
-
}
|
|
151
|
-
});
|
|
152
|
-
});
|
|
153
|
-
describe('Phone Number Tools', () => {
|
|
154
|
-
test('should list all phone numbers', async () => {
|
|
155
|
-
console.log('Calling list_phone_numbers tool...');
|
|
156
|
-
const rawResult = await mcpClient.callTool({
|
|
157
|
-
name: 'list_phone_numbers',
|
|
158
|
-
arguments: {},
|
|
159
|
-
});
|
|
160
|
-
const result = parseToolResponse(rawResult);
|
|
161
|
-
expect(result).toBeDefined();
|
|
162
|
-
if (Array.isArray(result)) {
|
|
163
|
-
console.log(`Received ${result.length} phone numbers in array form`);
|
|
164
|
-
}
|
|
165
|
-
else {
|
|
166
|
-
console.log(`Received phone numbers in non-array form:`, result);
|
|
167
|
-
}
|
|
168
|
-
});
|
|
169
|
-
test('should handle invalid phone number ID', async () => {
|
|
170
|
-
const invalidId = 'non-existent-phone-number-id-' + Date.now();
|
|
171
|
-
console.log(`Testing error handling with invalid phone number ID: ${invalidId}`);
|
|
172
|
-
try {
|
|
173
|
-
const rawResult = await mcpClient.callTool({
|
|
174
|
-
name: 'get_phone_number',
|
|
175
|
-
arguments: { phoneNumberId: invalidId },
|
|
176
|
-
});
|
|
177
|
-
const result = parseToolResponse(rawResult);
|
|
178
|
-
// Check for error in the result
|
|
179
|
-
if (result.error ||
|
|
180
|
-
(typeof result === 'string' &&
|
|
181
|
-
(result.includes('Error') || result.includes('error')))) {
|
|
182
|
-
// Test passed - we got an error response
|
|
183
|
-
expect(true).toBe(true);
|
|
184
|
-
}
|
|
185
|
-
else {
|
|
186
|
-
// If we somehow got a successful result, fail the test
|
|
187
|
-
expect(result).toContain('error');
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
catch (error) {
|
|
191
|
-
// This is also acceptable - the API might throw instead of returning error
|
|
192
|
-
expect(error).toBeDefined();
|
|
193
|
-
}
|
|
194
|
-
});
|
|
195
|
-
});
|
|
196
|
-
describe('Call Tools', () => {
|
|
197
|
-
test('should list all calls', async () => {
|
|
198
|
-
console.log('Calling list_calls tool...');
|
|
199
|
-
const rawResult = await mcpClient.callTool({
|
|
200
|
-
name: 'list_calls',
|
|
201
|
-
arguments: {},
|
|
202
|
-
});
|
|
203
|
-
const result = parseToolResponse(rawResult);
|
|
204
|
-
expect(result).toBeDefined();
|
|
205
|
-
if (Array.isArray(result)) {
|
|
206
|
-
console.log(`Received ${result.length} calls in array form`);
|
|
207
|
-
}
|
|
208
|
-
else {
|
|
209
|
-
console.log(`Received calls in non-array form:`, result);
|
|
210
|
-
}
|
|
211
|
-
});
|
|
212
|
-
test('should handle invalid call ID', async () => {
|
|
213
|
-
const invalidId = 'non-existent-call-id-' + Date.now();
|
|
214
|
-
console.log(`Testing error handling with invalid call ID: ${invalidId}`);
|
|
215
|
-
try {
|
|
216
|
-
const rawResult = await mcpClient.callTool({
|
|
217
|
-
name: 'get_call',
|
|
218
|
-
arguments: { callId: invalidId },
|
|
219
|
-
});
|
|
220
|
-
const result = parseToolResponse(rawResult);
|
|
221
|
-
// Check for error in the result
|
|
222
|
-
if (result.error ||
|
|
223
|
-
(typeof result === 'string' &&
|
|
224
|
-
(result.includes('Error') || result.includes('error')))) {
|
|
225
|
-
// Test passed - we got an error response
|
|
226
|
-
expect(true).toBe(true);
|
|
227
|
-
}
|
|
228
|
-
else {
|
|
229
|
-
// If we somehow got a successful result, fail the test
|
|
230
|
-
expect(result).toContain('error');
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
catch (error) {
|
|
234
|
-
// This is also acceptable - the API might throw instead of returning error
|
|
235
|
-
expect(error).toBeDefined();
|
|
236
|
-
}
|
|
237
|
-
});
|
|
238
|
-
});
|
|
239
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-server-mock.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/mcp-server-mock.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { jest } from '@jest/globals';
|
|
2
|
-
import { createMcpServer } from '../index.js';
|
|
3
|
-
import { createMockVapiClient } from './mocks/vapi-client.mock.js';
|
|
4
|
-
jest.mock('../client.js', () => ({
|
|
5
|
-
createVapiClient: jest.fn(() => createMockVapiClient()),
|
|
6
|
-
}));
|
|
7
|
-
process.env.VAPI_TOKEN = 'test-mock-token';
|
|
8
|
-
describe('MCP Server Unit Test (with mocks)', () => {
|
|
9
|
-
let mcpServer;
|
|
10
|
-
beforeAll(() => {
|
|
11
|
-
console.log('Creating MCP server with mocked Vapi client...');
|
|
12
|
-
mcpServer = createMcpServer();
|
|
13
|
-
console.log('MCP server created successfully');
|
|
14
|
-
});
|
|
15
|
-
test('server should be initialized correctly', () => {
|
|
16
|
-
expect(mcpServer).toBeDefined();
|
|
17
|
-
expect(typeof mcpServer).toBe('object');
|
|
18
|
-
expect(mcpServer.constructor.name).toBe('McpServer');
|
|
19
|
-
expect(mcpServer.connect).toBeDefined();
|
|
20
|
-
expect(typeof mcpServer.connect).toBe('function');
|
|
21
|
-
});
|
|
22
|
-
test('server should have required internal structures', () => {
|
|
23
|
-
expect(mcpServer._registeredTools).toBeDefined();
|
|
24
|
-
expect(typeof mcpServer._registeredTools).toBe('object');
|
|
25
|
-
});
|
|
26
|
-
test('server should have registered Vapi tools', () => {
|
|
27
|
-
const registeredTools = Object.keys(mcpServer._registeredTools);
|
|
28
|
-
expect(registeredTools.length).toBeGreaterThan(0);
|
|
29
|
-
console.log('Found registered tools:', registeredTools);
|
|
30
|
-
expect(registeredTools).toContain('list_assistants');
|
|
31
|
-
expect(registeredTools).toContain('create_assistant');
|
|
32
|
-
expect(registeredTools).toContain('get_assistant');
|
|
33
|
-
});
|
|
34
|
-
});
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
export declare class MockVapiClient {
|
|
2
|
-
assistants: {
|
|
3
|
-
list: jest.Mock<any, any, any>;
|
|
4
|
-
get: jest.Mock<any, any, any>;
|
|
5
|
-
create: jest.Mock<any, any, any>;
|
|
6
|
-
};
|
|
7
|
-
}
|
|
8
|
-
export declare const createMockVapiClient: () => MockVapiClient;
|
|
9
|
-
//# sourceMappingURL=vapi-client.mock.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"vapi-client.mock.d.ts","sourceRoot":"","sources":["../../../src/__tests__/mocks/vapi-client.mock.ts"],"names":[],"mappings":"AAAA,qBAAa,cAAc;IACzB,UAAU;;;;MAuCR;CACH;AAED,eAAO,MAAM,oBAAoB,sBAEhC,CAAC"}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
export class MockVapiClient {
|
|
2
|
-
assistants = {
|
|
3
|
-
list: jest.fn().mockResolvedValue([
|
|
4
|
-
{
|
|
5
|
-
id: 'mock-assistant-id-1',
|
|
6
|
-
name: 'Mock Assistant 1',
|
|
7
|
-
model: 'gpt-4',
|
|
8
|
-
instructions: 'Example instructions',
|
|
9
|
-
createdAt: new Date().toISOString(),
|
|
10
|
-
updatedAt: new Date().toISOString(),
|
|
11
|
-
},
|
|
12
|
-
{
|
|
13
|
-
id: 'mock-assistant-id-2',
|
|
14
|
-
name: 'Mock Assistant 2',
|
|
15
|
-
model: 'claude-3-opus',
|
|
16
|
-
instructions: 'Another example',
|
|
17
|
-
createdAt: new Date().toISOString(),
|
|
18
|
-
updatedAt: new Date().toISOString(),
|
|
19
|
-
},
|
|
20
|
-
]),
|
|
21
|
-
get: jest.fn().mockImplementation((id) => {
|
|
22
|
-
return Promise.resolve({
|
|
23
|
-
id,
|
|
24
|
-
name: `Mock Assistant ${id}`,
|
|
25
|
-
model: 'gpt-4',
|
|
26
|
-
instructions: 'Example instructions',
|
|
27
|
-
createdAt: new Date().toISOString(),
|
|
28
|
-
updatedAt: new Date().toISOString(),
|
|
29
|
-
});
|
|
30
|
-
}),
|
|
31
|
-
create: jest.fn().mockImplementation((data) => {
|
|
32
|
-
return Promise.resolve({
|
|
33
|
-
id: 'new-mock-assistant-id',
|
|
34
|
-
...data,
|
|
35
|
-
createdAt: new Date().toISOString(),
|
|
36
|
-
updatedAt: new Date().toISOString(),
|
|
37
|
-
});
|
|
38
|
-
}),
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
export const createMockVapiClient = () => {
|
|
42
|
-
return new MockVapiClient();
|
|
43
|
-
};
|