agentkit.md is a public, agent-ready registry of plugin cards: structured, AI-generated profiles of agent-harness plugins (what a plugin does, how to install it, which platforms and agents it supports), served over a keyless REST API, an MCP server, and a searchable catalog.
Everything below is keyless: no account, no API key, no OAuth. The machine-readable version of this page is the OpenAPI spec at [https://agentkit.md/openapi.json](https://agentkit.md/openapi.json); the auth posture is documented at [https://agentkit.md/auth.md](https://agentkit.md/auth.md); an MCP interface to the same data is available at https://agentkit.md/mcp.
Five calls and you are productive:
bash
curl "https://agentkit.md/api/v1/plugins?limit=5"
curl "https://agentkit.md/api/v1/cards/0cv/herdr-mobile-relay"
curl "https://agentkit.md/api/v1/search?q=notify+me+when+a+build+fails"
curl -X POST "https://agentkit.md/api/v1/search" \
-H "content-type: application/json" \
-H "idempotency-key: my-unique-key-001" \
-d '{"q":"notify me when a build fails","limit":5}'
curl "https://agentkit.md/api/v1/plugins?limit=5&cursor=<next_cursor>"
curl "https://agentkit.md/api/v1/plugins.md?limit=5"
Returns a filtered, paginated list of plugin cards. All filters are optional and matched case-insensitively. Paginate by offset (limit + offset) or by cursor (opaque next_cursor token, null on the last page); the response envelope is { total, limit, offset, next_cursor, results }.
Response: `{ total, limit, offset, next_cursor, results: PluginCard[] }` — `next_cursor` is a base64url token for the next page, or `null` on the last page.
Returns the full card for one plugin. Card keys are owner/repo pairs and therefore contain a slash (e.g. 0cv/herdr-mobile-relay) — URL-encode or pass them directly in the path. Unknown keys return 404 with code CARD_NOT_FOUND.
Response: the full PluginCard object. Errors: `404` problem with code `CARD_NOT_FOUND`.
Fuzzy server-side search across the full corpus (name, key, tags, capabilities, description — weighted in that order). Returns the matched cards with a relevance score in [0,1] and the server-side tookMs.
Response: `{ query, tookMs, results: [{ card, score }] }` with score in [0,1], higher is better.
Body variant of GET /api/v1/search for clients that prefer a JSON body: { q: string, limit?: integer 1-50 }. Honors an optional Idempotency-Key header: it is echoed in the response, and repeated keys within 60 seconds replay the first successful response (per-isolate best-effort window).
Response: same shape as GET /api/v1/search.
Aggregate counts over the whole card corpus: totals, per-harness, per-surface, per-platform, per-agent, and per-quality breakdowns, plus generatedAt (when the bundle was built).
The harness dimension values the registry indexes today with per-harness card counts. All current cards are Herdr plugins; this endpoint exists so multi-harness clients do not hardcode that fact.
GET /api/v1/plugins supports two interchangeable pagination styles, both documented in the OpenAPI response schemas.
Offset pagination (original style): pass `limit` (1-200, default 50) and `offset` (default 0). Loop `offset += limit` until `offset >= total`.
Cursor pagination (recommended for agents): pass `limit` and the opaque `cursor` value from the previous response. Every list response now carries `next_cursor`: a base64url-encoded continuation token, or `null` on the last page. Pass it back unmodified — the token is opaque and may change format between releases, so never decode or construct one. Cursor pages stay aligned even if `limit` changes between requests.
Both styles return the same envelope `{ total, limit, offset, next_cursor, results }`; results are in stable index order within one bundle generation (see `generatedAt` in `/stats`), so keys are stable but new cards may shift offsets after a regeneration.
POST /api/v1/search honors an optional Idempotency-Key request header (any string up to 200 characters). The key is echoed back in the response as `idempotency-key`; repeating a request with the same key within 60 seconds replays the first successful response instead of re-running the search, and the replay is additionally marked with an `idempotent-replay: true` header. This makes retries safe: a timeout or dropped connection cannot change the result you already received. Keys are held per edge isolate for 60 seconds, so treat replay as best-effort rather than a durability guarantee. Failed requests are never cached — only 200 responses are replayed.
Every API response carries RateLimit headers: ratelimit-limit (120), ratelimit-remaining, and ratelimit-reset (seconds until the current window rolls over). Limits are applied per IP over a 60-second window. When you exceed the limit the endpoint returns 429 with a Retry-After header and a problem+json body (code RATE_LIMIT_EXCEEDED); back off for the stated seconds and retry. Because the deployment is edge-distributed, the counters are a per-node approximation: treat the headers as guidance, not a contract.
Every error is RFC 9457 `application/problem+json`:
json
{
"type": "about:blank",
"title": "Card not found",
"status": 404,
"code": "CARD_NOT_FOUND",
"detail": "No plugin card exists with key \"nope/nope\".",
"instance": "/api/v1/cards/nope/nope"
}
Machine handling: branch on `code` — `INVALID_QUERY`, `EMPTY_QUERY`, `INVALID_CURSOR`, `INVALID_BODY`, `INVALID_IDEMPOTENCY_KEY`, `CARD_NOT_FOUND`, `RATE_LIMIT_EXCEEDED` — show `detail` to humans, and treat `issues` (present on validation 400s) as structured field errors. Success responses are always plain `application/json` with 2xx status. Every operation in the OpenAPI spec documents its 4XX and 429 responses against the Problem schema (RFC 9457), so generated clients type them for you.
Every content endpoint also answers at path + `.md` with `text/markdown; charset=utf-8`: [/api/v1/plugins.md](https://agentkit.md/api/v1/plugins.md), [/api/v1/cards/{key}.md](https://agentkit.md/api/v1/cards/0cv/herdr-mobile-relay.md), [/api/v1/search.md](https://agentkit.md/api/v1/search.md?q=git), [/api/v1/stats.md](https://agentkit.md/api/v1/stats.md), [/api/v1/harnesses.md](https://agentkit.md/api/v1/harnesses.md). Each markdown response opens with a YAML frontmatter block (`title`, `description`, `canonical`, `last-updated`) followed by a readable rendering of the same data — tables for listings, key/value lists for single cards. Fetch those when you want prose, the JSON when you want structure.
The API is versioned by URL path prefix (/api/v1). Breaking changes ship only under a new major prefix (/api/v2) and are never made in place on /api/v1. When an endpoint or parameter is deprecated it keeps working, and its responses carry Deprecation: true plus a Sunset: <HTTP-date> header naming the removal date — at least 90 days after the deprecation is announced in the developer portal (/docs). After the Sunset date the endpoint may answer 410 Gone or be removed. Non-breaking additions — new optional query parameters, new response fields, new endpoints — may land in v1 at any time, so write clients that ignore unknown fields.
Deprecations never break you silently. When an endpoint or parameter is deprecated it keeps working, and its responses carry two headers: `Deprecation: true` (RFC 9745) and `Sunset: <HTTP-date>` (RFC 8591) naming the exact removal date. The Sunset date is always at least 90 days after the deprecation is announced here, so a client that reads headers has a guaranteed migration window; after the Sunset date the endpoint may answer `410 Gone` or be removed. Watch for these headers on any response and surface them in your logs. In practice: today the only stable prefix is `/api/v1`, and nothing on it is deprecated. When a breaking change is unavoidable it lands under `/api/v2`, and this document announces the v1 deprecation window — with explicit `Deprecation` and `Sunset` headers on every affected response — before any v1 endpoint is removed. Additive changes — new optional query parameters, new fields on existing responses, new endpoints — may ship in v1 at any time without notice, so write clients that ignore unknown fields.