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

A note's sender who works through MCP reads the crew's replies to it, as the forge button shows them #10926

Merged opened this

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

Merged

MCP `app_notes` now returns `replies: [{ by, body, at }]` on every note. It reads them from `repliesTo`, the function behind the forge button's replies.

shipped production is serving it deploy befa93c

opened this

MCP app_notes now returns replies: [{ by, body, at }] on every note. It reads them from repliesTo, the function behind the forge button's replies.

bun x tsc --noEmit is clean, and bun test passes on test/mcp-app-notes.test.ts, test/mcp-size.test.ts, test/app_notes.test.ts and test/apps-feedback.test.ts (39 tests) with no ceiling raised. bun scripts/gate.ts printed only a pre-push message asking for a "Try:" commit, so I could not confirm the gate itself passed. I added that commit and pushed.

What changed

  • OwnerNote in src/actions/app_notes.ts gains replies, filled by repliesTo.
  • The studio card, the terminal screen and the MCP tool all list notes through ownerNotes, so all three now see replies.
  • test/mcp-app-notes.test.ts has a new test. A visitor note starts with replies: []. A writer's comment then comes back through /mcp as { by: "wes", body, at }. An outsider's comment on the same issue does not appear.

What I chose not to do

  • No untrusted marker: repliesTo drops outsiders' comments, as the button does. A marker would also mean showing those comments, which the button never does. If you want outsiders' replies visible over MCP, the marker is a small follow-up.
  • No new tool: app_notes is still admin-only, and I left that alone.

To try it Send a note from an app's forge button, comment on its issue as a writer or crew member, and call app_notes { app: "owner/name" }.

Closes #10908

commented

MCP app_notes now returns replies: [{ by, body, at }] on every note. It reads them from repliesTo, the function behind the forge button's replies.

bun x tsc --noEmit is clean, and bun test passes on test/mcp-app-notes.test.ts, test/mcp-size.test.ts, test/app_notes.test.ts and test/apps-feedback.test.ts (39 tests) with no ceiling raised. bun scripts/gate.ts printed only a pre-push message asking for a "Try:" commit, so I could not confirm the gate itself passed. I added that commit and pushed.

What changed

  • OwnerNote in src/actions/app_notes.ts gains replies, filled by repliesTo.
  • The studio card, the terminal screen and the MCP tool all list notes through ownerNotes, so all three now see replies.
  • test/mcp-app-notes.test.ts has a new test. A visitor note starts with replies: []. A writer's comment then comes back through /mcp as { by: "wes", body, at }. An outsider's comment on the same issue does not appear.

What I chose not to do

  • No untrusted marker: repliesTo drops outsiders' comments, as the button does. A marker would also mean showing those comments, which the button never does. If you want outsiders' replies visible over MCP, the marker is a small follow-up.
  • No new tool: app_notes is still admin-only, and I left that alone.

To try it Send a note from an app's forge button, comment on its issue as a writer or crew member, and call app_notes { app: "owner/name" }.

Changes requested — before the latest push

Two things to fix in one round. (1) Box 1 asks for outsiders replies to come back marked untrusted; this head drops them entirely (repliesTo filters them), so the marker is absent. Either return outsiders replies with untrusted: true in the MCP view (a variant of repliesTo that keeps them flagged, without changing what the button shows), or ask the issue author to amend the box. Deferring it as a follow-up leaves the box unmet. (2) Document the new replies field on app_notes in its docs/features how-to (the MCP tool description/schema too, if it lists the note shape). The test is good for what it covers; extend it to assert the untrusted outsider reply. I did not verify box 3 (mcp-size) myself; mason reports it passes with no ceiling raised.

commented

MCP app_notes now returns each note with replies: [{ by, body, at }], and an outsider's reply is included and flagged untrusted: true. bun x tsc --noEmit is clean. Tests pass on test/mcp-app-notes.test.ts, test/mcp-size.test.ts, test/app_notes.test.ts, test/apps-feedback.test.ts and test/mcp.test.ts (87 tests), with no ceiling raised. The second push went out with no new "Try:" commit, and I did not re-run bun scripts/gate.ts this round, so it is untested on this head.

  • Outsiders' replies: repliesTo in src/actions/app_notes.ts takes an optional keepOutsiders flag. The owner's list (ownerView) passes it and gets outsiders' comments marked untrusted. The forge button's view does not pass it, so the button still drops outsiders' comments.
  • Same list everywhere: the studio card, the terminal screen, the API GET /api/v1/repos/:owner/:name/app/notes and MCP all read the same ownerNotes, so all four now show replies, including the flagged outsider ones.
  • Test: the MCP test now asserts the writer's reply carries no untrusted marker and the outsider's comes back with untrusted: true.
  • Docs: docs/APPS.md describes the field next to the notes API. I found no how-to under docs/features/ for app_notes, so that is where I put it. The MCP tool's description names replies and the untrusted marker.
Changes requested — before the latest push

Boxes 1 and 2 are met in substance, but three things need fixing in one round. (1) Unbounded output (medium-high, DoS / context flooding): replies now include every outsider comment on every note, with no cap on count or length. Anyone who can comment on a note issue can bloat the admin MCP/API/studio/terminal answer, and the text goes straight into an agent context. Cap replies per note (e.g. latest N) and truncate each body, and add a test that fails without the cap. (2) Docs: the how-to belongs in docs/features/notes.md (or the feature doc for app notes), not as a long paragraph inside docs/APPS.md; move it and wrap the line. (3) The same OwnerNote now feeds the terminal screen and the studio card, but neither shows replies; the issue asked for MCP only, so either render them in both doors or state that they are intentionally left out. Also extend the test to the API GET list and to the cap. Box 3 (mcp-size) was reported passing by the builder; the tool doc text grew, so keep it within the ceiling.

commented

When an app's admin lists notes through MCP app_notes or GET /api/v1/repos/:owner/:name/app/notes, each note now carries its issue's replies, oldest first. Replies from the crew and the repository's writers come back the same way the forge button shows them. A comment from anyone else is kept too, marked untrusted: true, and the button still never shows those.

This round (review by @sentinel):

  • Size cap (src/actions/app_notes.ts): outsiders write this text, so each note keeps only its latest 10 replies (OWNER_REPLIES). Each reply is cut to 1000 characters (OWNER_REPLY_CHARS) and ends in … when cut, without splitting an emoji or other two-unit character. A new field, earlierReplies, says how many older replies were left out, the same way the button says how many are earlier.
  • Docs: the how-to moved out of docs/APPS.md into docs/features/notes.md, and the line in docs/APPS.md is now short.
  • Studio card and terminal: both still leave replies out, on purpose, because the issue asked for MCP only. The whole thread stays on the note's issue. This is now written in the OwnerNote doc comment and in docs/features/notes.md.
  • MCP tool description: shortened to "each with its latest replies (outsiders' untrusted)". The MCP size tests (test/mcp-size*.test.ts, test/brief-mcp-size.test.ts) pass.
  • Tests (test/mcp-app-notes.test.ts): one new test checks that the API list returns the same replies as MCP. Another adds 15 long outsider comments and checks that only the latest 10 come back, each cut and marked untrusted, with earlierReplies: 5. It fails without the cap.

Not done: the cap is applied after the comments are read from the database, not in the query, and the sender's own button view is unchanged (it already shows only the latest five).

Checks: bun scripts/gate.ts and bun x tsc --noEmit pass. scripts/fast.ts selected 1504 test files for this change, too many to run here. I ran the app-notes, MCP-size and terminal-notes test files instead: 281 pass, 1 skipped, 0 fail.

To try it: on an app with a note from its button, comment on the note's issue as a writer and as someone else. Then call app_notes over MCP, or GET the API list, with an admin's rw token.

🤖 Generated with Claude Code

Approved

Earlier asks are met. Outsiders replies now come back flagged untrusted through a keepOutsiders variant, and the button view is unchanged. The size cap is in: latest 10 replies, bodies cut to 1000 characters without splitting a surrogate pair, plus an earlierReplies count. The how-to moved to docs/features/notes.md and the MCP doc string is short. Tests cover MCP, the API list and the flood cap. Non-blocking: the cap is applied after all comments are read from the database, so a query LIMIT would be cheaper on a very long thread.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs