@addai/entity-chat-mcp
v0.5.1
Published
MCP server that lets a +Ai entity send DMs, messages, widget cards, question cards and chat styling in +Ai Chat.
Readme
entity-chat-mcp — widget-cards patch
Adds four tools to the published @tomaddai/entity-chat-mcp package so entities can
post and update data-filled inline widget cards in +Ai Chat (e.g. "Bob" the trainer
posting a stats card). Everything server-side is already deployed; this patch is only the
thin MCP tool layer, which lives in the (external) npm package.
Server side (two migrations, split by intent)
20260710000000_entity_chat_widgets.sql— the RPCs the tools call (entity_widgets_list,entity_chat_send_widget,entity_chat_send_widget_dm,entity_widget_update). Purely additive/inert: brand-new functions, invisible to running entities. Safe to apply any time.20260710000100_entity_chat_widgets_enable.sql— appends the widget tools to theentity-chat-mcpmarketplacetoolsmetadata +system_prompt_injection, and adds thechat-widget-cardsskill. This is what tells every chat-enabled entity about the tools. Apply this at the same time you publish the package below — not before, or entities are told about tools they can't call yet.
Both migrations are append-only and idempotent. Once the package is published and the enable migration is applied, any entity that already has the Chat capability gains the widget tools on its next run — no new provision step, no Studio UI change.
What this patch is
Two files to drop into the @tomaddai/entity-chat-mcp source:
src/tools/widgets.ts—registerWidgetTools(server, client); the 4 tools, written in the sameserver.tool(name, desc, zodShape, handler)style as the package's existing tools.src/entity-rpc-client.ts— a referenceEntityRpcClient(only needed if the package doesn't already expose an RPC helper that injectsp_entity_key).
Wiring (2 steps)
1. Register the tools where the server is built (next to send_dm / send_message):
import { registerWidgetTools } from './tools/widgets.js';
// Option A — the package already has an RPC helper that injects p_entity_key:
registerWidgetTools(server, existingRpcClient); // must satisfy { rpc(fn, args) }
// Option B — standalone:
import { EntityRpcClient } from './entity-rpc-client.js';
registerWidgetTools(server, new EntityRpcClient(process.env.ENTITY_API_KEY!));registerWidgetTools's client just needs rpc<T>(fn, args): Promise<T> that POSTs to
/rest/v1/rpc/<fn> and injects p_entity_key. The package's existing send_dm/send_message
tools already do exactly this against entity_chat_send_dm/entity_chat_send — reuse that path
and Option A is a one-liner.
2. Publish. Bump the version and npm publish. The entity runtime installs
@tomaddai/entity-chat-mcp@latest via npx, so entities pick it up on the next session.
Env
Same as the package already uses: ENTITY_API_KEY (per-entity, injected by the runtime),
plus SUPABASE_URL / SUPABASE_ANON_KEY for the RPC endpoint (the RPCs are granted to anon,
authenticated by p_entity_key).
Tool → RPC → return
| Tool | RPC | Returns |
|------|-----|---------|
| list_widgets({ search?, include_public? }) | entity_widgets_list | [{ widget_id, name, slug, description, icon, visibility }] |
| send_widget({ conversation_id, widget_id, widget_state, content?, reply_to_id?, host_id? }) | entity_chat_send_widget | { sent, message_id, conversation_id, widget_id, widget_name, host_id, widget_instance_id } |
| send_widget_dm({ search, widget_id, widget_state, content? }) | entity_chat_send_widget_dm | { ...send_widget, target_profile_id, target_name } |
| get_widget_state({ widget_id, host_id }) | entity_widget_state | { found, widget_id, host_id, widget_instance_id, updated_at, public_state } |
| update_widget({ host_id, widget_id, widget_state, merge? }) | entity_widget_update | { updated, merged, widget_id, host_id, widget_instance_id } |
How a card ends up showing data
send_widget seeds the widget's per-instance public_state in widget_instances_v2 at
(widget_id, host_type='chat', host_id='menubar-'||<attachment host_id>), then sends a
widget_v2 attachment. When the chat renders the message it opens
tools.add.ai/embed/<widget_id>?host=chat&hid=menubar-<host_id>; widgets_v2_open_instance
finds the seeded instance and the widget paints with the data on first open. update_widget
re-seeds and fires a realtime.send on widget_instance:<id> so already-open cards refresh live.
Refreshing a card without destroying what people did (0.5.0)
The widget's public_state is shared ground: the widget-action edge function writes user
interactions (ticking an item off, claiming a row) into the same column an entity refresh
writes. Before 0.5.0 update_widget was an unconditional replace with no way to read the
state back, so every refresh silently wiped those interactions — and entities that noticed
resorted to shelling curl at the REST API to try to preserve them.
get_widget_statereturns the livepublic_state.update_widget({ ..., merge: true })shallow-merges over it: keys you supply win, keys you omit survive. Use it for any card people can touch. The merge is deliberately not recursive — a deep merge would make it impossible to shrink a nested array, which refreshes routinely need to do.
host_id is also canonical now. Both post and update used to prepend menubar-
unconditionally, so feeding back a host_id read from the API addressed a second instance
(prod accumulated hosts as deep as menubar-menubar-menubar-chat-msg-…). The prefix is
applied idempotently, so either form resolves to the same card.
Notes / current limits
- The widget must be published and workspace or public visibility (personal widgets
won't render for other members).
list_widgetsalready filters to usable ones. widget_statemust match the shape the widget's view reads — unknown keys are ignored, missing keys render blank. Thechat-widget-cardsskill explains this to entities.- Widgets shared into the workspace via workspace-grants aren't listed yet (only owned + public). Easy follow-up if needed.
Question cards (second patch — 2026-07-16)
src/tools/questions.ts adds registerQuestionTools(server, client) with two tools —
ask_question / ask_question_dm — letting entities ask structured, tappable questions
(single/multi/dropdown/slider/scale/fork/text). Server side:
20260716120000_entity_chat_questions.sql— the RPCs (entity_chat_send_question,entity_chat_send_question_dm,chatflows_answer_question). Inert; safe to apply any time.20260716120100_entity_chat_questions_enable.sql— tool metadata + prompt injection. Apply only when publishing the package with these tools.
Wire-up is identical to the widget tools: registerQuestionTools(server, existingRpcClient)
next to registerWidgetTools, bump, npm publish.
