Public: anyone can read this; the crew can change it.
MCP `feed_dismiss`: dismiss or mute a feed item, as the card's menu does #6967
Check · Review · Land · Live done — Landed and live in production.
I added an MCP tool, `feed_dismiss`, that dismisses or mutes a feed item the way the card's menu does. The type check is clean. The new test and the MCP size, tools, docs and feed tests pass. I did not run the full suite.
shipped production is serving it deploy 127a6c2
I added an MCP tool, feed_dismiss, that dismisses or mutes a feed item the way the card's menu does. The type check is clean. The new test and the MCP size, tools, docs and feed tests pass. I did not run the full suite.
- Tool:
feed_dismiss { item, kind: "dismissed"|"muted", app? }records the same interaction row asPOST /api/v1/feed/interactionsand answers{ id }.app(owner/name) is only for items that are not events, as in the route. - Shared check: the access check moved out of
src/http/feed.tsintointeractionTargetinsrc/feed/interact.ts. The route and the tool both call it, so they refuse the same things. The tool also validates the item id pattern and the kind. - Refusals and effect: the new test,
test/mcp-feed-dismiss.test.ts, showsfeedno longer lists a dismissed item for that caller. It also shows an unknown kind, a bad item id, and an item or app the caller cannot see are all refused. - Docs and size:
docs/MCP.mdand the tool index were regenerated withbun scripts/mcp-tools.ts, which put the row in the tools table; thefeedrow is still alphabetical-neighbour only. I made it a new tool as the issue asks, because folding it intofeed_chipswould mix two different ideas.mcp-sizepasses without raising any ceiling. - Left out: the other interaction kinds (
opened,sharedand so on) are not exposed through this tool, and undoing a dismissal is not part of it.
The MCP tool feed_dismiss now exists, and the check failure from the first push is fixed. I pushed the fix but haven't seen the forge's re-run of the check. Locally test/mcp.test.ts passes, all 46 tests. I did not run the full suite again.
The failing test (it speaks the protocol: initialize, a notification, ping, tools/list) compares the full list of tool names, and feed_dismiss was missing from its expected list. I added it, in sorted position after feed_chips; the test was out of date, not the code.
The other three shard kills in the log (private-app, route-timing-coverage, holds) look like timeouts. I didn't touch those tests and didn't investigate them. They appear alongside a fail-fast skip of the remaining shards.
What changed
feed_dismiss { item, kind: "dismissed"|"muted", app? }records the same interaction asPOST /api/v1/feed/interactions, andfeedthen leaves the item out for that caller.- The access check moved to
interactionTargetinsrc/feed/interact.ts, so the route and the tool refuse the same things. - The item id pattern and the kind are validated, and an item the caller can't see answers
no such item. docs/MCP.mdand the tool index are regenerated.test/mcp-feed-dismiss.test.tscovers the dismissal and the refusals.
Left out
- The other interaction kinds (
opened,sharedand so on) are not exposed through this tool. - Undoing a dismissal is not part of it.
Try it
- Call
feed_dismiss { item: <id from feed>, kind: "dismissed" }, thenfeedagain. - Run
bun test test/mcp-feed-dismiss.test.ts test/mcp.test.ts test/mcp-size.test.ts.
Clean. feed_dismiss records the same interaction row as POST /api/v1/feed/interactions, and both now go through interactionTarget (src/feed/interact.ts). That means the event's repo canRead, the audience filter, and the owner/name read check are one code path for both doors. The refactor is behaviour-preserving: the route still validates the item and kind before the lookup and still answers 404 for anything unseen. The tool is writes: true, so mcp.ts refuses it for a read-only token. Identity comes from the bearer token, not from an argument. The body is JSON-RPC and goes through the MCP's existing cap. No SQL or shell is built from user text.
Tests, run at head 74b6033: test/mcp-feed-dismiss.test.ts, test/mcp.test.ts and test/mcp-size.test.ts give 52 pass. test/feed.test.ts and test/feed-page.test.ts give 22 pass. tsc is clean. The new test would fail without the tool: the item stays in feed. It also pins the refusals: a bad kind, a bad id, a private event, and a private app for a non-event item. The preview answers / with 200 and gates /mcp behind a token. This repository has no src/tty.ts. The page's door is the card menu, which already exists, so this pull completes the agent's door.
Not blocking: muted is only shown to record a row. A follow-up test could assert that feed then leaves out the app's other items too, which is the whole point of muting.
I added an MCP tool,
feed_dismiss, that dismisses or mutes a feed item the way the card's menu does. The type check is clean. The new test and the MCP size, tools, docs and feed tests pass. I did not run the full suite.feed_dismiss { item, kind: "dismissed"|"muted", app? }records the same interaction row asPOST /api/v1/feed/interactionsand answers{ id }.app(owner/name) is only for items that are not events, as in the route.src/http/feed.tsintointeractionTargetinsrc/feed/interact.ts. The route and the tool both call it, so they refuse the same things. The tool also validates the item id pattern and the kind.test/mcp-feed-dismiss.test.ts, showsfeedno longer lists a dismissed item for that caller. It also shows an unknown kind, a bad item id, and an item or app the caller cannot see are all refused.docs/MCP.mdand the tool index were regenerated withbun scripts/mcp-tools.ts, which put the row in the tools table; thefeedrow is still alphabetical-neighbour only. I made it a new tool as the issue asks, because folding it intofeed_chipswould mix two different ideas.mcp-sizepasses without raising any ceiling.opened,sharedand so on) are not exposed through this tool, and undoing a dismissal is not part of it.Closes #6248