@upstash/context7-mcp 2.2.3 → 2.2.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/dist/index.js +91 -118
- package/dist/lib/api.js +0 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -89,35 +89,26 @@ function getClientIp(req) {
|
|
|
89
89
|
}
|
|
90
90
|
return undefined;
|
|
91
91
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
92
|
+
function createMcpServer() {
|
|
93
|
+
const server = new McpServer({
|
|
94
|
+
name: "Context7",
|
|
95
|
+
version: SERVER_VERSION,
|
|
96
|
+
websiteUrl: "https://context7.com",
|
|
97
|
+
description: "Context7 provides up-to-date documentation and code examples for libraries and frameworks.",
|
|
98
|
+
icons: [
|
|
99
|
+
{
|
|
100
|
+
src: "https://context7.com/context7-icon-green.png",
|
|
101
|
+
mimeType: "image/png",
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
}, {
|
|
105
|
+
instructions: `Use this server to fetch current documentation whenever the user asks about a library, framework, SDK, API, CLI tool, or cloud service -- even well-known ones like React, Next.js, Prisma, Express, Tailwind, Django, or Spring Boot. This includes API syntax, configuration, version migration, library-specific debugging, setup instructions, and CLI tool usage. Use even when you think you know the answer -- your training data may not reflect recent changes. Prefer this over web search for library docs.
|
|
105
106
|
|
|
106
107
|
Do not use for: refactoring, writing scripts from scratch, debugging business logic, code review, or general programming concepts.`,
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
if (clientVersion) {
|
|
112
|
-
stdioClientInfo = {
|
|
113
|
-
ide: clientVersion.name,
|
|
114
|
-
version: clientVersion.version,
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
};
|
|
118
|
-
server.registerTool("resolve-library-id", {
|
|
119
|
-
title: "Resolve Context7 Library ID",
|
|
120
|
-
description: `Resolves a package/product name to a Context7-compatible library ID and returns matching libraries.
|
|
108
|
+
});
|
|
109
|
+
server.registerTool("resolve-library-id", {
|
|
110
|
+
title: "Resolve Context7 Library ID",
|
|
111
|
+
description: `Resolves a package/product name to a Context7-compatible library ID and returns matching libraries.
|
|
121
112
|
|
|
122
113
|
You MUST call this function before 'Query Documentation' tool 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.
|
|
123
114
|
|
|
@@ -150,99 +141,70 @@ Response Format:
|
|
|
150
141
|
For ambiguous queries, request clarification before proceeding with a best-guess match.
|
|
151
142
|
|
|
152
143
|
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.`,
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
}, async ({ query, libraryName }) => {
|
|
168
|
-
|
|
169
|
-
|
|
144
|
+
inputSchema: {
|
|
145
|
+
query: z
|
|
146
|
+
.string()
|
|
147
|
+
.describe("The question or task you need help with. This is used to rank library results by relevance to what the user is trying to accomplish. The query is sent to the Context7 API for processing. Do not include any sensitive or confidential information such as API keys, passwords, credentials, personal data, or proprietary code in your query."),
|
|
148
|
+
libraryName: z
|
|
149
|
+
.string()
|
|
150
|
+
.describe("Library name to search for and retrieve a Context7-compatible library ID. Use the official library name with proper punctuation — e.g., 'Next.js' instead of 'nextjs', 'Customer.io' instead of 'customerio', 'Three.js' instead of 'threejs'."),
|
|
151
|
+
},
|
|
152
|
+
annotations: {
|
|
153
|
+
readOnlyHint: true,
|
|
154
|
+
destructiveHint: false,
|
|
155
|
+
openWorldHint: true,
|
|
156
|
+
idempotentHint: true,
|
|
157
|
+
},
|
|
158
|
+
}, async ({ query, libraryName }) => {
|
|
159
|
+
const searchResponse = await searchLibraries(query, libraryName, getClientContext());
|
|
160
|
+
if (!searchResponse.results || searchResponse.results.length === 0) {
|
|
161
|
+
return {
|
|
162
|
+
content: [
|
|
163
|
+
{
|
|
164
|
+
type: "text",
|
|
165
|
+
text: searchResponse.error
|
|
166
|
+
? searchResponse.error
|
|
167
|
+
: "No libraries found matching the provided name.",
|
|
168
|
+
},
|
|
169
|
+
],
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
const resultsText = formatSearchResults(searchResponse);
|
|
173
|
+
const responseText = `Available Libraries:
|
|
174
|
+
|
|
175
|
+
${resultsText}`;
|
|
170
176
|
return {
|
|
171
177
|
content: [
|
|
172
178
|
{
|
|
173
179
|
type: "text",
|
|
174
|
-
text:
|
|
175
|
-
? searchResponse.error
|
|
176
|
-
: "No libraries found matching the provided name.",
|
|
180
|
+
text: responseText,
|
|
177
181
|
},
|
|
178
182
|
],
|
|
179
183
|
};
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
${resultsText}`;
|
|
185
|
-
return {
|
|
186
|
-
content: [
|
|
187
|
-
{
|
|
188
|
-
type: "text",
|
|
189
|
-
text: responseText,
|
|
190
|
-
},
|
|
191
|
-
],
|
|
192
|
-
};
|
|
193
|
-
});
|
|
194
|
-
server.registerTool("query-docs", {
|
|
195
|
-
title: "Query Documentation",
|
|
196
|
-
description: `Retrieves and queries up-to-date documentation and code examples from Context7 for any programming library or framework.
|
|
184
|
+
});
|
|
185
|
+
server.registerTool("query-docs", {
|
|
186
|
+
title: "Query Documentation",
|
|
187
|
+
description: `Retrieves and queries up-to-date documentation and code examples from Context7 for any programming library or framework.
|
|
197
188
|
|
|
198
189
|
You must call 'Resolve Context7 Library ID' tool 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.
|
|
199
190
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
idempotentHint: true,
|
|
218
|
-
},
|
|
219
|
-
}, async ({ query, libraryId, researchMode }, { sendNotification, _meta }) => {
|
|
220
|
-
// Emit periodic progress notifications while the upstream call is in flight.
|
|
221
|
-
// MCP clients that opt into resetTimeoutOnProgress (e.g. opencode) reset their
|
|
222
|
-
// request timer on each notification, which keeps long-running tools (notably
|
|
223
|
-
// researchMode) alive past the SDK's default 60s wall-clock timeout. Clients
|
|
224
|
-
// that don't pass a progressToken simply never see these, so behavior is unchanged.
|
|
225
|
-
const progressToken = _meta?.progressToken;
|
|
226
|
-
let progressInterval;
|
|
227
|
-
if (researchMode && progressToken !== undefined) {
|
|
228
|
-
let progress = 0;
|
|
229
|
-
progressInterval = setInterval(() => {
|
|
230
|
-
progress += 1;
|
|
231
|
-
sendNotification({
|
|
232
|
-
method: "notifications/progress",
|
|
233
|
-
params: {
|
|
234
|
-
progressToken,
|
|
235
|
-
progress,
|
|
236
|
-
message: "Researching documentation...",
|
|
237
|
-
},
|
|
238
|
-
}).catch(() => {
|
|
239
|
-
// Notifications are best-effort; swallow transport errors so the tool
|
|
240
|
-
// call itself isn't aborted by a notification write failure.
|
|
241
|
-
});
|
|
242
|
-
}, 20_000);
|
|
243
|
-
}
|
|
244
|
-
try {
|
|
245
|
-
const response = await fetchLibraryContext({ query, libraryId, researchMode }, getClientContext());
|
|
191
|
+
Do not call this tool more than 3 times per question.`,
|
|
192
|
+
inputSchema: {
|
|
193
|
+
libraryId: z
|
|
194
|
+
.string()
|
|
195
|
+
.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'."),
|
|
196
|
+
query: z
|
|
197
|
+
.string()
|
|
198
|
+
.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'. The query is sent to the Context7 API for processing. Do not include any sensitive or confidential information such as API keys, passwords, credentials, personal data, or proprietary code in your query."),
|
|
199
|
+
},
|
|
200
|
+
annotations: {
|
|
201
|
+
readOnlyHint: true,
|
|
202
|
+
destructiveHint: false,
|
|
203
|
+
openWorldHint: true,
|
|
204
|
+
idempotentHint: true,
|
|
205
|
+
},
|
|
206
|
+
}, async ({ query, libraryId }) => {
|
|
207
|
+
const response = await fetchLibraryContext({ query, libraryId }, getClientContext());
|
|
246
208
|
return {
|
|
247
209
|
content: [
|
|
248
210
|
{
|
|
@@ -251,12 +213,9 @@ Workflow: call first without researchMode. If that doesn't answer the question,
|
|
|
251
213
|
},
|
|
252
214
|
],
|
|
253
215
|
};
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
clearInterval(progressInterval);
|
|
258
|
-
}
|
|
259
|
-
});
|
|
216
|
+
});
|
|
217
|
+
return server;
|
|
218
|
+
}
|
|
260
219
|
async function main() {
|
|
261
220
|
const transportType = TRANSPORT_TYPE;
|
|
262
221
|
if (transportType === "http") {
|
|
@@ -346,14 +305,16 @@ async function main() {
|
|
|
346
305
|
// Use SSE responses for tool calls (enableJsonResponse: false). The SDK then
|
|
347
306
|
// flushes response headers immediately after parsing the request rather than
|
|
348
307
|
// buffering until the tool returns. This is required for long-running tools
|
|
349
|
-
//
|
|
350
|
-
//
|
|
308
|
+
// because some MCP HTTP clients cap the underlying fetch at 60s waiting for
|
|
309
|
+
// headers, even though the per-tool timeout is much higher.
|
|
351
310
|
const transport = new StreamableHTTPServerTransport({
|
|
352
311
|
sessionIdGenerator: undefined,
|
|
353
312
|
enableJsonResponse: false,
|
|
354
313
|
});
|
|
314
|
+
const server = createMcpServer();
|
|
355
315
|
res.on("close", () => {
|
|
356
316
|
transport.close();
|
|
317
|
+
server.close();
|
|
357
318
|
});
|
|
358
319
|
await requestContext.run(context, async () => {
|
|
359
320
|
await server.connect(transport);
|
|
@@ -452,6 +413,18 @@ async function main() {
|
|
|
452
413
|
else {
|
|
453
414
|
stdioApiKey = cliOptions.apiKey || process.env.CONTEXT7_API_KEY;
|
|
454
415
|
const transport = new StdioServerTransport();
|
|
416
|
+
const server = createMcpServer();
|
|
417
|
+
// Capture client info from MCP initialize handshake (stdio only — HTTP
|
|
418
|
+
// mode plumbs client info through requestContext per request).
|
|
419
|
+
server.server.oninitialized = () => {
|
|
420
|
+
const clientVersion = server.server.getClientVersion();
|
|
421
|
+
if (clientVersion) {
|
|
422
|
+
stdioClientInfo = {
|
|
423
|
+
ide: clientVersion.name,
|
|
424
|
+
version: clientVersion.version,
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
};
|
|
455
428
|
await server.connect(transport);
|
|
456
429
|
console.error(`Context7 Documentation MCP Server v${SERVER_VERSION} running on stdio`);
|
|
457
430
|
}
|
package/dist/lib/api.js
CHANGED
|
@@ -120,8 +120,6 @@ export async function fetchLibraryContext(request, context = {}) {
|
|
|
120
120
|
const url = new URL(`${CONTEXT7_API_BASE_URL}/v2/context`);
|
|
121
121
|
url.searchParams.set("query", request.query);
|
|
122
122
|
url.searchParams.set("libraryId", request.libraryId);
|
|
123
|
-
if (request.researchMode)
|
|
124
|
-
url.searchParams.set("researchMode", "true");
|
|
125
123
|
const headers = generateHeaders(context);
|
|
126
124
|
const response = await fetch(url, { headers });
|
|
127
125
|
if (!response.ok) {
|