REST API
Base URL: https://api.protogrid.dev (a local stack answers at http://localhost:8080). All responses are JSON and carry next_actions. Anonymous access is rate limited per IP (20 requests per minute); a free API key raises that to 60 per minute and 5,000 per day. The x-ratelimit-* and retry-after headers say when to back off. Server names are reverse-DNS (io.github.owner/name) and may be given URL-encoded (io.github.owner%2Fname) or with the literal slash.
GET /v1/search
Section titled “GET /v1/search”| param | type | notes |
|---|---|---|
q |
string, required | intent in plain words, 1–500 chars |
limit |
1–50 | default 10 |
class |
comma list | R0,R1,R2,L0,unknown |
transport |
streamable-http | sse |
keeps servers with a non-templated remote of that transport |
category |
string | one of the fixed taxonomy ids |
min_trust |
0–100 | |
flags |
comma list | every listed trust flag must be present |
exclude_flags |
comma list | any listed flag excludes a server, e.g. multi-version-spam,duplicate-repo |
Hybrid search: tool-level and server-level keyword and semantic candidates, fused by reciprocal rank, re-ranked by trust, grouped by server. mode says hybrid or keyword (fallback when no embedding model is loaded).
Result fields: name, title, description, connection_class, autonomous, human_steps, categories, trust_score, trust_flags[], quality_score, quality_label (see quality labels), tool_count, score, matched_tools[] {name, description}.
GET /v1/servers/{name}
Section titled “GET /v1/servers/{name}”The descriptor. ?schemas=true includes tool input/output schemas. 404 when unknown.
GET /v1/servers/{name}/tools
Section titled “GET /v1/servers/{name}/tools”Current tools with full schemas, ordered by name. limit (1–100, default 100), cursor (the next_cursor of the previous page).
{ "server": "…", "count": 100, "tools": [{ "name": "…", "description": "…", "input_schema": { }, "output_schema": null, "annotations": { }, "source": "probe", "observed_at": "…" }], "next_cursor": "…", "next_actions": [ ] }GET /v1/servers/{name}/connection
Section titled “GET /v1/servers/{name}/connection”target = mcpServers (default) | vscode | cursor | claude-code-cli | codex-toml | gemini | goose.
Returns server, key (the config key, last segment of the name), kind (remote | package | bundle), class, autonomous, human_steps, target, content_type (application/json for config objects, text/plain for the CLI, TOML and YAML targets), connection, secrets[] {name, where}, and for remotes auth_type and oauth metadata when known.
| status | when |
|---|---|
403 blocked |
on the blocklist |
410 deleted |
deleted from the official registry |
409 no_connection |
no usable remote or package |
Examples per target:
# claude-code-cliclaude mcp add --transport http agentdm https://api.agentdm.ai/mcp/v1/grid --header "Authorization: ${AGENTDM_TOKEN}"
# codex-toml[mcp_servers.agentdm]url = "https://api.agentdm.ai/mcp/v1/grid"env_http_headers = { "Authorization" = "AGENTDM_TOKEN" }// cursor{ "mcpServers": { "docs-mcp": { "type": "http", "url": "https://tandem.ac/mcp" } }, "deeplink": "cursor://anysphere.cursor-deeplink/mcp/install?name=docs-mcp&config=eyJ0eXBlIjoi…" }GET /v1/servers/{name}/quality
Section titled “GET /v1/servers/{name}/quality”The descriptor’s quality block (see quality score) plus history, the daily score for the last days (1–400, default 90), blocked (true when the listing is on the blocklist, whatever its score) and owner (the descriptor’s owner-verified block).
curl -s 'https://api.protogrid.dev/v1/servers/io.github.acme%2Facme-mcp/quality?days=30'GET /v1/servers/{name}/changes
Section titled “GET /v1/servers/{name}/changes”Tool-definition history, newest first: tool, kind (added, removed, changed), observed_at, fields (what changed), description_before and description_after when the description changed, description_similarity (share of words in common), and both definition hashes.
limit (1–200, default 50) and before (the next_before of the previous page).
A server’s first observation is a baseline and records nothing.
GET /v1/servers/{name}/dependencies
Section titled “GET /v1/servers/{name}/dependencies”For each npm or PyPI package of the server: status (resolved, incomplete, unknown to the resolver, or pending), dependency_count, the official MCP SDK in the graph (mcp_sdk), and every known advisory with id, severity, summary, the affected package and version, relation (direct or indirect), fixed_in (the lowest published fix above that version, or null) and a url on osv.dev.
Advisories are matched daily; graphs are read on each new release and refreshed monthly, daily for monitored servers (see Quality score); the attribution field carries the data credit.
curl -s 'https://api.protogrid.dev/v1/servers/io.github.acme%2Facme-mcp/dependencies'POST /v1/check
Section titled “POST /v1/check”Probes a remote MCP server URL once and reports its quality checks and its readiness for the Claude and OpenAI directories (see Check your server).
Body { "url": "https://mcp.example.com/mcp" }; ?wait=N (0 to 25 seconds) holds the request until the result is in.
curl -s -X POST 'https://api.protogrid.dev/v1/check?wait=25' -H 'content-type: application/json' -d '{"url":"https://mcp.example.com/mcp"}'The answer is 202 with status queued or running, a Location and retry-after while the probe runs, and 200 with status done and result once it finished.
result holds probe (outcome, protocol, a summary of the OAuth metadata, the tools without schemas, redirects), quality (the same checks as the descriptor, stability na) and readiness (one entry per directory with every requirement, its status, detail and source).
server names the catalog server when the URL is one of its remotes, and page is the shareable result page.
The same URL within 5 minutes returns the recent check.
A refused URL answers 400 invalid_url with a reason; an exhausted allowance answers 429 check_quota_exceeded with scope (you, host or global) and retry-after.
GET /v1/check/{id}
Section titled “GET /v1/check/{id}”Reads a check; ?wait=N as above.
Finished results never change and are cacheable for an hour; unknown ids answer 404.
Results are kept 30 days and every check response carries X-Robots-Tag: noindex.
Caching
Section titled “Caching”Every successful GET carries Cache-Control: public, max-age=…, stale-while-revalidate=… and a weak ETag; If-None-Match answers 304. Defaults: search 60 s, server descriptors, tools and connections 300 s, /v0.1 300 s. /mcp, /healthz, errors and non-GET responses are no-store. Cached responses do not carry the per-IP rate-limit headers; a 429 always does, with retry-after.
Errors
Section titled “Errors”Validation errors answer 400 with { "error": "bad_request", "issues": { "fieldErrors": { … } } }. Unknown routes answer 404 { "error": "not_found" }. Rate limiting answers 429 with retry-after; an exhausted daily quota answers 429 daily_quota_exceeded, and a bad API key 401 invalid_api_key (see API keys and limits).
GET /healthz
Section titled “GET /healthz”{ "ok": true, "search": "hybrid" | "keyword", "mcp": "/mcp" }.