@upstash/context7-mcp 1.1.0-canary-20251128121456 → 2.0.1
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 +1 -1
- package/dist/index.js +34 -54
- package/dist/lib/api.js +49 -75
- package/dist/lib/types.js +1 -7
- package/dist/lib/utils.js +0 -17
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-

|
|
1
|
+

|
|
2
2
|
|
|
3
3
|
[](https://cursor.com/en/install-mcp?name=context7&config=eyJ1cmwiOiJodHRwczovL21jcC5jb250ZXh0Ny5jb20vbWNwIn0%3D) [<img alt="Install in VS Code (npx)" src="https://img.shields.io/badge/Install%20in%20VS%20Code-0098FF?style=for-the-badge&logo=visualstudiocode&logoColor=white">](https://insiders.vscode.dev/redirect?url=vscode%3Amcp%2Finstall%3F%7B%22name%22%3A%22context7%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40upstash%2Fcontext7-mcp%40latest%22%5D%7D)
|
|
4
4
|
|
package/dist/index.js
CHANGED
|
@@ -2,15 +2,12 @@
|
|
|
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 { searchLibraries,
|
|
5
|
+
import { searchLibraries, fetchLibraryContext } from "./lib/api.js";
|
|
6
6
|
import { formatSearchResults } from "./lib/utils.js";
|
|
7
|
-
import { DOCUMENTATION_MODES } from "./lib/types.js";
|
|
8
7
|
import express from "express";
|
|
9
8
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
10
9
|
import { Command } from "commander";
|
|
11
10
|
import { AsyncLocalStorage } from "async_hooks";
|
|
12
|
-
/** Default number of results to return per page */
|
|
13
|
-
const DEFAULT_RESULTS_LIMIT = 10;
|
|
14
11
|
/** Default HTTP server port */
|
|
15
12
|
const DEFAULT_PORT = 3000;
|
|
16
13
|
// Parse CLI arguments using commander
|
|
@@ -70,15 +67,15 @@ function getClientIp(req) {
|
|
|
70
67
|
}
|
|
71
68
|
const server = new McpServer({
|
|
72
69
|
name: "Context7",
|
|
73
|
-
version: "
|
|
70
|
+
version: "2.0.0",
|
|
74
71
|
}, {
|
|
75
72
|
instructions: "Use this server to retrieve up-to-date documentation and code examples for any library.",
|
|
76
73
|
});
|
|
77
74
|
server.registerTool("resolve-library-id", {
|
|
78
75
|
title: "Resolve Context7 Library ID",
|
|
79
|
-
description: `Resolves a package/product name to a Context7-compatible library ID and returns
|
|
76
|
+
description: `Resolves a package/product name to a Context7-compatible library ID and returns matching libraries.
|
|
80
77
|
|
|
81
|
-
You MUST call this function before '
|
|
78
|
+
You MUST call this function before 'query-docs' to obtain a valid Context7-compatible library ID UNLESS the user explicitly provides a library ID in the format '/org/project' or '/org/project/version' in their query.
|
|
82
79
|
|
|
83
80
|
Selection Process:
|
|
84
81
|
1. Analyze the query to understand what library/package the user is looking for
|
|
@@ -95,15 +92,24 @@ Response Format:
|
|
|
95
92
|
- If multiple good matches exist, acknowledge this but proceed with the most relevant one
|
|
96
93
|
- If no good matches exist, clearly state this and suggest query refinements
|
|
97
94
|
|
|
98
|
-
For ambiguous queries, request clarification before proceeding with a best-guess match
|
|
95
|
+
For ambiguous queries, request clarification before proceeding with a best-guess match.
|
|
96
|
+
|
|
97
|
+
IMPORTANT: Do not call this tool more than 3 times per question. If you cannot find what you need after 3 calls, use the best result you have.`,
|
|
99
98
|
inputSchema: {
|
|
99
|
+
query: z
|
|
100
|
+
.string()
|
|
101
|
+
.describe("The user's original question or task. This is used to rank library results by relevance to what the user is trying to accomplish. IMPORTANT: Do not include any sensitive or confidential information such as API keys, passwords, credentials, or personal data in your query."),
|
|
100
102
|
libraryName: z
|
|
101
103
|
.string()
|
|
102
104
|
.describe("Library name to search for and retrieve a Context7-compatible library ID."),
|
|
103
105
|
},
|
|
104
|
-
|
|
106
|
+
annotations: {
|
|
107
|
+
readOnlyHint: true,
|
|
108
|
+
},
|
|
109
|
+
}, async ({ query, libraryName }) => {
|
|
105
110
|
const ctx = requestContext.getStore();
|
|
106
|
-
const
|
|
111
|
+
const apiKey = ctx?.apiKey || globalApiKey;
|
|
112
|
+
const searchResponse = await searchLibraries(query, libraryName, ctx?.clientIp, apiKey);
|
|
107
113
|
if (!searchResponse.results || searchResponse.results.length === 0) {
|
|
108
114
|
return {
|
|
109
115
|
content: [
|
|
@@ -111,7 +117,7 @@ For ambiguous queries, request clarification before proceeding with a best-guess
|
|
|
111
117
|
type: "text",
|
|
112
118
|
text: searchResponse.error
|
|
113
119
|
? searchResponse.error
|
|
114
|
-
: "
|
|
120
|
+
: "No libraries found matching the provided name.",
|
|
115
121
|
},
|
|
116
122
|
],
|
|
117
123
|
};
|
|
@@ -142,53 +148,33 @@ ${resultsText}`;
|
|
|
142
148
|
],
|
|
143
149
|
};
|
|
144
150
|
});
|
|
145
|
-
server.registerTool("
|
|
146
|
-
title: "
|
|
147
|
-
description:
|
|
151
|
+
server.registerTool("query-docs", {
|
|
152
|
+
title: "Query Documentation",
|
|
153
|
+
description: `Retrieves and queries up-to-date documentation and code examples from Context7 for any programming library or framework.
|
|
154
|
+
|
|
155
|
+
You must call 'resolve-library-id' first to obtain the exact Context7-compatible library ID required to use this tool, UNLESS the user explicitly provides a library ID in the format '/org/project' or '/org/project/version' in their query.
|
|
156
|
+
|
|
157
|
+
IMPORTANT: Do not call this tool more than 3 times per question. If you cannot find what you need after 3 calls, use the best information you have.`,
|
|
148
158
|
inputSchema: {
|
|
149
|
-
|
|
159
|
+
libraryId: z
|
|
150
160
|
.string()
|
|
151
161
|
.describe("Exact Context7-compatible library ID (e.g., '/mongodb/docs', '/vercel/next.js', '/supabase/supabase', '/vercel/next.js/v14.3.0-canary.87') retrieved from 'resolve-library-id' or directly from user query in the format '/org/project' or '/org/project/version'."),
|
|
152
|
-
|
|
153
|
-
.enum(["code", "info"])
|
|
154
|
-
.optional()
|
|
155
|
-
.default("code")
|
|
156
|
-
.describe("Documentation mode: 'code' for API references and code examples (default), 'info' for conceptual guides, narrative information, and architectural questions."),
|
|
157
|
-
topic: z
|
|
162
|
+
query: z
|
|
158
163
|
.string()
|
|
159
|
-
.
|
|
160
|
-
.describe("Topic to focus documentation on (e.g., 'hooks', 'routing')."),
|
|
161
|
-
page: z
|
|
162
|
-
.number()
|
|
163
|
-
.int()
|
|
164
|
-
.min(1)
|
|
165
|
-
.max(10)
|
|
166
|
-
.optional()
|
|
167
|
-
.describe("Page number for pagination (start: 1, default: 1). If the context is not sufficient, try page=2, page=3, page=4, etc. with the same topic."),
|
|
164
|
+
.describe("The question or task you need help with. Be specific and include relevant details. Good: 'How to set up authentication with JWT in Express.js' or 'React useEffect cleanup function examples'. Bad: 'auth' or 'hooks'. IMPORTANT: Do not include any sensitive or confidential information such as API keys, passwords, credentials, or personal data in your query."),
|
|
168
165
|
},
|
|
169
|
-
|
|
166
|
+
annotations: {
|
|
167
|
+
readOnlyHint: true,
|
|
168
|
+
},
|
|
169
|
+
}, async ({ query, libraryId }) => {
|
|
170
170
|
const ctx = requestContext.getStore();
|
|
171
171
|
const apiKey = ctx?.apiKey || globalApiKey;
|
|
172
|
-
const
|
|
173
|
-
page,
|
|
174
|
-
limit: DEFAULT_RESULTS_LIMIT,
|
|
175
|
-
topic,
|
|
176
|
-
}, ctx?.clientIp, apiKey);
|
|
177
|
-
if (!fetchDocsResponse) {
|
|
178
|
-
return {
|
|
179
|
-
content: [
|
|
180
|
-
{
|
|
181
|
-
type: "text",
|
|
182
|
-
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.",
|
|
183
|
-
},
|
|
184
|
-
],
|
|
185
|
-
};
|
|
186
|
-
}
|
|
172
|
+
const response = await fetchLibraryContext({ query, libraryId }, ctx?.clientIp, apiKey);
|
|
187
173
|
return {
|
|
188
174
|
content: [
|
|
189
175
|
{
|
|
190
176
|
type: "text",
|
|
191
|
-
text:
|
|
177
|
+
text: response.data,
|
|
192
178
|
},
|
|
193
179
|
],
|
|
194
180
|
};
|
|
@@ -197,7 +183,6 @@ async function main() {
|
|
|
197
183
|
const transportType = TRANSPORT_TYPE;
|
|
198
184
|
if (transportType === "http") {
|
|
199
185
|
const initialPort = CLI_PORT ?? DEFAULT_PORT;
|
|
200
|
-
let actualPort = initialPort;
|
|
201
186
|
const app = express();
|
|
202
187
|
app.use(express.json());
|
|
203
188
|
app.use((req, res, next) => {
|
|
@@ -227,12 +212,8 @@ async function main() {
|
|
|
227
212
|
};
|
|
228
213
|
const extractApiKey = (req) => {
|
|
229
214
|
return (extractBearerToken(req.headers.authorization) ||
|
|
230
|
-
extractHeaderValue(req.headers["Context7-API-Key"]) ||
|
|
231
|
-
extractHeaderValue(req.headers["X-API-Key"]) ||
|
|
232
215
|
extractHeaderValue(req.headers["context7-api-key"]) ||
|
|
233
216
|
extractHeaderValue(req.headers["x-api-key"]) ||
|
|
234
|
-
extractHeaderValue(req.headers["Context7_API_Key"]) ||
|
|
235
|
-
extractHeaderValue(req.headers["X_API_Key"]) ||
|
|
236
217
|
extractHeaderValue(req.headers["context7_api_key"]) ||
|
|
237
218
|
extractHeaderValue(req.headers["x_api_key"]));
|
|
238
219
|
};
|
|
@@ -286,8 +267,7 @@ async function main() {
|
|
|
286
267
|
}
|
|
287
268
|
});
|
|
288
269
|
httpServer.once("listening", () => {
|
|
289
|
-
|
|
290
|
-
console.error(`Context7 Documentation MCP Server running on HTTP at http://localhost:${actualPort}/mcp`);
|
|
270
|
+
console.error(`Context7 Documentation MCP Server running on HTTP at http://localhost:${port}/mcp`);
|
|
291
271
|
});
|
|
292
272
|
};
|
|
293
273
|
startServer(initialPort);
|
package/dist/lib/api.js
CHANGED
|
@@ -1,49 +1,37 @@
|
|
|
1
1
|
import { generateHeaders } from "./encryption.js";
|
|
2
2
|
import { ProxyAgent, setGlobalDispatcher } from "undici";
|
|
3
|
-
import { DOCUMENTATION_MODES } from "./types.js";
|
|
4
|
-
import { maskApiKey } from "./utils.js";
|
|
5
3
|
const CONTEXT7_API_BASE_URL = "https://context7.com/api";
|
|
6
|
-
const DEFAULT_TYPE = "txt";
|
|
7
4
|
/**
|
|
8
|
-
* Parses
|
|
9
|
-
*
|
|
10
|
-
* @
|
|
11
|
-
|
|
12
|
-
function parseLibraryId(libraryId) {
|
|
13
|
-
// Remove leading slash if present
|
|
14
|
-
const cleaned = libraryId.startsWith("/") ? libraryId.slice(1) : libraryId;
|
|
15
|
-
const parts = cleaned.split("/");
|
|
16
|
-
if (parts.length < 2) {
|
|
17
|
-
throw new Error(`Invalid library ID format: ${libraryId}. Expected format: /username/library or /username/library/tag`);
|
|
18
|
-
}
|
|
19
|
-
return {
|
|
20
|
-
username: parts[0],
|
|
21
|
-
library: parts[1],
|
|
22
|
-
tag: parts[2], // undefined if not present
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Generates appropriate error messages based on HTTP status codes
|
|
27
|
-
* @param errorCode The HTTP error status code
|
|
28
|
-
* @param apiKey Optional API key (used for rate limit message)
|
|
5
|
+
* Parses error response from the Context7 API
|
|
6
|
+
* Extracts the server's error message, falling back to status-based messages if parsing fails
|
|
7
|
+
* @param response The fetch Response object
|
|
8
|
+
* @param apiKey Optional API key (used for fallback messages)
|
|
29
9
|
* @returns Error message string
|
|
30
10
|
*/
|
|
31
|
-
function
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (!apiKey) {
|
|
41
|
-
return "Unauthorized. Please provide an API key.";
|
|
42
|
-
}
|
|
43
|
-
return `Unauthorized. Please check your API key. The API key you provided (possibly incorrect) is: ${maskApiKey(apiKey)}. API keys should start with 'ctx7sk'`;
|
|
44
|
-
default:
|
|
45
|
-
return `Failed to fetch documentation. Please try again later. Error code: ${errorCode}`;
|
|
11
|
+
async function parseErrorResponse(response, apiKey) {
|
|
12
|
+
try {
|
|
13
|
+
const json = (await response.json());
|
|
14
|
+
if (json.message) {
|
|
15
|
+
return json.message;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
// JSON parsing failed, fall through to default
|
|
46
20
|
}
|
|
21
|
+
// Fallback for non-JSON responses
|
|
22
|
+
const status = response.status;
|
|
23
|
+
if (status === 429) {
|
|
24
|
+
return apiKey
|
|
25
|
+
? "Rate limited or quota exceeded. Upgrade your plan at https://context7.com/plans for higher limits."
|
|
26
|
+
: "Rate limited or quota exceeded. Create a free API key at https://context7.com/dashboard for higher limits.";
|
|
27
|
+
}
|
|
28
|
+
if (status === 404) {
|
|
29
|
+
return "The library you are trying to access does not exist. Please try with a different library ID.";
|
|
30
|
+
}
|
|
31
|
+
if (status === 401) {
|
|
32
|
+
return "Invalid API key. Please check your API key. API keys should start with 'ctx7sk' prefix.";
|
|
33
|
+
}
|
|
34
|
+
return `Request failed with status ${status}. Please try again later.`;
|
|
47
35
|
}
|
|
48
36
|
// Pick up proxy configuration in a variety of common env var names.
|
|
49
37
|
const PROXY_URL = process.env.HTTPS_PROXY ??
|
|
@@ -66,20 +54,21 @@ if (PROXY_URL && !PROXY_URL.startsWith("$") && /^(http|https):\/\//i.test(PROXY_
|
|
|
66
54
|
}
|
|
67
55
|
/**
|
|
68
56
|
* Searches for libraries matching the given query
|
|
69
|
-
* @param query The
|
|
57
|
+
* @param query The user's question or task (used for LLM relevance ranking)
|
|
58
|
+
* @param libraryName The library name to search for in the database
|
|
70
59
|
* @param clientIp Optional client IP address to include in headers
|
|
71
60
|
* @param apiKey Optional API key for authentication
|
|
72
61
|
* @returns Search results or null if the request fails
|
|
73
62
|
*/
|
|
74
|
-
export async function searchLibraries(query, clientIp, apiKey) {
|
|
63
|
+
export async function searchLibraries(query, libraryName, clientIp, apiKey) {
|
|
75
64
|
try {
|
|
76
|
-
const url = new URL(`${CONTEXT7_API_BASE_URL}/v2/search`);
|
|
65
|
+
const url = new URL(`${CONTEXT7_API_BASE_URL}/v2/libs/search`);
|
|
77
66
|
url.searchParams.set("query", query);
|
|
67
|
+
url.searchParams.set("libraryName", libraryName);
|
|
78
68
|
const headers = generateHeaders(clientIp, apiKey);
|
|
79
69
|
const response = await fetch(url, { headers });
|
|
80
70
|
if (!response.ok) {
|
|
81
|
-
const
|
|
82
|
-
const errorMessage = createErrorMessage(errorCode, apiKey);
|
|
71
|
+
const errorMessage = await parseErrorResponse(response, apiKey);
|
|
83
72
|
console.error(errorMessage);
|
|
84
73
|
return {
|
|
85
74
|
results: [],
|
|
@@ -96,50 +85,35 @@ export async function searchLibraries(query, clientIp, apiKey) {
|
|
|
96
85
|
}
|
|
97
86
|
}
|
|
98
87
|
/**
|
|
99
|
-
* Fetches
|
|
100
|
-
* @param
|
|
101
|
-
* @param docMode Documentation mode (CODE for API references and code examples, INFO for conceptual guides)
|
|
102
|
-
* @param options Optional request parameters (page, limit, topic)
|
|
88
|
+
* Fetches intelligent, reranked context for a natural language query
|
|
89
|
+
* @param request The context request parameters (query, topic, library, mode)
|
|
103
90
|
* @param clientIp Optional client IP address to include in headers
|
|
104
91
|
* @param apiKey Optional API key for authentication
|
|
105
|
-
* @returns
|
|
92
|
+
* @returns Context response with data
|
|
106
93
|
*/
|
|
107
|
-
export async function
|
|
94
|
+
export async function fetchLibraryContext(request, clientIp, apiKey) {
|
|
108
95
|
try {
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
if (tag) {
|
|
113
|
-
urlPath += `/${tag}`;
|
|
114
|
-
}
|
|
115
|
-
const url = new URL(urlPath);
|
|
116
|
-
url.searchParams.set("type", DEFAULT_TYPE);
|
|
117
|
-
if (options.topic)
|
|
118
|
-
url.searchParams.set("topic", options.topic);
|
|
119
|
-
if (options.page)
|
|
120
|
-
url.searchParams.set("page", options.page.toString());
|
|
121
|
-
if (options.limit)
|
|
122
|
-
url.searchParams.set("limit", options.limit.toString());
|
|
96
|
+
const url = new URL(`${CONTEXT7_API_BASE_URL}/v2/context`);
|
|
97
|
+
url.searchParams.set("query", request.query);
|
|
98
|
+
url.searchParams.set("libraryId", request.libraryId);
|
|
123
99
|
const headers = generateHeaders(clientIp, apiKey, { "X-Context7-Source": "mcp-server" });
|
|
124
100
|
const response = await fetch(url, { headers });
|
|
125
101
|
if (!response.ok) {
|
|
126
|
-
const
|
|
127
|
-
const errorMessage = createErrorMessage(errorCode, apiKey);
|
|
102
|
+
const errorMessage = await parseErrorResponse(response, apiKey);
|
|
128
103
|
console.error(errorMessage);
|
|
129
|
-
return errorMessage;
|
|
104
|
+
return { data: errorMessage };
|
|
130
105
|
}
|
|
131
106
|
const text = await response.text();
|
|
132
|
-
if (!text
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
return `No ${docMode} documentation available for this library.${suggestion}`;
|
|
107
|
+
if (!text) {
|
|
108
|
+
return {
|
|
109
|
+
data: "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.",
|
|
110
|
+
};
|
|
137
111
|
}
|
|
138
|
-
return text;
|
|
112
|
+
return { data: text };
|
|
139
113
|
}
|
|
140
114
|
catch (error) {
|
|
141
|
-
const errorMessage = `Error fetching library
|
|
115
|
+
const errorMessage = `Error fetching library context. Please try again later. ${error}`;
|
|
142
116
|
console.error(errorMessage);
|
|
143
|
-
return errorMessage;
|
|
117
|
+
return { data: errorMessage };
|
|
144
118
|
}
|
|
145
119
|
}
|
package/dist/lib/types.js
CHANGED
package/dist/lib/utils.js
CHANGED
|
@@ -58,20 +58,3 @@ export function formatSearchResults(searchResponse) {
|
|
|
58
58
|
const formattedResults = searchResponse.results.map(formatSearchResult);
|
|
59
59
|
return formattedResults.join("\n----------\n");
|
|
60
60
|
}
|
|
61
|
-
/**
|
|
62
|
-
* Masks an API key by showing only the first 10 characters and last 4 characters.
|
|
63
|
-
* This prevents full API keys from being exposed in logs while maintaining some
|
|
64
|
-
* identifiability for debugging.
|
|
65
|
-
*
|
|
66
|
-
* @param apiKey The API key to mask
|
|
67
|
-
* @returns Masked API key string (e.g., "ctx7sk-abc...xyz1") or "[NO-API-KEY]" if no key provided
|
|
68
|
-
*/
|
|
69
|
-
export function maskApiKey(apiKey) {
|
|
70
|
-
if (apiKey.length <= 14) {
|
|
71
|
-
// If the key is too short to mask meaningfully, just show first part
|
|
72
|
-
return apiKey.substring(0, 7) + "...";
|
|
73
|
-
}
|
|
74
|
-
const firstPart = apiKey.substring(0, 10);
|
|
75
|
-
const lastPart = apiKey.substring(apiKey.length - 4);
|
|
76
|
-
return `${firstPart}...${lastPart}`;
|
|
77
|
-
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@upstash/context7-mcp",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"mcpName": "io.github.upstash/context7",
|
|
5
5
|
"description": "MCP server for Context7",
|
|
6
6
|
"repository": {
|
|
@@ -46,13 +46,14 @@
|
|
|
46
46
|
},
|
|
47
47
|
"scripts": {
|
|
48
48
|
"build": "tsc && chmod 755 dist/index.js",
|
|
49
|
-
"test": "echo \"
|
|
49
|
+
"test": "echo \"No tests yet\"",
|
|
50
|
+
"typecheck": "tsc --noEmit",
|
|
50
51
|
"lint": "eslint .",
|
|
51
52
|
"lint:check": "eslint .",
|
|
52
53
|
"format": "prettier --write .",
|
|
53
54
|
"format:check": "prettier --check .",
|
|
54
55
|
"dev": "tsc --watch",
|
|
55
56
|
"start": "node dist/index.js --transport http",
|
|
56
|
-
"pack-mcpb": "pnpm install && pnpm run build && rm -rf node_modules && pnpm install --prod &&
|
|
57
|
+
"pack-mcpb": "pnpm install && pnpm run build && rm -rf node_modules && pnpm install --prod && cp mcpb/manifest.json manifest.json && cp ../../public/icon.png icon.png && mcpb validate manifest.json && mcpb pack . mcpb/context7.mcpb && rm manifest.json icon.png && pnpm install"
|
|
57
58
|
}
|
|
58
59
|
}
|