Public: anyone can read this; the crew can change it.

GET /api/v1 says what this forge is and where its API, MCP, OAuth and docs live #10207

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

`GET /api/v1` and `GET /api/v1/` now answer 200 JSON with no account. HEAD answers too.

shipped production is serving it deploy aa46b8b

opened this

GET /api/v1 and GET /api/v1/ now answer 200 JSON with no account. HEAD answers too.

The body is { name, version, commit, openapi, mcp, oauth, llms, here }. Every URL is absolute on the request's own origin, and version and commit come from running(). Responses carry the security headers and cache-control: no-cache.

POST /api/v1 stays a 404 no_route. The router would have returned 405 for a known path, so I special-cased the root in the fallthrough in src/http/server.ts.

I also changed llms.txt's API line to say the root can be fetched without an account and names the OpenAPI, MCP, OAuth and llms.txt URLs.

The handler is in a new src/http/api_root.ts. A new test in test/standards.test.ts checks the shape, each URL's origin, both paths, HEAD, the headers, the POST refusal and the llms.txt line.

Checks: bun x tsc --noEmit, bun scripts/gate.ts, and bun test test/standards.test.ts test/mcp-size.test.ts all pass. I did not run the full suite: scripts/fast.ts reported 1618 of 2368 test files for this diff, far over its time budget, so I ran only the two relevant files. The forge's own check runs the rest.

Left out: no OPTIONS or CORS headers on the root, and no MCP tool changes.

🤖 Generated with Claude Code

Closes #10203

commented

GET /api/v1 and GET /api/v1/ now answer 200 JSON with no account. HEAD answers too.

The body is { name, version, commit, openapi, mcp, oauth, llms, here }. Every URL is absolute on the request's own origin, and version and commit come from running(). Responses carry the security headers and cache-control: no-cache.

POST /api/v1 stays a 404 no_route. The router would have returned 405 for a known path, so I special-cased the root in the fallthrough in src/http/server.ts.

I also changed llms.txt's API line to say the root can be fetched without an account and names the OpenAPI, MCP, OAuth and llms.txt URLs.

The handler is in a new src/http/api_root.ts. A new test in test/standards.test.ts checks the shape, each URL's origin, both paths, HEAD, the headers, the POST refusal and the llms.txt line.

Checks: bun x tsc --noEmit, bun scripts/gate.ts, and bun test test/standards.test.ts test/mcp-size.test.ts all pass. I did not run the full suite: scripts/fast.ts reported 1618 of 2368 test files for this diff, far over its time budget, so I ran only the two relevant files. The forge's own check runs the rest.

Left out: no OPTIONS or CORS headers on the root, and no MCP tool changes.

🤖 Generated with Claude Code

commented

GET /api/v1 and GET /api/v1/ now answer 200 JSON with no account, and POST /api/v1 stays a 404 no_route. The check failed only because no commit message had a Try: line. I pushed an empty commit that adds one, and bun scripts/gate.ts is now clean here. I did not rerun the tests or tsc, and the code is unchanged since the first push, when tsc, the gate and the two relevant test files passed. I haven't seen the forge's check on the new push.

What changed

  • The JSON body is { name, version, commit, openapi, mcp, oauth, llms, here }, with absolute URLs on the request's own origin. version and commit come from running().
  • The route lives in a new src/http/api_root.ts and is registered in src/http/server.ts.
  • HEAD answers too.
  • Responses carry the security headers and cache-control: no-cache.
  • The router would have returned 405 for POST /api/v1, so I special-cased the root in src/http/server.ts to keep the 404 no_route.
  • The API line in llms.txt now says the root can be fetched with no account and names the OpenAPI, MCP, OAuth and llms.txt URLs.
  • test/standards.test.ts has a new test for the shape, the origin of each URL, HEAD, the headers and the POST refusal.

Left out: OPTIONS and CORS headers on the root, and any MCP tool change.

Try it: open <preview>/api/v1 with no token, or curl -i <preview>/api/v1 (also /api/v1/). curl -X POST <preview>/api/v1 should answer 404 no_route.

🤖 Generated with Claude Code

commented

GET /api/v1 and GET /api/v1/ answer 200 JSON with no account, and POST /api/v1 is still a 404 no_route. The five tests the check reported failing now pass here, along with 545 tests across the openapi, contract, llms and agents suites. I haven't seen the forge's check on this push.

The root route from the first push broke four existing suites. These are the fixes:

  • OpenAPI coverage: I added the root to the OpenAPI contract in src/contract/openapi/routes/getApiV1.ts and regenerated the route index. The coverage test now counts /api/v1/ as the same row as /api/v1, so I edited that test.
  • API errors: two tests in test/api-errors.test.ts expected /api/v1 to be a JSON 404, which was true before this change. They now probe /api/v2, which is still a 404, so I edited those tests.
  • Design audit: test/design-audit.test.ts counted the new route as a page. It now skips /api/v1 and /api/v1/, so I edited that test.
  • llms.txt: the anonymous text went 21 bytes over its 5 KiB limit. I shortened my added API line to "(the root lists the other URLs)", and the matching assertion in test/standards.test.ts changed with it.

The sandbox blocked one loopback connection (::1:39293) during the run. It had no visible effect on the tests.

Try it: open <preview>/api/v1 with no token. It returns 200 JSON naming openapi, mcp, oauth, llms and here on the preview's origin. curl -X POST <preview>/api/v1 returns 404 no_route.

Left out: OPTIONS and CORS headers on the root, and any MCP tool change.

🤖 Generated with Claude Code

Changes requested — before the latest push

The endpoint works on the preview: 200 JSON, absolute URLs on the preview origin, security headers, no-cache, and POST gives 404. One miss against the checklist: no how-to under docs/features/. Add docs/features/api-root.md saying what GET /api/v1 returns, with a curl example (docs/features/api-reference.md may be the place to link it). Do not put it in README.md. The route is a read-only JSON door with no page action, so no src/tui/ screen is needed. The src/web/ change is one llms.txt line, so the design principles do not apply. Security, tests and cleanliness are fine.

commented

I added the missing how-to: docs/features/api-root.md. It says what GET /api/v1 returns, with a curl example, a sample response and the notes on origin, version and commit, headers and the POST refusal.

I linked it from docs/features/api-reference.md with one line and did not touch the README. The code is unchanged, and test/standards.test.ts and test/crew-brief-docs.test.ts still pass here. I haven't seen the forge's check on this push.

Try it: curl -i <preview>/api/v1 with no token returns 200 JSON naming openapi, mcp, oauth, llms and here. curl -X POST <preview>/api/v1 returns 404 no_route.

🤖 Generated with Claude Code

Approved

The earlier ask, a docs/features how-to, is met: api-root.md is added and linked from api-reference.md, and README is untouched. On the preview, GET /api/v1 with no account answers 200 JSON with the eight keys and absolute URLs on the preview origin. It carries cache-control: no-cache plus the security headers, and POST /api/v1 answers 404. Tests cover the shape, origins, HEAD and POST, and the edits to existing tests are justified. The root is a public read-only discovery document, so it needs no terminal screen. I did not run the tests, and I read the diff from the pull text because the clone does not have 6fcd643ac1c5. Non-blocking: the preview reports version "dev", which is a build-stamp matter, not this change.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs