Skip to main content
Glama
searchDocs.ts5.41 kB
import { type SearchResponseRevirApiDocsCom, type SearchResponseRvtDocsCom, type SearchResult, SearchResultTypes, } from "../types/index.ts"; import { z } from "zod"; export async function searchWrapper( query: string, year: number, max: number, types: ReadonlyArray<(typeof SearchResultTypes)[number]> = SearchResultTypes, ): Promise<SearchResult[]> { const results1 = await searchRvtDocsCom(query, year, max * 2, types); const results2 = await searchRevitApiDocsCom(query, year, max * 2, types); const allResults = [...results1, ...results2]; const dedupedResults = dedupeByUrl(allResults); const sortedResults = sortByType(dedupedResults); return sortedResults.slice(0, max); } function dedupeByUrl(results: SearchResult[]): SearchResult[] { const urlMap = new Map<string, SearchResult>(); const countEmpty = (obj: SearchResult) => Object.values(obj).filter((v) => v === "").length; for (const result of results) { const url = result.url; if (!urlMap.has(url)) { urlMap.set(url, result); } else { const existing = urlMap.get(url); if (existing && countEmpty(result) < countEmpty(existing)) { urlMap.set(url, result); } } } return Array.from(urlMap.values()); } function sortByType(results: SearchResult[]): SearchResult[] { const typeOrder = [ "Class", "Methods", "Properties", "Constructor", ]; return results.sort((a, b) => { const aIndex = typeOrder.indexOf(a.type); const bIndex = typeOrder.indexOf(b.type); if (aIndex === -1 && bIndex === -1) return 0; if (aIndex === -1) return 1; if (bIndex === -1) return -1; return aIndex - bIndex; }); } /** * Searches Revit API documentation using the rvtdocs.com search endpoint */ export async function searchRvtDocsCom( query: string, year: number, maxResults: number, types: ReadonlyArray<(typeof SearchResultTypes)[number]> = SearchResultTypes, ): Promise<SearchResult[]> { try { // Using the rvtdocs.com search API endpoint const searchUrl = "https://rvtdocs.com/search/api/search"; const requestBody = { query: query, current_version: year.toString(), include_description: false, }; // Make the search request const response = await fetch(searchUrl, { method: "POST", body: JSON.stringify(requestBody), }); if (!response.ok) { throw new Error( `Search request failed: ${response.status} ${response.statusText}`, ); } const data = await response.json() as SearchResponseRvtDocsCom; const results: SearchResult[] = []; // Parse the response based on the expected format if ( data.current_version_results && Array.isArray(data.current_version_results) ) { for (const item of data.current_version_results.slice(0, maxResults)) { results.push({ title: item.title || "", description: (item.description || "").replace( /^Description:\s*/i, "", ), namespace: (item.namespace || "").replace(/^Namespace:\s*/i, ""), type: item.type || "", url: item.url || "", }); } } return results.filter((r) => types.includes(r.type as (typeof SearchResultTypes)[number]) ); } catch (error) { console.error("Error searching Revit API docs using rvtdocs.com:", error); throw error; } } /** * Searches Revit API documentation using the www.revitapidocs.com search endpoint * This is necessary to keep because it makes "Properties" and "Methods" pages available */ export async function searchRevitApiDocsCom( query: string, year: number, maxResults: number, types: ReadonlyArray<(typeof SearchResultTypes)[number]> = SearchResultTypes, ): Promise<SearchResult[]> { try { // Construct the search URL with current timestamp const timestamp = Date.now(); const searchUrl = `https://ac.cnstrc.com/autocomplete/${ encodeURIComponent(query) }?query=${ encodeURIComponent(query) }&autocomplete_key=key_yyAC1mb0cTgZTwSo&c=ciojs-2.1233.4&num_results=${maxResults}&i=d705c917-8e5a-491f-8bc4-9b43e78de48c&s=10&_dt=${timestamp}`; // Make the search request const response = await fetch(searchUrl); if (!response.ok) { throw new Error( `Search request failed: ${response.status} ${response.statusText}`, ); } const data = await response.json() as SearchResponseRevirApiDocsCom; const results: SearchResult[] = []; if (data.sections?.Products) { for (const item of data.sections.Products) { if (TypeFromTitle.parse(item.value) !== "Members") { results.push({ title: item.value, url: `/${year}/${item.data.url.split(".")[0]}`, type: TypeFromTitle.parse(item.value), }); } } } return results.filter((r) => types.includes(r.type as (typeof SearchResultTypes)[number]) ); } catch (error) { console.error( "Error searching Revit API docs using revitapidocs.com:", error, ); throw error; } } const TypeFromTitle = z.string().transform((title) => { const parts = title.trim().split(/\s+/); const lastPart = parts[parts.length - 1]; for (const t of [...SearchResultTypes, "Members"]) { if (lastPart === t) return t; } return "Unknown"; });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/kaitpw/Rvt_Docs_MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server