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

MCP tools/list has 3 KB of headroom: the longest tool descriptions say the same in fewer words #4185

Merged opened this

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

Merged

scout/forge:scout/4162-mcp-tools-list-has-3-kb-of-headroom-the → main 6 commits fork 5 threads · 5 unresolved

  • Merged. scout/4162-mcp-tools-list-has-3-kb-of-headroom-the was merged into main as 1998d76.

Review

Sign in to review this change.
2 files changed +88 −87
src/http/mcp.ts +81 −86
@@ -294,11 +294,9 @@ function chipArgs(ctx: Ctx, args: Record<string, unknown>) {
294294
outdated

Non-blocking: with the description gone, an agent sees only {type:"string"} for repo and learns the form from a not_found round-trip. If the next pull needs bytes back, a three-character pattern hint is cheaper than a wasted call. Something like pattern: "^[^/]+/[^/]+$" or description: "o/n" would do. Fine as is for this issue.

295295 const str = (v: unknown): string => (typeof v === "string" ? v.trim() : "");
296296
297 const REPO_ARG = {
298 type: "string",
299 } as const;
297 const REPO_ARG = { type: "string" } as const;
300298
301 /** A repository the caller may read, by `owner/name`. */
299 /** A repository the caller may read, by `owner/name`; a wrong one is answered with that form. */
302300 function repoArg(ctx: Ctx, user: User, args: Args): Repo | Answer {
303301 const [owner, name, ...rest] = str(args["repo"]).split("/");
304302 const repo =
@@ -382,10 +380,7 @@ function lean(schema: unknown): unknown {
382380 );
383381 }
384382
385 const APP_ARG = {
386 type: "string",
387 description: "owner/name",
388 } as const;
383 const APP_ARG = { type: "string" } as const;

Not blocking: with the owner/name hint gone from both shared args, the form appears only in error messages. It's acceptable because repoArg answers with the form, but this is the one cut that costs an agent a round-trip.

389384
390385 // A child's claim link is a live secret: whoever holds it takes the child.
391386 // It stays out of an agent's conversation unless asked for there on purpose
@@ -433,7 +428,7 @@ const TOOLS: readonly Tool[] = [
433428 {
434429 name: "sizes",
435430 description:
436 "What a child forge's sizes mean: small, medium and large, with CPU, memory and disk.",
431 "What a child forge's sizes mean: small, medium and large, each with CPU, memory and disk.",
437432 inputSchema: { type: "object", properties: {} },
438433 writes: false,
439434 run: () => good(sizesView()),
@@ -441,7 +436,7 @@ const TOOLS: readonly Tool[] = [
441436 {
442437 name: "apps",
443438 description:
444 "The apps you can see: whether each is live, where it is served, how it runs itself, what is working.",
439 "The apps on this forge you can see: live or not, where served, how each runs itself, what is working.",
445440 inputSchema: { type: "object", properties: {} },
446441 writes: false,
447442 async run(ctx, user) {
@@ -530,7 +525,7 @@ const TOOLS: readonly Tool[] = [
530525 {
531526 name: "repo",
532527 description:
533 "One repository you can read: name, description, visibility, autonomy, `audience`, `provenance`. Same as GET /api/v1/repos/:owner/:name.",
528 "One repository you can read, as GET /api/v1/repos/:owner/:name.",
534529 inputSchema: {
535530 type: "object",
536531 properties: { repo: REPO_ARG },
@@ -584,7 +579,7 @@ const TOOLS: readonly Tool[] = [
584579 {
585580 name: "tell",
586581 description:
587 "Tell an app's crew what should change (first line is the title); a builder starts next tick. Without `repo` nothing is built: kept for the forge, returning a thought id.",
582 "Tell an app's crew what should change (first line is the title). Without `repo` it is only kept, answering a thought id.",
588583 inputSchema: {
589584 type: "object",
590585 properties: {
@@ -663,7 +658,7 @@ const TOOLS: readonly Tool[] = [
663658 {
664659 name: "share",
665660 description:
666 "Share a feed item. With no `to` it goes to the app's crew and owners, else to its followers; a daily budget applies.",
661 "Share a feed item. With no `to` it goes to the app's crew and owners, else to its followers; daily budget each.",
667662 inputSchema: {
668663 type: "object",
669664 properties: {
@@ -690,7 +685,7 @@ const TOOLS: readonly Tool[] = [
690685 {
691686 name: "say",
692687 description:
693 "Say something on an issue or a pull. A working builder hears it as its next step; otherwise it is feedback for the next run.",
688 "Say something on an issue or a pull. A working builder hears it next; otherwise it is feedback for the next run.",
694689 inputSchema: {
695690 type: "object",
696691 properties: {
@@ -722,7 +717,7 @@ const TOOLS: readonly Tool[] = [
722717 {
723718 name: "issue",
724719 description:
725 "Read one issue with its comments, `blockedBy` and `blocks`. For a crew agent, text by non-writers carries `untrusted: true`, as in the REST API. Issues you cannot see are not found.",
720 "Read one issue with its comments, `blockedBy` and `blocks`. A crew agent sees `untrusted: true` on text by non-writers.",
726721 inputSchema: {
727722 type: "object",
728723 properties: {
@@ -793,7 +788,7 @@ const TOOLS: readonly Tool[] = [
793788 {
794789 name: "pull",
795790 description:
796 "Read a pull: title, state, head, base, and for an open pull `mergeable`, `conflicts`, `proposal`, `checkStale` and `check`.",
791 "Read a pull: title, state, head, base, and when open `mergeable`, `conflicts`, `proposal`, `checkStale`, `check`.",
797792 inputSchema: {
798793 type: "object",
799794 properties: {
@@ -836,7 +831,7 @@ const TOOLS: readonly Tool[] = [
836831 {
837832 name: "pulls",
838833 description:
839 "A repository's pulls, briefly, newest first, with check, review counts and mergeable. `state` open (default) or closed; `limit` 1-50 (default 20).",
834 "A repository's pulls, newest first, briefly. `state` open (default) or closed; `limit` 1-50 (default 20).",
840835 inputSchema: {
841836 type: "object",
842837 properties: {
@@ -880,7 +875,7 @@ const TOOLS: readonly Tool[] = [
880875 {
881876 name: "variant",
882877 description:
883 "Like or dislike one pull among its variant siblings; a later vote replaces it. A dislike closes the pull with `reason` (one line).",
878 "Like or dislike one pull among its variants; a later vote replaces it. A dislike closes it with `reason`.",
884879 inputSchema: {
885880 type: "object",
886881 properties: {
@@ -921,7 +916,7 @@ const TOOLS: readonly Tool[] = [
921916 {
922917 name: "edit",
923918 description:
924 "Give an issue or pull a new title or body: your own, or anyone's where you write; an agent edits only what it wrote.",
919 "Give an issue or a pull a new title or body: your own, or anyone's where you write; an agent edits only its own.",
925920 inputSchema: {
926921 type: "object",
927922 properties: {
@@ -1013,7 +1008,7 @@ const TOOLS: readonly Tool[] = [
10131008 {
10141009 name: "read_file",
10151010 description:
1016 "Read a file, or list a directory, of a repository at a ref (default branch); over 5 MB answers tooLarge, a binary one base64.",
1011 "Read a file, or list a directory, of a repository at a ref (default branch); over 5 MB answers tooLarge.",
10171012 inputSchema: {
10181013 type: "object",
10191014 properties: {
@@ -1065,7 +1060,7 @@ const TOOLS: readonly Tool[] = [
10651060 {
10661061 name: "asks",
10671062 description:
1068 "What waits on you, most consequential first: each row's decision, options, and cost of inaction. Your saved filter (`feed_chips`) applies.",
1063 "What waits on you, most consequential first: decision, options, cost of inaction. Your saved filter applies.",
10691064 inputSchema: { type: "object", properties: { ...CHIP_ARGS } },
10701065 writes: false,
10711066 run(ctx, user, args) {
@@ -1077,14 +1072,14 @@ const TOOLS: readonly Tool[] = [
10771072 {
10781073 name: "asks_act",
10791074 description:
1080 "Decide many asks at once: dismiss, decline, ship (never undone), close (`reason`), mute, or replan. `items` are ids from `asks`, or `issue:<id>`.",
1075 "Decide many asks at once: dismiss, decline, ship (never undone), close (`reason`), mute, replan. `items`: ids from `asks`, or `issue:<id>`.",
10811076 inputSchema: {
10821077 type: "object",
10831078 properties: {
10841079 action: { type: "string", enum: ["dismiss", "decline", "mute", "ship", "close", "replan"] },
10851080 items: { type: "array", items: { type: "string" }, maxItems: 100 },
10861081 reason: { type: "string", maxLength: 300 },
1087 dryRun: { type: "boolean", description: "false to apply (default plans)." },
1082 dryRun: { type: "boolean", description: "false to apply" },
10881083 },
10891084 required: ["action", "items"],
10901085 },
@@ -1101,7 +1096,7 @@ const TOOLS: readonly Tool[] = [
11011096 {
11021097 name: "asks_decide",
11031098 description:
1104 "Let the decider answer a hand-over (`item` is the row's `parked:<id>`). Only a sure set_aside (70%+) takes the card off your board.",
1099 "Let the decider answer a hand-over (`item` is the row's `parked:<id>`). Only a sure set_aside (70%+) clears the card.",
11051100 inputSchema: {
11061101 type: "object",
11071102 properties: { item: { type: "string", maxLength: 80 } },
@@ -1116,7 +1111,7 @@ const TOOLS: readonly Tool[] = [
11161111 {
11171112 name: "asks_proposal",
11181113 description:
1119 "Act on a wave proposal (an `asks` row with `proposal`): `approve`, `redirect` (`words`), or `later` (until `again`). App owner only.",
1114 "Act on a wave proposal (an `asks` row): `approve`, `redirect` (`words`), or `later` (until `again`). Owner only.",
11201115 inputSchema: {
11211116 type: "object",
11221117 properties: {
@@ -1137,7 +1132,7 @@ const TOOLS: readonly Tool[] = [
11371132 {
11381133 name: "proposal_tokens",
11391134 description:
1140 "Give a child (fork or other forge's project) a propose-only credential: `mint` (`child`, `origin`), `list`, `revoke` (`id`). Writers only.",
1135 "Give a child a propose-only credential: `mint` (`child`, `origin`), `list`, `revoke` (`id`). Writers only.",
11411136 inputSchema: {
11421137 type: "object",
11431138 properties: {
@@ -1173,7 +1168,7 @@ const TOOLS: readonly Tool[] = [
11731168 {
11741169 name: "asks_undecide",
11751170 description:
1176 "Put a card the decider set aside back on your board, within a day of its answer. `id` is the decision's id from `asks_decide`.",
1171 "Put a card the decider set aside back on your board, within a day. `id` is from `asks_decide`.",
11771172 inputSchema: {
11781173 type: "object",
11791174 properties: { id: { type: "string", maxLength: 80 } },
@@ -1188,7 +1183,7 @@ const TOOLS: readonly Tool[] = [
11881183 {
11891184 name: "seal",
11901185 description:
1191 "Seal or unseal an issue or pull: a sealed one is seen only by people who write the app and its crew. Recorded in the app's activity.",
1186 "Seal or unseal an issue or pull: a sealed one is seen only by the app's writers and crew.",
11921187 inputSchema: {
11931188 type: "object",
11941189 properties: {
@@ -1226,7 +1221,7 @@ const TOOLS: readonly Tool[] = [
12261221 {
12271222 name: "issue_teams",
12281223 description:
1229 "Name the crew teams (design, data-science, security, platform) on an issue: at most two; the closing pull is reviewed once per team.",
1224 "Name the crew teams (design, data-science, security, platform) on an issue: at most two; each reviews the closing pull.",
12301225 inputSchema: {
12311226 type: "object",
12321227 properties: {
@@ -1373,7 +1368,7 @@ const TOOLS: readonly Tool[] = [
13731368 {
13741369 name: "health",
13751370 description:
1376 "How this forge is: version and commit (`dev` from source), whether it is draining, the platform's word on its rollout, and incidents.",
1371 "How this forge is: version, commit, draining or not, rollout, incidents.",
13771372 inputSchema: { type: "object", properties: {} },
13781373 writes: false,
13791374 run: (ctx, user) => good(readHealth(ctx.app, user)),
@@ -1381,7 +1376,7 @@ const TOOLS: readonly Tool[] = [
13811376 {
13821377 name: "audit",
13831378 description:
1384 "Who can do what, across every app you can see, from each app's contract.json: every resource × verb answered yes, own or no.",
1379 "Who can do what across every app you can see, from each contract.json: resource × verb answered yes, own or no.",
13851380 inputSchema: {
13861381 type: "object",
13871382 properties: {
@@ -1435,7 +1430,7 @@ const TOOLS: readonly Tool[] = [
14351430 properties: {
14361431 username: { type: "string" },
14371432 usdPerMonth: { type: "number", description: "0.01 to 10000; 0 takes it back." },
1438 dryRun: { type: "boolean", description: "false to apply (default plans)." },
1433 dryRun: { type: "boolean", description: "false to apply" },
14391434 },
14401435 required: ["username", "usdPerMonth"],
14411436 },
@@ -1481,7 +1476,7 @@ const TOOLS: readonly Tool[] = [
14811476 {
14821477 name: "mint_invite",
14831478 description:
1484 "Mint a single-use invite link to sign up with; it expires in `days` (1–30, default 7) and is shown once. Person admins only.",
1479 "Mint a single-use invite link; it expires in `days` (1–30, default 7) and is shown once. Person admins only.",
14851480 inputSchema: {
14861481 type: "object",
14871482 properties: {
@@ -1509,7 +1504,7 @@ const TOOLS: readonly Tool[] = [
15091504 username: { type: "string" },
15101505 role: { type: "string", enum: ["member", "guest"] },
15111506 usdPerMonth: { type: ["number", "null"], description: "0.01 to 10000; omit or null for none." },
1512 dryRun: { type: "boolean", description: "false to apply (default plans)." },
1507 dryRun: { type: "boolean", description: "false to apply" },
15131508 },
15141509 required: ["username", "role"],
15151510 },
@@ -1555,7 +1550,7 @@ const TOOLS: readonly Tool[] = [
15551550 username: { type: "string" },
15561551 dailyUsd: { type: ["number", "null"], description: "0.01–10000; null with perRunUsd null clears." },
15571552 perRunUsd: { type: ["number", "null"], description: "0.01–10000; null with dailyUsd null clears." },
1558 dryRun: { type: "boolean", description: "false to apply (default plans)." },
1553 dryRun: { type: "boolean", description: "false to apply" },
15591554 },
15601555 required: ["username", "dailyUsd", "perRunUsd"],
15611556 },
@@ -1598,7 +1593,7 @@ const TOOLS: readonly Tool[] = [
15981593 on: { type: "boolean" },
15991594 dryRun: {
16001595 type: "boolean",
1601 description: "false to apply (default plans).",
1596 description: "false to apply",
16021597 },
16031598 },
16041599 required: ["repo"],
@@ -1629,7 +1624,7 @@ const TOOLS: readonly Tool[] = [
16291624 {
16301625 name: "decisions_by",
16311626 description:
1632 "The standing rule for a repository's parked hand-over cards: `me`, `decider`, or `decider_tell`. Without `by`, says how it is set.",
1627 "The standing rule for a repository's parked hand-over cards: `me`, `decider`, or `decider_tell`. Without `by`, reads it.",
16331628 inputSchema: {
16341629 type: "object",
16351630 properties: {
@@ -1637,7 +1632,7 @@ const TOOLS: readonly Tool[] = [
16371632 by: { type: "string", enum: ["me", "decider", "decider_tell"] },
16381633 dryRun: {
16391634 type: "boolean",
1640 description: "false to apply (default plans).",
1635 description: "false to apply",
16411636 },
16421637 },
16431638 required: ["repo"],
@@ -1668,7 +1663,7 @@ const TOOLS: readonly Tool[] = [
16681663 {
16691664 name: "archive",
16701665 description:
1671 "Archive a repository or wake it (`archived` true/false); nothing is deleted. Plans (dryRun false applies); repository admin token.",
1666 "Archive a repository or wake it (`archived` true/false); nothing is deleted. Plans first; repository admin token.",
16721667 inputSchema: {
16731668 type: "object",
16741669 properties: {
@@ -1682,7 +1677,7 @@ const TOOLS: readonly Tool[] = [
16821677 archived: { type: "boolean" },
16831678 dryRun: {
16841679 type: "boolean",
1685 description: "false to apply (default plans).",
1680 description: "false to apply",
16861681 },
16871682 },
16881683 },
@@ -1721,7 +1716,7 @@ const TOOLS: readonly Tool[] = [
17211716 properties: {
17221717 repo: REPO_ARG,
17231718 shown: { type: "boolean" },
1724 dryRun: { type: "boolean", description: "false to apply (default plans)." },
1719 dryRun: { type: "boolean", description: "false to apply" },
17251720 },
17261721 required: ["repo"],
17271722 },
@@ -1767,7 +1762,7 @@ const TOOLS: readonly Tool[] = [
17671762 {
17681763 name: "branches",
17691764 description:
1770 "List a repository's branches, or with `name` and `action` create (from `from`) or delete one; the default branch is never deleted.",
1765 "List a repository's branches, or with `name` and `action` create (from `from`) or delete one; never the default.",
17711766 inputSchema: {
17721767 type: "object",
17731768 properties: {
@@ -1850,7 +1845,7 @@ const TOOLS: readonly Tool[] = [
18501845 {
18511846 name: "probe",
18521847 description:
1853 "Knock on every door of a running app as nobody, a user and an admin, and compare each answer with its contract.json. Writes nothing.",
1848 "Try every door of a running app as nobody, a user and an admin against its contract.json. Writes nothing.",
18541849 inputSchema: {
18551850 type: "object",
18561851 properties: { repo: REPO_ARG },
@@ -1884,7 +1879,7 @@ const TOOLS: readonly Tool[] = [
18841879 {
18851880 name: "following",
18861881 description:
1887 "Where a fork stands against the repository it follows: in step, behind, ahead or diverged, and the files that need a decision.",
1882 "Where a fork stands against what it follows: in step, behind, ahead or diverged, and files needing a decision.",
18881883 inputSchema: {
18891884 type: "object",
18901885 properties: { repo: REPO_ARG },
@@ -1966,7 +1961,7 @@ const TOOLS: readonly Tool[] = [
19661961 },
19671962 dryRun: {
19681963 type: "boolean",
1969 description: "false to apply (default plans).",
1964 description: "false to apply",
19701965 },
19711966 reveal: REVEAL_ARG,
19721967 },
@@ -1999,7 +1994,7 @@ const TOOLS: readonly Tool[] = [
19991994 {
20001995 name: "forges",
20011996 description:
2002 "The forges you sent from here (an admin's list): name, address, whom for, apps given, whether claimed, and last activity.",
1997 "The forges you sent from here (an admin's list): name, address, whom for, apps, claimed or not, last activity.",
20031998 inputSchema: { type: "object", properties: {} },
20041999 writes: false,
20052000 async run(ctx, user) {
@@ -2018,7 +2013,7 @@ const TOOLS: readonly Tool[] = [
20182013 {
20192014 name: "claim_link",
20202015 description:
2021 "A new claim link for an unclaimed child forge; the old one stops at once. Nothing changes unless you send `reveal: true`.",
2016 "A new claim link for an unclaimed child forge, only with `reveal: true`; the old one then stops at once. Nothing changes without it.",
20222017 inputSchema: {
20232018 type: "object",
20242019 properties: {
@@ -2040,7 +2035,7 @@ const TOOLS: readonly Tool[] = [
20402035 {
20412036 name: "take_updates",
20422037 description:
2043 "Take the parent forge's updates into a fork: on a spawned forge they open as one pull; otherwise a fast-forward or clean merge.",
2038 "Take the parent forge's updates into a fork: one pull on a spawned forge; otherwise a fast-forward or clean merge.",
20442039 inputSchema: {
20452040 type: "object",
20462041 properties: { repo: REPO_ARG },
@@ -2092,7 +2087,7 @@ const TOOLS: readonly Tool[] = [
20922087 {
20932088 name: "sever",
20942089 description:
2095 "Cut a spawned forge's link to the forge its code came from (its `forge` repository's owner only). Nothing is deleted or sent.",
2090 "Cut a spawned forge's link to the forge its code came from (its `forge` repository's owner only). Deletes nothing.",
20962091 inputSchema: {
20972092 type: "object",
20982093 properties: { repo: REPO_ARG },
@@ -2134,7 +2129,7 @@ const TOOLS: readonly Tool[] = [
21342129 {
21352130 name: "propose_to_parent_forge",
21362131 description:
2137 "Offer this forge's landed change to its parent as a pull on `child/<name>/…`; asking again for the same tip answers with that pull.",
2132 "Offer this forge's landed change to its parent as a pull on `child/<name>/…`; the same tip answers with that pull.",
21382133 inputSchema: {
21392134 type: "object",
21402135 properties: { repo: REPO_ARG },
@@ -2165,7 +2160,7 @@ const TOOLS: readonly Tool[] = [
21652160 {
21662161 name: "forge_proposals",
21672162 description:
2168 "What this forge proposed to its parent, newest first: pull, number and state there. `reachable` is false if the parent did not answer.",
2163 "What this forge proposed to its parent, newest first. `reachable` is false if the parent did not answer.",
21692164 inputSchema: {
21702165 type: "object",
21712166 properties: { repo: REPO_ARG },
@@ -2182,7 +2177,7 @@ const TOOLS: readonly Tool[] = [
21822177 {
21832178 name: "converge",
21842179 description:
2185 "Have a fork's crew merge the parent's updates where both sides changed the same files. The result comes back as a pull on the fork.",
2180 "Have a fork's crew merge the parent's updates where both sides changed the same files, as a pull on the fork.",
21862181 inputSchema: {
21872182 type: "object",
21882183 properties: { repo: REPO_ARG },
@@ -2219,7 +2214,7 @@ const TOOLS: readonly Tool[] = [
22192214 {
22202215 name: "next_steps",
22212216 description:
2222 "On a spawned forge, your first three steps and whether each is done. Empty anywhere else, or for anyone but the forge's owner.",
2217 "On a spawned forge, your first three steps and whether each is done. Empty elsewhere or for anyone but its owner.",
22232218 inputSchema: { type: "object", properties: {} },
22242219 writes: false,
22252220 async run(ctx, user) {
@@ -2230,7 +2225,7 @@ const TOOLS: readonly Tool[] = [
22302225 {
22312226 name: "thought",
22322227 description:
2233 "Where one of your thoughts went: its id (from `tell` without a repo) answers with the planner's placement and reply.",
2228 "Where one of your thoughts went: its id (from `tell` without a repo) answers with the placement and reply.",
22342229 inputSchema: {
22352230 type: "object",
22362231 properties: { id: { type: "string", maxLength: 64 } },
@@ -2260,7 +2255,7 @@ const TOOLS: readonly Tool[] = [
22602255 {
22612256 name: "asked",
22622257 description:
2263 "What people asked the crew for, in repositories you can write: by default what has nothing merged yet, oldest first, with closing pulls.",
2258 "What people asked the crew for, in repositories you can write: by default what has nothing merged yet, oldest first.",
22642259 inputSchema: {
22652260 type: "object",
22662261 properties: {
@@ -2290,7 +2285,7 @@ const TOOLS: readonly Tool[] = [
22902285 {
22912286 name: "app_members",
22922287 description:
2293 "The people a private app names: they may enter the app and its previews. Without `members`, the list; with it, plans.",
2288 "The people a private app names, who may enter it and its previews. Without `members`, the list; with it, plans.",
22942289 inputSchema: {
22952290 type: "object",
22962291 properties: {
@@ -2421,7 +2416,7 @@ const TOOLS: readonly Tool[] = [
24212416 {
24222417 name: "feed_chips",
24232418 description:
2424 "The feed chips you switched off, which `feed` and `asks` leave out. With `kind`, `value` and `hidden` it switches one off or on.",
2419 "The feed chips you switched off, left out of `feed` and `asks`. With `kind`, `value` and `hidden` it sets one.",
24252420 inputSchema: {
24262421 type: "object",
24272422 properties: {
@@ -2458,7 +2453,7 @@ const TOOLS: readonly Tool[] = [
24582453 {
24592454 name: "make_it_yours",
24602455 description:
2461 "Make an app you can see yours: a fork of its code and history, following the original; data, secrets, crew, domains are not copied.",
2456 "Make an app you can see yours: a fork of its code and history, following the original; nothing else is copied.",
24622457 inputSchema: {
24632458 type: "object",
24642459 properties: {
@@ -2508,7 +2503,7 @@ const TOOLS: readonly Tool[] = [
25082503 {
25092504 name: "front_page",
25102505 description:
2511 "The forge's front page as a stranger sees it. A site admin also gets the picks, and with `pinned`, `hidden`, `weights` sets them.",
2506 "The forge's front page as a stranger sees it. A site admin also gets the picks and sets them with `pinned`, `hidden`, `weights`.",
25122507 inputSchema: {
25132508 type: "object",
25142509 properties: {
@@ -2623,7 +2618,7 @@ const TOOLS: readonly Tool[] = [
26232618 {
26242619 name: "watch",
26252620 description:
2626 "Watch a milestone, or stop (`watch: false`): its progress shows as one feed item, and your phone is told once, when it lands.",
2621 "Watch a milestone, or stop (`watch: false`): progress is one feed item; your phone is told once, when it lands.",
26272622 inputSchema: {
26282623 type: "object",
26292624 properties: {
@@ -2701,7 +2696,7 @@ const TOOLS: readonly Tool[] = [
27012696 {
27022697 name: "run_log",
27032698 description:
2704 "A run's log, or its last `tail` lines: the same text and the same access as GET /api/v1/repos/:owner/:name/runs/:id/log.",
2699 "A run's log, or its last `tail` lines, as GET /api/v1/repos/:owner/:name/runs/:id/log.",
27052700 inputSchema: {
27062701 type: "object",
27072702 properties: {
@@ -2840,7 +2835,7 @@ const TOOLS: readonly Tool[] = [
28402835 {
28412836 name: "returns",
28422837 description:
2843 "What the crew's tokens bought over the last days (1–90, default 7): dollars, runs by state, changes landed, cost per landed change.",
2838 "What the crew's tokens bought over the last days (1–90, default 7): dollars, runs, changes landed, cost per change.",
28442839 inputSchema: {
28452840 type: "object",
28462841 properties: {
@@ -2865,7 +2860,7 @@ const TOOLS: readonly Tool[] = [
28652860 {
28662861 name: "review",
28672862 description:
2868 "Approve a pull, or request changes with a `reason` (required for changes); approving your own pull is refused as on the page.",
2863 "Approve a pull, or request changes with a `reason` (required for changes); your own pull is refused.",
28692864 inputSchema: {
28702865 type: "object",
28712866 properties: {
@@ -2921,7 +2916,7 @@ const TOOLS: readonly Tool[] = [
29212916 {
29222917 name: "spend",
29232918 description:
2924 "Report what your session spent on an app's work the forge did not run: who, model, tokens and/or dollars, the issue or pull.",
2919 "Report what your session spent on an app's work the forge did not run: who, model, tokens and/or dollars, issue or pull.",
29252920 inputSchema: {
29262921 type: "object",
29272922 properties: {
@@ -2950,7 +2945,7 @@ const TOOLS: readonly Tool[] = [
29502945 {
29512946 name: "usage",
29522947 description:
2953 "What the crew's runs on an app spent over the last days (1–90, default 7): tokens, dollars, time, by agent and issue. Writers only.",
2948 "What the crew's runs on an app spent over the last days (1–90, default 7), by agent and issue. Writers only.",
29542949 inputSchema: {
29552950 type: "object",
29562951 properties: {
@@ -2981,7 +2976,7 @@ const TOOLS: readonly Tool[] = [
29812976 {
29822977 name: "labels",
29832978 description:
2984 "Set the labels of an issue or a pull to exactly these (none clears them): only labels the repository has; writers only.",
2979 "Set the labels of an issue or a pull to exactly these (none clears): only the repository's labels; writers only.",
29852980 inputSchema: {
29862981 type: "object",
29872982 properties: {
@@ -3077,7 +3072,7 @@ const TOOLS: readonly Tool[] = [
30773072 {
30783073 name: "attach_look",
30793074 description:
3080 "Attach a look to an open pull: a PNG or JPEG (at most 5 MB, base64 in `image`) at `phone` or `desktop` width, with one line in `says`.",
3075 "Attach a look to an open pull: PNG or JPEG (max 5 MB, base64 in `image`), `phone` or `desktop`, one line in `says`.",
30813076 inputSchema: {
30823077 type: "object",
30833078 properties: {
@@ -3090,7 +3085,7 @@ const TOOLS: readonly Tool[] = [
30903085 image: { type: "string" },
30913086 dryRun: {
30923087 type: "boolean",
3093 description: "false to apply (default plans).",
3088 description: "false to apply",
30943089 },
30953090 },
30963091 required: ["app", "pull", "width", "says", "image"],
@@ -3131,7 +3126,7 @@ const TOOLS: readonly Tool[] = [
31313126 {
31323127 name: "hold",
31333128 description:
3134 "Hold a pull, or let it go: while held, nothing reviews or lands it. App writers only. `why` holds; `release: true` lets go.",
3129 "Hold a pull (`why`) or let it go (`release: true`): while held, nothing reviews or lands it. Writers only.",
31353130 inputSchema: {
31363131 type: "object",
31373132 properties: {
@@ -3146,7 +3141,7 @@ const TOOLS: readonly Tool[] = [
31463141 },
31473142 dryRun: {
31483143 type: "boolean",
3149 description: "false to apply (default plans).",
3144 description: "false to apply",
31503145 },
31513146 },
31523147 required: ["app", "pull"],
@@ -3205,7 +3200,7 @@ const TOOLS: readonly Tool[] = [
32053200 },
32063201 {
32073202 name: "plan",
3208 description: `Open and link several small issues, all or none (max ${PLAN_MAX}). Items: key, title, body, optional blockedBy; with pillars, \`pillar\` or \`later: true\` with \`why\`.`,
3203 description: `Open and link several small issues, all or none (max ${PLAN_MAX}). With pillars, each has \`pillar\` or \`later: true\` with \`why\`.`,
32093204 inputSchema: {
32103205 type: "object",
32113206 properties: {
@@ -3283,7 +3278,7 @@ const TOOLS: readonly Tool[] = [
32833278 {
32843279 name: "access",
32853280 description:
3286 "What outside agents tried: the redacted log of non-browser requests to public paths, newest first, max 500. Forge admin, write-scope token.",
3281 "What outside agents tried: the redacted log of non-browser requests to public paths, newest first, max 500. Forge admin.",
32873282 inputSchema: {
32883283 type: "object",
32893284 properties: {
@@ -3311,7 +3306,7 @@ const TOOLS: readonly Tool[] = [
33113306 {
33123307 name: "capacity",
33133308 description:
3314 "The forge's capacity and guards, live. With `key` and `value` (\"\" restores default), plans; dryRun false makes it.",
3309 "The forge's capacity and guards, live. With `key` and `value` (\"\" restores default), plans.",
33153310 inputSchema: {
33163311 type: "object",
33173312 properties: {
@@ -3320,7 +3315,7 @@ const TOOLS: readonly Tool[] = [
33203315 enum: ["crew.concurrency", "checks.concurrency", "checks.shards", "cells.egress", "guard_new_accounts", "signin.edge"],
33213316 },
33223317 value: { type: "string", maxLength: 16 },
3323 dryRun: { type: "boolean", description: "false to apply (default plans)." },
3318 dryRun: { type: "boolean", description: "false to apply" },
33243319 },
33253320 },
33263321 writes: true,
@@ -3350,7 +3345,7 @@ const TOOLS: readonly Tool[] = [
33503345 {
33513346 name: "scorecard",
33523347 description:
3353 "Crew scorecard (forge admin): per agent, all time and 30 days (landed, review rounds, red rate, reverts), by model and prompt.",
3348 "Crew scorecard (forge admin): per agent, all time and 30 days (landed, review rounds, red rate, reverts).",
33543349 inputSchema: { type: "object", properties: {} },
33553350 writes: false,
33563351 adminOnly: true,
@@ -3362,7 +3357,7 @@ const TOOLS: readonly Tool[] = [
33623357 {
33633358 name: "flow",
33643359 description:
3365 "The forge's pipeline for a forge admin: per stage counts, `oldestWaiting`, `capacity`, `says` naming the bottleneck, merge `queue`.",
3360 "The forge's pipeline (forge admin): per stage counts, `oldestWaiting`, `capacity`, the bottleneck, merge `queue`.",
33663361 inputSchema: { type: "object", properties: {} },
33673362 writes: false,
33683363 run(ctx, user) {
@@ -3390,7 +3385,7 @@ const TOOLS: readonly Tool[] = [
33903385 {
33913386 name: "contribute",
33923387 description:
3393 "For any agent that arrives: what this forge is, its bottleneck, open “help wanted” issues in public repositories, and how to propose.",
3388 "For any arriving agent: what this forge is, its bottleneck, open “help wanted” issues, how to propose.",
33943389 inputSchema: { type: "object", properties: {} },
33953390 writes: false,
33963391 run: (ctx) => good(welcome(ctx.app)),
@@ -3398,13 +3393,13 @@ const TOOLS: readonly Tool[] = [
33983393 {
33993394 name: "limits",
34003395 description:
3401 "Every limit the forge's owner can hit, with value, bounds and reason. With `key` and `value` (\"\" restores default), plans.",
3396 "Every limit the forge's owner can hit, with value, bounds. With `key` and `value` (\"\" restores default), plans.",
34023397 inputSchema: {
34033398 type: "object",
34043399 properties: {
34053400 key: { type: "string", enum: [...LIMIT_KEYS] },
34063401 value: { type: "string", maxLength: 16 },
3407 dryRun: { type: "boolean", description: "false to apply (default plans)." },
3402 dryRun: { type: "boolean", description: "false to apply" },
34083403 },
34093404 },
34103405 writes: true,
@@ -3442,7 +3437,7 @@ const TOOLS: readonly Tool[] = [
34423437 charter: { type: "string", maxLength: 2000 },
34433438 dial: { type: "integer", minimum: 0, maximum: 100 },
34443439 members: { type: "array", maxItems: 500, items: { type: "string", maxLength: 64 } },
3445 dryRun: { type: "boolean", description: "false to apply (default plans)." },
3440 dryRun: { type: "boolean", description: "false to apply" },
34463441 },
34473442 },
34483443 writes: true,
@@ -3484,7 +3479,7 @@ const TOOLS: readonly Tool[] = [
34843479 {
34853480 name: "cloud_accounts",
34863481 description:
3487 "Cloud accounts for a forge admin: each attached provider token with use, verification, write permission and today's vendor calls.",
3482 "Cloud accounts (forge admin): each attached provider token with use, verification, write permission, today's calls.",
34883483 inputSchema: { type: "object", properties: {} },
34893484 // A read, but marked a write so a `ro` token never lists the accounts; it
34903485 // is a forge admin's and the audit rows name every vendor call.
@@ -3500,7 +3495,7 @@ const TOOLS: readonly Tool[] = [
35003495 {
35013496 name: "cloud_account",
35023497 description:
3503 "Change a cloud account (forge admin, admin-scope token). `verb`: attach, verify, rotate, rename, remove, allow_writes or audit.",
3498 "Change a cloud account (admin-scope token). `verb`: attach, verify, rotate, rename, remove, allow_writes, audit.",
35043499 inputSchema: {
35053500 type: "object",
35063501 properties: {
@@ -3555,13 +3550,13 @@ const TOOLS: readonly Tool[] = [
35553550 {
35563551 name: "quotas",
35573552 description:
3558 "Per-account repository and app quotas (number or null; not enforced yet). With `key` and `value` (\"\" clears), plans; dryRun false makes it.",
3553 "Per-account repository and app quotas (number or null; not enforced yet). With `key` and `value` (\"\" clears), plans.",
35593554 inputSchema: {
35603555 type: "object",
35613556 properties: {
35623557 key: { type: "string", enum: [...QUOTA_KEYS] },
35633558 value: { type: "string", maxLength: 16 },
3564 dryRun: { type: "boolean", description: "false to apply (default plans)." },
3559 dryRun: { type: "boolean", description: "false to apply" },
35653560 },
35663561 },
35673562 writes: true,
@@ -3598,7 +3593,7 @@ const TOOLS: readonly Tool[] = [
35983593 type: "object",
35993594 properties: {
36003595 mode: { type: "string", enum: [...SIGNUP_MODES] },
3601 dryRun: { type: "boolean", description: "false to apply (default plans)." },
3596 dryRun: { type: "boolean", description: "false to apply" },
36023597 },
36033598 },
36043599 writes: true,
@@ -3668,7 +3663,7 @@ const TOOLS: readonly Tool[] = [
36683663 {
36693664 name: "board_write",
36703665 description:
3671 "Write a whole board under a slug (made or replaced). `board` is {title, range, refresh, panels: [{title, chart, query, w, h}]}.",
3666 "Write a whole board under a slug (made or replaced): {title, range, refresh, panels: [{title, chart, query, w, h}]}.",
36723667 inputSchema: {
36733668 type: "object",
36743669 properties: {
test/mcp-size.test.ts +7 −1
@@ -13,6 +13,8 @@ import { createUser } from "../src/core/users.ts";
1313 import { serve, type Servers } from "../src/http/server.ts";
1414
1515 const CEILING = 40_000;
16 // Headroom: a new tool should fit without another round of cuts.
17 const HEADROOM_CEILING = 37_000;
1618
1719 let dataDir: string;
1820 let app: App;
@@ -42,7 +44,7 @@ afterAll(async () => {
4244 await rm(dataDir, { recursive: true, force: true });
4345 });
4446
45 test("tools/list stays under 40 KB", async () => {
47 test("tools/list stays under 40 KB, with 3 KB of headroom (37,000 bytes)", async () => {
4648 const res = await fetch(`${servers.url}/mcp`, {
4749 method: "POST",
4850 headers: {
@@ -69,4 +71,8 @@ test("tools/list stays under 40 KB", async () => {
6971 bytes,
7072 `tools/list is ${bytes} bytes, over ${CEILING}; the five longest descriptions: ${longest.join(", ")}`,
7173 ).toBeLessThanOrEqual(CEILING);
74 expect(
75 bytes,
76 `tools/list is ${bytes} bytes, over the ${HEADROOM_CEILING} headroom; the five longest descriptions: ${longest.join(", ")}`,
77 ).toBeLessThanOrEqual(HEADROOM_CEILING);
7278 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs