@upstash/context7-mcp
Version:
MCP server for Context7
110 lines (109 loc) • 4.62 kB
JavaScript
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;
}