@upstash/context7-mcp 3.1.0 → 3.2.0
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 +38 -7
- package/dist/index.js +16 -4
- package/dist/lib/auth/auth-prompt.js +62 -28
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -52,7 +52,7 @@ Check out our [project addition guide](https://context7.com/docs/adding-librarie
|
|
|
52
52
|
### Requirements
|
|
53
53
|
|
|
54
54
|
- Node.js >= v18.0.0
|
|
55
|
-
- Cursor, Claude Code, VSCode,
|
|
55
|
+
- Cursor, Claude Code, VSCode, Devin Desktop or another MCP Client
|
|
56
56
|
- Context7 API Key (Optional) for higher rate limits and private repositories (Get yours by creating an account at [context7.com/dashboard](https://context7.com/dashboard))
|
|
57
57
|
|
|
58
58
|
> [!TIP]
|
|
@@ -60,7 +60,7 @@ Check out our [project addition guide](https://context7.com/docs/adding-librarie
|
|
|
60
60
|
>
|
|
61
61
|
> After installing Context7 (see instructions below), enhance your workflow by adding a rule so you don't have to type `use context7` in every prompt. Define a simple rule in your MCP client's rule section to automatically invoke Context7 on any code question:
|
|
62
62
|
>
|
|
63
|
-
> - For
|
|
63
|
+
> - For Devin Desktop, in `.devin/rules/` directory
|
|
64
64
|
> - For Cursor, from `Cursor Settings > Rules` section
|
|
65
65
|
> - For Claude Code, in `CLAUDE.md` file
|
|
66
66
|
> - Or the equivalent in your MCP client
|
|
@@ -172,11 +172,11 @@ amp mcp add context7 --header "CONTEXT7_API_KEY=YOUR_API_KEY" https://mcp.contex
|
|
|
172
172
|
</details>
|
|
173
173
|
|
|
174
174
|
<details>
|
|
175
|
-
<summary><b>Install in
|
|
175
|
+
<summary><b>Install in Devin Desktop</b></summary>
|
|
176
176
|
|
|
177
|
-
Add this to your
|
|
177
|
+
Add this to your Devin Desktop MCP config file. See [Devin Desktop MCP docs](https://docs.devin.ai/desktop/cascade/mcp) for more info.
|
|
178
178
|
|
|
179
|
-
####
|
|
179
|
+
#### Devin Desktop Remote Server Connection
|
|
180
180
|
|
|
181
181
|
```json
|
|
182
182
|
{
|
|
@@ -191,7 +191,7 @@ Add this to your Windsurf MCP config file. See [Windsurf MCP docs](https://docs.
|
|
|
191
191
|
}
|
|
192
192
|
```
|
|
193
193
|
|
|
194
|
-
####
|
|
194
|
+
#### Devin Desktop Local Server Connection
|
|
195
195
|
|
|
196
196
|
```json
|
|
197
197
|
{
|
|
@@ -863,7 +863,7 @@ If you prefer to run the MCP server in a Docker container:
|
|
|
863
863
|
# EXPOSE 3000
|
|
864
864
|
|
|
865
865
|
# Default command to run the server
|
|
866
|
-
CMD ["context7-mcp"]
|
|
866
|
+
CMD ["context7-mcp", "--transport", "stdio"]
|
|
867
867
|
```
|
|
868
868
|
|
|
869
869
|
</details>
|
|
@@ -897,6 +897,37 @@ If you prefer to run the MCP server in a Docker container:
|
|
|
897
897
|
|
|
898
898
|
_Note: This is an example configuration. Please refer to the specific examples for your MCP client (like Cursor, VS Code, etc.) earlier in this README to adapt the structure (e.g., `mcpServers` vs `servers`). Also, ensure the image name in `args` matches the tag used during the `docker build` command._
|
|
899
899
|
|
|
900
|
+
If you use the Docker MCP Toolkit image (`mcp/context7`) with a stdio-based client,
|
|
901
|
+
set `MCP_TRANSPORT=stdio` so the container starts with stdio transport instead of its
|
|
902
|
+
HTTP default. For Cline, Roo Code, and Claude Desktop, use:
|
|
903
|
+
|
|
904
|
+
```json
|
|
905
|
+
{
|
|
906
|
+
"mcpServers": {
|
|
907
|
+
"context7": {
|
|
908
|
+
"command": "docker",
|
|
909
|
+
"args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT=stdio", "mcp/context7"]
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
```
|
|
914
|
+
|
|
915
|
+
For VS Code, use the same Docker command in the `servers` format:
|
|
916
|
+
|
|
917
|
+
```json
|
|
918
|
+
{
|
|
919
|
+
"servers": {
|
|
920
|
+
"context7": {
|
|
921
|
+
"type": "stdio",
|
|
922
|
+
"command": "docker",
|
|
923
|
+
"args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT=stdio", "mcp/context7"]
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
```
|
|
928
|
+
|
|
929
|
+
Keep using the remote server URL for HTTP-based clients.
|
|
930
|
+
|
|
900
931
|
</details>
|
|
901
932
|
|
|
902
933
|
<details>
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { ListPromptsRequestSchema, ListResourcesRequestSchema, ListResourceTemplatesRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
4
5
|
import { z } from "zod";
|
|
5
6
|
import { searchLibraries, fetchLibraryContext } from "./lib/api.js";
|
|
6
7
|
import { formatSearchResults, extractClientInfoFromUserAgent } from "./lib/utils.js";
|
|
@@ -13,7 +14,7 @@ import { AsyncLocalStorage } from "async_hooks";
|
|
|
13
14
|
import { randomUUID } from "node:crypto";
|
|
14
15
|
import { createSessionStore } from "./lib/sessionStore.js";
|
|
15
16
|
import { SERVER_VERSION, RESOURCE_URL, AUTH_SERVER_URL, OPENAI_APPS_CHALLENGE_TOKEN, } from "./lib/constants.js";
|
|
16
|
-
import {
|
|
17
|
+
import { maybeElicitAuthSignIn } from "./lib/auth/auth-prompt.js";
|
|
17
18
|
/** Default HTTP server port */
|
|
18
19
|
const DEFAULT_PORT = 3000;
|
|
19
20
|
// Parse CLI arguments using commander
|
|
@@ -167,22 +168,24 @@ IMPORTANT: Do not call this tool more than 3 times per question. If you cannot f
|
|
|
167
168
|
const searchResponse = await searchLibraries(query, libraryName, ctx);
|
|
168
169
|
if (!searchResponse.results || searchResponse.results.length === 0) {
|
|
169
170
|
const text = searchResponse.error ?? "No libraries found matching the provided name.";
|
|
171
|
+
maybeElicitAuthSignIn(server, ctx);
|
|
170
172
|
return {
|
|
171
173
|
content: [
|
|
172
174
|
{
|
|
173
175
|
type: "text",
|
|
174
|
-
text
|
|
176
|
+
text,
|
|
175
177
|
},
|
|
176
178
|
],
|
|
177
179
|
};
|
|
178
180
|
}
|
|
179
181
|
const resultsText = formatSearchResults(searchResponse);
|
|
180
182
|
const responseText = `Available Libraries:\n\n${resultsText}`;
|
|
183
|
+
maybeElicitAuthSignIn(server, ctx);
|
|
181
184
|
return {
|
|
182
185
|
content: [
|
|
183
186
|
{
|
|
184
187
|
type: "text",
|
|
185
|
-
text:
|
|
188
|
+
text: responseText,
|
|
186
189
|
},
|
|
187
190
|
],
|
|
188
191
|
};
|
|
@@ -211,15 +214,24 @@ Do not call this tool more than 3 times per question.`,
|
|
|
211
214
|
}, async ({ query, libraryId }) => {
|
|
212
215
|
const ctx = getClientContext();
|
|
213
216
|
const response = await fetchLibraryContext({ query, libraryId }, ctx);
|
|
217
|
+
maybeElicitAuthSignIn(server, ctx);
|
|
214
218
|
return {
|
|
215
219
|
content: [
|
|
216
220
|
{
|
|
217
221
|
type: "text",
|
|
218
|
-
text:
|
|
222
|
+
text: response.data,
|
|
219
223
|
},
|
|
220
224
|
],
|
|
221
225
|
};
|
|
222
226
|
});
|
|
227
|
+
server.server.registerCapabilities({ prompts: {}, resources: {} });
|
|
228
|
+
server.server.setRequestHandler(ListPromptsRequestSchema, async () => ({ prompts: [] }));
|
|
229
|
+
server.server.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
|
230
|
+
resources: [],
|
|
231
|
+
}));
|
|
232
|
+
server.server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => ({
|
|
233
|
+
resourceTemplates: [],
|
|
234
|
+
}));
|
|
223
235
|
return server;
|
|
224
236
|
}
|
|
225
237
|
const GLOBAL_ALIASES = {
|
|
@@ -14,38 +14,72 @@ function clientFlagForCli(ide) {
|
|
|
14
14
|
return "--gemini";
|
|
15
15
|
return "";
|
|
16
16
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
* authenticated callers or when the signal wasn't set.
|
|
22
|
-
*/
|
|
23
|
-
export function appendAuthPrompt(text, ctx) {
|
|
24
|
-
if (ctx.apiKey || !ctx.shouldPrompt)
|
|
25
|
-
return text;
|
|
26
|
-
return `${text}\n\n${buildAuthPrompt({
|
|
27
|
-
clientIde: ctx.clientInfo?.ide,
|
|
28
|
-
transport: ctx.transport,
|
|
29
|
-
})}`;
|
|
30
|
-
}
|
|
31
|
-
export function buildAuthPrompt(opts) {
|
|
32
|
-
const flag = clientFlagForCli(opts.clientIde);
|
|
33
|
-
const transportFlag = opts.transport === "stdio" ? " --stdio" : "";
|
|
34
|
-
const command = flag
|
|
17
|
+
function buildAuthCommand(clientIde, transport) {
|
|
18
|
+
const flag = clientFlagForCli(clientIde);
|
|
19
|
+
const transportFlag = transport === "stdio" ? " --stdio" : "";
|
|
20
|
+
return flag
|
|
35
21
|
? `npx ctx7 setup ${flag} --mcp${transportFlag} -y`
|
|
36
22
|
: `npx ctx7 setup --mcp${transportFlag}`;
|
|
23
|
+
}
|
|
24
|
+
function buildElicitMessage(clientIde, transport) {
|
|
25
|
+
const command = buildAuthCommand(clientIde, transport);
|
|
37
26
|
return [
|
|
38
|
-
"
|
|
39
|
-
`**[Heads up] Notice for the user — please relay the following section to them and offer to run the command for them after their confirmation:**`,
|
|
27
|
+
"You're using Context7 anonymously. To unlock free higher rate limits, run this in your terminal:",
|
|
40
28
|
"",
|
|
41
|
-
|
|
42
|
-
">",
|
|
43
|
-
"> ```",
|
|
44
|
-
`> ${command}`,
|
|
45
|
-
"> ```",
|
|
46
|
-
">",
|
|
47
|
-
"> It opens the browser, completes sign-in, and writes credentials into your MCP client config. After it finishes, disable + re-enable the Context7 MCP server in your editor so the new credentials take effect.",
|
|
29
|
+
` ${command}`,
|
|
48
30
|
"",
|
|
49
|
-
|
|
31
|
+
"It opens your browser, signs you in, and writes credentials into your MCP client config.",
|
|
32
|
+
"After it finishes, disable then re-enable the Context7 MCP server in your editor so the new credentials take effect.",
|
|
50
33
|
].join("\n");
|
|
51
34
|
}
|
|
35
|
+
// User-facing strings double as enum const values: keeps the schema in the
|
|
36
|
+
// simpler `enum: [...]` shape, which clients render more reliably than
|
|
37
|
+
// `oneOf` with separate `const`/`title`.
|
|
38
|
+
const CHOICE_RUN_SETUP = "I'll run the command to sign in";
|
|
39
|
+
const CHOICE_STAY_ANON = "Continue anonymously with smaller limits";
|
|
40
|
+
/**
|
|
41
|
+
* Fires a form-mode elicitation that surfaces a sign-in nudge in the client UI
|
|
42
|
+
* when the backend has signaled (via `X-Context7-Auth-Prompt: 1`, captured on
|
|
43
|
+
* `ctx.shouldPrompt` in api.ts) that the anonymous caller should be prompted
|
|
44
|
+
* to authenticate.
|
|
45
|
+
*
|
|
46
|
+
* The message is delivered out-of-band to the human via the client, not into
|
|
47
|
+
* the tool result the LLM reads, so it does not trip prompt-injection guards.
|
|
48
|
+
*
|
|
49
|
+
* The backend owns how often this fires: it sets the header at most once per
|
|
50
|
+
* MCP session, so the server holds no suppression state — it simply shows the
|
|
51
|
+
* dialog whenever the header is present. The command itself is shown in the
|
|
52
|
+
* dialog message for the user to copy; the server does not attempt to drive
|
|
53
|
+
* the client to run it.
|
|
54
|
+
*
|
|
55
|
+
* No-op for authenticated callers, when the signal wasn't set, or when the
|
|
56
|
+
* client did not advertise the `elicitation` capability. Fire-and-forget:
|
|
57
|
+
* never blocks or fails the surrounding tool response.
|
|
58
|
+
*/
|
|
59
|
+
export function maybeElicitAuthSignIn(server, ctx) {
|
|
60
|
+
if (ctx.apiKey || !ctx.shouldPrompt)
|
|
61
|
+
return;
|
|
62
|
+
if (!server.server.getClientCapabilities()?.elicitation)
|
|
63
|
+
return;
|
|
64
|
+
void server.server
|
|
65
|
+
.elicitInput({
|
|
66
|
+
message: buildElicitMessage(ctx.clientInfo?.ide, ctx.transport),
|
|
67
|
+
requestedSchema: {
|
|
68
|
+
type: "object",
|
|
69
|
+
properties: {
|
|
70
|
+
choice: {
|
|
71
|
+
type: "string",
|
|
72
|
+
title: "How would you like to continue?",
|
|
73
|
+
enum: [CHOICE_RUN_SETUP, CHOICE_STAY_ANON],
|
|
74
|
+
default: CHOICE_RUN_SETUP,
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
required: ["choice"],
|
|
78
|
+
},
|
|
79
|
+
})
|
|
80
|
+
.catch(() => {
|
|
81
|
+
// Client may not support elicitation despite the capability flag, or
|
|
82
|
+
// the session may have closed before the user responded. Either way,
|
|
83
|
+
// a missed nudge should never affect the tool result.
|
|
84
|
+
});
|
|
85
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@upstash/context7-mcp",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"mcpName": "io.github.upstash/context7",
|
|
5
5
|
"description": "MCP server for Context7",
|
|
6
6
|
"repository": {
|
|
@@ -33,14 +33,14 @@
|
|
|
33
33
|
},
|
|
34
34
|
"homepage": "https://github.com/upstash/context7#readme",
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
36
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
37
37
|
"@types/express": "^5.0.4",
|
|
38
38
|
"@upstash/redis": "^1.38.0",
|
|
39
39
|
"commander": "^14.0.0",
|
|
40
40
|
"express": "^5.1.0",
|
|
41
41
|
"jose": "^6.1.3",
|
|
42
|
-
"undici": "^
|
|
43
|
-
"zod": "^4.3
|
|
42
|
+
"undici": "^7.27.0",
|
|
43
|
+
"zod": "^4.4.3"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@types/node": "^25.0.3",
|