diff --git a/.changeset/bump-jose-6-2-3.md b/.changeset/bump-jose-6-2-3.md new file mode 100644 index 000000000..4abefa7b6 --- /dev/null +++ b/.changeset/bump-jose-6-2-3.md @@ -0,0 +1,5 @@ +--- +"@upstash/context7-mcp": patch +--- + +Bump jose from 6.1.3 to 6.2.3. diff --git a/.github/workflows/canary-release.yml b/.github/workflows/canary-release.yml index daa173a27..b11fe8d72 100644 --- a/.github/workflows/canary-release.yml +++ b/.github/workflows/canary-release.yml @@ -16,7 +16,7 @@ jobs: contents: read steps: - name: Checkout Repo - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: ref: ${{ inputs.branch || github.ref }} diff --git a/.github/workflows/ecr-deploy.yml b/.github/workflows/ecr-deploy.yml index 4a58bb623..6e849389e 100644 --- a/.github/workflows/ecr-deploy.yml +++ b/.github/workflows/ecr-deploy.yml @@ -14,7 +14,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v6 diff --git a/.github/workflows/mcp-registry.yml b/.github/workflows/mcp-registry.yml index 9ec0446c9..b5adecf42 100644 --- a/.github/workflows/mcp-registry.yml +++ b/.github/workflows/mcp-registry.yml @@ -17,7 +17,7 @@ jobs: contents: read steps: - name: Checkout Repo - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Setup Node uses: actions/setup-node@v6 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a38555dc2..dd7e22e81 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,7 +16,7 @@ jobs: pull-requests: write steps: - name: Checkout Repo - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Setup Node uses: actions/setup-node@v6 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 19680955c..3100beafb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -23,7 +23,7 @@ jobs: test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - name: Setup Node uses: actions/setup-node@v6 @@ -42,7 +42,7 @@ jobs: echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT - name: Cache pnpm dependencies - uses: actions/cache@v5 + uses: actions/cache@v6 with: path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} diff --git a/.github/workflows/triage-library-report.yml b/.github/workflows/triage-library-report.yml new file mode 100644 index 000000000..f19a44a92 --- /dev/null +++ b/.github/workflows/triage-library-report.yml @@ -0,0 +1,40 @@ +name: Triage library report + +# When a "Library Report" issue is opened, forward it to the triage service so the agent can +# analyze it and post its findings as a comment. +# +# Cross-repo note: this issue lives here (upstash/context7) but the triage agent + its secrets +# (Redis/Vector/model creds + a token that can comment on this repo) live in the context7app +# deployment. A GitHub Action here cannot run the agent, so it only forwards the issue number to +# the app's endpoint with a shared secret; the app runs the read-only agent and posts the comment. +# +# Required repo secrets (Settings → Secrets and variables → Actions): +# TRIAGE_WEBHOOK_URL https:///api/triage/analyze +# TRIAGE_WEBHOOK_SECRET same value as TRIAGE_WEBHOOK_SECRET in the context7app environment + +on: + issues: + types: [opened] + +permissions: {} # no GITHUB_TOKEN needed — the app comments with its own token + +jobs: + triage: + if: contains(github.event.issue.title, 'Library Report') + runs-on: ubuntu-latest + steps: + - name: Forward to triage service + env: + TRIAGE_WEBHOOK_URL: ${{ secrets.TRIAGE_WEBHOOK_URL }} + TRIAGE_WEBHOOK_SECRET: ${{ secrets.TRIAGE_WEBHOOK_SECRET }} + ISSUE_NUMBER: ${{ github.event.issue.number }} + run: | + if [ -z "$TRIAGE_WEBHOOK_URL" ]; then + echo "TRIAGE_WEBHOOK_URL not configured; skipping triage." + exit 0 + fi + echo "Triaging issue #$ISSUE_NUMBER" + curl -fsS --max-time 180 -X POST "$TRIAGE_WEBHOOK_URL" \ + -H "Authorization: Bearer $TRIAGE_WEBHOOK_SECRET" \ + -H "Content-Type: application/json" \ + -d "{\"issueNumber\": $ISSUE_NUMBER}" diff --git a/docs/adding-libraries.mdx b/docs/adding-libraries.mdx index 3ee7c6a93..952049090 100644 --- a/docs/adding-libraries.mdx +++ b/docs/adding-libraries.mdx @@ -30,6 +30,14 @@ The fastest way to add a library is through the web interface: This page is for **public** libraries. To add internal or private documentation, see the [Private Sources](/howto/private-sources) guide (requires a Pro or Enterprise plan). +## What Gets Indexed + +Context7 parses documentation files — `.md`, `.mdx`, `.markdown`, `.rst`, `.txt`, and `.ipynb` — and extracts the code examples and explanations they contain. Raw source code files (`.py`, `.ts`, `.go`, and so on) are not indexed when documentation is present; a library's docs are expected to show the important usage examples. + +If a repository contains little or no documentation content, Context7 falls back to generating examples from the source code itself. For public repositories this fallback is automatic — there is no setting to turn it on or off. For [private repositories](/howto/private-sources), generating docs from source code is opt-in: check **Generate docs** when adding the source, or pass the `generateDocs` flag via the [API](/api-guide). + +To control which folders and files are scanned, set included folders when submitting, or commit a `context7.json` file to the repository (see [Library Owners](/library-owners)). + ## Maintain the Library? If you own or maintain the library, you can take control of how it appears in Context7 — configure parsing with `context7.json`, manage versions through a web admin panel, and get higher refresh limits. diff --git a/docs/agentic-tools/ai-sdk/tools/query-docs.mdx b/docs/agentic-tools/ai-sdk/tools/query-docs.mdx index 84525a4f0..046315ee2 100644 --- a/docs/agentic-tools/ai-sdk/tools/query-docs.mdx +++ b/docs/agentic-tools/ai-sdk/tools/query-docs.mdx @@ -63,7 +63,7 @@ The tool accepts the following inputs from the AI model: - 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 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". ### Output Format diff --git a/docs/api-guide.mdx b/docs/api-guide.mdx index f6f32aa62..1be3de0ba 100644 --- a/docs/api-guide.mdx +++ b/docs/api-guide.mdx @@ -59,9 +59,10 @@ You can pin a specific version with either `/owner/repo/` or `/owner/re ### Complete Workflow Example ```python +import os import requests -headers = {"Authorization": "Bearer CONTEXT7_API_KEY"} +headers = {"Authorization": f"Bearer {os.environ['CONTEXT7_API_KEY']}"} # Step 1: Search for the library search_response = requests.get( diff --git a/docs/docs.json b/docs/docs.json index bf57e3969..7c1f29bc6 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -73,10 +73,12 @@ "group": "On-Premise", "pages": [ "enterprise/on-premise", + "enterprise/changelog", { "group": "Integrations", "pages": [ - "enterprise/integrations/github" + "enterprise/integrations/github", + "enterprise/integrations/confluence" ] }, { @@ -119,6 +121,7 @@ "enterprise/api/parse/parse-a-git-repository", "enterprise/api/parse/parse-an-openapi-spec-by-url", "enterprise/api/parse/upload-an-openapi-spec-file", + "enterprise/api/parse/import-libraries", "enterprise/api/parse/parse-a-website", "enterprise/api/parse/refresh-a-library", "enterprise/api/parse/get-parse-status" diff --git a/docs/enterprise/api/parse/import-libraries.mdx b/docs/enterprise/api/parse/import-libraries.mdx new file mode 100644 index 000000000..c51bbe989 --- /dev/null +++ b/docs/enterprise/api/parse/import-libraries.mdx @@ -0,0 +1,5 @@ +--- +title: "Import a library bundle" +description: "Import pre-parsed libraries into an airgapped on-premise install, as a JSON body or a Context7 Cloud export file" +openapi: "openapi-enterprise.json POST /import-libraries" +--- diff --git a/docs/enterprise/changelog.mdx b/docs/enterprise/changelog.mdx new file mode 100644 index 000000000..a3ac731f6 --- /dev/null +++ b/docs/enterprise/changelog.mdx @@ -0,0 +1,81 @@ +--- +title: "Changelog" +description: "Release notes for the Context7 On-Premise image." +--- + + + +### Added + +- Improved the relevance of responses from the MCP server and API through new reranking capabilities. +- Added support for GitOps-based routing for Confluence and other on-premise data parsers. + + + + + +### Changed + +- Improved compatibility with OpenID Connect (OIDC) providers adhering to RFC 9207. + +### Security + +- Enhanced the security of the enterprise OpenID Connect (OIDC) Single Sign-On (SSO) flow. + + + + + +### Added + +- Added a REST API for programmatic library import. + + + + + +### Added + +- Added support for ingesting content from Confluence Cloud and Data Center. +- Added full end-to-end integration with GitHub Enterprise Server. +- Added support for ingesting content from llms.txt files. + +### Fixed + +- Improved parsing robustness by preventing process hangs and cleaning up orphaned staging data. + + + + + +### Fixed + +- Fixed the path used for GitHub Apps on GitHub Enterprise Server. + + + + + +### Fixed + +- Fixed the GitHub App client to correctly target the on-premise GitHub Enterprise Server API. + + + + + +### Added + +- GitOps declarative repository management: define the repositories to index in a Git manifest (`repos.yaml`) and have Context7 reconcile them on startup, on a schedule, on manual trigger, and on GitHub push webhooks. Supports per-repo branch, folder, and file filters, plus pruning of repositories removed from the manifest. +- Generic OIDC single sign-on for the on-premise dashboard. +- Configure a GitHub Enterprise Server host from the dashboard so the GitHub App and tokens target your own server instead of github.com. + + + + + +### Added + +- Initial tracked release of the Context7 On-Premise image. + + diff --git a/docs/enterprise/gitops.mdx b/docs/enterprise/gitops.mdx index 1f91fc3d5..64d147fde 100644 --- a/docs/enterprise/gitops.mdx +++ b/docs/enterprise/gitops.mdx @@ -4,7 +4,7 @@ sidebarTitle: "GitOps" description: "Manage the list of indexed repositories declaratively from a Git repo and reconcile your on-premise instance to match it" --- -GitOps lets you keep the list of repositories Context7 indexes in a Git repository, instead of adding them one by one in the dashboard. Context7 reads a manifest file from that repo and reconciles its own state to match: it indexes anything new, re-indexes anything whose settings changed, and (optionally) removes anything you have deleted from the manifest. +GitOps lets you keep the list of sources Context7 indexes in a Git repository, instead of adding them one by one in the dashboard. Alongside Git repositories, a source can be a Confluence space, a website, an llms.txt index, or an OpenAPI specification. Context7 reads a manifest file from that repo and reconciles its own state to match: it indexes anything new, re-indexes anything whose settings changed, and (optionally) removes anything you have deleted from the manifest. The point is to keep your configuration in code. After a disaster, or when standing up a second environment, you point a fresh instance at the manifest repository and the entire index rebuilds itself with no manual clicks. @@ -24,10 +24,10 @@ If you set up the GitHub App **before** GitOps was available, it is not subscrib You commit a manifest file (by default `repos.yaml`) to a Git repository. Context7 clones that repository using the same Git credentials you already configured under **Settings → Integrations** (a GitHub App or a personal access token), reads the manifest, and compares it against the repositories it currently has indexed. -For each repository in the manifest, Context7 decides what to do: +For each source in the manifest, Context7 decides what to do: - **Add** it if it is in the manifest but not yet indexed. -- **Re-index** it if its manifest entry changed, for example a different branch or new folder filters, or if its last indexing run did not finish. +- **Re-index** it if its manifest entry changed, for example a different branch, new folder filters, or a changed page limit, or if its last indexing run did not finish. - **Leave it untouched** if it already matches. Running a sync with no changes is a safe no-op. When pruning is enabled, repositories that you remove from the manifest are also deleted from the instance. Pruning only ever removes repositories that GitOps itself added; repositories you add by hand in the dashboard are never touched. @@ -38,33 +38,55 @@ GitOps is the source of truth only for the repositories listed in the manifest. ## The manifest file -The manifest is a YAML file with a list of repositories under `repos`. An optional `defaults` block applies to every entry unless an entry overrides it. +The manifest is a YAML file with a list of sources under `repos`. An optional `defaults` block applies to every entry unless an entry overrides it. + +An entry's `type` selects which parser handles it. Omit `type` and the entry is treated as a Git repository, so manifests written before multi-source support keep working unchanged. ```yaml repos.yaml defaults: indexSourceCode: false branch: "" # empty means the repository's default branch repos: + # Git repository (the default when type is omitted) - url: https://github.com/acme/payments branch: main folders: [docs] excludeFolders: [node_modules] - - url: https://github.com/acme/billing - indexSourceCode: true + + # Confluence space (uses the connection from Settings → Integrations) + - url: https://acme.atlassian.net/wiki + type: confluence + spaceKey: TEAM + + # llms.txt index + - url: https://docs.acme.com/llms.txt + type: llmstxt + + # Website crawl + - url: https://docs.acme.com/ + type: website + maxPages: 200 + + # OpenAPI specification + - url: https://petstore.swagger.io/v3/openapi.json + type: openapi ``` -Each entry accepts the same options as the **Add Repository** form in the dashboard: +Every entry needs a `url` (or `repoUrl`). The remaining fields depend on `type`: + +| `type` | Fields | +| ------ | ------ | +| `git` (default) | `branch`, `folders`, `excludeFolders`, `excludeFiles`, `indexSourceCode` — the same options as the **Add Repository** form. | +| `confluence` | `spaceKey` (required), `spaceId` (Cloud only, resolved automatically from the key when omitted), `spaceName`, `title`, `description`. The whole space is indexed. | +| `website` | `maxPages`, `maxDepth`, `excludePatterns`, `title`, `description`. | +| `llmstxt` | `maxPages`, `title`, `description`. The URL must point to an `llms.txt`, `llms-full.txt`, or `llms-small.txt` file. | +| `openapi` | `title`, `description`. | -| Field | Description | -| ----- | ----------- | -| `url` (or `repoUrl`) | The Git repository to index. Required. | -| `branch` | Branch to index. Leave blank to use the repository's default branch. | -| `folders` | Only index these folders. Omit to index the whole repository. | -| `excludeFolders` | Folders to skip. | -| `excludeFiles` | File patterns to skip, for example `["*.test.md"]`. | -| `indexSourceCode` | Whether to also generate documentation from source code. Defaults to `false`. | + +Confluence entries reuse the site URL and API token you set under **Settings → Integrations → Confluence**, so no credentials go in the manifest. Website, llms.txt, and OpenAPI sources are fetched over public HTTP and need no credentials. Configure Confluence before adding Confluence entries, or the sync reports those entries as failed. + -If the manifest cannot be parsed, the whole sync is rejected and nothing changes, so a malformed file can never partially apply or wipe your index. +The manifest is validated strictly: an unknown `type`, a field that does not belong to the entry's type (a common source of silent mistakes), or a missing required field rejects the **whole** sync. Nothing changes until the file parses cleanly, so a malformed manifest can never partially apply or wipe your index. ## Configuring in the dashboard diff --git a/docs/enterprise/integrations/confluence.mdx b/docs/enterprise/integrations/confluence.mdx new file mode 100644 index 000000000..f8edeb9d3 --- /dev/null +++ b/docs/enterprise/integrations/confluence.mdx @@ -0,0 +1,84 @@ +--- +title: "Confluence Integration" +sidebarTitle: "Confluence" +description: "Index Confluence spaces from Atlassian Cloud or a self-hosted Data Center instance" +--- + +Context7 can index the pages of a Confluence space and serve them like any other library. It works with **Atlassian Cloud** and with self-hosted **Data Center / Server**. + +An admin connects Confluence once under **Settings → Integrations** using an API token. After that, anyone who can add libraries picks a space and pages from **Add → Confluence**. There is no OAuth app to register, which keeps the setup self-contained on your own infrastructure. + + +Confluence pages are behind authentication, so Context7 reaches them with the token you configure. The token needs read access to the spaces you want to index. + + +## Connect Confluence + +Open **Settings → Integrations → Confluence**, choose your deployment, fill in the fields, and use **Test connection** to confirm before saving. + + + ![Confluence integration settings with the Data Center deployment selected](/images/enterprise/integrations/confluence-settings.png) + + + + + | Field | Value | + | ----- | ----- | + | Deployment | Cloud | + | Site URL | `https://your-company.atlassian.net` | + | Email | The account email that owns the token | + | API Token | Create one at [id.atlassian.com](https://id.atlassian.com/manage-profile/security/api-tokens) | + + Cloud uses the Confluence v2 REST API and authenticates with the email and API token together. + + + | Field | Value | + | ----- | ----- | + | Deployment | Data Center | + | Site URL | `https://confluence.your-company.com` (include the context path if your install uses one, for example `/confluence`) | + | Email | Leave blank | + | Personal Access Token | Create one in Confluence under your profile, **Personal Access Tokens** | + + Data Center uses the v1 REST API and authenticates with a Personal Access Token. Personal Access Tokens require Confluence 7.9 or later. If your instance uses basic auth instead, put the username in the Email field and the password in the token field. + + + + +Point Context7 at a service account rather than a personal login, so indexing does not depend on one person's access. + + +## Index a space + +Go to **Add** and choose **Confluence**. + + + ![The Add Libraries source picker with a Confluence tile](/images/enterprise/integrations/confluence-source.png) + + +Pick a space, then index the whole space or choose specific pages. Give the project a title and description if you want to override the auto-detected ones, then choose **Start Parsing**. + + + ![Selecting a Confluence space and pages, with the page tree and Start Parsing button](/images/enterprise/integrations/confluence-add.png) + + + + + Choose the space from the dropdown, or paste a space URL (for example `https://your-site.atlassian.net/wiki/spaces/TEAM`) into **Space URL** to select it directly — handy when the account can read many spaces. + + + Leave **Index the entire space** on to index everything, which is the fastest path and the right choice for large spaces. Turn it off to pick individual pages: the page tree mirrors the space hierarchy, so selecting a parent selects its children. Very large spaces load their pages in batches — use **Load more pages** to fetch the rest, and the search box to filter. + + + Context7 fetches the pages, converts them to Markdown (code blocks, tables, and callouts are preserved), and indexes them. The project appears under your libraries when it finishes. + + + + +Indexing the entire space means a later refresh also picks up pages added since. Selecting a specific subset pins the project to exactly those pages. + + +You can also index Confluence spaces declaratively with [GitOps](/enterprise/gitops) — add a `type: confluence` entry with a `spaceKey` to your manifest, and it reuses this same connection. + +## Refresh and delete + +A Confluence project behaves like any other library. **Refresh** re-indexes the space using the stored selection and the current admin token, so rotating the token in Settings updates future refreshes without editing the project. **Delete** removes the project and its indexed content. diff --git a/docs/enterprise/library-import.mdx b/docs/enterprise/library-import.mdx index 7d68a3891..0c4169fea 100644 --- a/docs/enterprise/library-import.mdx +++ b/docs/enterprise/library-import.mdx @@ -121,6 +121,61 @@ Because your install didn't parse these libraries from source, it can't refresh Re-importing a library you already have does nothing unless **Overwrite existing libraries** is checked. The import skips libraries that already exist and tells you which ones it skipped. +## Automating with the API + +Besides the dashboard flow, both sides expose an API so you can script imports. For example, you can sync a fixed set of libraries into on-premise on a schedule from a bridge host that can reach both networks. + + + + + +Call the cloud export endpoint with your **enterprise license key** in the `Authorization` header. It returns one library's full content (every code and info snippet, without embeddings), already shaped for the import endpoint. + +```bash +curl -X POST https://context7.com/api/v1/enterprise/export \ + -H "Authorization: Bearer YOUR_LICENSE_KEY" \ + -H "Content-Type: application/json" \ + -d '{"library":"vercel/next.js"}' +``` + +Only public libraries can be exported this way. + + + + + +Post that JSON to your install's [import endpoint](/enterprise/api/parse/import-libraries), authenticated with a [personal API key](/enterprise/api/authentication#creating-an-api-key). The body is `{ "libraries": [...], "force": false }`, which is exactly the export response shape. + +```bash +curl -X POST https://your-instance.example.com/api/import-libraries \ + -H "Authorization: Bearer YOUR_API_KEY" \ + -H "Content-Type: application/json" \ + --data @library.json +``` + + + + + +From a host that can reach both networks, such as a bridge or import gateway, the two calls chain directly: + +```bash +curl -sf -X POST https://context7.com/api/v1/enterprise/export \ + -H "Authorization: Bearer YOUR_LICENSE_KEY" \ + -H "Content-Type: application/json" \ + -d '{"library":"vercel/next.js"}' \ +| curl -X POST https://your-instance.example.com/api/import-libraries \ + -H "Authorization: Bearer YOUR_API_KEY" \ + -H "Content-Type: application/json" \ + --data-binary @- +``` + +For a strictly airgapped install, save the export response to a file, transfer it the same way you would the `.zip`, then import it. The import endpoint also accepts the dashboard `.zip` bundle as a `multipart/form-data` upload (field `bundleFile`). See the [endpoint reference](/enterprise/api/parse/import-libraries) for details. + + +The import endpoint is available only on offline-license installs, the same gate as the dashboard flow. A library that already exists is skipped unless you overwrite it with `force`, either as a `?force=true` query param (handy when piping the export straight through) or a `"force": true` field in a JSON body. The response lists what it queued and skipped. + + ## Limits - Up to 50 libraries per export. diff --git a/docs/howto/private-sources.mdx b/docs/howto/private-sources.mdx index 4cf6bc785..ba6f89abf 100644 --- a/docs/howto/private-sources.mdx +++ b/docs/howto/private-sources.mdx @@ -27,6 +27,9 @@ Add private sources to Context7 to make your internal documentation available to Authorize Context7 to access your source (if required). + + For repositories with little or no written documentation, check **Generate docs** to have Context7 generate documentation from the source code. Also available as the `generateDocs` flag in the [API](/api-guide). + Submit the source for parsing. diff --git a/docs/images/clients/manus/connector.png b/docs/images/clients/manus/connector.png new file mode 100644 index 000000000..7dba06ecc Binary files /dev/null and b/docs/images/clients/manus/connector.png differ diff --git a/docs/images/clients/manus/custom-mcp.png b/docs/images/clients/manus/custom-mcp.png new file mode 100644 index 000000000..8681b10fe Binary files /dev/null and b/docs/images/clients/manus/custom-mcp.png differ diff --git a/docs/images/enterprise/integrations/confluence-add.png b/docs/images/enterprise/integrations/confluence-add.png new file mode 100644 index 000000000..bc1a59603 Binary files /dev/null and b/docs/images/enterprise/integrations/confluence-add.png differ diff --git a/docs/images/enterprise/integrations/confluence-settings.png b/docs/images/enterprise/integrations/confluence-settings.png new file mode 100644 index 000000000..fec5faf26 Binary files /dev/null and b/docs/images/enterprise/integrations/confluence-settings.png differ diff --git a/docs/images/enterprise/integrations/confluence-source.png b/docs/images/enterprise/integrations/confluence-source.png new file mode 100644 index 000000000..14b6061cb Binary files /dev/null and b/docs/images/enterprise/integrations/confluence-source.png differ diff --git a/docs/library-owners.mdx b/docs/library-owners.mdx index 456a988b0..b6d94675a 100644 --- a/docs/library-owners.mdx +++ b/docs/library-owners.mdx @@ -27,6 +27,8 @@ If you're the library owner, you can claim your library on Context7 to unlock a For more control over how Context7 parses and presents your library, you can add a `context7.json` file to the root of your repository. This file works similarly to `robots.txt` and tells Context7 how to handle your project. +`context7.json` controls *where* Context7 looks, not *what kind* of files it reads — see [What Gets Indexed](/adding-libraries#what-gets-indexed) for the parsed file types and the source-code fallback for repositories without documentation. + ### Configuration Fields Here's an example `context7.json` file with all available options: diff --git a/docs/openapi-enterprise.json b/docs/openapi-enterprise.json index d2a6293e5..0c1d116f2 100644 --- a/docs/openapi-enterprise.json +++ b/docs/openapi-enterprise.json @@ -309,6 +309,69 @@ ] } }, + "/import-libraries": { + "post": { + "summary": "Import a library bundle", + "description": "Import pre-parsed libraries into an airgapped on-premise install. Accepts either a Context7 Cloud export `.zip` (`multipart/form-data`) or a JSON body (`application/json`) for programmatic clients. Requires a personal API key (or an admin session). Each library is queued as its own job and its snippets are re-embedded locally with this install's configured provider. Available only on installs running an **offline (airgapped) license**.", + "operationId": "importLibraries", + "tags": ["Parse"], + "parameters": [ + { + "name": "force", + "in": "query", + "required": false, + "schema": { "type": "boolean", "default": false }, + "description": "Overwrite libraries that already exist. Handy when posting a piped export body, which carries no `force` field. Without it, existing libraries are skipped." + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ImportLibrariesRequest" + } + }, + "multipart/form-data": { + "schema": { + "type": "object", + "required": ["bundleFile"], + "properties": { + "bundleFile": { + "type": "string", + "format": "binary", + "description": "A Context7 Cloud `context7-libraries.zip` export (up to 100 MB)" + }, + "force": { + "type": "boolean", + "description": "Overwrite libraries that already exist on this install", + "default": false + } + } + } + } + } + }, + "responses": { + "202": { + "$ref": "#/components/responses/LibrariesImported" + }, + "400": { + "$ref": "#/components/responses/BadRequestError" + }, + "401": { + "$ref": "#/components/responses/UnauthorizedError" + }, + "403": { + "$ref": "#/components/responses/ForbiddenError" + }, + "500": { + "$ref": "#/components/responses/InternalServerError" + } + }, + "security": [{ "bearerAuth": [] }] + } + }, "/parse-website": { "post": { "summary": "Parse a website", @@ -581,6 +644,111 @@ }, "required": ["message", "project", "queueId"] }, + "ImportLibrariesRequest": { + "type": "object", + "required": ["libraries"], + "properties": { + "libraries": { + "type": "array", + "description": "The libraries to import. Each carries its full snippet content; embeddings are recomputed on this install.", + "items": { + "$ref": "#/components/schemas/ImportLibrary" + } + }, + "force": { + "type": "boolean", + "description": "Overwrite libraries that already exist on this install", + "default": false + } + } + }, + "ImportLibrary": { + "type": "object", + "required": ["project", "codeSnippets", "infoSnippets"], + "properties": { + "project": { + "type": "string", + "description": "Library identifier, e.g. `/your-org/your-repo`", + "example": "/vercel/next.js" + }, + "title": { "type": "string" }, + "description": { "type": "string" }, + "type": { + "type": "string", + "description": "Source type of the library (e.g. `repo`, `website`, `openapi`)" + }, + "language": { "type": "string", "example": "English" }, + "branch": { "type": "string" }, + "docsRepoUrl": { "type": "string" }, + "docsSiteUrl": { "type": "string" }, + "totalTokens": { "type": "integer" }, + "codeSnippets": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "title": { "type": "string" }, + "description": { "type": "string" }, + "language": { "type": "string" }, + "codeList": { + "type": "array", + "items": { + "type": "object", + "properties": { + "language": { "type": "string" }, + "code": { "type": "string" } + } + } + }, + "tokens": { "type": "integer" } + } + } + }, + "infoSnippets": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "content": { "type": "string" }, + "breadcrumb": { "type": "string" }, + "tokens": { "type": "integer" } + } + } + } + } + }, + "LibrariesImportedResponse": { + "type": "object", + "properties": { + "message": { + "type": "string", + "example": "Queued 2 libraries for import" + }, + "queued": { + "type": "array", + "description": "Library identifiers queued for import", + "items": { "type": "string" }, + "example": ["/vercel/next.js", "/facebook/react"] + }, + "skipped": { + "type": "array", + "description": "Libraries that were not imported, with a reason", + "items": { + "type": "object", + "properties": { + "project": { "type": "string" }, + "reason": { + "type": "string", + "example": "already exists" + } + } + } + } + }, + "required": ["message", "queued", "skipped"] + }, "ParseRepoRequest": { "type": "object", "required": ["repoUrl"], @@ -684,6 +852,25 @@ } } }, + "LibrariesImported": { + "description": "Libraries accepted and queued for import", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LibrariesImportedResponse" + } + } + } + }, + "ForbiddenError": { + "description": "Forbidden - the action is not permitted for this install or user", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Error" }, + "example": { "error": "Library import is available only with an offline license." } + } + } + }, "BadRequestError": { "description": "Bad Request - invalid or missing parameters", "content": { diff --git a/docs/openapi.json b/docs/openapi.json index 1fa290b94..68f1f1011 100644 --- a/docs/openapi.json +++ b/docs/openapi.json @@ -458,7 +458,11 @@ "/vercel/next.js" ], "repoFilters": { - "minStars": 100 + "minStars": 100, + "allowedLicenses": [ + "MIT", + "Apache-2.0" + ] } }, "select": { @@ -587,6 +591,39 @@ } } }, + "allowPermissiveLicenses": { + "summary": "Restrict public repos to permissive SPDX licenses", + "value": { + "libraryFilters": { + "quality": { + "repoFilters": { + "allowedLicenses": { + "add": [ + "MIT", + "Apache-2.0", + "BSD-3-Clause", + "ISC" + ] + } + } + } + } + } + }, + "clearLicenseRestriction": { + "summary": "Remove all license restrictions", + "value": { + "libraryFilters": { + "quality": { + "repoFilters": { + "allowedLicenses": { + "clear": true + } + } + } + } + } + }, "allowDomain": { "summary": "Allow libraries from a specific domain (select mode)", "value": { @@ -891,6 +928,10 @@ "skipVersionFiltering": { "type": "boolean", "description": "Skip filtering out version-specific documentation pages" + }, + "generateDocs": { + "type": "boolean", + "description": "Generate documentation from the repository source code. Applies to private repository submissions." } } }, @@ -1998,6 +2039,10 @@ "skipVersionFiltering": { "type": "boolean", "description": "Skip filtering out version-specific documentation pages" + }, + "generateDocs": { + "type": "boolean", + "description": "Generate documentation from the repository source code. Applies to private repository submissions." } } }, @@ -2122,10 +2167,18 @@ "type": "integer", "nullable": true, "description": "Minimum GitHub stars required" + }, + "allowedLicenses": { + "type": "array", + "items": { + "type": "string" + }, + "description": "SPDX license identifiers (e.g. 'MIT', 'Apache-2.0') that are allowed. Empty array means no license restriction. Applies to public repos only; non-repo source types pass through." } }, "required": [ - "minStars" + "minStars", + "allowedLicenses" ] }, "websiteFilters": { @@ -2223,6 +2276,34 @@ } } }, + "LicenseListOperation": { + "type": "object", + "description": "Incremental add/remove operation on a list of SPDX license identifiers. Use 'clear' to remove all entries at once.", + "properties": { + "add": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[A-Za-z0-9.+\\-_]+$", + "maxLength": 64 + }, + "description": "SPDX license identifiers to add (e.g. 'MIT', 'Apache-2.0', 'BSD-3-Clause')" + }, + "remove": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[A-Za-z0-9.+\\-_]+$", + "maxLength": 64 + }, + "description": "SPDX license identifiers to remove" + }, + "clear": { + "type": "boolean", + "description": "Set to true to remove all entries. Cannot be used together with 'add' or 'remove'." + } + } + }, "PatchPoliciesRequest": { "type": "object", "description": "Incremental policy update. All fields are optional — only provided fields are modified. At least one of 'sourceTypes' or 'libraryFilters' must be provided.", @@ -2314,6 +2395,9 @@ "nullable": true, "minimum": 0, "description": "Minimum stars threshold (null to clear)" + }, + "allowedLicenses": { + "$ref": "#/components/schemas/LicenseListOperation" } } }, diff --git a/docs/resources/all-clients.mdx b/docs/resources/all-clients.mdx index 8688080e9..ba3ce73ff 100644 --- a/docs/resources/all-clients.mdx +++ b/docs/resources/all-clients.mdx @@ -996,34 +996,29 @@ docker build -t context7-mcp . } ``` -If you use the Docker MCP Toolkit image (`mcp/context7`) with a stdio-based client, set `MCP_TRANSPORT=stdio` so the container starts with stdio transport instead of its HTTP default. For Cline, Roo Code, and Claude Desktop, use: + -```json -{ - "mcpServers": { - "context7": { - "command": "docker", - "args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT=stdio", "mcp/context7"] - } - } -} -``` + -For VS Code, use the same Docker command in the `servers` format: +Context7 is available in the [Docker MCP Catalog](https://hub.docker.com/mcp/server/context7/overview) as a **remote** server, so you don't need to build, pull, or run a local image. The MCP Toolkit connects to the hosted endpoint for you. + +1. In Docker Desktop, open **MCP Toolkit → Catalog**, search for **Context7**, and enable it. +2. (Optional) In the server's **Config** tab, set your Context7 API key (`CONTEXT7_API_KEY`) for higher rate limits. The server also works without one. +3. Connect your MCP client to Docker's MCP gateway. Either run `docker mcp client connect ` (for example `vscode`, `cursor`, or `claude-desktop`), or add the gateway to your client config manually: ```json { - "servers": { - "context7": { - "type": "stdio", + "mcpServers": { + "MCP_DOCKER": { "command": "docker", - "args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT=stdio", "mcp/context7"] + "args": ["mcp", "gateway", "run"], + "type": "stdio" } } } ``` -Keep using the remote server URL for HTTP-based clients. +The gateway exposes every server you've enabled in the MCP Toolkit, including Context7. Tools appear under the `MCP_DOCKER` server in your client. @@ -1278,6 +1273,38 @@ See [Local and Remote MCPs for Perplexity](https://www.perplexity.ai/help-center + + +Manus includes a built-in Context7 connector: + +1. Navigate `Settings` > `Connectors`. +2. In the `Apps` tab, search for `Context7`. +3. Click `Context7` to enable it. + + + Context7 connector in Manus + + +Alternatively, add it as a custom MCP connection with your API key: + +1. Navigate `Settings` > `Connectors` > `Custom MCP`. +2. Fill in the configuration: + +| Field | Value | +| ------------------ | ------------------------------ | +| **Server Name** | `Context7` | +| **Transport Type** | `HTTP` | +| **Server URL** | `https://mcp.context7.com/mcp` | +| **Custom headers** | `CONTEXT7_API_KEY`: `YOUR_API_KEY` | + +3. Click `Save`. + + + Context7 custom MCP configuration in Manus + + + + Factory's droid supports MCP servers through its CLI. See [Factory MCP docs](https://docs.factory.ai/cli/configuration/mcp) for more info. @@ -1312,4 +1339,31 @@ Install the [context7.mcpb](https://github.com/upstash/context7/tree/master/pack + + +AnythingLLM supports MCP Servers through GUI. + +1. Navigate `AnythingLLM` > `Agents Skills` +2. Select `MCP Servers`. +3. Click `Edit MCP config`. +4. Paste. +5. Click to "Refresh" button. + +#### Remote Connection +```json +{ +"mcpServers": { + "context7": { + "type": "streamable", + "url": "https://mcp.context7.com/mcp", + "headers": { + "CONTEXT7_API_KEY": "YOUR_API_KEY" + } + } +} +} +``` + + + diff --git a/package.json b/package.json index edcba4f52..9781189ab 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "@typescript-eslint/parser": "^8.28.0", "eslint": "^9.34.0", "eslint-config-prettier": "^10.1.1", - "eslint-plugin-prettier": "^5.2.5", + "eslint-plugin-prettier": "^5.5.6", "prettier": "^3.6.2", "typescript": "^5.8.2", "typescript-eslint": "^8.28.0" diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 2e9978d9a..45fbe3aff 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 0.5.4 + +### Patch Changes + +- 33229cb: Clarify the `query-docs` query description so it asks for a single concept per query. When a question spans multiple distinct topics, callers are now told to make a separate query per concept instead of combining them (unless the question is about how the concepts interact), which avoids diluted, shallow results. Applied consistently across the MCP server, CLI, pi, and AI SDK tools. + ## 0.5.3 ### Patch Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index 24dc59b81..232f95256 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "ctx7", - "version": "0.5.3", + "version": "0.5.4", "description": "Context7 CLI - Fetch documentation context and configure Context7", "type": "module", "bin": { @@ -37,12 +37,12 @@ "@typescript-eslint/eslint-plugin": "^8.28.0", "@typescript-eslint/parser": "^8.28.0", "eslint": "^9.34.0", - "eslint-plugin-prettier": "^5.2.5", + "eslint-plugin-prettier": "^5.5.6", "prettier": "^3.6.2", "tsup": "^8.5.0", "typescript": "^5.8.2", "typescript-eslint": "^8.28.0", - "vitest": "^4.0.13" + "vitest": "^4.1.9" }, "keywords": [ "context7", diff --git a/packages/cli/src/__tests__/find-docs-skill-alignment.test.ts b/packages/cli/src/__tests__/find-docs-skill-alignment.test.ts new file mode 100644 index 000000000..5bd725dfe --- /dev/null +++ b/packages/cli/src/__tests__/find-docs-skill-alignment.test.ts @@ -0,0 +1,35 @@ +import { describe, test, expect } from "vitest"; +import { readFile } from "fs/promises"; +import { join, dirname } from "path"; +import { fileURLToPath } from "url"; + +const repoRoot = join(dirname(fileURLToPath(import.meta.url)), "../../../.."); +const skillPath = join(repoRoot, "skills/find-docs/SKILL.md"); +const rulePath = join(repoRoot, "rules/context7-cli.md"); + +describe("find-docs skill aligns with context7-cli rule", () => { + test("uses npx ctx7@latest as the canonical invocation style", async () => { + const skill = await readFile(skillPath, "utf-8"); + expect(skill).toContain("npx ctx7@latest library"); + expect(skill).toContain("npx ctx7@latest docs"); + expect(skill).not.toMatch(/^ctx7 library/m); + expect(skill).not.toContain("ctx7 library nextjs"); + }); + + test("documents official library naming guidance", async () => { + const skill = await readFile(skillPath, "utf-8"); + const rule = await readFile(rulePath, "utf-8"); + + expect(skill).toContain('"Next.js" not "nextjs"'); + expect(rule).toContain('"Next.js" not "nextjs"'); + expect(skill).toContain('library "Next.js"'); + }); + + test("does not recommend global install as the primary workflow", async () => { + const skill = await readFile(skillPath, "utf-8"); + const npxIndex = skill.indexOf("npx ctx7@latest"); + const globalInstallIndex = skill.indexOf("npm install -g ctx7@latest"); + expect(npxIndex).toBeGreaterThanOrEqual(0); + expect(globalInstallIndex).toBeGreaterThan(npxIndex); + }); +}); diff --git a/packages/cli/src/commands/docs.ts b/packages/cli/src/commands/docs.ts index a7f6d4c3f..28ee67a60 100644 --- a/packages/cli/src/commands/docs.ts +++ b/packages/cli/src/commands/docs.ts @@ -229,7 +229,10 @@ export function registerDocsCommands(program: Command): void { program .command("docs") .argument("", "Context7 library ID (e.g., /facebook/react)") - .argument("", "Question or task to get docs for") + .argument( + "", + "Single-topic question to get docs for (run a separate query per distinct concept, unless asking how they interact)" + ) .option("--json", "Output as JSON") .description("Query documentation for a library") .action(async (libraryId: string, query: string, options: { json?: boolean }) => { diff --git a/packages/cli/src/setup/templates.ts b/packages/cli/src/setup/templates.ts index 26b961400..b8ddb04ef 100644 --- a/packages/cli/src/setup/templates.ts +++ b/packages/cli/src/setup/templates.ts @@ -3,7 +3,7 @@ const GITHUB_RAW_URLS = [ "https://raw.githubusercontent.com/upstash/context7/main/rules", ]; -const FALLBACK_MCP = `Use Context7 MCP 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. +const FALLBACK_MCP = `Use Context7 MCP 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. Do not use for: refactoring, writing scripts from scratch, debugging business logic, code review, or general programming concepts. @@ -11,11 +11,11 @@ Do not use for: refactoring, writing scripts from scratch, debugging business lo 1. \`resolve-library-id\` with the library name and the user's question. Use the official library name with proper punctuation (e.g., "Next.js" not "nextjs", "Customer.io" not "customerio", "Three.js" not "threejs") 2. Pick the best match by: exact name match, description relevance, code snippet count, source reputation (High/Medium preferred), and benchmark score (higher is better). Use version-specific IDs when the user mentions a version -3. \`query-docs\` with the selected library ID and the user's full question (not single words) +3. \`query-docs\` with the selected library ID and the user's full question (not single words), scoped to a single concept. If the question spans multiple distinct concepts (e.g. routing and auth and caching), make a separate \`query-docs\` call per concept with the same library ID, unless the question is about how the concepts interact — combined queries dilute ranking and return shallow results for each topic 4. Answer using the fetched docs `; -const FALLBACK_CLI = `Use the \`ctx7\` CLI 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. +const FALLBACK_CLI = `Use the \`ctx7\` CLI 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. Do not use for: refactoring, writing scripts from scratch, debugging business logic, code review, or general programming concepts. @@ -23,10 +23,10 @@ Do not use for: refactoring, writing scripts from scratch, debugging business lo 1. Resolve library: \`npx ctx7@latest library ""\` 2. Pick the best match by: exact name match, description relevance, code snippet count, source reputation (High/Medium preferred), and benchmark score (higher is better). If results don't look right, try the full name with punctuation (e.g., "next.js" not "nextjs") -3. Fetch docs: \`npx ctx7@latest docs ""\` +3. Fetch docs: \`npx ctx7@latest docs ""\` — run a separate \`docs\` command per distinct concept if the question spans multiple topics, unless it's about how they interact 4. Answer using the fetched documentation -You MUST call \`library\` first to get a valid ID (format: \`/org/project\`) unless the user provides one directly. Use the user's full question as the query -- specific and detailed queries return better results than vague single words. Do not run more than 3 commands per question. Do not include sensitive information (API keys, passwords, credentials) in queries. +You MUST call \`library\` first to get a valid ID (format: \`/org/project\`) unless the user provides one directly. Use the user's full question as the query — specific and detailed queries return better results than vague single words, but keep each query to a single concept unless the question is about how concepts interact; combined multi-topic queries dilute ranking and return shallow results for each topic. Do not run more than 3 commands per question. Do not include sensitive information (API keys, passwords, credentials) in queries. For version-specific docs, use \`/org/project/version\` from the \`library\` output (e.g., \`/vercel/next.js/v14.3.0\`). diff --git a/packages/mcp/CHANGELOG.md b/packages/mcp/CHANGELOG.md index 0dbd6fd6f..cd3b15c99 100644 --- a/packages/mcp/CHANGELOG.md +++ b/packages/mcp/CHANGELOG.md @@ -1,5 +1,12 @@ # @upstash/context7-mcp +## 3.2.3 + +### Patch Changes + +- 41878ec: Skip loopback (`127.0.0.0/8`), link-local (`169.254.0.0/16`), CGNAT (`100.64.0.0/10`), IPv6 loopback (`::1`), IPv6 link-local (`fe80::/10`), and IPv6 unique-local (`fc00::/7`) addresses when extracting the client IP from `X-Forwarded-For`, so proxy-internal hops no longer pollute the reported client IP. +- 33229cb: Clarify the `query-docs` query description so it asks for a single concept per query. When a question spans multiple distinct topics, callers are now told to make a separate query per concept instead of combining them (unless the question is about how the concepts interact), which avoids diluted, shallow results. Applied consistently across the MCP server, CLI, pi, and AI SDK tools. + ## 3.2.2 ### Patch Changes diff --git a/packages/mcp/README.md b/packages/mcp/README.md index 3c53ca531..c807e888a 100644 --- a/packages/mcp/README.md +++ b/packages/mcp/README.md @@ -1366,6 +1366,25 @@ See the [Emdash repository](https://github.com/generalaction/emdash) for more in +
+Install in Autohand Code + +Use the [Autohand Code CLI](https://github.com/autohandai/code-cli/) to add the local Context7 server: + +```sh +autohand mcp add context7 npx -y @upstash/context7-mcp --api-key YOUR_API_KEY +``` + +Add `--scope project` before `context7` to save the server in the current project's `.autohand` configuration instead of your user configuration. + +For basic usage without an API key, you can connect to the remote server instead: + +```sh +autohand mcp add --transport http context7 https://mcp.context7.com/mcp +``` + +
+ ## 🔨 Available Tools Context7 MCP provides the following tools that LLMs can use: diff --git a/packages/mcp/package.json b/packages/mcp/package.json index 1dfa279bb..112c4d580 100644 --- a/packages/mcp/package.json +++ b/packages/mcp/package.json @@ -1,6 +1,6 @@ { "name": "@upstash/context7-mcp", - "version": "3.2.2", + "version": "3.2.3", "mcpName": "io.github.upstash/context7", "description": "MCP server for Context7", "scripts": { @@ -50,13 +50,13 @@ "@upstash/redis": "^1.38.0", "commander": "^13.1.0", "express": "^5.1.0", - "jose": "^6.1.3", + "jose": "^6.2.3", "undici": "^6.26.0", "zod": "^4.4.3" }, "devDependencies": { "@types/node": "^25.0.3", "typescript": "^5.8.2", - "vitest": "^4.0.13" + "vitest": "^4.1.9" } } diff --git a/packages/mcp/src/index.ts b/packages/mcp/src/index.ts index 6f56fa921..566bb38ed 100644 --- a/packages/mcp/src/index.ts +++ b/packages/mcp/src/index.ts @@ -27,6 +27,7 @@ import { OPENAI_APPS_CHALLENGE_TOKEN, } from "./lib/constants.js"; import { maybeElicitAuthSignIn } from "./lib/auth/auth-prompt.js"; +import { getClientIp } from "./lib/client-ip.js"; /** Default HTTP server port */ const DEFAULT_PORT = 3000; @@ -108,36 +109,6 @@ function getClientContext(): ClientContext { }; } -/** - * Extract client IP address from request headers. - * Handles X-Forwarded-For header for proxied requests. - */ -function getClientIp(req: express.Request): string | undefined { - const forwardedFor = req.headers["x-forwarded-for"] || req.headers["X-Forwarded-For"]; - - if (forwardedFor) { - const ips = Array.isArray(forwardedFor) ? forwardedFor[0] : forwardedFor; - const ipList = ips.split(",").map((ip) => ip.trim()); - - for (const ip of ipList) { - const plainIp = ip.replace(/^::ffff:/, ""); - if ( - !plainIp.startsWith("10.") && - !plainIp.startsWith("192.168.") && - !/^172\.(1[6-9]|2[0-9]|3[0-1])\./.test(plainIp) - ) { - return plainIp; - } - } - return ipList[0].replace(/^::ffff:/, ""); - } - - if (req.socket?.remoteAddress) { - return req.socket.remoteAddress.replace(/^::ffff:/, ""); - } - return undefined; -} - function createMcpServer() { const server = new McpServer( { @@ -154,7 +125,7 @@ function createMcpServer() { ], }, { - 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. + 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. Do not use for: refactoring, writing scripts from scratch, debugging business logic, code review, or general programming concepts.`, } @@ -265,7 +236,7 @@ Do not call this tool more than 3 times per question.`, query: z .string() .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." + "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." ), }, annotations: { diff --git a/packages/mcp/src/lib/client-ip.ts b/packages/mcp/src/lib/client-ip.ts new file mode 100644 index 000000000..5c8ab2d47 --- /dev/null +++ b/packages/mcp/src/lib/client-ip.ts @@ -0,0 +1,78 @@ +import type express from "express"; + +function stripIpv4MappedPrefix(ip: string): string { + return ip.replace(/^::ffff:/i, ""); +} + +/** + * Returns true for RFC1918, CGNAT, loopback, link-local, and IPv6 private ranges. + */ +export function isPrivateOrLocalIp(ip: string): boolean { + const plainIp = stripIpv4MappedPrefix(ip).toLowerCase(); + + if (plainIp.includes(".")) { + return ( + plainIp.startsWith("10.") || + plainIp.startsWith("192.168.") || + /^172\.(1[6-9]|2[0-9]|3[0-1])\./.test(plainIp) || + /^100\.(6[4-9]|[7-9][0-9]|1[01][0-9]|12[0-7])\./.test(plainIp) || + plainIp.startsWith("127.") || + plainIp.startsWith("169.254.") + ); + } + + // ::1 loopback in any textual form (e.g. "0::1", "0:0:0:0:0:0:0:1") + if (/^[0:]+1$/.test(plainIp)) { + return true; + } + + // First hextets in fe80::/10 and fc00::/7 start with a non-zero digit, so a + // valid textual form always spells out all 4 digits. + + // fe80::/10 link-local + if (/^fe[89ab][0-9a-f]:/.test(plainIp)) { + return true; + } + + // fc00::/7 unique local + if (/^f[cd][0-9a-f]{2}:/.test(plainIp)) { + return true; + } + + return false; +} + +function pickClientIpFromForwardedList(ipList: string[]): string | undefined { + for (const ip of ipList) { + const plainIp = stripIpv4MappedPrefix(ip); + if (!isPrivateOrLocalIp(plainIp)) { + return plainIp; + } + } + + if (ipList.length === 0) { + return undefined; + } + + return stripIpv4MappedPrefix(ipList[0]); +} + +/** + * Extract client IP address from request headers. + * Handles X-Forwarded-For header for proxied requests. + */ +export function getClientIp(req: express.Request): string | undefined { + const forwardedFor = req.headers["x-forwarded-for"] || req.headers["X-Forwarded-For"]; + + if (forwardedFor) { + const ips = Array.isArray(forwardedFor) ? forwardedFor[0] : forwardedFor; + const ipList = ips.split(",").map((ip) => ip.trim()); + return pickClientIpFromForwardedList(ipList); + } + + if (req.socket?.remoteAddress) { + return stripIpv4MappedPrefix(req.socket.remoteAddress); + } + + return undefined; +} diff --git a/packages/mcp/test/client-ip.test.ts b/packages/mcp/test/client-ip.test.ts new file mode 100644 index 000000000..6b415297d --- /dev/null +++ b/packages/mcp/test/client-ip.test.ts @@ -0,0 +1,96 @@ +import { describe, expect, test } from "vitest"; +import type express from "express"; +import { getClientIp, isPrivateOrLocalIp } from "../src/lib/client-ip.js"; + +describe("isPrivateOrLocalIp", () => { + test.each([ + "10.0.0.1", + "192.168.1.1", + "172.16.0.1", + "127.0.0.1", + "169.254.1.1", + "100.64.0.1", + "100.127.255.255", + "::1", + "0::1", + "0:0:0:0:0:0:0:1", + "fe80::1", + "FE80::1", + "febf::1", + "fc00::1", + "fd12::1", + "fdff::1", + "::ffff:127.0.0.1", + ])("treats %s as private or local", (ip) => { + expect(isPrivateOrLocalIp(ip)).toBe(true); + }); + + test.each([ + "8.8.8.8", + "203.0.113.10", + "100.63.255.255", + "100.128.0.1", + "2001:db8::1", + "1::1", + "::11", + "::ffff:8.8.8.8", + // abbreviated first hextets that look like fe80::/10 or fc00::/7 but are not + "fe8::1", + "fc::1", + "fd0::1", + // fec0::/10 (deprecated site-local) is outside fe80::/10 + "fec0::1", + ])("treats %s as public", (ip) => { + expect(isPrivateOrLocalIp(ip)).toBe(false); + }); +}); + +describe("getClientIp", () => { + function makeRequest(headers: Record): express.Request { + return { + headers, + socket: undefined, + } as express.Request; + } + + test("skips loopback and link-local entries in X-Forwarded-For", () => { + const req = makeRequest({ + "x-forwarded-for": "127.0.0.1, 8.8.8.8", + }); + + expect(getClientIp(req)).toBe("8.8.8.8"); + }); + + test("skips RFC1918 and returns the first public forwarded IP", () => { + const req = makeRequest({ + "x-forwarded-for": "10.0.0.1, 192.168.0.2, 203.0.113.10", + }); + + expect(getClientIp(req)).toBe("203.0.113.10"); + }); + + test("skips IPv6 loopback and private ranges in X-Forwarded-For", () => { + const req = makeRequest({ + "x-forwarded-for": "::1, fe80::1, fc00::1, 2001:db8::5", + }); + + expect(getClientIp(req)).toBe("2001:db8::5"); + }); + + test("falls back to the first forwarded IP when all entries are private", () => { + const req = makeRequest({ + "x-forwarded-for": "127.0.0.1, 10.0.0.1", + }); + + expect(getClientIp(req)).toBe("127.0.0.1"); + }); + + test("uses socket remote address when X-Forwarded-For is absent", () => { + const req = { + headers: {}, + socket: { remoteAddress: "::ffff:203.0.113.10" }, + } as express.Request; + + expect(getClientIp(req)).toBe("203.0.113.10"); + }); +}); diff --git a/packages/pi/CHANGELOG.md b/packages/pi/CHANGELOG.md index d0ed308f5..f57bc6b29 100644 --- a/packages/pi/CHANGELOG.md +++ b/packages/pi/CHANGELOG.md @@ -1,5 +1,11 @@ # @upstash/context7-pi +## 0.1.1 + +### Patch Changes + +- 33229cb: Clarify the `query-docs` query description so it asks for a single concept per query. When a question spans multiple distinct topics, callers are now told to make a separate query per concept instead of combining them (unless the question is about how the concepts interact), which avoids diluted, shallow results. Applied consistently across the MCP server, CLI, pi, and AI SDK tools. + ## 0.1.0 ### Minor Changes diff --git a/packages/pi/lib/prompts.ts b/packages/pi/lib/prompts.ts index e6b5525f7..a4d1ba33a 100644 --- a/packages/pi/lib/prompts.ts +++ b/packages/pi/lib/prompts.ts @@ -57,4 +57,4 @@ export const QUERY_DOCS_LIBRARY_ID_DESCRIPTION = "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'."; export const QUERY_DOCS_QUERY_DESCRIPTION = - "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."; + "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."; diff --git a/packages/pi/package.json b/packages/pi/package.json index 2cd5eaa17..91dae485d 100644 --- a/packages/pi/package.json +++ b/packages/pi/package.json @@ -1,6 +1,6 @@ { "name": "@upstash/context7-pi", - "version": "0.1.0", + "version": "0.1.1", "description": "Official Context7 extension for pi.dev — adds resolve-library-id and query-docs tools to the pi coding agent", "type": "module", "pi": { @@ -64,6 +64,6 @@ "dotenv": "^17.4.2", "typebox": "^1.1.38", "typescript": "^5.9.3", - "vitest": "^4.0.14" + "vitest": "^4.1.9" } } diff --git a/packages/pi/skills/context7-docs/SKILL.md b/packages/pi/skills/context7-docs/SKILL.md index bacbd905b..1556e3118 100644 --- a/packages/pi/skills/context7-docs/SKILL.md +++ b/packages/pi/skills/context7-docs/SKILL.md @@ -33,7 +33,7 @@ Reach for these tools whenever a question involves a specific library, framework ## Workflow 1. **Resolve the library ID.** Call `resolve-library-id` with the library name and the user's question. The tool returns matching libraries with their Context7 IDs (`/org/project` format), descriptions, snippet counts, and quality scores. Pick the best match — prioritize official sources, name match, and high benchmark scores. -2. **Query the docs.** Call `query-docs` with the chosen library ID and the user's question. The tool returns documentation snippets and code examples. +2. **Query the docs.** Call `query-docs` with the chosen library ID and the user's question, scoped to a single concept — if the question spans multiple distinct concepts, make a separate call per concept with the same library ID, unless the question is about how the concepts interact. The tool returns documentation snippets and code examples. 3. **Answer.** Cite the library ID you used and quote code examples verbatim when relevant. If the user supplies a library ID in `/org/project` or `/org/project/version` format directly, skip step 1 and call `query-docs` immediately. diff --git a/packages/sdk/package.json b/packages/sdk/package.json index d57030373..5b6cdbd96 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -51,6 +51,6 @@ "dotenv": "^17.4.2", "tsup": "^8.5.1", "typescript": "^5.8.2", - "vitest": "^4.0.13" + "vitest": "^4.1.9" } } diff --git a/packages/tools-ai-sdk/CHANGELOG.md b/packages/tools-ai-sdk/CHANGELOG.md index 85fd3adac..388d50cce 100644 --- a/packages/tools-ai-sdk/CHANGELOG.md +++ b/packages/tools-ai-sdk/CHANGELOG.md @@ -1,5 +1,11 @@ # @upstash/context7-tools-ai-sdk +## 0.2.4 + +### Patch Changes + +- 33229cb: Clarify the `query-docs` query description so it asks for a single concept per query. When a question spans multiple distinct topics, callers are now told to make a separate query per concept instead of combining them (unless the question is about how the concepts interact), which avoids diluted, shallow results. Applied consistently across the MCP server, CLI, pi, and AI SDK tools. + ## 0.2.3 ### Patch Changes diff --git a/packages/tools-ai-sdk/package.json b/packages/tools-ai-sdk/package.json index 9e7161618..125d62a89 100644 --- a/packages/tools-ai-sdk/package.json +++ b/packages/tools-ai-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@upstash/context7-tools-ai-sdk", - "version": "0.2.3", + "version": "0.2.4", "description": "Context7 tools for Vercel AI SDK", "type": "module", "main": "./dist/index.cjs", @@ -42,7 +42,7 @@ "dotenv": "^17.4.2", "tsup": "^8.5.1", "typescript": "^5.8.2", - "vitest": "^4.0.13" + "vitest": "^4.1.9" }, "repository": { "type": "git", diff --git a/packages/tools-ai-sdk/src/tools/query-docs.ts b/packages/tools-ai-sdk/src/tools/query-docs.ts index 8427c1bf8..d07de22b8 100644 --- a/packages/tools-ai-sdk/src/tools/query-docs.ts +++ b/packages/tools-ai-sdk/src/tools/query-docs.ts @@ -45,7 +45,7 @@ export function queryDocs(config: Context7ToolsConfig = {}) { query: z .string() .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'. IMPORTANT: Do not include any sensitive or confidential information such as API keys, passwords, credentials, or personal data in your query." + "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'. IMPORTANT: Do not include any sensitive or confidential information such as API keys, passwords, credentials, or personal data in your query." ), }), execute: async ({ libraryId, query }: { libraryId: string; query: string }) => { diff --git a/plugins/claude/context7/agents/docs-researcher.md b/plugins/claude/context7/agents/docs-researcher.md index ec7e8aa07..ebd408a61 100644 --- a/plugins/claude/context7/agents/docs-researcher.md +++ b/plugins/claude/context7/agents/docs-researcher.md @@ -25,7 +25,7 @@ When given a question about a library or framework, fetch the relevant documenta 4. **Fetch documentation**: Call `query-docs` with: - `libraryId`: The selected Context7 library ID (e.g., `/vercel/next.js`) - - `query`: The user's specific question for targeted results + - `query`: The user's specific question for targeted results, scoped to a single concept 5. **Return a focused answer**: Summarize the relevant documentation with: - Direct answer to the question @@ -34,7 +34,8 @@ When given a question about a library or framework, fetch the relevant documenta ## Guidelines -- Pass the user's full question as the query parameter for better relevance +- Pass the user's full question as the query parameter for better relevance, but keep each query to a single concept +- If the question spans multiple distinct concepts (e.g. routing and auth and caching), make a separate `query-docs` call per concept with the same library ID, unless the question is about how the concepts interact — combined queries dilute ranking and return shallow results for each topic - When the user mentions a version (e.g., "Next.js 15"), use version-specific library IDs if available - If `resolve-library-id` returns multiple matches, prefer official/primary packages over community forks - Keep responses concise - the goal is to answer the question, not dump entire documentation diff --git a/plugins/claude/context7/commands/docs.md b/plugins/claude/context7/commands/docs.md index 8a2df7818..1e6415d15 100644 --- a/plugins/claude/context7/commands/docs.md +++ b/plugins/claude/context7/commands/docs.md @@ -14,7 +14,7 @@ Fetches up-to-date documentation and code examples for a library. ``` - **library**: The library name, or a Context7 ID starting with `/` -- **query**: What you're looking for (optional but recommended) +- **query**: What you're looking for (optional but recommended; run the command once per distinct concept, unless asking how they interact) ## Examples diff --git a/plugins/claude/context7/skills/context7-mcp/SKILL.md b/plugins/claude/context7/skills/context7-mcp/SKILL.md index 234659d9f..245b08005 100644 --- a/plugins/claude/context7/skills/context7-mcp/SKILL.md +++ b/plugins/claude/context7/skills/context7-mcp/SKILL.md @@ -36,7 +36,9 @@ From the resolution results, choose based on: Call `query-docs` with: - `libraryId`: The selected Context7 library ID (e.g., `/vercel/next.js`) -- `query`: The user's specific question +- `query`: The user's specific question, scoped to a single concept + +If the user's question spans multiple distinct concepts (e.g. routing and auth and caching), make a separate `query-docs` call per concept with the same library ID, unless the question is about how the concepts interact — combined queries dilute ranking and return shallow results for each topic. ### Step 4: Use the Documentation @@ -48,6 +50,7 @@ Incorporate the fetched documentation into your response: ## Guidelines -- **Be specific**: Pass the user's full question as the query for better results +- **Be specific**: Pass the user's full question as the query for better results, but keep each query to a single concept +- **One topic per query**: Split multi-topic questions into separate `query-docs` calls — resolve the library ID once, then query per concept, unless the question is about how the concepts interact - **Version awareness**: When users mention versions ("Next.js 15", "React 19"), use version-specific library IDs if available from the resolution step - **Prefer official sources**: When multiple matches exist, prefer official/primary packages over community forks diff --git a/plugins/codex/context7/skills/context7-mcp/SKILL.md b/plugins/codex/context7/skills/context7-mcp/SKILL.md index 234659d9f..245b08005 100644 --- a/plugins/codex/context7/skills/context7-mcp/SKILL.md +++ b/plugins/codex/context7/skills/context7-mcp/SKILL.md @@ -36,7 +36,9 @@ From the resolution results, choose based on: Call `query-docs` with: - `libraryId`: The selected Context7 library ID (e.g., `/vercel/next.js`) -- `query`: The user's specific question +- `query`: The user's specific question, scoped to a single concept + +If the user's question spans multiple distinct concepts (e.g. routing and auth and caching), make a separate `query-docs` call per concept with the same library ID, unless the question is about how the concepts interact — combined queries dilute ranking and return shallow results for each topic. ### Step 4: Use the Documentation @@ -48,6 +50,7 @@ Incorporate the fetched documentation into your response: ## Guidelines -- **Be specific**: Pass the user's full question as the query for better results +- **Be specific**: Pass the user's full question as the query for better results, but keep each query to a single concept +- **One topic per query**: Split multi-topic questions into separate `query-docs` calls — resolve the library ID once, then query per concept, unless the question is about how the concepts interact - **Version awareness**: When users mention versions ("Next.js 15", "React 19"), use version-specific library IDs if available from the resolution step - **Prefer official sources**: When multiple matches exist, prefer official/primary packages over community forks diff --git a/plugins/context7-power/POWER.md b/plugins/context7-power/POWER.md index 184aa477e..b6759e400 100644 --- a/plugins/context7-power/POWER.md +++ b/plugins/context7-power/POWER.md @@ -49,7 +49,9 @@ From the resolution results, choose based on: Call `query-docs` with: - `libraryId`: The selected Context7 library ID (e.g., /vercel/next.js) -- `query`: The user's full, specific question rather than a single word +- `query`: The user's full, specific question rather than a single word, scoped to a single concept + +If the user's question spans multiple distinct concepts (e.g. routing and auth and caching), make a separate `query-docs` call per concept with the same library ID, unless the question is about how the concepts interact — combined queries dilute ranking and return shallow results for each topic. ### Step 4: Use the Documentation @@ -61,11 +63,12 @@ Incorporate the fetched documentation into your response: ## Best Practices -- Pass the user's full question as the query for better results +- Pass the user's full question as the query for better results, but keep each query to a single concept +- Keep each query to one topic; split multi-topic questions into separate `query-docs` calls, unless the question is about how the concepts interact - When users mention versions ("Next.js 15", "React 19"), use version-specific library IDs if available from the resolution step - When multiple matches exist, prefer official/primary packages over community forks - Use this Power for API syntax, configuration, setup instructions, version migration, library-specific debugging, and CLI tool usage -- Use Context7 even when you think you know the answer -- your training data may not reflect recent changes. Prefer this over web search for library docs. +- Use Context7 even when you think you know the answer — your training data may not reflect recent changes. Prefer this over web search for library docs. - Do not use Context7 for refactoring, writing scripts from scratch, debugging business logic, code review, or general programming concepts. ## License and Support diff --git a/plugins/copilot/context7/agents/docs-researcher.agent.md b/plugins/copilot/context7/agents/docs-researcher.agent.md index 424de0636..de3309ba2 100644 --- a/plugins/copilot/context7/agents/docs-researcher.agent.md +++ b/plugins/copilot/context7/agents/docs-researcher.agent.md @@ -24,7 +24,7 @@ When given a question about a library or framework, fetch the relevant documenta 4. **Fetch documentation**: Call `query-docs` with: - `libraryId`: The selected Context7 library ID (e.g., `/vercel/next.js`) - - `query`: The user's specific question for targeted results + - `query`: The user's specific question for targeted results, scoped to a single concept 5. **Return a focused answer**: Summarize the relevant documentation with: - Direct answer to the question @@ -33,7 +33,8 @@ When given a question about a library or framework, fetch the relevant documenta ## Guidelines -- Pass the user's full question as the query parameter for better relevance +- Pass the user's full question as the query parameter for better relevance, but keep each query to a single concept +- If the question spans multiple distinct concepts (e.g. routing and auth and caching), make a separate `query-docs` call per concept with the same library ID, unless the question is about how the concepts interact — combined queries dilute ranking and return shallow results for each topic - When the user mentions a version (e.g., "Next.js 15"), use version-specific library IDs if available - If `resolve-library-id` returns multiple matches, prefer official/primary packages over community forks - Keep responses concise - the goal is to answer the question, not dump entire documentation diff --git a/plugins/copilot/context7/commands/docs.md b/plugins/copilot/context7/commands/docs.md index a4cefa3aa..e3494ddfe 100644 --- a/plugins/copilot/context7/commands/docs.md +++ b/plugins/copilot/context7/commands/docs.md @@ -13,7 +13,7 @@ Fetches up-to-date documentation and code examples for a library. ``` - **library**: The library name, or a Context7 ID starting with `/` -- **query**: What you're looking for (optional but recommended) +- **query**: What you're looking for (optional but recommended; run the command once per distinct concept, unless asking how they interact) ## Examples diff --git a/plugins/copilot/context7/skills/context7-mcp/SKILL.md b/plugins/copilot/context7/skills/context7-mcp/SKILL.md index 234659d9f..245b08005 100644 --- a/plugins/copilot/context7/skills/context7-mcp/SKILL.md +++ b/plugins/copilot/context7/skills/context7-mcp/SKILL.md @@ -36,7 +36,9 @@ From the resolution results, choose based on: Call `query-docs` with: - `libraryId`: The selected Context7 library ID (e.g., `/vercel/next.js`) -- `query`: The user's specific question +- `query`: The user's specific question, scoped to a single concept + +If the user's question spans multiple distinct concepts (e.g. routing and auth and caching), make a separate `query-docs` call per concept with the same library ID, unless the question is about how the concepts interact — combined queries dilute ranking and return shallow results for each topic. ### Step 4: Use the Documentation @@ -48,6 +50,7 @@ Incorporate the fetched documentation into your response: ## Guidelines -- **Be specific**: Pass the user's full question as the query for better results +- **Be specific**: Pass the user's full question as the query for better results, but keep each query to a single concept +- **One topic per query**: Split multi-topic questions into separate `query-docs` calls — resolve the library ID once, then query per concept, unless the question is about how the concepts interact - **Version awareness**: When users mention versions ("Next.js 15", "React 19"), use version-specific library IDs if available from the resolution step - **Prefer official sources**: When multiple matches exist, prefer official/primary packages over community forks diff --git a/plugins/cursor/context7/agents/docs-researcher.md b/plugins/cursor/context7/agents/docs-researcher.md index 424de0636..de3309ba2 100644 --- a/plugins/cursor/context7/agents/docs-researcher.md +++ b/plugins/cursor/context7/agents/docs-researcher.md @@ -24,7 +24,7 @@ When given a question about a library or framework, fetch the relevant documenta 4. **Fetch documentation**: Call `query-docs` with: - `libraryId`: The selected Context7 library ID (e.g., `/vercel/next.js`) - - `query`: The user's specific question for targeted results + - `query`: The user's specific question for targeted results, scoped to a single concept 5. **Return a focused answer**: Summarize the relevant documentation with: - Direct answer to the question @@ -33,7 +33,8 @@ When given a question about a library or framework, fetch the relevant documenta ## Guidelines -- Pass the user's full question as the query parameter for better relevance +- Pass the user's full question as the query parameter for better relevance, but keep each query to a single concept +- If the question spans multiple distinct concepts (e.g. routing and auth and caching), make a separate `query-docs` call per concept with the same library ID, unless the question is about how the concepts interact — combined queries dilute ranking and return shallow results for each topic - When the user mentions a version (e.g., "Next.js 15"), use version-specific library IDs if available - If `resolve-library-id` returns multiple matches, prefer official/primary packages over community forks - Keep responses concise - the goal is to answer the question, not dump entire documentation diff --git a/plugins/cursor/context7/skills/context7-mcp/SKILL.md b/plugins/cursor/context7/skills/context7-mcp/SKILL.md index 234659d9f..245b08005 100644 --- a/plugins/cursor/context7/skills/context7-mcp/SKILL.md +++ b/plugins/cursor/context7/skills/context7-mcp/SKILL.md @@ -36,7 +36,9 @@ From the resolution results, choose based on: Call `query-docs` with: - `libraryId`: The selected Context7 library ID (e.g., `/vercel/next.js`) -- `query`: The user's specific question +- `query`: The user's specific question, scoped to a single concept + +If the user's question spans multiple distinct concepts (e.g. routing and auth and caching), make a separate `query-docs` call per concept with the same library ID, unless the question is about how the concepts interact — combined queries dilute ranking and return shallow results for each topic. ### Step 4: Use the Documentation @@ -48,6 +50,7 @@ Incorporate the fetched documentation into your response: ## Guidelines -- **Be specific**: Pass the user's full question as the query for better results +- **Be specific**: Pass the user's full question as the query for better results, but keep each query to a single concept +- **One topic per query**: Split multi-topic questions into separate `query-docs` calls — resolve the library ID once, then query per concept, unless the question is about how the concepts interact - **Version awareness**: When users mention versions ("Next.js 15", "React 19"), use version-specific library IDs if available from the resolution step - **Prefer official sources**: When multiple matches exist, prefer official/primary packages over community forks diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bc119bb8d..4845a5539 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -34,8 +34,8 @@ importers: specifier: ^10.1.1 version: 10.1.8(eslint@9.39.4(jiti@2.7.0)) eslint-plugin-prettier: - specifier: ^5.2.5 - version: 5.5.4(eslint-config-prettier@10.1.8(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0))(prettier@3.6.2) + specifier: ^5.5.6 + version: 5.5.6(eslint-config-prettier@10.1.8(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0))(prettier@3.6.2) prettier: specifier: ^3.6.2 version: 3.6.2 @@ -89,8 +89,8 @@ importers: specifier: ^9.34.0 version: 9.39.4(jiti@2.7.0) eslint-plugin-prettier: - specifier: ^5.2.5 - version: 5.5.4(eslint-config-prettier@10.1.8(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0))(prettier@3.6.2) + specifier: ^5.5.6 + version: 5.5.6(eslint-config-prettier@10.1.8(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0))(prettier@3.6.2) prettier: specifier: ^3.6.2 version: 3.6.2 @@ -104,8 +104,8 @@ importers: specifier: ^8.28.0 version: 8.47.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) vitest: - specifier: ^4.0.13 - version: 4.0.14(@opentelemetry/api@1.9.0)(@types/node@22.19.7)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.9.0) + specifier: ^4.1.9 + version: 4.1.9(@opentelemetry/api@1.9.0)(@types/node@22.19.7)(vite@7.2.4(@types/node@22.19.7)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.9.0)) packages/mcp: dependencies: @@ -125,8 +125,8 @@ importers: specifier: ^5.1.0 version: 5.1.0 jose: - specifier: ^6.1.3 - version: 6.1.3 + specifier: ^6.2.3 + version: 6.2.3 undici: specifier: ^6.26.0 version: 6.26.0 @@ -141,8 +141,8 @@ importers: specifier: ^5.8.2 version: 5.9.3 vitest: - specifier: ^4.0.13 - version: 4.0.14(@opentelemetry/api@1.9.0)(@types/node@25.0.3)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.9.0) + specifier: ^4.1.9 + version: 4.1.9(@opentelemetry/api@1.9.0)(@types/node@25.0.3)(vite@7.2.4(@types/node@25.0.3)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.9.0)) packages/pi: devDependencies: @@ -162,8 +162,8 @@ importers: specifier: ^5.9.3 version: 5.9.3 vitest: - specifier: ^4.0.14 - version: 4.0.14(@opentelemetry/api@1.9.0)(@types/node@25.9.1)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.9.0) + specifier: ^4.1.9 + version: 4.1.9(@opentelemetry/api@1.9.0)(@types/node@25.9.1)(vite@7.2.4(@types/node@25.9.1)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.9.0)) packages/sdk: devDependencies: @@ -180,8 +180,8 @@ importers: specifier: ^5.8.2 version: 5.9.3 vitest: - specifier: ^4.0.13 - version: 4.0.14(@opentelemetry/api@1.9.0)(@types/node@25.0.3)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.9.0) + specifier: ^4.1.9 + version: 4.1.9(@opentelemetry/api@1.9.0)(@types/node@25.0.3)(vite@7.2.4(@types/node@25.0.3)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.9.0)) packages/tools-ai-sdk: devDependencies: @@ -207,8 +207,8 @@ importers: specifier: ^5.8.2 version: 5.9.3 vitest: - specifier: ^4.0.13 - version: 4.0.14(@opentelemetry/api@1.9.0)(@types/node@25.0.3)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.9.0) + specifier: ^4.1.9 + version: 4.1.9(@opentelemetry/api@1.9.0)(@types/node@25.0.3)(vite@7.2.4(@types/node@25.0.3)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.9.0)) zod: specifier: ^4.4.3 version: 4.4.3 @@ -1077,9 +1077,9 @@ packages: resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} engines: {node: '>=8.0.0'} - '@pkgr/core@0.2.9': - resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} - engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + '@pkgr/core@0.3.6': + resolution: {integrity: sha512-SEeaJLb3qBNF/OaXnaR1NmmBbFYk1zC0ZH/52fATcRPLFg/p791YrcyFFy44Bo9sLaGuSuLp5Q6axbb/O+v/RA==} + engines: {node: ^14.18.0 || >=16.0.0} '@protobufjs/aspromise@1.1.2': resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} @@ -1431,34 +1431,34 @@ packages: resolution: {integrity: sha512-Fw28YZpRnA3cAHHDlkt7xQHiJ0fcL+NRcIqsocZQUSmbzeIKRpwttJjik5ZGanXP+vlA4SbTg+AbA3bP363l+w==} engines: {node: '>= 20'} - '@vitest/expect@4.0.14': - resolution: {integrity: sha512-RHk63V3zvRiYOWAV0rGEBRO820ce17hz7cI2kDmEdfQsBjT2luEKB5tCOc91u1oSQoUOZkSv3ZyzkdkSLD7lKw==} + '@vitest/expect@4.1.9': + resolution: {integrity: sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==} - '@vitest/mocker@4.0.14': - resolution: {integrity: sha512-RzS5NujlCzeRPF1MK7MXLiEFpkIXeMdQ+rN3Kk3tDI9j0mtbr7Nmuq67tpkOJQpgyClbOltCXMjLZicJHsH5Cg==} + '@vitest/mocker@4.1.9': + resolution: {integrity: sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==} peerDependencies: msw: ^2.4.9 - vite: ^6.0.0 || ^7.0.0-0 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: msw: optional: true vite: optional: true - '@vitest/pretty-format@4.0.14': - resolution: {integrity: sha512-SOYPgujB6TITcJxgd3wmsLl+wZv+fy3av2PpiPpsWPZ6J1ySUYfScfpIt2Yv56ShJXR2MOA6q2KjKHN4EpdyRQ==} + '@vitest/pretty-format@4.1.9': + resolution: {integrity: sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==} - '@vitest/runner@4.0.14': - resolution: {integrity: sha512-BsAIk3FAqxICqREbX8SetIteT8PiaUL/tgJjmhxJhCsigmzzH8xeadtp7LRnTpCVzvf0ib9BgAfKJHuhNllKLw==} + '@vitest/runner@4.1.9': + resolution: {integrity: sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==} - '@vitest/snapshot@4.0.14': - resolution: {integrity: sha512-aQVBfT1PMzDSA16Y3Fp45a0q8nKexx6N5Amw3MX55BeTeZpoC08fGqEZqVmPcqN0ueZsuUQ9rriPMhZ3Mu19Ag==} + '@vitest/snapshot@4.1.9': + resolution: {integrity: sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==} - '@vitest/spy@4.0.14': - resolution: {integrity: sha512-JmAZT1UtZooO0tpY3GRyiC/8W7dCs05UOq9rfsUUgEZEdq+DuHLmWhPsrTt0TiW7WYeL/hXpaE07AZ2RCk44hg==} + '@vitest/spy@4.1.9': + resolution: {integrity: sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==} - '@vitest/utils@4.0.14': - resolution: {integrity: sha512-hLqXZKAWNg8pI+SQXyXxWCTOpA3MvsqcbVeNgSi8x/CSN2wi26dSzn1wrOhmCmFjEvN9p8/kLFRHa6PI8jHazw==} + '@vitest/utils@4.1.9': + resolution: {integrity: sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==} accepts@2.0.0: resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} @@ -1629,8 +1629,8 @@ packages: resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==} engines: {node: '>=16'} - chai@6.2.1: - resolution: {integrity: sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg==} + chai@6.2.2: + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} engines: {node: '>=18'} chalk@4.1.2: @@ -1709,6 +1709,9 @@ packages: resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} engines: {node: '>= 0.6'} + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + cookie-signature@1.2.2: resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} engines: {node: '>=6.6.0'} @@ -1805,8 +1808,8 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-module-lexer@1.7.0: - resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + es-module-lexer@2.3.0: + resolution: {integrity: sha512-KLdwQm2NvGLDkQDCGvmiQrhkd0JbMzXthwQAUgWjQuQdBLFa3eiBP5arXZyA+f8x+x7OXgud6bq2rxjGtHV2tw==} es-object-atoms@1.1.1: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} @@ -1835,8 +1838,8 @@ packages: peerDependencies: eslint: '>=7.0.0' - eslint-plugin-prettier@5.5.4: - resolution: {integrity: sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==} + eslint-plugin-prettier@5.5.6: + resolution: {integrity: sha512-ifetmTcxWfz+4qRW3pH/ujdTq2jQIj59AxJMIN26K5avYgU8dxycUETQonWiW+wPrYXA0j3Try0l1CnwVQtDqQ==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: '@types/eslint': '>=8.0.0' @@ -1911,8 +1914,8 @@ packages: resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} engines: {node: '>=18.0.0'} - expect-type@1.2.2: - resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==} + expect-type@1.4.0: + resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} engines: {node: '>=12.0.0'} express-rate-limit@8.5.2: @@ -2235,8 +2238,8 @@ packages: resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} hasBin: true - jose@6.1.3: - resolution: {integrity: sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==} + jose@6.2.3: + resolution: {integrity: sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==} joycon@3.1.1: resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} @@ -2593,8 +2596,8 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} - prettier-linter-helpers@1.0.0: - resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} + prettier-linter-helpers@1.0.1: + resolution: {integrity: sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==} engines: {node: '>=6.0.0'} prettier@2.8.8: @@ -2785,8 +2788,8 @@ packages: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} - std-env@3.10.0: - resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + std-env@4.1.0: + resolution: {integrity: sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==} stdin-discarder@0.3.2: resolution: {integrity: sha512-eCPu1qRxPVkl5605OTWF8Wz40b4Mf45NY5LQmVPQ599knfs5QhASUm9GbJ5BDMDOXgrnh0wyEdvzmL//YMlw0A==} @@ -2832,8 +2835,8 @@ packages: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} - synckit@0.11.11: - resolution: {integrity: sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==} + synckit@0.11.13: + resolution: {integrity: sha512-eNRKgb3z66Yp3D2CixVujOUvXLFUTij/zVnV8KRyvFdQwpz7I5DS8UfRkTeLzb64u+dkzDSdelE24izu+zSSUg==} engines: {node: ^14.18.0 || >=16.0.0} term-size@2.2.1: @@ -2853,12 +2856,16 @@ packages: tinyexec@0.3.2: resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + tinyexec@1.2.4: + resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==} + engines: {node: '>=18'} + tinyglobby@0.2.15: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} - tinyrainbow@3.0.3: - resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} + tinyrainbow@3.1.0: + resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} engines: {node: '>=14.0.0'} to-regex-range@5.0.1: @@ -3017,20 +3024,23 @@ packages: yaml: optional: true - vitest@4.0.14: - resolution: {integrity: sha512-d9B2J9Cm9dN9+6nxMnnNJKJCtcyKfnHj15N6YNJfaFHRLua/d3sRKU9RuKmO9mB0XdFtUizlxfz/VPbd3OxGhw==} + vitest@4.1.9: + resolution: {integrity: sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@opentelemetry/api': ^1.9.0 '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.0.14 - '@vitest/browser-preview': 4.0.14 - '@vitest/browser-webdriverio': 4.0.14 - '@vitest/ui': 4.0.14 + '@vitest/browser-playwright': 4.1.9 + '@vitest/browser-preview': 4.1.9 + '@vitest/browser-webdriverio': 4.1.9 + '@vitest/coverage-istanbul': 4.1.9 + '@vitest/coverage-v8': 4.1.9 + '@vitest/ui': 4.1.9 happy-dom: '*' jsdom: '*' + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: '@edge-runtime/vm': optional: true @@ -3044,6 +3054,10 @@ packages: optional: true '@vitest/browser-webdriverio': optional: true + '@vitest/coverage-istanbul': + optional: true + '@vitest/coverage-v8': + optional: true '@vitest/ui': optional: true happy-dom: @@ -4080,7 +4094,7 @@ snapshots: express: 5.2.1 express-rate-limit: 8.5.2(express@5.2.1) hono: 4.12.23 - jose: 6.1.3 + jose: 6.2.3 json-schema-typed: 8.0.2 pkce-challenge: 5.0.0 raw-body: 3.0.2 @@ -4105,7 +4119,7 @@ snapshots: '@opentelemetry/api@1.9.0': {} - '@pkgr/core@0.2.9': {} + '@pkgr/core@0.3.6': {} '@protobufjs/aspromise@1.1.2': {} @@ -4455,60 +4469,62 @@ snapshots: '@vercel/oidc@3.1.0': {} - '@vitest/expect@4.0.14': + '@vitest/expect@4.1.9': dependencies: '@standard-schema/spec': 1.1.0 '@types/chai': 5.2.3 - '@vitest/spy': 4.0.14 - '@vitest/utils': 4.0.14 - chai: 6.2.1 - tinyrainbow: 3.0.3 + '@vitest/spy': 4.1.9 + '@vitest/utils': 4.1.9 + chai: 6.2.2 + tinyrainbow: 3.1.0 - '@vitest/mocker@4.0.14(vite@7.2.4(@types/node@22.19.7)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.9.0))': + '@vitest/mocker@4.1.9(vite@7.2.4(@types/node@22.19.7)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.9.0))': dependencies: - '@vitest/spy': 4.0.14 + '@vitest/spy': 4.1.9 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: vite: 7.2.4(@types/node@22.19.7)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.9.0) - '@vitest/mocker@4.0.14(vite@7.2.4(@types/node@25.0.3)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.9.0))': + '@vitest/mocker@4.1.9(vite@7.2.4(@types/node@25.0.3)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.9.0))': dependencies: - '@vitest/spy': 4.0.14 + '@vitest/spy': 4.1.9 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: vite: 7.2.4(@types/node@25.0.3)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.9.0) - '@vitest/mocker@4.0.14(vite@7.2.4(@types/node@25.9.1)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.9.0))': + '@vitest/mocker@4.1.9(vite@7.2.4(@types/node@25.9.1)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.9.0))': dependencies: - '@vitest/spy': 4.0.14 + '@vitest/spy': 4.1.9 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: vite: 7.2.4(@types/node@25.9.1)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.9.0) - '@vitest/pretty-format@4.0.14': + '@vitest/pretty-format@4.1.9': dependencies: - tinyrainbow: 3.0.3 + tinyrainbow: 3.1.0 - '@vitest/runner@4.0.14': + '@vitest/runner@4.1.9': dependencies: - '@vitest/utils': 4.0.14 + '@vitest/utils': 4.1.9 pathe: 2.0.3 - '@vitest/snapshot@4.0.14': + '@vitest/snapshot@4.1.9': dependencies: - '@vitest/pretty-format': 4.0.14 + '@vitest/pretty-format': 4.1.9 + '@vitest/utils': 4.1.9 magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/spy@4.0.14': {} + '@vitest/spy@4.1.9': {} - '@vitest/utils@4.0.14': + '@vitest/utils@4.1.9': dependencies: - '@vitest/pretty-format': 4.0.14 - tinyrainbow: 3.0.3 + '@vitest/pretty-format': 4.1.9 + convert-source-map: 2.0.0 + tinyrainbow: 3.1.0 accepts@2.0.0: dependencies: @@ -4680,7 +4696,7 @@ snapshots: camelcase@8.0.0: {} - chai@6.2.1: {} + chai@6.2.2: {} chalk@4.1.2: dependencies: @@ -4731,6 +4747,8 @@ snapshots: content-type@1.0.5: {} + convert-source-map@2.0.0: {} + cookie-signature@1.2.2: {} cookie@0.7.2: {} @@ -4802,7 +4820,7 @@ snapshots: es-errors@1.3.0: {} - es-module-lexer@1.7.0: {} + es-module-lexer@2.3.0: {} es-object-atoms@1.1.1: dependencies: @@ -4874,12 +4892,12 @@ snapshots: dependencies: eslint: 9.39.4(jiti@2.7.0) - eslint-plugin-prettier@5.5.4(eslint-config-prettier@10.1.8(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0))(prettier@3.6.2): + eslint-plugin-prettier@5.5.6(eslint-config-prettier@10.1.8(eslint@9.39.4(jiti@2.7.0)))(eslint@9.39.4(jiti@2.7.0))(prettier@3.6.2): dependencies: eslint: 9.39.4(jiti@2.7.0) prettier: 3.6.2 - prettier-linter-helpers: 1.0.0 - synckit: 0.11.11 + prettier-linter-helpers: 1.0.1 + synckit: 0.11.13 optionalDependencies: eslint-config-prettier: 10.1.8(eslint@9.39.4(jiti@2.7.0)) @@ -4965,7 +4983,7 @@ snapshots: dependencies: eventsource-parser: 3.0.6 - expect-type@1.2.2: {} + expect-type@1.4.0: {} express-rate-limit@8.5.2(express@5.2.1): dependencies: @@ -5345,7 +5363,7 @@ snapshots: jiti@2.7.0: {} - jose@6.1.3: {} + jose@6.2.3: {} joycon@3.1.1: {} @@ -5649,7 +5667,7 @@ snapshots: prelude-ls@1.2.1: {} - prettier-linter-helpers@1.0.0: + prettier-linter-helpers@1.0.1: dependencies: fast-diff: 1.3.0 @@ -5871,7 +5889,7 @@ snapshots: statuses@2.0.2: {} - std-env@3.10.0: {} + std-env@4.1.0: {} stdin-discarder@0.3.2: {} @@ -5920,9 +5938,9 @@ snapshots: dependencies: has-flag: 4.0.0 - synckit@0.11.11: + synckit@0.11.13: dependencies: - '@pkgr/core': 0.2.9 + '@pkgr/core': 0.3.6 term-size@2.2.1: {} @@ -5938,12 +5956,14 @@ snapshots: tinyexec@0.3.2: {} + tinyexec@1.2.4: {} + tinyglobby@0.2.15: dependencies: fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 - tinyrainbow@3.0.3: {} + tinyrainbow@3.1.0: {} to-regex-range@5.0.1: dependencies: @@ -6095,119 +6115,89 @@ snapshots: tsx: 4.21.0 yaml: 2.9.0 - vitest@4.0.14(@opentelemetry/api@1.9.0)(@types/node@22.19.7)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.9.0): - dependencies: - '@vitest/expect': 4.0.14 - '@vitest/mocker': 4.0.14(vite@7.2.4(@types/node@22.19.7)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.9.0)) - '@vitest/pretty-format': 4.0.14 - '@vitest/runner': 4.0.14 - '@vitest/snapshot': 4.0.14 - '@vitest/spy': 4.0.14 - '@vitest/utils': 4.0.14 - es-module-lexer: 1.7.0 - expect-type: 1.2.2 + vitest@4.1.9(@opentelemetry/api@1.9.0)(@types/node@22.19.7)(vite@7.2.4(@types/node@22.19.7)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.9.0)): + dependencies: + '@vitest/expect': 4.1.9 + '@vitest/mocker': 4.1.9(vite@7.2.4(@types/node@22.19.7)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.9.0)) + '@vitest/pretty-format': 4.1.9 + '@vitest/runner': 4.1.9 + '@vitest/snapshot': 4.1.9 + '@vitest/spy': 4.1.9 + '@vitest/utils': 4.1.9 + es-module-lexer: 2.3.0 + expect-type: 1.4.0 magic-string: 0.30.21 obug: 2.1.1 pathe: 2.0.3 picomatch: 4.0.3 - std-env: 3.10.0 + std-env: 4.1.0 tinybench: 2.9.0 - tinyexec: 0.3.2 + tinyexec: 1.2.4 tinyglobby: 0.2.15 - tinyrainbow: 3.0.3 + tinyrainbow: 3.1.0 vite: 7.2.4(@types/node@22.19.7)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.0 '@types/node': 22.19.7 transitivePeerDependencies: - - jiti - - less - - lightningcss - msw - - sass - - sass-embedded - - stylus - - sugarss - - terser - - tsx - - yaml - vitest@4.0.14(@opentelemetry/api@1.9.0)(@types/node@25.0.3)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.9.0): - dependencies: - '@vitest/expect': 4.0.14 - '@vitest/mocker': 4.0.14(vite@7.2.4(@types/node@25.0.3)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.9.0)) - '@vitest/pretty-format': 4.0.14 - '@vitest/runner': 4.0.14 - '@vitest/snapshot': 4.0.14 - '@vitest/spy': 4.0.14 - '@vitest/utils': 4.0.14 - es-module-lexer: 1.7.0 - expect-type: 1.2.2 + vitest@4.1.9(@opentelemetry/api@1.9.0)(@types/node@25.0.3)(vite@7.2.4(@types/node@25.0.3)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.9.0)): + dependencies: + '@vitest/expect': 4.1.9 + '@vitest/mocker': 4.1.9(vite@7.2.4(@types/node@25.0.3)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.9.0)) + '@vitest/pretty-format': 4.1.9 + '@vitest/runner': 4.1.9 + '@vitest/snapshot': 4.1.9 + '@vitest/spy': 4.1.9 + '@vitest/utils': 4.1.9 + es-module-lexer: 2.3.0 + expect-type: 1.4.0 magic-string: 0.30.21 obug: 2.1.1 pathe: 2.0.3 picomatch: 4.0.3 - std-env: 3.10.0 + std-env: 4.1.0 tinybench: 2.9.0 - tinyexec: 0.3.2 + tinyexec: 1.2.4 tinyglobby: 0.2.15 - tinyrainbow: 3.0.3 + tinyrainbow: 3.1.0 vite: 7.2.4(@types/node@25.0.3)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.0 '@types/node': 25.0.3 transitivePeerDependencies: - - jiti - - less - - lightningcss - msw - - sass - - sass-embedded - - stylus - - sugarss - - terser - - tsx - - yaml - vitest@4.0.14(@opentelemetry/api@1.9.0)(@types/node@25.9.1)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.9.0): - dependencies: - '@vitest/expect': 4.0.14 - '@vitest/mocker': 4.0.14(vite@7.2.4(@types/node@25.9.1)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.9.0)) - '@vitest/pretty-format': 4.0.14 - '@vitest/runner': 4.0.14 - '@vitest/snapshot': 4.0.14 - '@vitest/spy': 4.0.14 - '@vitest/utils': 4.0.14 - es-module-lexer: 1.7.0 - expect-type: 1.2.2 + vitest@4.1.9(@opentelemetry/api@1.9.0)(@types/node@25.9.1)(vite@7.2.4(@types/node@25.9.1)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.9.0)): + dependencies: + '@vitest/expect': 4.1.9 + '@vitest/mocker': 4.1.9(vite@7.2.4(@types/node@25.9.1)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.9.0)) + '@vitest/pretty-format': 4.1.9 + '@vitest/runner': 4.1.9 + '@vitest/snapshot': 4.1.9 + '@vitest/spy': 4.1.9 + '@vitest/utils': 4.1.9 + es-module-lexer: 2.3.0 + expect-type: 1.4.0 magic-string: 0.30.21 obug: 2.1.1 pathe: 2.0.3 picomatch: 4.0.3 - std-env: 3.10.0 + std-env: 4.1.0 tinybench: 2.9.0 - tinyexec: 0.3.2 + tinyexec: 1.2.4 tinyglobby: 0.2.15 - tinyrainbow: 3.0.3 + tinyrainbow: 3.1.0 vite: 7.2.4(@types/node@25.9.1)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.0 '@types/node': 25.9.1 transitivePeerDependencies: - - jiti - - less - - lightningcss - msw - - sass - - sass-embedded - - stylus - - sugarss - - terser - - tsx - - yaml web-streams-polyfill@3.3.3: {} diff --git a/rules/context7-cli.md b/rules/context7-cli.md index f723bde16..def26cb9a 100644 --- a/rules/context7-cli.md +++ b/rules/context7-cli.md @@ -1,4 +1,4 @@ -Use the `ctx7` CLI 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. +Use the `ctx7` CLI 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. Do not use for: refactoring, writing scripts from scratch, debugging business logic, code review, or general programming concepts. @@ -6,10 +6,10 @@ Do not use for: refactoring, writing scripts from scratch, debugging business lo 1. Resolve library: `npx ctx7@latest library ""` — use the official library name with proper punctuation (e.g., "Next.js" not "nextjs", "Customer.io" not "customerio", "Three.js" not "threejs") 2. Pick the best match (ID format: `/org/project`) by: exact name match, description relevance, code snippet count, source reputation (High/Medium preferred), and benchmark score (higher is better). If results don't look right, try alternate names or queries (e.g., "next.js" not "nextjs", or rephrase the question) -3. Fetch docs: `npx ctx7@latest docs ""` +3. Fetch docs: `npx ctx7@latest docs ""` — run a separate `docs` command per distinct concept if the question spans multiple topics, unless it's about how they interact 4. Answer using the fetched documentation -You MUST call `library` first to get a valid ID unless the user provides one directly in `/org/project` format. Use the user's full question as the query -- specific and detailed queries return better results than vague single words. Do not run more than 3 commands per question. Do not include sensitive information (API keys, passwords, credentials) in queries. +You MUST call `library` first to get a valid ID unless the user provides one directly in `/org/project` format. Use the user's full question as the query — specific and detailed queries return better results than vague single words, but keep each query to a single concept unless the question is about how concepts interact; combined multi-topic queries dilute ranking and return shallow results for each topic. Do not run more than 3 commands per question. Do not include sensitive information (API keys, passwords, credentials) in queries. For version-specific docs, use `/org/project/version` from the `library` output (e.g., `/vercel/next.js/v14.3.0`). diff --git a/rules/context7-mcp.md b/rules/context7-mcp.md index fe994d41e..bc6ccb3e6 100644 --- a/rules/context7-mcp.md +++ b/rules/context7-mcp.md @@ -1,4 +1,4 @@ -Use Context7 MCP 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. +Use Context7 MCP 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. Do not use for: refactoring, writing scripts from scratch, debugging business logic, code review, or general programming concepts. @@ -6,5 +6,5 @@ Do not use for: refactoring, writing scripts from scratch, debugging business lo 1. Always start with `resolve-library-id` using the library name and the user's question, unless the user provides an exact library ID in `/org/project` format 2. Pick the best match (ID format: `/org/project`) by: exact name match, description relevance, code snippet count, source reputation (High/Medium preferred), and benchmark score (higher is better). If results don't look right, try alternate names or queries (e.g., "next.js" not "nextjs", or rephrase the question). Use version-specific IDs when the user mentions a version -3. `query-docs` with the selected library ID and the user's full question (not single words) +3. `query-docs` with the selected library ID and the user's full question (not single words), scoped to a single concept. If the question spans multiple distinct concepts (e.g. routing and auth and caching), make a separate `query-docs` call per concept with the same library ID, unless the question is about how the concepts interact — combined queries dilute ranking and return shallow results for each topic 4. Answer using the fetched docs diff --git a/skills/context7-cli/references/docs.md b/skills/context7-cli/references/docs.md index ef5cae9b0..6c06cb72b 100644 --- a/skills/context7-cli/references/docs.md +++ b/skills/context7-cli/references/docs.md @@ -78,16 +78,17 @@ IMPORTANT: Do not call `ctx7 docs` more than 3 times per question. If you cannot ### Writing good queries -The query directly affects the quality of results. Be specific and include relevant details. Do not include any sensitive or confidential information such as API keys, passwords, credentials, personal data, or proprietary code in your query. +The query directly affects the quality of results. Be specific and include relevant details, but keep each query to one topic — if the question spans multiple distinct concepts, run a separate `ctx7 docs` command per concept instead of combining them, unless the question is about how the concepts interact. Do not include any sensitive or confidential information such as API keys, passwords, credentials, personal data, or proprietary code in your query. | Quality | Example | |---------|---------| | Good | `"How to set up authentication with JWT in Express.js"` | | Good | `"React useEffect cleanup function with async operations"` | -| Bad | `"auth"` | -| Bad | `"hooks"` | +| Bad (too vague) | `"auth"` | +| Bad (too vague) | `"hooks"` | +| Bad (too broad) | `"routing and auth and caching in Next.js"` | -Use the user's full question as the query when possible — vague one-word queries return generic results. +Use the user's full question as the query when possible — vague one-word queries return generic results, and multi-topic queries dilute ranking and return shallow results for each topic. The output contains two types of content: **code snippets** (titled, with language-tagged blocks) and **info snippets** (prose explanations with breadcrumb context). diff --git a/skills/context7-mcp/SKILL.md b/skills/context7-mcp/SKILL.md index 234659d9f..245b08005 100644 --- a/skills/context7-mcp/SKILL.md +++ b/skills/context7-mcp/SKILL.md @@ -36,7 +36,9 @@ From the resolution results, choose based on: Call `query-docs` with: - `libraryId`: The selected Context7 library ID (e.g., `/vercel/next.js`) -- `query`: The user's specific question +- `query`: The user's specific question, scoped to a single concept + +If the user's question spans multiple distinct concepts (e.g. routing and auth and caching), make a separate `query-docs` call per concept with the same library ID, unless the question is about how the concepts interact — combined queries dilute ranking and return shallow results for each topic. ### Step 4: Use the Documentation @@ -48,6 +50,7 @@ Incorporate the fetched documentation into your response: ## Guidelines -- **Be specific**: Pass the user's full question as the query for better results +- **Be specific**: Pass the user's full question as the query for better results, but keep each query to a single concept +- **One topic per query**: Split multi-topic questions into separate `query-docs` calls — resolve the library ID once, then query per concept, unless the question is about how the concepts interact - **Version awareness**: When users mention versions ("Next.js 15", "React 19"), use version-specific library IDs if available from the resolution step - **Prefer official sources**: When multiple matches exist, prefer official/primary packages over community forks diff --git a/skills/find-docs/SKILL.md b/skills/find-docs/SKILL.md index 99e5c6e14..da5f683cd 100644 --- a/skills/find-docs/SKILL.md +++ b/skills/find-docs/SKILL.md @@ -3,7 +3,7 @@ name: find-docs description: >- Retrieves up-to-date documentation, API references, and code examples for any developer technology. Use this skill whenever the user asks about a specific - library, framework, SDK, CLI tool, or cloud service -- even for well-known ones + library, framework, SDK, CLI tool, or cloud service — even for well-known ones like React, Next.js, Prisma, Express, Tailwind, Django, or Spring Boot. Your training data may not reflect recent API changes or version updates. @@ -11,7 +11,7 @@ description: >- issues, "how do I" questions mentioning a library name, debugging that involves library-specific behavior, setup instructions, and CLI tool usage. - Use even when you think you know the answer -- do not rely on training data + Use even when you think you know the answer — do not rely on training data for API details, signatures, or configuration options as they are frequently outdated. Always verify against current docs. Prefer this over web search for library documentation and API details. @@ -21,16 +21,17 @@ description: >- Retrieve current documentation and code examples for any library using the Context7 CLI. -Make sure the CLI is up to date before running commands: +Run commands with `npx ctx7@latest` so setup always uses the latest CLI without a global install: ```bash -npm install -g ctx7@latest +npx ctx7@latest library "" +npx ctx7@latest docs "" ``` -Or run directly without installing: +Optionally install globally if you prefer a bare `ctx7` command: ```bash -npx ctx7@latest +npm install -g ctx7@latest ``` ## Workflow @@ -39,13 +40,13 @@ Two-step process: resolve the library name to an ID, then query docs with that I ```bash # Step 1: Resolve library ID -ctx7 library +npx ctx7@latest library "" # Step 2: Query documentation -ctx7 docs +npx ctx7@latest docs "" ``` -You MUST call `ctx7 library` first to obtain a valid library ID UNLESS the user explicitly provides a library ID in the format `/org/project` or `/org/project/version`. +You MUST call `library` first to obtain a valid library ID UNLESS the user explicitly provides a library ID in the format `/org/project` or `/org/project/version`. IMPORTANT: Do not run these commands more than 3 times per question. If you cannot find what you need after 3 attempts, use the best result you have. @@ -54,11 +55,13 @@ IMPORTANT: Do not run these commands more than 3 times per question. If you cann Resolves a package/product name to a Context7-compatible library ID and returns matching libraries. ```bash -ctx7 library react "How to clean up useEffect with async operations" -ctx7 library nextjs "How to set up app router with middleware" -ctx7 library prisma "How to define one-to-many relations with cascade delete" +npx ctx7@latest library React "How to clean up useEffect with async operations" +npx ctx7@latest library "Next.js" "How to set up app router with middleware" +npx ctx7@latest library Prisma "How to define one-to-many relations with cascade delete" ``` +Use the official library name with proper punctuation (e.g., "Next.js" not "nextjs", "Customer.io" not "customerio", "Three.js" not "threejs"). If results look wrong, try alternate spellings such as `next.js` before changing the query. + Always pass a `query` argument — it is required and directly affects result ranking. Use the user's intent to form the query, which helps disambiguate when multiple libraries share a similar name. Do not include any sensitive or confidential information such as API keys, passwords, credentials, personal data, or proprietary code in your query. ### Result fields @@ -92,36 +95,37 @@ If the user mentions a specific version, use a version-specific library ID: ```bash # General (latest indexed) -ctx7 docs /vercel/next.js "How to set up app router" +npx ctx7@latest docs /vercel/next.js "How to set up app router" # Version-specific -ctx7 docs /vercel/next.js/v14.3.0-canary.87 "How to set up app router" +npx ctx7@latest docs /vercel/next.js/v14.3.0-canary.87 "How to set up app router" ``` -The available versions are listed in the `ctx7 library` output. Use the closest match to what the user specified. +The available versions are listed in the `library` command output. Use the closest match to what the user specified. ## Step 2: Query Documentation Retrieves up-to-date documentation and code examples for the resolved library. ```bash -ctx7 docs /facebook/react "How to clean up useEffect with async operations" -ctx7 docs /vercel/next.js "How to add authentication middleware to app router" -ctx7 docs /prisma/prisma "How to define one-to-many relations with cascade delete" +npx ctx7@latest docs /facebook/react "How to clean up useEffect with async operations" +npx ctx7@latest docs /vercel/next.js "How to add authentication middleware to app router" +npx ctx7@latest docs /prisma/prisma "How to define one-to-many relations with cascade delete" ``` ### Writing good queries -The query directly affects the quality of results. Be specific and include relevant details. Do not include any sensitive or confidential information such as API keys, passwords, credentials, personal data, or proprietary code in your query. +The query directly affects the quality of results. Be specific and include relevant details, but keep each query to one topic — if the question spans multiple distinct concepts, run a separate `docs` command per concept instead of combining them, unless the question is about how the concepts interact. Do not include any sensitive or confidential information such as API keys, passwords, credentials, personal data, or proprietary code in your query. | Quality | Example | |---------|---------| | Good | `"How to set up authentication with JWT in Express.js"` | | Good | `"React useEffect cleanup function with async operations"` | -| Bad | `"auth"` | -| Bad | `"hooks"` | +| Bad (too vague) | `"auth"` | +| Bad (too vague) | `"hooks"` | +| Bad (too broad) | `"routing and auth and caching in Next.js"` | -Use the user's full question as the query when possible, vague one-word queries return generic results. +Use the user's full question as the query when possible — vague one-word queries return generic results, and multi-topic queries dilute ranking and return shallow results for each topic. The output contains two types of content: **code snippets** (titled, with language-tagged blocks) and **info snippets** (prose explanations with breadcrumb context). @@ -134,14 +138,14 @@ Works without authentication. For higher rate limits: export CONTEXT7_API_KEY=your_key # Option B: OAuth login -ctx7 login +npx ctx7@latest login ``` ## Error Handling If a command fails with a quota error ("Monthly quota reached" or "quota exceeded"): 1. Inform the user their Context7 quota is exhausted -2. Suggest they authenticate for higher limits: `ctx7 login` +2. Suggest they authenticate for higher limits: `npx ctx7@latest login` 3. If they cannot or choose not to authenticate, answer from training knowledge and clearly note it may be outdated Do not silently fall back to training data — always tell the user why Context7 was not used. @@ -149,6 +153,7 @@ Do not silently fall back to training data — always tell the user why Context7 ## Common Mistakes - Library IDs require a `/` prefix — `/facebook/react` not `facebook/react` -- Always run `ctx7 library` first — `ctx7 docs react "hooks"` will fail without a valid ID +- Always run `npx ctx7@latest library` first — `npx ctx7@latest docs react "hooks"` will fail without a valid ID - Use descriptive queries, not single words — `"React useEffect cleanup function"` not `"hooks"` +- One topic per query — split `"routing and auth and caching"` into a separate `docs` command per concept, unless the question is about how they interact - Do not include sensitive information (API keys, passwords, credentials) in queries