* 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>
31 KiB
SimpleX MCP server: API
The five MCP tools, the agent control chat commands, replies and cards, and the CLI of
@simplex-chat/mcp0.1.0.Related specs: README | Architecture | Flows
Source: tools.ts | control.ts | cli.ts | messages.ts | actions.ts
Table of contents
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 |
| Tool descriptions and server instructions; the instructions name the agent's display name | tools.ts:301-332, serve.ts:109 |
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 |
additionalProperties: false is set in every input schema |
tools.ts:177, :192, :203, :206, :214 |
A result is sent as structuredContent and as its JSON in one text item |
tools.ts:370 |
A tool error is sent as isError: true with one text item: the ToolError text, or the errorText of other errors |
tools.ts:372, log.ts:39-43 |
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, messages.ts:200-204 |
The call is aborted when the client closes the response stream or sends notifications/cancelled; the action is kept |
src-BhnGYybN.cjs:6338-6341, :6425 |
| A call with the same tool and action fields as an action in memory is attached to it | tools.ts:1003-1005 |
| Messages are marked read after the last wait of a call that is not aborted | tools.ts:460-461, :598-600, :679-680 |
Action fields of the key:
| Tool | Fields |
|---|---|
connect |
targetincognito (default false)message (default null) |
send |
chattextreply_to (default null)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. Perform: performConnect. Schemas: input, output.
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 |
done, chat unknown |
action, status, type, connected: false, plan fields name, peer_type, description, welcome, commands, known_as when present |
tools.ts:428, :899-905 |
done, chat known |
action, status, chat fields, messages |
tools.ts:444-464 |
failed |
action, status, error |
tools.ts:435 |
rejected, expired |
action, status |
tools.ts:435 |
Chat fields (chatFields):
| 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, :456-463).
Tool errors:
| Text | Condition | Code |
|---|---|---|
Not a SimpleX link or name: TARGET |
whitespace in target, or invalidConnReq from the plan command |
tools.ts:468, :474 |
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, :826, log.ts:45-47 |
TARGET can't be joined now: PLAN. |
invitationLink ownLink or connectingcontactAddress ownLink or connectingProhibitgroupLink ownLink, connectingProhibit, noRelays or updateRequired |
tools.ts:817, :830, :848, :870 |
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 |
| refusals | see 1.8 |
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, :841, :859).
1.3 send
Handler: AgentTools.send. Result: sendResult. Perform: performSend. Schemas: input, output.
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 |
The agent has role ROLE in CHAT and can't post. |
membership role observer or relay |
tools.ts:576-579 |
reply_to: CHAT has no item N. |
the read of around=N fails, or the item is absent from it |
tools.ts:723-728 |
mention: CHAT is a contact chat; mentions apply to groups and channels. |
mention with @N |
tools.ts:731 |
mention: CHAT has no member N. |
ID absent from /_members |
tools.ts:735 |
| refusals | see 1.8 |
1.4 receive
Handler: AgentTools.receive. Schemas: input, output.
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, :659-661); other chat errors as their error text (log.ts:39-47).
Messages are read by receive without an action or an approval.
1.5 list
Handler: AgentTools.listChats. Schemas: input, output.
Input: an empty object.
Output:
| Field | Item fields |
|---|---|
chats |
chattypenamesimplex_name, when verifiedpeer_type, when presentincognitoknown_as, when incognitoconnectedunread: chatStats.unreadCountapproved: true, only under a standing approval in mode requests |
actions |
actiontoolinput: the input of the creating callstateopen: 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). actions: every action in memory (tools.ts:694).
1.6 leave
Handler: AgentTools.leave. Perform: performLeave. Schemas: input, output.
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/_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); refusals.
1.7 Message and command objects
Message (Message, toMessage, schema):
| Field | Type | Presence | Source |
|---|---|---|---|
chat |
string | always | chat reference of the item |
item |
integer | always | meta.itemId |
from |
string | always | groupRcv: member display namedirectRcv: contact display namechannelRcv: channel display namesent 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, :123-125).
Command (commandsOf, schema):
| Form | Fields |
|---|---|
| command | keywordlabelparams, when present |
| menu | labelcommands: commands and menus |
1.8 Action refusals
Refusals of a new connect, send or leave action, first match (actions.ts:136, control.ts:105-111):
| 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): 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, ownerRequest):
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), and it runs the expiry check before each command (control.ts:164).
| 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, :166 |
| any request | sender not an approver, --owners absent |
Only owners approve actions. |
control.ts:167 |
| any request | sender not an approver, --owners given |
Only the owners listed in --owners approve actions. |
control.ts:167 |
approve, reject |
action N absent, not pending, or without a card |
Action N is not pending. |
control.ts:182 |
approve CHAT N, reject CHAT N |
action N is a connect, or for another chat |
Action N is not for CHAT. |
control.ts:183 |
approve, reject |
accepted | none; the card is edited at the outcome | control.ts:184-187 |
approve CHAT all |
CHAT unknown or the control chat | No chat CHAT. /list shows the agent's chats. |
control.ts:194 |
approve CHAT all |
accepted | Approved for KIND NAME (CHAT) by APPROVER. /'revoke CHAT' |
control.ts:202 |
revoke CHAT |
CHAT unknown, or without a standing approval | Messages to CHAT have no standing approval. |
control.ts:207 |
revoke CHAT |
accepted | Messages to KIND NAME (CHAT) need approval again. |
control.ts:210 |
pending |
no pending action with a card | No pending actions. |
control.ts:171 |
pending |
otherwise | one line per action: N WHAT expires YYYY-MM-DD HH:MM UTC |
control.ts:170, :472-474 |
list |
no chats | No chats. |
control.ts:220 |
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 |
KIND is contact, bot, agent, business, group or channel (tools.ts:928-935). WHAT is connect to SUBJECT, send to SUBJECT, delete SUBJECT or leave SUBJECT (actions.ts:268-278). Names are shown by safeName (control.ts:498-501).
2.2 Cards
Card text: request lines, then one last line (cardText, requestLines).
Request lines:
| Tool | First line | Further lines |
|---|---|---|
connect |
I want to connect to SUBJECT, as myself. or , incognito.done and connected: I connected to ...done and not connected: I asked to connect to ... |
link description, one code span per line welcome, one code span per line 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 chatsdone: I sent a message to ... |
text, one code span per linereplying to NAME: QUOTE with reply_tomentioning @A, @'B C' with mention |
leave |
I want to delete KIND NAME (@N). or I want to leave KIND NAME (#N).done: I deleted ... or I left ... |
SUBJECT is KIND NAME, or an unnamed KIND, followed by (SIMPLEX_NAME) when known (actions.ts:263-266). 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).
Command lines (commandsLine):
| 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).
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, :64-65).
| Situation | Text | Code |
|---|---|---|
owner joined, --owners absent |
I'm your agent. I ask here before I connect or send. (chat only) |
control.ts:19, :270 |
member joined, --owners without a list |
NAME joined (member ID). Restart the server with --owners ENTRIES |
control.ts:275-278 |
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 |
| 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 |
| a listed owner stopped being a human owner | NAME (member ID) is no longer an owner. Restart the server with --owners ENTRIESnone remains: NAME (member ID) is no longer an owner. No owners remain: restart the server with --reset-owners --owners |
control.ts:288-293 |
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).
2.4 Group profile
| Field | Value | Code |
|---|---|---|
| display name | the agent's display name, updated at start when it differs | control.ts:91-93 |
| full name | empty | control.ts:377 |
| description | Supervise your AI agent here: approve or reject its connections and messages with one tap. |
control.ts:378 |
| history | on | control.ts:379 |
| command menu | /pending "Pending actions"/list "Chats and approvals"updated at start when it differs |
control.ts:10-13, :91-93 |
3. CLI
Binary: simplex-mcp, compiled dist/cli.js (package.json:5-7). Help text: cli.ts:10-35.
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).
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 |
--display-name |
text | NAME when the user is created | must equal validName(text) |
cli.ts:69-71, :146-150 |
--approve |
requests or host |
requests |
cli.ts:72-74 | |
--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, :167-171 |
--expire |
hours | 23 | integer 1 to 23 | cli.ts:78-80 |
--reset-owners |
flag | off | refused with an --owners list |
cli.ts:81-83, :97-99 |
--new-token |
flag | off | cli.ts:84-86 | |
--port |
number | 5226 | integer 1 to 65535 | cli.ts:87-89 |
--qr-code |
small or large |
small |
cli.ts:90-92 |
3.2 register
| Option | Value | Default | Code |
|---|---|---|---|
--scope |
user or local |
user |
cli.ts:107-109 |
--data |
folder | ~/.simplex-mcp/NAME |
cli.ts:110-112 |
Entry (registryEntry):
| 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).
3.3 Exit codes and messages
| Code | Message | Code location |
|---|---|---|
| 0 | --help text on stdout; a stop after a signal |
cli.ts:194-196, serve.ts:53 |
| 2 | Usage: simplex-mcp serve NAME [options] or simplex-mcp register NAME [options]. Run simplex-mcp --help. |
cli.ts:45 |
| 2 | NAME: 1 to 32 lowercase letters, digits and single hyphens, starting with a letter or digit. |
cli.ts:46 |
| 2 | OPTION: a value expected. |
cli.ts:131 |
| 2 | Unknown option OPTION. Run simplex-mcp --help. |
cli.ts:184 |
| 2 | --display-name: not a valid SimpleX name; try VALID |
cli.ts:148 |
| 2 | --approve: requests or host expected, got VALUE |
cli.ts:153 |
| 2 | --qr-code: small or large expected, got VALUE |
cli.ts:158 |
| 2 | --scope: user or local expected, got VALUE |
cli.ts:163 |
| 2 | --owners: ID:NAME expected, got ENTRY |
cli.ts:169 |
| 2 | --expire: hours from 1 to 23 expected, got VALUE |
cli.ts:79, :175 |
| 2 | --port: a port from 1 to 65535 expected, got VALUE |
cli.ts:88, :175 |
| 2 | --reset-owners takes --owners without a list: member IDs change in a new agent control chat. |
cli.ts:98 |
| 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 |
| 1 | Another server uses DATA (PID N). |
files.ts:21, :23 |
| 1 | Port PORT is in use. Start with --port N. |
serve.ts:205 |
| 1 | Agent "NAME" was never started. Start it with: npx -y @simplex-chat/mcp serve NAME |
register.ts:32 |
| 1 | Add this entry under LOCATION in ~/.claude.json: {"simplex-NAME": ENTRY} |
register.ts:52-55 |
| 1 | claude mcp add-json failed: STDERR |
register.ts:39 |
| 1 | simplex-mcp: ERROR for other errors, stop: ERROR for a failed stop |
cli.ts:210-211, serve.ts:55-56 |
LOCATION is "mcpServers" for scope user, and "projects" > "PROJECT" > "mcpServers" for scope local (register.ts:53).
3.4 Terminal lines
stdout (log.ts:31-33):
| Moment | Line | Code |
|---|---|---|
| start | SimpleX MCP server VERSION, agent "NAME"Data folder: DATAApproval mode: MODE |
serve.ts:72-74 |
| control chat created | Agent control chat "DISPLAY" created. |
control.ts:383 |
| link opened | Join the agent control chat in the SimpleX app. The link works once:QR code link small QR code: If the QR code does not scan, restart with --qr-code large, or paste the link into the app.role line |
control.ts:304-310 |
| listening | Listening on http://127.0.0.1:PORT/mcp |
serve.ts:120 |
| owner setup done | Add the agent to Claude Code: npx -y @simplex-chat/mcp register NAME [--data DATA] |
serve.ts:164-166 |
| owner joined | NAME joined (member ID) and is now an owner. The link is deleted. |
control.ts:271 |
| listed owners set | Owners: ENTRIES. |
control.ts:262 |
approvers gone, --owners absent |
No owners in the agent control chat. |
control.ts:20, :157, :238 |
| control chat lost | The agent lost its control chat. Restart the server with --reset-owners. |
control.ts:298 |
| action created | Action N: WHAT with , as itself or , incognito for connect and (CHAT) for send and leave |
actions.ts:140-141, :268-278 |
perform ended, mode requests |
Action N approved by NAME: RESULT. with RESULT sent, deleted, left, connected, connection requested or failed: ERROR |
actions.ts:238, :284-291 |
perform ended, mode host |
Action N: performed without approval (mode host)., or ...; failed: ERROR. |
actions.ts:286 |
| rejected | Action N rejected by NAME. |
actions.ts:188 |
| expired | Action N expired. |
actions.ts:158 |
| stop | Cancelled K action. or Cancelled K actions. when K > 0Stopped. |
serve.ts:155, :159 |
| membership messages | see 2.3 |
stderr: CONTEXT: ERROR from logError (log.ts:35-37), 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, api.ts:187).