@upstash/context7-mcp 2.2.2 → 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 CHANGED
@@ -89,35 +89,26 @@ function getClientIp(req) {
89
89
  }
90
90
  return undefined;
91
91
  }
92
- const server = new McpServer({
93
- name: "Context7",
94
- version: SERVER_VERSION,
95
- websiteUrl: "https://context7.com",
96
- description: "Context7 provides up-to-date documentation and code examples for libraries and frameworks.",
97
- icons: [
98
- {
99
- src: "https://context7.com/context7-icon-green.png",
100
- mimeType: "image/png",
101
- },
102
- ],
103
- }, {
104
- 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.
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
- // Capture client info from MCP initialize handshake
109
- server.server.oninitialized = () => {
110
- const clientVersion = server.server.getClientVersion();
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,79 +141,81 @@ 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
- inputSchema: {
154
- query: z
155
- .string()
156
- .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."),
157
- libraryName: z
158
- .string()
159
- .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'."),
160
- },
161
- annotations: {
162
- readOnlyHint: true,
163
- destructiveHint: false,
164
- openWorldHint: true,
165
- idempotentHint: true,
166
- },
167
- }, async ({ query, libraryName }) => {
168
- const searchResponse = await searchLibraries(query, libraryName, getClientContext());
169
- if (!searchResponse.results || searchResponse.results.length === 0) {
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: searchResponse.error
175
- ? searchResponse.error
176
- : "No libraries found matching the provided name.",
180
+ text: responseText,
177
181
  },
178
182
  ],
179
183
  };
180
- }
181
- const resultsText = formatSearchResults(searchResponse);
182
- const responseText = `Available Libraries:
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
191
  Do not call this tool more than 3 times per question.`,
201
- inputSchema: {
202
- libraryId: z
203
- .string()
204
- .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'."),
205
- query: z
206
- .string()
207
- .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."),
208
- },
209
- annotations: {
210
- readOnlyHint: true,
211
- destructiveHint: false,
212
- openWorldHint: true,
213
- idempotentHint: true,
214
- },
215
- }, async ({ query, libraryId }) => {
216
- const response = await fetchLibraryContext({ query, libraryId }, getClientContext());
217
- return {
218
- content: [
219
- {
220
- type: "text",
221
- text: response.data,
222
- },
223
- ],
224
- };
225
- });
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());
208
+ return {
209
+ content: [
210
+ {
211
+ type: "text",
212
+ text: response.data,
213
+ },
214
+ ],
215
+ };
216
+ });
217
+ return server;
218
+ }
226
219
  async function main() {
227
220
  const transportType = TRANSPORT_TYPE;
228
221
  if (transportType === "http") {
@@ -309,12 +302,19 @@ async function main() {
309
302
  clientInfo: extractClientInfoFromUserAgent(req.headers["user-agent"]),
310
303
  transport: "http",
311
304
  };
305
+ // Use SSE responses for tool calls (enableJsonResponse: false). The SDK then
306
+ // flushes response headers immediately after parsing the request rather than
307
+ // buffering until the tool returns. This is required for long-running tools
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.
312
310
  const transport = new StreamableHTTPServerTransport({
313
311
  sessionIdGenerator: undefined,
314
- enableJsonResponse: true,
312
+ enableJsonResponse: false,
315
313
  });
314
+ const server = createMcpServer();
316
315
  res.on("close", () => {
317
316
  transport.close();
317
+ server.close();
318
318
  });
319
319
  await requestContext.run(context, async () => {
320
320
  await server.connect(transport);
@@ -413,6 +413,18 @@ async function main() {
413
413
  else {
414
414
  stdioApiKey = cliOptions.apiKey || process.env.CONTEXT7_API_KEY;
415
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
+ };
416
428
  await server.connect(transport);
417
429
  console.error(`Context7 Documentation MCP Server v${SERVER_VERSION} running on stdio`);
418
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) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@upstash/context7-mcp",
3
- "version": "2.2.2",
3
+ "version": "2.2.4",
4
4
  "mcpName": "io.github.upstash/context7",
5
5
  "description": "MCP server for Context7",
6
6
  "repository": {