@upstash/context7-mcp 1.0.3 → 1.0.5

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
@@ -1,4 +1,5 @@
1
1
  # Context7 MCP - Up-to-date Docs For Any Cursor Prompt
2
+ [![Website](https://img.shields.io/badge/Website-context7.com-blue)](https://context7.com) [![smithery badge](https://smithery.ai/badge/@upstash/context7-mcp)](https://smithery.ai/server/@upstash/context7-mcp)
2
3
 
3
4
  ## ❌ Without Context7
4
5
 
@@ -26,7 +27,7 @@ How do I invalidate a query in React Query? use context7
26
27
  How do I protect a route with NextAuth? use context7
27
28
  ```
28
29
 
29
- Context7 fetches up-to-date documentation and working code examples right into your LLMs context.
30
+ Context7 fetches up-to-date documentation and working code examples right into your LLM's context.
30
31
 
31
32
  - 1️⃣ Ask your question naturally
32
33
  - 2️⃣ Tell the LLM to `use context7`
@@ -41,23 +42,63 @@ No tab-switching, no hallucinated APIs that don't exist, no outdated code genera
41
42
  - Node.js >= v18.0.0
42
43
  - Cursor, Windsurf, Claude Desktop or another MCP Client
43
44
 
45
+ ### Installing via Smithery
46
+
47
+ To install Context7 MCP Server for Claude Desktop automatically via [Smithery](https://smithery.ai/server/@upstash/context7-mcp):
48
+
49
+ ```bash
50
+ npx -y @smithery/cli install @upstash/context7-mcp --client claude
51
+ ```
52
+
44
53
  ### Install in Cursor
45
54
 
46
55
  Go to: `Settings` -> `Cursor Settings` -> `MCP` -> `Add new global MCP server`
47
56
 
48
- Paste this into your Cursor `~/.cursor/mcp.json` file. See [Cursor MCP docs](https://docs.cursor.com/context/model-context-protocol) for more info.
57
+ Pasting the following configuration into your Cursor `~/.cursor/mcp.json` file is the recommended approach. See [Cursor MCP docs](https://docs.cursor.com/context/model-context-protocol) for more info.
49
58
 
50
59
  ```json
51
60
  {
52
61
  "mcpServers": {
53
62
  "context7": {
54
63
  "command": "npx",
55
- "args": ["-y", "@upstash/context7-mcp"]
64
+ "args": ["-y", "@upstash/context7-mcp@latest"]
56
65
  }
57
66
  }
58
67
  }
59
68
  ```
60
69
 
70
+ <details>
71
+ <summary>Alternative: Use Bun</summary>
72
+
73
+ ```json
74
+ {
75
+ "mcpServers": {
76
+ "context7": {
77
+ "command": "bunx",
78
+ "args": ["-y", "@upstash/context7-mcp@latest"]
79
+ }
80
+ }
81
+ }
82
+ ```
83
+
84
+ </details>
85
+
86
+ <details>
87
+ <summary>Alternative: Use Deno</summary>
88
+
89
+ ```json
90
+ {
91
+ "mcpServers": {
92
+ "context7": {
93
+ "command": "deno",
94
+ "args": ["run", "--allow-net", "npm:@upstash/context7-mcp"]
95
+ }
96
+ }
97
+ }
98
+ ```
99
+
100
+ </details>
101
+
61
102
  ### Install in Windsurf
62
103
 
63
104
  Add this to your Windsurf MCP config file. See [Windsurf MCP docs](https://docs.windsurf.com/windsurf/mcp) for more info.
@@ -67,7 +108,46 @@ Add this to your Windsurf MCP config file. See [Windsurf MCP docs](https://docs.
67
108
  "mcpServers": {
68
109
  "context7": {
69
110
  "command": "npx",
70
- "args": ["-y", "@upstash/context7-mcp"]
111
+ "args": ["-y", "@upstash/context7-mcp@latest"]
112
+ }
113
+ }
114
+ }
115
+ ```
116
+
117
+ ### Install in VSCode
118
+
119
+ Add this to your VSCode MCP config file. See [VSCode MCP docs](https://code.visualstudio.com/docs/copilot/chat/mcp-servers) for more info.
120
+
121
+ ```json
122
+ {
123
+ "servers": {
124
+ "Context7": {
125
+ "type": "stdio",
126
+ "command": "npx",
127
+ "args": ["-y", "@upstash/context7-mcp@latest"]
128
+ }
129
+ }
130
+ }
131
+ ```
132
+
133
+ ### Install in Claude Code
134
+
135
+ Run this command. See [Claude Code MCP docs](https://docs.anthropic.com/en/docs/agents-and-tools/claude-code/tutorials#set-up-model-context-protocol-mcp) for more info.
136
+
137
+ ```sh
138
+ claude mcp add context7 -- npx -y @upstash/context7-mcp@latest
139
+ ```
140
+
141
+ ### Install in Claude Desktop
142
+
143
+ Add this to your Claude Desktop `claude_desktop_config.json` file. See [Claude Desktop MCP docs](https://modelcontextprotocol.io/quickstart/user) for more info.
144
+
145
+ ```json
146
+ {
147
+ "mcpServers": {
148
+ "Context7": {
149
+ "command": "npx",
150
+ "args": ["-y", "@upstash/context7-mcp@latest"]
71
151
  }
72
152
  }
73
153
  }
@@ -112,9 +192,28 @@ bun run build
112
192
  ### Testing with MCP Inspector
113
193
 
114
194
  ```bash
115
- npx -y @modelcontextprotocol/inspector npx @upstash/context7-mcp
195
+ npx -y @modelcontextprotocol/inspector npx @upstash/context7-mcp@latest
196
+ ```
197
+
198
+ ## Troubleshooting
199
+
200
+ ### ERR_MODULE_NOT_FOUND
201
+
202
+ If you see this error, try using `bunx` instead of `npx`.
203
+
204
+ ```json
205
+ {
206
+ "mcpServers": {
207
+ "context7": {
208
+ "command": "bunx",
209
+ "args": ["-y", "@upstash/context7-mcp@latest"]
210
+ }
211
+ }
212
+ }
116
213
  ```
117
214
 
215
+ This often resolves module resolution issues, especially in environments where `npx` does not properly install or resolve packages.
216
+
118
217
  ## License
119
218
 
120
219
  MIT
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: {},
@@ -18,11 +19,10 @@ const server = new McpServer({
18
19
  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.", {
19
20
  libraryName: z
20
21
  .string()
21
- .optional()
22
- .describe("Optional library name to search for and rerank results based on."),
22
+ .describe("Library name to search for and retrieve a Context7-compatible library ID."),
23
23
  }, async ({ libraryName }) => {
24
- const projects = await fetchProjects();
25
- if (!projects) {
24
+ const searchResponse = await searchLibraries(libraryName);
25
+ if (!searchResponse || !searchResponse.results) {
26
26
  return {
27
27
  content: [
28
28
  {
@@ -32,28 +32,22 @@ server.tool("resolve-library-id", "Required first step: Resolves a general packa
32
32
  ],
33
33
  };
34
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) {
35
+ if (searchResponse.results.length === 0) {
38
36
  return {
39
37
  content: [
40
38
  {
41
39
  type: "text",
42
- text: "No finalized documentation libraries available",
40
+ text: "No documentation libraries available",
43
41
  },
44
42
  ],
45
43
  };
46
44
  }
47
- // Rerank projects if a library name is provided
48
- const rankedProjects = libraryName
49
- ? rerankProjects(finalizedProjects, libraryName)
50
- : finalizedProjects;
51
- const projectsText = formatProjectsList(rankedProjects);
45
+ const resultsText = formatSearchResults(searchResponse);
52
46
  return {
53
47
  content: [
54
48
  {
55
49
  type: "text",
56
- text: "Available libraries and their Context7-compatible library ID:\n\n" + projectsText,
50
+ text: "Available libraries and their Context7-compatible library IDs:\n\n" + resultsText,
57
51
  },
58
52
  ],
59
53
  };
@@ -67,12 +61,26 @@ server.tool("get-library-docs", "Fetches up-to-date documentation for a library.
67
61
  .optional()
68
62
  .describe("Topic to focus documentation on (e.g., 'hooks', 'routing')."),
69
63
  tokens: z
70
- .number()
71
- .min(5000)
64
+ .preprocess((val) => (typeof val === "string" ? Number(val) : val), z.number())
65
+ .refine((val) => typeof val === "number" && val >= DEFAULT_MINIMUM_TOKENS, {
66
+ message: `Must be a number >= ${DEFAULT_MINIMUM_TOKENS}`,
67
+ })
72
68
  .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);
69
+ .describe(`Maximum number of tokens of documentation to retrieve (default: ${DEFAULT_MINIMUM_TOKENS}). Higher values provide more context but consume more tokens.`),
70
+ }, async ({ context7CompatibleLibraryID, tokens = DEFAULT_MINIMUM_TOKENS, topic = "" }) => {
71
+ // Extract folders parameter if present in the ID
72
+ let folders = "";
73
+ let libraryId = context7CompatibleLibraryID;
74
+ if (context7CompatibleLibraryID.includes("?folders=")) {
75
+ const [id, foldersParam] = context7CompatibleLibraryID.split("?folders=");
76
+ libraryId = id;
77
+ folders = foldersParam;
78
+ }
79
+ const documentationText = await fetchLibraryDocumentation(libraryId, {
80
+ tokens,
81
+ topic,
82
+ folders,
83
+ });
76
84
  if (!documentationText) {
77
85
  return {
78
86
  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}\n\nDescription: ${result.description}`;
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--------------------\n");
127
20
  }
package/package.json CHANGED
@@ -1,44 +1 @@
1
- {
2
- "name": "@upstash/context7-mcp",
3
- "version": "1.0.3",
4
- "description": "MCP server for Context7",
5
- "scripts": {
6
- "test": "echo \"Error: no test specified\" && exit 1",
7
- "build": "tsc && chmod 755 dist/index.js",
8
- "format": "prettier --write .",
9
- "lint": "eslint \"**/*.{js,ts,tsx}\" --fix"
10
- },
11
- "repository": {
12
- "type": "git",
13
- "url": "git+https://github.com/upstash/context7-mcp.git"
14
- },
15
- "keywords": [],
16
- "author": "",
17
- "license": "MIT",
18
- "type": "module",
19
- "bin": {
20
- "context7-mcp": "dist/index.js"
21
- },
22
- "files": [
23
- "dist"
24
- ],
25
- "bugs": {
26
- "url": "https://github.com/upstash/context7-mcp/issues"
27
- },
28
- "homepage": "https://github.com/upstash/context7-mcp#readme",
29
- "dependencies": {
30
- "@modelcontextprotocol/sdk": "^1.8.0",
31
- "zod": "^3.24.2"
32
- },
33
- "devDependencies": {
34
- "@types/node": "^22.13.14",
35
- "@typescript-eslint/eslint-plugin": "^8.28.0",
36
- "@typescript-eslint/parser": "^8.28.0",
37
- "eslint": "^9.23.0",
38
- "eslint-config-prettier": "^10.1.1",
39
- "eslint-plugin-prettier": "^5.2.5",
40
- "prettier": "^3.5.3",
41
- "typescript": "^5.8.2",
42
- "typescript-eslint": "^8.28.0"
43
- }
44
- }
1
+ {"name":"@upstash/context7-mcp","version":"v1.0.5","description":"MCP server for Context7","scripts":{"test":"echo \"Error: no test specified\" && exit 1","build":"tsc && chmod 755 dist/index.js","format":"prettier --write .","lint":"eslint \"**/*.{js,ts,tsx}\" --fix"},"repository":{"type":"git","url":"git+https://github.com/upstash/context7-mcp.git"},"keywords":["modelcontextprotocol","mcp","context7"],"author":"abdush","license":"MIT","type":"module","bin":{"context7-mcp":"dist/index.js"},"files":["dist"],"bugs":{"url":"https://github.com/upstash/context7-mcp/issues"},"homepage":"https://github.com/upstash/context7-mcp#readme","dependencies":{"@modelcontextprotocol/sdk":"^1.8.0","zod":"^3.24.2"},"devDependencies":{"@types/node":"^22.13.14","@typescript-eslint/eslint-plugin":"^8.28.0","@typescript-eslint/parser":"^8.28.0","eslint":"^9.23.0","eslint-config-prettier":"^10.1.1","eslint-plugin-prettier":"^5.2.5","prettier":"^3.5.3","typescript":"^5.8.2","typescript-eslint":"^8.28.0"}}