Client tools from Node.js: a client that lends mm a tool
client.js is an ACP client of mm -acp that offers the agent one of its own tools, host_info: a JavaScript function that reports the operating system, CPU, memory or uptime of the machine the client runs on. The agent has no shell (bashTool: false): the client's tool is its only way to know anything about the machine. mm discovers the tool over ACP and the model calls it like its own, as client_host_info (ticket 202, the protocol is in ACP client tools).
No dependency: Node.js 20 or later, node:child_process, node:readline, node:os.
Files
| File | |
|---|---|
client.js |
the client: starts mm, advertises the tool, asks one question, answers the tool calls, prints the answer |
agent.yaml |
the agent: Qwen3.5 4B on llmman, no bash, yolo: true, acp.clientTools.discovery: meta |
agent-request.yaml |
the same agent with acp.clientTools.discovery: request |
Run it
Needs an mm built with ticket 202 (acp.clientTools), and llmman with the model:
llmman pull hf.co/unsloth/Qwen3.5-4B-GGUF:Q4_K_M
llmman serve
cd demo.acp.sdk/client-tools-nodejs
node client.js # default question, discovery "meta"
node client.js "How much memory is free on my machine?" # your question
node client.js --request "Which CPU does my machine have?" # discovery "request"
mm must be on the PATH, or named by MM_BIN (MM_BIN=/path/to/mm node client.js).
Expected output (the values are your machine's):
[acp] client tools (meta): client_host_info
[client] discovery: meta, session 20261110-230430-6df8f9dd
❓ What operating system does my machine run, and how much memory does it have?
🛠️ client: host_info
🛠️ client: host_info
[client] host_info {"topic":"os"} (session 20261110-230430-6df8f9dd)
[client] host_info {"topic":"memory"} (session 20261110-230430-6df8f9dd)
│ Linux 7.0.14 (linux/arm64), host name claude-mini-me
│ 15.0 GiB free of 15.8 GiB
Your machine runs Linux (version 7.0.14 on ARM64) and has 15.8 GiB of total memory, with 15.0 GiB currently free.
[end_turn]
[acp] …is mm's own stderr line: it saw the tool. In--requestmode it is preceded by[client] list requested for session …: mm asked for the list while it opened the session.[client] host_info …is printed by the client when mm calls the tool: the model's arguments, and the session from_meta.🛠️and│are mm'ssession/updatenotifications: the tool call and its result, as an editor would show them.
How the client does it
| Step | In client.js |
|---|---|
| describe the tool | hostInfoTool: name, description, method (_demo.minime/host_info, an ACP extension method), inputSchema |
| advertise it | initialize with clientCapabilities._meta["org.mini-me/tools"]: the list (meta) or true (request) |
give the list (request only) |
answer _org.mini-me/tools/list with {tools: [hostInfoTool]} |
| run a call | answer the request _demo.minime/host_info: the arguments are at the top level of params, the session in params._meta["org.mini-me/sessionId"]; the answer is {content: [{type: "text", text}]}, or isError: true with a message the model can read |
confirm a call (yolo: false) |
answer session/request_permission: allow the titles that start with client: , refuse the rest |
| anything else mm asks | JSON-RPC error -32601 (method not found) |
Things to try
- Set
yolo: falseinagent.yaml: each call goes throughsession/request_permissionfirst, and the client prints[client] permission for "client: host_info": allowed. - Mix the modes up: set
discovery: requestinagent-request.yamlback tometaand runnode client.js --request. The client advertises onlytrue, mm expects the list itself: it prints[acp] client tools: "org.mini-me/tools" is not a list of tools; …and the model gets no tool. - Add a second tool: one more entry in the list, one more
caseinonRequest.
Measured (2026-11-10, in the development sandbox, Linux arm64, llmman with Qwen3.5 4B, mm built from branch feature/acp-client-tools)
| Run | Result |
|---|---|
node client.js |
two calls (os, memory), answer built from both |
node client.js --request "Which CPU does my machine have, and since when is it up?" |
list requested at session/new, two calls (uptime, cpu), answer built from both |
yolo: false |
permission asked and allowed, then the call, then the answer |
Also run by the author on macOS (2026-11-10), with mm rebuilt from the branch: works.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
|