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

The plugin install lines in mcp.json and /llms.txt work for someone with no clone of the forge #8359

Merged opened this

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

Merged

torque/forge:torque/8324-the-plugin-install-lines-in-mcp-json-and → main 1 commit fork

  • Merged. torque/8324-the-plugin-install-lines-in-mcp-json-and was merged into main as 90573a2.

Review

Sign in to review this change.
3 files changed +30 −9
src/core/mcp_card.ts +16 −4
@@ -21,25 +21,36 @@ export interface HarnessPlugin {
2121 readonly harness: string;
2222 readonly label: string;
2323 readonly path: string;
24 readonly install: string;
24 /** The one line, given the origin it was read from; it works in an empty directory. */
25 readonly install: (origin: string) => string;
2526 /** What to set before it runs. */
2627 readonly setup: string;
2728 }
2829
30 /** The repository that holds the plugins; it must be public on the forge for the lines to work. */
31 const PLUGIN_REPO = "trevato/forge";
32
33 /** The clone URL of that repository on this forge's origin. */
34 export function pluginSource(origin: string): string {
35 return `${origin}/${PLUGIN_REPO}.git`;
36 }
37
2938 /** The one list the card and /llms.txt both read, so they cannot drift. */
3039 export const HARNESS_PLUGINS: readonly HarnessPlugin[] = [
3140 {
3241 harness: "claude-code",
3342 label: "Claude Code",
3443 path: "plugins/claude-code",
35 install: "claude --plugin-dir plugins/claude-code",
44 install: (o) =>
45 `git clone --depth 1 --filter=blob:none --sparse ${pluginSource(o)} forge-plugin && git -C forge-plugin sparse-checkout set plugins/claude-code && claude --plugin-dir forge-plugin/plugins/claude-code`,
3646 setup: "set FORGE_URL, then sign in through /mcp with the forge server; or also set FORGE_TOKEN and use the forge-token server",
3747 },
3848 {
3949 harness: "opencode",
4050 label: "OpenCode",
4151 path: "plugins/opencode/forge.ts",
42 install: "mkdir -p .opencode/plugin && cp plugins/opencode/forge.ts .opencode/plugin/",
52 install: (o) =>
53 `mkdir -p .opencode/plugin && curl -fsSL ${o}/${PLUGIN_REPO}/raw/main/plugins/opencode/forge.ts -o .opencode/plugin/forge.ts`,
4354 setup: "set FORGE_URL and FORGE_TOKEN",
4455 },
4556 ];
@@ -64,7 +75,8 @@ export function mcpCard(origin: string, now: Running = running()) {
6475 plugins: HARNESS_PLUGINS.map((p) => ({
6576 harness: p.harness,
6677 path: p.path,
67 install: p.install,
78 source: pluginSource(origin),
79 install: p.install(origin),
6880 })),
6981 };
7082 }
src/web/standards.ts +2 −2
@@ -38,7 +38,7 @@ import type { Ctx } from "../http/context.ts";
3838 import { notFound } from "../http/respond.ts";
3939 import { SITE_LINE } from "./site_line.ts";
4040 import { mcpToolsSection } from "./llms_tools.ts";
41 import { HARNESS_PLUGINS, mcpCard, mcpPointers } from "../core/mcp_card.ts";
41 import { HARNESS_PLUGINS, mcpCard, mcpPointers, pluginSource } from "../core/mcp_card.ts";
4242 import { running } from "../kernel/version.ts";
4343 import { timePhase, timePhaseAsync } from "../core/route_timing.ts";
4444 import { shippedReleases, type Shipped } from "../release/shipped.ts";
@@ -391,7 +391,7 @@ function renderLlmsTxt(ctx: Ctx, o: string, tell: string | undefined, running: s
391391 `- MCP endpoint: ${mcp.url} (streamable HTTP; OAuth metadata at ${mcp.oauth}). Or send a token as Authorization: Bearer.`,
392392 ...HARNESS_PLUGINS.map(
393393 (p) =>
394 `- ${p.label} plugin (source ${p.path} in this forge's repository; ${p.setup}): \`${p.install}\``,
394 `- ${p.label} plugin (source ${pluginSource(o)}, path ${p.path}; ${p.setup}; needs that repository to be public): \`${p.install(o)}\``,
395395 ),
396396 "",
397397 "## What you can do on any page",
test/mcp-card-plugins.test.ts +12 −3
@@ -25,12 +25,21 @@ test("card and llms.txt name both plugins, their install lines, and paths that e
2525 const card = (await fetch(`${servers.url}/.well-known/mcp.json`).then((r) => r.json())) as any;
2626 const llms = await fetch(`${servers.url}/llms.txt`).then((r) => r.text());
2727 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");
3028 for (const p of card.plugins) {
31 expect(existsSync(join(import.meta.dir, "..", p.path))).toBe(true);
29 expect(p.source).toBe(`${servers.url}/trevato/forge.git`);
30 expect(llms).toContain(p.source);
3231 expect(llms).toContain(p.install);
3332 expect(llms).toContain(p.path);
33 // Nothing outside what the line fetched: no bare plugins/ path, no clone-relative cp.
34 const bare = p.install.replace(/forge-plugin\/plugins\//g, "").replace(/sparse-checkout set plugins\//, "").replace(/\/raw\/main\/plugins\//g, "");
35 expect(bare).not.toMatch(/(^|\s)plugins\//);
36 expect(p.install).not.toMatch(/\bcp /);
3437 }
38 const claude = card.plugins.find((p: any) => p.harness === "claude-code");
39 expect(claude.install).toContain("--sparse");
40 expect(claude.install).toContain("--plugin-dir forge-plugin/plugins/claude-code");
41 const oc = card.plugins.find((p: any) => p.harness === "opencode");
42 expect(oc.install).toContain(`curl -fsSL ${servers.url}/trevato/forge/raw/main/plugins/opencode/forge.ts`);
43 for (const p of card.plugins) expect(existsSync(join(import.meta.dir, "..", p.path))).toBe(true);
3544 expect(card.name).toBe("forge");
3645 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs