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

/.well-known/mcp.json and /llms.txt name the Claude Code and OpenCode plugins, each with its one install line #8296

Merged opened this

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

Merged

torque/forge:torque/8273-well-known-mcp-json-and-llms-txt-name-th → main 1 commit fork

  • Merged. torque/8273-well-known-mcp-json-and-llms-txt-name-th was merged into main as 76c3a68.

Review

Sign in to review this change.
3 files changed +70 −3
src/core/mcp_card.ts +29
@@ -16,6 +16,30 @@ export function mcpPointers(origin: string): { url: string; oauth: string } {
1616 };
1717 }
1818
19 /** A thin plugin the forge ships for a harness: where its source is and its one install line. */
20 export interface HarnessPlugin {
21 readonly harness: string;
22 readonly label: string;
23 readonly path: string;
24 readonly install: string;
25 }
26
27 /** The one list the card and /llms.txt both read, so they cannot drift. */
28 export const HARNESS_PLUGINS: readonly HarnessPlugin[] = [
29 {
30 harness: "claude-code",
31 label: "Claude Code",
32 path: "plugins/claude-code",
33 install: "claude --plugin-dir plugins/claude-code",
34 },
35 {
36 harness: "opencode",
37 label: "OpenCode",
38 path: "plugins/opencode/forge.ts",
39 install: "mkdir -p .opencode/plugin && cp plugins/opencode/forge.ts .opencode/plugin/",
40 },
41 ];
42
1943 /**
2044 * The server card. The format is still a draft, so only what we can keep
2145 * true is named: who answers, where, over what transport, how to sign in,
@@ -32,5 +56,10 @@ export function mcpCard(origin: string, now: Running = running()) {
3256 protectedResourceMetadata: oauth,
3357 documentation: `${origin}/llms.txt`,
3458 tokenFreeTools: ["report"],
59 plugins: HARNESS_PLUGINS.map((p) => ({
60 harness: p.harness,
61 path: p.path,
62 install: p.install,
63 })),
3564 };
3665 }
src/web/standards.ts +5 −3
@@ -36,7 +36,7 @@ import type { Ctx } from "../http/context.ts";
3636 import { notFound } from "../http/respond.ts";
3737 import { SITE_LINE } from "./site_line.ts";
3838 import { mcpToolsSection } from "./llms_tools.ts";
39 import { mcpCard, mcpPointers } from "../core/mcp_card.ts";
39 import { HARNESS_PLUGINS, mcpCard, mcpPointers } from "../core/mcp_card.ts";
4040 import { running } from "../kernel/version.ts";
4141 import { timePhase } from "../core/route_timing.ts";
4242 import { shippedReleases, type Shipped } from "../release/shipped.ts";
@@ -359,8 +359,10 @@ export function llmsTxt(ctx: Ctx): Response {
359359 "## Use it from your harness",
360360 "",
361361 `- MCP endpoint: ${mcp.url} (streamable HTTP; OAuth metadata at ${mcp.oauth}). Or send a token as Authorization: Bearer.`,
362 "- Claude Code plugin: `claude --plugin-dir plugins/claude-code` in a clone of this forge's repository (set FORGE_URL and FORGE_TOKEN).",
363 "- OpenCode plugin: copy plugins/opencode/forge.ts into .opencode/plugin/ (set FORGE_URL and FORGE_TOKEN).",
362 ...HARNESS_PLUGINS.map(
363 (p) =>
364 `- ${p.label} plugin (source ${p.path} in this forge's repository; set FORGE_URL and FORGE_TOKEN): \`${p.install}\``,
365 ),
364366 "",
365367 "## What you can do on any page",
366368 "",
test/mcp-card-plugins.test.ts added +36
@@ -0,0 +1,36 @@
1 /** The card and /llms.txt name each shipped plugin with its install line, from one constant. */
2 import { afterAll, beforeAll, expect, test } from "bun:test";
3 import { existsSync } from "node:fs";
4 import { mkdtemp, rm } from "node:fs/promises";
5 import { tmpdir } from "node:os";
6 import { join } from "node:path";
7 import { createApp, type App } from "../src/app.ts";
8 import { serve, type Servers } from "../src/http/server.ts";
9
10 let dataDir: string;
11 let app: App;
12 let servers: Servers;
13
14 beforeAll(async () => {
15 dataDir = await mkdtemp(join(tmpdir(), "forge-card-plugins-"));
16 app = createApp({ dataDir, port: 0 });
17 servers = await serve(app);
18 });
19 afterAll(async () => {
20 await servers.stop();
21 await rm(dataDir, { recursive: true, force: true });
22 });
23
24 test("card and llms.txt name both plugins, their install lines, and paths that exist", async () => {
25 const card = (await fetch(`${servers.url}/.well-known/mcp.json`).then((r) => r.json())) as any;
26 const llms = await fetch(`${servers.url}/llms.txt`).then((r) => r.text());
27 expect(card.plugins.map((p: any) => p.harness).sort()).toEqual(["claude-code", "opencode"]);
28 const claude = card.plugins.find((p: any) => p.harness === "claude-code");
29 expect(claude.install).toBe("claude --plugin-dir plugins/claude-code");
30 for (const p of card.plugins) {
31 expect(existsSync(join(import.meta.dir, "..", p.path))).toBe(true);
32 expect(llms).toContain(p.install);
33 expect(llms).toContain(p.path);
34 }
35 expect(card.name).toBe("forge");
36 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs