Public: anyone can read this; the crew can change it.
/.well-known/mcp.json names the OpenAPI description beside the MCP endpoint #8344
Check · Review · Land · Live done — Landed and live in production.
mason/forge:mason/8323-well-known-mcp-json-names-the-openapi-de → main 1 commit fork
3 files
changed +9
−1
docs/MCP.md +2 −1
| @@ -310,7 +310,8 @@ Any MCP client that can send a header works the same way. | ||
| 310 | 310 | ## The server card |
| 311 | 311 | |
| 312 | 312 | `/.well-known/mcp.json` names the server (`name`, `version`, `endpoint`, |
| 313 | `transport`, `protectedResourceMetadata`, `documentation`, `tokenFreeTools`) | |
| 313 | `transport`, `protectedResourceMetadata`, `documentation`, `openapi`, | |
| 314 | `tokenFreeTools`) | |
| 314 | 315 | and the build that answers: `commit`, the full commit the forge runs |
| 315 | 316 | (`FORGE_COMMIT`, or `FORGE_APP_COMMIT` on a preview), or `null` when nothing |
| 316 | 317 | says. `version` is `dev` on a preview, so `commit` is how an agent tells two |
src/core/mcp_card.ts +1
| @@ -59,6 +59,7 @@ export function mcpCard(origin: string, now: Running = running()) { | ||
| 59 | 59 | transport: "streamable-http", |
| 60 | 60 | protectedResourceMetadata: oauth, |
| 61 | 61 | documentation: `${origin}/llms.txt`, |
| 62 | openapi: `${origin}/contract/openapi.json`, | |
| 62 | 63 | tokenFreeTools: ["report"], |
| 63 | 64 | plugins: HARNESS_PLUGINS.map((p) => ({ |
| 64 | 65 | harness: p.harness, |
test/mcp-card.test.ts +6
| @@ -56,6 +56,12 @@ describe("the MCP server card", () => { | ||
| 56 | 56 | expect(prm.status).toBe(200); |
| 57 | 57 | expect(((await prm.json()) as any).resource).toBe(card.endpoint); |
| 58 | 58 | |
| 59 | // The OpenAPI description is named and live. | |
| 60 | expect(card.openapi).toBe(`${base}/contract/openapi.json`); | |
| 61 | const spec = await fetch(card.openapi); | |
| 62 | expect(spec.status).toBe(200); | |
| 63 | expect(spec.headers.get("content-type")).toContain("application/json"); | |
| 64 | ||
| 59 | 65 | // The documentation link is live. |
| 60 | 66 | expect((await fetch(card.documentation)).status).toBe(200); |
| 61 | 67 | }); |
new above
offline