# Help widget demo: source

> The code that makes the Help widget demo work, walked through snippet by snippet.

The agent reaches Ledgerly's billing system through a connection, and a refund waits until a person approves it.

## The connection

Reads are free to use. The one write is governed.

```json file=demos/ledgerly-help/demo.json
"connections": [
  {
    "name": "ledgerly-help Ledgerly MCP",
    "type": "mcp_server",
    …
    "tools": [
      { "name": "get_account", "access": "read", "exposures": ["agents"] },
      { "name": "list_charges", "access": "read", "exposures": ["agents"] },
      { "name": "issue_refund", "access": "write", "exposures": ["agents"], "governed": true }
    ]
```

## No refund without approval

Until an operator approves the write, the agent says so instead of promising.

```md file=demos/ledgerly-help/prompt/policy-rules.md
7. Never say a refund was issued, is pending, is submitted or is on its way, and never promise one, unless issue_refund returned a refundId in this conversation. If issue_refund is missing, refused or fails, say plainly that this action needs approval by a Ledgerly operator before you can run it, and that nothing has been refunded yet.
```

## Seeing the refusal

A refused call arrives as a run event, and the page shows it.

```tsx file=src/demos/widget/index.tsx
if (e.type !== 'tool_end') return
if (e.refused === true) {
  setRefused(true)
  return
}
```

## Honest about what's connected

One flag tells the page whether the connection is live, so it never claims a tool it doesn't have.

```ts file=src/demos/widget/data.ts
export const BILLING_TOOLS_CONNECTED = true
```
