Public endpoints

Public read endpoints for tools that want to read observatory state. Responses are JSON or text/event-stream, with text/csv from the batch endpoint via format=csv. No authentication required.

Endpoints

GET /api/events Server-sent events: live release + dependency activity.

response

{
  "kind": "release",
  "source": "npm",
  "server_id": "npm:fastmcp",
  "version": "1.2.3",
  "at": "2026-05-24T18:00:00Z"
}

cache

headerno-store
ttlno-store
GET /api/stats Aggregate counts: lifetime, 24h, active maintainers, last event.

response

{
  "totalTracked": 18432,
  "byKind": [
    {
      "kind": "npm",
      "count": 12044
    },
    {
      "kind": "pypi",
      "count": 4810
    },
    {
      "kind": "github-only",
      "count": 1578
    }
  ],
  "releasesToday": 47,
  "releasesPrior24h": 41,
  "activeMaintainers24h": 612,
  "topMaintainers": [
    {
      "maintainer": "alice",
      "count": 22
    },
    {
      "maintainer": "bob",
      "count": 18
    }
  ],
  "lastEventAt": 1748109501000,
  "hourlyReleases24h": [
    0,
    0,
    1,
    3,
    2,
    4,
    1,
    0,
    2,
    5,
    3,
    1,
    0,
    2,
    4,
    1,
    3,
    2,
    1,
    0,
    4,
    2,
    3,
    1
  ]
}

cache

headerpublic, max-age=30
ttl30s
GET /api/servers/{id} One server detail: identity, sources, dependencies, recent releases.

response

{
  "canonicalId": "npm:fastmcp",
  "server": {
    "id": "npm:fastmcp",
    "name": "fastmcp",
    "kind": "npm",
    "description": "A fast MCP server",
    "repoUrl": "https://github.com/example/fastmcp",
    "homepage": null,
    "license": "MIT",
    "language": "TypeScript",
    "maintainer": "alice",
    "firstSeen": "2026-01-15T10:00:00Z",
    "lastSeen": "2026-05-24T17:58:21Z",
    "currentVersion": "1.2.3",
    "tags": [
      "mcp",
      "server"
    ]
  },
  "releases": [
    {
      "version": "1.2.3",
      "publishedAt": "2026-05-24T17:58:21Z",
      "source": "npm"
    }
  ],
  "deps": {
    "out": [
      {
        "fromServerId": "npm:fastmcp",
        "toServerId": "npm:zod",
        "type": "runtime"
      }
    ],
    "in": [
      {
        "fromServerId": "npm:other-mcp",
        "toServerId": "npm:fastmcp",
        "type": "runtime"
      }
    ]
  },
  "related": [
    {
      "id": "npm:slowmcp",
      "reason": "maintainer"
    }
  ]
}

cache

headerpublic, max-age=60
ttl60s
GET /api/servers?ids={a,b,…} Batch lookup (≤100 ids): risk grade, CVE count, 30d cadence per server. format=csv for CSV.

response

{
  "servers": [
    {
      "id": "npm:fastmcp",
      "name": "fastmcp",
      "kind": "npm",
      "maintainer": "alice",
      "description": "A fast MCP server",
      "license": "MIT",
      "currentVersion": "1.2.3",
      "lastSeen": "2026-05-24T17:58:21Z",
      "riskGrade": "B",
      "riskScore": 23,
      "cveCount": 0,
      "releases30d": 4
    }
  ],
  "unknownIds": [
    "npm:typo-mcp"
  ],
  "ignoredIds": [],
  "ignoredCount": 0
}

cache

headerpublic, max-age=60
ttl60s
GET /api/search?q={query} Typeahead search across servers + maintainers (≤8 hits).

response

{
  "hits": [
    {
      "id": "npm:fastmcp",
      "name": "fastmcp",
      "maintainer": "alice",
      "source": "npm",
      "type": "server"
    },
    {
      "id": "alice",
      "name": "alice",
      "maintainer": null,
      "source": "npm",
      "type": "maintainer",
      "count": 7
    }
  ]
}

cache

headerno-store
ttlno-store
GET /api/healthz Per-source poller health; 200 when all critical sources are fresh, 503 otherwise.

response

{
  "status": "ok",
  "failingCritical": [],
  "sources": [
    {
      "source": "npm",
      "status": "ok",
      "critical": true,
      "healthy": true,
      "lastFetchedAt": "2026-05-24T17:55:00Z",
      "nextDueAt": "2026-05-24T18:05:00Z",
      "overdueMs": 0,
      "lastError": null
    }
  ]
}

cache

headerno-store
ttlno-store
GET /api/livez Process liveness only — can this web process serve a request? Says nothing about data freshness; use /api/healthz for ecosystem/source status.

response

{
  "ok": true,
  "db": "open"
}

cache

headerno-store
ttlno-store

Live cadence

The /api/events stream emits as the pollers ingest upstream activity; their poll intervals and last-fetched times are published on the feeds page, and the same events render human-readable on activity.

Querying server details

For the /api/servers/{id} endpoint, URL-encoding is not required! You can pass the full unencoded string (e.g. npm:@org/svr) because it uses a catch-all route. Bare names without a source prefix will automatically fall back through npm:, pypi:, then github: to find a match. To find an id to query, browse servers.

Every server also has a stable 12-character hex short id (shown on its detail page) that needs no URL-encoding — handy when several ids share one URL. Both /api/servers/{id} and the batch endpoint accept short ids interchangeably with canonical ids, and the server detail response echoes the canonical id back as canonicalId so a caller can normalise whatever handle it looked up. Short ids minted before the id was widened are 8 characters; those still resolve, as a prefix of the current 12-character value — deterministic today, but a prefix match can in principle be ambiguous, so new links should use the full 12-character id. Retired ids forward to their survivor, so old links keep resolving.

Batch lookup

/api/servers?ids={a,b,…} takes a comma-separated ids list — canonical ids, short ids, or bare names, mixed freely — capped at 100 per call; extras are not processed and are reported back via ignoredCount (the true total) plus ignoredIds, a sample of at most 20 of those extras — use ignoredCount, not the length of ignoredIds, to know how many were dropped. Ids are de-duplicated after resolution; any that resolve to nothing come back in unknownIds rather than failing the batch. An empty ids param returns 400. Add &format=csv for a text/csv body (one header row, then one row per server) suited to spreadsheet or SBOM-tool import; otherwise the response is JSON. The CSV path can't express these arrays in the body, so the same sample/count pair rides along as headers instead — X-Ignored-Ids/X-Ignored-Count and X-Unknown-Ids/X-Unknown-Count.

Data licence

The observatory corpus — the observations, canonical identities, and derived signals published through these endpoints — is licensed CC BY 4.0. Use it for anything, including commercially; attribute MCP Observatory and link back to mcpobservatory.com.

This covers our data about the servers, not the servers themselves. Each tracked project keeps whatever licence its authors chose — that is reported per-server in the license field and on licences, and nothing here changes it.

The corpus is also described as a schema.org Dataset in this page’s structured data, carrying the same licence.