Files
EvgenyandEvgeny @ SimpleX Chat 46eb5cc001 packages: SimpleX Chat MCP server (#7752)
* 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>
2026-10-19 22:57:25 +01:00

473 lines
31 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SimpleX MCP server: API
> The five MCP tools, the agent control chat commands, replies and cards, and the CLI of `@simplex-chat/mcp` 0.1.0.
>
> Related specs: [README](README.md) | [Architecture](architecture.md) | [Flows](flows.md)
**Source:** [`tools.ts`](../src/tools.ts) | [`control.ts`](../src/control.ts) | [`cli.ts`](../src/cli.ts) | [`messages.ts`](../src/messages.ts) | [`actions.ts`](../src/actions.ts)
---
## Table of contents
1. [Tools](#1-tools)
- [1.1 Common rules](#11-common-rules)
- [1.2 `connect`](#12-connect)
- [1.3 `send`](#13-send)
- [1.4 `receive`](#14-receive)
- [1.5 `list`](#15-list)
- [1.6 `leave`](#16-leave)
- [1.7 Message and command objects](#17-message-and-command-objects)
- [1.8 Action refusals](#18-action-refusals)
2. [Agent control chat](#2-agent-control-chat)
- [2.1 Commands and replies](#21-commands-and-replies)
- [2.2 Cards](#22-cards)
- [2.3 Membership messages](#23-membership-messages)
- [2.4 Group profile](#24-group-profile)
3. [CLI](#3-cli)
- [3.1 `serve`](#31-serve)
- [3.2 `register`](#32-register)
- [3.3 Exit codes and messages](#33-exit-codes-and-messages)
- [3.4 Terminal lines](#34-terminal-lines)
---
## 1. Tools
### 1.1 Common rules
| Rule | Code |
|---|---|
| Tools are registered on each per-request `McpServer` with description, input schema, output schema and, in mode `host`, `_meta: {"anthropic/requiresUserInteraction": true}` for `connect`, `send` and `leave` | [tools.ts:355-365](../src/tools.ts#L355-L365) |
| Tool descriptions and server instructions; the instructions name the agent's display name | [tools.ts:301-332](../src/tools.ts#L301-L332), [serve.ts:109](../src/serve.ts#L109) |
| Arguments are validated by the SDK with Ajv; a failure is a result with `isError: true` and the text `Input validation error: Invalid arguments for tool NAME: ERRORS` | [mcp-BumlNux0.cjs:1812-1817](../node_modules/@modelcontextprotocol/server/dist/mcp-BumlNux0.cjs#L1812-L1817) |
| `additionalProperties: false` is set in every input schema | [tools.ts:177](../src/tools.ts#L177), [:192](../src/tools.ts#L192), [:203](../src/tools.ts#L203), [:206](../src/tools.ts#L206), [:214](../src/tools.ts#L214) |
| A result is sent as `structuredContent` and as its JSON in one text item | [tools.ts:370](../src/tools.ts#L370) |
| A tool error is sent as `isError: true` with one text item: the `ToolError` text, or the `errorText` of other errors | [tools.ts:372](../src/tools.ts#L372), [log.ts:39-43](../src/log.ts#L39-L43) |
| With a `progressToken` in `_meta`, `notifications/progress` is sent at the start of each wait and every 60 s, with `progress` 0, 1, 2, ... and a `message` | [tools.ts:1011-1021](../src/tools.ts#L1011-L1021), [messages.ts:200-204](../src/messages.ts#L200-L204) |
| The call is aborted when the client closes the response stream or sends `notifications/cancelled`; the action is kept | [src-BhnGYybN.cjs:6338-6341](../node_modules/@modelcontextprotocol/server/dist/src-BhnGYybN.cjs#L6338-L6341), [:6425](../node_modules/@modelcontextprotocol/server/dist/src-BhnGYybN.cjs#L6425) |
| A call with the same tool and action fields as an action in memory is attached to it | [tools.ts:1003-1005](../src/tools.ts#L1003-L1005) |
| Messages are marked read after the last wait of a call that is not aborted | [tools.ts:460-461](../src/tools.ts#L460-L461), [:598-600](../src/tools.ts#L598-L600), [:679-680](../src/tools.ts#L679-L680) |
Action fields of the key:
| Tool | Fields |
|---|---|
| `connect` | `target`<br>`incognito` (default `false`)<br>`message` (default `null`) |
| `send` | `chat`<br>`text`<br>`reply_to` (default `null`)<br>`mention` (default `null`) |
| `leave` | `chat` |
Result status of the three action tools:
| Status | Meaning | Extra fields |
|---|---|---|
| `done` | performed | per tool |
| `failed` | failure of the card post, the counter write or the perform, or loss of the control chat | `error` |
| `rejected` | an approver sent `/reject` | |
| `expired` | no decision before the expiry time | |
| `known` | `connect` only: the target is an existing chat past the prepared state; no action | chat fields |
### 1.2 `connect`
Handler: [`AgentTools.connect`](../src/tools.ts#L412-L425). Perform: [`performConnect`](../src/tools.ts#L480-L512). Schemas: [input](../src/tools.ts#L167-L178), [output](../src/tools.ts#L220-L230).
Input:
| Field | Type | Range | Default | Rule |
|---|---|---|---|---|
| `target` | string | 1 character or more | required | SimpleX link (`https://...`, `simplex:/...`), contact name `@name.simplex` or channel name `#name.simplex`; whitespace refused |
| `incognito` | boolean | | `false` | `true`: `incognito=on` in the connect command |
| `message` | string | 1 to 2000 characters | | first message of a contact request; plans with `takesMessage` only |
| `wait` | number | 0 to 86400 | 60 | seconds from the approval: for the other side to accept and, with `message`, for its first reply |
| `idle` | number | 1 to 300 | 30 | seconds without a new message, after which the server ends the wait for messages |
Output fields by case:
| Case | Fields | Code |
|---|---|---|
| `known` | `status`, chat fields of the known chat | [tools.ts:417](../src/tools.ts#L417) |
| `done`, chat unknown | `action`, `status`, `type`, `connected: false`, plan fields `name`, `peer_type`, `description`, `welcome`, `commands`, `known_as` when present | [tools.ts:428](../src/tools.ts#L428), [:899-905](../src/tools.ts#L899-L905) |
| `done`, chat known | `action`, `status`, chat fields, `messages` | [tools.ts:444-464](../src/tools.ts#L444-L464) |
| `failed` | `action`, `status`, `error` | [tools.ts:435](../src/tools.ts#L435) |
| `rejected`, `expired` | `action`, `status` | [tools.ts:435](../src/tools.ts#L435) |
Chat fields ([`chatFields`](../src/tools.ts#L740-L767)):
| Field | Contact | Group, business chat or channel |
|---|---|---|
| `chat` | `@contactId` | `#groupId` |
| `type` | `contact` | `business` with `businessChat`; `channel` with `useRelays` or group type `channel`; otherwise `group` |
| `name` | profile display name | group display name |
| `simplex_name` | `@` + `contactDomain.domain` when verified | `#` + `groupDomainClaim.domain` when verified |
| `peer_type` | profile peer type, when present | from the plan, when present |
| `description` | profile `shortDescr` | group `shortDescr` |
| `welcome` | from the plan: link data message | group `description` |
| `commands` | profile `preferences.commands` | `groupPreferences.commands` |
| `known_as` | incognito: `customUserProfile` of `/_info @C` | incognito: the membership's display name |
| `role` | | the agent's member role |
| `connected` | active connection status `ready` | membership status `connected` or `complete` |
`messages`: message items among the last 50 items of the chat when the result is built, replaced by their collected copies, followed by the items collected since the connection; live items are left out; new items are marked read ([tools.ts:446](../src/tools.ts#L446), [:456-463](../src/tools.ts#L456-L463)).
Tool errors:
| Text | Condition | Code |
|---|---|---|
| `Not a SimpleX link or name: TARGET` | whitespace in `target`, or `invalidConnReq` from the plan command | [tools.ts:468](../src/tools.ts#L468), [:474](../src/tools.ts#L474) |
| an app error text, for example `Name not found: This SimpleX name is not registered. Please check the name.`, or `SimpleX error: CHAIN: DETAILS` | other errors of the plan command, or a plan of type `error` | [tools.ts:475](../src/tools.ts#L475), [:826](../src/tools.ts#L826), [log.ts:45-47](../src/log.ts#L45-L47) |
| `TARGET can't be joined now: PLAN.` | `invitationLink ownLink` or `connecting`<br>`contactAddress ownLink` or `connectingProhibit`<br>`groupLink ownLink`, `connectingProhibit`, `noRelays` or `updateRequired` | [tools.ts:817](../src/tools.ts#L817), [:830](../src/tools.ts#L830), [:848](../src/tools.ts#L848), [:870](../src/tools.ts#L870) |
| `message: TARGET is not a contact or business address with short link data, or its contact exists without a connection.` | `message` given and the plan has `takesMessage: false` | [tools.ts:419](../src/tools.ts#L419) |
| refusals | see [1.8](#18-action-refusals) | |
`takesMessage` is `true` for a contact address with short link data, for a known prepared contact, and for a known prepared business group ([tools.ts:821](../src/tools.ts#L821), [:841](../src/tools.ts#L841), [:859](../src/tools.ts#L859)).
### 1.3 `send`
Handler: [`AgentTools.send`](../src/tools.ts#L573-L588). Result: [`sendResult`](../src/tools.ts#L590-L608). Perform: [`performSend`](../src/tools.ts#L610-L629). Schemas: [input](../src/tools.ts#L180-L193), [output](../src/tools.ts#L232-L244).
Input:
| Field | Type | Range | Default | Rule |
|---|---|---|---|---|
| `chat` | string | | required | `@N` or `#N` of the agent |
| `text` | string | 1 to 12000 characters | required | sent as one text message |
| `reply_to` | integer | 1 or more | | item ID in `chat` |
| `mention` | integer array | 1 to 3, unique, each 1 or more | | member IDs of a group; names missing from `text` are prefixed as `@name` |
| `wait` | number | 0 to 86400 | 60 | seconds from the send; from the call when it attaches to a done action |
| `idle` | number | 1 to 300 | 30 | seconds without a new response, once one arrived |
| `limit` | integer | 1 to 100 | 50 | most responses returned |
Output:
| Field | Presence | Value |
|---|---|---|
| `action` | always | action number |
| `status` | always | `done`, `failed`, `rejected`, `expired` |
| `chat` | `done` | the chat reference |
| `item` | `done` | item ID of the sent message |
| `responses` | `done` | received message items with IDs above `item`, in arrival order, live items left out, at most `limit` |
| `more` | `done` | collected responses not returned, live ones and overflow included |
| `error` | `failed` | error text |
Tool errors:
| Text | Condition | Code |
|---|---|---|
| `No chat CHAT. list shows the agent's chats.` | invalid reference, the control chat, or a store error when the chat is read | [tools.ts:714-721](../src/tools.ts#L714-L721) |
| `The agent has role ROLE in CHAT and can't post.` | membership role `observer` or `relay` | [tools.ts:576-579](../src/tools.ts#L576-L579) |
| `reply_to: CHAT has no item N.` | the read of `around=N` fails, or the item is absent from it | [tools.ts:723-728](../src/tools.ts#L723-L728) |
| `mention: CHAT is a contact chat; mentions apply to groups and channels.` | `mention` with `@N` | [tools.ts:731](../src/tools.ts#L731) |
| `mention: CHAT has no member N.` | ID absent from `/_members` | [tools.ts:735](../src/tools.ts#L735) |
| refusals | see [1.8](#18-action-refusals) | |
### 1.4 `receive`
Handler: [`AgentTools.receive`](../src/tools.ts#L653-L686). Schemas: [input](../src/tools.ts#L195-L204), [output](../src/tools.ts#L246-L254).
Input:
| Field | Type | Range | Default | Rule |
|---|---|---|---|---|
| `chat` | string | | required | `@N` or `#N` of the agent |
| `wait` | number | 0 to 86400 | 0 | seconds to wait when no message is new |
| `limit` | integer | 1 to 100 | 50 | most messages returned |
Output (all fields always present):
| Field | Value |
|---|---|
| `chat` | the chat reference |
| `messages` | unread received messages in chat order, then received messages that arrived during the call; live items left out; at most `limit` |
| `more` | new messages not returned, overflow, and unread items left unread by the page reads |
Tool errors: `No chat CHAT. list shows the agent's chats.` for an invalid reference, the control chat, or a store error while the unread items are read ([tools.ts:655](../src/tools.ts#L655), [:659-661](../src/tools.ts#L659-L661)); other chat errors as their error text ([log.ts:39-47](../src/log.ts#L39-L47)).
Messages are read by `receive` without an action or an approval.
### 1.5 `list`
Handler: [`AgentTools.listChats`](../src/tools.ts#L688-L696). Schemas: [input](../src/tools.ts#L206), [output](../src/tools.ts#L256-L288).
Input: an empty object.
Output:
| Field | Item fields |
|---|---|
| `chats` | `chat`<br>`type`<br>`name`<br>`simplex_name`, when verified<br>`peer_type`, when present<br>`incognito`<br>`known_as`, when incognito<br>`connected`<br>`unread`: `chatStats.unreadCount`<br>`approved: true`, only under a standing approval in mode `requests` |
| `actions` | `action`<br>`tool`<br>`input`: the input of the creating call<br>`state`<br>`open`: `true` while a call waits on it |
`chats`: the contacts and groups from `/_get chats USER pcc=off`, paged by 100 with `before=CHAT_TS`, except the control chat ([tools.ts:698-712](../src/tools.ts#L698-L712)). `actions`: every action in memory ([tools.ts:694](../src/tools.ts#L694)).
### 1.6 `leave`
Handler: [`AgentTools.leave`](../src/tools.ts#L631-L640). Perform: [`performLeave`](../src/tools.ts#L646-L651). Schemas: [input](../src/tools.ts#L208-L215), [output](../src/tools.ts#L290-L299).
Input: `chat`, string, required, `@N` or `#N` of the agent.
Output:
| Field | Presence | Value |
|---|---|---|
| `action` | always | action number |
| `status` | always | `done`, `failed`, `rejected`, `expired` |
| `chat` | `done` | the deleted chat |
| `error` | `failed` | error text |
Perform:
| Chat | Commands |
|---|---|
| `@N` | `/_delete @N full` |
| `#N` | `/_leave #N` while the membership is current, `pending_approval` or `pending_review`<br>`/_delete #N full` |
Tool errors: `No chat CHAT. list shows the agent's chats.` when no action with the same key is in memory and the chat is invalid, the control chat or unknown ([tools.ts:634-636](../src/tools.ts#L634-L636)); refusals.
### 1.7 Message and command objects
Message ([`Message`](../src/messages.ts#L11-L22), [`toMessage`](../src/messages.ts#L135-L152), [schema](../src/tools.ts#L119-L139)):
| Field | Type | Presence | Source |
|---|---|---|---|
| `chat` | string | always | chat reference of the item |
| `item` | integer | always | `meta.itemId` |
| `from` | string | always | `groupRcv`: member display name<br>`directRcv`: contact display name<br>`channelRcv`: channel display name<br>sent items in `connect`: the agent's name in that chat |
| `from_member` | integer | `groupRcv` | `groupMemberId` |
| `from_type` | string | when the profile has a peer type | sender's `peerType`; the agent's own for its non-incognito items in `connect` |
| `time` | string | always | `meta.itemTs` |
| `text` | string | always | `msgContent.text` |
| `new` | boolean | always | item status `rcvNew` when read |
| `reply_to` | object | quoted item with an ID | `{chat, item: quotedItem.itemId}` |
| `mentions_me` | boolean | always | `meta.userMention` |
Only received message items (`rcvMsgContent`) are returned by `send` and `receive`; `connect` also returns sent ones (`sndMsgContent`) ([messages.ts:73](../src/messages.ts#L73), [:123-125](../src/messages.ts#L123-L125)).
Command ([`commandsOf`](../src/tools.ts#L985-L992), [schema](../src/tools.ts#L141-L151)):
| Form | Fields |
|---|---|
| command | `keyword`<br>`label`<br>`params`, when present |
| menu | `label`<br>`commands`: commands and menus |
### 1.8 Action refusals
Refusals of a new `connect`, `send` or `leave` action, first match ([actions.ts:136](../src/actions.ts#L136), [control.ts:105-111](../src/control.ts#L105-L111)):
| Text | Condition |
|---|---|
| `The server is stopping.` | `Actions.stop` has started |
| `The agent lost its control chat. The person who runs it has to restart it with --reset-owners.` | control chat lost |
| `20 actions are pending; wait for the owner's decisions.` | 20 actions in state `pending` |
| `text: too long for its approval card; shorten it.` | `send` whose largest card form exceeds 15000 bytes |
| `The agent has no owner. The person who runs it has to join its control chat with the link the server printed.` | no approver, `--owners` absent |
| `The agent has no approving owner. The person who runs it has to restart it with the --owners options it printed.` | no approver, `--owners` given |
Largest card form ([control.ts:476-484](../src/control.ts#L476-L484)): the maximum of the creation form, the `done` form and a `failed` form with an error of 600 × `€`, with the action's own decider or the longest current approver name; each form is measured as JSON-escaped UTF-8: `"`, `\`, `\n`, `\r`, `\t` 2 bytes, other characters below U+0020 6 bytes.
---
## 2. Agent control chat
### 2.1 Commands and replies
Accepted text ([`parseCommand`](../src/control.ts#L410-L415), [`ownerRequest`](../src/control.ts#L521-L530)):
```
message one line, an optional trailing newline
line spaces, "/", KEYWORD, PARAMS
KEYWORD characters up to the first whitespace
PARAMS the rest, trimmed, split on whitespace
CHAT @N or #N, 1 to 15 digits
requests
approve N
approve CHAT N
reject N
reject CHAT N
approve CHAT all
revoke CHAT
pending
list
```
The server ignores other text. It answers a command with a reply that quotes it ([control.ts:142](../src/control.ts#L142)), and it runs the expiry check before each command ([control.ts:164](../src/control.ts#L164)).
| Request | Condition | Reply | Code |
|---|---|---|---|
| one-line `/` text outside the requests | any sender | `Commands: /approve, /reject, /revoke, /pending, /list. Each card shows its commands.` | [control.ts:15](../src/control.ts#L15), [:166](../src/control.ts#L166) |
| any request | sender not an approver, `--owners` absent | `Only owners approve actions.` | [control.ts:167](../src/control.ts#L167) |
| any request | sender not an approver, `--owners` given | `Only the owners listed in --owners approve actions.` | [control.ts:167](../src/control.ts#L167) |
| `approve`, `reject` | action N absent, not `pending`, or without a card | `Action N is not pending.` | [control.ts:182](../src/control.ts#L182) |
| `approve CHAT N`, `reject CHAT N` | action N is a `connect`, or for another chat | `Action N is not for CHAT.` | [control.ts:183](../src/control.ts#L183) |
| `approve`, `reject` | accepted | none; the card is edited at the outcome | [control.ts:184-187](../src/control.ts#L184-L187) |
| `approve CHAT all` | CHAT unknown or the control chat | `No chat CHAT. /list shows the agent's chats.` | [control.ts:194](../src/control.ts#L194) |
| `approve CHAT all` | accepted | `Approved for KIND NAME (CHAT) by APPROVER. /'revoke CHAT'` | [control.ts:202](../src/control.ts#L202) |
| `revoke CHAT` | CHAT unknown, or without a standing approval | `Messages to CHAT have no standing approval.` | [control.ts:207](../src/control.ts#L207) |
| `revoke CHAT` | accepted | `Messages to KIND NAME (CHAT) need approval again.` | [control.ts:210](../src/control.ts#L210) |
| `pending` | no pending action with a card | `No pending actions.` | [control.ts:171](../src/control.ts#L171) |
| `pending` | otherwise | one line per action: `N WHAT expires YYYY-MM-DD HH:MM UTC` | [control.ts:170](../src/control.ts#L170), [:472-474](../src/control.ts#L472-L474) |
| `list` | no chats | `No chats.` | [control.ts:220](../src/control.ts#L220) |
| `list` | otherwise | one line per chat: `CHAT KIND NAME`, then `, approved for this chat by APPROVER` and `, not connected` when applicable | [control.ts:213-221](../src/control.ts#L213-L221) |
KIND is `contact`, `bot`, `agent`, `business`, `group` or `channel` ([tools.ts:928-935](../src/tools.ts#L928-L935)). WHAT is `connect to SUBJECT`, `send to SUBJECT`, `delete SUBJECT` or `leave SUBJECT` ([actions.ts:268-278](../src/actions.ts#L268-L278)). Names are shown by `safeName` ([control.ts:498-501](../src/control.ts#L498-L501)).
### 2.2 Cards
Card text: request lines, then one last line ([`cardText`](../src/control.ts#L417-L436), [`requestLines`](../src/control.ts#L438-L459)).
Request lines:
| Tool | First line | Further lines |
|---|---|---|
| `connect` | `I want to connect to SUBJECT, as myself.` or `, incognito.`<br>done and connected: `I connected to ...`<br>done and not connected: `I asked to connect to ...` | link description, one code span per line<br>welcome, one code span per line<br>with `message`: `With the message:` and its lines |
| `send` | `I want to send a message to SUBJECT:`, with `, where I am incognito` before the colon in incognito chats<br>done: `I sent a message to ...` | text, one code span per line<br>`replying to NAME: QUOTE` with `reply_to`<br>`mentioning @A, @'B C'` with `mention` |
| `leave` | `I want to delete KIND NAME (@N).` or `I want to leave KIND NAME (#N).`<br>done: `I deleted ...` or `I left ...` | |
SUBJECT is `KIND NAME`, or `an unnamed KIND`, followed by ` (SIMPLEX_NAME)` when known ([actions.ts:263-266](../src/actions.ts#L263-L266)). For `connect`, SIMPLEX_NAME is the target when it is a name; for `send`, the chat's verified SimpleX name.
Last line:
| State | Condition | Last line |
|---|---|---|
| `pending` | | command line |
| `pending` | interrupted | `Cancelled: the server stopped.` |
| `performing` | interrupted | `DECIDER; interrupted: the server stopped.` |
| `done` | standing | `Approved for this chat by NAME. /'revoke CHAT'` |
| `done` | | `DECIDER.` |
| `failed` | | ``DECIDER; failed: `ERROR`.`` |
| `rejected` | | `Rejected by NAME.` |
| `expired` | | `Expired.` |
DECIDER is `Performed without approval (mode host)` in mode `host`, `Approved for this chat by NAME` for a standing send, and `Approved by NAME` otherwise ([control.ts:419](../src/control.ts#L419)).
Command lines ([`commandsLine`](../src/control.ts#L461-L470)):
| Tool | Command line |
|---|---|
| `connect` | `/'approve N' or /'reject N'` |
| `send` | `/'approve CHAT N', /'approve CHAT all' or /'reject CHAT N'` |
| `leave` | `/'approve CHAT N' or /'reject CHAT N'` |
The app shows `/'approve N'` as the tappable command `/approve N` ([PLAN, What the owner sees](../PLAN.md#what-the-owner-sees-the-agent-control-chat)).
### 2.3 Membership messages
Posted in the chat and logged; in the chat, names are shown by `safeName` and the `--owners ...` part is a code span; in the log, names are folded to one line ([control.ts:322-325](../src/control.ts#L322-L325), [:64-65](../src/control.ts#L64-L65)).
| Situation | Text | Code |
|---|---|---|
| owner joined, `--owners` absent | `I'm your agent. I ask here before I connect or send.` (chat only) | [control.ts:19](../src/control.ts#L19), [:270](../src/control.ts#L270) |
| member joined, `--owners` without a list | `NAME joined (member ID). Restart the server with --owners ENTRIES` | [control.ts:275-278](../src/control.ts#L275-L278) |
| start with `--owners` without a list and humans present | `A joined (member ID). B joined (member ID). Restart the server with --owners ENTRIES` | [control.ts:242-245](../src/control.ts#L242-L245) |
| a human became owner outside the list | `NAME (member ID) is now an owner and can't approve actions yet. Restart the server with --owners ENTRIES` | [control.ts:283-287](../src/control.ts#L283-L287) |
| a listed owner stopped being a human owner | `NAME (member ID) is no longer an owner. Restart the server with --owners ENTRIES`<br>none remains: `NAME (member ID) is no longer an owner. No owners remain: restart the server with --reset-owners --owners` | [control.ts:288-293](../src/control.ts#L288-L293) |
ENTRIES: `ID:NAME` of each human, or of each human owner, joined by commas; an entry with characters other than letters, digits, `_` and `-` is quoted for the shell ([control.ts:581-591](../src/control.ts#L581-L591)).
### 2.4 Group profile
| Field | Value | Code |
|---|---|---|
| display name | the agent's display name, updated at start when it differs | [control.ts:91-93](../src/control.ts#L91-L93) |
| full name | empty | [control.ts:377](../src/control.ts#L377) |
| description | `Supervise your AI agent here: approve or reject its connections and messages with one tap.` | [control.ts:378](../src/control.ts#L378) |
| history | on | [control.ts:379](../src/control.ts#L379) |
| command menu | `/pending` "Pending actions"<br>`/list` "Chats and approvals"<br>updated at start when it differs | [control.ts:10-13](../src/control.ts#L10-L13), [:91-93](../src/control.ts#L91-L93) |
---
## 3. CLI
Binary: `simplex-mcp`, compiled `dist/cli.js` ([package.json:5-7](../package.json#L5-L7)). Help text: [cli.ts:10-35](../src/cli.ts#L10-L35).
```
simplex-mcp --help
simplex-mcp serve NAME [options]
simplex-mcp register NAME [options]
```
NAME: 1 to 32 characters of `[a-z0-9]` and single hyphens, starting with a letter or digit ([cli.ts:142-144](../src/cli.ts#L142-L144)).
### 3.1 `serve`
| Option | Value | Default | Rule | Code |
|---|---|---|---|---|
| `--data` | folder | `~/.simplex-mcp/NAME` | resolved to an absolute path; `dataGiven` is set | [cli.ts:65-68](../src/cli.ts#L65-L68) |
| `--display-name` | text | NAME when the user is created | must equal `validName(text)` | [cli.ts:69-71](../src/cli.ts#L69-L71), [:146-150](../src/cli.ts#L146-L150) |
| `--approve` | `requests` or `host` | `requests` | | [cli.ts:72-74](../src/cli.ts#L72-L74) |
| `--owners` | absent value, or `ID:NAME,...` | absent | the value is absent when the next argument is missing or starts with `--`; entries match `^([1-9]\d*):(.+)$` | [cli.ts:75-77](../src/cli.ts#L75-L77), [:167-171](../src/cli.ts#L167-L171) |
| `--expire` | hours | 23 | integer 1 to 23 | [cli.ts:78-80](../src/cli.ts#L78-L80) |
| `--reset-owners` | flag | off | refused with an `--owners` list | [cli.ts:81-83](../src/cli.ts#L81-L83), [:97-99](../src/cli.ts#L97-L99) |
| `--new-token` | flag | off | | [cli.ts:84-86](../src/cli.ts#L84-L86) |
| `--port` | number | 5226 | integer 1 to 65535 | [cli.ts:87-89](../src/cli.ts#L87-L89) |
| `--qr-code` | `small` or `large` | `small` | | [cli.ts:90-92](../src/cli.ts#L90-L92) |
### 3.2 `register`
| Option | Value | Default | Code |
|---|---|---|---|
| `--scope` | `user` or `local` | `user` | [cli.ts:107-109](../src/cli.ts#L107-L109) |
| `--data` | folder | `~/.simplex-mcp/NAME` | [cli.ts:110-112](../src/cli.ts#L110-L112) |
Entry ([`registryEntry`](../src/register.ts#L43-L50)):
| Field | Value |
|---|---|
| `type` | `http` |
| `url` | `http://127.0.0.1:PORT/mcp` |
| `headersHelper` | `cat DATA/headers.json`, Windows `type DATA\headers.json`, path shell-quoted |
| `timeout` | `(expireHours + 24) × 3600000 + 600000` ms |
Server name: `simplex-NAME` ([register.ts:33](../src/register.ts#L33)).
### 3.3 Exit codes and messages
| Code | Message | Code location |
|---|---|---|
| 0 | `--help` text on stdout; a stop after a signal | [cli.ts:194-196](../src/cli.ts#L194-L196), [serve.ts:53](../src/serve.ts#L53) |
| 2 | `Usage: simplex-mcp serve NAME [options] or simplex-mcp register NAME [options]. Run simplex-mcp --help.` | [cli.ts:45](../src/cli.ts#L45) |
| 2 | `NAME: 1 to 32 lowercase letters, digits and single hyphens, starting with a letter or digit.` | [cli.ts:46](../src/cli.ts#L46) |
| 2 | `OPTION: a value expected.` | [cli.ts:131](../src/cli.ts#L131) |
| 2 | `Unknown option OPTION. Run simplex-mcp --help.` | [cli.ts:184](../src/cli.ts#L184) |
| 2 | `--display-name: not a valid SimpleX name; try VALID` | [cli.ts:148](../src/cli.ts#L148) |
| 2 | `--approve: requests or host expected, got VALUE` | [cli.ts:153](../src/cli.ts#L153) |
| 2 | `--qr-code: small or large expected, got VALUE` | [cli.ts:158](../src/cli.ts#L158) |
| 2 | `--scope: user or local expected, got VALUE` | [cli.ts:163](../src/cli.ts#L163) |
| 2 | `--owners: ID:NAME expected, got ENTRY` | [cli.ts:169](../src/cli.ts#L169) |
| 2 | `--expire: hours from 1 to 23 expected, got VALUE` | [cli.ts:79](../src/cli.ts#L79), [:175](../src/cli.ts#L175) |
| 2 | `--port: a port from 1 to 65535 expected, got VALUE` | [cli.ts:88](../src/cli.ts#L88), [:175](../src/cli.ts#L175) |
| 2 | `--reset-owners takes --owners without a list: member IDs change in a new agent control chat.` | [cli.ts:98](../src/cli.ts#L98) |
| 2 | `--owners: member ENTRY is not in the agent control chat. Start with --reset-owners --owners, or use the IDs of current members.` | [control.ts:251](../src/control.ts#L251) |
| 1 | `Another server uses DATA (PID N).` | [files.ts:21](../src/files.ts#L21), [:23](../src/files.ts#L23) |
| 1 | `Port PORT is in use. Start with --port N.` | [serve.ts:205](../src/serve.ts#L205) |
| 1 | `Agent "NAME" was never started. Start it with: npx -y @simplex-chat/mcp serve NAME` | [register.ts:32](../src/register.ts#L32) |
| 1 | `Add this entry under LOCATION in ~/.claude.json: {"simplex-NAME": ENTRY}` | [register.ts:52-55](../src/register.ts#L52-L55) |
| 1 | `claude mcp add-json failed: STDERR` | [register.ts:39](../src/register.ts#L39) |
| 1 | `simplex-mcp: ERROR` for other errors, `stop: ERROR` for a failed stop | [cli.ts:210-211](../src/cli.ts#L210-L211), [serve.ts:55-56](../src/serve.ts#L55-L56) |
LOCATION is `"mcpServers"` for scope `user`, and `"projects" > "PROJECT" > "mcpServers"` for scope `local` ([register.ts:53](../src/register.ts#L53)).
### 3.4 Terminal lines
stdout ([log.ts:31-33](../src/log.ts#L31-L33)):
| Moment | Line | Code |
|---|---|---|
| start | `SimpleX MCP server VERSION, agent "NAME"`<br>`Data folder: DATA`<br>`Approval mode: MODE` | [serve.ts:72-74](../src/serve.ts#L72-L74) |
| control chat created | `Agent control chat "DISPLAY" created.` | [control.ts:383](../src/control.ts#L383) |
| link opened | `Join the agent control chat in the SimpleX app. The link works once:`<br>QR code<br>link<br>small QR code: `If the QR code does not scan, restart with --qr-code large, or paste the link into the app.`<br>role line | [control.ts:304-310](../src/control.ts#L304-L310) |
| listening | `Listening on http://127.0.0.1:PORT/mcp` | [serve.ts:120](../src/serve.ts#L120) |
| owner setup done | `Add the agent to Claude Code:`<br>` npx -y @simplex-chat/mcp register NAME [--data DATA]` | [serve.ts:164-166](../src/serve.ts#L164-L166) |
| owner joined | `NAME joined (member ID) and is now an owner. The link is deleted.` | [control.ts:271](../src/control.ts#L271) |
| listed owners set | `Owners: ENTRIES.` | [control.ts:262](../src/control.ts#L262) |
| approvers gone, `--owners` absent | `No owners in the agent control chat.` | [control.ts:20](../src/control.ts#L20), [:157](../src/control.ts#L157), [:238](../src/control.ts#L238) |
| control chat lost | `The agent lost its control chat. Restart the server with --reset-owners.` | [control.ts:298](../src/control.ts#L298) |
| action created | `Action N: WHAT` with `, as itself` or `, incognito` for `connect` and ` (CHAT)` for `send` and `leave` | [actions.ts:140-141](../src/actions.ts#L140-L141), [:268-278](../src/actions.ts#L268-L278) |
| perform ended, mode `requests` | `Action N approved by NAME: RESULT.` with RESULT `sent`, `deleted`, `left`, `connected`, `connection requested` or `failed: ERROR` | [actions.ts:238](../src/actions.ts#L238), [:284-291](../src/actions.ts#L284-L291) |
| perform ended, mode `host` | `Action N: performed without approval (mode host).`, or `...; failed: ERROR.` | [actions.ts:286](../src/actions.ts#L286) |
| rejected | `Action N rejected by NAME.` | [actions.ts:188](../src/actions.ts#L188) |
| expired | `Action N expired.` | [actions.ts:158](../src/actions.ts#L158) |
| stop | `Cancelled K action.` or `Cancelled K actions.` when K > 0<br>`Stopped.` | [serve.ts:155](../src/serve.ts#L155), [:159](../src/serve.ts#L159) |
| membership messages | see [2.3](#23-membership-messages) | |
stderr: `CONTEXT: ERROR` from `logError` ([log.ts:35-37](../src/log.ts#L35-L37)), with CONTEXT one of `action N`, `action N card`, `control chat command`, `EVENT in the agent control chat`, `EVENT event`, `MCP`, `HTTP request`, `stop`, `simplex-mcp`. The library's download lines are written to stderr, and its event processing errors to stdout ([libs.ts:114](../../simplex-chat-nodejs/src/libs.ts#L114), [api.ts:187](../../simplex-chat-nodejs/src/api.ts#L187)).