Skip to content

Auto-dismiss HITL approval popup on elicitation timeout#27

Open
pragati-agrawal-glean wants to merge 1 commit into
mainfrom
fix/hitl-elicitation-timeout-dismiss
Open

Auto-dismiss HITL approval popup on elicitation timeout#27
pragati-agrawal-glean wants to merge 1 commit into
mainfrom
fix/hitl-elicitation-timeout-dismiss

Conversation

@pragati-agrawal-glean

Copy link
Copy Markdown
Contributor

When a user doesn't respond to a HITL approval prompt within HITL_TIMEOUT_MS, the popup got stuck: the user had to press Escape and retry. Root cause is a client-side MCP SDK bug. The first server->client request in a session gets JSON-RPC id 0. The SDK's _oncancel bails on a falsy requestId (if (!notification.params.requestId) return), and !0 is true — so the notifications/cancelled we send on timeout for request id 0 is silently dropped by the client, and the accept/decline popup never auto-dismisses (it lingers with dead buttons). Prompts 2+ get id>=1 and dismiss fine, which is why it looked flaky ("sometimes it disappears").

Fix: burn id 0 with a throwaway ping before the first elicitation, so every approval prompt uses id>=1 and its timeout cancellation is honored. The id is consumed synchronously inside request(), so it's fire-and-forget and tolerant of ping failure. The real fix belongs in the SDK's _oncancel; this guards us until clients ship it.

  • src/tools/run-tool.ts: primeElicitationCancellation(), once per server
  • tests/run-tool.test.ts: cover the burn (pings once; not when no approval)
  • plugins/glean/dist/index.js: rebuilt
  • bump all 3 plugin manifests 0.2.33 -> 0.2.34

… id 0)

When a user doesn't respond to a HITL approval prompt within HITL_TIMEOUT_MS,
the popup got stuck: the user had to press Escape and retry. Root cause is a
client-side MCP SDK bug. The first server->client request in a session gets
JSON-RPC id 0. The SDK's `_oncancel` bails on a falsy requestId
(`if (!notification.params.requestId) return`), and `!0` is true — so the
`notifications/cancelled` we send on timeout for request id 0 is silently
dropped by the client, and the accept/decline popup never auto-dismisses
(it lingers with dead buttons). Prompts 2+ get id>=1 and dismiss fine, which
is why it looked flaky ("sometimes it disappears").

Fix: burn id 0 with a throwaway ping before the first elicitation, so every
approval prompt uses id>=1 and its timeout cancellation is honored. The id is
consumed synchronously inside request(), so it's fire-and-forget and tolerant
of ping failure. The real fix belongs in the SDK's `_oncancel`; this guards us
until clients ship it.

- src/tools/run-tool.ts: primeElicitationCancellation(), once per server
- tests/run-tool.test.ts: cover the burn (pings once; not when no approval)
- plugins/glean/dist/index.js: rebuilt
- bump all 3 plugin manifests 0.2.34 -> 0.2.35

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@pragati-agrawal-glean pragati-agrawal-glean force-pushed the fix/hitl-elicitation-timeout-dismiss branch from 14e8d21 to adcacb5 Compare June 24, 2026 13:12

@swarup-padhi-glean swarup-padhi-glean left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also post screenshots of its working in the slack channel?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants