Skip to content

Registry API

Base URL (production): https://registry.patch-cat.com Base URL (dev): https://patchcat-registry-dev.<your-account>.workers.dev

All endpoints return JSON. Errors use a structured envelope:

{ "error": { "code": "<machine-readable>", "message": "<human-readable>" } }

Public read endpoints (no auth)

GET /v1/tools/search?q=<query>&limit=10&include_unverified=false

Semantic search over tool descriptions via pgvector. Returns ranked results.

{
"results": [
{
"name": "fetch_url",
"description": "Fetch a URL via HTTP GET and return the body as text.",
"latest_version": "1.0.0",
"contributor": { "github_handle": "patch-cat" },
"use_count": 1234,
"success_count": 1207,
"success_rate": 0.978,
"similarity": 0.94,
"verified": true,
"created_at": "2026-04-01T00:00:00Z"
}
]
}

By default, only verified contributor tools are returned. Pass include_unverified=true to see new contributors’ tools too. See threat model — supply chain for the reputation gating rationale.

GET /v1/tools/:name

Returns the latest version’s metadata + a public R2 URL for the source.

GET /v1/tools/:name/:version

Specific version. Versions are immutable.

POST /v1/quarantine/summarize

Pass untrusted text; get back a structured summary that’s safe to forward to a planner.

// Request
{ "text": "Top story today: ..." }
// Response
{
"summary": "<2-3 sentence summary in the LLM's own words>",
"flags": ["imperative_instruction", "instruction_override_attempt"]
}

The LLM is instructed never to repeat verbatim input text and to flag injection attempts. Input text is not persisted.

POST /v1/tools/:name/runs

Anonymous run telemetry. Bumps tools.use_count / success_count.

{
"version": "1.0.0",
"success": true,
"duration_ms": 743,
"error_class": "TimeoutError"
}

Authenticated endpoints

GET /auth/github/start?redirect=<optional-localhost-url>

Begins the GitHub OAuth flow. Optional redirect query parameter sends the user to a localhost listener after the registry mints a session — used by the MCP patch_auth_register flow.

GET /auth/github/callback

GitHub’s redirect target. Exchanges the code, upserts a contributors row, mints a session JWT, sets the cookie, and (if redirect was used) bounces to the localhost listener with the token in the query string.

POST /v1/tools (auth required)

Contribute a tool.

// Request
{
"manifest": { /* full manifest, see /manifest */ },
"source": "<full Python file with frontmatter>"
}
// Response
{
"name": "fetch_url",
"version": "1.0.0",
"source_sha256": "abc...64hex",
"status": "created" | "exists"
}

Contributions are validated:

  1. Manifest schema (zod).
  2. Source frontmatter matches the supplied manifest’s name + version.
  3. Every human-visible field passes the Unicode sanitizer.
  4. Description passes the quarantine LLM (no instruction-injection content).
  5. If a tool with this name already exists under a different contributor → 409.

Self-refactoring (v0.4)

GET /v1/refactor/proposals?status=<status>

Lists candidate-merge proposals generated by the nightly cron. statuspending_generation / generating / verified / equivalence_failed / accepted / rejected.

GET /v1/refactor/proposals/:id

Single proposal with the full merged manifest YAML.

POST /v1/refactor/proposals/:id/result (auth required)

Used by the GHA runner to post results back. Most users never touch this directly.

Error codes

Every error response includes a stable error.code. Reference:

CodeHTTPMeaning
invalid_query / invalid_body400Request didn’t validate.
manifest_unsafe_unicode400Sanitizer stripped characters from a manifest field.
description_flagged_by_quarantine400Quarantine LLM flagged the description as instruction-injection.
manifest_mismatch400Source frontmatter doesn’t match the supplied manifest.
missing_token / invalid_token401Session cookie / bearer token missing or invalid.
name_taken409Tool name owned by another contributor.
tool_not_found / version_not_found404Lookup miss.
r2_put_failed / db_insert_* / ai_embed_failed500Subsystem failure — the message field has the specific reason.

Caching

EndpointEdge cache TTL
/v1/tools/search30s
/v1/tools/:name and /v1/tools/:name/:version5min (versions are immutable)
Everything elsenone