@upstash/context7-mcp 1.0.0 → 1.0.1-canary-20251124144836

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.js DELETED
@@ -1,103 +0,0 @@
1
- #!/usr/bin/env node
2
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
- import { z } from "zod";
5
- import { fetchProjects, fetchLibraryDocumentation } from "./lib/api.js";
6
- import { formatProjectsList, rerankProjects } from "./lib/utils.js";
7
- // Create server instance
8
- const server = new McpServer({
9
- name: "Context7",
10
- description: "Retrieves documentation and code examples for software libraries.",
11
- version: "1.0.0",
12
- capabilities: {
13
- resources: {},
14
- tools: {},
15
- },
16
- });
17
- // Register Context7 tools
18
- server.tool("list-available-docs", "Lists all available library documentation from Context7. The library names can be used with 'get-library-documentation' to retrieve documentation.", {
19
- libraryName: z
20
- .string()
21
- .optional()
22
- .describe("Optional library name to search for and rerank results based on"),
23
- }, async ({ libraryName }) => {
24
- const projects = await fetchProjects();
25
- if (!projects) {
26
- return {
27
- content: [
28
- {
29
- type: "text",
30
- text: "Failed to retrieve library documentation data from Context7",
31
- },
32
- ],
33
- };
34
- }
35
- // Filter projects to only include those with state "finalized"
36
- const finalizedProjects = projects.filter((project) => project.version.state === "finalized");
37
- if (finalizedProjects.length === 0) {
38
- return {
39
- content: [
40
- {
41
- type: "text",
42
- text: "No finalized documentation libraries available",
43
- },
44
- ],
45
- };
46
- }
47
- // Rerank projects if a library name is provided
48
- const rankedProjects = libraryName
49
- ? rerankProjects(finalizedProjects, libraryName)
50
- : finalizedProjects;
51
- const projectsText = formatProjectsList(rankedProjects);
52
- return {
53
- content: [
54
- {
55
- type: "text",
56
- text: projectsText,
57
- },
58
- ],
59
- };
60
- });
61
- server.tool("get-library-documentation", "Retrieves documentation for a specific library from Context7. Use 'list-available-docs' first to see what's available.", {
62
- libraryName: z
63
- .string()
64
- .describe("Name of the library to retrieve documentation for (e.g., 'mongodb/docs', 'vercel/nextjs'). Must match exactly a library name from 'list-available-docs'."),
65
- topic: z
66
- .string()
67
- .optional()
68
- .describe("Specific topic within the library to focus the documentation on (e.g., 'hooks', 'routing')."),
69
- tokens: z
70
- .number()
71
- .min(5000)
72
- .optional()
73
- .describe("Maximum number of tokens of documentation to retrieve (default: 5000). Higher values provide more comprehensive documentation but use more context window."),
74
- }, async ({ libraryName, tokens = 5000, topic = "" }) => {
75
- const documentationText = await fetchLibraryDocumentation(libraryName, tokens, topic);
76
- if (!documentationText) {
77
- return {
78
- content: [
79
- {
80
- type: "text",
81
- text: "Documentation not found or not finalized for this library. Verify you've provided a valid library name exactly as listed by the 'list-available-docs' tool.",
82
- },
83
- ],
84
- };
85
- }
86
- return {
87
- content: [
88
- {
89
- type: "text",
90
- text: documentationText,
91
- },
92
- ],
93
- };
94
- });
95
- async function main() {
96
- const transport = new StdioServerTransport();
97
- await server.connect(transport);
98
- console.error("Context7 Documentation MCP Server running on stdio");
99
- }
100
- main().catch((error) => {
101
- console.error("Fatal error in main():", error);
102
- process.exit(1);
103
- });
package/build/lib/api.js DELETED
@@ -1,70 +0,0 @@
1
- const CONTEXT7_BASE_URL = "https://context7.com";
2
- /**
3
- * Fetches projects from the Context7 API
4
- * @returns Array of projects or null if the request fails
5
- */
6
- export async function fetchProjects() {
7
- try {
8
- const response = await fetch(`${CONTEXT7_BASE_URL}/api/projects`);
9
- if (!response.ok) {
10
- console.error(`Failed to fetch projects: ${response.status}`);
11
- return null;
12
- }
13
- return await response.json();
14
- }
15
- catch (error) {
16
- console.error("Error fetching projects:", error);
17
- return null;
18
- }
19
- }
20
- /**
21
- * Fetches documentation context for a specific library
22
- * @param libraryName The library name to fetch documentation for
23
- * @param tokens Number of tokens to retrieve (default: 5000)
24
- * @param topic Optional topic to rerank context for
25
- * @returns The documentation text or null if the request fails
26
- */
27
- export async function fetchLibraryDocumentation(libraryName, tokens = 5000, topic = "") {
28
- try {
29
- // if libraryName has a "/" as the first character, remove it
30
- if (libraryName.startsWith("/")) {
31
- libraryName = libraryName.slice(1);
32
- }
33
- // Handle folders parameter
34
- let basePath = libraryName;
35
- let folders = "";
36
- if (libraryName.includes("?folders=")) {
37
- const [path, foldersParam] = libraryName.split("?folders=");
38
- basePath = path;
39
- folders = foldersParam;
40
- }
41
- let contextURL = `${CONTEXT7_BASE_URL}/${basePath}/llms.txt`;
42
- const params = [];
43
- if (folders) {
44
- params.push(`folders=${encodeURIComponent(folders)}`);
45
- }
46
- if (tokens) {
47
- params.push(`tokens=${tokens}`);
48
- }
49
- if (topic) {
50
- params.push(`topic=${encodeURIComponent(topic)}`);
51
- }
52
- if (params.length > 0) {
53
- contextURL += `?${params.join("&")}`;
54
- }
55
- const response = await fetch(contextURL);
56
- if (!response.ok) {
57
- console.error(`Failed to fetch documentation: ${response.status}`);
58
- return null;
59
- }
60
- const text = await response.text();
61
- if (!text || text === "No content available" || text === "No context data available") {
62
- return null;
63
- }
64
- return text;
65
- }
66
- catch (error) {
67
- console.error("Error fetching library documentation:", error);
68
- return null;
69
- }
70
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,127 +0,0 @@
1
- /**
2
- * Format a project into a string representation
3
- * @param project Project to format
4
- * @returns Formatted project string
5
- */
6
- export function formatProject(project) {
7
- return `Title: ${project.settings.title}\nLibrary name: ${project.settings.project}\n`;
8
- }
9
- /**
10
- * Format a list of projects into a string representation
11
- * @param projects Projects to format
12
- * @returns Formatted projects string
13
- */
14
- export function formatProjectsList(projects) {
15
- const formattedProjects = projects.map(formatProject);
16
- return (formattedProjects.length +
17
- " available documentation libraries:\n\n" +
18
- formattedProjects.join("\n"));
19
- }
20
- /**
21
- * Rerank projects based on a search term
22
- * @param projects Projects to rerank
23
- * @param searchTerm Search term to rerank by
24
- * @returns Reranked projects
25
- */
26
- export function rerankProjects(projects, searchTerm) {
27
- if (!searchTerm)
28
- return projects;
29
- // Normalize the search term - remove special characters and convert to lowercase
30
- const normalizedSearchTerm = searchTerm.toLowerCase().replace(/[^\w\s]/g, "");
31
- return [...projects].sort((a, b) => {
32
- const aTitle = a.settings.title.toLowerCase();
33
- const aProject = a.settings.project.toLowerCase();
34
- const aProjectName = aProject.split("/").pop() || "";
35
- const aProjectPath = aProject.split("/").slice(0, -1).join("/");
36
- const bTitle = b.settings.title.toLowerCase();
37
- const bProject = b.settings.project.toLowerCase();
38
- const bProjectName = bProject.split("/").pop() || "";
39
- const bProjectPath = bProject.split("/").slice(0, -1).join("/");
40
- // Normalize project names for better matching - remove special characters
41
- const normalizedATitle = aTitle.replace(/[^\w\s]/g, "");
42
- const normalizedAProject = aProject.replace(/[^\w\s]/g, "");
43
- const normalizedAProjectName = aProjectName.replace(/[^\w\s]/g, "");
44
- const normalizedBTitle = bTitle.replace(/[^\w\s]/g, "");
45
- const normalizedBProject = bProject.replace(/[^\w\s]/g, "");
46
- const normalizedBProjectName = bProjectName.replace(/[^\w\s]/g, "");
47
- // Calculate match scores for better ranking
48
- const aScore = calculateMatchScore(normalizedSearchTerm, {
49
- original: {
50
- title: aTitle,
51
- project: aProject,
52
- projectName: aProjectName,
53
- projectPath: aProjectPath,
54
- },
55
- normalized: {
56
- title: normalizedATitle,
57
- project: normalizedAProject,
58
- projectName: normalizedAProjectName,
59
- },
60
- });
61
- const bScore = calculateMatchScore(normalizedSearchTerm, {
62
- original: {
63
- title: bTitle,
64
- project: bProject,
65
- projectName: bProjectName,
66
- projectPath: bProjectPath,
67
- },
68
- normalized: {
69
- title: normalizedBTitle,
70
- project: normalizedBProject,
71
- projectName: normalizedBProjectName,
72
- },
73
- });
74
- // Higher score first
75
- if (aScore !== bScore) {
76
- return bScore - aScore;
77
- }
78
- // Default to alphabetical by project name
79
- return aProject.localeCompare(bProject);
80
- });
81
- }
82
- /**
83
- * Calculate a match score for ranking
84
- * Higher score means better match
85
- */
86
- function calculateMatchScore(searchTerm, projectData) {
87
- const { original, normalized } = projectData;
88
- let score = 0;
89
- // Exact matches (highest priority)
90
- if (original.project === searchTerm ||
91
- original.title === searchTerm ||
92
- original.projectName === searchTerm) {
93
- score += 100;
94
- }
95
- // Normalized exact matches
96
- if (normalized.project === searchTerm ||
97
- normalized.title === searchTerm ||
98
- normalized.projectName === searchTerm) {
99
- score += 90;
100
- }
101
- // Starts with matches
102
- if (original.project.startsWith(searchTerm) ||
103
- original.title.startsWith(searchTerm) ||
104
- original.projectName.startsWith(searchTerm)) {
105
- score += 80;
106
- }
107
- // Normalized starts with matches
108
- if (normalized.project.startsWith(searchTerm) ||
109
- normalized.title.startsWith(searchTerm) ||
110
- normalized.projectName.startsWith(searchTerm)) {
111
- score += 70;
112
- }
113
- // Contains matches
114
- if (original.project.includes(searchTerm) ||
115
- original.title.includes(searchTerm) ||
116
- original.projectName.includes(searchTerm) ||
117
- original.projectPath.includes(searchTerm)) {
118
- score += 60;
119
- }
120
- // Normalized contains matches
121
- if (normalized.project.includes(searchTerm) ||
122
- normalized.title.includes(searchTerm) ||
123
- normalized.projectName.includes(searchTerm)) {
124
- score += 50;
125
- }
126
- return score;
127
- }