@vapi-ai/mcp-server 0.0.11 → 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.
Files changed (59) hide show
  1. package/README.md +241 -104
  2. package/dist/knowledge/docs.d.ts +96 -0
  3. package/dist/knowledge/docs.d.ts.map +1 -0
  4. package/dist/knowledge/docs.js +16 -0
  5. package/dist/knowledge/skills.d.ts +28 -0
  6. package/dist/knowledge/skills.d.ts.map +1 -0
  7. package/dist/knowledge/skills.js +131 -0
  8. package/dist/schemas/assistant-openapi.d.ts +31 -0
  9. package/dist/schemas/assistant-openapi.d.ts.map +1 -0
  10. package/dist/schemas/assistant-openapi.js +87 -0
  11. package/dist/schemas/assistant-openapi.json +2341 -0
  12. package/dist/schemas/index.d.ts +161 -1181
  13. package/dist/schemas/index.d.ts.map +1 -1
  14. package/dist/schemas/index.js +99 -172
  15. package/dist/schemas/simulation-openapi.d.ts +9 -0
  16. package/dist/schemas/simulation-openapi.d.ts.map +1 -0
  17. package/dist/schemas/simulation-openapi.js +81 -0
  18. package/dist/schemas/simulation-openapi.json +27558 -0
  19. package/dist/schemas/simulation.d.ts +54 -0
  20. package/dist/schemas/simulation.d.ts.map +1 -0
  21. package/dist/schemas/simulation.js +76 -0
  22. package/dist/tools/annotations.d.ts +5 -0
  23. package/dist/tools/annotations.d.ts.map +1 -0
  24. package/dist/tools/annotations.js +14 -0
  25. package/dist/tools/api-schema.d.ts +4 -0
  26. package/dist/tools/api-schema.d.ts.map +1 -0
  27. package/dist/tools/api-schema.js +69 -0
  28. package/dist/tools/assistant.d.ts.map +1 -1
  29. package/dist/tools/assistant.js +33 -17
  30. package/dist/tools/call.d.ts.map +1 -1
  31. package/dist/tools/call.js +20 -11
  32. package/dist/tools/docs.d.ts +3 -0
  33. package/dist/tools/docs.d.ts.map +1 -0
  34. package/dist/tools/docs.js +27 -0
  35. package/dist/tools/index.d.ts.map +1 -1
  36. package/dist/tools/index.js +8 -0
  37. package/dist/tools/input-schema.d.ts +5 -0
  38. package/dist/tools/input-schema.d.ts.map +1 -0
  39. package/dist/tools/input-schema.js +59 -0
  40. package/dist/tools/list.d.ts +34 -0
  41. package/dist/tools/list.d.ts.map +1 -0
  42. package/dist/tools/list.js +47 -0
  43. package/dist/tools/phone-number.d.ts.map +1 -1
  44. package/dist/tools/phone-number.js +17 -10
  45. package/dist/tools/simulation.d.ts +4 -0
  46. package/dist/tools/simulation.d.ts.map +1 -0
  47. package/dist/tools/simulation.js +146 -0
  48. package/dist/tools/skills.d.ts +3 -0
  49. package/dist/tools/skills.d.ts.map +1 -0
  50. package/dist/tools/skills.js +31 -0
  51. package/dist/tools/tool.d.ts.map +1 -1
  52. package/dist/tools/tool.js +31 -14
  53. package/dist/tools/validation.d.ts +24 -0
  54. package/dist/tools/validation.d.ts.map +1 -0
  55. package/dist/tools/validation.js +94 -0
  56. package/dist/transformers/index.d.ts +3 -3
  57. package/dist/transformers/index.d.ts.map +1 -1
  58. package/dist/transformers/index.js +52 -31
  59. package/package.json +9 -6
@@ -0,0 +1,131 @@
1
+ import { parse } from 'yaml';
2
+ import { z } from 'zod';
3
+ const REPOSITORY = 'https://github.com/VapiAI/skills';
4
+ const API = 'https://api.github.com/repos/VapiAI/skills';
5
+ const RAW = 'https://raw.githubusercontent.com/VapiAI/skills';
6
+ const CACHE_MS = 5 * 60_000;
7
+ const TEXT_FILE = /\.(md|txt|json|ya?ml|py|[cm]?js|ts|sh|rst|toml)$/i;
8
+ const commitSchema = z.object({ sha: z.string().regex(/^[a-f0-9]{40}$/) });
9
+ const treeSchema = z.object({
10
+ truncated: z.literal(false),
11
+ tree: z.array(z.object({ path: z.string(), type: z.string(), mode: z.string() })),
12
+ });
13
+ // Each MCP server has its own bounded-lifetime snapshot. Rejected fetches are
14
+ // evicted so an upstream outage doesn't poison later calls.
15
+ export class PublicSkills {
16
+ fetcher;
17
+ cached;
18
+ constructor(fetcher = fetch) {
19
+ this.fetcher = fetcher;
20
+ }
21
+ async read(url, maxBytes = 512_000) {
22
+ const response = await this.fetcher(url, {
23
+ signal: AbortSignal.timeout(15_000),
24
+ redirect: 'error',
25
+ headers: { Accept: 'application/vnd.github+json', 'User-Agent': 'vapi-mcp-server' },
26
+ });
27
+ if (!response.ok) {
28
+ await response.body?.cancel();
29
+ throw new Error(`Public skills request failed (HTTP ${response.status}). Retry later.`);
30
+ }
31
+ if (!response.body)
32
+ throw new Error('Public skills returned an empty response.');
33
+ const reader = response.body.getReader();
34
+ const chunks = [];
35
+ let size = 0;
36
+ try {
37
+ while (true) {
38
+ const { done, value } = await reader.read();
39
+ if (done)
40
+ break;
41
+ size += value.byteLength;
42
+ if (size > maxBytes)
43
+ throw new Error('Public skills response exceeds the supported size.');
44
+ chunks.push(value);
45
+ }
46
+ }
47
+ finally {
48
+ await reader.cancel().catch(() => undefined);
49
+ reader.releaseLock();
50
+ }
51
+ return Buffer.concat(chunks).toString('utf8');
52
+ }
53
+ snapshot() {
54
+ if (this.cached && this.cached.expiresAt > Date.now())
55
+ return this.cached.promise;
56
+ const promise = (async () => {
57
+ // Pin file reads to one commit so SKILL.md and its references agree even
58
+ // when the public repository changes between requests.
59
+ const { sha } = commitSchema.parse(JSON.parse(await this.read(`${API}/commits/main`)));
60
+ const tree = treeSchema.parse(JSON.parse(await this.read(`${API}/git/trees/${sha}?recursive=1`, 2_000_000)));
61
+ const files = new Set(tree.tree.filter(entry => entry.type === 'blob' && ['100644', '100755'].includes(entry.mode)).map(entry => entry.path));
62
+ const names = [...files].filter(path => /^[a-z0-9]+(?:-[a-z0-9]+)*\/SKILL\.md$/.test(path)).map(path => path.split('/')[0]).sort();
63
+ if (!names.length)
64
+ throw new Error('No public Vapi skills were found. Retry later.');
65
+ return { revision: sha, files, names, contents: new Map() };
66
+ })();
67
+ this.cached = { expiresAt: Date.now() + CACHE_MS, promise };
68
+ void promise.catch(() => { if (this.cached?.promise === promise)
69
+ this.cached = undefined; });
70
+ return promise;
71
+ }
72
+ content(snapshot, path) {
73
+ const cached = snapshot.contents.get(path);
74
+ if (cached)
75
+ return cached;
76
+ const encodedPath = path.split('/').map(encodeURIComponent).join('/');
77
+ const promise = this.read(`${RAW}/${snapshot.revision}/${encodedPath}`);
78
+ if (snapshot.contents.size >= 100)
79
+ snapshot.contents.delete(snapshot.contents.keys().next().value);
80
+ snapshot.contents.set(path, promise);
81
+ void promise.catch(() => snapshot.contents.delete(path));
82
+ return promise;
83
+ }
84
+ async skill(snapshot, name) {
85
+ const content = await this.content(snapshot, `${name}/SKILL.md`);
86
+ const frontmatter = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/.exec(content);
87
+ if (!frontmatter)
88
+ throw new Error(`Invalid frontmatter for public skill ${name}.`);
89
+ const metadata = z.object({ name: z.literal(name), description: z.string().min(1) }).parse(parse(frontmatter[1]));
90
+ return { name, description: metadata.description, content };
91
+ }
92
+ async search(query, limit) {
93
+ const snapshot = await this.snapshot();
94
+ const skills = [];
95
+ // Limit concurrent public requests when warming a new snapshot.
96
+ for (let index = 0; index < snapshot.names.length; index += 4) {
97
+ skills.push(...await Promise.all(snapshot.names.slice(index, index + 4).map(name => this.skill(snapshot, name))));
98
+ }
99
+ const terms = [...new Set(query.toLowerCase().match(/[\p{L}\p{N}]+/gu) ?? [])];
100
+ const matches = skills.map(skill => ({
101
+ skill,
102
+ score: terms.reduce((score, term) => score +
103
+ (skill.name.includes(term) ? 5 : 0) +
104
+ (skill.description.toLowerCase().includes(term) ? 3 : 0) +
105
+ (skill.content.toLowerCase().includes(term) ? 1 : 0), 0),
106
+ })).filter(({ score }) => !terms.length || score > 0)
107
+ .sort((a, b) => b.score - a.score || a.skill.name.localeCompare(b.skill.name));
108
+ return {
109
+ repository: REPOSITORY, revision: snapshot.revision, total: matches.length,
110
+ skills: matches.slice(0, limit).map(({ skill }) => ({
111
+ name: skill.name, description: skill.description,
112
+ sourceUrl: `${REPOSITORY}/blob/${snapshot.revision}/${skill.name}/SKILL.md`,
113
+ })),
114
+ };
115
+ }
116
+ async load(name, path) {
117
+ const snapshot = await this.snapshot();
118
+ if (!snapshot.names.includes(name))
119
+ throw new Error(`Unknown public skill. Use search_skills to find a skill name.`);
120
+ const files = [...snapshot.files].filter(file => file.startsWith(`${name}/`) && TEXT_FILE.test(file)).map(file => file.slice(name.length + 1)).sort();
121
+ // Exact tree membership, not URL/path normalization, bounds reads to this
122
+ // skill and excludes traversal, URLs, symlinks and binary assets.
123
+ if (!files.includes(path))
124
+ throw new Error('Unknown skill file. Load SKILL.md to see the available files.');
125
+ return {
126
+ name, path, revision: snapshot.revision,
127
+ sourceUrl: `${REPOSITORY}/blob/${snapshot.revision}/${name}/${path}`,
128
+ content: await this.content(snapshot, `${name}/${path}`), files,
129
+ };
130
+ }
131
+ }
@@ -0,0 +1,31 @@
1
+ import { z } from 'zod';
2
+ type JsonSchema = z.core.JSONSchema.JSONSchema;
3
+ export declare const requiredToolConfigurations: string[];
4
+ export declare const functionRequiresName: boolean;
5
+ export declare function inputContract(value: unknown, schemaMap?: boolean): unknown;
6
+ export declare function providerContract(field: 'voice' | 'transcriber' | 'llm'): JsonSchema;
7
+ export declare const providersRequiringExpandedInputs: {
8
+ [k: string]: string[];
9
+ };
10
+ type VoiceConfiguration = {
11
+ provider: string;
12
+ voiceId: string;
13
+ model?: string;
14
+ };
15
+ type TranscriberConfiguration = {
16
+ provider: string;
17
+ model?: string;
18
+ };
19
+ export declare const VoiceInputSchema: z.ZodType<VoiceConfiguration>;
20
+ export declare const TranscriberInputSchema: z.ZodType<TranscriberConfiguration>;
21
+ export declare const CreateAssistantNameSchema: z.ZodType<string>;
22
+ export declare const UpdateAssistantNameSchema: z.ZodType<string>;
23
+ export declare const LLMInputSchema: z.ZodType<{
24
+ provider: string;
25
+ model?: string;
26
+ }>;
27
+ export declare function fieldContract(name: string): JsonSchema;
28
+ export declare function apiField<T = unknown>(name: string, overrides?: Partial<JsonSchema>): z.ZodType<T>;
29
+ export declare function apiObjectField<T = Record<string, unknown>>(name: string): z.ZodType<T>;
30
+ export {};
31
+ //# sourceMappingURL=assistant-openapi.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assistant-openapi.d.ts","sourceRoot":"","sources":["../../src/schemas/assistant-openapi.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,KAAK,UAAU,GAAG,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC;AAkB/C,eAAO,MAAM,0BAA0B,UAAsC,CAAC;AAC9E,eAAO,MAAM,oBAAoB,SAAgC,CAAC;AAKlE,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,UAAQ,GAAG,OAAO,CAiCxE;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,aAAa,GAAG,KAAK,GAAG,UAAU,CAanF;AAID,eAAO,MAAM,gCAAgC;;CAI5C,CAAC;AAEF,KAAK,kBAAkB,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAChF,KAAK,wBAAwB,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAIrE,eAAO,MAAM,gBAAgB,EAAoF,CAAC,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC;AAC/I,eAAO,MAAM,sBAAsB,EAA0F,CAAC,CAAC,OAAO,CAAC,wBAAwB,CAAC,CAAC;AACjK,eAAO,MAAM,yBAAyB,EAA+E,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;AACvI,eAAO,MAAM,yBAAyB,EAA+E,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;AAEvI,eAAO,MAAM,cAAc,EAAkF,CAAC,CAAC,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAE7J,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAKtD;AAED,wBAAgB,QAAQ,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,GAAE,OAAO,CAAC,UAAU,CAAM,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAErG;AAED,wBAAgB,cAAc,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAKtF"}
@@ -0,0 +1,87 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { z } from 'zod';
3
+ const snapshot = JSON.parse(readFileSync(new URL('./assistant-openapi.json', import.meta.url), 'utf8'));
4
+ export const requiredToolConfigurations = snapshot.requiredToolConfigurations;
5
+ export const functionRequiresName = snapshot.functionRequiresName;
6
+ // The published spec uses overlapping oneOf [enum, string] branches for preset
7
+ // IDs and model names. The API accepts both. Change only this recognizable
8
+ // pattern, leaving discriminated/exclusive unions intact.
9
+ export function inputContract(value, schemaMap = false) {
10
+ if (Array.isArray(value))
11
+ return value.map(item => inputContract(item));
12
+ if (!value || typeof value !== 'object')
13
+ return value;
14
+ const schema = value;
15
+ // Property names such as oneOf/default are user-defined keys in schema maps,
16
+ // rather than JSON Schema keywords. Preserve them and normalize their values.
17
+ if (schemaMap)
18
+ return Object.fromEntries(Object.entries(schema).map(([key, nested]) => [key, inputContract(nested)]));
19
+ // An empty published preset enum denotes no preset values. Represent that
20
+ // branch as an always-failing schema so it remains valid JSON Schema.
21
+ if (Array.isArray(schema.enum) && schema.enum.length === 0)
22
+ return { not: {} };
23
+ const alternatives = Array.isArray(schema.oneOf) ? schema.oneOf : undefined;
24
+ const overlap = alternatives?.some(branch => branch.type === 'string' &&
25
+ Object.keys(branch).every(key => ['type', 'title', 'description'].includes(key))) &&
26
+ alternatives.some(branch => branch.type === 'string' && Array.isArray(branch.enum));
27
+ return Object.fromEntries(Object.entries(schema)
28
+ // OpenAPI defaults are annotations; preserve omitted input fields and let
29
+ // the API apply its defaults rather than materializing them in requests.
30
+ .filter(([key]) => key !== 'default')
31
+ .map(([key, nested]) => [
32
+ key === 'oneOf' && overlap ? 'anyOf' : key,
33
+ // Some published voice IDs repeat in enum arrays. Deduplication changes
34
+ // no accepted values and keeps the result valid JSON Schema for Ajv.
35
+ key === 'enum' && Array.isArray(nested) ? [...new Set(nested)]
36
+ // Published name patterns include JavaScript /.../ delimiters, while
37
+ // JSON Schema pattern expects the regex source without delimiters.
38
+ : key === 'pattern' && typeof nested === 'string' && nested.startsWith('/') && nested.endsWith('/') ? nested.slice(1, -1)
39
+ // BackoffPlan.type is published as object with a string enum. Keep the
40
+ // enum's actual value type; the incompatible type annotation is a spec defect.
41
+ : key === 'type' && nested === 'object' && Array.isArray(schema.enum) && schema.enum.every(v => typeof v === 'string') ? 'string'
42
+ : ['properties', 'definitions', '$defs', 'patternProperties', 'dependentSchemas'].includes(key) ? inputContract(nested, true)
43
+ : ['allOf', 'anyOf', 'oneOf', 'not', 'items', 'additionalProperties', 'contains', 'if', 'then', 'else'].includes(key) ? inputContract(nested)
44
+ : nested,
45
+ ]));
46
+ }
47
+ export function providerContract(field) {
48
+ const supported = snapshot[field].filter(definition => definition.required.every(key => key in definition.properties));
49
+ return {
50
+ oneOf: supported.map(definition => ({
51
+ type: 'object',
52
+ required: definition.required,
53
+ properties: inputContract(definition.properties, true),
54
+ // API DTOs reject undeclared provider fields. In particular, a provider
55
+ // without a model property must reject model, not silently accept it.
56
+ additionalProperties: false,
57
+ })),
58
+ };
59
+ }
60
+ // Providers requiring fields outside the existing compact MCP surface stay
61
+ // unsupported, rather than having those API-required fields silently dropped.
62
+ export const providersRequiringExpandedInputs = Object.fromEntries(['voice', 'transcriber', 'llm'].map(field => [field, snapshot[field]
63
+ .filter(definition => definition.required.some(key => !(key in definition.properties)))
64
+ .map(definition => definition.name)]));
65
+ // fromJSONSchema validates at runtime but intentionally returns an untyped
66
+ // ZodType. These types describe the projected fields, not a second validator.
67
+ export const VoiceInputSchema = z.fromJSONSchema(providerContract('voice'), { defaultTarget: 'openapi-3.0' });
68
+ export const TranscriberInputSchema = z.fromJSONSchema(providerContract('transcriber'), { defaultTarget: 'openapi-3.0' });
69
+ export const CreateAssistantNameSchema = z.fromJSONSchema(snapshot.name.create, { defaultTarget: 'openapi-3.0' });
70
+ export const UpdateAssistantNameSchema = z.fromJSONSchema(snapshot.name.update, { defaultTarget: 'openapi-3.0' });
71
+ export const LLMInputSchema = z.fromJSONSchema(providerContract('llm'), { defaultTarget: 'openapi-3.0' });
72
+ export function fieldContract(name) {
73
+ const field = snapshot.fields[name];
74
+ if (!field)
75
+ throw new Error(`Unknown API input field: ${name}`);
76
+ return JSON.parse(JSON.stringify(inputContract({ ...field, definitions: snapshot.definitions }), (key, value) => key === '$ref' && typeof value === 'string' ? value.replace('#/components/schemas/', '#/definitions/') : value));
77
+ }
78
+ export function apiField(name, overrides = {}) {
79
+ return z.fromJSONSchema({ ...fieldContract(name), ...overrides }, { defaultTarget: 'openapi-3.0' });
80
+ }
81
+ export function apiObjectField(name) {
82
+ const schema = apiField(name);
83
+ if (!(schema instanceof z.ZodObject))
84
+ throw new Error(`${name} is no longer an object schema`);
85
+ // Preserve the existing MCP policy of stripping unknown envelope fields.
86
+ return schema.strip();
87
+ }