mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-10-25 01:43:24 +00:00
* packages: simplex-chat MCP server * update * standing approvals per chat, leave tool, simplify * fixes * add error messages * LICENSE * specification * migration * update simplex-chat, tests, fixes * update simplex-chat libraries * use agent peer type * better server and tool descriptions * short readme * reference * images * update readme * update readme * update * change links to master * fix links to point to master branch --------- Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
544 lines
25 KiB
Markdown
544 lines
25 KiB
Markdown
# SimpleX MCP server: flows
|
||
|
||
> Sequence diagrams of the main flows, each with notes and source locations.
|
||
>
|
||
> Related specs: [README](README.md) | [Architecture](architecture.md) | [API](api.md)
|
||
|
||
Participants:
|
||
|
||
| Letter | Participant |
|
||
|---|---|
|
||
| P | the person at the terminal |
|
||
| A | the agent in Claude Code |
|
||
| H | the HTTP listener with the MCP SDK |
|
||
| T | `AgentTools` |
|
||
| X | `Actions` |
|
||
| K | `Control` |
|
||
| C | the SimpleX core behind `ChatApi` |
|
||
| O | the owner in the SimpleX app |
|
||
| D | a peer |
|
||
|
||
`#G` is the agent control chat, `USER` the agent's user ID, `LINK` the link as `connFullLink [connShortLink]`. Chat commands are shown as the server sends them. Card and progress texts with a semicolon are shown with a comma in the diagrams; the exact texts are in the notes.
|
||
|
||
---
|
||
|
||
## Table of contents
|
||
|
||
1. [First start and the owner joins](#1-first-start-and-the-owner-joins)
|
||
2. [`register`](#2-register)
|
||
3. [`connect` with approval](#3-connect-with-approval)
|
||
4. [`send` with responses](#4-send-with-responses)
|
||
5. [`receive` with `wait`](#5-receive-with-wait)
|
||
6. [`send` under a standing approval, and `/revoke`](#6-send-under-a-standing-approval-and-revoke)
|
||
7. [`leave`](#7-leave)
|
||
8. [Mode `host`](#8-mode-host)
|
||
9. [Expiry](#9-expiry)
|
||
10. [A lost call and its re-attachment](#10-a-lost-call-and-its-re-attachment)
|
||
11. [Shutdown with pending actions](#11-shutdown-with-pending-actions)
|
||
12. [`--owners` setup](#12---owners-setup)
|
||
|
||
---
|
||
|
||
## 1. First start and the owner joins
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant P as Person
|
||
participant S as serve.ts
|
||
participant C as Core
|
||
participant K as Control
|
||
participant O as Owner
|
||
P->>S: simplex-mcp serve my-agent --display-name "My Agent"
|
||
S->>S: DATA mode 0700, lock, token, headers.json
|
||
S->>C: ChatApi.init DATA/simplex
|
||
S->>P: version line, data folder, approval mode
|
||
S->>C: /user (no active user)
|
||
S->>C: /_create user {"profile":{"displayName":"My Agent","fullName":"","peerType":"bot"},...}
|
||
S->>S: server.json
|
||
S->>C: /network socks=off smp-proxy=always smp-proxy-fallback=protected
|
||
S->>C: startChat, events buffered
|
||
S->>C: /_groups USER (no control marker)
|
||
S->>C: /_group USER {My Agent, history on, menu pending and list}
|
||
S->>C: /_set custom #G {"simplexMcp":{"control":true,"nextAction":1}}
|
||
S->>P: Agent control chat "My Agent" created.
|
||
S->>K: load
|
||
K->>C: /_members #G
|
||
S->>S: HTTP listens on 127.0.0.1:5226
|
||
K->>C: /_delete link #G (groupLinkNotFound ignored)
|
||
K->>C: /_create link #G member
|
||
K->>P: join line, QR code, link, QR hint, owner line
|
||
S->>P: Listening on http://127.0.0.1:5226/mcp
|
||
O->>C: joins by the link
|
||
C->>S: joinedGroupMember
|
||
S->>K: onEvent, queued
|
||
K->>C: /_members #G
|
||
K->>C: /_delete link #G
|
||
K->>C: /_member role #G 3 owner
|
||
K->>C: /_members #G
|
||
K->>O: I'm your agent. I ask here before I connect or send.
|
||
K->>P: Alice joined (member 3) and is now an owner. The link is deleted.
|
||
K->>P: Add the agent to Claude Code: npx -y @simplex-chat/mcp register my-agent
|
||
```
|
||
|
||
Notes:
|
||
|
||
- At first use, libsimplex and the Node.js add-on are downloaded, with one `Downloading ...` line each on stderr ([libs.ts:112-133](../../simplex-chat-nodejs/src/libs.ts#L112-L133)).
|
||
- With `--data`, `--data <folder>` is appended to the register hint, shell-quoted ([serve.ts:164-166](../src/serve.ts#L164-L166)).
|
||
- With `--qr-code small`, the line `If the QR code does not scan, ...` is printed after the link ([control.ts:307](../src/control.ts#L307)).
|
||
- The server runs `ownerJoined` for every `joinedGroupMember` event of a human, and the link is deleted in that handler ([control.ts:152-153](../src/control.ts#L152-L153), [:266-273](../src/control.ts#L266-L273)).
|
||
|
||
Source: [cli.ts:191-200](../src/cli.ts#L191-L200), [serve.ts:64-123](../src/serve.ts#L64-L123), [files.ts:12-44](../src/files.ts#L12-L44), [control.ts:374-385](../src/control.ts#L374-L385), [:88-103](../src/control.ts#L88-L103), [:236-240](../src/control.ts#L236-L240), [:301-311](../src/control.ts#L301-L311), [:146-155](../src/control.ts#L146-L155).
|
||
|
||
---
|
||
|
||
## 2. `register`
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant P as Person
|
||
participant R as register.ts
|
||
participant L as claude CLI
|
||
participant CC as Claude Code
|
||
participant H as HTTP
|
||
P->>R: simplex-mcp register my-agent [--scope local] [--data DIR]
|
||
R->>R: read DATA/server.json
|
||
alt server.json missing
|
||
R->>P: Agent "my-agent" was never started. Start it with: ... (exit 1)
|
||
end
|
||
R->>L: claude mcp remove --scope user simplex-my-agent
|
||
R->>L: claude mcp add-json --scope user simplex-my-agent {"type":"http","url":"http://127.0.0.1:5226/mcp","headersHelper":"cat DATA/headers.json","timeout":169800000}
|
||
alt claude not found
|
||
R->>P: Add this entry under "mcpServers" in ~/.claude.json: {...} (exit 1)
|
||
else add-json exit status other than 0
|
||
R->>P: claude mcp add-json failed: STDERR (exit 1)
|
||
end
|
||
R->>P: Added to Claude Code: scope user, server simplex-my-agent.
|
||
CC->>CC: headersHelper: cat DATA/headers.json
|
||
CC->>H: POST /mcp with Authorization: Bearer TOKEN
|
||
```
|
||
|
||
Notes:
|
||
|
||
- `simplex-mcp` ignores the exit status of `claude mcp remove` and stops at that step only when the `claude` binary is missing ([register.ts:35-36](../src/register.ts#L35-L36)).
|
||
- `timeout` is `(expireHours + 24) × 3600000 + 600000` ms from `server.json` ([register.ts:48](../src/register.ts#L48)).
|
||
- With `--scope local`, the project folder in the manual entry is the git repository root, the main repository of a worktree, or the current folder outside a repository ([register.ts:53](../src/register.ts#L53), [:57-81](../src/register.ts#L57-L81)).
|
||
|
||
Source: [cli.ts:103-118](../src/cli.ts#L103-L118), [:200](../src/cli.ts#L200), [register.ts:30-87](../src/register.ts#L30-L87), [files.ts:50-53](../src/files.ts#L50-L53).
|
||
|
||
---
|
||
|
||
## 3. `connect` with approval
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant A as Agent
|
||
participant H as HTTP and SDK
|
||
participant T as AgentTools
|
||
participant X as Actions
|
||
participant K as Control
|
||
participant C as Core
|
||
participant O as Owner
|
||
participant D as Peer
|
||
A->>H: tools/call connect {target: "@directory.simplex", wait, idle}
|
||
H->>T: validated input, abort signal, progress token
|
||
T->>X: find(key): none
|
||
T->>C: /_connect plan USER @directory.simplex
|
||
C-->>T: connectionPlan: contactAddress ok, planSimplexName
|
||
T->>T: classifyPlan: prepareContact with domain
|
||
T->>X: create(key, connect fields)
|
||
X->>K: refusal: none
|
||
X->>K: saveCounter: /_set custom #G
|
||
X->>K: postCard
|
||
K->>O: I want to connect to bot X (@directory.simplex), as myself. /'approve N' or /'reject N'
|
||
T->>X: decided(action, call)
|
||
X-->>A: progress: Action N waits for an approver until TIME.
|
||
O->>C: /approve N
|
||
C->>K: newChatItems
|
||
K->>X: approve(action, "Alice")
|
||
X->>T: performConnect
|
||
T->>C: /_connect plan USER @directory.simplex
|
||
T->>C: /_prepare contact USER LINK domain=directory.simplex {link data}
|
||
T->>C: /_connect contact @C incognito=off
|
||
C-->>T: startedConnectionToContact
|
||
D->>C: accepts the request
|
||
C->>T: contactConnected: markConnected, collect @C
|
||
T-->>X: perform ends
|
||
X->>K: outcome
|
||
K->>O: card edited: I connected to bot X (@directory.simplex), as myself. Approved by Alice.
|
||
T->>C: /_get chat @C count=50
|
||
T->>T: wait until idle or startedAt + wait
|
||
T->>C: /_read chat items @C, new item IDs
|
||
T->>X: remove(action)
|
||
T-->>A: {action: N, status: done, chat: @C, connected: true, messages}
|
||
```
|
||
|
||
Notes:
|
||
|
||
- The plan is read again in the perform step ([tools.ts:484](../src/tools.ts#L484)).
|
||
- Join commands per plan ([tools.ts:514-542](../src/tools.ts#L514-L542)):
|
||
|
||
| Plan | Commands |
|
||
|---|---|
|
||
| known prepared contact or group | `/_connect contact @C incognito=X [json M]`<br>`/_connect group #G incognito=X [json M]` |
|
||
| channel link (`direct` false, with link data) | `/_prepare group USER LINK direct=off [domain=D] JSON`<br>`/_connect group #G incognito=X` |
|
||
| contact address with link data, and a SimpleX name or a message | `/_prepare contact USER LINK [domain=D] JSON`<br>`/_connect contact @C incognito=X [json M]`, or `/_connect group #G ...` for a business |
|
||
| `contactViaAddress` | `/_connect contact USER incognito=X CONTACT_ID` |
|
||
| other links | `/_connect USER incognito=X LINK` |
|
||
|
||
- After a failed prepared connect, the server deletes the prepared chat with `notify=off` ([tools.ts:544-551](../src/tools.ts#L544-L551)).
|
||
- The connection is matched by `contactConnected` or `userJoinedGroup`: by contact or group ID, or by the pending connection ID ([tools.ts:387-395](../src/tools.ts#L387-L395), [:557-562](../src/tools.ts#L557-L562)).
|
||
- In the perform step the server waits for the other side until `startedAt + wait`, at most until `created + 23 h 50 min`, and then reports `connected: false` ([tools.ts:483](../src/tools.ts#L483), [:507-511](../src/tools.ts#L507-L511)).
|
||
- Without `message`, the idle time is counted from the connection; with `message`, from the first reply ([tools.ts:568](../src/tools.ts#L568), [messages.ts:225](../src/messages.ts#L225)).
|
||
- For a target the agent is connected to, the result is `status: known` with its chat fields, and no action is created ([tools.ts:417](../src/tools.ts#L417)).
|
||
- Exact progress texts: `Action N waits for an approver until TIME.`, then `Action N waits for the connection until TIME.`, then `K messages in CHAT; waiting until TIME.` ([actions.ts:296-299](../src/actions.ts#L296-L299), [tools.ts:452](../src/tools.ts#L452)).
|
||
|
||
Source: [tools.ts:412-464](../src/tools.ts#L412-L464), [:466-571](../src/tools.ts#L466-L571), [:811-874](../src/tools.ts#L811-L874), [actions.ts:119-183](../src/actions.ts#L119-L183), [control.ts:134-190](../src/control.ts#L134-L190).
|
||
|
||
---
|
||
|
||
## 4. `send` with responses
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant A as Agent
|
||
participant T as AgentTools
|
||
participant X as Actions
|
||
participant K as Control
|
||
participant C as Core
|
||
participant O as Owner
|
||
participant D as Peer
|
||
A->>T: send {chat: #15, text, reply_to, mention: [44], wait, idle, limit}
|
||
T->>C: /_get chat #15 count=0
|
||
T->>C: /_get chat #15 around=REPLY_TO count=0
|
||
T->>C: /_members #15
|
||
T->>K: standingApprover(chatInfo): none
|
||
T->>X: create(key, send fields)
|
||
X->>K: card size check and approvers
|
||
K->>O: I want to send a message to group X: `text` replying to ... mentioning @Switchback /'approve #15 N', /'approve #15 all' or /'reject #15 N'
|
||
O->>C: /approve #15 N
|
||
K->>X: approve(action, "Alice")
|
||
X->>T: performSend
|
||
T->>T: collect #15 items with ID above the sent item
|
||
T->>C: /_send #15 json [{msgContent, mentions, quotedItemId}]
|
||
C-->>T: sent item S
|
||
D->>C: replies S+1, S+2
|
||
C->>T: newChatItems: collected
|
||
X->>K: outcome
|
||
K->>O: card edited: I sent a message to group X: `text` ... Approved by Alice.
|
||
T->>T: wait until idle seconds after the last reply, or sentAt + wait
|
||
T->>C: /_read chat items #15 S+1,S+2
|
||
T->>X: remove(action)
|
||
T-->>A: {action: N, status: done, chat: #15, item: S, responses, more: 0}
|
||
```
|
||
|
||
Notes:
|
||
|
||
- Checks before the action: chat reference, role `observer` or `relay` refused, `reply_to` item present, mentioned members present and the chat a group ([tools.ts:573-581](../src/tools.ts#L573-L581)).
|
||
- The server starts collecting before the send and, after it, drops items with IDs up to the sent item ([tools.ts:612](../src/tools.ts#L612), [:622-624](../src/tools.ts#L622-L624)).
|
||
- Mentioned names missing from the text are prefixed as `@name` ([tools.ts:614-615](../src/tools.ts#L614-L615)).
|
||
- While a collected message is live, the server waits for its final update; live items are left out of `responses` and counted in `more` ([messages.ts:224-225](../src/messages.ts#L224-L225), [tools.ts:599](../src/tools.ts#L599), [:605](../src/tools.ts#L605)).
|
||
- At most 100 replies are kept per action; later ones are counted as overflow ([messages.ts:78-79](../src/messages.ts#L78-L79)).
|
||
|
||
Source: [tools.ts:573-629](../src/tools.ts#L573-L629), [messages.ts:71-83](../src/messages.ts#L71-L83), [:222-230](../src/messages.ts#L222-L230), [control.ts:448-453](../src/control.ts#L448-L453).
|
||
|
||
---
|
||
|
||
## 5. `receive` with `wait`
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant A as Agent
|
||
participant T as AgentTools
|
||
participant W as ChatItemsWatch
|
||
participant C as Core
|
||
participant D as Peer
|
||
A->>T: receive {chat: #12, wait: 3600, limit: 50}
|
||
T->>W: collect #12 into arrived
|
||
T->>C: /_get chat #12 initial=1
|
||
C-->>T: chatStats.unreadCount 0
|
||
T-->>A: progress: No new messages in #12, waiting until TIME.
|
||
D->>C: post, item 40
|
||
C->>W: newChatItems: arrived
|
||
T->>C: /_read chat items #12 40
|
||
T->>W: stop collecting
|
||
T-->>A: {chat: #12, messages: [40], more: 0}
|
||
```
|
||
|
||
Notes:
|
||
|
||
- With unread items the server reads `around=<minUnreadItemId> count=0`, then pages of `after=<last> count=100` until it holds `limit + 1` unread, non-live messages or a page is short ([messages.ts:183-194](../src/messages.ts#L183-L194)).
|
||
- Unread items other than messages in the read pages are marked read with the returned messages ([tools.ts:663](../src/tools.ts#L663), [:680](../src/tools.ts#L680)).
|
||
- `more`: the arrived messages not returned, the overflow and the unread items not read ([tools.ts:682](../src/tools.ts#L682)).
|
||
- Items received while the pages are read are returned once ([tools.ts:662-665](../src/tools.ts#L662-L665)).
|
||
- Exact progress text: `No new messages in CHAT; waiting until TIME.` ([tools.ts:669](../src/tools.ts#L669)).
|
||
|
||
Source: [tools.ts:653-686](../src/tools.ts#L653-L686), [messages.ts:183-220](../src/messages.ts#L183-L220).
|
||
|
||
---
|
||
|
||
## 6. `send` under a standing approval, and `/revoke`
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant A as Agent
|
||
participant T as AgentTools
|
||
participant X as Actions
|
||
participant K as Control
|
||
participant C as Core
|
||
participant O as Owner
|
||
O->>C: /approve #9 all (on a pending send card)
|
||
C->>K: newChatItems
|
||
K->>C: /_get chat #9 count=0
|
||
K->>C: /_set custom #9 {"simplexMcp":{"approvedBy":"Alice","approvedById":3,"approvedAt":"..."}}
|
||
K->>X: approve each pending send to #9 with a card
|
||
K->>O: Approved for business X (#9) by Alice. /'revoke #9'
|
||
A->>T: send {chat: #9, text}
|
||
T->>C: /_get chat #9 count=0
|
||
T->>K: standingApprover: Alice
|
||
T->>X: create(key, fields, "Alice"): performing, standing
|
||
X->>K: saveCounter (no card)
|
||
X->>T: performSend
|
||
T->>C: /_send #9 json [...]
|
||
X->>K: outcome
|
||
K->>O: I sent a message to business X: `text` Approved for this chat by Alice. /'revoke #9'
|
||
T-->>A: {action, status: done, chat: #9, item, responses, more}
|
||
O->>C: /revoke #9
|
||
K->>C: /_get chat #9 count=0
|
||
K->>C: /_set custom #9 {other keys}
|
||
K->>O: Messages to business X (#9) need approval again.
|
||
```
|
||
|
||
Notes:
|
||
|
||
- The approval is used in mode `requests` while member `approvedById` is an approver ([control.ts:113-116](../src/control.ts#L113-L116)).
|
||
- For sends that were pending at `/approve CHAT all`, the card ends with the plain `Approved by Alice.` ([control.ts:199-201](../src/control.ts#L199-L201), [:419](../src/control.ts#L419)).
|
||
- `connect` and `leave` actions are created without the standing approver, so they stay pending ([tools.ts:423](../src/tools.ts#L423), [:638](../src/tools.ts#L638), [commands.test.ts:360-369](../test/unit/commands.test.ts#L360-L369)).
|
||
|
||
Source: [control.ts:192-211](../src/control.ts#L192-L211), [tools.ts:586](../src/tools.ts#L586), [actions.ts:119-146](../src/actions.ts#L119-L146), [:223-240](../src/actions.ts#L223-L240).
|
||
|
||
---
|
||
|
||
## 7. `leave`
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant A as Agent
|
||
participant T as AgentTools
|
||
participant X as Actions
|
||
participant K as Control
|
||
participant C as Core
|
||
participant O as Owner
|
||
A->>T: leave {chat: #9}
|
||
T->>X: find(key): none
|
||
T->>C: /_get chat #9 count=0
|
||
T->>X: create(key, leave fields)
|
||
K->>O: I want to leave business X (#9). /'approve #9 N' or /'reject #9 N'
|
||
O->>C: /approve #9 N
|
||
K->>X: approve(action, "Alice")
|
||
X->>T: performLeave
|
||
T->>C: /_get chat #9 count=0
|
||
T->>C: /_leave #9 (membership current or pending)
|
||
T->>C: /_delete #9 full
|
||
X->>K: outcome
|
||
K->>O: card edited: I left business X (#9). Approved by Alice.
|
||
T-->>A: {action: N, status: done, chat: #9}
|
||
```
|
||
|
||
Notes:
|
||
|
||
- For a contact, the card text is `I want to delete KIND NAME (@N).`, and the perform step sends `/_delete @N full` ([actions.ts:280-282](../src/actions.ts#L280-L282), [tools.ts:647](../src/tools.ts#L647)).
|
||
- The delete mode is `full` without `notify=off`, so the core notifies the contact or the members ([types.js:76-81](../node_modules/@simplex-chat/types/dist/types.js#L76-L81)).
|
||
- The server looks for an attached action before it reads the chat, so a repeated call after the deletion is attached to the done action ([tools.ts:633-636](../src/tools.ts#L633-L636), [messages.test.ts:745-759](../test/unit/messages.test.ts#L745-L759)).
|
||
|
||
Source: [tools.ts:631-651](../src/tools.ts#L631-L651), [control.ts:454-457](../src/control.ts#L454-L457).
|
||
|
||
---
|
||
|
||
## 8. Mode `host`
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant P as Person
|
||
participant A as Agent
|
||
participant H as HTTP and SDK
|
||
participant T as AgentTools
|
||
participant X as Actions
|
||
participant K as Control
|
||
participant O as Owner
|
||
Note over P,H: the person started serve my-agent --approve host
|
||
A->>H: tools/list
|
||
H-->>A: connect, send, leave with _meta {"anthropic/requiresUserInteraction": true}
|
||
A->>A: Claude Code asks its user
|
||
A->>T: send {chat: @4, text}
|
||
T->>X: create: performing
|
||
X->>K: saveCounter (no card)
|
||
X->>T: performSend
|
||
X->>P: Action N: performed without approval (mode host).
|
||
X->>K: outcome
|
||
K->>O: I sent a message to bot X: `text` Performed without approval (mode host).
|
||
T-->>A: {action: N, status: done, ...}
|
||
```
|
||
|
||
Notes:
|
||
|
||
- New actions are still refused unless the control chat has an approver ([control.ts:107-110](../src/control.ts#L107-L110)).
|
||
- In mode `host`, `undefined` is returned by `standingApprover`, so standing approvals are ignored by `send`, `list` and `/list` ([control.ts:115](../src/control.ts#L115)).
|
||
- `_meta` is set on `connect`, `send` and `leave` only ([tools.ts:361](../src/tools.ts#L361)).
|
||
|
||
Source: [tools.ts:355-365](../src/tools.ts#L355-L365), [actions.ts:130](../src/actions.ts#L130), [:238](../src/actions.ts#L238), [control.ts:419](../src/control.ts#L419).
|
||
|
||
---
|
||
|
||
## 9. Expiry
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant A as Agent
|
||
participant X as Actions
|
||
participant K as Control
|
||
participant O as Owner
|
||
A->>X: send call creates pending action N at T
|
||
K->>O: card N
|
||
Note over X: expires = T + expireHours
|
||
loop every 60 s, and before each owner command
|
||
X->>X: expireDue
|
||
end
|
||
X->>X: pending and due: expired
|
||
X->>K: outcome
|
||
K->>O: card edited: ... Expired.
|
||
X-->>A: {action: N, status: expired}
|
||
X->>X: removed with the result
|
||
O->>K: /approve N
|
||
K->>O: Action N is not pending.
|
||
```
|
||
|
||
Notes:
|
||
|
||
- A decided action with an open count of 0 is removed at the first tick after its expiry time ([actions.ts:160-161](../src/actions.ts#L160-L161)).
|
||
- Expiry times are listed in the `/pending` reply ([control.ts:472-474](../src/control.ts#L472-L474)).
|
||
- The server ends the connect wait at `created + 23 h 50 min`, within the 24 hours in which a message can be edited ([tools.ts:112](../src/tools.ts#L112), [:483](../src/tools.ts#L483)).
|
||
|
||
Source: [actions.ts:97](../src/actions.ts#L97), [:153-164](../src/actions.ts#L153-L164), [control.ts:164](../src/control.ts#L164), [:433-434](../src/control.ts#L433-L434).
|
||
|
||
---
|
||
|
||
## 10. A lost call and its re-attachment
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant A as Agent
|
||
participant H as HTTP and SDK
|
||
participant T as AgentTools
|
||
participant X as Actions
|
||
participant O as Owner
|
||
A->>H: send {chat: @4, text: "t"}
|
||
H->>T: call
|
||
T->>X: create: pending, open 1
|
||
A->>H: closes the response stream
|
||
H->>T: ctx.mcpReq.signal aborted
|
||
T->>X: decided: false, open 0
|
||
Note over H: result discarded by the SDK
|
||
A->>T: list
|
||
T-->>A: actions: [{action: N, tool: send, input, state: pending, open: false}]
|
||
O->>X: /approve N
|
||
X->>X: perform, done, replies collected
|
||
A->>T: send {chat: @4, text: "t"}
|
||
T->>X: find(key): action N
|
||
T->>T: doneAtCall: wait counted from now
|
||
T->>X: remove(action)
|
||
T-->>A: {action: N, status: done, item, responses, more}
|
||
```
|
||
|
||
Notes:
|
||
|
||
- The key is the tool name with the action fields: `target`, `incognito`, `message` for `connect`; `chat`, `text`, `reply_to`, `mention` for `send`; `chat` for `leave` ([tools.ts:413](../src/tools.ts#L413), [:582](../src/tools.ts#L582), [:633](../src/tools.ts#L633), [:1003-1005](../src/tools.ts#L1003-L1005)).
|
||
- The `wait`, `idle` and `limit` of the attaching call are used ([tools.ts:593](../src/tools.ts#L593)).
|
||
- For a call aborted before `create`, the server returns before it creates an action ([tools.ts:422](../src/tools.ts#L422), [:585](../src/tools.ts#L585), [:637](../src/tools.ts#L637)).
|
||
- Abort path in the SDK: on `res` close the web request is aborted ([node index.cjs:299-303](../node_modules/@modelcontextprotocol/node/dist/index.cjs#L299-L303)); the per-request transport is closed ([server index.cjs:86-90](../node_modules/@modelcontextprotocol/server/dist/index.cjs#L86-L90)); the handler signal is aborted ([src-BhnGYybN.cjs:6425](../node_modules/@modelcontextprotocol/server/dist/src-BhnGYybN.cjs#L6425)).
|
||
|
||
Source: [tools.ts:431-442](../src/tools.ts#L431-L442), [:590-608](../src/tools.ts#L590-L608), [:694](../src/tools.ts#L694), [actions.ts:166-177](../src/actions.ts#L166-L177), [messages.ts:206-220](../src/messages.ts#L206-L220).
|
||
|
||
---
|
||
|
||
## 11. Shutdown with pending actions
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant P as Person
|
||
participant S as serve.ts
|
||
participant X as Actions
|
||
participant K as Control
|
||
participant H as HTTP
|
||
participant C as Core
|
||
participant O as Owner
|
||
P->>S: Ctrl-C (SIGINT)
|
||
S->>S: stopped: events ignored
|
||
par actions
|
||
S->>X: stop()
|
||
X->>X: stopping, timer cleared, cards being posted awaited
|
||
X->>X: pending and performing: interrupted
|
||
X->>K: outcome of each
|
||
K->>O: pending: ... Cancelled: the server stopped.
|
||
K->>O: performing: ... Approved by Alice, interrupted: the server stopped.
|
||
and HTTP
|
||
S->>H: close, closeAllConnections (open calls aborted)
|
||
end
|
||
S->>P: Cancelled 2 actions.
|
||
S->>C: /sql chat: agent connection IDs of #G members
|
||
S->>C: /sql agent: count of pending deliveries, every 100 ms, at most 10 s
|
||
S->>C: /_stop, close the store
|
||
S->>S: remove DATA/lock
|
||
S->>P: Stopped.
|
||
Note over P,O: next start: counter continues, /approve of an old number: Action N is not pending.
|
||
```
|
||
|
||
Notes:
|
||
|
||
- The interrupted card line is `DECIDER; interrupted: the server stopped.` ([control.ts:426](../src/control.ts#L426)).
|
||
- A host-mode action whose counter is being written at the stop is set to `pending` and cancelled without a perform ([actions.ts:226](../src/actions.ts#L226), [actions.test.ts:380-391](../test/unit/actions.test.ts#L380-L391)).
|
||
- After a crash, the cards of that run are left with their commands; `/approve` of their numbers gets the reply `Action N is not pending.` ([control.ts:182](../src/control.ts#L182)).
|
||
|
||
Source: [serve.ts:48-62](../src/serve.ts#L48-L62), [:150-176](../src/serve.ts#L150-L176), [actions.ts:200-209](../src/actions.ts#L200-L209), [http.ts:46-50](../src/http.ts#L46-L50), [control.ts:421-426](../src/control.ts#L421-L426).
|
||
|
||
---
|
||
|
||
## 12. `--owners` setup
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant P as Person
|
||
participant K as Control
|
||
participant C as Core
|
||
participant O as Owner Alice
|
||
participant B as Member Bob
|
||
P->>K: serve my-agent --owners
|
||
K->>C: /_create link #G observer
|
||
K->>P: The link adds an observer. Its ID and name are printed when it joins.
|
||
O->>C: joins by the link
|
||
C->>K: joinedGroupMember
|
||
K->>C: /_delete link #G
|
||
K->>P: Alice joined (member 3). Restart the server with --owners 3:Alice
|
||
K->>O: Alice joined (member 3). Restart the server with `--owners 3:Alice`
|
||
P->>K: Ctrl-C, then serve my-agent --owners 3:Alice
|
||
K->>C: /_member role #G 3 owner
|
||
K->>C: /_remove #G ID, for each other human
|
||
K->>C: /_delete link #G
|
||
K->>P: Owners: 3:Alice.
|
||
K->>P: Add the agent to Claude Code: ...
|
||
O->>C: adds Bob, makes Bob an owner
|
||
C->>K: memberRole
|
||
K->>P: Bob (member 7) is now an owner and can't approve actions yet. Restart ... --owners 3:Alice,7:Bob
|
||
K->>O: same line, the --owners part as code
|
||
B->>K: /approve N
|
||
K->>B: Only the owners listed in --owners approve actions.
|
||
```
|
||
|
||
Notes:
|
||
|
||
- Until the restart with a list, new actions are refused: `The agent has no approving owner. ...` ([control.ts:108-110](../src/control.ts#L108-L110)).
|
||
- An entry must match a human by member ID and display name, or the server exits with code 2 ([control.ts:249-253](../src/control.ts#L249-L253)).
|
||
- With `--reset-owners`, the server deletes the control chat for all members and creates a new one with the same counter; the CLI refuses the option with an `--owners` list ([control.ts:387-392](../src/control.ts#L387-L392), [cli.ts:97-99](../src/cli.ts#L97-L99)).
|
||
- When a listed owner leaves and none remains: `... is no longer an owner. No owners remain: restart the server with --reset-owners --owners` ([control.ts:288-293](../src/control.ts#L288-L293)).
|
||
|
||
Source: [control.ts:242-264](../src/control.ts#L242-L264), [:275-294](../src/control.ts#L275-L294), [:357-361](../src/control.ts#L357-L361), [:565-591](../src/control.ts#L565-L591).
|