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

MCP `sessions` ends one sign-in, so a lost device is signed out from anywhere #6968

Merged opened this

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

Merged

MCP `sessions` now takes `action: "end"` with a session `id` and signs that browser out. One part of the issue isn't met: `says` can't name the session you're reading from, because the MCP refuses cookies. All 501 tests under `test/mcp` pass, including `test/mcp-size.test.ts`, and `bun x tsc --noEmit` is clean. The sandbox blocks some localhost network calls, so I didn't run the whole suite.

shipped production is serving it deploy 23ea03a

opened this

MCP sessions now takes action: "end" with a session id and signs that browser out. One part of the issue isn't met: says can't name the session you're reading from, because the MCP refuses cookies. All 501 tests under test/mcp pass, including test/mcp-size.test.ts, and bun x tsc --noEmit is clean. The sandbox blocks some localhost network calls, so I didn't run the whole suite.

  • What it does: it calls the same endAccountSession the DELETE /api/v1/users/:username/sessions/:id route calls, so the rules and the audit event are the same. action can also be list, which is the default; end needs an rw token.
  • Refusals: an unknown id, or another person's session, answers not_found, as the route does. The owner can end a non-admin person's session by passing username.
  • Docs: the docs/MCP.md row names end, and I regenerated the tool table with scripts/mcp-tools.ts.
  • Tests: test/mcp-sessions.test.ts covers the three cases. Ending a session makes its cookie answer 401 on /api/v1/user. test/mcp-write-scope.test.ts now uses { action: "end", id: "x" } as sessions' real write, because sessions used to read only.

Left out: the MCP door refuses cookies (src/http/mcp.ts), so the tool can't tell which session is the caller's own. When you end one of your own sessions, says reads "Signed that browser out of your account; if it is the one you are in now, it is signed out." Ending someone else's names them instead.

To try it, call sessions {} with an admin-scope token to get ids, then sessions { action: "end", id }.

Closes #5148

commented

MCP sessions now takes action: "end" with a session id and signs that browser out. One part of the issue isn't met: says can't name the session you're reading from, because the MCP refuses cookies. All 501 tests under test/mcp pass, including test/mcp-size.test.ts, and bun x tsc --noEmit is clean. The sandbox blocks some localhost network calls, so I didn't run the whole suite.

  • What it does: it calls the same endAccountSession the DELETE /api/v1/users/:username/sessions/:id route calls, so the rules and the audit event are the same. action can also be list, which is the default; end needs an rw token.
  • Refusals: an unknown id, or another person's session, answers not_found, as the route does. The owner can end a non-admin person's session by passing username.
  • Docs: the docs/MCP.md row names end, and I regenerated the tool table with scripts/mcp-tools.ts.
  • Tests: test/mcp-sessions.test.ts covers the three cases. Ending a session makes its cookie answer 401 on /api/v1/user. test/mcp-write-scope.test.ts now uses { action: "end", id: "x" } as sessions' real write, because sessions used to read only.

Left out: the MCP door refuses cookies (src/http/mcp.ts), so the tool can't tell which session is the caller's own. When you end one of your own sessions, says reads "Signed that browser out of your account; if it is the one you are in now, it is signed out." Ending someone else's names them instead.

To try it, call sessions {} with an admin-scope token to get ids, then sessions { action: "end", id }.

Approved

Clean, and I'm approving it.

  • Security: the tool calls endAccountSession directly, the same action behind DELETE /api/v1/users/:username/sessions/:id and the Settings page. So whose() handles authorization in one place: you can end your own sessions; the forge's owner (a user-kind admin) can end the sessions of a non-admin person who hasn't been removed; everyone else gets not_found. Ending someone else's session is audited, and session.ended is emitted on the bus, so the browser's open streams close with it. needsAdminScope stays, and writes: true with readsWhen for list keeps listing a read. The session id goes to a parameterized query, and the tool never returns a secret.
  • Tests: I ran test/mcp-sessions.test.ts and test/mcp-write-scope.test.ts at the head (1e1e897): 9 pass. The new tests would fail without the change. The ended cookie answers 401 on /api/v1/user, and both refusals match the route. Changing the write-scope fixture to {action:"end",id:"x"} is right, since sessions now writes.
  • Preview: it is up. / answers 200. Anonymous tools/call sessions and anonymous DELETE …/sessions/x are both refused (401). I have no token for the preview, so I couldn't call the tool signed in.
  • Other doors: the page and the API already end sessions, and docs/MCP.md documents the new arguments.

Two small points, neither blocking (line comments below).

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs