search_msfs_docs
Search Microsoft Flight Simulator SDK documentation by topic, category, or keyword to find development resources and technical information.
Instructions
Search MSFS SDK documentation for specific topics
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | Search query for MSFS SDK documentation | |
| category | No | Optional category filter (e.g., "contents", "index", "glossary") | |
| limit | No | Maximum number of results to return |
Implementation Reference
- Core handler function that implements the 'search_msfs_docs' tool. Fetches the MSFS SDK documentation search page, parses HTML with Cheerio to extract relevant .htm links containing the query, categorizes results, and returns formatted markdown results.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 (schema)Tool schema definition including input parameters: query (required), category (enum), limit.{ 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:34-120 (registration)Registration of the tool in the ListToolsRequestHandler, including its name, description, and schema.this.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { 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'] } }, { name: 'get_doc_content', description: 'Get detailed content from a specific MSFS SDK documentation page', inputSchema: { type: 'object', properties: { url: { type: 'string', description: 'URL of the documentation page to retrieve', }, section: { type: 'string', description: 'Specific section to extract (e.g., "overview", "examples", "api-reference")', } }, required: ['url'] } }, { name: 'list_categories', description: 'List all available MSFS SDK documentation categories', inputSchema: { type: 'object', properties: {} } }, { name: 'natural_language_query', description: 'Process natural language queries like "Search livery op msfs sdk"', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Natural language query (e.g., "Search livery op msfs sdk")', } }, required: ['query'] } }, { name: 'list_category_items', description: 'Returns all items for a given documentation category', inputSchema: { type: 'object', properties: { category: { type: 'string', description: 'Category to list items from (index, contents, or glossary)', enum: ['index', 'contents', 'glossary'] } }, required: ['category'] } } ], }; });
- src/index.ts:127-136 (handler)Direct handler case in CallToolRequestHandler that validates input 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) );
- Helper function to parse natural language queries like 'Search livery op msfs sdk' into search_msfs_docs tool call. Additional pattern at lines 49-64 for category-specific searches.static parse(command: string): { tool: string; arguments: any } | null { // Match "Search [term] op msfs sdk" const searchPattern = /^Search\s+(.+?)\s+op\s+msfs\s+sdk\s*$/i; const match = command.match(searchPattern); if (match) { const query = match[1]; return { tool: "search_msfs_docs", arguments: { query: query, category: "all", limit: 5 } }; }