@upstash/context7-mcp 1.0.3 → 1.0.4

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/README.md CHANGED
@@ -52,7 +52,7 @@ Paste this into your Cursor `~/.cursor/mcp.json` file. See [Cursor MCP docs](htt
52
52
  "mcpServers": {
53
53
  "context7": {
54
54
  "command": "npx",
55
- "args": ["-y", "@upstash/context7-mcp"]
55
+ "args": ["-y", "@upstash/context7-mcp@latest"]
56
56
  }
57
57
  }
58
58
  }
@@ -67,7 +67,23 @@ Add this to your Windsurf MCP config file. See [Windsurf MCP docs](https://docs.
67
67
  "mcpServers": {
68
68
  "context7": {
69
69
  "command": "npx",
70
- "args": ["-y", "@upstash/context7-mcp"]
70
+ "args": ["-y", "@upstash/context7-mcp@latest"]
71
+ }
72
+ }
73
+ }
74
+ ```
75
+
76
+ ### Install in VSCode
77
+
78
+ Add this to your VSCode MCP config file. See [VSCode MCP docs](https://code.visualstudio.com/docs/copilot/chat/mcp-servers) for more info.
79
+
80
+ ```json
81
+ {
82
+ "servers": {
83
+ "Context7": {
84
+ "type": "stdio",
85
+ "command": "npx",
86
+ "args": ["-y", "@upstash/context7-mcp@latest"]
71
87
  }
72
88
  }
73
89
  }
@@ -112,7 +128,7 @@ bun run build
112
128
  ### Testing with MCP Inspector
113
129
 
114
130
  ```bash
115
- npx -y @modelcontextprotocol/inspector npx @upstash/context7-mcp
131
+ npx -y @modelcontextprotocol/inspector npx @upstash/context7-mcp@latest
116
132
  ```
117
133
 
118
134
  ## License
package/dist/index.js CHANGED
@@ -2,13 +2,14 @@
2
2
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
3
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
4
  import { z } from "zod";
5
- import { fetchProjects, fetchLibraryDocumentation } from "./lib/api.js";
6
- import { formatProjectsList, rerankProjects } from "./lib/utils.js";
5
+ import { searchLibraries, fetchLibraryDocumentation } from "./lib/api.js";
6
+ import { formatSearchResults } from "./lib/utils.js";
7
+ const DEFAULT_MINIMUM_TOKENS = 5000;
7
8
  // Create server instance
8
9
  const server = new McpServer({
9
10
  name: "Context7",
10
11
  description: "Retrieves up-to-date documentation and code examples for any library.",
11
- version: "1.0.0",
12
+ version: "1.0.4",
12
13
  capabilities: {
13
14
  resources: {},
14
15
  tools: {},
@@ -21,8 +22,8 @@ server.tool("resolve-library-id", "Required first step: Resolves a general packa
21
22
  .optional()
22
23
  .describe("Optional library name to search for and rerank results based on."),
23
24
  }, async ({ libraryName }) => {
24
- const projects = await fetchProjects();
25
- if (!projects) {
25
+ const searchResponse = await searchLibraries(libraryName || "");
26
+ if (!searchResponse || !searchResponse.results) {
26
27
  return {
27
28
  content: [
28
29
  {
@@ -32,28 +33,22 @@ server.tool("resolve-library-id", "Required first step: Resolves a general packa
32
33
  ],
33
34
  };
34
35
  }
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) {
36
+ if (searchResponse.results.length === 0) {
38
37
  return {
39
38
  content: [
40
39
  {
41
40
  type: "text",
42
- text: "No finalized documentation libraries available",
41
+ text: "No documentation libraries available",
43
42
  },
44
43
  ],
45
44
  };
46
45
  }
47
- // Rerank projects if a library name is provided
48
- const rankedProjects = libraryName
49
- ? rerankProjects(finalizedProjects, libraryName)
50
- : finalizedProjects;
51
- const projectsText = formatProjectsList(rankedProjects);
46
+ const resultsText = formatSearchResults(searchResponse);
52
47
  return {
53
48
  content: [
54
49
  {
55
50
  type: "text",
56
- text: "Available libraries and their Context7-compatible library ID:\n\n" + projectsText,
51
+ text: "Available libraries and their Context7-compatible library IDs:\n\n" + resultsText,
57
52
  },
58
53
  ],
59
54
  };
@@ -68,11 +63,23 @@ server.tool("get-library-docs", "Fetches up-to-date documentation for a library.
68
63
  .describe("Topic to focus documentation on (e.g., 'hooks', 'routing')."),
69
64
  tokens: z
70
65
  .number()
71
- .min(5000)
66
+ .min(DEFAULT_MINIMUM_TOKENS)
72
67
  .optional()
73
- .describe("Maximum number of tokens of documentation to retrieve (default: 5000). Higher values provide more context but consume more tokens."),
74
- }, async ({ context7CompatibleLibraryID, tokens = 5000, topic = "" }) => {
75
- const documentationText = await fetchLibraryDocumentation(context7CompatibleLibraryID, tokens, topic);
68
+ .describe(`Maximum number of tokens of documentation to retrieve (default: ${DEFAULT_MINIMUM_TOKENS}). Higher values provide more context but consume more tokens.`),
69
+ }, async ({ context7CompatibleLibraryID, tokens = DEFAULT_MINIMUM_TOKENS, topic = "" }) => {
70
+ // Extract folders parameter if present in the ID
71
+ let folders = "";
72
+ let libraryId = context7CompatibleLibraryID;
73
+ if (context7CompatibleLibraryID.includes("?folders=")) {
74
+ const [id, foldersParam] = context7CompatibleLibraryID.split("?folders=");
75
+ libraryId = id;
76
+ folders = foldersParam;
77
+ }
78
+ const documentationText = await fetchLibraryDocumentation(libraryId, {
79
+ tokens,
80
+ topic,
81
+ folders,
82
+ });
76
83
  if (!documentationText) {
77
84
  return {
78
85
  content: [
package/dist/lib/api.js CHANGED
@@ -1,51 +1,46 @@
1
- const CONTEXT7_BASE_URL = "https://context7.com";
1
+ const CONTEXT7_API_BASE_URL = "https://context7.com/api";
2
+ const DEFAULT_TYPE = "txt";
2
3
  /**
3
- * Fetches projects from the Context7 API
4
- * @returns Array of projects or null if the request fails
4
+ * Searches for libraries matching the given query
5
+ * @param query The search query
6
+ * @returns Search results or null if the request fails
5
7
  */
6
- export async function fetchProjects() {
8
+ export async function searchLibraries(query) {
7
9
  try {
8
- const response = await fetch(`${CONTEXT7_BASE_URL}/api/libraries`);
10
+ const url = new URL(`${CONTEXT7_API_BASE_URL}/v1/search`);
11
+ url.searchParams.set("query", query);
12
+ const response = await fetch(url);
9
13
  if (!response.ok) {
10
- console.error(`Failed to fetch projects: ${response.status}`);
14
+ console.error(`Failed to search libraries: ${response.status}`);
11
15
  return null;
12
16
  }
13
17
  return await response.json();
14
18
  }
15
19
  catch (error) {
16
- console.error("Error fetching projects:", error);
20
+ console.error("Error searching libraries:", error);
17
21
  return null;
18
22
  }
19
23
  }
20
24
  /**
21
25
  * 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
26
+ * @param libraryId The library ID to fetch documentation for
27
+ * @param options Options for the request
25
28
  * @returns The documentation text or null if the request fails
26
29
  */
27
- export async function fetchLibraryDocumentation(libraryName, tokens = 5000, topic = "") {
30
+ export async function fetchLibraryDocumentation(libraryId, options = {}) {
28
31
  try {
29
- // if libraryName has a "/" as the first character, remove it
30
- if (libraryName.startsWith("/")) {
31
- libraryName = libraryName.slice(1);
32
+ if (libraryId.startsWith("/")) {
33
+ libraryId = libraryId.slice(1);
32
34
  }
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
- const contextURL = new URL(`${CONTEXT7_BASE_URL}/${basePath}/llms.txt`);
42
- if (folders)
43
- contextURL.searchParams.set("folders", folders);
44
- if (tokens)
45
- contextURL.searchParams.set("tokens", tokens.toString());
46
- if (topic)
47
- contextURL.searchParams.set("topic", topic);
48
- const response = await fetch(contextURL, {
35
+ const url = new URL(`${CONTEXT7_API_BASE_URL}/v1/${libraryId}`);
36
+ if (options.tokens)
37
+ url.searchParams.set("tokens", options.tokens.toString());
38
+ if (options.topic)
39
+ url.searchParams.set("topic", options.topic);
40
+ if (options.folders)
41
+ url.searchParams.set("folders", options.folders);
42
+ url.searchParams.set("type", DEFAULT_TYPE);
43
+ const response = await fetch(url, {
49
44
  headers: {
50
45
  "X-Context7-Source": "mcp-server",
51
46
  },
package/dist/lib/utils.js CHANGED
@@ -1,127 +1,20 @@
1
1
  /**
2
- * Format a project into a string representation
3
- * @param project Project to format
4
- * @returns Formatted project string
2
+ * Format a search result into a string representation
3
+ * @param result SearchResult to format
4
+ * @returns Formatted search result string
5
5
  */
6
- export function formatProject(project) {
7
- return `Title: ${project.settings.title}\nContext7-compatible library ID: ${project.settings.project}\n`;
6
+ export function formatSearchResult(result) {
7
+ return `Title: ${result.title}\n\nContext7-compatible library ID: ${result.id}`;
8
8
  }
9
9
  /**
10
- * Format a list of projects into a string representation
11
- * @param projects Projects to format
12
- * @returns Formatted projects string
10
+ * Format search results into a string representation
11
+ * @param searchResponse The search response to format
12
+ * @returns Formatted search results string
13
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;
14
+ export function formatSearchResults(searchResponse) {
15
+ if (!searchResponse.results || searchResponse.results.length === 0) {
16
+ return "No documentation libraries found matching your query.";
125
17
  }
126
- return score;
18
+ const formattedResults = searchResponse.results.map(formatSearchResult);
19
+ return formattedResults.join("\n\n");
127
20
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@upstash/context7-mcp",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "MCP server for Context7",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",