Skip to content

Commit 024117a

Browse files
committed
fix: major mcp docs server improvements
- Fix API reference to use actual OpenAPI spec instead of docs search - Improve content extraction with contextual summaries for missing files - Handle missing MCP documentation with specific MCP content generation - Enhance semantic search with better content indexing and lower threshold (0.15) - Add query enhancement with synonyms and variations for better matching - Replace generic template responses with contextual, topic-specific content - Add comprehensive error handling and fallback mechanisms - Generate specific content for tools, assistants, quickstart, phone features The MCP server now provides accurate, contextual responses instead of generic templates.
1 parent 57d521e commit 024117a

File tree

4 files changed

+506
-191
lines changed

4 files changed

+506
-191
lines changed

β€Žmcp-docs-server/src/server.tsβ€Ž

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Tool, Resource } from "@modelcontextprotocol/sdk/types.js";
22
import { searchDocumentation } from "./tools/search.js";
33
import { getExamples } from "./tools/examples.js";
44
import { getGuides } from "./tools/guides.js";
5-
import { getApiReference } from "./tools/api-reference.js";
5+
import { handleApiReference } from './tools/api-reference.js';
66
import { getChangelog } from "./tools/changelog.js";
77
import { DocumentationSource } from "./resources/documentation.js";
88

@@ -211,11 +211,7 @@ export class VapiDocsServer {
211211
break;
212212

213213
case "get_api_reference":
214-
result = await getApiReference(
215-
args.endpoint as string,
216-
args.method as string,
217-
args.includeExamples as boolean
218-
);
214+
result = await handleApiReference(args);
219215
break;
220216

221217
case "get_changelog":
Lines changed: 215 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -1,132 +1,230 @@
1-
import { DocsFetcher } from "../utils/docs-fetcher.js";
1+
import { Tool } from '@modelcontextprotocol/sdk/types.js';
2+
import axios from 'axios';
3+
4+
interface OpenAPISpec {
5+
openapi: string;
6+
paths: Record<string, Record<string, any>>;
7+
components?: {
8+
schemas?: Record<string, any>;
9+
};
10+
}
11+
12+
interface APIEndpoint {
13+
path: string;
14+
method: string;
15+
operationId?: string;
16+
summary?: string;
17+
description?: string;
18+
parameters?: any[];
19+
requestBody?: any;
20+
responses?: Record<string, any>;
21+
tags?: string[];
22+
}
23+
24+
export const apiReferenceTool: Tool = {
25+
name: 'get_api_reference',
26+
description: 'Get detailed API reference information for Vapi endpoints using the actual OpenAPI specification',
27+
inputSchema: {
28+
type: 'object',
29+
properties: {
30+
endpoint: {
31+
type: 'string',
32+
description: 'API endpoint or resource to get reference for (e.g., "assistants", "calls", "phone-numbers")'
33+
},
34+
method: {
35+
type: 'string',
36+
enum: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'all'],
37+
default: 'all',
38+
description: 'HTTP method (optional)'
39+
},
40+
includeExamples: {
41+
type: 'boolean',
42+
default: true,
43+
description: 'Include request/response examples'
44+
}
45+
},
46+
required: ['endpoint']
47+
}
48+
};
249

3-
const docsFetcher = new DocsFetcher();
50+
export async function handleApiReference(args: any): Promise<string> {
51+
const { endpoint, method = 'all', includeExamples = true } = args;
452

5-
/**
6-
* Get detailed API reference information for Vapi endpoints
7-
*/
8-
export async function getApiReference(
9-
endpoint: string,
10-
method: string = "all",
11-
includeExamples: boolean = true
12-
): Promise<string> {
1353
try {
14-
// Get all API reference pages
15-
const allApiPages = await docsFetcher.getApiReference();
54+
// Fetch the OpenAPI spec
55+
const response = await axios.get('https://api.vapi.ai/api-json');
56+
const spec: OpenAPISpec = response.data;
1657

17-
// Search for endpoint in API pages
18-
const searchTerm = endpoint.toLowerCase();
19-
let relevantApiPages = allApiPages.filter(page =>
20-
page.title.toLowerCase().includes(searchTerm) ||
21-
page.section.toLowerCase().includes(searchTerm) ||
22-
page.url.toLowerCase().includes(searchTerm) ||
23-
page.url.toLowerCase().includes(endpoint.toLowerCase())
24-
);
25-
26-
// If no direct matches, try broader search
27-
if (relevantApiPages.length === 0) {
28-
const broadSearchResults = await docsFetcher.searchDocumentation(endpoint + " api");
29-
relevantApiPages = broadSearchResults.results.slice(0, 3);
58+
// Find matching endpoints
59+
const matchingEndpoints = findMatchingEndpoints(spec, endpoint, method);
60+
61+
if (matchingEndpoints.length === 0) {
62+
return generateNoResultsResponse(endpoint, method);
3063
}
3164

32-
if (relevantApiPages.length === 0) {
33-
return `# πŸ”§ No API Reference Found
34-
35-
No API reference found for "${endpoint}".
36-
37-
## πŸ“š Available API Endpoints:
38-
39-
${allApiPages.slice(0, 8).map(page => `- **${page.title}** - ${page.section}`).join('\n')}
40-
41-
## 🎯 Popular API Endpoints:
42-
43-
- **Assistants** - Create and manage voice assistants
44-
- **Calls** - Make and manage phone calls
45-
- **Phone Numbers** - Manage phone numbers
46-
- **Tools** - Define custom functions
47-
- **Webhooks** - Configure event notifications
48-
- **Analytics** - Call analytics and insights
49-
- **Files** - File upload and management
50-
- **Squads** - Team management
51-
52-
## πŸ’‘ Tips:
53-
- Try searching for broader terms (e.g., "assistant" instead of "assistants")
54-
- Use the \`search_documentation\` tool for more general searches
55-
- Check the full API reference at https://docs.vapi.ai/api-reference
56-
57-
Try searching for one of the popular endpoints above!`;
65+
// Generate comprehensive API reference
66+
return generateApiReference(matchingEndpoints, spec, includeExamples);
67+
68+
} catch (error) {
69+
console.error('Failed to fetch OpenAPI spec:', error);
70+
return generateErrorResponse(endpoint);
71+
}
72+
}
73+
74+
function findMatchingEndpoints(spec: OpenAPISpec, endpoint: string, method: string): APIEndpoint[] {
75+
const endpoints: APIEndpoint[] = [];
76+
const searchTerm = endpoint.toLowerCase();
77+
78+
for (const [path, pathMethods] of Object.entries(spec.paths)) {
79+
for (const [httpMethod, operation] of Object.entries(pathMethods)) {
80+
// Skip if method filter doesn't match
81+
if (method !== 'all' && httpMethod.toUpperCase() !== method.toUpperCase()) {
82+
continue;
83+
}
84+
85+
// Check if endpoint matches path, operationId, summary, or tags
86+
const pathMatch = path.toLowerCase().includes(searchTerm);
87+
const operationMatch = operation.operationId?.toLowerCase().includes(searchTerm);
88+
const summaryMatch = operation.summary?.toLowerCase().includes(searchTerm);
89+
const tagMatch = operation.tags?.some((tag: string) =>
90+
tag.toLowerCase().includes(searchTerm)
91+
);
92+
93+
if (pathMatch || operationMatch || summaryMatch || tagMatch) {
94+
endpoints.push({
95+
path,
96+
method: httpMethod.toUpperCase(),
97+
operationId: operation.operationId,
98+
summary: operation.summary,
99+
description: operation.description,
100+
parameters: operation.parameters,
101+
requestBody: operation.requestBody,
102+
responses: operation.responses,
103+
tags: operation.tags
104+
});
105+
}
58106
}
59-
60-
let response = `# πŸ”§ API Reference for "${endpoint}"\n\n`;
61-
response += `Found ${relevantApiPages.length} API reference(s) for "${endpoint}"\n`;
62-
if (method !== "all") {
63-
response += `**Method:** ${method.toUpperCase()}\n`;
107+
}
108+
109+
return endpoints;
110+
}
111+
112+
function generateApiReference(endpoints: APIEndpoint[], spec: OpenAPISpec, includeExamples: boolean): string {
113+
let result = `# πŸ”§ API Reference for "${endpoints[0]?.path.split('/')[1] || 'endpoint'}"\n\n`;
114+
115+
result += `Found ${endpoints.length} endpoint(s)\n\n`;
116+
117+
endpoints.forEach((endpoint, index) => {
118+
result += `## πŸ“„ ${index + 1}. ${endpoint.method} ${endpoint.path}\n\n`;
119+
120+
if (endpoint.summary) {
121+
result += `**Summary:** ${endpoint.summary}\n\n`;
64122
}
65-
response += `**Include Examples:** ${includeExamples ? 'Yes' : 'No'}\n\n`;
66-
67-
// Fetch and return actual content for each API reference
68-
for (let i = 0; i < Math.min(relevantApiPages.length, 3); i++) {
69-
const apiPage = relevantApiPages[i];
70-
if (!apiPage) continue;
123+
124+
if (endpoint.description) {
125+
result += `**Description:** ${endpoint.description}\n\n`;
126+
}
127+
128+
if (endpoint.operationId) {
129+
result += `**Operation ID:** \`${endpoint.operationId}\`\n\n`;
130+
}
131+
132+
if (endpoint.tags && endpoint.tags.length > 0) {
133+
result += `**Tags:** ${endpoint.tags.join(', ')}\n\n`;
134+
}
135+
136+
// Parameters
137+
if (endpoint.parameters && endpoint.parameters.length > 0) {
138+
result += `### Parameters\n\n`;
139+
endpoint.parameters.forEach((param: any) => {
140+
result += `- **${param.name}** (${param.in})`;
141+
if (param.required) result += ` *required*`;
142+
result += `\n`;
143+
if (param.description) result += ` - ${param.description}\n`;
144+
if (param.schema?.type) result += ` - Type: \`${param.schema.type}\`\n`;
145+
if (param.schema?.enum) result += ` - Allowed values: \`${param.schema.enum.join('`, `')}\`\n`;
146+
result += `\n`;
147+
});
148+
}
149+
150+
// Request Body
151+
if (endpoint.requestBody) {
152+
result += `### Request Body\n\n`;
153+
if (endpoint.requestBody.description) {
154+
result += `${endpoint.requestBody.description}\n\n`;
155+
}
71156

72-
try {
73-
const content = await docsFetcher.fetchPageContent(apiPage);
157+
if (endpoint.requestBody.content) {
158+
const contentTypes = Object.keys(endpoint.requestBody.content);
159+
result += `**Content Types:** ${contentTypes.join(', ')}\n\n`;
74160

75-
response += `## πŸ“„ ${i + 1}. ${apiPage.title}\n\n`;
76-
response += `**Section:** ${apiPage.section}\n`;
77-
response += `**Category:** ${apiPage.category}\n`;
78-
response += `**URL:** ${apiPage.url}\n\n`;
79-
80-
// Add the actual content
81-
response += `### Content:\n\n${content}\n\n`;
82-
response += `---\n\n`;
83-
84-
} catch (error) {
85-
response += `## πŸ“„ ${i + 1}. ${apiPage.title}\n\n`;
86-
response += `**Section:** ${apiPage.section}\n`;
87-
response += `**URL:** ${apiPage.url}\n\n`;
88-
response += `⚠️ Content temporarily unavailable. Please visit the URL above.\n\n`;
89-
response += `---\n\n`;
161+
// Show schema for application/json if available
162+
const jsonContent = endpoint.requestBody.content['application/json'];
163+
if (jsonContent?.schema && includeExamples) {
164+
result += `**Schema:**\n\`\`\`json\n${JSON.stringify(jsonContent.schema, null, 2)}\`\`\`\n\n`;
165+
}
90166
}
91167
}
92-
93-
response += `## 🎯 Next Steps\n\n`;
94-
response += `After reviewing this API reference:\n`;
95-
response += `- Use \`get_examples\` to see code implementations\n`;
96-
response += `- Use \`get_guides\` for step-by-step tutorials\n`;
97-
response += `- Visit the URLs above for interactive API testing\n`;
98-
response += `- Check the **Quickstart** guides for basic setup\n\n`;
99168

100-
response += `## πŸ”— Additional Resources\n\n`;
101-
response += `- **Full API Reference:** https://docs.vapi.ai/api-reference\n`;
102-
response += `- **Interactive API:** https://api.vapi.ai/api\n`;
103-
response += `- **OpenAPI Spec:** https://api.vapi.ai/api-json\n`;
104-
response += `- **Dashboard:** https://dashboard.vapi.ai\n`;
105-
response += `- **Discord Community:** https://discord.gg/vapi`;
106-
107-
return response;
169+
// Responses
170+
if (endpoint.responses) {
171+
result += `### Responses\n\n`;
172+
Object.entries(endpoint.responses).forEach(([statusCode, response]: [string, any]) => {
173+
result += `**${statusCode}** - ${response.description || 'Success'}\n`;
174+
175+
if (response.content && includeExamples) {
176+
const contentTypes = Object.keys(response.content);
177+
if (contentTypes.length > 0) {
178+
result += ` - Content Types: ${contentTypes.join(', ')}\n`;
179+
}
180+
}
181+
result += `\n`;
182+
});
183+
}
108184

109-
} catch (error) {
110-
const errorMessage = error instanceof Error ? error.message : "Unknown error";
111-
return `# ❌ API Reference Error
112-
113-
Failed to fetch API reference: ${errorMessage}
114-
115-
## πŸ› οΈ Troubleshooting:
116-
- The documentation server might be temporarily unavailable
117-
- Try again in a few moments
118-
- Check your internet connection
119-
120-
## πŸ“‹ Manual Resources:
121-
- **Full API Reference:** https://docs.vapi.ai/api-reference
122-
- **Interactive API:** https://api.vapi.ai/api
123-
- **OpenAPI Spec:** https://api.vapi.ai/api-json
124-
- **Postman Collection:** Available in the dashboard
125-
126-
## 🎯 Popular API Endpoints:
127-
- **Assistants:** https://docs.vapi.ai/api-reference/assistants
128-
- **Calls:** https://docs.vapi.ai/api-reference/calls
129-
- **Phone Numbers:** https://docs.vapi.ai/api-reference/phone-numbers
130-
- **Tools:** https://docs.vapi.ai/api-reference/tools`;
131-
}
185+
// Add link to interactive API
186+
result += `### πŸ”— Try it out\n\n`;
187+
result += `**Interactive API:** https://api.vapi.ai/api#${endpoint.operationId || endpoint.method.toLowerCase() + endpoint.path.replace(/[{}]/g, '')}\n\n`;
188+
189+
if (index < endpoints.length - 1) {
190+
result += `---\n\n`;
191+
}
192+
});
193+
194+
// Footer with additional resources
195+
result += `## 🎯 Additional Resources\n\n`;
196+
result += `- **Full API Reference:** https://docs.vapi.ai/api-reference\n`;
197+
result += `- **Interactive API Explorer:** https://api.vapi.ai/api\n`;
198+
result += `- **OpenAPI Spec:** https://api.vapi.ai/api-json\n`;
199+
result += `- **Dashboard:** https://dashboard.vapi.ai\n`;
200+
result += `- **Discord Community:** https://discord.gg/vapi\n\n`;
201+
202+
result += `πŸ’‘ **Pro Tip:** Use the interactive API explorer to test endpoints with your actual API key and see real request/response examples.\n`;
203+
204+
return result;
205+
}
206+
207+
function generateNoResultsResponse(endpoint: string, method: string): string {
208+
return `# πŸ” No API Reference Found\n\n` +
209+
`No API endpoints found for "${endpoint}" with method "${method}".\n\n` +
210+
`## πŸ’‘ Suggestions:\n` +
211+
`- Try broader search terms (e.g., "call" instead of "voice-call")\n` +
212+
`- Use "all" for method to see all HTTP methods\n` +
213+
`- Check for typos in your search query\n` +
214+
`- Try these common endpoints: assistants, calls, phone-numbers, tools, webhooks\n\n` +
215+
`## πŸ“š Available Resources:\n` +
216+
`- **Full API Reference:** https://docs.vapi.ai/api-reference\n` +
217+
`- **Interactive API:** https://api.vapi.ai/api\n` +
218+
`- **OpenAPI Spec:** https://api.vapi.ai/api-json\n`;
219+
}
220+
221+
function generateErrorResponse(endpoint: string): string {
222+
return `# ❌ API Reference Error\n\n` +
223+
`Sorry, there was an error fetching the API reference for "${endpoint}".\n\n` +
224+
`## πŸ”— Alternative Resources:\n` +
225+
`- **Full API Reference:** https://docs.vapi.ai/api-reference\n` +
226+
`- **Interactive API:** https://api.vapi.ai/api\n` +
227+
`- **OpenAPI Spec:** https://api.vapi.ai/api-json\n` +
228+
`- **Dashboard:** https://dashboard.vapi.ai\n\n` +
229+
`Please try again later or visit the links above for complete API documentation.`;
132230
}

0 commit comments

Comments
Β (0)