@upstash/context7-mcp 3.2.2 → 3.2.3

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
@@ -15,6 +15,7 @@ import { randomUUID } from "node:crypto";
15
15
  import { createSessionStore } from "./lib/sessionStore.js";
16
16
  import { SERVER_VERSION, RESOURCE_URL, AUTH_SERVER_URL, OPENAI_APPS_CHALLENGE_TOKEN, } from "./lib/constants.js";
17
17
  import { maybeElicitAuthSignIn } from "./lib/auth/auth-prompt.js";
18
+ import { getClientIp } from "./lib/client-ip.js";
18
19
  /** Default HTTP server port */
19
20
  const DEFAULT_PORT = 3000;
20
21
  // Parse CLI arguments using commander
@@ -73,30 +74,6 @@ function getClientContext() {
73
74
  sessionId: stdioSessionId,
74
75
  };
75
76
  }
76
- /**
77
- * Extract client IP address from request headers.
78
- * Handles X-Forwarded-For header for proxied requests.
79
- */
80
- function getClientIp(req) {
81
- const forwardedFor = req.headers["x-forwarded-for"] || req.headers["X-Forwarded-For"];
82
- if (forwardedFor) {
83
- const ips = Array.isArray(forwardedFor) ? forwardedFor[0] : forwardedFor;
84
- const ipList = ips.split(",").map((ip) => ip.trim());
85
- for (const ip of ipList) {
86
- const plainIp = ip.replace(/^::ffff:/, "");
87
- if (!plainIp.startsWith("10.") &&
88
- !plainIp.startsWith("192.168.") &&
89
- !/^172\.(1[6-9]|2[0-9]|3[0-1])\./.test(plainIp)) {
90
- return plainIp;
91
- }
92
- }
93
- return ipList[0].replace(/^::ffff:/, "");
94
- }
95
- if (req.socket?.remoteAddress) {
96
- return req.socket.remoteAddress.replace(/^::ffff:/, "");
97
- }
98
- return undefined;
99
- }
100
77
  function createMcpServer() {
101
78
  const server = new McpServer({
102
79
  name: "Context7",
@@ -110,7 +87,7 @@ function createMcpServer() {
110
87
  },
111
88
  ],
112
89
  }, {
113
- 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.
90
+ 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.
114
91
 
115
92
  Do not use for: refactoring, writing scripts from scratch, debugging business logic, code review, or general programming concepts.`,
116
93
  });
@@ -203,7 +180,7 @@ Do not call this tool more than 3 times per question.`,
203
180
  .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'."),
204
181
  query: z
205
182
  .string()
206
- .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."),
183
+ .describe("The question or task you need help with, scoped to a single concept. Be specific and include relevant details, but keep each query to one topic — if the user's question spans multiple distinct concepts, make a separate call per concept instead of combining them, unless the question is about how the concepts interact. Good: 'How to set up authentication with JWT in Express.js' or 'React useEffect cleanup function examples'. Bad (too vague): 'auth' or 'hooks'. Bad (too broad): 'routing and auth and caching in Next.js'. 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."),
207
184
  },
208
185
  annotations: {
209
186
  readOnlyHint: true,
@@ -0,0 +1,60 @@
1
+ function stripIpv4MappedPrefix(ip) {
2
+ return ip.replace(/^::ffff:/i, "");
3
+ }
4
+ /**
5
+ * Returns true for RFC1918, CGNAT, loopback, link-local, and IPv6 private ranges.
6
+ */
7
+ export function isPrivateOrLocalIp(ip) {
8
+ const plainIp = stripIpv4MappedPrefix(ip).toLowerCase();
9
+ if (plainIp.includes(".")) {
10
+ return (plainIp.startsWith("10.") ||
11
+ plainIp.startsWith("192.168.") ||
12
+ /^172\.(1[6-9]|2[0-9]|3[0-1])\./.test(plainIp) ||
13
+ /^100\.(6[4-9]|[7-9][0-9]|1[01][0-9]|12[0-7])\./.test(plainIp) ||
14
+ plainIp.startsWith("127.") ||
15
+ plainIp.startsWith("169.254."));
16
+ }
17
+ // ::1 loopback in any textual form (e.g. "0::1", "0:0:0:0:0:0:0:1")
18
+ if (/^[0:]+1$/.test(plainIp)) {
19
+ return true;
20
+ }
21
+ // First hextets in fe80::/10 and fc00::/7 start with a non-zero digit, so a
22
+ // valid textual form always spells out all 4 digits.
23
+ // fe80::/10 link-local
24
+ if (/^fe[89ab][0-9a-f]:/.test(plainIp)) {
25
+ return true;
26
+ }
27
+ // fc00::/7 unique local
28
+ if (/^f[cd][0-9a-f]{2}:/.test(plainIp)) {
29
+ return true;
30
+ }
31
+ return false;
32
+ }
33
+ function pickClientIpFromForwardedList(ipList) {
34
+ for (const ip of ipList) {
35
+ const plainIp = stripIpv4MappedPrefix(ip);
36
+ if (!isPrivateOrLocalIp(plainIp)) {
37
+ return plainIp;
38
+ }
39
+ }
40
+ if (ipList.length === 0) {
41
+ return undefined;
42
+ }
43
+ return stripIpv4MappedPrefix(ipList[0]);
44
+ }
45
+ /**
46
+ * Extract client IP address from request headers.
47
+ * Handles X-Forwarded-For header for proxied requests.
48
+ */
49
+ export function getClientIp(req) {
50
+ const forwardedFor = req.headers["x-forwarded-for"] || req.headers["X-Forwarded-For"];
51
+ if (forwardedFor) {
52
+ const ips = Array.isArray(forwardedFor) ? forwardedFor[0] : forwardedFor;
53
+ const ipList = ips.split(",").map((ip) => ip.trim());
54
+ return pickClientIpFromForwardedList(ipList);
55
+ }
56
+ if (req.socket?.remoteAddress) {
57
+ return stripIpv4MappedPrefix(req.socket.remoteAddress);
58
+ }
59
+ return undefined;
60
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@upstash/context7-mcp",
3
- "version": "3.2.2",
3
+ "version": "3.2.3",
4
4
  "mcpName": "io.github.upstash/context7",
5
5
  "description": "MCP server for Context7",
6
6
  "repository": {