search_msfs_docs
Search Microsoft Flight Simulator SDK documentation for specific topics and filter by categories like contents, index, or glossary. Retrieve relevant results quickly using structured queries.
Instructions
Search MSFS SDK documentation for specific topics
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| category | No | Optional category filter (e.g., "contents", "index", "glossary") | |
| limit | No | Maximum number of results to return | |
| query | Yes | Search query for MSFS SDK documentation |
Implementation Reference
- Core implementation of the search_msfs_docs tool: constructs search URL for MSFS docs site, fetches HTML, parses with Cheerio to extract results, categorizes them, and formats output.async searchDocumentation( query: string, category: string = 'all', limit: number = 10 ): Promise<{ content: Array<{ type: string; text: string }> }> { try { // Normalize category const normalizedCategory = category.toLowerCase(); const agtParam = this.categories[normalizedCategory as keyof typeof this.categories] || this.categories.all; // Build search URL using the website's search functionality let searchUrl = `${this.searchBaseUrl}?rhsearch=${encodeURIComponent(query)}`; if (agtParam) { searchUrl += `&agt=${agtParam}`; } console.log(`Searching MSFS docs: ${searchUrl}`); const response = await fetch(searchUrl, { headers: { 'User-Agent': 'MSFS-SDK-MCP-Server/1.0' } }); if (!response.ok) { throw new Error(`Failed to fetch search results: ${response.status}`); } const html = await response.text(); const $ = cheerio.load(html); console.log(`Search page loaded, length: ${html.length}`); // Extract search results from the page const searchResults: DocumentationResult[] = []; console.log('Analyzing HTML structure for search results...'); // Debug: Log some of the HTML to understand the structure const bodyText = $('body').text(); if (bodyText.includes('result(s) found')) { console.log('Found search results indicator in page'); } // Try multiple approaches to find search results const possibleSelectors = [ '.search-result', '.result-item', '.topic', '.search-hit', '.result', 'li:contains("Samples, Schemas, Tutorials")', 'li:contains("How To")', 'li:contains("Content Configuration")', 'li:contains("Developer Mode")', 'div:contains("result(s) found") ~ *', 'a[href*=".htm"]' ]; // First, try to find structured search results for (const selector of possibleSelectors.slice(0, -1)) { $(selector).each((i, elem) => { if (searchResults.length >= limit) return false; const $elem = $(elem); const href = $elem.attr('href') || $elem.find('a').attr('href'); const text = $elem.text().trim(); if (href && href.includes('.htm') && text) { // Clean up the href and construct proper absolute URL let cleanHref = href; if (href.startsWith('../')) { cleanHref = href.replace(/^\.\.\//, '/html/'); } else if (!href.startsWith('/') && !href.startsWith('http')) { cleanHref = `/html/${href}`; } const absoluteUrl = cleanHref.startsWith('http') ? cleanHref : `${this.baseUrl}${cleanHref}`; // Skip if we already have this URL if (searchResults.some(r => r.url === absoluteUrl)) { return; } const categoryFromPath = this.deriveCategoryFromPath(href); searchResults.push({ title: text.substring(0, 100), // Limit title length url: absoluteUrl, description: `Documentation page containing "${query}"`, category: categoryFromPath }); console.log(`Found result via ${selector}: ${text.substring(0, 50)}...`); } }); if (searchResults.length > 0) { console.log(`Found ${searchResults.length} results using selector: ${selector}`); break; } } // If no structured results found, fall back to all links if (searchResults.length === 0) { console.log('No structured results found, trying all links...'); $('a[href*=".htm"]').each((i, elem) => { if (searchResults.length >= limit) return false; const $elem = $(elem); const href = $elem.attr('href'); const text = $elem.text().trim(); if (href && text && text.toLowerCase().includes(query.toLowerCase())) { // Clean up the href and construct proper absolute URL let cleanHref = href; if (href.startsWith('../')) { cleanHref = href.replace(/^\.\.\//, '/html/'); } else if (!href.startsWith('/') && !href.startsWith('http')) { cleanHref = `/html/${href}`; } const absoluteUrl = cleanHref.startsWith('http') ? cleanHref : `${this.baseUrl}${cleanHref}`; // Skip if we already have this URL if (searchResults.some(r => r.url === absoluteUrl)) { return; } const categoryFromPath = this.deriveCategoryFromPath(href); searchResults.push({ title: text.substring(0, 100), url: absoluteUrl, description: `Found "${query}" in link text`, category: categoryFromPath }); } }); } // If no specific search results found, try to extract any documentation links if (searchResults.length === 0) { $('a[href*=".htm"]').each((i, elem) => { if (searchResults.length >= limit) return false; const $elem = $(elem); const href = $elem.attr('href'); const text = $elem.text().trim(); if (href && text && text.toLowerCase().includes(query.toLowerCase())) { // Clean up the href and construct proper absolute URL let cleanHref = href; if (href.startsWith('../')) { cleanHref = href.replace(/^\.\.\//, '/html/'); } else if (!href.startsWith('/') && !href.startsWith('http')) { cleanHref = `/html/${href}`; } const absoluteUrl = cleanHref.startsWith('http') ? cleanHref : `${this.baseUrl}${cleanHref}`; // Skip if we already have this URL if (searchResults.some(r => r.url === absoluteUrl)) { return; } const categoryFromPath = this.deriveCategoryFromPath(href); searchResults.push({ title: text.substring(0, 100), url: absoluteUrl, description: `Found "${query}" in link text`, category: categoryFromPath }); } }); } if (searchResults.length > 0) { const formattedResults = searchResults.map((result: DocumentationResult) => `**${result.title}**\n- Category: ${result.category}\n- URL: ${result.url}\n- Description: ${result.description}\n` ).join('\n---\n'); return { content: [ { type: 'text', text: `Search results for "${query}" in category "${category}":\n\n${formattedResults}` }, ], }; } // If still no results, return a message return { content: [ { type: 'text', text: `No results found for "${query}" in category "${category}". The search was performed on the MSFS documentation website.` }, ], }; } catch (error) { console.error('Search error:', error); return { content: [ { type: 'text', text: `Error searching MSFS documentation: ${(error as Error).message}. Please try a different search term or check your internet connection.` }, ], }; } }
- src/index.ts:37-62 (registration)Tool registration in ListToolsRequestHandler, including name, description, and input schema.{ name: 'search_msfs_docs', description: 'Search MSFS SDK documentation for specific topics', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query for MSFS SDK documentation', }, category: { type: 'string', description: 'Optional category filter (e.g., "contents", "index", "glossary")', enum: ['contents', 'index', 'glossary', 'all'] }, limit: { type: 'number', description: 'Maximum number of results to return', minimum: 1, maximum: 20, default: 10 } }, required: ['query'] } },
- src/index.ts:127-136 (handler)Dispatch handler in CallToolRequestHandler that validates inputs and delegates to documentationService.searchDocumentation.case 'search_msfs_docs': if (!args?.query) { throw new Error('Query parameter is required'); } return await this.documentationService.searchDocumentation( String(args.query), String(args.category || 'all'), Number(args.limit || 10) );
- src/index.ts:40-61 (schema)Input schema definition for the search_msfs_docs tool.inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query for MSFS SDK documentation', }, category: { type: 'string', description: 'Optional category filter (e.g., "contents", "index", "glossary")', enum: ['contents', 'index', 'glossary', 'all'] }, limit: { type: 'number', description: 'Maximum number of results to return', minimum: 1, maximum: 20, default: 10 } }, required: ['query'] }
- Helper in NaturalLanguageService.parse() that maps natural language 'Search ... op msfs sdk' to search_msfs_docs tool call.return { tool: "search_msfs_docs", arguments: { query: query, category: "all", limit: 5 } };