@vapi-ai/mcp-server 0.1.0 → 0.2.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/README.md CHANGED
@@ -1,96 +1,46 @@
1
1
  # Vapi MCP Server
2
2
 
3
- Build AI voice assistants and phone agents with [Vapi](https://vapi.ai) using the [Model Context Protocol](https://modelcontextprotocol.com/).
3
+ Build AI voice assistants and phone agents with [Vapi](https://vapi.ai) from any agent or app that speaks the [Model Context Protocol](https://modelcontextprotocol.com/).
4
4
 
5
- ## Setup
5
+ ## Connect to the hosted server
6
6
 
7
- The MCP server requires a Vapi API key. Get one from the [Vapi dashboard](https://dashboard.vapi.ai/org/api-keys).
7
+ **You don't need to clone, install or run anything.** Vapi hosts this server for you:
8
8
 
9
- ### Local Server (stdio)
9
+ - **URL:** `https://mcp.vapi.ai/mcp` (Streamable HTTP)
10
+ - **Header:** `Authorization: Bearer YOUR_VAPI_API_KEY`
10
11
 
11
- Configure any MCP client that supports local stdio servers to run:
12
+ Get an API key from the [Vapi dashboard](https://dashboard.vapi.ai/org/api-keys).
12
13
 
13
- ```text
14
- npx -y @vapi-ai/mcp-server
15
- ```
14
+ 👉 **[Step-by-step setup for Codex, Claude Code, Cursor, VS Code, Hermes and Claude Desktop](https://docs.vapi.ai/sdk/mcp-server#connect-your-agent)**
16
15
 
17
- Set `VAPI_TOKEN` in the server environment. MCP client configuration formats vary, but the server definition generally looks like this:
16
+ The two most common setups:
18
17
 
19
- ```json
20
- {
21
- "mcpServers": {
22
- "vapi": {
23
- "command": "npx",
24
- "args": ["-y", "@vapi-ai/mcp-server"],
25
- "env": {
26
- "VAPI_TOKEN": "<your_vapi_token>"
27
- }
28
- }
29
- }
30
- }
31
- ```
32
-
33
- ### Remote Server (Streamable HTTP)
34
-
35
- Clients that support remote MCP servers can connect directly:
36
-
37
- - URL: `https://mcp.vapi.ai/mcp`
38
- - Header: `Authorization: Bearer your_vapi_api_key_here`
39
-
40
- ### Client-Specific Examples
41
-
42
- #### Claude Code
18
+ **Claude Code**
43
19
 
44
20
  ```bash
45
- claude mcp add -e VAPI_TOKEN=your_vapi_token vapi -- npx -y @vapi-ai/mcp-server
46
- ```
47
-
48
- #### Claude Desktop
49
-
50
- Use the local server configuration above in the Claude Desktop configuration file. To connect to the hosted server through an stdio bridge instead:
51
-
52
- ```json
53
- {
54
- "mcpServers": {
55
- "vapi": {
56
- "command": "npx",
57
- "args": [
58
- "mcp-remote",
59
- "https://mcp.vapi.ai/mcp",
60
- "--header",
61
- "Authorization: Bearer ${VAPI_TOKEN}"
62
- ],
63
- "env": {
64
- "VAPI_TOKEN": "<your_vapi_token>"
65
- }
66
- }
67
- }
68
- }
21
+ export VAPI_API_KEY="YOUR_VAPI_API_KEY"
22
+ claude mcp add --transport http --scope user vapi \
23
+ https://mcp.vapi.ai/mcp \
24
+ --header 'Authorization: Bearer ${VAPI_API_KEY}'
69
25
  ```
70
26
 
71
- ### Optional Agent Skill
72
-
73
- The [`skill`](./skill) directory contains reusable instructions for AI coding agents that support Agent Skills. Install it using your host's skill installation process.
74
-
75
- For Claude Code:
27
+ **Codex**
76
28
 
77
29
  ```bash
78
- mkdir -p ~/.claude/skills/vapi
79
- curl -o ~/.claude/skills/vapi/SKILL.md https://raw.githubusercontent.com/VapiAI/mcp-server/main/skill/SKILL.md
30
+ export VAPI_API_KEY="YOUR_VAPI_API_KEY"
31
+ codex mcp add vapi --url https://mcp.vapi.ai/mcp \
32
+ --bearer-token-env-var VAPI_API_KEY
80
33
  ```
81
34
 
82
- ---
83
-
84
- ## Example Usage
85
-
86
- ### Create a Voice Assistant
35
+ Keep `VAPI_API_KEY` set whenever you launch your agent. Start a new session, then try:
87
36
 
88
- Ask your MCP-enabled agent:
89
37
  ```
90
- I want to build a voice assistant that can schedule appointments
38
+ List my Vapi assistants.
91
39
  ```
92
40
 
93
- ### Make an Outbound Call
41
+ ```
42
+ I want to build a voice assistant that can schedule appointments.
43
+ ```
94
44
 
95
45
  ```
96
46
  Call +1234567890 using my appointment reminder assistant with these details:
@@ -99,25 +49,34 @@ Call +1234567890 using my appointment reminder assistant with these details:
99
49
  - Appointment time: 2:30 PM
100
50
  ```
101
51
 
102
- ### Schedule a Future Call
103
-
104
52
  ```
105
53
  Schedule a call with my support assistant for next Tuesday at 3:00 PM to +1555123456
106
54
  ```
107
55
 
108
- ---
56
+ > The legacy SSE endpoint (`https://mcp.vapi.ai/sse`) is deprecated. Move existing remote configurations to `https://mcp.vapi.ai/mcp`.
57
+
58
+ ### Optional: add the Vapi agent skill
59
+
60
+ The [`skill`](./skill) directory contains reusable instructions for AI coding agents that support Agent Skills. Install it using your host's skill installation process.
109
61
 
110
- ## Using Variable Values in Assistant Prompts
62
+ For Claude Code:
63
+
64
+ ```bash
65
+ mkdir -p ~/.claude/skills/vapi
66
+ curl -o ~/.claude/skills/vapi/SKILL.md https://raw.githubusercontent.com/VapiAI/mcp-server/main/skill/SKILL.md
67
+ ```
68
+
69
+ ### Using Variable Values in Assistant Prompts
111
70
 
112
71
  The `create_call` action supports passing dynamic variables through `assistantOverrides.variableValues`. Use double curly braces in your assistant's prompts: `{{variableName}}`.
113
72
 
114
- ### Example Prompt with Variables
73
+ #### Example Prompt with Variables
115
74
 
116
75
  ```
117
76
  Hello {{customerName}}, this is a reminder about your appointment on {{appointmentDate}} at {{appointmentTime}} with {{doctorName}}.
118
77
  ```
119
78
 
120
- ### Default Variables
79
+ #### Default Variables
121
80
 
122
81
  These are automatically available (no need to pass):
123
82
 
@@ -133,25 +92,59 @@ See [Vapi documentation](https://docs.vapi.ai/assistants/dynamic-variables#defau
133
92
 
134
93
  ---
135
94
 
136
- ## Remote MCP Server
95
+ ## Oh, you're still here?
96
+
97
+ You must want to [leave feedback](#leave-feedback), [run a local server yourself](#run-a-local-server) or [peek under the hood](#peek-under-the-hood). Read on.
137
98
 
138
- Connect to Vapi's hosted MCP server from any MCP client:
99
+ ### Leave feedback
139
100
 
140
- ### Streamable HTTP (Recommended)
101
+ - **Found a bug or want a tool we don't have?** [Open an issue](https://github.com/VapiAI/mcp-server/issues).
102
+ - **Have a question or want to talk it through?** Join the [Vapi Discord](https://discord.gg/v5Ee6FhAZt).
103
+ - **Want to fix it yourself?** Pull requests are welcome. See [Development](#development) for how to build and test.
141
104
 
142
- - URL: `https://mcp.vapi.ai/mcp`
143
- - Header: `Authorization: Bearer your_vapi_api_key_here`
105
+ ### Run a local server
144
106
 
145
- ### SSE (Deprecated)
107
+ Run the server on your own machine if your client only supports local stdio servers or you want to try unreleased changes. It uses the same tools as the hosted server.
146
108
 
147
- - URL: `https://mcp.vapi.ai/sse`
148
- - Header: `Authorization: Bearer your_vapi_api_key_here`
109
+ Configure your MCP client to run:
110
+
111
+ ```text
112
+ npx -y @vapi-ai/mcp-server
113
+ ```
114
+
115
+ Set `VAPI_TOKEN` to your Vapi API key in the server environment. MCP client configuration formats vary, but the server definition generally looks like this:
116
+
117
+ ```json
118
+ {
119
+ "mcpServers": {
120
+ "vapi": {
121
+ "command": "npx",
122
+ "args": ["-y", "@vapi-ai/mcp-server"],
123
+ "env": {
124
+ "VAPI_TOKEN": "<your_vapi_token>"
125
+ }
126
+ }
127
+ }
128
+ }
129
+ ```
130
+
131
+ For Claude Code:
132
+
133
+ ```bash
134
+ claude mcp add -e VAPI_TOKEN=your_vapi_token vapi -- npx -y @vapi-ai/mcp-server
135
+ ```
136
+
137
+ For Claude Desktop, add the configuration above to the Claude Desktop configuration file.
138
+
139
+ To run a build from this repository instead, see [Local Development Config](#local-development-config).
149
140
 
150
141
  ---
151
142
 
152
- ## Available Tools
143
+ ## Peek under the hood
144
+
145
+ ### Available Tools
153
146
 
154
- ### Documentation
147
+ #### Documentation
155
148
  | Tool | Description |
156
149
  |------|-------------|
157
150
  | `ask_vapi_docs` | Query the Fern docs MCP for current documentation passages and source URLs |
@@ -169,36 +162,36 @@ at startup. Discovery through `tools/list` makes no network requests; upstream
169
162
  failures are returned as tool errors. No extra MCP connection is needed. Do not
170
163
  include secrets or private customer data in docs questions.
171
164
 
172
- ### Assistants
165
+ #### Assistants
173
166
  | Tool | Description |
174
167
  |------|-------------|
175
- | `list_assistants` | List all assistants |
168
+ | `list_assistants` | List assistants in pages of up to 10 |
176
169
  | `get_assistant` | Get assistant by ID |
177
170
  | `create_assistant` | Create new assistant |
178
171
  | `update_assistant` | Update assistant |
179
172
 
180
- ### Calls
173
+ #### Calls
181
174
  | Tool | Description |
182
175
  |------|-------------|
183
- | `list_calls` | List call history |
176
+ | `list_calls` | List call history in pages of up to 10 |
184
177
  | `get_call` | Get call details |
185
178
  | `create_call` | Start outbound call (immediate or scheduled) |
186
179
 
187
- ### Phone Numbers
180
+ #### Phone Numbers
188
181
  | Tool | Description |
189
182
  |------|-------------|
190
- | `list_phone_numbers` | List phone numbers |
183
+ | `list_phone_numbers` | List phone numbers in pages of up to 10 |
191
184
  | `get_phone_number` | Get phone number details |
192
185
 
193
- ### Tools (Function Calling)
186
+ #### Tools (Function Calling)
194
187
  | Tool | Description |
195
188
  |------|-------------|
196
- | `list_tools` | List custom tools |
189
+ | `list_tools` | List custom tools in pages of up to 10 |
197
190
  | `get_tool` | Get tool details |
198
191
  | `create_tool` | Create tool for API integration |
199
192
  | `update_tool` | Update tool |
200
193
 
201
- ### Public Skills
194
+ #### Public Skills
202
195
  | Tool | Description |
203
196
  |------|-------------|
204
197
  | `search_skills` | Search public Vapi skills by keywords; omit `query` to browse |
@@ -224,7 +217,7 @@ calls and send no Vapi credentials upstream. The server still requires
224
217
  upstream failures are returned as tool errors. No GitHub token is needed for this
225
218
  public content. GitHub's unauthenticated API rate limits apply.
226
219
 
227
- ### Simulations
220
+ #### Simulations
228
221
 
229
222
  | Tool | Description |
230
223
  |------|-------------|
@@ -247,9 +240,7 @@ metadata in `itemsMetadata` and `itemsTruncated: true` when more items exist.
247
240
  Legacy array responses are also supported. Use `get_simulation_run_item` for
248
241
  the full details of a known item.
249
242
 
250
- ---
251
-
252
- ## Development
243
+ ### Development
253
244
 
254
245
  ```bash
255
246
  # Install dependencies
@@ -262,7 +253,7 @@ npm run build
262
253
  npm run inspector
263
254
  ```
264
255
 
265
- ### Local Development Config
256
+ #### Local Development Config
266
257
 
267
258
  ```json
268
259
  {
@@ -278,7 +269,7 @@ npm run inspector
278
269
  }
279
270
  ```
280
271
 
281
- ### Testing
272
+ #### Testing
282
273
 
283
274
  ```bash
284
275
  # Safe default suite (build, mocked execution, contract, and stdio compatibility)
@@ -308,7 +299,7 @@ export VAPI_TOKEN=your_test_org_token_here
308
299
  npm run test:live:mutating
309
300
  ```
310
301
 
311
- ### Input validation from OpenAPI
302
+ #### Input validation from OpenAPI
312
303
 
313
304
  All 21 parameterized MCP tools use imported OpenAPI constraints through
314
305
  Zod's experimental `z.fromJSONSchema()`, pinned to Zod 4.6.7. This covers
@@ -1,6 +1,9 @@
1
1
  import { z } from 'zod';
2
2
  import { VoiceInputSchema } from './assistant-openapi.js';
3
3
  export declare const EmptyInputSchema: z.ZodObject<{}, z.core.$strip>;
4
+ export declare const ListInputSchema: z.ZodObject<{
5
+ before: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodISODateTime>>;
6
+ }, z.core.$strict>;
4
7
  export declare const ModelProvider: {
5
8
  readonly OpenAI: "openai";
6
9
  readonly Anthropic: "anthropic";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schemas/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAwD,gBAAgB,EAAsH,MAAM,wBAAwB,CAAC;AAEpO,eAAO,MAAM,gBAAgB,gCAAe,CAAC;AAI7C,eAAO,MAAM,aAAa;;;;CAIhB,CAAC;AAEX,eAAO,MAAM,YAAY;;;CAGf,CAAC;AAEX,eAAO,MAAM,eAAe;;;CAGlB,CAAC;AAEX,eAAO,MAAM,YAAY;;;;;CAKf,CAAC;AAEX,MAAM,MAAM,iBAAiB,GAC3B,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,OAAO,aAAa,CAAC,CAAC;AACrD,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,OAAO,YAAY,CAAC,CAAC;AAC/E,MAAM,MAAM,kBAAkB,GAC5B,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,OAAO,eAAe,CAAC,CAAC;AACzD,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,OAAO,YAAY,CAAC,CAAC;AAa/E,eAAO,MAAM,WAAW;;;CAGvB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC,UAAU,CAAC,CAAC;AAE7E,eAAO,MAAM,aAAa;cACF,iBAAiB;;CAExC,CAAC;AAEF,eAAO,MAAM,mBAAmB;;;CAG/B,CAAC;AAEF,eAAO,MAAM,kBAAkB;;;;;;CAMrB,CAAC;AAIX,eAAO,MAAM,kBAAkB;;;;iBAI7B,CAAC;AAIH,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA2BrC,CAAC;AAEH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;iBAgBhC,CAAC;AAEH,eAAO,MAAM,uBAAuB;;iBAElC,CAAC;AAEH,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAwBrC,CAAC;AAIH,eAAO,MAAM,eAAe;;;;;;;;;;iBA4B1B,CAAC;AAEH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;iBAW3B,CAAC;AAEH,eAAO,MAAM,kBAAkB;;iBAE7B,CAAC;AAIH,eAAO,MAAM,yBAAyB;;iBAEpC,CAAC;AAEH,eAAO,MAAM,uBAAuB;;;;;;;;;;;iBAUlC,CAAC;AAIH,eAAO,MAAM,kBAAkB;;iBAE7B,CAAC;AAqEH,eAAO,MAAM,qBAAqB;;;;;;;;;;kBAnEqB,QAAQ;oBAAU,MAAM;wBAAc,MAAM;uBAAa,MAAM;0BAAgB,MAAM;;kBAArF,QAAQ;oBAAU,MAAM;wBAAc,MAAM;uBAAa,MAAM;0BAAgB,MAAM;;;;;;;;;;;;;;;;;;;;;;;;iBAgFxI,CAAC;AAEL,eAAO,MAAM,qBAAqB;;;;;;;;;;kBAlFqB,QAAQ;oBAAU,MAAM;wBAAc,MAAM;uBAAa,MAAM;0BAAgB,MAAM;;kBAArF,QAAQ;oBAAU,MAAM;wBAAc,MAAM;uBAAa,MAAM;0BAAgB,MAAM;;;;;;;;;;;;;;;;;;;;;;;;iBAoF1I,CAAC;AAEH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;iBAQ3B,CAAC;AAIH,eAAO,MAAM,uBAAuB;;;iBAalC,CAAC;AAEH,cAAc,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schemas/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAwD,gBAAgB,EAAsH,MAAM,wBAAwB,CAAC;AAEpO,eAAO,MAAM,gBAAgB,gCAAe,CAAC;AAE7C,eAAO,MAAM,eAAe;;kBAK1B,CAAC;AAIH,eAAO,MAAM,aAAa;;;;CAIhB,CAAC;AAEX,eAAO,MAAM,YAAY;;;CAGf,CAAC;AAEX,eAAO,MAAM,eAAe;;;CAGlB,CAAC;AAEX,eAAO,MAAM,YAAY;;;;;CAKf,CAAC;AAEX,MAAM,MAAM,iBAAiB,GAC3B,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,OAAO,aAAa,CAAC,CAAC;AACrD,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,OAAO,YAAY,CAAC,CAAC;AAC/E,MAAM,MAAM,kBAAkB,GAC5B,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,OAAO,eAAe,CAAC,CAAC;AACzD,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,OAAO,YAAY,CAAC,CAAC;AAa/E,eAAO,MAAM,WAAW;;;CAGvB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC,UAAU,CAAC,CAAC;AAE7E,eAAO,MAAM,aAAa;cACF,iBAAiB;;CAExC,CAAC;AAEF,eAAO,MAAM,mBAAmB;;;CAG/B,CAAC;AAEF,eAAO,MAAM,kBAAkB;;;;;;CAMrB,CAAC;AAIX,eAAO,MAAM,kBAAkB;;;;iBAI7B,CAAC;AAIH,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA2BrC,CAAC;AAEH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;iBAgBhC,CAAC;AAEH,eAAO,MAAM,uBAAuB;;iBAElC,CAAC;AAEH,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAwBrC,CAAC;AAIH,eAAO,MAAM,eAAe;;;;;;;;;;iBA4B1B,CAAC;AAEH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;iBAW3B,CAAC;AAEH,eAAO,MAAM,kBAAkB;;iBAE7B,CAAC;AAIH,eAAO,MAAM,yBAAyB;;iBAEpC,CAAC;AAEH,eAAO,MAAM,uBAAuB;;;;;;;;;;;iBAUlC,CAAC;AAIH,eAAO,MAAM,kBAAkB;;iBAE7B,CAAC;AAqEH,eAAO,MAAM,qBAAqB;;;;;;;;;;kBAnEqB,QAAQ;oBAAU,MAAM;wBAAc,MAAM;uBAAa,MAAM;0BAAgB,MAAM;;kBAArF,QAAQ;oBAAU,MAAM;wBAAc,MAAM;uBAAa,MAAM;0BAAgB,MAAM;;;;;;;;;;;;;;;;;;;;;;;;iBAgFxI,CAAC;AAEL,eAAO,MAAM,qBAAqB;;;;;;;;;;kBAlFqB,QAAQ;oBAAU,MAAM;wBAAc,MAAM;uBAAa,MAAM;0BAAgB,MAAM;;kBAArF,QAAQ;oBAAU,MAAM;wBAAc,MAAM;uBAAa,MAAM;0BAAgB,MAAM;;;;;;;;;;;;;;;;;;;;;;;;iBAoF1I,CAAC;AAEH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;iBAQ3B,CAAC;AAIH,eAAO,MAAM,uBAAuB;;;iBAalC,CAAC;AAEH,cAAc,iBAAiB,CAAC"}
@@ -1,6 +1,10 @@
1
1
  import { z } from 'zod';
2
2
  import { CreateAssistantNameSchema, UpdateAssistantNameSchema, VoiceInputSchema, TranscriberInputSchema, LLMInputSchema, apiField, apiObjectField, requiredToolConfigurations, functionRequiresName } from './assistant-openapi.js';
3
3
  export const EmptyInputSchema = z.object({});
4
+ export const ListInputSchema = z.strictObject({
5
+ // Keep discovery compact while validating the full ISO timestamp at runtime.
6
+ before: z.string().meta({ format: 'date-time' }).pipe(z.iso.datetime({ offset: true })).optional().describe('Use pagination.nextBefore to continue.'),
7
+ });
4
8
  // ===== Model Provider and Models =====
5
9
  export const ModelProvider = {
6
10
  OpenAI: 'openai',
@@ -0,0 +1,5 @@
1
+ import type { ToolAnnotations } from '@modelcontextprotocol/sdk/types.js';
2
+ export declare const readOnly: (title: string, openWorldHint?: boolean) => ToolAnnotations;
3
+ export declare const creates: (title: string, openWorldHint?: boolean) => ToolAnnotations;
4
+ export declare const updates: (title: string) => ToolAnnotations;
5
+ //# sourceMappingURL=annotations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"annotations.d.ts","sourceRoot":"","sources":["../../src/tools/annotations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAC;AAO1E,eAAO,MAAM,QAAQ,GAAI,OAAO,MAAM,EAAE,uBAAqB,KAAG,eAE9D,CAAC;AAEH,eAAO,MAAM,OAAO,GAAI,OAAO,MAAM,EAAE,uBAAqB,KAAG,eAE7D,CAAC;AAGH,eAAO,MAAM,OAAO,GAAI,OAAO,MAAM,KAAG,eAEtC,CAAC"}
@@ -0,0 +1,14 @@
1
+ // MCP tool annotations. Clients use these to label tools and decide when to ask
2
+ // for confirmation, and directory review requires a title and readOnlyHint.
3
+ // openWorldHint is false for calls into the Vapi API itself (a closed domain)
4
+ // and true when a tool reaches outside it, such as dialing a phone number.
5
+ export const readOnly = (title, openWorldHint = false) => ({
6
+ title, readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint,
7
+ });
8
+ export const creates = (title, openWorldHint = false) => ({
9
+ title, readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint,
10
+ });
11
+ // Updates overwrite fields on an existing resource, so clients should confirm them.
12
+ export const updates = (title) => ({
13
+ title, readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false,
14
+ });
@@ -1 +1 @@
1
- {"version":3,"file":"api-schema.d.ts","sourceRoot":"","sources":["../../src/tools/api-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,UAAU,EAAmB,MAAM,qBAAqB,CAAC;AAkElE,eAAO,MAAM,sBAAsB,GACjC,QAAQ,SAAS,EACjB,YAAY,UAAU,SAuCvB,CAAC"}
1
+ {"version":3,"file":"api-schema.d.ts","sourceRoot":"","sources":["../../src/tools/api-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,UAAU,EAAmB,MAAM,qBAAqB,CAAC;AAmElE,eAAO,MAAM,sBAAsB,GACjC,QAAQ,SAAS,EACjB,YAAY,UAAU,SAwCvB,CAAC"}
@@ -1,6 +1,7 @@
1
1
  import { VapiEnvironment } from '@vapi-ai/server-sdk';
2
2
  import { GetApiSchemaInputSchema } from '../schemas/index.js';
3
3
  import { createToolHandler } from './utils.js';
4
+ import { readOnly } from './annotations.js';
4
5
  // The OpenAPI spec is about 2 MB, so load it once per client and keep only
5
6
  // its schemas. It is fetched through the client so it comes from the same
6
7
  // API environment the other tools call.
@@ -47,6 +48,7 @@ export const registerApiSchemaTools = (server, vapiClient) => {
47
48
  'References to other schemas are left as "#/components/schemas/<Name>" and listed in referencedSchemas, so look those up next if you need them. ' +
48
49
  'Use it to find fields that other tools accept through additionalFields.',
49
50
  inputSchema: GetApiSchemaInputSchema.shape,
51
+ annotations: readOnly('Get API Schema'),
50
52
  }, createToolHandler(async (data) => {
51
53
  const schemas = await loadSchemas(vapiClient);
52
54
  if (data.name) {
@@ -1 +1 @@
1
- {"version":3,"file":"assistant.d.ts","sourceRoot":"","sources":["../../src/tools/assistant.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,UAAU,EAAQ,MAAM,qBAAqB,CAAC;AAcvD,eAAO,MAAM,sBAAsB,GACjC,QAAQ,SAAS,EACjB,YAAY,UAAU,SA4FvB,CAAC"}
1
+ {"version":3,"file":"assistant.d.ts","sourceRoot":"","sources":["../../src/tools/assistant.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,UAAU,EAAQ,MAAM,qBAAqB,CAAC;AAevD,eAAO,MAAM,sBAAsB,GACjC,QAAQ,SAAS,EACjB,YAAY,UAAU,SA2FvB,CAAC"}
@@ -1,20 +1,19 @@
1
1
  import { registerValidatedTool } from './validation.js';
2
- import { EmptyInputSchema, CreateAssistantInputSchema, GetAssistantInputSchema, UpdateAssistantInputSchema, } from '../schemas/index.js';
2
+ import { creates, readOnly, updates } from './annotations.js';
3
+ import { ListInputSchema, CreateAssistantInputSchema, GetAssistantInputSchema, UpdateAssistantInputSchema, } from '../schemas/index.js';
3
4
  import { transformAssistantInput, transformAssistantOutput, transformUpdateAssistantInput, } from '../transformers/index.js';
4
5
  import { createToolHandler } from './utils.js';
6
+ import { createV1ListHandler } from './list.js';
5
7
  export const registerAssistantTools = (server, vapiClient) => {
6
8
  registerValidatedTool(server, 'list_assistants', {
7
- description: 'Lists all Vapi assistants',
8
- inputSchema: EmptyInputSchema,
9
- }, createToolHandler(async () => {
10
- // console.log('list_assistants');
11
- const assistants = await vapiClient.assistants.list({ limit: 10 });
12
- // console.log('assistants', assistants);
13
- return assistants.map(transformAssistantOutput);
14
- }));
9
+ description: 'Lists up to 10 Vapi assistants',
10
+ inputSchema: ListInputSchema,
11
+ annotations: readOnly('List Assistants'),
12
+ }, createV1ListHandler(request => vapiClient.assistants.list(request), transformAssistantOutput));
15
13
  registerValidatedTool(server, 'create_assistant', {
16
14
  description: 'Creates a new Vapi assistant',
17
15
  inputSchema: CreateAssistantInputSchema,
16
+ annotations: creates('Create Assistant'),
18
17
  }, createToolHandler(async (data) => {
19
18
  // console.log('create_assistant', data);
20
19
  const createAssistantDto = transformAssistantInput(data);
@@ -24,6 +23,7 @@ export const registerAssistantTools = (server, vapiClient) => {
24
23
  registerValidatedTool(server, 'get_assistant', {
25
24
  description: 'Gets a Vapi assistant by ID',
26
25
  inputSchema: GetAssistantInputSchema,
26
+ annotations: readOnly('Get Assistant'),
27
27
  }, createToolHandler(async (data) => {
28
28
  // console.log('get_assistant', data);
29
29
  const assistantId = data.assistantId;
@@ -42,6 +42,7 @@ export const registerAssistantTools = (server, vapiClient) => {
42
42
  registerValidatedTool(server, 'update_assistant', {
43
43
  description: 'Updates an existing Vapi assistant',
44
44
  inputSchema: UpdateAssistantInputSchema,
45
+ annotations: updates('Update Assistant'),
45
46
  }, createToolHandler(async (data) => {
46
47
  const assistantId = data.assistantId;
47
48
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"call.d.ts","sourceRoot":"","sources":["../../src/tools/call.ts"],"names":[],"mappings":"AACA,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,SAyCvB,CAAC"}
1
+ {"version":3,"file":"call.d.ts","sourceRoot":"","sources":["../../src/tools/call.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,UAAU,EAAQ,MAAM,qBAAqB,CAAC;AAUvD,eAAO,MAAM,iBAAiB,GAC5B,QAAQ,SAAS,EACjB,YAAY,UAAU,SAyCvB,CAAC"}
@@ -1,18 +1,19 @@
1
1
  import { registerValidatedTool } from './validation.js';
2
- import { EmptyInputSchema, CallInputSchema, GetCallInputSchema } from '../schemas/index.js';
2
+ import { creates, readOnly } from './annotations.js';
3
+ import { ListInputSchema, CallInputSchema, GetCallInputSchema } from '../schemas/index.js';
3
4
  import { transformCallInput, transformCallOutput, } from '../transformers/index.js';
4
5
  import { createToolHandler } from './utils.js';
6
+ import { createV1ListHandler } from './list.js';
5
7
  export const registerCallTools = (server, vapiClient) => {
6
8
  registerValidatedTool(server, 'list_calls', {
7
- description: 'Lists all Vapi calls',
8
- inputSchema: EmptyInputSchema,
9
- }, createToolHandler(async () => {
10
- const calls = await vapiClient.calls.list({ limit: 10 });
11
- return calls.map(transformCallOutput);
12
- }));
9
+ description: 'Lists up to 10 Vapi calls',
10
+ inputSchema: ListInputSchema,
11
+ annotations: readOnly('List Calls'),
12
+ }, createV1ListHandler(request => vapiClient.calls.list(request), transformCallOutput));
13
13
  registerValidatedTool(server, 'create_call', {
14
14
  description: 'Creates a outbound call',
15
15
  inputSchema: CallInputSchema,
16
+ annotations: creates('Create Outbound Call', true),
16
17
  }, createToolHandler(async (data) => {
17
18
  const createCallDto = transformCallInput(data);
18
19
  const call = await vapiClient.calls.create(createCallDto);
@@ -21,6 +22,7 @@ export const registerCallTools = (server, vapiClient) => {
21
22
  registerValidatedTool(server, 'get_call', {
22
23
  description: 'Gets details of a specific call',
23
24
  inputSchema: GetCallInputSchema,
25
+ annotations: readOnly('Get Call'),
24
26
  }, createToolHandler(async (data) => {
25
27
  const call = await vapiClient.calls.get({ id: data.callId });
26
28
  return transformCallOutput(call);
@@ -1 +1 @@
1
- {"version":3,"file":"docs.d.ts","sourceRoot":"","sources":["../../src/tools/docs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAazE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,QAelD"}
1
+ {"version":3,"file":"docs.d.ts","sourceRoot":"","sources":["../../src/tools/docs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAczE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,QAclD"}
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import { askVapiDocs } from '../knowledge/docs.js';
3
+ import { readOnly } from './annotations.js';
3
4
  async function knowledgeResult(action) {
4
5
  try {
5
6
  return await action();
@@ -9,13 +10,12 @@ async function knowledgeResult(action) {
9
10
  }
10
11
  }
11
12
  export function registerDocsTools(server) {
12
- const annotations = { readOnlyHint: true, destructiveHint: false, openWorldHint: true };
13
13
  server.registerTool('ask_vapi_docs', {
14
14
  description: 'Ask about current Vapi documentation through the Fern docs MCP. Returns relevant documentation passages and source URLs; use them to answer the question.',
15
15
  inputSchema: {
16
16
  question: z.string().trim().min(1).max(4000).describe('A specific question about Vapi configuration, APIs, SDKs or troubleshooting. Send no API keys or private customer data.'),
17
17
  topK: z.number().int().min(1).max(20).default(8).describe('Maximum number of relevant documentation passages.'),
18
- }, annotations,
18
+ }, annotations: readOnly('Ask Vapi Docs', true),
19
19
  }, ({ question, topK }, extra) => knowledgeResult(async () => {
20
20
  const result = await askVapiDocs(question, topK, extra.signal);
21
21
  // The upstream SDK also types legacy toolResult responses. Fern uses the
@@ -0,0 +1,34 @@
1
+ export declare function v1ListPage<T extends {
2
+ createdAt: string;
3
+ }>(rows: T[]): {
4
+ results: T[];
5
+ pagination: {
6
+ warning?: string | undefined;
7
+ pageSize: number;
8
+ hasMore: boolean;
9
+ nextBefore: string | null;
10
+ };
11
+ };
12
+ export declare function createV1ListHandler<T extends {
13
+ createdAt: string;
14
+ }, R>(list: (request: {
15
+ limit: number;
16
+ createdAtLt?: string;
17
+ }) => Promise<T[]>, transform: (row: T) => R): (data: {
18
+ before?: string;
19
+ }) => Promise<import("./utils.js").ToolResponse | {
20
+ content: {
21
+ type: "text";
22
+ text: string;
23
+ }[];
24
+ structuredContent: {
25
+ results: R[];
26
+ pagination: {
27
+ warning?: string | undefined;
28
+ pageSize: number;
29
+ hasMore: boolean;
30
+ nextBefore: string | null;
31
+ };
32
+ };
33
+ }>;
34
+ //# sourceMappingURL=list.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../src/tools/list.ts"],"names":[],"mappings":"AAKA,wBAAgB,UAAU,CAAC,CAAC,SAAS;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,EAAE,IAAI,EAAE,CAAC,EAAE;;;;;;;;EAoBpE;AAED,wBAAgB,mBAAmB,CAAC,CAAC,SAAS;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,EAAE,CAAC,EACpE,IAAI,EAAE,CAAC,OAAO,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,KAAK,OAAO,CAAC,CAAC,EAAE,CAAC,EACxE,SAAS,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,IAEV,MAAM;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE;;;;;;;;;;;;;;GAoBxC"}
@@ -0,0 +1,47 @@
1
+ import { createErrorResponse, createSuccessResponse } from './utils.js';
2
+ const PAGE_SIZE = 10;
3
+ const TIE_WARNING = 'V1 cannot safely continue: more than 10 items share the same creation timestamp. No continuation is provided because it would skip items.';
4
+ export function v1ListPage(rows) {
5
+ let end = Math.min(rows.length, PAGE_SIZE);
6
+ const hasMore = rows.length > PAGE_SIZE;
7
+ // V1 sorts only by creation time. Keep a timestamp group on the same page,
8
+ // using one bounded lookahead row to avoid skipping ties at the boundary.
9
+ if (hasMore) {
10
+ while (end > 0 && rows[end - 1].createdAt === rows[PAGE_SIZE].createdAt)
11
+ end--;
12
+ }
13
+ const blocked = hasMore && end === 0;
14
+ const results = rows.slice(0, blocked ? PAGE_SIZE : end);
15
+ return {
16
+ results,
17
+ pagination: {
18
+ pageSize: PAGE_SIZE,
19
+ hasMore,
20
+ nextBefore: hasMore && !blocked ? results[results.length - 1].createdAt : null,
21
+ ...(blocked ? { warning: TIE_WARNING } : {}),
22
+ },
23
+ };
24
+ }
25
+ export function createV1ListHandler(list, transform) {
26
+ return async (data) => {
27
+ try {
28
+ const page = v1ListPage(await list({
29
+ limit: PAGE_SIZE + 1,
30
+ ...(data.before ? { createdAtLt: data.before } : {}),
31
+ }));
32
+ const results = page.results.map(transform);
33
+ const response = createSuccessResponse(results);
34
+ return {
35
+ // Preserve the original array in the first text block for existing clients.
36
+ content: [...response.content, {
37
+ type: 'text',
38
+ text: JSON.stringify({ pagination: page.pagination }),
39
+ }],
40
+ structuredContent: { results, pagination: page.pagination },
41
+ };
42
+ }
43
+ catch (error) {
44
+ return createErrorResponse(error);
45
+ }
46
+ };
47
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"phone-number.d.ts","sourceRoot":"","sources":["../../src/tools/phone-number.ts"],"names":[],"mappings":"AACA,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,SA8BvB,CAAC"}
1
+ {"version":3,"file":"phone-number.d.ts","sourceRoot":"","sources":["../../src/tools/phone-number.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAOjD,eAAO,MAAM,wBAAwB,GACnC,QAAQ,SAAS,EACjB,YAAY,UAAU,SA6BvB,CAAC"}
@@ -1,18 +1,19 @@
1
1
  import { registerValidatedTool } from './validation.js';
2
+ import { readOnly } from './annotations.js';
2
3
  import { transformPhoneNumberOutput } from '../transformers/index.js';
3
4
  import { createToolHandler } from './utils.js';
4
- import { EmptyInputSchema, GetPhoneNumberInputSchema } from '../schemas/index.js';
5
+ import { ListInputSchema, GetPhoneNumberInputSchema } from '../schemas/index.js';
6
+ import { createV1ListHandler } from './list.js';
5
7
  export const registerPhoneNumberTools = (server, vapiClient) => {
6
8
  registerValidatedTool(server, 'list_phone_numbers', {
7
- description: 'Lists all Vapi phone numbers',
8
- inputSchema: EmptyInputSchema,
9
- }, createToolHandler(async () => {
10
- const phoneNumbers = await vapiClient.phoneNumbers.list({ limit: 10 });
11
- return phoneNumbers.map(transformPhoneNumberOutput);
12
- }));
9
+ description: 'Lists up to 10 Vapi phone numbers',
10
+ inputSchema: ListInputSchema,
11
+ annotations: readOnly('List Phone Numbers'),
12
+ }, createV1ListHandler(request => vapiClient.phoneNumbers.list(request), transformPhoneNumberOutput));
13
13
  registerValidatedTool(server, 'get_phone_number', {
14
14
  description: 'Gets details of a specific phone number',
15
15
  inputSchema: GetPhoneNumberInputSchema,
16
+ annotations: readOnly('Get Phone Number'),
16
17
  }, createToolHandler(async (data) => {
17
18
  const phoneNumberId = data.phoneNumberId;
18
19
  const phoneNumber = await vapiClient.phoneNumbers.get({
@@ -1 +1 @@
1
- {"version":3,"file":"simulation.d.ts","sourceRoot":"","sources":["../../src/tools/simulation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,UAAU,EAAQ,MAAM,qBAAqB,CAAC;AA2CvD,eAAO,MAAM,uBAAuB,GAClC,QAAQ,SAAS,EACjB,YAAY,UAAU,SA6NvB,CAAC"}
1
+ {"version":3,"file":"simulation.d.ts","sourceRoot":"","sources":["../../src/tools/simulation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,UAAU,EAAQ,MAAM,qBAAqB,CAAC;AA4CvD,eAAO,MAAM,uBAAuB,GAClC,QAAQ,SAAS,EACjB,YAAY,UAAU,SAyOvB,CAAC"}
@@ -1,5 +1,6 @@
1
1
  import { CreateSimulationInputSchema, CreateSimulationPersonalityInputSchema, CreateSimulationScenarioInputSchema, CreateSimulationSuiteInputSchema, GetSimulationRunInputSchema, GetSimulationRunItemInputSchema, ListSimulationResourcesInputSchema, ListSimulationRunsInputSchema, ListSimulationScenariosInputSchema, RunSimulationsInputSchema, } from '../schemas/index.js';
2
2
  import { registerValidatedTool } from './validation.js';
3
+ import { creates, readOnly } from './annotations.js';
3
4
  import { createToolHandler } from './utils.js';
4
5
  const DEFAULT_LIMIT = 50;
5
6
  // Run items carry call snapshots and transcripts in these fields, which can
@@ -21,16 +22,19 @@ export const registerSimulationTools = (server, vapiClient) => {
21
22
  registerValidatedTool(server, 'list_simulation_personalities', {
22
23
  description: 'Lists simulation personalities: the simulated callers (testers) used in Vapi simulations',
23
24
  inputSchema: ListSimulationResourcesInputSchema,
25
+ annotations: readOnly('List Simulation Personalities'),
24
26
  }, createToolHandler(async (data) => vapiClient.simulationPersonalities.personalityControllerFindAll({
25
27
  limit: data.limit ?? DEFAULT_LIMIT,
26
28
  })));
27
29
  registerValidatedTool(server, 'create_simulation_personality', {
28
30
  description: 'Creates a simulation personality: the simulated caller that talks to the assistant under test',
29
31
  inputSchema: CreateSimulationPersonalityInputSchema,
32
+ annotations: creates('Create Simulation Personality'),
30
33
  }, createToolHandler(async (data) => vapiClient.simulationPersonalities.personalityControllerCreate(data)));
31
34
  registerValidatedTool(server, 'list_simulation_scenarios', {
32
35
  description: 'Lists simulation scenarios: the scripts testers follow and the checks that decide pass or fail',
33
36
  inputSchema: ListSimulationScenariosInputSchema,
37
+ annotations: readOnly('List Simulation Scenarios'),
34
38
  }, createToolHandler(async (data) => vapiClient.simulationScenarios.scenarioControllerFindAll({
35
39
  limit: data.limit ?? DEFAULT_LIMIT,
36
40
  ...(data.name && { name: data.name }),
@@ -38,6 +42,7 @@ export const registerSimulationTools = (server, vapiClient) => {
38
42
  registerValidatedTool(server, 'create_simulation_scenario', {
39
43
  description: 'Creates a simulation scenario: instructions for the tester plus evaluations that decide whether the assistant passed',
40
44
  inputSchema: CreateSimulationScenarioInputSchema,
45
+ annotations: creates('Create Simulation Scenario'),
41
46
  }, createToolHandler(async ({ additionalFields, ...data }) => vapiClient.simulationScenarios.scenarioControllerCreate({
42
47
  ...data,
43
48
  ...additionalFields,
@@ -45,22 +50,26 @@ export const registerSimulationTools = (server, vapiClient) => {
45
50
  registerValidatedTool(server, 'list_simulations', {
46
51
  description: 'Lists simulations. Each simulation pairs one scenario with one personality.',
47
52
  inputSchema: ListSimulationResourcesInputSchema,
53
+ annotations: readOnly('List Simulations'),
48
54
  }, createToolHandler(async (data) => vapiClient.simulations.simulationControllerFindAll({
49
55
  limit: data.limit ?? DEFAULT_LIMIT,
50
56
  })));
51
57
  registerValidatedTool(server, 'create_simulation', {
52
58
  description: 'Creates a simulation that pairs a scenario with a personality. Run it with run_simulations.',
53
59
  inputSchema: CreateSimulationInputSchema,
60
+ annotations: creates('Create Simulation'),
54
61
  }, createToolHandler(async (data) => vapiClient.simulations.simulationControllerCreate(data)));
55
62
  registerValidatedTool(server, 'list_simulation_suites', {
56
63
  description: 'Lists simulation suites: named groups of simulations',
57
64
  inputSchema: ListSimulationResourcesInputSchema,
65
+ annotations: readOnly('List Simulation Suites'),
58
66
  }, createToolHandler(async (data) => vapiClient.simulationSuites.simulationSuiteControllerFindAll({
59
67
  limit: data.limit ?? DEFAULT_LIMIT,
60
68
  })));
61
69
  registerValidatedTool(server, 'create_simulation_suite', {
62
70
  description: 'Creates a simulation suite that groups simulations so they can be run together',
63
71
  inputSchema: CreateSimulationSuiteInputSchema,
72
+ annotations: creates('Create Simulation Suite'),
64
73
  }, createToolHandler(async ({ additionalFields, ...data }) => vapiClient.simulationSuites.simulationSuiteControllerCreate({
65
74
  ...data,
66
75
  ...additionalFields,
@@ -69,6 +78,7 @@ export const registerSimulationTools = (server, vapiClient) => {
69
78
  description: 'Starts a simulation run of simulations and/or suites against an assistant or squad. ' +
70
79
  'Returns the run with its ID and a dashboard URL to share with the user. Check progress with get_simulation_run.',
71
80
  inputSchema: RunSimulationsInputSchema,
81
+ annotations: creates('Run Simulations'),
72
82
  }, createToolHandler(async (data) => {
73
83
  const simulations = [
74
84
  ...(data.simulationIds ?? []).map((simulationId) => ({
@@ -94,6 +104,7 @@ export const registerSimulationTools = (server, vapiClient) => {
94
104
  registerValidatedTool(server, 'list_simulation_runs', {
95
105
  description: 'Lists simulation runs, newest first',
96
106
  inputSchema: ListSimulationRunsInputSchema,
107
+ annotations: readOnly('List Simulation Runs'),
97
108
  }, createToolHandler(async (data) => vapiClient.simulationRuns.simulationRunControllerFindAll({
98
109
  limit: data.limit ?? DEFAULT_LIMIT,
99
110
  ...(data.status && { status: data.status }),
@@ -104,6 +115,7 @@ export const registerSimulationTools = (server, vapiClient) => {
104
115
  'itemsTruncated indicates omitted items; itemsMetadata preserves API pagination metadata. ' +
105
116
  'Use get_simulation_run_item for an item’s transcript and call details.',
106
117
  inputSchema: GetSimulationRunInputSchema,
118
+ annotations: readOnly('Get Simulation Run'),
107
119
  }, createToolHandler(async (data) => {
108
120
  const [run, response] = await Promise.all([
109
121
  vapiClient.simulationRuns.simulationRunControllerFindOne({
@@ -126,6 +138,7 @@ export const registerSimulationTools = (server, vapiClient) => {
126
138
  registerValidatedTool(server, 'get_simulation_run_item', {
127
139
  description: 'Gets one simulation run item in full, including results, call snapshots, transcript, and improvement suggestions',
128
140
  inputSchema: GetSimulationRunItemInputSchema,
141
+ annotations: readOnly('Get Simulation Run Item'),
129
142
  }, createToolHandler(async (data) => vapiClient.simulationRuns.simulationRunControllerFindItem({
130
143
  id: data.runId,
131
144
  itemId: data.itemId,
@@ -1 +1 @@
1
- {"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../src/tools/skills.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAiBzE,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,QAkBnD"}
1
+ {"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../src/tools/skills.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAkBzE,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,QAiBnD"}
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import { PublicSkills } from '../knowledge/skills.js';
3
+ import { readOnly } from './annotations.js';
3
4
  async function knowledgeResult(action) {
4
5
  try {
5
6
  return await action();
@@ -13,19 +14,18 @@ function jsonResult(data) {
13
14
  }
14
15
  export function registerSkillTools(server) {
15
16
  const skills = new PublicSkills();
16
- const annotations = { readOnlyHint: true, destructiveHint: false, openWorldHint: true };
17
17
  server.registerTool('search_skills', {
18
18
  description: 'Find Vapi agent skills in the public VapiAI/skills repository by name, description or content. Omit query to browse. Use load_skill to read a result.',
19
19
  inputSchema: {
20
20
  query: z.string().trim().max(500).default('').describe('Keywords describing the Vapi task, or empty to browse all skills.'),
21
21
  limit: z.number().int().min(1).max(30).default(10),
22
- }, annotations,
22
+ }, annotations: readOnly('Search Vapi Skills', true),
23
23
  }, ({ query, limit }) => knowledgeResult(async () => jsonResult(await skills.search(query, limit))));
24
24
  server.registerTool('load_skill', {
25
25
  description: 'Read a public Vapi skill or one of its supporting files. Start with SKILL.md, then load relevant files from the returned file list as needed. Returns instructions for this task; does not install skills or execute scripts.',
26
26
  inputSchema: {
27
27
  name: z.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/).max(100).describe('Exact skill name from search_skills.'),
28
28
  path: z.string().min(1).max(300).default('SKILL.md').describe('File path relative to the skill directory, such as SKILL.md or references/hooks.md.'),
29
- }, annotations,
29
+ }, annotations: readOnly('Load Vapi Skill', true),
30
30
  }, ({ name, path }) => knowledgeResult(async () => jsonResult(await skills.load(name, path))));
31
31
  }
@@ -1 +1 @@
1
- {"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/tools/tool.ts"],"names":[],"mappings":"AACA,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,SA2DvB,CAAC"}
1
+ {"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/tools/tool.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,UAAU,EAAQ,MAAM,qBAAqB,CAAC;AAOvD,eAAO,MAAM,iBAAiB,GAC5B,QAAQ,SAAS,EACjB,YAAY,UAAU,SA4DvB,CAAC"}
@@ -1,18 +1,19 @@
1
1
  import { registerValidatedTool } from './validation.js';
2
- import { EmptyInputSchema, GetToolInputSchema, CreateToolInputSchema, UpdateToolInputSchema } from '../schemas/index.js';
2
+ import { creates, readOnly, updates } from './annotations.js';
3
+ import { ListInputSchema, GetToolInputSchema, CreateToolInputSchema, UpdateToolInputSchema } from '../schemas/index.js';
3
4
  import { transformToolInput, transformUpdateToolInput, transformToolOutput } from '../transformers/index.js';
4
5
  import { createToolHandler } from './utils.js';
6
+ import { createV1ListHandler } from './list.js';
5
7
  export const registerToolTools = (server, vapiClient) => {
6
8
  registerValidatedTool(server, 'list_tools', {
7
- description: 'Lists all Vapi tools',
8
- inputSchema: EmptyInputSchema,
9
- }, createToolHandler(async () => {
10
- const tools = await vapiClient.tools.list({ limit: 10 });
11
- return tools.map(transformToolOutput);
12
- }));
9
+ description: 'Lists up to 10 Vapi tools',
10
+ inputSchema: ListInputSchema,
11
+ annotations: readOnly('List Tools'),
12
+ }, createV1ListHandler(request => vapiClient.tools.list(request), transformToolOutput));
13
13
  registerValidatedTool(server, 'get_tool', {
14
14
  description: 'Gets details of a specific tool',
15
15
  inputSchema: GetToolInputSchema,
16
+ annotations: readOnly('Get Tool'),
16
17
  }, createToolHandler(async (data) => {
17
18
  const tool = await vapiClient.tools.get({ id: data.toolId });
18
19
  return transformToolOutput(tool);
@@ -20,6 +21,7 @@ export const registerToolTools = (server, vapiClient) => {
20
21
  registerValidatedTool(server, 'create_tool', {
21
22
  description: 'Creates a new Vapi tool',
22
23
  inputSchema: CreateToolInputSchema,
24
+ annotations: creates('Create Tool'),
23
25
  }, createToolHandler(async (data) => {
24
26
  const createToolDto = transformToolInput(data);
25
27
  const tool = await vapiClient.tools.create(createToolDto);
@@ -28,6 +30,7 @@ export const registerToolTools = (server, vapiClient) => {
28
30
  registerValidatedTool(server, 'update_tool', {
29
31
  description: 'Updates an existing Vapi tool',
30
32
  inputSchema: UpdateToolInputSchema,
33
+ annotations: updates('Update Tool'),
31
34
  }, createToolHandler(async (data) => {
32
35
  const existingTool = await vapiClient.tools.get({ id: data.toolId });
33
36
  const updateToolDto = transformUpdateToolInput(data, existingTool);
@@ -1,4 +1,5 @@
1
1
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { ToolAnnotations } from '@modelcontextprotocol/sdk/types.js';
2
3
  import { z } from 'zod';
3
4
  import type { ToolResponse } from './utils.js';
4
5
  type Issue = z.core.$ZodIssue;
@@ -17,6 +18,7 @@ export declare function validationIssues(issues: Issue[], input: unknown, prefix
17
18
  export declare function registerValidatedTool<Schema extends z.ZodType>(server: McpServer, name: string, config: {
18
19
  description: string;
19
20
  inputSchema: Schema;
21
+ annotations: ToolAnnotations;
20
22
  }, handler: (params: z.output<Schema>) => Promise<ToolResponse>): import("@modelcontextprotocol/sdk/server/mcp.js").RegisteredTool;
21
23
  export {};
22
24
  //# sourceMappingURL=validation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/tools/validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG/C,KAAK,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC;AAC9B,KAAK,UAAU,GAAG;IAChB,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,OAAO,EAAE,CAAC;IACvE,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CACxE,CAAC;AAcF,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,GAAE,WAAW,EAAO,GAAG,UAAU,EAAE,CA8C1G;AAWD,wBAAgB,qBAAqB,CAAC,MAAM,SAAS,CAAC,CAAC,OAAO,EAC5D,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,EACpD,OAAO,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,OAAO,CAAC,YAAY,CAAC,oEAsB7D"}
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/tools/validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAC;AAC1E,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG/C,KAAK,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC;AAC9B,KAAK,UAAU,GAAG;IAChB,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,OAAO,EAAE,CAAC;IACvE,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CACxE,CAAC;AAcF,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,GAAE,WAAW,EAAO,GAAG,UAAU,EAAE,CA8C1G;AAWD,wBAAgB,qBAAqB,CAAC,MAAM,SAAS,CAAC,CAAC,OAAO,EAC5D,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,eAAe,CAAA;CAAE,EAClF,OAAO,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,OAAO,CAAC,YAAY,CAAC,oEAsB7D"}
@@ -78,7 +78,7 @@ export function registerValidatedTool(server, name, config, handler) {
78
78
  // boundary lets us format validation failures before the SDK stringifies a
79
79
  // raw ZodError. No request reaches the API handler before parsing succeeds.
80
80
  const transportSchema = z.object({}).passthrough().meta(summarizeInputSchema(z.toJSONSchema(schema, { io: 'input', target: 'draft-07' })));
81
- return server.registerTool(name, { description: config.description, inputSchema: transportSchema }, async (raw) => {
81
+ return server.registerTool(name, { description: config.description, inputSchema: transportSchema, annotations: config.annotations }, async (raw) => {
82
82
  const parsed = await schema.safeParseAsync(raw);
83
83
  if (!parsed.success) {
84
84
  const all = validationIssues(parsed.error.issues, raw);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vapi-ai/mcp-server",
3
3
  "description": "Build AI voice assistants and phone agents with Vapi through the Model Context Protocol",
4
- "version": "0.1.0",
4
+ "version": "0.2.0",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "type": "module",