This is page 3 of 3. Use http://codebase.md/upstash/context7-mcp?lines=true&page={x} to view the full context.
# Directory Structure
```
├── .github
│   ├── dependabot.yml
│   ├── ISSUE_TEMPLATE
│   │   ├── bug_report.yml
│   │   ├── documentation.yml
│   │   └── feature_request.yml
│   └── workflows
│       ├── check.yaml
│       ├── publish-mcp.yml
│       └── release.yml
├── .gitignore
├── bun.lock
├── Dockerfile
├── docs
│   ├── adding-projects.md
│   ├── README.ar.md
│   ├── README.de.md
│   ├── README.es.md
│   ├── README.fr.md
│   ├── README.id-ID.md
│   ├── README.it.md
│   ├── README.ja.md
│   ├── README.ko.md
│   ├── README.pt-BR.md
│   ├── README.ru.md
│   ├── README.tr.md
│   ├── README.uk.md
│   ├── README.vi.md
│   ├── README.zh-CN.md
│   └── README.zh-TW.md
├── eslint.config.js
├── gemini-extension.json
├── LICENSE
├── mcpb
│   ├── .mcpbignore
│   ├── context7.mcpb
│   └── manifest.json
├── package.json
├── prettier.config.mjs
├── public
│   ├── context7-icon-green.svg
│   ├── context7-icon.svg
│   ├── context7-logo.svg
│   ├── cover.png
│   └── icon.png
├── README.md
├── schema
│   └── context7.json
├── server.json
├── smithery.yaml
├── src
│   ├── index.ts
│   └── lib
│       ├── api.ts
│       ├── encryption.ts
│       ├── types.ts
│       └── utils.ts
└── tsconfig.json
```
# Files
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
```typescript
  1 | #!/usr/bin/env node
  2 | 
  3 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
  4 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
  5 | import { z } from "zod";
  6 | import { searchLibraries, fetchLibraryDocumentation } from "./lib/api.js";
  7 | import { formatSearchResults } from "./lib/utils.js";
  8 | import { SearchResponse } from "./lib/types.js";
  9 | import express from "express";
 10 | import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
 11 | import { Command } from "commander";
 12 | import { AsyncLocalStorage } from "async_hooks";
 13 | 
 14 | /** Minimum allowed tokens for documentation retrieval */
 15 | const MINIMUM_TOKENS = 1000;
 16 | /** Default tokens when none specified */
 17 | const DEFAULT_TOKENS = 5000;
 18 | /** Default HTTP server port */
 19 | const DEFAULT_PORT = 3000;
 20 | 
 21 | // Parse CLI arguments using commander
 22 | const program = new Command()
 23 |   .option("--transport <stdio|http>", "transport type", "stdio")
 24 |   .option("--port <number>", "port for HTTP transport", DEFAULT_PORT.toString())
 25 |   .option("--api-key <key>", "API key for authentication (or set CONTEXT7_API_KEY env var)")
 26 |   .allowUnknownOption() // let MCP Inspector / other wrappers pass through extra flags
 27 |   .parse(process.argv);
 28 | 
 29 | const cliOptions = program.opts<{
 30 |   transport: string;
 31 |   port: string;
 32 |   apiKey?: string;
 33 | }>();
 34 | 
 35 | // Validate transport option
 36 | const allowedTransports = ["stdio", "http"];
 37 | if (!allowedTransports.includes(cliOptions.transport)) {
 38 |   console.error(
 39 |     `Invalid --transport value: '${cliOptions.transport}'. Must be one of: stdio, http.`
 40 |   );
 41 |   process.exit(1);
 42 | }
 43 | 
 44 | // Transport configuration
 45 | const TRANSPORT_TYPE = (cliOptions.transport || "stdio") as "stdio" | "http";
 46 | 
 47 | // Disallow incompatible flags based on transport
 48 | const passedPortFlag = process.argv.includes("--port");
 49 | const passedApiKeyFlag = process.argv.includes("--api-key");
 50 | 
 51 | if (TRANSPORT_TYPE === "http" && passedApiKeyFlag) {
 52 |   console.error(
 53 |     "The --api-key flag is not allowed when using --transport http. Use header-based auth at the HTTP layer instead."
 54 |   );
 55 |   process.exit(1);
 56 | }
 57 | 
 58 | if (TRANSPORT_TYPE === "stdio" && passedPortFlag) {
 59 |   console.error("The --port flag is not allowed when using --transport stdio.");
 60 |   process.exit(1);
 61 | }
 62 | 
 63 | // HTTP port configuration
 64 | const CLI_PORT = (() => {
 65 |   const parsed = parseInt(cliOptions.port, 10);
 66 |   return isNaN(parsed) ? undefined : parsed;
 67 | })();
 68 | 
 69 | const requestContext = new AsyncLocalStorage<{
 70 |   clientIp?: string;
 71 |   apiKey?: string;
 72 | }>();
 73 | 
 74 | function getClientIp(req: express.Request): string | undefined {
 75 |   const forwardedFor = req.headers["x-forwarded-for"] || req.headers["X-Forwarded-For"];
 76 | 
 77 |   if (forwardedFor) {
 78 |     const ips = Array.isArray(forwardedFor) ? forwardedFor[0] : forwardedFor;
 79 |     const ipList = ips.split(",").map((ip) => ip.trim());
 80 | 
 81 |     for (const ip of ipList) {
 82 |       const plainIp = ip.replace(/^::ffff:/, "");
 83 |       if (
 84 |         !plainIp.startsWith("10.") &&
 85 |         !plainIp.startsWith("192.168.") &&
 86 |         !/^172\.(1[6-9]|2[0-9]|3[0-1])\./.test(plainIp)
 87 |       ) {
 88 |         return plainIp;
 89 |       }
 90 |     }
 91 |     return ipList[0].replace(/^::ffff:/, "");
 92 |   }
 93 | 
 94 |   if (req.socket?.remoteAddress) {
 95 |     return req.socket.remoteAddress.replace(/^::ffff:/, "");
 96 |   }
 97 |   return undefined;
 98 | }
 99 | 
100 | const server = new McpServer(
101 |   {
102 |     name: "Context7",
103 |     version: "1.0.13",
104 |   },
105 |   {
106 |     instructions:
107 |       "Use this server to retrieve up-to-date documentation and code examples for any library.",
108 |   }
109 | );
110 | 
111 | server.registerTool(
112 |   "resolve-library-id",
113 |   {
114 |     title: "Resolve Context7 Library ID",
115 |     description: `Resolves a package/product name to a Context7-compatible library ID and returns a list of matching libraries.
116 | 
117 | You MUST call this function before 'get-library-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.
118 | 
119 | Selection Process:
120 | 1. Analyze the query to understand what library/package the user is looking for
121 | 2. Return the most relevant match based on:
122 | - Name similarity to the query (exact matches prioritized)
123 | - Description relevance to the query's intent
124 | - Documentation coverage (prioritize libraries with higher Code Snippet counts)
125 | - Trust score (consider libraries with scores of 7-10 more authoritative)
126 | 
127 | Response Format:
128 | - Return the selected library ID in a clearly marked section
129 | - Provide a brief explanation for why this library was chosen
130 | - If multiple good matches exist, acknowledge this but proceed with the most relevant one
131 | - If no good matches exist, clearly state this and suggest query refinements
132 | 
133 | For ambiguous queries, request clarification before proceeding with a best-guess match.`,
134 |     inputSchema: {
135 |       libraryName: z
136 |         .string()
137 |         .describe("Library name to search for and retrieve a Context7-compatible library ID."),
138 |     },
139 |   },
140 |   async ({ libraryName }) => {
141 |     const ctx = requestContext.getStore();
142 |     const searchResponse: SearchResponse = await searchLibraries(
143 |       libraryName,
144 |       ctx?.clientIp,
145 |       ctx?.apiKey
146 |     );
147 | 
148 |     if (!searchResponse.results || searchResponse.results.length === 0) {
149 |       return {
150 |         content: [
151 |           {
152 |             type: "text",
153 |             text: searchResponse.error
154 |               ? searchResponse.error
155 |               : "Failed to retrieve library documentation data from Context7",
156 |           },
157 |         ],
158 |       };
159 |     }
160 | 
161 |     const resultsText = formatSearchResults(searchResponse);
162 | 
163 |     const responseText = `Available Libraries (top matches):
164 | 
165 | Each result includes:
166 | - Library ID: Context7-compatible identifier (format: /org/project)
167 | - Name: Library or package name
168 | - Description: Short summary
169 | - Code Snippets: Number of available code examples
170 | - Trust Score: Authority indicator
171 | - Versions: List of versions if available. Use one of those versions if the user provides a version in their query. The format of the version is /org/project/version.
172 | 
173 | For best results, select libraries based on name match, trust score, snippet coverage, and relevance to your use case.
174 | 
175 | ----------
176 | 
177 | ${resultsText}`;
178 | 
179 |     return {
180 |       content: [
181 |         {
182 |           type: "text",
183 |           text: responseText,
184 |         },
185 |       ],
186 |     };
187 |   }
188 | );
189 | 
190 | server.registerTool(
191 |   "get-library-docs",
192 |   {
193 |     title: "Get Library Docs",
194 |     description:
195 |       "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, UNLESS the user explicitly provides a library ID in the format '/org/project' or '/org/project/version' in their query.",
196 |     inputSchema: {
197 |       context7CompatibleLibraryID: z
198 |         .string()
199 |         .describe(
200 |           "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'."
201 |         ),
202 |       topic: z
203 |         .string()
204 |         .optional()
205 |         .describe("Topic to focus documentation on (e.g., 'hooks', 'routing')."),
206 |       tokens: z
207 |         .preprocess((val) => (typeof val === "string" ? Number(val) : val), z.number())
208 |         .transform((val) => (val < MINIMUM_TOKENS ? MINIMUM_TOKENS : val))
209 |         .optional()
210 |         .describe(
211 |           `Maximum number of tokens of documentation to retrieve (default: ${DEFAULT_TOKENS}). Higher values provide more context but consume more tokens.`
212 |         ),
213 |     },
214 |   },
215 |   async ({ context7CompatibleLibraryID, tokens = DEFAULT_TOKENS, topic = "" }) => {
216 |     const ctx = requestContext.getStore();
217 |     const fetchDocsResponse = await fetchLibraryDocumentation(
218 |       context7CompatibleLibraryID,
219 |       {
220 |         tokens,
221 |         topic,
222 |       },
223 |       ctx?.clientIp,
224 |       ctx?.apiKey
225 |     );
226 | 
227 |     if (!fetchDocsResponse) {
228 |       return {
229 |         content: [
230 |           {
231 |             type: "text",
232 |             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.",
233 |           },
234 |         ],
235 |       };
236 |     }
237 | 
238 |     return {
239 |       content: [
240 |         {
241 |           type: "text",
242 |           text: fetchDocsResponse,
243 |         },
244 |       ],
245 |     };
246 |   }
247 | );
248 | 
249 | async function main() {
250 |   const transportType = TRANSPORT_TYPE;
251 | 
252 |   if (transportType === "http") {
253 |     const initialPort = CLI_PORT ?? DEFAULT_PORT;
254 |     let actualPort = initialPort;
255 | 
256 |     const app = express();
257 |     app.use(express.json());
258 | 
259 |     app.use((req: express.Request, res: express.Response, next: express.NextFunction) => {
260 |       res.setHeader("Access-Control-Allow-Origin", "*");
261 |       res.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,DELETE");
262 |       res.setHeader(
263 |         "Access-Control-Allow-Headers",
264 |         "Content-Type, MCP-Session-Id, MCP-Protocol-Version, X-Context7-API-Key, Context7-API-Key, X-API-Key, Authorization"
265 |       );
266 |       res.setHeader("Access-Control-Expose-Headers", "MCP-Session-Id");
267 | 
268 |       if (req.method === "OPTIONS") {
269 |         res.sendStatus(200);
270 |         return;
271 |       }
272 |       next();
273 |     });
274 | 
275 |     const extractHeaderValue = (value: string | string[] | undefined): string | undefined => {
276 |       if (!value) return undefined;
277 |       return typeof value === "string" ? value : value[0];
278 |     };
279 | 
280 |     const extractBearerToken = (authHeader: string | string[] | undefined): string | undefined => {
281 |       const header = extractHeaderValue(authHeader);
282 |       if (!header) return undefined;
283 | 
284 |       if (header.startsWith("Bearer ")) {
285 |         return header.substring(7).trim();
286 |       }
287 | 
288 |       return header;
289 |     };
290 | 
291 |     const extractApiKey = (req: express.Request): string | undefined => {
292 |       return (
293 |         extractBearerToken(req.headers.authorization) ||
294 |         extractHeaderValue(req.headers["Context7-API-Key"]) ||
295 |         extractHeaderValue(req.headers["X-API-Key"]) ||
296 |         extractHeaderValue(req.headers["context7-api-key"]) ||
297 |         extractHeaderValue(req.headers["x-api-key"]) ||
298 |         extractHeaderValue(req.headers["Context7_API_Key"]) ||
299 |         extractHeaderValue(req.headers["X_API_Key"]) ||
300 |         extractHeaderValue(req.headers["context7_api_key"]) ||
301 |         extractHeaderValue(req.headers["x_api_key"])
302 |       );
303 |     };
304 | 
305 |     app.all("/mcp", async (req: express.Request, res: express.Response) => {
306 |       try {
307 |         const clientIp = getClientIp(req);
308 |         const apiKey = extractApiKey(req);
309 | 
310 |         const transport = new StreamableHTTPServerTransport({
311 |           sessionIdGenerator: undefined,
312 |           enableJsonResponse: true,
313 |         });
314 | 
315 |         res.on("close", () => {
316 |           transport.close();
317 |         });
318 | 
319 |         await requestContext.run({ clientIp, apiKey }, async () => {
320 |           await server.connect(transport);
321 |           await transport.handleRequest(req, res, req.body);
322 |         });
323 |       } catch (error) {
324 |         console.error("Error handling MCP request:", error);
325 |         if (!res.headersSent) {
326 |           res.status(500).json({
327 |             jsonrpc: "2.0",
328 |             error: { code: -32603, message: "Internal server error" },
329 |             id: null,
330 |           });
331 |         }
332 |       }
333 |     });
334 | 
335 |     app.get("/ping", (_req: express.Request, res: express.Response) => {
336 |       res.json({ status: "ok", message: "pong" });
337 |     });
338 | 
339 |     // Catch-all 404 handler - must be after all other routes
340 |     app.use((_req: express.Request, res: express.Response) => {
341 |       res.status(404).json({
342 |         error: "not_found",
343 |         message: "Endpoint not found. Use /mcp for MCP protocol communication.",
344 |       });
345 |     });
346 | 
347 |     const startServer = (port: number, maxAttempts = 10) => {
348 |       const httpServer = app.listen(port, () => {
349 |         actualPort = port;
350 |         console.error(
351 |           `Context7 Documentation MCP Server running on HTTP at http://localhost:${actualPort}/mcp`
352 |         );
353 |       });
354 | 
355 |       httpServer.on("error", (err: NodeJS.ErrnoException) => {
356 |         if (err.code === "EADDRINUSE" && port < initialPort + maxAttempts) {
357 |           console.warn(`Port ${port} is in use, trying port ${port + 1}...`);
358 |           startServer(port + 1, maxAttempts);
359 |         } else {
360 |           console.error(`Failed to start server: ${err.message}`);
361 |           process.exit(1);
362 |         }
363 |       });
364 |     };
365 | 
366 |     startServer(initialPort);
367 |   } else {
368 |     const apiKey = cliOptions.apiKey || process.env.CONTEXT7_API_KEY;
369 |     const transport = new StdioServerTransport();
370 | 
371 |     await requestContext.run({ apiKey }, async () => {
372 |       await server.connect(transport);
373 |     });
374 | 
375 |     console.error("Context7 Documentation MCP Server running on stdio");
376 |   }
377 | }
378 | 
379 | main().catch((error) => {
380 |   console.error("Fatal error in main():", error);
381 |   process.exit(1);
382 | });
383 | 
```