# Using the MCP servers > Connect an AI assistant to GPU Ninja over MCP, sign in to the consumer server or use the sign-in-free connector, and see what each tool does. URL: https://gpuninja.com/docs/mcp Last updated: 2026-10-05 ## Overview GPU Ninja runs two remote MCP servers. Both offer exactly the same ten read-only tools, answer from the same public data, and return the same results for the same call. They differ only in sign-in, hostname and rate limiting. | | Consumer MCP | Connector | | ----------- | ---------------------------------------------------------------------------- | ------------------------------------- | | Endpoint | `https://mcp.gpuninja.com/mcp` | `https://connector.gpuninja.com/mcp` | | Sign-in | Free registration, through OAuth | None | | Listed in | Claude's Connectors Directory | ChatGPT's plugin directory | | Rate limits | Per account: 60 tool calls and 300 requests a minute, 5,000 tool calls a day | One global ceiling shared by everyone | | Source code | Open source, in `gpuninja-mcp` | Runs the same open-source core | Use the consumer MCP from Claude or any MCP client that supports OAuth sign-in. Use the connector from ChatGPT, or from any client where you'd rather not sign in. Registration is not access control. Every tool reads GPU Ninja's public data, which is also open to anyone through the public API (https://gpuninja.com/docs/api) with no key, and through the sign-in-free connector. Registering gives you an identity, your own rate limits and, unless you opt out, the daily digest (https://gpuninja.com/docs/digest). Step-by-step setup guides: - Using GPU Ninja in Claude (https://gpuninja.com/docs/claude-connector): the directory listing, claude.ai, Claude Code and other MCP clients. - Using GPU Ninja in ChatGPT (https://gpuninja.com/docs/chatgpt): the sign-in-free connector. ## Sign in The consumer MCP at `https://mcp.gpuninja.com/mcp` requires a free GPU Ninja account. You don't create one in advance: the first time your MCP client connects, it opens GPU Ninja's sign-in page, and signing in for the first time registers you. ### What registering means - Your identity is a verified email address. There are no passwords. You sign in by opening a link GPU Ninja emails to you. - Registering subscribes you to the daily digest (https://gpuninja.com/docs/digest) by default. The sign-in form has a checkbox for it. In the EEA, the UK, or when your country can't be determined, the box starts unticked; elsewhere it starts ticked. Either way, you choose before you submit. The choice applies only when the sign-in creates a new account. - Opting out of the digest never affects MCP access. You can unsubscribe from any digest email, or from your account page, at any time. - Registration doesn't unlock any data. The same data is open through the public API (https://gpuninja.com/docs/api) and the connector at `https://connector.gpuninja.com/mcp`. See Your account (https://gpuninja.com/docs/account) for managing the digest, exporting your data and deleting your account. ### Sign in from Claude GPU Ninja is listed in Claude's Connectors Directory at `https://claude.ai/directory/connectors/gpu-ninja`. Add it from there, or add `https://mcp.gpuninja.com/mcp` yourself as a custom connector in claude.ai or as an HTTP MCP server in Claude Code. Claude then walks you through the sign-in below. Using GPU Ninja in Claude (https://gpuninja.com/docs/claude-connector) has the step-by-step version. ### The sign-in flow 1. Your client calls the MCP endpoint without a token. The server answers `401` with a `WWW-Authenticate` header pointing at its protected resource metadata, which names `https://app.gpuninja.com` as the authorization server. 2. Your client opens GPU Ninja's sign-in page at `app.gpuninja.com` in your browser. Enter your email address, complete the bot check, and choose whether to receive the digest. If you're already signed in there, you skip straight to step 5. 3. GPU Ninja emails you a sign-in link. It works once and expires after 15 minutes. 4. Open the link. If you open it in the same browser, you're signed in straight away. If you open it on another device, such as your phone, choose Approve sign-in on that device, and the original browser tab signs in on its own within a few seconds. You can instead choose Sign in on this device instead. 5. A consent page names the app asking for access (as the app calls itself), where its sign-in information comes from, where the approval will be sent, the address you're signed in as, and what the app may do: "Use GPU Ninja's read-only MCP tools as you", plus "Stay connected" when the app asks for `offline_access`. If every return address the app registered is on your own computer, as with Claude Code, the page says that only apps running on this computer can receive the approval. Choose Allow or Deny. 6. Your browser returns to the app, which exchanges the approval for tokens. You're connected. Access tokens last 15 minutes and your client refreshes them on its own. A refresh token lasts 30 days from its last use, so a client you use at least once a month stays connected. ### Connected apps and signing out Your account page at `https://app.gpuninja.com/account` lists every connected app with when it was connected and last used. Revoke disconnects one. Because access tokens are verified without a call back to the account service, a revoked app can keep working for up to 15 minutes. If you delete your account, every connected app stops working. Tool calls then fail with "Your GPU Ninja account no longer exists. Reconnect to sign in again." Reconnecting signs you in afresh, as a new account. ### Any other OAuth client Any MCP client that implements MCP authorization can sign in. The details it needs are all published: | Item | Value | | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Protected resource metadata | `https://mcp.gpuninja.com/.well-known/oauth-protected-resource/mcp` (also at `/.well-known/oauth-protected-resource`) | | Resource | `https://mcp.gpuninja.com/mcp` | | Authorization server | `https://app.gpuninja.com` | | Authorization server metadata | `https://app.gpuninja.com/.well-known/oauth-authorization-server` | | Grant | Authorization code with PKCE (`S256` only); public clients only, with no client secret | | Client identification | A Client ID Metadata Document (an `https://` `client_id`), preferred; or dynamic client registration at `https://app.gpuninja.com/oauth/register` | | Redirect URIs | `https://` on a non-loopback host, or `http://` on `localhost`, `127.0.0.1` or `[::1]`. A loopback redirect matches on any port | | Scopes | `mcp` (always granted) and `offline_access`. You get a refresh token when you ask for `offline_access` or your client registered the `refresh_token` grant type | | Tokens | ES256-signed JWT access tokens, 15 minutes; rotating refresh tokens, 30 days from last use | | Revocation | `https://app.gpuninja.com/oauth/revoke`, with a refresh token, disconnects the app | Send the access token in the `Authorization: Bearer` header. A token in the query string is ignored. Refresh tokens rotate on every use; presenting a used refresh token again (outside a 60-second retry grace) revokes the whole connection, and you have to sign in again. > Note: > A dynamically registered client that never completes a sign-in is deleted after 24 hours, and any client unused for 90 days is deleted. Your client simply registers or identifies itself again. ## Enable the connector in ChatGPT ChatGPT lists the sign-in-free connector, `https://connector.gpuninja.com/mcp`. Find GPU Ninja in ChatGPT's plugin directory and enable it. There is no account and no sign-in step, and ChatGPT sends GPU Ninja no identity. See Using GPU Ninja in ChatGPT (https://gpuninja.com/docs/chatgpt) for details and for how the connector differs from the consumer MCP. The connector isn't limited to ChatGPT: any MCP client can use it without authentication. ## Protocol Both servers speak MCP over Streamable HTTP at a single endpoint, `/mcp`. - Methods. Send JSON-RPC requests with `POST` and `Content-Type: application/json`. `GET` returns `405`; there is no SSE stream, and responses are always plain JSON, whatever your `Accept` header says. - Protocol versions. `2026-07-28` and `2025-11-25`, plus `2025-06-18` and `2025-03-26` for older clients that propose them. The server is stateless: it never issues a session ID, and no request depends on an earlier one. - Capabilities. Tools only. There are no resources, prompts or logging, and the tool list is the same for every caller. - Requests. Bodies are limited to 65,536 bytes, and batch (array) requests are rejected. - Health. `https://mcp.gpuninja.com/health` reports whether the consumer server can read GPU Ninja's data. The connector's is `https://connector.gpuninja.com/healthz`. Every successful tool result carries both a readable `text` item for the model and `structuredContent` that matches the tool's declared output schema. Output property names are the public API (https://gpuninja.com/docs/api)'s camelCase names, such as `chipSlug`, `lowestPriceUsd`, `nextCursor` and `asOf`. Tool names and input argument names are snake_case. Every tool is read-only and annotated with `readOnlyHint: true`, `destructiveHint: false`, `idempotentHint: true` and `openWorldHint: false`. ## Tools Most tools take a slug, the identifier GPU Ninja uses in every URL: for example `asus-rog-strix-4090` for a card or `ad102` for a chip. If you don't know a slug, search for it first with `search_boards` or `search_chips`. List tools take `limit` (1 to 20, default 10) and `cursor`, and return `total`, `nextCursor` and `asOf`. Searching and paging (https://gpuninja.com/docs/mcp#searching-and-paging) explains how they behave. ### search_boards Finds graphics cards (boards) by name text and by filters. Discontinued cards are excluded unless you ask for them. | Argument | Type | Meaning | | ---------------------- | --------------------------------- | ------------------------------------------------------------ | | `query` | string, 1 to 100 characters | Text to match against cards | | `vendor` | array of 1 to 10 distinct strings | Board vendors to include, such as `ASUS` or `MSI` | | `chip` | slug | Only cards built on this chip, such as `ad102` | | `min_vram_gb` | number, 0 to 1024 | Smallest VRAM to include, in GB | | `max_vram_gb` | number, 0 to 1024 | Largest VRAM to include, in GB | | `max_length_mm` | integer, 50 to 1000 | Longest card you can fit, in millimetres | | `max_slot_width` | number, 1 to 6 | Thickest card you can fit, in expansion slots, such as `2.5` | | `max_tdp_w` | integer, 1 to 3000 | Highest board power to include, in watts | | `max_price` | number, 0 to 1,000,000 | Upper bound, in US dollars, on the lowest current offer | | `in_stock_only` | boolean, default `false` | Only cards with at least one in-stock offer | | `include_discontinued` | boolean, default `false` | Also return discontinued cards | | `limit` | integer, 1 to 20, default 10 | Maximum results to return | | `cursor` | string | `nextCursor` from a previous call, to fetch the next page | All arguments are optional. If `min_vram_gb` is greater than `max_vram_gb`, the call fails. Each item has `slug`, `name`, `chipSlug`, `vendor`, `vramGb`, `tdpW`, `lengthMm`, `slotWidth`, `lowestPriceUsd`, `inStock`, `status` and `links` (the card's API URL, page and `.txt` page). A page may hold fewer than `limit` items; keep calling with `nextCursor` until it is `null`. #### Example Cards with at least 16 GB of VRAM that are no longer than 300 mm: ```json { "jsonrpc": "2.0", "id": 7, "method": "tools/call", "params": { "name": "search_boards", "arguments": { "min_vram_gb": 16, "max_length_mm": 300, "limit": 2 } } } ``` The `structuredContent` of the result looks like this (one item shown): ```json { "items": [ { "slug": "gigabyte-rtx-4070-ti-super-windforce-oc", "name": "Gigabyte RTX 4070 Ti Super Windforce OC", "chipSlug": "ad103", "vendor": "Gigabyte", "vramGb": 16, "tdpW": 285, "lengthMm": 261, "slotWidth": 2.5, "lowestPriceUsd": 799, "inStock": true, "status": "active", "links": { "self": "https://gpuninja.com/api/v1/boards/gigabyte-rtx-4070-ti-super-windforce-oc", "page": "https://gpuninja.com/boards/gigabyte-rtx-4070-ti-super-windforce-oc", "text": "https://gpuninja.com/boards/gigabyte-rtx-4070-ti-super-windforce-oc.txt" } } ], "total": 14, "nextCursor": "c1.eyJ0Ijoic2VhcmNoX2JvYXJkcyIsImgiOiI5ZjJjMWQwYTRiN2U4YzMxIiwiYSI6IjIwMjYtMTAtMDRUMDY6MDA6MDBaIiwicSI6IjEwIn0", "asOf": "2026-10-04T06:00:00Z" } ``` The text item gives the same results as a numbered list, and ends by telling the model how to get the next page: ``` GPU Ninja · search_boards · 2 of 14 results · data as of 2026-10-04T06:00:00Z 1. Gigabyte RTX 4070 Ti Super Windforce OC — gigabyte-rtx-4070-ti-super-windforce-oc chip ad103 · 16 GB · 285 W · 261 mm · 2.5 slots · from 799.00 USD · in stock https://gpuninja.com/boards/gigabyte-rtx-4070-ti-super-windforce-oc 2. … More results: call search_boards again with the same arguments and cursor "c1.eyJ…". ``` ### search_chips Finds GPU chips, the processor a card is built on, such as AD102. Discontinued chips are excluded unless you ask for them. | Argument | Type | Meaning | | ---------------------- | --------------------------------- | ------------------------------------------------------------------------------------------------------------- | | `query` | string, 1 to 100 characters | Text to match against chips | | `vendor` | array of 1 to 10 distinct strings | Chip designers to include, such as `NVIDIA`, `AMD` or `Intel` | | `min_vram_gb` | number, 0 to 1024 | Smallest maximum VRAM the chip supports, in GB | | `max_tdp_w` | integer, 1 to 3000 | Highest reference power to include, in watts | | `pci_device_id` | string | PCI device ID as the operating system reports it: four hex digits, such as `2684` or `0x2684`, in either case | | `include_discontinued` | boolean, default `false` | Also return discontinued chips | | `limit` | integer, 1 to 20, default 10 | Maximum results to return | | `cursor` | string | `nextCursor` from a previous call | All arguments are optional. Each item has `slug`, `name`, `vendor`, `vramGb` (the maximum the chip supports), `tdpW` (reference power), `pciVendorId`, `pciDeviceIds`, `status` and `links`. A page may hold fewer than `limit` items; keep calling with `nextCursor` until it is `null`. #### Example Identify a GPU from the PCI device ID your operating system reports: ```json { "name": "search_chips", "arguments": { "pci_device_id": "0x2684" } } ``` This returns one item, with `slug` `ad102` and `pciVendorId` `10de`, `total: 1` and `nextCursor: null`. ### get_board Returns the full specification of one graphics card: chip, VRAM, clocks, dimensions, power connectors, cooling, current offers, benchmark results and data freshness. | Argument | Type | Meaning | | -------- | -------------- | ---------------------------------------------- | | `slug` | slug, required | The card's slug, such as `asus-rog-strix-4090` | `structuredContent` has `board` (the `data` of the card's API document (https://gpuninja.com/docs/api)), `omittedMembers`, `links` and `asOf`. `omittedMembers` names any members left out to keep the answer under 24,000 characters; the text holds the full page. When nothing is left out it is `[]`, and `board` is the document's `data` unchanged. Members are left out in this order, only as far as needed: `provenance`, then `benchmarkResults`, then `offers`. The text item is the card's `.txt` page, the same content as its page on gpuninja.com. If that text is over 24,000 characters it is cut at a line break and ends with a link to the full text. If the `.txt` page can't be fetched, the call still succeeds, and the text gives the card's name, slug, status and page link instead. #### Example ```json { "name": "get_board", "arguments": { "slug": "asus-rog-strix-4090" } } ``` The text comes from `https://gpuninja.com/boards/asus-rog-strix-4090.txt`, `links.self` is `https://gpuninja.com/api/v1/boards/asus-rog-strix-4090`, and `omittedMembers` is `[]`. If a card's slug has been renamed, the old slug still works: the result reports the card under its current slug. ### get_chip Returns the full specification of one GPU chip: architecture, die, core counts, memory bus, reference clocks and power, launch details, and the cards built on it. | Argument | Type | Meaning | | -------- | -------------- | -------------------------------- | | `slug` | slug, required | The chip's slug, such as `ad102` | `structuredContent` has `chip`, `omittedMembers`, `links` and `asOf`, and the text item is the chip's `.txt` page, exactly as for `get_board`. `omittedMembers` names any members left out to keep the answer under 24,000 characters; the text holds the full page. For a chip they are left out in this order: `provenance`, then `benchmarkResults`, then `boards`. #### Example ```json { "name": "get_chip", "arguments": { "slug": "ad102" } } ``` ### compare_boards Compares two or three graphics cards side by side: key specs, lowest current price, and benchmark results. | Argument | Type | Meaning | | -------- | ---------------------------------------- | ----------------------------------------------- | | `slugs` | array of 2 or 3 distinct slugs, required | The cards to compare, in the order to show them | `structuredContent` has: - `boards`: each card's `slug`, `name`, `status` and `links`, in your order. - `specs`: one row per field, always in this order: `chipSlug`, `vendor`, `vramGb`, `vramType`, `factoryBoostClockMhz`, `tdpW`, `lengthMm`, `widthMm`, `slotWidth`, `powerConnectors`, `recommendedPsuW`, `coolingType`, `lowestPriceUsd`, `inStock` and `status`. Each row has `field`, a readable `label`, a `unit` (or `null`) and `values`, which follow the order of `boards`, with `null` where a value isn't recorded. - `benchmarks`: one row per combination of benchmark, AI model and test platform for which at least one card has a result. Results only share a row when all three match, because numbers from different platforms aren't comparable. When a card has several results for one row, the latest run wins. Rows are sorted by benchmark, then model, then platform, and capped at 40; `benchmarksTruncated` is `true` when rows were left out, either by the cap or to keep the answer under 24,000 characters. - `asOf`. If any slug doesn't exist, the call fails and lists every missing slug. If two of your slugs turn out to be the same card (an old slug and its renamed one), the call fails and says so. #### Example ```json { "name": "compare_boards", "arguments": { "slugs": ["asus-rog-strix-4090", "msi-rtx-4080-super-gaming-x"] } } ``` The text item is a fixed-width table (some rows left out here), one block per benchmark row, and a link to each card's page: ``` GPU Ninja · compare_boards · 2 cards · data as of 2026-10-04T06:00:00Z asus-rog-strix-4090 msi-rtx-4080-super-gaming-x Chip ad102 ad103 VRAM (GB) 24 16 Length (mm) 358 322 Lowest price (USD) 1799.00 999.00 In stock yes yes Status active active llama-cpp-tg128 / llama-3-3-70b-q4 on pl-ryzen-9950x-cuda-12-6 — tok/s (higher is better) 41.2 — asus-rog-strix-4090: https://gpuninja.com/boards/asus-rog-strix-4090 msi-rtx-4080-super-gaming-x: https://gpuninja.com/boards/msi-rtx-4080-super-gaming-x ``` ### which_chassis_fit Lists the PC cases and enclosures a graphics card fits in. Each result has a verdict: `fits`, `check` (within 10 mm of the case's clearance) or `does-not-fit`, with the reasons and a power supply note. | Argument | Type | Meaning | | ---------------------- | -------------------------------------------------------------- | ---------------------------------------------------- | | `board` | slug, required | The card's slug | | `fit` | array of 1 to 3 distinct verdicts, default `["fits", "check"]` | Verdicts to include: `fits`, `check`, `does-not-fit` | | `include_discontinued` | boolean, default `false` | Also return discontinued cases | | `limit` | integer, 1 to 20, default 10 | Maximum results to return | | `cursor` | string | `nextCursor` from a previous call | `structuredContent` has `board` (its `slug`, `name` and `links`) and `items`, each with `chassis`, `fit` and `psu`, plus `total`, `nextCursor` and `asOf`. A page may hold fewer than `limit` items; keep calling with `nextCursor` until it is `null`. The verdicts are GPU Ninja's precomputed ones, the same as on the website, in GPU Ninja's order; the server never works out fit itself. Each reason has a `code` (`length-clear`, `length-tight`, `length-exceeds`, `length-unknown`, `slots-ok`, `slots-exceed`, `slots-unknown` or `override`) and the measurements behind it, with `null` for members that code doesn't use. The text item turns each reason into a sentence, such as "Card length 358 mm exceeds the 355 mm clearance by 3 mm." #### Example ```json { "name": "which_chassis_fit", "arguments": { "board": "asus-rog-strix-4090", "fit": ["does-not-fit"] } } ``` One item from the result: the 358 mm, 3.5-slot card against a case with 355 mm and 3 slots of clearance. ```json { "chassis": { "slug": "fractal-north", "name": "Fractal Design North", "vendor": "Fractal Design", "formFactor": "mid-tower", "maxGpuLengthMm": 355, "maxGpuSlots": 3, "psuIncludedW": null, "lowestPriceUsd": 139.99, "inStock": true, "status": "active", "links": { "self": "https://gpuninja.com/api/v1/chassis/fractal-north", "page": "https://gpuninja.com/chassis/fractal-north", "text": "https://gpuninja.com/chassis/fractal-north.txt" } }, "fit": { "verdict": "does-not-fit", "basis": "dimensions", "reasons": [ { "code": "length-exceeds", "boardLengthMm": 358, "clearanceMm": 355, "marginMm": -3, "boardSlotWidth": null, "maxSlots": null, "fits": null, "note": null }, { "code": "slots-exceed", "boardLengthMm": null, "clearanceMm": null, "marginMm": null, "boardSlotWidth": 3.5, "maxSlots": 3, "fits": null, "note": null } ] }, "psu": { "includedW": null, "recommendedW": 1000, "sufficient": null } } ``` ### what_fits_in_chassis Lists the graphics cards that fit in a PC case or enclosure, with the same verdicts and reasons as `which_chassis_fit`, plus each card's specs and price. | Argument | Type | Meaning | | ---------------------- | -------------------------------------------------------------- | ---------------------------------------------------- | | `chassis` | slug, required | The case's slug, such as `fractal-north` | | `fit` | array of 1 to 3 distinct verdicts, default `["fits", "check"]` | Verdicts to include: `fits`, `check`, `does-not-fit` | | `include_discontinued` | boolean, default `false` | Also return discontinued cards | | `limit` | integer, 1 to 20, default 10 | Maximum results to return | | `cursor` | string | `nextCursor` from a previous call | `structuredContent` has `chassis` (its `slug`, `name` and `links`) and `items`, each with `board` (the same fields as a `search_boards` item), `fit` and `psu`, plus `total`, `nextCursor` and `asOf`. A page may hold fewer than `limit` items; keep calling with `nextCursor` until it is `null`. #### Example ```json { "name": "what_fits_in_chassis", "arguments": { "chassis": "fractal-north", "fit": ["fits"], "limit": 5 } } ``` ### which_models_run Lists the AI models a graphics card has enough VRAM to run, with each model's parameter count, quantization, minimum VRAM and the VRAM left over. | Argument | Type | Meaning | | -------- | ---------------------------- | --------------------------------- | | `board` | slug, required | The card's slug | | `limit` | integer, 1 to 20, default 10 | Maximum results to return | | `cursor` | string | `nextCursor` from a previous call | `structuredContent` has `board`, `determinable` and `items`, each with `model` and `headroomGb`, plus `total`, `nextCursor` and `asOf`. `determinable` is `false` when the card's VRAM isn't recorded, in which case nothing can be listed. A page may hold fewer than `limit` items; keep calling with `nextCursor` until it is `null`. For measured performance, use `get_benchmark_results`. #### Example ```json { "name": "which_models_run", "arguments": { "board": "asus-rog-strix-4090", "limit": 3 } } ``` ### which_boards_run_model Lists the graphics cards with enough VRAM to run an AI model, with each card's specs, price, stock and the VRAM left over. | Argument | Type | Meaning | | ---------------------- | ---------------------------- | ----------------------------------------------- | | `model` | slug, required | The AI model's slug, such as `llama-3-3-70b-q4` | | `in_stock_only` | boolean, default `false` | Only cards with at least one in-stock offer | | `include_discontinued` | boolean, default `false` | Also return discontinued cards | | `limit` | integer, 1 to 20, default 10 | Maximum results to return | | `cursor` | string | `nextCursor` from a previous call | `structuredContent` has `model`, `determinable` and `items`, each with `board` (the same fields as a `search_boards` item) and `headroomGb`, plus `total`, `nextCursor` and `asOf`. `determinable` is `false` when the model's minimum VRAM isn't recorded, in which case no cards can be listed. A page may hold fewer than `limit` items; keep calling with `nextCursor` until it is `null`. #### Example ```json { "name": "which_boards_run_model", "arguments": { "model": "llama-3-3-70b-q4", "in_stock_only": true } } ``` ### get_benchmark_results Lists benchmark measurements for a graphics card, an AI model or a test platform, optionally narrowed by benchmark. Each result names the benchmark, metric, unit, value, model, platform, run date and source. | Argument | Type | Meaning | | ----------- | ---------------------------- | ----------------------------------------- | | `board` | slug | A card's slug | | `model` | slug | An AI model's slug | | `platform` | slug | A test platform's slug | | `benchmark` | slug | A benchmark's slug, to narrow the results | | `limit` | integer, 1 to 20, default 10 | Maximum results to return | | `cursor` | string | `nextCursor` from a previous call | Provide at least one of `board`, `model` or `platform`; otherwise the call fails with "Provide at least one of board, model or platform." The results come from the first of those you give, in that order, and are narrowed by every other slug you give. A narrowing slug that matches nothing gives an empty list, not an error. `structuredContent` has `scope` (the `kind` and `slug` the results came from), `items`, `total`, `nextCursor` and `asOf`. Each item has `benchmark` (with `metric`, `unit` and `higherIsBetter`), `subject`, `model` (or `null`), `platform`, `value`, `runDate` and `sourceUrl`. Items are ordered by benchmark, then model, then platform, then subject, then newest run first. A page may hold fewer than `limit` items; keep calling with `nextCursor` until it is `null`. #### Example ```json { "name": "get_benchmark_results", "arguments": { "board": "asus-rog-strix-4090", "benchmark": "llama-cpp-tg128" } } ``` ## Searching and paging `search_boards` and `search_chips` use the same search library as the website's buyer's guide (https://gpuninja.com/docs/buyers-guide) and the desktop app, so the same filters give the same results in the same order. Searching the index (https://gpuninja.com/docs/api/search) describes the library itself. ### Text matching `query` is matched against each item's name, vendor, chip and slug. - Case and accents are ignored, and so are symbols such as `™`. `GeForce`, `geforce` and `GEFORCE` are the same. - Letters and digits are separate words. `rtx4090` searches for `rtx` and `4090`, and `16GB` for `16` and `gb`. - Every word must match. A word matches when it equals a word in the item, or when it has at least two characters and is the start of one: `40` matches both `4080` and `4090`. - Small typos are forgiven in words made of letters. A word of 5 or more letters can be one edit away (a wrong, missing, extra or swapped letter), so `geforec` finds `geforce`. A word of 9 or more letters can be two edits away. - Numbers are never fuzzy. `4080` never matches `4090`. A single character matches only an identical word. - At most 16 different words. A longer `query` fails with an "Invalid search" error. ### Filters - Filters combine with each other and with `query`: an item must satisfy all of them. - Ranges are inclusive. `max_length_mm: 300` includes a 300 mm card. - `vendor` ignores case, accents and extra spaces. - An item whose value for a filtered field isn't recorded doesn't match that filter. For example, `max_price` excludes cards with no current offer. - Discontinued items are excluded by default. Set `include_discontinued: true` to include them. Discontinued items carry `status: "discontinued"`, and their line in the text ends with `· DISCONTINUED`. ### Ordering - With `query`, the best matches come first, then by name, A to Z. - Without `query`, results are ordered by name, A to Z. - Ties are broken by slug, so the same call always returns the same order. A word scores more when it matches exactly than as a prefix, and more as a prefix than as a typo. It also scores more in the name than in the vendor or chip, and least in the slug: an exact word in the name is worth 24, a prefix in the name 16, a one-letter typo in the name 8, and an exact word in the vendor or chip 12. Your words appearing in order in the name add 8, and a name made of exactly your words adds 16 more. These are the search library's worked examples, over three test cards with discontinued cards included: A is "ASUS ROG Strix GeForce RTX 4090 OC" (chip `ad102`), B is "MSI GeForce RTX 4080 Gaming X Trio" (chip `ad103`), and C is "NVIDIA GeForce RTX 4090 Founders Edition" (chip `ad102`, discontinued). | Text | Results in order (score) | | ------------------------------------------ | ------------------------ | | `rtx 4090` | A (56), C (56) | | `4090` | A (24), C (24) | | `40` | A (16), B (16), C (16) | | `rtx 40` | A (48), B (48), C (48) | | `geforec 4080` | B (32) | | `asus` | A (24) | | `4` | (none) | | `nvidia geforce rtx 4090 founders edition` | C (168) | | `ad102` | A (24), C (24) | The search tools take no sort argument. These are the library's defaults they use: | Search fact | Value | Meaning | | -------------------------- | ------------------------------------------------------------- | ----------------------------------------------------------- | | `LIMITS.maxTextTokens` | `16` | Most different words a `query` may hold | | `DEFAULT_SORT.withText` | `[{"by":"relevance","dir":"desc"},{"by":"name","dir":"asc"}]` | Order with a `query`, before the final tie-break by slug | | `DEFAULT_SORT.withoutText` | `[{"by":"name","dir":"asc"}]` | Order without a `query`, before the final tie-break by slug | | `includeDiscontinued` | `false` | Discontinued items are left out unless you ask for them | The compatibility and benchmark tools keep GPU Ninja's own order for their lists. ### Cursors When more results remain, `nextCursor` holds an opaque string. To get the next page, call the same tool again with the same arguments and `cursor` set to that string. You can change `limit` between pages. On the last page, `nextCursor` is `null`. - A page may hold fewer than `limit` items even when more remain, because the server shortens a page that would not fit in 24,000 characters. Keep calling with `nextCursor` until it is `null`; no item is skipped or repeated. - `total` counts every matching item across all pages. - A cursor only works with the tool and arguments that produced it. Otherwise the call fails with "This cursor doesn't match the call. Repeat the call without cursor to start again." - A cursor stops working when GPU Ninja's data is updated, so a walk through the pages never mixes two versions of the catalogue. The call fails with "GPU Ninja's data was updated since the first page. Repeat the call without cursor to start again." ## Results and errors `asOf` is when the data was generated, as an absolute UTC timestamp. It is never the time of your request. Results can lag the website by up to five minutes after an update, while caches refresh. Every text item and every `structuredContent` is at most 24,000 characters. Long results are shortened, as each tool describes, rather than failing. Problems with a call come back as a tool error, with `isError: true`, no `structuredContent`, and one plain-English text the model can act on: - Unknown slug. For `get_board` and `get_chip`: `No board with slug "…".` (or `chip`), then up to five similar slugs under "Did you mean:", then "Use search_boards to find slugs." (or `search_chips`). Other tools name the kind and the slug, and for a card add the pointer to `search_boards`. - Invalid arguments. The message names the argument and the rule it broke. A call whose `arguments` hold more than 64 values in total fails with "Too many values in arguments: the limit is 64." - Data unavailable. "GPU Ninja's data service didn't respond. Try again in a minute." - Unexpected data. "GPU Ninja returned data in an unexpected format. This has been logged." - Too large. "GPU Ninja's data for this request is too large to return." This happens only when even a single item or a shortened result can't fit. A search with no matches isn't an error: it returns `items: []`, `total: 0` and a text saying no items match, followed by the filters you used. ## Limits | Limit | Consumer MCP | Connector | | ---------------- | ---------------------------------------- | ----------------------------------------------- | | Items per result | Up to 20 (10 by default) | Up to 20 (10 by default) | | Tool calls | 60 a minute and 5,000 a day, per account | Shared ceiling of 200 requests every 10 seconds | | All requests | 300 a minute, per account | Included in the ceiling above | | Request body | 65,536 bytes | 65,536 bytes | - On the consumer MCP, that's 60 tool calls and 300 requests per minute per account, and 5,000 tool calls per day. Over a limit, the server answers HTTP `429` with a `Retry-After` header giving the seconds to wait. - The daily count resets at 00:00 UTC. Only `tools/call` counts towards the per-minute tool limit and the daily limit; every request, including listing tools and other protocol messages, counts towards the 300 a minute. - The connector's ceiling is shared by every user of the connector and counted separately at each Cloudflare location, so it isn't a per-person allowance. Over it, the connector also answers `429` with `Retry-After`. - There is no per-IP limit on either server. ### When you hit a limit: the busy message The message for an over-limit call starts "GPU Ninja is busy" and ends by saying how long to wait, for example "Try again in 60 seconds." On the consumer MCP it names the limit you reached; for the daily limit it also gives the reset time in UTC. It means what it says: wait the given time and try again. On the connector, it usually means the service as a whole is busy for a few seconds, not that you did anything wrong. Some clients show a `429` as a connection error rather than passing the message to the assistant. ## What is logged Each server writes one log line per request. It records the time, the HTTP status, the protocol method and version, the tool name, which argument names were used, the public catalogue item looked up (only when it exists), the client software's name, whether sign-in and the rate limits let the request through, the outcome, the number of results, the latency and a Cloudflare request ID. Neither server logs your IP address, your account identifier, the app's client ID, your token, the values of your arguments (so never the text of a search), cursors, or the results. The connector never logs the identifiers ChatGPT attaches to requests, and it logs only a quarter of successful requests, while logging every error and every `429`. On the consumer MCP, the user ID inside your access token is used only to apply your rate limits. Your daily count is kept by GPU Ninja's account service for 30 days. Logs are kept by Cloudflare, as processor, for the Workers Logs default, currently 7 days. The privacy policy (https://gpuninja.com/legal/privacy) is the authoritative statement. ## Open source The consumer MCP server, including the search component and the data types it uses, is open source under the Apache License 2.0, in the public `gpuninja-mcp` repository at https://github.com/gpuninja/gpuninja-mcp. The connector runs the same core. See Open source (https://gpuninja.com/docs/open-source) for what the repository contains, how to run your own copy, and how to contribute. ## Help Report problems through Support (https://gpuninja.com/docs/support).