UNPKG

@upstash/context7-mcp

Version:
110 lines (109 loc) • 4.62 kB
import { CLIENT_INFO_META_KEY } from "@modelcontextprotocol/server"; /** * Maps numeric source reputation score to an interpretable label for LLM consumption. * * @returns One of: "High", "Medium", "Low", or "Unknown" */ function getSourceReputationLabel(sourceReputation) { if (sourceReputation === undefined || sourceReputation < 0) return "Unknown"; if (sourceReputation >= 7) return "High"; if (sourceReputation >= 4) return "Medium"; return "Low"; } /** * Libraries built from a Git repository have source that query-code can search. * This is a client-side guess from the ID and the branch: the server decides * with sourceRepo() in its /api/v2/code route and answers 422 "no_source". * Keep the rule the same as hasSourceCode in packages/cli/src/utils/api.ts. */ export function hasSourceCode(result) { return !/^\/(websites|llmstxt)\//.test(result.id) && !result.branch?.startsWith("_"); } /** * Formats a search result into a human-readable string representation. * Only shows code snippet count and GitHub stars when available (not equal to -1). * * @param result The SearchResult object to format * @returns A formatted string with library information */ export function formatSearchResult(result, options = {}) { // Always include these basic details const formattedResult = [ `- Title: ${result.title}`, `- Context7-compatible library ID: ${result.id}`, `- Description: ${result.description}`, ]; // Only add code snippets count if it's a valid value if (result.totalSnippets !== -1 && result.totalSnippets !== undefined) { formattedResult.push(`- Code Snippets: ${result.totalSnippets}`); } // Always add categorized source reputation const reputationLabel = getSourceReputationLabel(result.trustScore); formattedResult.push(`- Source Reputation: ${reputationLabel}`); // Only add benchmark score if it's a valid value if (result.benchmarkScore !== undefined && result.benchmarkScore > 0) { formattedResult.push(`- Benchmark Score: ${result.benchmarkScore}`); } // Only add versions if it's a valid value if (result.versions !== undefined && result.versions.length > 0) { formattedResult.push(`- Versions: ${result.versions.join(", ")}`); } if (result.source) { formattedResult.push(`- Source: ${result.source}`); } if (options.sourceCode && hasSourceCode(result)) { formattedResult.push(`- Source code: searchable with query-code`); } // Join all parts with newlines return formattedResult.join("\n"); } /** * Formats a search response into a human-readable string representation. * Each result is formatted using formatSearchResult. * * @param searchResponse The SearchResponse object to format * @returns A formatted string with search results */ export function formatSearchResults(searchResponse, options = {}) { const parts = []; if (searchResponse.searchFilterApplied) { parts.push("**Note:** Your results only include libraries matching your teamspace's library filters. To adjust quality thresholds or blocked libraries, update your filters at https://context7.com/dashboard?tab=policies"); } if (!searchResponse.results || searchResponse.results.length === 0) { parts.push("No documentation libraries found matching your query."); } else { const formattedResults = searchResponse.results.map((result) => formatSearchResult(result, options)); parts.push(formattedResults.join("\n----------\n")); } return parts.join("\n\n"); } /** * Reads the client name/version that modern (2026-07-28) clients attach to * every request's `_meta` envelope. Legacy (2025) clients declare it once in * the initialize handshake instead. * * The envelope is untyped (`RequestMetaEnvelope = {}`) as of SDK 2.0.0, so * the shape probed here is not compile-checked against the SDK — * utils.test.ts locks it so an SDK bump that changes it fails loudly. */ export function envelopeClientInfo(envelope) { const info = envelope?.[CLIENT_INFO_META_KEY]; return info ? { ide: info.name, version: info.version } : undefined; } /** * Extract client info from User-Agent header. * Parses formats like "Cursor/2.2.44 (darwin arm64)" or "claude-code/2.0.71" */ export function extractClientInfoFromUserAgent(userAgent) { if (!userAgent) return undefined; const match = userAgent.match(/^([^\/\s]+)\/([^\s(]+)/); if (match) { return { ide: match[1], version: match[2] }; } return undefined; }