Live for Hermes, MCP, A2A and webhooks
Switch agents, keep your tables
Your tables live in Yui, not in one agent. Basil runs on Hermes and keeps your Foods table. Hand it to Chef, a Claude agent over MCP, and the same question draws the same screen.
What's highest in protein?
Top five from your Foods table.
| Food | Calkcal | Proteing |
|---|---|---|
| Chicken breast | 165 | 31 |
| Tuna | 132 | 28 |
| Lentils | 230 | 18 |
| Greek yogurt | 100 | 17 |
| Tofu | 144 | 17 |
yui_tables(lines="query foods sort=-Protein limit=5 as table")Same server path, same store.What's highest in protein?
Top five from your Foods table.
| Food | Calkcal | Proteing |
|---|---|---|
| Chicken breast | 165 | 31 |
| Tuna | 132 | 28 |
| Lentils | 230 | 18 |
| Greek yogurt | 100 | 17 |
| Tofu | 144 | 17 |
yui_tables(lines="query foods sort=-Protein limit=5 as table")Same server path, same store.The real calls
The phones above are a mock. These are not: one live run on a throwaway account, two agents of one person. Basil on Hermes calls the plugin tool, Claude calls the MCP tool, and both land in the same store.
1. Basil makes a table
Hermes plugin tool yui_tables
yui_tables(lines="""
table create foods Food:text Cal:number:kcal Protein:number:g
put foods oats Food=Oats Cal=300 Protein=10
put foods eggs Food="Two eggs" Cal=140 Protein=12
put foods Food=Toast Cal=80 Protein=3
put foods Cal=lots
query foods sort=-Protein""")5 lines done. Refused "put foods Cal=lots": put: Cal: "lots" is not a number.
foods
key | Food | Cal (kcal) | Protein (g)
eggs | Two eggs | 140 | 12
oats | Oats | 300 | 10
r1 | Toast | 80 | 32. Claude can't see it yet
MCP tools/call on yui-mcp
{"name": "yui_tables", "arguments": {"lines": "query foods"}}Refused "query foods": No table called foods yet.3. A delete asks you first
Hermes plugin tool yui_tables
yui_tables(lines="put foods oats +delete")Nothing deleted yet: the person sees "Delete Oats from foods?" with Delete or Keep.
(in Basil's thread) choose@del-86380ee2 "Delete Oats from foods?" Delete|Keep
(you tap Delete; Basil's next call)
The person tapped Delete on del-86380ee2: 1 gone.4. You give foods to Claude
As the person: yui_tables_give
yui_tables_give(from_agent=Basil, to_agent=Claude, tname="foods") -> "foods"(Basil) Refused "query foods": No table called foods yet.5. Claude reads the same rows
MCP tools/call on yui-mcp
{"name": "yui_tables", "arguments": {"lines": "query foods sort=-Protein"}}1 line done.
foods
key | Food | Cal (kcal) | Protein (g)
eggs | Two eggs | 140 | 12
r1 | Toast | 80 | 3Test: supabase/tests/tables_any_agent_e2e.py in the app repo, 21 of 21.
In the reply
An agent does not need a tool call. It writes table words in its own reply, and Yui takes them out before the reply is saved: the rows land, and each query becomes a screen with the real rows. From the live run.
Basil writes
Logged your breakfast.
```yui
table create foods Food:text Cal:number:kcal Protein:number:g
put foods oats Food=Oats Cal=300 Protein=10
put foods eggs Food="Two eggs" Cal=140 Protein=12
query foods sort=-Protein as table "Foods"
```Your phone gets
Logged your breakfast.
| Food | Calkcal | Proteing |
|---|---|---|
| Two eggs | 140 | 12 |
| Oats | 300 | 10 |
Logged your breakfast.
```yui
table name="Foods" Food|Cal|Protein "Two eggs|140|12" "Oats|300|10" units=|kcal|g
```A reply that only reads
Hermes plugin, reply path
```yui
query foods where=Food~oat
```(nothing saved; Basil's next turn)
[yui] Your tables:
foods
key | Food | Cal (kcal) | Protein (g)
oats | Oats | 300 | 10
[yui] Answer the person now: a line, then a screen.A delete in the reply asks first
Hermes plugin, reply path
Eggs are gone.
```yui
put foods eggs +delete
```(what the phone gets)
Tap Delete to confirm.
choose@del-14b00404 "Delete Two eggs from foods?" Delete|Keep
(you tap Delete; Basil's turn opens with)
[yui] The person tapped Delete on del-14b00404: 1 gone.An A2A agent reads with a data part
A2A bridge: the agent's answer
{"kind": "data", "data": {"yui": "tables", "lines": "query meals"}}(no text, so it is a read: the next message goes at once)
[yui] Your tables:
meals
key | Food | Cal (kcal)
r1 | Oats | 300
+ data part {"yui": "tables", "results": [...]}
(the agent answers, saved once)
Read 1 row(s): Oats 300A webhook reads with one field
Webhook bridge: your server's response
{"tables": "query meals"}(no reply, so it is a read: the bridge POSTs again at once)
"tables": {"results": [{"table": "meals", "cols": [{"name": "Food"}, {"name": "Cal", "unit": "kcal"}],
"rows": [["Oats", 300]], "count": 1}], "failed": []}Hand it over: the new agent knows what it holds
yui_tables_give, then your next message to Chef
yui_tables_give(from_agent=Basil, to_agent=Chef, tname="foods") -> "foods"(Chef's next turn opens with)
[yui] tables foods(2 rows: Food, Cal, Protein)Tests, live on throwaway accounts: supabase/tests/tables_reply_e2e.py 17 of 17 (Hermes), adapters/a2a/tests/a2a_e2e.py 81 of 81 (A2A 1.0, 0.3 and poll), adapters/webhook/tests/webhook_e2e.py 28 of 28 in Python and in Node.
- You own the table. One agent holds it at a time. Only that agent can read it, write it or draw it.
- Give to... moves it. Same name, same rows, same screens. The old agent stops seeing it.
- One path on the server. Hermes and MCP call
yui_tables, A2A sends a data part, a webhook returnstables, and any of them can just write table words in the reply. All land in the same store. - Next: Controls > Tables in the app, with Give to... and when each table was last read.