Skip to content

feat(api): publish an RFC 9727 API catalog - #90

Merged
nibzard merged 1 commit into
mainfrom
feat/api-catalog
Jul 30, 2026
Merged

feat(api): publish an RFC 9727 API catalog#90
nibzard merged 1 commit into
mainfrom
feat/api-catalog

Conversation

@nibzard

@nibzard nibzard commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Problem

isitagentready.com scores checks.discovery.apiCatalog as fail, and behind the check is a real gap: an agent reading docs.steel.dev has no machine-readable route to the Steel API or its OpenAPI description.

Change

Publish the RFC 9727 catalog at /.well-known/api-catalog as an RFC 9264 linkset:

{
  "linkset": [
    { "anchor": "https://docs.steel.dev/.well-known/api-catalog",
      "item": [{ "href": "https://api.steel.dev" }] },
    { "anchor": "https://api.steel.dev",
      "service-desc": [{ "href": "https://api.steel.dev/sdk-openapi.json", "type": "application/openapi+json" }],
      "service-doc":  [{ "href": "https://docs.steel.dev/overview/intro-to-steel", "type": "text/html" }] }
  ]
}

Plus /openapi.json308https://api.steel.dev/sdk-openapi.json, for anyone who wants the short URL.

Why it links the spec instead of mirroring it

The original plan was to copy the spec into the docs deploy and serve it at docs.steel.dev/openapi.json. Checking the upstream headers changed the answer:

$ curl -sD- https://api.steel.dev/sdk-openapi.json
access-control-allow-origin: *
cache-control: public, max-age=0
etag: W/"4ee17-19fa57f24e8"

CORS is already open, so a browser-based agent can fetch the canonical spec today. A mirror would add nothing fetchable that isn't fetchable now, while creating a second copy that is only as fresh as the last docs deploy. For an API description that is a bad trade: an agent generating a client against a silently stale spec is worse off than one following a redirect. Mirroring becomes the right call if the upstream ever drops CORS, or if docs need a pinned snapshot matching a documented API version.

Two details the endpoint needed

  • Media type. RFC 9727 requires application/linkset+json, and the file has no extension, so static serving would guess. A next.config.mjs header rule sets it, including the profile parameter the RFC recommends.
  • Markdown negotiation. /.well-known/api-catalog has no file extension, so the middleware would have classified it as a docs page and rewritten agent requests for it into a 404. It is now an excluded exact path. This is exactly the class of bug the negotiation work could keep producing, so there is a regression test for it.

Verification

Written failing first. Six unit tests on the linkset shape, one asserting the path is excluded from negotiation, and three end-to-end against a real server: the catalog returns 200 application/linkset+json, it still does so for a markdown-preferring client with a claude-code user agent, and /openapi.json returns 308 to the canonical spec.

Full suite 227 pass / 0 fail, bun run check and bun run typecheck clean.

Agents had no machine-readable way to find the Steel API from the docs.
Add the catalog at /.well-known/api-catalog, listing the API and linking
its OpenAPI description and human documentation.

The catalog points at https://api.steel.dev/sdk-openapi.json rather than a
copy served from docs. The API already sends the spec with open CORS, so a
mirror would add nothing an agent cannot already fetch while introducing a
second copy that goes stale between docs deploys. /openapi.json redirects
to the same canonical URL for anyone who wants the short form.

Two details the endpoint needed: RFC 9727 requires the linkset media type,
which a header rule supplies because the file has no extension, and the
path is excluded from markdown negotiation, which would otherwise treat an
extensionless path as a docs page and rewrite it into a 404.
@vercel

vercel Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs Building Building Preview Jul 30, 2026 11:42am

Request Review

@nibzard
nibzard merged commit b4d2ff0 into main Jul 30, 2026
2 of 3 checks passed
@nibzard
nibzard deleted the feat/api-catalog branch July 30, 2026 11:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant