bots-garden/mini-mepublic Fork
⑂ v1.0.6
Commits
⬇ Clone ▾
git clone https://git.rickub.com/bots-garden/mini-me.git
git clone ssh://git@rickub.com/bots-garden/mini-me.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Download ZIP
🛟 Updated. 74efbf2 · on v1.0.6 · k33g · 3d ago
acp-client-tools.md · 47 lines · 6.0 KBmarkdown
Blame HistoryOpen raw

ACP client tools — explanation

What is this about?

An editor, or any program that drives mm -acp, often knows things the agent cannot reach: the buffers open on screen, the selection, an application's data, a device. ACP client tools let that program lend the agent functions of its own. The model calls them like bash or read_file; mm forwards each call to the client as an ACP request and gives the answer back to the model. The exact protocol is in the reference; this page is about why it looks the way it does.

Why mm needed its own convention

ACP has an extension mechanism: either side may define methods whose name starts with _, and may advertise them in the _meta object of the capabilities exchanged at initialize. That is enough to call a method, but not to make it a tool: a model needs a name, a description and a schema of the arguments, and ACP defines no way to describe any of them. So mm defines one, under its own namespace (org.mini-me), using only what ACP already provides — _meta for the advertisement, extension methods for the calls. A client that does not know about it sends nothing, and nothing changes.

The tool description deliberately copies MCP's (name, description, inputSchema) and so does the answer (content blocks, isError). Anyone who has written an MCP tool already knows both shapes, and mm already knew how to present an MCP schema to a small model (the same cleaning of required and enum applies, for the same reasons: see MCP decisions).

Two discovery modes, and why meta is the default

  • meta: the list travels in initialize. One message, nothing to answer, and the list is known before any session exists. Most clients have a fixed set of tools, and for them this is the least code to write. Hence the default.
  • request: the client only says it has tools, and mm asks for the list each time a session is opened, loaded or resumed. This is for a client whose tools depend on the session — its working directory, the user behind it — or change over time.

The request mode has one constraint worth knowing: mm sends the list request while the client is still waiting for its session/new response. A client that answered requests one at a time would wait for mm while mm waits for it. The ACP SDKs (Go, TypeScript) run each incoming request on its own, so this is free for them; it is written in the specification for clients that roll their own JSON-RPC.

Why the session travels in _meta, and the arguments at the top level

The call's params are exactly the model's arguments. This keeps an existing extension method usable as is: a client that already answers _com.example/workspace/buffers {"language": "go"} can expose it as a tool without an adapter. The session id is not an argument the model chose, so it goes where ACP puts out-of-band data — _meta — and the model cannot forge it: a _meta it invented is replaced.

Why the tools are built per session and never registered

Genkit keeps a registry of named tools, and registering the same name twice is an error. Client tools change per connection (and per session in request mode), so mm builds them as dynamic tools (ai.NewTool), which Genkit registers in a child registry for the length of one generation. Two sessions can then hold client_open_buffers with different definitions, and nothing has to be unregistered when a session ends.

Why errors are answers

A client that does not answer, answers Method not found, or reports a failure gives the model a [client tool error: …] text, and the turn goes on. This is mm's general rule (see Architecture): the model is the one that must react, so it is the one that must read what went wrong. The same rule governs a bad list: an entry that cannot be used is skipped with a line on stderr, and the session opens with the others. A client's mistake must not cost the user the conversation.

Permissions

Client tools follow the rule of the tools that come from elsewhere, with their own key (acp.clientTools.permissions): ask always asks, auto asks only when yolo is off. The default is auto, where MCP's is ask, because the trust is not the same: an MCP server is third-party code, a client tool is a function of the very program the user started to talk to the agent. See Yolo mode and permissions.

Rejected alternatives

  • Using the mcpServers of session/new. ACP lets a client hand the agent MCP servers to connect to. That would have meant the client running an MCP server — a second protocol, a second transport — to expose a function it already holds in the same process, at the other end of the pipe it already shares with mm.
  • One generic method, _org.mini-me/tools/call {name, arguments}. Simpler to dispatch, but it would force every client to adopt mm's method name; a method per tool lets a client expose extension methods it already has.
  • Declaring the tools in agent.yaml. The agent file does not know which client will start it, and the same agent can be driven by an editor today and by a script tomorrow. The tools belong to the client, so the client announces them.
  • Only one discovery mode. meta alone cannot serve tools that depend on the session; request alone costs every client a request handler and a round trip for a list that is usually fixed.

How it relates to the rest

 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
# ACP client tools — explanation

## What is this about?

An editor, or any program that drives `mm -acp`, often knows things the agent cannot reach: the buffers open on screen, the selection, an application's data, a device. ACP client tools let that program lend the agent functions of its own. The model calls them like `bash` or `read_file`; mm forwards each call to the client as an ACP request and gives the answer back to the model. The exact protocol is in the [reference](../reference/acp-client-tools.md); this page is about why it looks the way it does.

## Why mm needed its own convention

ACP has an extension mechanism: either side may define methods whose name starts with `_`, and may advertise them in the `_meta` object of the capabilities exchanged at `initialize`. That is enough to *call* a method, but not to make it a *tool*: a model needs a name, a description and a schema of the arguments, and ACP defines no way to describe any of them. So mm defines one, under its own namespace (`org.mini-me`), using only what ACP already provides — `_meta` for the advertisement, extension methods for the calls. A client that does not know about it sends nothing, and nothing changes.

The tool description deliberately copies MCP's (`name`, `description`, `inputSchema`) and so does the answer (`content` blocks, `isError`). Anyone who has written an MCP tool already knows both shapes, and mm already knew how to present an MCP schema to a small model (the same cleaning of `required` and `enum` applies, for the same reasons: see [MCP decisions](mcp-decisions.md)).

## Two discovery modes, and why `meta` is the default

- **`meta`**: the list travels in `initialize`. One message, nothing to answer, and the list is known before any session exists. Most clients have a fixed set of tools, and for them this is the least code to write. Hence the default.
- **`request`**: the client only says it has tools, and mm asks for the list each time a session is opened, loaded or resumed. This is for a client whose tools depend on the session — its working directory, the user behind it — or change over time.

The `request` mode has one constraint worth knowing: mm sends the list request while the client is still waiting for its `session/new` response. A client that answered requests one at a time would wait for mm while mm waits for it. The ACP SDKs (Go, TypeScript) run each incoming request on its own, so this is free for them; it is written in the specification for clients that roll their own JSON-RPC.

## Why the session travels in `_meta`, and the arguments at the top level

The call's `params` are exactly the model's arguments. This keeps an existing extension method usable as is: a client that already answers `_com.example/workspace/buffers {"language": "go"}` can expose it as a tool without an adapter. The session id is not an argument the model chose, so it goes where ACP puts out-of-band data — `_meta` — and the model cannot forge it: a `_meta` it invented is replaced.

## Why the tools are built per session and never registered

Genkit keeps a registry of named tools, and registering the same name twice is an error. Client tools change per connection (and per session in `request` mode), so mm builds them as *dynamic* tools (`ai.NewTool`), which Genkit registers in a child registry for the length of one generation. Two sessions can then hold `client_open_buffers` with different definitions, and nothing has to be unregistered when a session ends.

## Why errors are answers

A client that does not answer, answers `Method not found`, or reports a failure gives the model a `[client tool error: …]` text, and the turn goes on. This is mm's general rule (see [Architecture](architecture.md)): the model is the one that must react, so it is the one that must read what went wrong. The same rule governs a bad list: an entry that cannot be used is skipped with a line on stderr, and the session opens with the others. A client's mistake must not cost the user the conversation.

## Permissions

Client tools follow the rule of the tools that come from elsewhere, with their own key (`acp.clientTools.permissions`): `ask` always asks, `auto` asks only when `yolo` is off. The default is `auto`, where MCP's is `ask`, because the trust is not the same: an MCP server is third-party code, a client tool is a function of the very program the user started to talk to the agent. See [Yolo mode and permissions](yolo-and-permissions.md).

## Rejected alternatives

- **Using the `mcpServers` of `session/new`.** ACP lets a client hand the agent MCP servers to connect to. That would have meant the client running an MCP server — a second protocol, a second transport — to expose a function it already holds in the same process, at the other end of the pipe it already shares with mm.
- **One generic method, `_org.mini-me/tools/call {name, arguments}`.** Simpler to dispatch, but it would force every client to adopt mm's method name; a `method` per tool lets a client expose extension methods it already has.
- **Declaring the tools in `agent.yaml`.** The agent file does not know which client will start it, and the same agent can be driven by an editor today and by a script tomorrow. The tools belong to the client, so the client announces them.
- **Only one discovery mode.** `meta` alone cannot serve tools that depend on the session; `request` alone costs every client a request handler and a round trip for a list that is usually fixed.

## How it relates to the rest

- The tools exist only in ACP mode: the terminal and the window have no client to lend them.
- Once declared, they are ordinary tools for the rest of mm: [tool routing](decision-models.md) can leave them out of a turn when one of its groups matches their names (`^client_`), [hooks](hooks.md) see their calls, the loop detector counts them.
- [`demo.acp.sdk/go`](../../../demo.acp.sdk/go/README.en.md) (`Options.Tools`) and [`demo.acp.sdk/client-tools-nodejs`](../../../demo.acp.sdk/client-tools-nodejs/README.en.md) are two client implementations; [How to offer tools from your ACP client](../how-to/offer-tools-from-an-acp-client.md) walks through one.