revert: commit updated build

This commit is contained in:
Josh 2025-04-11 10:58:35 +02:00
parent 846464146f
commit 37f916367d
4 changed files with 0 additions and 301 deletions

View File

@ -1,103 +0,0 @@
#!/usr/bin/env node
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { fetchProjects, fetchLibraryDocumentation } from "./lib/api.js";
import { formatProjectsList, rerankProjects } from "./lib/utils.js";
// Create server instance
const server = new McpServer({
name: "Context7",
description: "Retrieves up-to-date documentation and code examples for npm packages.",
version: "1.0.0",
capabilities: {
resources: {},
tools: {},
},
});
// Register Context7 tools
server.tool("resolve-library-id", "Required first step: Resolves a general package name into a Context7-compatible library ID. Must be called before using 'get-library-docs' to retrieve a valid Context7-compatible library ID.", {
libraryName: z
.string()
.optional()
.describe("Optional library name to search for and rerank results based on."),
}, async ({ libraryName }) => {
const projects = await fetchProjects();
if (!projects) {
return {
content: [
{
type: "text",
text: "Failed to retrieve library documentation data from Context7",
},
],
};
}
// Filter projects to only include those with state "finalized"
const finalizedProjects = projects.filter((project) => project.version.state === "finalized");
if (finalizedProjects.length === 0) {
return {
content: [
{
type: "text",
text: "No finalized documentation libraries available",
},
],
};
}
// Rerank projects if a library name is provided
const rankedProjects = libraryName
? rerankProjects(finalizedProjects, libraryName)
: finalizedProjects;
const projectsText = formatProjectsList(rankedProjects);
return {
content: [
{
type: "text",
text: "Available libraries and their Context7-compatible library ID:\n\n" + projectsText,
},
],
};
});
server.tool("get-library-docs", "Fetches up-to-date documentation for a library. You must call 'resolve-library-id' first to obtain the exact Context7-compatible library ID required to use this tool.", {
context7CompatibleLibraryID: z
.string()
.describe("Exact Context7-compatible library ID (e.g., 'mongodb/docs', 'vercel/nextjs') retrieved from 'resolve-library-id'."),
topic: z
.string()
.optional()
.describe("Topic to focus documentation on (e.g., 'hooks', 'routing')."),
tokens: z
.number()
.min(5000)
.optional()
.describe("Maximum number of tokens of documentation to retrieve (default: 5000). Higher values provide more context but consume more tokens."),
}, async ({ context7CompatibleLibraryID, tokens = 5000, topic = "" }) => {
const documentationText = await fetchLibraryDocumentation(context7CompatibleLibraryID, tokens, topic);
if (!documentationText) {
return {
content: [
{
type: "text",
text: "Documentation not found or not finalized for this library. This might have happened because you used an invalid Context7-compatible library ID. To get a valid Context7-compatible library ID, use the 'resolve-library-id' with the package name you wish to retrieve documentation for.",
},
],
};
}
return {
content: [
{
type: "text",
text: documentationText,
},
],
};
});
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Context7 Documentation MCP Server running on stdio");
}
main().catch((error) => {
console.error("Fatal error in main():", error);
process.exit(1);
});

View File

@ -1,70 +0,0 @@
const CONTEXT7_BASE_URL = "https://context7.com";
/**
* Fetches projects from the Context7 API
* @returns Array of projects or null if the request fails
*/
export async function fetchProjects() {
try {
const response = await fetch(`${CONTEXT7_BASE_URL}/api/projects`);
if (!response.ok) {
console.error(`Failed to fetch projects: ${response.status}`);
return null;
}
return await response.json();
}
catch (error) {
console.error("Error fetching projects:", error);
return null;
}
}
/**
* Fetches documentation context for a specific library
* @param libraryName The library name to fetch documentation for
* @param tokens Number of tokens to retrieve (default: 5000)
* @param topic Optional topic to rerank context for
* @returns The documentation text or null if the request fails
*/
export async function fetchLibraryDocumentation(libraryName, tokens = 5000, topic = "") {
try {
// if libraryName has a "/" as the first character, remove it
if (libraryName.startsWith("/")) {
libraryName = libraryName.slice(1);
}
// Handle folders parameter
let basePath = libraryName;
let folders = "";
if (libraryName.includes("?folders=")) {
const [path, foldersParam] = libraryName.split("?folders=");
basePath = path;
folders = foldersParam;
}
let contextURL = `${CONTEXT7_BASE_URL}/${basePath}/llms.txt`;
const params = [];
if (folders) {
params.push(`folders=${encodeURIComponent(folders)}`);
}
if (tokens) {
params.push(`tokens=${tokens}`);
}
if (topic) {
params.push(`topic=${encodeURIComponent(topic)}`);
}
if (params.length > 0) {
contextURL += `?${params.join("&")}`;
}
const response = await fetch(contextURL);
if (!response.ok) {
console.error(`Failed to fetch documentation: ${response.status}`);
return null;
}
const text = await response.text();
if (!text || text === "No content available" || text === "No context data available") {
return null;
}
return text;
}
catch (error) {
console.error("Error fetching library documentation:", error);
return null;
}
}

View File

@ -1 +0,0 @@
export {};

View File

@ -1,127 +0,0 @@
/**
* Format a project into a string representation
* @param project Project to format
* @returns Formatted project string
*/
export function formatProject(project) {
return `Title: ${project.settings.title}\nContext7-compatible library ID: ${project.settings.project}\n`;
}
/**
* Format a list of projects into a string representation
* @param projects Projects to format
* @returns Formatted projects string
*/
export function formatProjectsList(projects) {
const formattedProjects = projects.map(formatProject);
return (formattedProjects.length +
" available documentation libraries:\n\n" +
formattedProjects.join("\n"));
}
/**
* Rerank projects based on a search term
* @param projects Projects to rerank
* @param searchTerm Search term to rerank by
* @returns Reranked projects
*/
export function rerankProjects(projects, searchTerm) {
if (!searchTerm)
return projects;
// Normalize the search term - remove special characters and convert to lowercase
const normalizedSearchTerm = searchTerm.toLowerCase().replace(/[^\w\s]/g, "");
return [...projects].sort((a, b) => {
const aTitle = a.settings.title.toLowerCase();
const aProject = a.settings.project.toLowerCase();
const aProjectName = aProject.split("/").pop() || "";
const aProjectPath = aProject.split("/").slice(0, -1).join("/");
const bTitle = b.settings.title.toLowerCase();
const bProject = b.settings.project.toLowerCase();
const bProjectName = bProject.split("/").pop() || "";
const bProjectPath = bProject.split("/").slice(0, -1).join("/");
// Normalize project names for better matching - remove special characters
const normalizedATitle = aTitle.replace(/[^\w\s]/g, "");
const normalizedAProject = aProject.replace(/[^\w\s]/g, "");
const normalizedAProjectName = aProjectName.replace(/[^\w\s]/g, "");
const normalizedBTitle = bTitle.replace(/[^\w\s]/g, "");
const normalizedBProject = bProject.replace(/[^\w\s]/g, "");
const normalizedBProjectName = bProjectName.replace(/[^\w\s]/g, "");
// Calculate match scores for better ranking
const aScore = calculateMatchScore(normalizedSearchTerm, {
original: {
title: aTitle,
project: aProject,
projectName: aProjectName,
projectPath: aProjectPath,
},
normalized: {
title: normalizedATitle,
project: normalizedAProject,
projectName: normalizedAProjectName,
},
});
const bScore = calculateMatchScore(normalizedSearchTerm, {
original: {
title: bTitle,
project: bProject,
projectName: bProjectName,
projectPath: bProjectPath,
},
normalized: {
title: normalizedBTitle,
project: normalizedBProject,
projectName: normalizedBProjectName,
},
});
// Higher score first
if (aScore !== bScore) {
return bScore - aScore;
}
// Default to alphabetical by project name
return aProject.localeCompare(bProject);
});
}
/**
* Calculate a match score for ranking
* Higher score means better match
*/
function calculateMatchScore(searchTerm, projectData) {
const { original, normalized } = projectData;
let score = 0;
// Exact matches (highest priority)
if (original.project === searchTerm ||
original.title === searchTerm ||
original.projectName === searchTerm) {
score += 100;
}
// Normalized exact matches
if (normalized.project === searchTerm ||
normalized.title === searchTerm ||
normalized.projectName === searchTerm) {
score += 90;
}
// Starts with matches
if (original.project.startsWith(searchTerm) ||
original.title.startsWith(searchTerm) ||
original.projectName.startsWith(searchTerm)) {
score += 80;
}
// Normalized starts with matches
if (normalized.project.startsWith(searchTerm) ||
normalized.title.startsWith(searchTerm) ||
normalized.projectName.startsWith(searchTerm)) {
score += 70;
}
// Contains matches
if (original.project.includes(searchTerm) ||
original.title.includes(searchTerm) ||
original.projectName.includes(searchTerm) ||
original.projectPath.includes(searchTerm)) {
score += 60;
}
// Normalized contains matches
if (normalized.project.includes(searchTerm) ||
normalized.title.includes(searchTerm) ||
normalized.projectName.includes(searchTerm)) {
score += 50;
}
return score;
}