* bots, nodejs: document chat, connection, member contact and network commands, typed commands in library * bots, nodejs: restore apiConnect, wrap domain once, test all connect responses * nodejs: JSDoc for new methods * docs: plan for bot API commands and typed Node library methods * tests: channel prepared with a domain has a verified SimpleX name * docs: apply review fixes to the plan for bot API commands * docs: apply second review pass to the plan for bot API commands * docs: refer the bot API plan to the rewritten test commit * docs: record decisions in the plan for bot API commands * docs: plan tests of the generated Python command strings * python: test the generated strings of the documented commands * docs: plan for files, /leave and library methods in the MCP server * docs: wait at stop in the MCP plan * docs: library methods and /leave mechanics in the MCP plan * mcp: typed library methods for chat commands * docs: plan the message information command for bots * mcp: /leave CHAT owner command to leave a chat at once * docs: file servers, GIF and video choices decided in the MCP plan * docs: file mechanics in the MCP plan * mcp: files of received messages * bots, nodejs: message information with the file servers for bots * docs: plan the relay approval option of receiving files in Node * mcp: download of received files with owner approval * docs: name the xftp-server test helper in the files plan * nodejs: receive files without approving unknown relays * docs: plan the user servers command for bots * bots, nodejs: user servers and relays by operator for bots * nodejs: document the message information method * docs: plan the file upload progress event for bots * mcp: send files with owner approval, refusing private keys * bots: file upload progress event for bots * mcp: /leave template in the agent control chat menu * docs: sent copies are deleted for contacts too, at sndFileCompleteXFTP * bots: drop the file upload progress event * docs: drop the upload progress event plan * nodejs: document user servers and relay approval of received files * mcp: download cards name unknown file servers after the upload * docs: record the wait for the file description in the files plan * mcp: fixed wait at stop for cancelled cards, without /sql * docs: record decision C4, a fixed wait at stop, in the files plan * ci: test the MCP server package on pull requests * ci: state when the MCP workflow can be dispatched * ci: run the MCP server tests when its libraries change * mcp: log the errors of image previews and resizing * mcp: keep files in transfer in the data folder * mcp: restrict sent files by the real paths of refused folders * mcp: validate the file at the send by its inode and change time * mcp: limit ffmpeg and ffprobe to local files and no input commands * mcp: ask the owner for unknown file servers; read approvals at creation * mcp: name downloaded files by their ID; return paths of downloads only * mcp: end each download with a result for the agent and the card * mcp: show files above their sender's size limit as failed * mcp: refuse empty files, as without file and captions too long to send * mcp: tell the owner about failed uploads and unposted received files * mcp: delete resized copies left by the last run at start * mcp: name downloads and leaving in the help and greeting, align docs * mcp: compute the download path for received files only * mcp: send a /receive file to the owner under its name * mcp: keep files received without a download in the chat's folder * mcp: keep a left chat's files and delete them with /delete CHAT files * mcp: test that sent images are resized by the core * mcp: wait 30 minutes for a download's progress before cancelling it * mcp: end a download as deleted only when its message is not found * mcp: delete the copy of a /receive file whose post fails * mcp: count the quoted content in a message's length, as the core sends it * mcp: name received bytes, unknown-server downloads and left chat files plainly * mcp: test refused folders, file changes and download approvals * docs: align MCP server docs with refused folders, leaves and download waits * mcp: move received files only in full, delete partials only of downloads * mcp: report a received file missing from its folder as failed * mcp: move received files of a group's member chats into its folder * mcp: read a video's preview and duration as an MP4 or QuickTime file * mcp: 0.2.0 on simplex-chat 7.1.0-beta.10.1 * mcp: file folders by chat type, /delete cancels downloads, files reply * docs: MCP server specs and plan for 0.2.0 * mcp: /receive gives the agent a file, /forward sends it to the owner * docs: MCP server specs for /receive and /forward of download cards * docs: MCP plan records the temp folder command as a library exception * mcp: /delete cancels resumed downloads; /forward quote fits the limit * docs: MCP specs and plans for /forward quotes and resumed downloads * docs: MCP specs name the source commit they are checked against * docs: MCP PLAN states the /forward quote and move rules * mcp: a download moves its completed file whatever its size * mcp: /receive N all covers its card and later files, not pending ones * docs: MCP files approval scope, reply text and README server wording * mcp: a download approves relays only for a card naming unknown servers * mcp: files in member chats of a group are tracked as in the main chat * docs: MCP relay approval of downloads and files in member chats * mcp: a rejected file gets a card again under the files approval * docs: MCP rejected files under the files approval, README scope * mcp: an offered file without a description gets no card or action * docs: MCP a file without a description is not downloaded * mcp: 0.2.0 on simplex-chat 7.1.0-beta.10.2 * ci: test the MCP server as a scope of build-linux
37 KiB
SimpleX MCP server: flows
Sequence diagrams of the main flows, each with notes and source locations.
Related specs: README | Architecture | API
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, DATA the data folder. Calls to the core are shown as the ChatApi methods the server calls, with their arguments in short. Card and progress texts with a semicolon are shown with a comma in the diagrams; the exact texts are in API.
Table of contents
- First start and the owner joins
registerconnectwith approvalsendwith responsesreceivewithwaitsendunder a standing approval, and/revokeleave- Mode
host - Expiry
- A lost call and its re-attachment
- Shutdown with pending actions
--ownerssetupdownloadwith approval/forwardsendwith a file/leaveand/delete CHAT files
1. First start and the owner joins
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: apiGetActiveUser (none)
S->>C: apiCreateActiveUser {displayName: "My Agent", fullName: "", peerType: agent}
S->>C: apiSetNetworkConfig smpProxyMode always, smpProxyFallback allowProtected
S->>C: /_temp_folder DATA/tmp
S->>S: DATA/files/sent deleted
S->>C: startChat, events buffered
S->>C: apiListGroups USER (no control marker)
S->>C: apiNewGroup USER {My Agent, history on, menu pending, list and leave}
S->>C: apiSetGroupCustomData #G {"simplexMcp":{"control":true,"nextAction":1}}
S->>P: Agent control chat "My Agent" created.
S->>K: load
K->>C: apiListMembers #G
S->>S: HTTP listens on 127.0.0.1:5226, /mcp answers 503 until the control chat is started
S->>S: server.json
K->>C: apiDeleteGroupLink #G (groupLinkNotFound ignored)
K->>C: apiCreateGroupLink #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: apiListMembers #G
K->>C: apiDeleteGroupLink #G
K->>C: apiSetMembersRole #G [3] owner
K->>C: apiListMembers #G
K->>O: I'm your agent. I ask here before I connect, send, download a file or leave a chat.
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:114-135). - With
--data,--data <folder>is appended to the register hint, shell-quoted (serve.ts:185-187). - With
--qr-code small, the lineIf the QR code does not scan, ...is printed after the link (control.ts:412). - Events that arrive before the HTTP listener starts are buffered and handled in order (serve.ts:234-248).
- The server runs
ownerJoinedfor everyjoinedGroupMemberevent of a human without--owners, and the link is deleted in that handler (control.ts:189-190, :371-378).
Source: cli.ts:198-209, serve.ts:65-126, :205-223, files.ts:12-48, http.ts:14-46, control.ts:102-117, :341-345, :406-425, :499-520.
2. register
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-mcpignores the exit status ofclaude mcp removeand stops at that step only when theclaudebinary is missing (register.ts:38-39).- On Windows, a missing
claudeis run again throughcmd.exe, with each argument quoted for it (register.ts:86-101). timeoutis(expireHours + 24) × 3600000 + 600000ms fromserver.json(register.ts:51).- 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:56, :60-84).
Source: cli.ts:110-125, :207, register.ts:33-58, files.ts:50-53.
3. connect with approval
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: apiConnectPlan USER @directory.simplex
C-->>T: contactAddress ok, planSimplexName
T->>T: classifyPlan: prepareContact with domain
T->>X: create(key, connect fields)
X->>K: refusal: none
X->>K: saveCounter: apiSetGroupCustomData #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: apiConnectPlan USER @directory.simplex
T->>C: apiPrepareContact USER LINK linkData directory.simplex
T->>C: apiConnectPreparedContact @C incognito false
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: apiGetChat @C count 50
T->>T: wait until idle or startedAt + wait
T->>C: apiChatItemsRead @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:690).
- Join methods per plan (tools.ts:719-747, :1260-1323):
| Plan | Methods |
|---|---|
| known prepared contact or group | apiConnectPreparedContact(C, incognito, message)apiConnectPreparedGroup(G, incognito, message) |
channel link (direct false, with link data) |
apiPrepareGroup(USER, LINK, false, linkData, domain)apiConnectPreparedGroup(G, incognito) |
| contact address with link data, and a SimpleX name or a message | apiPrepareContact(USER, LINK, linkData, domain)apiConnectPreparedContact(C, incognito, message), or apiConnectPreparedGroup for a business |
contactViaAddress |
apiConnectContactViaAddress(USER, incognito, CONTACT_ID) |
| other links | apiConnectPreparedLink(USER, incognito, LINK) |
- After a failed prepared connect, the server deletes the prepared chat without notifying (tools.ts:749-756).
- The connection is matched by
contactConnectedoruserJoinedGroup: by contact or group ID, or by the pending connection ID (tools.ts:590-598, :758-763). - In the perform step the server waits for the other side until
startedAt + wait, at most untilcreated + 23 h 50 min, and then reportsconnected: false(tools.ts:143, :689, :713-716). - Without
message, the idle time is counted from the connection; withmessage, from the first reply (tools.ts:769, messages.ts:323-331). - For a target the agent is connected to, the result is
status: knownwith its chat fields, and no action is created (tools.ts:622). - Exact progress texts:
Action N waits for an approver until TIME., thenAction N waits for the connection until TIME., thenK messages in CHAT; waiting until TIME.(actions.ts:343-354, tools.ts:658).
Source: tools.ts:617-672, :686-772, actions.ts:155-186, :206-223, control.ts:141-152.
4. send with responses
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: apiGetChat #15 count 0
T->>C: apiGetChat #15 around REPLY_TO count 0
T->>C: apiListMembers #15
T->>C: apiGetChat #15 count 0
T->>K: standingApprover(chatInfo, messages): 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: apiSendMessages #15 [{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: apiChatItemsRead #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:
textorfile, the chat connected, roleobserverorrelayrefused,reply_toitem a message, mentioned members present and the chat a group, and the message length with its quote (tools.ts:779-792). - The server starts collecting before the send and, after it, drops items with IDs up to the sent item (tools.ts:825, :838-840).
- Mentioned names missing from the text are prefixed as
@name(tools.ts:1424-1427). - While a collected message is live, the server waits for its final update; live items are left out of
responsesand counted inmore(messages.ts:323-331, tools.ts:811, :817). - At most 100 replies are kept per action; later ones are counted as overflow (messages.ts:10, :109-110).
Source: tools.ts:774-846, messages.ts:99-114, :323-331, control.ts:583-589.
5. receive with wait
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: apiGetChat #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: apiChatItemsRead #12 [40]
T->>W: stop collecting
T-->>A: {chat: #12, messages: [40], more: 0}
Notes:
- With unread items the server reads
aroundthe first unread item,count0, then pages ofafterthe last item,count100, until it holdslimit + 1unread, non-live messages or a page is short (messages.ts:285-299). - Unread items other than messages in the read pages are marked read with the returned messages (tools.ts:1125, :1142).
more: the arrived messages not returned, the overflow and the unread items not read (tools.ts:1144).- Items received while the pages are read are returned once (tools.ts:1124-1127).
- Exact progress text:
No new messages in CHAT; waiting until TIME.(tools.ts:1131).
Source: tools.ts:1114-1148, messages.ts:285-331.
6. send under a standing approval, and /revoke
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: apiGetChat #9 count 0
K->>C: apiSetGroupCustomData #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: apiGetChat #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: apiSendMessages #9 [...]
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: apiGetChat #9 count 0
K->>C: apiSetGroupCustomData #9 {other keys}
K->>O: Messages to business X (#9) need approval again.
Notes:
- The approval is used in mode
requestswhile memberapprovedByIdis an approver (control.ts:130-133). - For sends that were pending at
/approve CHAT all, the card ends with the plainApproved by Alice.(control.ts:554, :563). connectandleaveactions are created without the standing approver, so they stay pending (tools.ts:628, :942)./receive CHAT N alland/receive CHAT offwork the same way for downloads, with the keysimplexMcpFiles, except that download N alone is performed, and the other pending downloads of the chat stay pending (control.ts:22-25, :251-253, tools.ts:975).
Source: control.ts:243-263, :337-339, tools.ts:796-798, actions.ts:155-186, :265-283.
7. leave
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: apiGetChat #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: leaveChat(#9)
T->>C: apiGetChat #9 count 0
T->>C: apiLeaveGroup #9 (membership current or pending)
T->>C: apiDeleteChat #9 full, notify
T->>T: DATA/forwarded/group-9 deleted
X->>K: outcome
K->>O: card edited: I left business X (#9). Approved by Alice.
K->>O: Files from business X (#9) are kept in `DATA/files/group-9`: K files, SIZE. /'delete #9 files' (when the folder has files)
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 callsapiDeleteChatfor the contact, full, with notify (control.ts:590-593, tools.ts:951). - The delete notifies, so the core notifies the contact or the members (tools.ts:951, :954).
- 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:937-940).
Source: tools.ts:935-958, control.ts:145-152, :299-302.
8. Mode host
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, download, 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:124-127).
- In mode
host,standingApproverreturnsundefined, so standing approvals are ignored bysend,download,listand/list, and/approve CHAT allstores nothing; downloads get no card, so/receive CHAT N allhas no card to act on (control.ts:132, :213). - A download in mode
hostis received without the approval of unknown servers, and it gets no card and no outcome post (tools.ts:545, :1030, control.ts:495-497). - The kept-files message also follows a done
leavein modehost(control.ts:150-151).
Source: tools.ts:488-498, actions.ts:166, :281, control.ts:145-152, :554.
9. Expiry
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:200-201).
- Expiry times are listed in the
/pendingreply (control.ts:618-620). - 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:143, :689).
Source: actions.ts:129, :163, :193-204, control.ts:201, :227.
10. A lost call and its re-attachment
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,messageforconnect;chat,text,file,as,reply_to,mentionforsend;chat,itemfordownload;chatforleave(tools.ts:618, :776, :937, :962, :1554-1556). - The
wait,idleandlimitof the attaching call are used (tools.ts:805). - For a call aborted before
create, the server returns before it creates an action (tools.ts:627, :797, :941, :976). - Abort path in the SDK: on
resclose the web request is aborted (node index.cjs:299-303); the per-request transport is closed (server index.cjs:86-90); the handler signal is aborted (src-BhnGYybN.cjs:6425).
Source: tools.ts:636-648, :802-820, :1156, actions.ts:206-217, messages.ts:307-321.
11. Shutdown with pending actions
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, performs aborted
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->>P: Stopping: sending 2 cancelled cards, up to 10 s. Ctrl-C to stop now.
S->>S: wait 10 s, or until a second Ctrl-C
S->>C: close
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:561). - An action of mode
hostor of a standing approval whose counter is being written at the stop is set topendingand cancelled without a perform (actions.ts:265-270). - After a crash, the cards of that run are left with their commands;
/approveof their numbers gets the replyAction N is not pending.(control.ts:227). - The stop signal of the performs is aborted after the actions are marked interrupted: a
connectwaiting for its connection ends, and an interrupted perform logs no line (actions.ts:245-246, :280, tools.ts:713-714). - The wait applies only when cards were edited or posted at the stop, a
downloadwithout a card excluded, and--stop-waitis above 0 (serve.ts:177, :189-203).
Source: serve.ts:47-63, :171-181, :189-203, actions.ts:240-250, http.ts:48-52, control.ts:552-571.
12. --owners setup
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: apiCreateGroupLink #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: apiDeleteGroupLink #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: apiSetMembersRole #G [3] owner
K->>C: apiRemoveMembers #G [ID], for each other human
K->>C: apiDeleteGroupLink #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:125-127). - An entry must match a human by member ID and display name, folded to one line as the restart line prints it, or the server exits with code 2 (control.ts:354-358, :758-760).
- 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--ownerslist (control.ts:522-527, serve.ts:222, cli.ts:104-106). - 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:393-398).
Source: control.ts:347-369, :380-399, :406-416, :758-784.
13. download with approval
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: download {chat: #15, item: 30}
T->>X: find(key): none
T->>C: apiGetChat #15 count 0
T->>C: apiGetChat #15 around 30 count 0
T->>C: apiGetChatItemInfo #15 30: fileXftpServers
opt no servers yet
T-->>A: progress: The sender is uploading the file of item 30 in #15, waiting until TIME.
C->>T: rcvFileDescrReady
T->>C: apiGetChatItemInfo #15 30
end
T->>C: apiGetUserServers USER: no unknown servers
T->>C: apiGetChat #15 count 0
T->>K: standingApprover(chatInfo, files): none
T->>X: create(key, download fields)
K->>O: I want to download a file from Switchback in group Trail (#15): `report.pdf`, 2.4 MB, Servers: known. /'receive #15 N', /'receive #15 N all', /'forward #15 N' or /'reject #15 N'
O->>C: /receive #15 N
K->>X: approve(action, "Alice")
X->>T: performDownload: downloadFile
T->>C: apiGetChat #15 around 30 count 0
T->>C: apiReceiveFile 7 DATA/files/group-15/7.pdf
C->>T: rcvFileComplete
T->>T: a file at the download path: resolved
X->>K: outcome
K->>O: card edited: I downloaded a file from ... Approved by Alice.
T-->>A: {action: N, status: done, chat: #15, item: 30, path}
Notes:
- A check every 60 s reads the item: a deleted message or chat fails the download; 30 minutes without a rise of
rcvProgressfail and cancel it (tools.ts:1035-1062). - A download still running
waitseconds after the perform starts returnsstatus: performing; the same call again attaches to the action (tools.ts:1008-1011). /approve #15 Non a download card gets the replyAction N is a download: ...with the card's command line (control.ts:234).- A file on unknown servers gets a card that names their hosts and leaves out
/'receive CHAT N all'(control.ts:594-599, :611-614, :664-668). Only such a card approves the relays; the core checks the complete description of every other download, which fails at the acceptance, or at the next check when the description completes later (tools.ts:545, :1043). - The card of an image or video shows the sender's preview (control.ts:441-461).
- A file the core receives without a request, a short voice message, also in a member chat of a group, is moved into the chat's folder at
rcvFileComplete; one whose transfer started before a restart, only when it has its full size. Its message then shows it asoffered, anddownloadreturns its path after the approval (tools.ts:551-571, :1082-1091, messages.ts:194-208).
Source: tools.ts:960-1103, control.ts:573-616, serve.ts:139-155.
14. /forward
sequenceDiagram
participant A as Agent
participant T as AgentTools
participant K as Control
participant C as Core
participant O as Owner
O->>C: /forward #15 N (on a pending download card)
C->>K: newChatItems
K->>T: forwardFile(action N)
T->>C: apiReceiveFile 7 DATA/forwarded/group-15/7.pdf
C->>T: rcvFileComplete: resolved with the forwarded path
T->>T: copy for the post: DATA/files/sent/send-XXXXXX/report.pdf
K->>C: apiSendMessages #G [{fileSource: the copy, msgContent: file, quotedItemId: card N when the message fits}]
C->>T: sndFileCompleteXFTP: the copy's folder deleted
Note over K,O: the card stays pending; the agent's message shows the file as offered
O->>C: /receive #15 N
K->>T: performDownload: downloadFile
T->>T: the forwarded file moved to DATA/files/group-15/7.pdf
T-->>A: {action: N, status: done, chat: #15, item: 30, path}
Notes:
/forwardgets no reply; a download or post that fails is posted asAction N: the file was not received: `ERROR`., and a failed post deletes the copy (control.ts:265-281).- The copy is named by the name the sender gave (tools.ts:898-902).
- The post keeps the sender's preview of an image or video; it replies to the card when the message, with the card as its quote, is within 15602 bytes by the length rule of
send(control.ts:265-281, tools.ts:1433-1449). - The download is shared with the agent's: one download per file ID; the move to the download path waits for the copy of the post (tools.ts:522-549).
- The forwarded path is outside
DATA/files/, sosendrefuses the file there; it is deleted when the agent leaves the chat, and by/delete CHAT files(tools.ts:871-876, :909-927, :950-958).
Source: control.ts:221-239, :265-281, tools.ts:522-539, :848-858, :898-902.
15. send with a file
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: send {chat: #15, text: "trail map", file: "/home/alice/trail.jpg"}
T->>C: apiGetChat #15 count 0
T->>T: real path, size, inode, change time
T->>T: refused paths checked
T->>T: preview: core.resizeImage(file, MessagePreview)
T->>T: message length checked
T->>X: create(key, send fields with the file)
K->>O: image card with the preview: I want to send a message to group Trail: `trail map`, attaching image `/home/alice/trail.jpg`, SIZE /'approve #15 N', ...
O->>C: /approve #15 N
X->>T: performSend
T->>T: the file again: same real path, size, inode, change time
T->>T: core.resizeImage(file, Image): DATA/files/sent/send-XXXXXX/trail.jpg
T->>C: apiSendMessages #15 [{fileSource: the copy, msgContent: image with the preview}]
C->>T: sndFileCompleteXFTP: the copy's folder deleted
Notes:
- A refused file ends the call with an error, and the refusal is posted to the control chat and logged (tools.ts:862-865, control.ts:154-158).
- A video's preview is the first frame from
ffmpegand its duration comes fromffprobe; a GIF or WebP gets the fixed preview (tools.ts:1512-1546). - A changed file fails the perform:
file: PATH changed after the request.(tools.ts:878-883). sndFileErrordeletes the copy too, and the failure is posted to the control chat and logged (serve.ts:158-160, control.ts:160-169).
Source: tools.ts:774-800, :822-896, control.ts:441-461, :583-589.
16. /leave and /delete CHAT files
sequenceDiagram
participant P as Person
participant T as AgentTools
participant X as Actions
participant K as Control
participant C as Core
participant O as Owner
participant D as Peer
O->>C: /leave #9
K->>T: knownChat #9
K->>T: leaveChat #9
T->>C: apiLeaveGroup #9, apiDeleteChat #9 full, notify
C->>D: the agent left the group
T->>T: DATA/forwarded/group-9 deleted
K->>P: Left business Ridgeline (#9), requested by Alice.
K->>X: reject each pending action of #9 with a card
K->>O: Left business Ridgeline (#9), requested by Alice.
K->>O: Files from business Ridgeline (#9) are kept in `DATA/files/group-9`: 3 files, 2.1 MB. /'delete #9 files'
O->>C: /delete #9 files
K->>T: deleteChatFiles #9
T->>T: each download of #9: failed, apiCancelFile, its partial file deleted
T->>C: apiGetChat #9 count 100, then before the first item, while a page has 100
T->>C: apiCancelFile for each other file in rcvAccepted, rcvTransfer or rcvWarning
T->>T: DATA/files/group-9 and DATA/forwarded/group-9 deleted
K->>O: Deleted 3 files from #9, 2.1 MB.
Notes:
/leaveacts at once in both modes, and works for a contact asDeleted bot X (@4), requested by Alice.(control.ts:283-297).- The line
Left ...goes to the log and to the owners; the members of #9 see only that the agent left. - The files message is posted after the reply, and only when the folder has files (control.ts:294-295, :309-311).
/delete CHAT filesworks for a current or a left chat; the failed downloads returnYour owner deleted the files of this chat., and the reply counts the forwarded files too (tools.ts:909-927).- The paged reads find the downloads the core continues after a restart, which have no download in memory; for a left chat they find none (messages.ts:271-279, tools.ts:916-922).
Source: control.ts:283-307, tools.ts:904-927, :950-958.