WebMCP: add remote human handoff session notifications - #59
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7aca621a1d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (!isExplicitOriginAllowed(request, handoffAllowedOrigins)) { | ||
| return sendJson(response, 403, rpcError(null, -32000, 'Forbidden handoff origin')); | ||
| } |
There was a problem hiding this comment.
Authenticate handoff creation instead of trusting Origin
A non-browser client can forge Origin: https://interdependentway.org, choose arbitrary valid session/write tokens, and create records without possessing any server-issued capability. Repeating this for more than 256 sessions triggers the store's oldest-record eviction and can continuously remove legitimate handoffs; near-limit request bodies can also consume hundreds of megabytes. Require an authenticated or server-signed creation capability, or enforce equivalent abuse controls, rather than treating Origin as proof that the website initiated the request.
Useful? React with 👍 / 👎.
| intentInput?.addEventListener('input', () => { | ||
| if (currentHandoff) clearPublishedHandoff('Request text changed. Press Send again before the agent receives the revision.'); | ||
| if (currentHandoff || remotePublished) { | ||
| clearPublishedHandoff('Request text changed. Press Send again before either agent surface receives the revision.'); |
There was a problem hiding this comment.
Invalidate handoffs that are still being published
If the user edits the request while the remote POST is pending—especially during a Render cold start—both currentHandoff and remotePublished are still false, so this handler does not invalidate anything. When the POST completes, the stale pre-edit request becomes available to the agent and the UI may report it as sent; changing the selected skill has the same race. Track a pending submission generation or abort/retract it whenever either input changes.
Useful? React with 👍 / 👎.
| for (const [session, record] of records) { | ||
| if (record.expiresAt <= timestamp) records.delete(session); | ||
| } |
There was a problem hiding this comment.
Broadcast the tool removal when a handoff expires
When a client has listed tiw_human_handoff and the record later reaches its TTL, this expiry path deletes the record without notifying the session's SSE stream. The server advertises listChanged and tells clients to keep that stream open, so a client can retain the now-stale tool indefinitely and only discover its removal through a failed call or an unsolicited relist. Route expiration through a callback or scheduled cleanup that emits notifications/tools/list_changed for the affected session.
Useful? React with 👍 / 👎.
Roll back only PR #59's remote human-handoff surface after post-merge review found forgeable Origin-based creation plus unresolved pending-submit and TTL notification races. Preserve the prior public registry MCP/browser WebMCP surface. Reintroduce remote handoff only with a server-authenticated creation capability and complete lifecycle semantics.
Roll back only PR #59's remote human-handoff surface after post-merge review found forgeable Origin-based creation plus unresolved pending-submit and TTL notification races. Preserve the prior public registry MCP/browser WebMCP surface. Reintroduce remote handoff only with a server-authenticated creation capability and complete lifecycle semantics.
Purpose
Make human Send reach both kinds of agent from one exact handoff payload.
Human/agent gestalt
tiw_human_handoffto browser WebMCP/mcp?session=<opaque>open and receivesnotifications/tools/list_changedtools/listexposestiw_human_handoff, which returns the exact selected skill, dependency closure, provenance, and human requestSecurity / privacy boundaries
/handoff/<session>interdependentway.orgbrowser originCompatibility
/mcpremains the same five read-only registry tools/mcpremains 405?session=is presenttools.listChanged=true; ordinary connections remain falsehmmm
The server can notify a conforming MCP host that the handoff tool appeared. Neither MCP nor WebMCP can force the model to reason or act; the agent host still chooses when to invoke available tools.