p PatchFirst

PatchFirst / Developers

API and MCP

Read findings for your paid servers, build a patch queue, and request a recalculation from your own tools.

One token per integration

Create a personal API token in the API tokens section of your dashboard. Give it a name of up to 64 characters, choose read or write (read and write), and set its lifetime from 1 to 365 days. Enter your current password again to confirm creation. All API tokens are revoked after a password change or recovery. Signing out preserves CI and MCP access. To revoke all tokens, use the separate “Revoke all tokens” button in the dashboard. The dashboard shows the revocation reason. You can have up to 50 active tokens. Copy the secret when it appears: it is shown once. The service stores only a verification hash. You can list and revoke tokens, and inspect their usage in the dashboard.

Use HTTPS and send the token only in Authorization: Bearer …. Tokens in URLs are rejected. The examples assume PATCHFIRST_API_TOKEN is set in your environment; keep the secret out of source control, screenshots and logs. The existing fleet read/write keys and agent tokens are separate credentials.

curl --fail-with-body "https://patchfirst.dev/v1/me/fleets" \
  -H "Authorization: Bearer $PATCHFIRST_API_TOKEN"

The token covers only the owner’s fleets. A fleet owned by someone else returns 404, just like a missing fleet. A read token cannot start a recalculation. Browser access from other origins is disabled (closed CORS).

API and MCP require paid monitoring. Without eligible payment, requests return 402. After monitoring expires, archive access includes only saved report snapshots from a previously paid period under the billing rules, without new data. CVE details require active payment: otherwise the API returns 402 and cve_details returns a payment error in the MCP result; current KEV, EPSS, exploits, sightings and mentions are unavailable in archive mode. New calculations require current monitoring eligibility, including any billing grace period; free slots alone do not grant API access.

API v1 endpoints

Base URL: https://patchfirst.dev. Replace {fleet} and {server} with UUIDs returned by the list endpoints. All endpoints below use a personal Bearer token.

Personal API
Method and pathResult
GET /v1/me/fleetsOwned fleets: {"fleets":[{"id":"…","created_at":1792713600}]}.
GET /v1/me/fleets/{fleet}/serversReadable paid servers: {"servers":[{"id":"…","name":"web-1","name_kind":"alias","updated_at":1792713600}]}.
GET /v1/me/fleets/{fleet}/reportFleet rating, P0–P3 counts and server reports in assets.
GET /v1/me/fleets/{fleet}/servers/{server}/reportServer rating, findings, CVEs, fixed versions and recommendations.
GET /v1/me/fleets/{fleet}/servers/{server}/history{"history":[…]}: persisted rating snapshots.
GET /v1/me/cves/{cve}CVE record, KEV, EPSS, exploits and source mentions. Example ID: CVE-2026-12345.
POST /v1/me/fleets/{fleet}/refresh202: recalculation accepted. Requires write and eligible monitoring. Read the report again for results.
curl --fail-with-body "https://patchfirst.dev/v1/me/fleets/11111111-1111-4111-8111-111111111111/report" \
  -H "Authorization: Bearer $PATCHFIRST_API_TOKEN"

curl --fail-with-body -X POST "https://patchfirst.dev/v1/me/fleets/11111111-1111-4111-8111-111111111111/refresh" \
  -H "Authorization: Bearer $PATCHFIRST_API_TOKEN"

Dashboard token management uses your signed-in browser session, separate from Bearer access: GET/POST /v1/account/api-tokens, DELETE /v1/account/api-tokens/{token_id}, and GET /v1/account/api-tokens/{token_id}/usage. Creation accepts {"name":"acme integration","scope":"read","expires_in_days":30,"password":"CURRENT_PASSWORD"} and returns 201 with token metadata and the one-time token secret. Session writes require same-origin request checks.

Response contract and names

The OpenAPI document defines the v1 JSON schemas. Existing fields retain their meaning and types; clients should accept additional fields. Success responses are JSON objects. Lists use fleets, servers or history arrays. Token metadata, fleet creation, server updates and history recorded_at use Unix seconds (UTC); report computed_at uses a UTC date-time string or null.

A server report contains id, name, name_kind, score, grade, counts and findings. Each finding includes priority (P0–P3), component, installed version, fix, recommended action and top CVE entries. fix may be empty or null when no fixed version is available; check unfixed, pro_only and action.

score and grade are the main rating for checked components. global_score and global_grade are the global rating, accounting for unknown risk in the unchecked share. The coverage object contains integer checked (assessed) and total (all discovered) counts. These fields are returned in server and fleet reports, server lists and persisted history snapshots. Fleet report coverage sums server counts, and the global rating uses the same weights as the main rating. A grade of ? means that the corresponding rating is unavailable; display ? even if a numeric value is retained for compatibility. Old snapshots recorded before two ratings may lack global rating data; its numeric value is null, so clients should allow null.

The complete, rating_complete, reasons and blind fields remain for compatibility and describe coverage. They do not replace the main rating or set its verdict. Global rating and coverage methodology →

Open aliases such as web-1 are returned with name_kind: "alias". When names are encrypted or unset, name_kind: "opaque" identifies an opaque label. The remote service does not know encrypted server names and cannot decrypt them. Never use a domain, IP, company or customer name as an open alias.

Errors, limits and usage

Errors have the stable shape {"error":"Readable explanation","code":"machine_code"}. Use the HTTP status and code for program logic; the explanation may change.

HTTP status codes
StatusMeaning
400Invalid input, unsupported parameters or token in URL.
401Missing, invalid, expired or revoked token.
402Paid monitoring or a readable paid snapshot is required.
403Insufficient scope or invalid origin; use a write token for refresh.
404Missing resource or a resource belonging to another owner.
405Unsupported method; MCP accepts POST.
409Limit of 50 active personal API tokens reached.
429Too many requests. Wait for Retry-After seconds before retrying.

The limit is 120 requests per minute per account, shared across all its tokens, API and MCP. Fleet refresh has a separate limit of 10 requests per minute per account, shared by API and MCP. The journal shows up to 100 latest stored requests. Matching requests (token, method, path, status) are sampled at most once per minute; 429 responses are excluded. Records are retained for up to 30 days and up to 1000 per token; an hourly cleanup removes excess records. Rating history returns up to 1000 latest entries. Usage records contain time, method, path and HTTP status; request and response bodies and the token secret are excluded.

Connect remote MCP

Endpoint: https://patchfirst.dev/v1/mcp. Transport: Streamable HTTP, protocol 2025-06-18, with the same Bearer token, ownership checks and payment rules. Start with a read token. Remote tools are list_servers, server_report, fleet_report, cve_details and patch_first (what to update first). Use refresh_fleet to start recalculation: it requires write, a fleet UUID and active payment, and shares the separate account refresh limit.

server_report takes fleet and server UUIDs; fleet_report takes fleet; cve_details takes cve. list_servers returns the fleet UUID in fleet. For list_servers and patch_first, fleet is optional and defaults to your fleet. Discover the input schemas with tools/list. Protocol failures use JSON-RPC error responses; tool execution failures, including payment and refresh limits, return HTTP 200 with isError: true.

Only static Bearer authentication is supported: create a token manually in the dashboard and configure it in your client. There is no OAuth authorization server and no automatic OAuth token acquisition. This is a limited static token mode; the full OAuth authorization flow in the MCP specification is not implemented. The public /.well-known/oauth-protected-resource endpoint publishes the MCP URL, documentation and dashboard token issuance link (token_issuance_uri). On 401, the WWW-Authenticate: Bearer resource_metadata="…" header points to that metadata. Use a client that accepts a manually configured Bearer token.

Claude Code

Set the token in your environment, then add the HTTP server:

claude mcp add --transport http patchfirst "https://patchfirst.dev/v1/mcp" \
  --header "Authorization: Bearer $PATCHFIRST_API_TOKEN"

Codex

Add this to ~/.codex/config.toml, and launch Codex with PATCHFIRST_API_TOKEN set:

[mcp_servers.patchfirst]
url = "https://patchfirst.dev/v1/mcp"
bearer_token_env_var = "PATCHFIRST_API_TOKEN"

Official OpenAI documentation for Codex MCP configuration.

Cursor

Add the server in MCP settings or your local ~/.cursor/mcp.json. Replace the placeholder with your token and protect this file as a secret:

{
  "mcpServers": {
    "patchfirst": {
      "url": "https://patchfirst.dev/v1/mcp",
      "headers": {"Authorization": "Bearer YOUR_API_TOKEN"}
    }
  }
}

Protocol example

The server is stateless. Send Accept: application/json, text/event-stream and Content-Type: application/json. Initialize first, send notifications/initialized, then use tools/list or tools/call with MCP-Protocol-Version: 2025-06-18. Notifications return 202 without a body; no server session identifier is required. GET /v1/mcp returns 405 with the Allow: POST header.

curl --fail-with-body "https://patchfirst.dev/v1/mcp" \
  -H "Authorization: Bearer $PATCHFIRST_API_TOKEN" \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  --data '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"acme","version":"1.0"}}}'

Recalculate through MCP

A tools/call example for a write token; replace the fleet UUID:

{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"refresh_fleet","arguments":{"fleet":"11111111-1111-4111-8111-111111111111"}}}

Local MCP with name decryption

The existing core/mcp_server.py remains available over stdio. Run python3 /path/to/patchfirst/core/mcp_server.py locally with your configured owner CLI and local fleet files (PSR_FLEETS, PSR_INVENTORY). It can restore names using your local encrypted dictionary. Remote MCP keeps encrypted names opaque.