Files
accounted/extensions/general/mcp-server/widgets/index.ts
T
Jakob WennbergandClaude Fable 5 4501f118c2 feat(mcp): approval-queue MCP Apps widget for staged operations (#1278)
* feat(mcp): approval-queue MCP Apps widget for staged operations

gnubok_list_pending_operations(render_ui=true) now renders an interactive
approval queue (claude.ai / Claude Desktop) where the user approves or
rejects each staged operation with a click. High-risk operations arm the
approve button and the second click sends confirmed=true, so the BFL
5 kap 5 acknowledgment is a first-party human action instead of the
agent asserting confirmed=true on the user's behalf (the audit weakness
flagged in dev_docs/erpclaw_analysis.md).

- New widget ui://pending-operations/app.html following the established
  self-contained postMessage/JSON-RPC pattern (no fetch, theme-aware,
  Swedish labels, expandable preview_data per row).
- Result-level _meta.ui hint gated on render_ui=true, mirroring the VAT
  report wiring; the tool stays data-only by default.
- Widget tool references project per namespace (accounted_* clients see
  accounted_ names inside the HTML).
- tools/list payload ceiling 58K -> 58.5K per the in-test convention:
  prose trimmed to the floor first, remainder is wire contract.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(mcp): time out the widget RPC bridge so a silent host cannot strand a row

Review follow-up: sendRequest never settled if the host dropped a
response, leaving op._working=true forever with the approve/reject
buttons gone. A 30s timeout rejects the promise; the existing catch
paths restore the row with an error message so the user can retry.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-29 18:02:17 +02:00

18 lines
521 B
TypeScript

import type { UiWidget } from './types'
import { receiptMatcherWidget } from './receipt-matcher'
import { vatReviewWidget } from './vat-review'
import { pendingOperationsWidget } from './pending-operations'
export const uiWidgets: UiWidget[] = [
receiptMatcherWidget,
vatReviewWidget,
pendingOperationsWidget,
]
export function findUiWidget(uri: string): UiWidget | null {
return uiWidgets.find((w) => w.uri === uri) ?? null
}
export type { UiWidget } from './types'
export { WIDGET_MIME_TYPE } from './types'