search_ember_docs
Search Ember.js documentation to find API references, guides, and community resources for development queries.
Instructions
Search through Ember.js documentation including API docs, guides, and community content. Returns relevant documentation with links to official sources. Use this for general queries about Ember concepts, features, or usage.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | Search query (e.g., 'component lifecycle', 'tracked properties', 'routing') | |
| category | No | Filter by documentation category (default: all) | |
| limit | No | Maximum number of results (default: 5) |
Implementation Reference
- index.js:169-193 (handler)The main handler function for the 'search_ember_docs' tool. It extracts arguments, calls DocumentationService.search, handles empty results, formats output with formatSearchResults, and returns MCP content.async handleSearchDocs(args) { const { query, category = "all", limit = 5 } = args; const results = await this.docService.search(query, category, limit); if (results.length === 0) { return { content: [ { type: "text", text: `No results found for "${query}". Try different keywords or broader search terms.`, }, ], }; } const formattedResults = formatSearchResults(results, this.docService.deprecationManager); return { content: [ { type: "text", text: formattedResults, }, ], }; }
- index.js:50-76 (registration)Registration of the 'search_ember_docs' tool in the ListTools response, including name, description, and input schema definition.{ name: "search_ember_docs", description: "Search through Ember.js documentation including API docs, guides, and community content. Returns relevant documentation with links to official sources. Use this for general queries about Ember concepts, features, or usage.", inputSchema: { type: "object", properties: { query: { type: "string", description: "Search query (e.g., 'component lifecycle', 'tracked properties', 'routing')", }, category: { type: "string", enum: ["all", "api", "guides", "community"], description: "Filter by documentation category (default: all)", }, limit: { type: "number", description: "Maximum number of results (default: 5)", default: 5, }, }, required: ["query"], }, },
- index.js:54-75 (schema)Input schema for the 'search_ember_docs' tool, defining parameters query (required), category, and limit.inputSchema: { type: "object", properties: { query: { type: "string", description: "Search query (e.g., 'component lifecycle', 'tracked properties', 'routing')", }, category: { type: "string", enum: ["all", "api", "guides", "community"], description: "Filter by documentation category (default: all)", }, limit: { type: "number", description: "Maximum number of results (default: 5)", default: 5, }, }, required: ["query"], },
- lib/documentation-service.js:214-314 (helper)Core search logic in DocumentationService.search: parses query, searches sections by category, computes relevance scores (exact phrase, term matches, proximity), extracts excerpts, checks deprecations, sorts and limits results.async search(query, category = "all", limit = 5) { const results = []; const queryLower = query.toLowerCase(); const searchTerms = queryLower.split(/\s+/).filter(term => term.length > 0); const sectionsToSearch = category === "all" ? Object.keys(this.sections) : category === "api" ? ["api-docs"] : category === "guides" ? Object.keys(this.sections).filter( (s) => !["api-docs", "community-bloggers"].includes(s) ) : category === "community" ? ["community-bloggers"] : []; for (const sectionName of sectionsToSearch) { const sectionItems = this.sections[sectionName] || []; for (const item of sectionItems) { const content = item.content.toLowerCase(); const title = this.extractTitle(item.content); const titleLower = title.toLowerCase(); // Calculate relevance score with better weighting let score = 0; let matchedTerms = []; let termPositions = []; // Exact phrase match - highest value if (content.includes(queryLower)) { score += SEARCH_CONFIG.EXACT_PHRASE_BONUS; matchedTerms.push(queryLower); } // Check each term searchTerms.forEach((term) => { const matches = (content.match(new RegExp(term, "gi")) || []).length; if (matches > 0) { matchedTerms.push(term); // Title matches are highly relevant if (titleLower.includes(term)) { score += SEARCH_CONFIG.TITLE_MATCH_BONUS; } // Base score for term presence score += matches * SEARCH_CONFIG.TERM_MATCH_WEIGHT; // Find first position of this term for proximity scoring const pos = content.indexOf(term); if (pos !== -1) { termPositions.push({ term, pos }); } } }); // All terms present - significant bonus if (matchedTerms.length === searchTerms.length) { score += SEARCH_CONFIG.ALL_TERMS_BONUS; // Proximity bonus: terms close together are more relevant if (termPositions.length > 1) { termPositions.sort((a, b) => a.pos - b.pos); const spread = termPositions[termPositions.length - 1].pos - termPositions[0].pos; // If all terms within proximity threshold, add proximity bonus if (spread < SEARCH_CONFIG.PROXIMITY_THRESHOLD) { score += Math.floor((SEARCH_CONFIG.PROXIMITY_THRESHOLD - spread) / SEARCH_CONFIG.PROXIMITY_BONUS_DIVISOR); } } } // Only include results with meaningful matches // Require at least 2 terms or a high-value single match if (score >= SEARCH_CONFIG.MIN_SCORE && (matchedTerms.length >= 2 || score >= SEARCH_CONFIG.MIN_SCORE_SINGLE_TERM)) { const excerpt = this.extractExcerpt(item.content, searchTerms, termPositions); // Check if this result is for a deprecated API const deprecationInfo = this.deprecationManager.checkSearchResult({ title, content: item.content }); results.push({ title, category: this.categorizeSectionName(sectionName), excerpt, score, url: generateUrl(sectionName, title), apiLink: generateApiLink(item.content), matchedTerms: matchedTerms.length, totalTerms: searchTerms.length, deprecationInfo: deprecationInfo, }); } } } // Sort by score and return top results results.sort((a, b) => b.score - a.score); return results.slice(0, limit); }
- lib/formatters.js:14-57 (helper)Helper function to format search results into a structured Markdown response for MCP, including titles, categories, match info, excerpts, links, and deprecation warnings.export function formatSearchResults(results, deprecationManager) { let output = `# Ember Documentation Search Results\n\n`; output += `Found ${results.length} result(s):\n\n`; results.forEach((result, index) => { output += `## ${index + 1}. ${result.title}`; // Add deprecation indicator if applicable if (result.deprecationInfo && result.deprecationInfo.status !== 'possibly-deprecated') { output += ` ${deprecationManager.generateWarning(result.title, 'short')}`; } output += `\n\n`; output += `**Category:** ${result.category}`; // Show match quality if (result.matchedTerms !== undefined && result.totalTerms !== undefined) { output += ` | **Match:** ${result.matchedTerms}/${result.totalTerms} terms`; } if (result.score !== undefined) { output += ` (relevance: ${result.score})`; } output += `\n\n`; // Add inline deprecation warning if applicable if (result.deprecationInfo && result.deprecationInfo.status !== 'possibly-deprecated') { output += `${deprecationManager.generateWarning(result.title, 'inline')}\n\n`; } output += `${result.excerpt}\n\n`; if (result.url) { output += `**Link:** ${result.url}\n\n`; } if (result.apiLink) { output += `**API Reference:** ${result.apiLink}\n\n`; } output += `---\n\n`; }); return output; }