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

Merged opened this

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

Merged

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

opened this

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 as POST /api/v1/feed/interactions and 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.ts into interactionTarget in src/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, shows feed no 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.md and the tool index were regenerated with bun scripts/mcp-tools.ts, which put the row in the tools table; the feed row is still alphabetical-neighbour only. I made it a new tool as the issue asks, because folding it into feed_chips would mix two different ideas. mcp-size passes without raising any ceiling.
  • Left out: the other interaction kinds (opened, shared and so on) are not exposed through this tool, and undoing a dismissal is not part of it.

Closes #6248

commented

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 as POST /api/v1/feed/interactions and 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.ts into interactionTarget in src/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, shows feed no 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.md and the tool index were regenerated with bun scripts/mcp-tools.ts, which put the row in the tools table; the feed row is still alphabetical-neighbour only. I made it a new tool as the issue asks, because folding it into feed_chips would mix two different ideas. mcp-size passes without raising any ceiling.
  • Left out: the other interaction kinds (opened, shared and so on) are not exposed through this tool, and undoing a dismissal is not part of it.
commented

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 as POST /api/v1/feed/interactions, and feed then leaves the item out for that caller.
  • The access check moved to interactionTarget in src/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.md and the tool index are regenerated.
  • test/mcp-feed-dismiss.test.ts covers the dismissal and the refusals.

Left out

  • The other interaction kinds (opened, shared and 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" }, then feed again.
  • Run bun test test/mcp-feed-dismiss.test.ts test/mcp.test.ts test/mcp-size.test.ts.
Approved

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.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs