@upstash/context7-mcp 1.0.1-rc → 1.0.2
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 +8 -8
- package/package.json +1 -1
- package/dist/index.js +0 -103
- package/dist/lib/api.js +0 -70
- package/dist/lib/types.js +0 -1
- package/dist/lib/utils.js +0 -127
package/README.md
CHANGED
|
@@ -14,16 +14,16 @@ Context7 MCP pulls up-to-date, version-specific documentation and code examples
|
|
|
14
14
|
|
|
15
15
|
Add `use context7` to your question in Cursor:
|
|
16
16
|
|
|
17
|
-
```
|
|
18
|
-
|
|
17
|
+
```txt
|
|
18
|
+
How do I use the new Next.js `after` function? use context7
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
```
|
|
22
|
-
|
|
21
|
+
```txt
|
|
22
|
+
How do I invalidate a query in React Query? use context7
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
```
|
|
26
|
-
|
|
25
|
+
```txt
|
|
26
|
+
How do I protect a route with NextAuth? use context7
|
|
27
27
|
```
|
|
28
28
|
|
|
29
29
|
Context7 fetches up-to-date documentation and working code examples right into your LLM’s context.
|
|
@@ -45,7 +45,7 @@ No tab-switching, no hallucinated APIs that don't exist, no outdated code genera
|
|
|
45
45
|
|
|
46
46
|
Go to: `Settings` -> `Cursor Settings` -> `MCP` -> `Add new global MCP server`
|
|
47
47
|
|
|
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.
|
|
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.
|
|
49
49
|
|
|
50
50
|
```json
|
|
51
51
|
{
|
|
@@ -75,7 +75,7 @@ Add this to your Windsurf MCP config file. See [Windsurf MCP docs](https://docs.
|
|
|
75
75
|
|
|
76
76
|
### Available Tools
|
|
77
77
|
|
|
78
|
-
- `resolve-library-id`: Resolves a general
|
|
78
|
+
- `resolve-library-id`: Resolves a general library name into a Context7-compatible library ID.
|
|
79
79
|
- `libraryName` (optional): Search and rerank results
|
|
80
80
|
- `get-library-docs`: Fetches documentation for a library using a Context7-compatible library ID.
|
|
81
81
|
- `context7CompatibleLibraryID` (required)
|
package/package.json
CHANGED
package/dist/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 up-to-date documentation and code examples for npm packages.",
|
|
11
|
-
version: "1.0.0",
|
|
12
|
-
capabilities: {
|
|
13
|
-
resources: {},
|
|
14
|
-
tools: {},
|
|
15
|
-
},
|
|
16
|
-
});
|
|
17
|
-
// Register Context7 tools
|
|
18
|
-
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
|
-
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: "Available libraries and their Context7-compatible library ID:\n\n" + projectsText,
|
|
57
|
-
},
|
|
58
|
-
],
|
|
59
|
-
};
|
|
60
|
-
});
|
|
61
|
-
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.", {
|
|
62
|
-
context7CompatibleLibraryID: z
|
|
63
|
-
.string()
|
|
64
|
-
.describe("Exact Context7-compatible library ID (e.g., 'mongodb/docs', 'vercel/nextjs') retrieved from 'resolve-library-id'."),
|
|
65
|
-
topic: z
|
|
66
|
-
.string()
|
|
67
|
-
.optional()
|
|
68
|
-
.describe("Topic to focus 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 context but consume more tokens."),
|
|
74
|
-
}, async ({ context7CompatibleLibraryID, tokens = 5000, topic = "" }) => {
|
|
75
|
-
const documentationText = await fetchLibraryDocumentation(context7CompatibleLibraryID, tokens, topic);
|
|
76
|
-
if (!documentationText) {
|
|
77
|
-
return {
|
|
78
|
-
content: [
|
|
79
|
-
{
|
|
80
|
-
type: "text",
|
|
81
|
-
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.",
|
|
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/dist/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
|
-
}
|
package/dist/lib/types.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/lib/utils.js
DELETED
|
@@ -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}\nContext7-compatible library ID: ${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
|
-
}
|