AgentStack
SKILL verified Apache-2.0 Self-run

Events

skill-deeleeramone-pywry-events · by deeleeramone

The PyWry event system — namespaced events, request/response round-trips, widget IDs, component IDs, and how tool results flow back to the agent.

No reviews yet
0 installs
13 views
0.0% view→install

Install

$ agentstack add skill-deeleeramone-pywry-events

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Events? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

PyWry Event System — Agent Reference

> The event bus is the plumbing underneath every MCP tool. You > rarely need to think about it — the typed tools wrap emit + wait + > state-poll for you — but when you reach for send_event or > interpret tool results, this is how it works.

Event names are namespaced

Every event has the form namespace:event-name, e.g.:

  • tvchart:symbol-search — ask the chart to open symbol search
  • tvchart:state-response — chart's reply with its current state
  • tvchart:data-request — chart asks Python for bars
  • tvchart:data-response — Python delivers bars
  • toolbar:request-state — ask a toolbar component for its value
  • toolbar:state-response — component's reply
  • chat:user-message — user typed something
  • chat:ai-response — model produced a token
  • pywry:update-theme — dark/light mode change

Never emit an event with a name that doesn't match namespace:event-name — the framework rejects it.

Widget IDs vs component IDs

widget_id — identifies the top-level PyWry widget (a chart, a grid, a chat panel, a dashboard). Every MCP tool takes widget_id as an argument because all events route to the widget first.

componentId — identifies a child inside a widget (a specific toolbar button, a marquee ticker slot, a chart pane). Component IDs are scoped to their containing widget.

When you call send_event(widget_id, event_type, data), the widget_id picks the target widget; anything identifying a specific component goes in the data payload (typically as data.componentId or data.chartId).

Request / response pattern

Some events are fire-and-forget (e.g. tvchart:symbol-search — "please do this"). Others are request/response round-trips where the caller wants a reply (e.g. tvchart:request-statetvchart:state-response).

The framework correlates request/response with a context token:

  1. Emitter generates a random context token.
  2. Emitter injects it into the request payload.
  3. Listener sees the request, attaches the same context to its

response, and emits the response event.

  1. Emitter sees the matching context on the response and wakes up.

All of this is handled inside request_response() in pywry.mcp.state — you never construct tokens yourself. Typed MCP tools that need a reply (tvchart_request_state, tvchart_list_indicators) use this under the hood and return the stripped response (no context token) in their tool result.

How tool results reach the agent

Agent             MCP Server          PyWry Widget (JS)
  │                   │                       │
  │ tool call ──────► │                       │
  │                   │ widget.emit() ──────► │
  │                   │                       │ (updates chart)
  │                   │                       │
  │                   │ ◄───── bridge.emit()  │
  │                   │ (state-response)      │
  │                   │                       │
  │ ◄──── tool result │                       │
  │ (includes state)  │                       │

Mutating tools (tvchart_symbol_search, tvchart_change_interval) poll tvchart:request-state after emitting the mutation, wait for the chart to actually reflect the change, and return the real post-change state in the tool result. The state field in the tool result contains the SAME structure as a direct call to tvchart_request_state.

If the mutation didn't settle in time, the result contains a note field explaining the discrepancy — relay the note to the user, do not invent state.

Emitting events from tools — send_event

Only reach for send_event when no typed MCP tool exists for the target event. It's a raw passthrough with no state polling:

send_event(widget_id, event_type, data)
  → { "widget_id": ..., "event_sent": true, "event_type": ... }

Example — apply a rare tvchart option with no typed wrapper:

send_event(
  widget_id="chart",
  event_type="tvchart:apply-options",
  data={"chartOptions": {"timeScale": {"secondsVisible": False}}},
)

The returned event_sent: true means the event was successfully handed to the widget — it does NOT mean the JS handler ran successfully. For confirmation, follow up with tvchart_request_state to read the new state.

Event capture (get_events)

Some events fire from the widget to Python (e.g. the user clicked a bar, moved the crosshair). These are automatically captured into a per-widget event buffer and can be retrieved with:

get_events(widget_id, event_types=[...], clear=True)
  → { "events": [{ "event_type": ..., "data": ..., "label": ... }, ...] }

Default captured events for charts:

  • tvchart:click
  • tvchart:crosshair-move
  • tvchart:visible-range-change
  • tvchart:drawing-added
  • tvchart:drawing-deleted
  • tvchart:open-layout-request
  • tvchart:interval-change
  • tvchart:chart-type-change

Use get_events if the user asks "what did I just click" or "what was the last drawing I added".

Don'ts

  • Do NOT synthesise event payloads. Only report event data the

framework actually handed you.

  • Do NOT emit events whose name doesn't match ns:name — they're

rejected.

  • Do NOT emit to a widget id that isn't registered — the tool will

return an error listing the registered widgets; correct and retry.

  • Do NOT assume event_sent: true means the downstream JS succeeded.

When it matters, follow up with tvchart_request_state (or the relevant state query) to confirm.

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.