🛟 Updated.
74efbf2 parent: d3d4844 modified
docs/en/README.md +1 -0 | @@ -101,6 +101,7 @@ This documentation is organised along the four [Diátaxis](https://diataxis.fr) | ||
| 101 | 101 | - [Hooks](explanation/hooks.md) — why external commands, why they plug into `engine.Generate` and `WrapTool`, why a stopped question leaves a marker, what ticket 36 left out, why `preAsk`/`postAsk` belong to the asking agent |
| 102 | 102 | - [Guard models](explanation/guard-models.md) — why the system message is a risk name, why each guard judges the question alone (`-p`), why a hook rather than `ask_agent`, who reacts (the model or the hook), failing closed, what guards do not give you |
| 103 | 103 | - [Decision models](explanation/decision-models.md) — why a decider is an agent, why routing appends the `$skill` line, the server's confidence, what was measured (0 wrong picks on 23 cases; 12 → 16 right with a 4B chat model), why not decision-4b; why command triage and tool routing only ever remove (questions, tools), a decider on its own |
| 104 | +- [ACP client tools](explanation/acp-client-tools.md) — why mm needed its own convention on top of ACP's extension methods, why `meta` is the default discovery, the session in `_meta`, dynamic tools per session, errors as answers, why not `mcpServers` | |
| 104 | 105 | - Package diagram (draw.io source): [`docs/diagrams/packages.drawio`](../diagrams/packages.drawio) |
| 105 | 106 | |
| 106 | 107 | ### MCP (Model Context Protocol) Explanation |
| @@ -101,6 +101,7 @@ This documentation is organised along the four [Diátaxis](https://diataxis.fr) | |||
| 101 | - [Hooks](explanation/hooks.md) — why external commands, why they plug into `engine.Generate` and `WrapTool`, why a stopped question leaves a marker, what ticket 36 left out, why `preAsk`/`postAsk` belong to the asking agent | 101 | - [Hooks](explanation/hooks.md) — why external commands, why they plug into `engine.Generate` and `WrapTool`, why a stopped question leaves a marker, what ticket 36 left out, why `preAsk`/`postAsk` belong to the asking agent |
| 102 | - [Guard models](explanation/guard-models.md) — why the system message is a risk name, why each guard judges the question alone (`-p`), why a hook rather than `ask_agent`, who reacts (the model or the hook), failing closed, what guards do not give you | 102 | - [Guard models](explanation/guard-models.md) — why the system message is a risk name, why each guard judges the question alone (`-p`), why a hook rather than `ask_agent`, who reacts (the model or the hook), failing closed, what guards do not give you |
| 103 | - [Decision models](explanation/decision-models.md) — why a decider is an agent, why routing appends the `$skill` line, the server's confidence, what was measured (0 wrong picks on 23 cases; 12 → 16 right with a 4B chat model), why not decision-4b; why command triage and tool routing only ever remove (questions, tools), a decider on its own | 103 | - [Decision models](explanation/decision-models.md) — why a decider is an agent, why routing appends the `$skill` line, the server's confidence, what was measured (0 wrong picks on 23 cases; 12 → 16 right with a 4B chat model), why not decision-4b; why command triage and tool routing only ever remove (questions, tools), a decider on its own |
| 104 | +- [ACP client tools](explanation/acp-client-tools.md) — why mm needed its own convention on top of ACP's extension methods, why `meta` is the default discovery, the session in `_meta`, dynamic tools per session, errors as answers, why not `mcpServers` | ||
| 104 | - Package diagram (draw.io source): [`docs/diagrams/packages.drawio`](../diagrams/packages.drawio) | 105 | - Package diagram (draw.io source): [`docs/diagrams/packages.drawio`](../diagrams/packages.drawio) |
| 105 | 106 | ||
| 106 | ### MCP (Model Context Protocol) Explanation | 107 | ### MCP (Model Context Protocol) Explanation |
added
docs/en/explanation/acp-client-tools.md +47 -0 | new file mode 100644 | ||
| @@ -0,0 +1,47 @@ | ||
| 1 | +# ACP client tools — explanation | |
| 2 | + | |
| 3 | +## What is this about? | |
| 4 | + | |
| 5 | +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. | |
| 6 | + | |
| 7 | +## Why mm needed its own convention | |
| 8 | + | |
| 9 | +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. | |
| 10 | + | |
| 11 | +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)). | |
| 12 | + | |
| 13 | +## Two discovery modes, and why `meta` is the default | |
| 14 | + | |
| 15 | +- **`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. | |
| 16 | +- **`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. | |
| 17 | + | |
| 18 | +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. | |
| 19 | + | |
| 20 | +## Why the session travels in `_meta`, and the arguments at the top level | |
| 21 | + | |
| 22 | +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. | |
| 23 | + | |
| 24 | +## Why the tools are built per session and never registered | |
| 25 | + | |
| 26 | +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. | |
| 27 | + | |
| 28 | +## Why errors are answers | |
| 29 | + | |
| 30 | +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. | |
| 31 | + | |
| 32 | +## Permissions | |
| 33 | + | |
| 34 | +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). | |
| 35 | + | |
| 36 | +## Rejected alternatives | |
| 37 | + | |
| 38 | +- **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. | |
| 39 | +- **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. | |
| 40 | +- **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. | |
| 41 | +- **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. | |
| 42 | + | |
| 43 | +## How it relates to the rest | |
| 44 | + | |
| 45 | +- The tools exist only in ACP mode: the terminal and the window have no client to lend them. | |
| 46 | +- 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. | |
| 47 | +- [`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. | |
| new file mode 100644 | |||
| @@ -0,0 +1,47 @@ | |||
| 1 | +# ACP client tools — explanation | ||
| 2 | + | ||
| 3 | +## What is this about? | ||
| 4 | + | ||
| 5 | +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. | ||
| 6 | + | ||
| 7 | +## Why mm needed its own convention | ||
| 8 | + | ||
| 9 | +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. | ||
| 10 | + | ||
| 11 | +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)). | ||
| 12 | + | ||
| 13 | +## Two discovery modes, and why `meta` is the default | ||
| 14 | + | ||
| 15 | +- **`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. | ||
| 16 | +- **`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. | ||
| 17 | + | ||
| 18 | +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. | ||
| 19 | + | ||
| 20 | +## Why the session travels in `_meta`, and the arguments at the top level | ||
| 21 | + | ||
| 22 | +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. | ||
| 23 | + | ||
| 24 | +## Why the tools are built per session and never registered | ||
| 25 | + | ||
| 26 | +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. | ||
| 27 | + | ||
| 28 | +## Why errors are answers | ||
| 29 | + | ||
| 30 | +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. | ||
| 31 | + | ||
| 32 | +## Permissions | ||
| 33 | + | ||
| 34 | +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). | ||
| 35 | + | ||
| 36 | +## Rejected alternatives | ||
| 37 | + | ||
| 38 | +- **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. | ||
| 39 | +- **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. | ||
| 40 | +- **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. | ||
| 41 | +- **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. | ||
| 42 | + | ||
| 43 | +## How it relates to the rest | ||
| 44 | + | ||
| 45 | +- The tools exist only in ACP mode: the terminal and the window have no client to lend them. | ||
| 46 | +- 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. | ||
| 47 | +- [`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. | ||
modified
docs/en/explanation/architecture.md +1 -1 | @@ -29,7 +29,7 @@ The entry point, `main.go`, only does wiring: it loads the settings, opens the e | ||
| 29 | 29 | | `consent` | Whether a tool call may run: the `yolo` switch (`Gate`), the four answers (`Answer`), what a session remembers per tool (`Policy`), and the hook through which the line mode asks (`SetPrompter`). The window, the editor façade and `/ask` draw the question their own way and share these rules. See [Yolo mode and permissions](yolo-and-permissions.md). | |
| 30 | 30 | | `hooks` | The `hooks:` block (tickets 36 and 87): runs each hook with `sh -c`, writes its JSON input, reads its reply, applies the chain order and `onError`, and returns decisions (`PreGeneration`, `PreToolCall`, `PostToolCall`, `PostGeneration`, and `PreAsk`, `PostAsk` for `ask`). No Genkit, no front end: `engine/hooks.go` applies the decisions around `Generate` and in the `WrapTool` middleware, so every front end gets them. See [Hooks](hooks.md). | |
| 31 | 31 | | `agent` | The terminal REPL. | |
| 32 | -| `acp` | The Agent Client Protocol façade. | | |
| 32 | +| `acp` | The Agent Client Protocol façade; also turns the tools an ACP client offers into session tools (`clienttools.go`, see [ACP client tools](acp-client-tools.md)). | | |
| 33 | 33 | | `tui` | The single-window front end (`mm -tui`): a `ui.Sink` drawing on tcell, one transcript value, an event loop woken by the engine's and tools' goroutines. | |
| 34 | 34 | |
| 35 | 35 | A draw.io diagram of these packages and their dependencies is kept at [`docs/diagrams/packages.drawio`](../../diagrams/packages.drawio). |
| @@ -29,7 +29,7 @@ The entry point, `main.go`, only does wiring: it loads the settings, opens the e | |||
| 29 | | `consent` | Whether a tool call may run: the `yolo` switch (`Gate`), the four answers (`Answer`), what a session remembers per tool (`Policy`), and the hook through which the line mode asks (`SetPrompter`). The window, the editor façade and `/ask` draw the question their own way and share these rules. See [Yolo mode and permissions](yolo-and-permissions.md). | | 29 | | `consent` | Whether a tool call may run: the `yolo` switch (`Gate`), the four answers (`Answer`), what a session remembers per tool (`Policy`), and the hook through which the line mode asks (`SetPrompter`). The window, the editor façade and `/ask` draw the question their own way and share these rules. See [Yolo mode and permissions](yolo-and-permissions.md). | |
| 30 | | `hooks` | The `hooks:` block (tickets 36 and 87): runs each hook with `sh -c`, writes its JSON input, reads its reply, applies the chain order and `onError`, and returns decisions (`PreGeneration`, `PreToolCall`, `PostToolCall`, `PostGeneration`, and `PreAsk`, `PostAsk` for `ask`). No Genkit, no front end: `engine/hooks.go` applies the decisions around `Generate` and in the `WrapTool` middleware, so every front end gets them. See [Hooks](hooks.md). | | 30 | | `hooks` | The `hooks:` block (tickets 36 and 87): runs each hook with `sh -c`, writes its JSON input, reads its reply, applies the chain order and `onError`, and returns decisions (`PreGeneration`, `PreToolCall`, `PostToolCall`, `PostGeneration`, and `PreAsk`, `PostAsk` for `ask`). No Genkit, no front end: `engine/hooks.go` applies the decisions around `Generate` and in the `WrapTool` middleware, so every front end gets them. See [Hooks](hooks.md). | |
| 31 | | `agent` | The terminal REPL. | | 31 | | `agent` | The terminal REPL. | |
| 32 | -| `acp` | The Agent Client Protocol façade. | | 32 | +| `acp` | The Agent Client Protocol façade; also turns the tools an ACP client offers into session tools (`clienttools.go`, see [ACP client tools](acp-client-tools.md)). | |
| 33 | | `tui` | The single-window front end (`mm -tui`): a `ui.Sink` drawing on tcell, one transcript value, an event loop woken by the engine's and tools' goroutines. | | 33 | | `tui` | The single-window front end (`mm -tui`): a `ui.Sink` drawing on tcell, one transcript value, an event loop woken by the engine's and tools' goroutines. | |
| 34 | 34 | ||
| 35 | A draw.io diagram of these packages and their dependencies is kept at [`docs/diagrams/packages.drawio`](../../diagrams/packages.drawio). | 35 | A draw.io diagram of these packages and their dependencies is kept at [`docs/diagrams/packages.drawio`](../../diagrams/packages.drawio). |
modified
docs/en/explanation/yolo-and-permissions.md +2 -0 | @@ -21,6 +21,8 @@ So `yolo: true` with `bashTool: false` is an agent that edits files freely but h | ||
| 21 | 21 | |
| 22 | 22 | Only the calls that change something ask: `bash`, `write_file`, `edit_file`, `fetch_content` when it saves, `lsp` `rename`, and the `plan` actions that change a plan. Reads (`read_file`, `read_skill`, the `lsp` navigation actions, `fetch_content` without `output`) never ask: a dialog on every read teaches people to click "allow" without reading. |
| 23 | 23 | |
| 24 | +Tools that come from elsewhere have their own key instead: MCP tools (`mcp.permissions`, per server) and the tools an ACP client offers (`acp.clientTools.permissions`). Both read `ask` as "always ask" and `auto` as "ask only when `yolo` is off". A client tool defaults to `auto` because it is the user's own program lending the agent a function; an MCP server defaults to `ask` because it is third-party code. | |
| 25 | + | |
| 24 | 26 | ## Four answers, one memory |
| 25 | 27 | |
| 26 | 28 | When a call asks, the answer can be *once* or *for the session*, and *yes* or *no*: allow once, allow this tool for the session, reject this call, never allow this tool in this session. "Never" exists because a refusal that only lasts one call leaves you rejecting the same retry again and again; "reject once" exists because without it the only way to say no would be final. A session's remembered answers are kept per tool name, survive `/new` (the editor still sees the same session), and disappear when mm exits. |
| @@ -21,6 +21,8 @@ So `yolo: true` with `bashTool: false` is an agent that edits files freely but h | |||
| 21 | 21 | ||
| 22 | Only the calls that change something ask: `bash`, `write_file`, `edit_file`, `fetch_content` when it saves, `lsp` `rename`, and the `plan` actions that change a plan. Reads (`read_file`, `read_skill`, the `lsp` navigation actions, `fetch_content` without `output`) never ask: a dialog on every read teaches people to click "allow" without reading. | 22 | Only the calls that change something ask: `bash`, `write_file`, `edit_file`, `fetch_content` when it saves, `lsp` `rename`, and the `plan` actions that change a plan. Reads (`read_file`, `read_skill`, the `lsp` navigation actions, `fetch_content` without `output`) never ask: a dialog on every read teaches people to click "allow" without reading. |
| 23 | 23 | ||
| 24 | +Tools that come from elsewhere have their own key instead: MCP tools (`mcp.permissions`, per server) and the tools an ACP client offers (`acp.clientTools.permissions`). Both read `ask` as "always ask" and `auto` as "ask only when `yolo` is off". A client tool defaults to `auto` because it is the user's own program lending the agent a function; an MCP server defaults to `ask` because it is third-party code. | ||
| 25 | + | ||
| 24 | ## Four answers, one memory | 26 | ## Four answers, one memory |
| 25 | 27 | ||
| 26 | When a call asks, the answer can be *once* or *for the session*, and *yes* or *no*: allow once, allow this tool for the session, reject this call, never allow this tool in this session. "Never" exists because a refusal that only lasts one call leaves you rejecting the same retry again and again; "reject once" exists because without it the only way to say no would be final. A session's remembered answers are kept per tool name, survive `/new` (the editor still sees the same session), and disappear when mm exits. | 28 | When a call asks, the answer can be *once* or *for the session*, and *yes* or *no*: allow once, allow this tool for the session, reject this call, never allow this tool in this session. "Never" exists because a refusal that only lasts one call leaves you rejecting the same retry again and again; "reject once" exists because without it the only way to say no would be final. A session's remembered answers are kept per tool name, survive `/new` (the editor still sees the same session), and disappear when mm exits. |
modified
docs/en/reference/acp-client-tools.md +1 -0 | @@ -200,6 +200,7 @@ Every result, failed or not, is cut to `maxOutput` characters (head and tail kep | ||
| 200 | 200 | |
| 201 | 201 | ## See also |
| 202 | 202 | |
| 203 | +- [ACP client tools — explanation](../explanation/acp-client-tools.md) — why the protocol looks this way | |
| 203 | 204 | - [How to offer tools from your ACP client](../how-to/offer-tools-from-an-acp-client.md) |
| 204 | 205 | - [Agent Client Protocol surface](acp.md) |
| 205 | 206 | - [Configuration file — `acp` keys](configuration.md#acp-keys) |
| @@ -200,6 +200,7 @@ Every result, failed or not, is cut to `maxOutput` characters (head and tail kep | |||
| 200 | 200 | ||
| 201 | ## See also | 201 | ## See also |
| 202 | 202 | ||
| 203 | +- [ACP client tools — explanation](../explanation/acp-client-tools.md) — why the protocol looks this way | ||
| 203 | - [How to offer tools from your ACP client](../how-to/offer-tools-from-an-acp-client.md) | 204 | - [How to offer tools from your ACP client](../how-to/offer-tools-from-an-acp-client.md) |
| 204 | - [Agent Client Protocol surface](acp.md) | 205 | - [Agent Client Protocol surface](acp.md) |
| 205 | - [Configuration file — `acp` keys](configuration.md#acp-keys) | 206 | - [Configuration file — `acp` keys](configuration.md#acp-keys) |
modified
docs/en/reference/tools.md +17 -0 | @@ -403,6 +403,23 @@ An entry with `wasm:` instead of `command:` keeps every row above except those a | ||
| 403 | 403 | | ACP and window | Tool call of kind `execute`, title `<name>: <file>.wasm <input JSON>`. | |
| 404 | 404 | | Concurrency | Calls of one module may run at once, each in its own instance; no state survives a call. | |
| 405 | 405 | |
| 406 | +## Client tools (ACP) | |
| 407 | + | |
| 408 | +Tools offered by the ACP client, in `mm -acp` only; the protocol is specified in [ACP client tools](acp-client-tools.md), the settings are [`acp.clientTools`](configuration.md#acp-keys). | |
| 409 | + | |
| 410 | +| Aspect | Behaviour | | |
| 411 | +|--------|-----------| | |
| 412 | +| Name | `client_<name>`, `<name>` being the client's, with every character outside `[A-Za-z0-9]` turned into `_`. | | |
| 413 | +| Declared | Per session, from the client's list: at `initialize` (discovery `meta`) or from `_org.mini-me/tools/list` at `session/new`, `session/load`, `session/resume` (discovery `request`). A session with no list has no client tool. | | |
| 414 | +| Schema sent to the model | The client's `inputSchema` without `required`, `enum`, `additionalProperties: false` and `$schema`; the required parameters end the description (`Required parameters: …`), the `enum` values end the property's description (`One of: …`). | | |
| 415 | +| Execution | An ACP request to the tool's `method` (an extension method, `_…`), params = the model's arguments plus `_meta["org.mini-me/sessionId"]`. | | |
| 416 | +| Output | The text blocks of the answer's `content`, joined; any other JSON answer as its text. | | |
| 417 | +| Failure | `[client tool error: <reason>]`: a missing required parameter (nothing is sent), a JSON-RPC error, `isError: true`, no answer within `acp.clientTools.timeout` (30 s by default). The turn goes on. | | |
| 418 | +| Permission | `acp.clientTools.permissions`: `auto` (default) asks only when `yolo: false`, `ask` always asks. A refusal returns `[client_<name> rejected by user]`. | | |
| 419 | +| Truncation | `maxOutput`, head and tail kept, like `bash`. | | |
| 420 | +| ACP | Tool call of kind `other`, title `client: <name>`. | | |
| 421 | +| Terminal and window | Not available. | | |
| 422 | + | |
| 406 | 423 | ## Loop detection |
| 407 | 424 | |
| 408 | 425 | Every tool records `(tool, input, output)` in the loop detector after each call. |
| @@ -403,6 +403,23 @@ An entry with `wasm:` instead of `command:` keeps every row above except those a | |||
| 403 | | ACP and window | Tool call of kind `execute`, title `<name>: <file>.wasm <input JSON>`. | | 403 | | ACP and window | Tool call of kind `execute`, title `<name>: <file>.wasm <input JSON>`. | |
| 404 | | Concurrency | Calls of one module may run at once, each in its own instance; no state survives a call. | | 404 | | Concurrency | Calls of one module may run at once, each in its own instance; no state survives a call. | |
| 405 | 405 | ||
| 406 | +## Client tools (ACP) | ||
| 407 | + | ||
| 408 | +Tools offered by the ACP client, in `mm -acp` only; the protocol is specified in [ACP client tools](acp-client-tools.md), the settings are [`acp.clientTools`](configuration.md#acp-keys). | ||
| 409 | + | ||
| 410 | +| Aspect | Behaviour | | ||
| 411 | +|--------|-----------| | ||
| 412 | +| Name | `client_<name>`, `<name>` being the client's, with every character outside `[A-Za-z0-9]` turned into `_`. | | ||
| 413 | +| Declared | Per session, from the client's list: at `initialize` (discovery `meta`) or from `_org.mini-me/tools/list` at `session/new`, `session/load`, `session/resume` (discovery `request`). A session with no list has no client tool. | | ||
| 414 | +| Schema sent to the model | The client's `inputSchema` without `required`, `enum`, `additionalProperties: false` and `$schema`; the required parameters end the description (`Required parameters: …`), the `enum` values end the property's description (`One of: …`). | | ||
| 415 | +| Execution | An ACP request to the tool's `method` (an extension method, `_…`), params = the model's arguments plus `_meta["org.mini-me/sessionId"]`. | | ||
| 416 | +| Output | The text blocks of the answer's `content`, joined; any other JSON answer as its text. | | ||
| 417 | +| Failure | `[client tool error: <reason>]`: a missing required parameter (nothing is sent), a JSON-RPC error, `isError: true`, no answer within `acp.clientTools.timeout` (30 s by default). The turn goes on. | | ||
| 418 | +| Permission | `acp.clientTools.permissions`: `auto` (default) asks only when `yolo: false`, `ask` always asks. A refusal returns `[client_<name> rejected by user]`. | | ||
| 419 | +| Truncation | `maxOutput`, head and tail kept, like `bash`. | | ||
| 420 | +| ACP | Tool call of kind `other`, title `client: <name>`. | | ||
| 421 | +| Terminal and window | Not available. | | ||
| 422 | + | ||
| 406 | ## Loop detection | 423 | ## Loop detection |
| 407 | 424 | ||
| 408 | Every tool records `(tool, input, output)` in the loop detector after each call. | 425 | Every tool records `(tool, input, output)` in the loop detector after each call. |
modified
docs/fr/README.md +1 -0 | @@ -101,6 +101,7 @@ Cette documentation est organisée selon les quatre quadrants [Diátaxis](https: | ||
| 101 | 101 | - [Les hooks](explanation/hooks.md) — pourquoi des commandes externes, pourquoi ils se branchent sur `engine.Generate` et `WrapTool`, pourquoi une question arrêtée laisse un marqueur, ce que le ticket 36 a laissé de côté, pourquoi `preAsk`/`postAsk` appartiennent à l'agent qui appelle |
| 102 | 102 | - [Les modèles de garde](explanation/guard-models.md) — pourquoi le message système est un nom de risque, pourquoi chaque garde juge la question seule (`-p`), pourquoi un hook plutôt que `ask_agent`, qui réagit (le modèle ou le hook), échouer fermé, ce que les gardes ne donnent pas |
| 103 | 103 | - [Les modèles de décision](explanation/decision-models.md) — pourquoi un décideur est un agent, pourquoi le routage ajoute la ligne `$skill`, la confiance du serveur, ce qui a été mesuré (0 mauvais choix sur 23 cas ; 12 → 16 justes avec un modèle de chat 4B), pourquoi pas decision-4b ; pourquoi le tri des commandes et le routage d'outils ne font que retirer (questions, outils), un décideur seul |
| 104 | +- [Outils du client ACP](explanation/acp-client-tools.md) — pourquoi mm avait besoin de sa propre convention au-dessus des méthodes d'extension d'ACP, pourquoi la découverte `meta` par défaut, la session dans `_meta`, des outils dynamiques par session, les erreurs comme réponses, pourquoi pas `mcpServers` | |
| 104 | 105 | - Diagramme des paquets (source draw.io) : [`docs/diagrams/packages.drawio`](../diagrams/packages.drawio) |
| 105 | 106 | |
| 106 | 107 | ### Explication MCP (Model Context Protocol) |
| @@ -101,6 +101,7 @@ Cette documentation est organisée selon les quatre quadrants [Diátaxis](https: | |||
| 101 | - [Les hooks](explanation/hooks.md) — pourquoi des commandes externes, pourquoi ils se branchent sur `engine.Generate` et `WrapTool`, pourquoi une question arrêtée laisse un marqueur, ce que le ticket 36 a laissé de côté, pourquoi `preAsk`/`postAsk` appartiennent à l'agent qui appelle | 101 | - [Les hooks](explanation/hooks.md) — pourquoi des commandes externes, pourquoi ils se branchent sur `engine.Generate` et `WrapTool`, pourquoi une question arrêtée laisse un marqueur, ce que le ticket 36 a laissé de côté, pourquoi `preAsk`/`postAsk` appartiennent à l'agent qui appelle |
| 102 | - [Les modèles de garde](explanation/guard-models.md) — pourquoi le message système est un nom de risque, pourquoi chaque garde juge la question seule (`-p`), pourquoi un hook plutôt que `ask_agent`, qui réagit (le modèle ou le hook), échouer fermé, ce que les gardes ne donnent pas | 102 | - [Les modèles de garde](explanation/guard-models.md) — pourquoi le message système est un nom de risque, pourquoi chaque garde juge la question seule (`-p`), pourquoi un hook plutôt que `ask_agent`, qui réagit (le modèle ou le hook), échouer fermé, ce que les gardes ne donnent pas |
| 103 | - [Les modèles de décision](explanation/decision-models.md) — pourquoi un décideur est un agent, pourquoi le routage ajoute la ligne `$skill`, la confiance du serveur, ce qui a été mesuré (0 mauvais choix sur 23 cas ; 12 → 16 justes avec un modèle de chat 4B), pourquoi pas decision-4b ; pourquoi le tri des commandes et le routage d'outils ne font que retirer (questions, outils), un décideur seul | 103 | - [Les modèles de décision](explanation/decision-models.md) — pourquoi un décideur est un agent, pourquoi le routage ajoute la ligne `$skill`, la confiance du serveur, ce qui a été mesuré (0 mauvais choix sur 23 cas ; 12 → 16 justes avec un modèle de chat 4B), pourquoi pas decision-4b ; pourquoi le tri des commandes et le routage d'outils ne font que retirer (questions, outils), un décideur seul |
| 104 | +- [Outils du client ACP](explanation/acp-client-tools.md) — pourquoi mm avait besoin de sa propre convention au-dessus des méthodes d'extension d'ACP, pourquoi la découverte `meta` par défaut, la session dans `_meta`, des outils dynamiques par session, les erreurs comme réponses, pourquoi pas `mcpServers` | ||
| 104 | - Diagramme des paquets (source draw.io) : [`docs/diagrams/packages.drawio`](../diagrams/packages.drawio) | 105 | - Diagramme des paquets (source draw.io) : [`docs/diagrams/packages.drawio`](../diagrams/packages.drawio) |
| 105 | 106 | ||
| 106 | ### Explication MCP (Model Context Protocol) | 107 | ### Explication MCP (Model Context Protocol) |
added
docs/fr/explanation/acp-client-tools.md +47 -0 | new file mode 100644 | ||
| @@ -0,0 +1,47 @@ | ||
| 1 | +# Outils du client ACP — explication | |
| 2 | + | |
| 3 | +## De quoi s'agit-il ? | |
| 4 | + | |
| 5 | +Un éditeur, ou tout programme qui pilote `mm -acp`, sait souvent des choses que l'agent ne peut pas atteindre : les buffers ouverts à l'écran, la sélection, les données d'une application, un appareil. Les outils du client ACP permettent à ce programme de prêter à l'agent ses propres fonctions. Le modèle les appelle comme `bash` ou `read_file` ; mm transmet chaque appel au client sous forme de requête ACP et rend la réponse au modèle. Le protocole exact est dans la [référence](../reference/acp-client-tools.md) ; cette page explique pourquoi il a cette forme. | |
| 6 | + | |
| 7 | +## Pourquoi mm avait besoin de sa propre convention | |
| 8 | + | |
| 9 | +ACP a un mécanisme d'extension : chaque côté peut définir des méthodes dont le nom commence par `_`, et les annoncer dans l'objet `_meta` des capacités échangées à `initialize`. Cela suffit pour *appeler* une méthode, pas pour en faire un *outil* : un modèle a besoin d'un nom, d'une description et d'un schéma des arguments, et ACP ne définit aucun moyen de décrire tout cela. mm en définit donc un, sous son propre espace de noms (`org.mini-me`), en n'utilisant que ce qu'ACP fournit déjà — `_meta` pour l'annonce, les méthodes d'extension pour les appels. Un client qui n'en sait rien n'envoie rien, et rien ne change. | |
| 10 | + | |
| 11 | +La description d'un outil reprend volontairement celle de MCP (`name`, `description`, `inputSchema`), et la réponse aussi (blocs `content`, `isError`). Quiconque a écrit un outil MCP connaît déjà les deux formes, et mm savait déjà présenter un schéma MCP à un petit modèle (le même nettoyage de `required` et `enum` s'applique, pour les mêmes raisons : voir [Choix de conception MCP](mcp-decisions.md)). | |
| 12 | + | |
| 13 | +## Deux modes de découverte, et pourquoi `meta` par défaut | |
| 14 | + | |
| 15 | +- **`meta`** : la liste voyage dans `initialize`. Un seul message, rien à répondre, et la liste est connue avant qu'aucune session n'existe. La plupart des clients ont un jeu d'outils fixe, et pour eux c'est le moins de code à écrire. D'où le choix par défaut. | |
| 16 | +- **`request`** : le client dit seulement qu'il a des outils, et mm demande la liste à chaque ouverture, chargement ou reprise de session. C'est pour un client dont les outils dépendent de la session — son répertoire de travail, l'utilisateur derrière — ou changent avec le temps. | |
| 17 | + | |
| 18 | +Le mode `request` a une contrainte à connaître : mm envoie la requête de liste pendant que le client attend encore sa réponse à `session/new`. Un client qui traiterait les requêtes une par une attendrait mm pendant que mm l'attend. Les SDK ACP (Go, TypeScript) traitent chaque requête entrante à part, donc cela ne leur coûte rien ; c'est écrit dans la spécification pour les clients qui font leur propre JSON-RPC. | |
| 19 | + | |
| 20 | +## Pourquoi la session voyage dans `_meta`, et les arguments au premier niveau | |
| 21 | + | |
| 22 | +Les `params` de l'appel sont exactement les arguments du modèle. Cela permet d'utiliser telle quelle une méthode d'extension existante : un client qui répond déjà à `_com.example/workspace/buffers {"language": "go"}` peut l'exposer comme outil sans adaptateur. L'id de session n'est pas un argument choisi par le modèle : il va là où ACP met les données hors bande — `_meta` — et le modèle ne peut pas le falsifier : un `_meta` qu'il aurait inventé est remplacé. | |
| 23 | + | |
| 24 | +## Pourquoi les outils sont construits par session et jamais enregistrés | |
| 25 | + | |
| 26 | +Genkit tient un registre d'outils nommés, et enregistrer deux fois le même nom est une erreur. Les outils du client changent selon la connexion (et selon la session en mode `request`) : mm les construit donc comme outils *dynamiques* (`ai.NewTool`), que Genkit enregistre dans un registre enfant le temps d'une génération. Deux sessions peuvent alors avoir un `client_open_buffers` défini différemment, et rien n'est à désenregistrer quand une session se termine. | |
| 27 | + | |
| 28 | +## Pourquoi les erreurs sont des réponses | |
| 29 | + | |
| 30 | +Un client qui ne répond pas, répond `Method not found` ou signale un échec donne au modèle un texte `[client tool error: …]`, et le tour continue. C'est la règle générale de mm (voir [Architecture](architecture.md)) : c'est le modèle qui doit réagir, c'est donc lui qui doit lire ce qui n'a pas marché. La même règle vaut pour une liste mal formée : une entrée inutilisable est ignorée avec une ligne sur stderr, et la session s'ouvre avec les autres. L'erreur d'un client ne doit pas coûter la conversation à l'utilisateur. | |
| 31 | + | |
| 32 | +## Permissions | |
| 33 | + | |
| 34 | +Les outils du client suivent la règle des outils venus d'ailleurs, avec leur propre clé (`acp.clientTools.permissions`) : `ask` demande toujours, `auto` ne demande que si `yolo` est désactivé. La valeur par défaut est `auto`, là où celle de MCP est `ask`, parce que la confiance n'est pas la même : un serveur MCP est du code tiers, un outil client est une fonction du programme même que l'utilisateur a lancé pour parler à l'agent. Voir [Mode yolo et permissions](yolo-and-permissions.md). | |
| 35 | + | |
| 36 | +## Alternatives écartées | |
| 37 | + | |
| 38 | +- **Utiliser les `mcpServers` de `session/new`.** ACP permet à un client de donner à l'agent des serveurs MCP auxquels se connecter. Cela aurait obligé le client à faire tourner un serveur MCP — un second protocole, un second transport — pour exposer une fonction qu'il détient déjà dans le même processus, au bout du tuyau qu'il partage déjà avec mm. | |
| 39 | +- **Une méthode générique, `_org.mini-me/tools/call {name, arguments}`.** Plus simple à aiguiller, mais elle imposerait à chaque client le nom de méthode de mm ; une `method` par outil permet à un client d'exposer des méthodes d'extension qu'il a déjà. | |
| 40 | +- **Déclarer les outils dans `agent.yaml`.** Le fichier d'agent ne sait pas quel client le lancera, et le même agent peut être piloté par un éditeur aujourd'hui et par un script demain. Les outils appartiennent au client : c'est au client de les annoncer. | |
| 41 | +- **Un seul mode de découverte.** `meta` seul ne peut pas servir des outils qui dépendent de la session ; `request` seul impose à chaque client un handler de requête et un aller-retour pour une liste généralement fixe. | |
| 42 | + | |
| 43 | +## Lien avec le reste | |
| 44 | + | |
| 45 | +- Les outils n'existent qu'en mode ACP : le terminal et la fenêtre n'ont pas de client pour les prêter. | |
| 46 | +- Une fois déclarés, ce sont des outils ordinaires pour le reste de mm : le [routage des outils](decision-models.md) peut les écarter d'un tour quand un de ses groupes correspond à leur nom (`^client_`), les [hooks](hooks.md) voient leurs appels, le détecteur de boucles les compte. | |
| 47 | +- [`demo.acp.sdk/go`](../../../demo.acp.sdk/go/README.fr.md) (`Options.Tools`) et [`demo.acp.sdk/client-tools-nodejs`](../../../demo.acp.sdk/client-tools-nodejs/README.fr.md) sont deux implémentations client ; [Proposer des outils depuis votre client ACP](../how-to/offer-tools-from-an-acp-client.md) en détaille une. | |
| new file mode 100644 | |||
| @@ -0,0 +1,47 @@ | |||
| 1 | +# Outils du client ACP — explication | ||
| 2 | + | ||
| 3 | +## De quoi s'agit-il ? | ||
| 4 | + | ||
| 5 | +Un éditeur, ou tout programme qui pilote `mm -acp`, sait souvent des choses que l'agent ne peut pas atteindre : les buffers ouverts à l'écran, la sélection, les données d'une application, un appareil. Les outils du client ACP permettent à ce programme de prêter à l'agent ses propres fonctions. Le modèle les appelle comme `bash` ou `read_file` ; mm transmet chaque appel au client sous forme de requête ACP et rend la réponse au modèle. Le protocole exact est dans la [référence](../reference/acp-client-tools.md) ; cette page explique pourquoi il a cette forme. | ||
| 6 | + | ||
| 7 | +## Pourquoi mm avait besoin de sa propre convention | ||
| 8 | + | ||
| 9 | +ACP a un mécanisme d'extension : chaque côté peut définir des méthodes dont le nom commence par `_`, et les annoncer dans l'objet `_meta` des capacités échangées à `initialize`. Cela suffit pour *appeler* une méthode, pas pour en faire un *outil* : un modèle a besoin d'un nom, d'une description et d'un schéma des arguments, et ACP ne définit aucun moyen de décrire tout cela. mm en définit donc un, sous son propre espace de noms (`org.mini-me`), en n'utilisant que ce qu'ACP fournit déjà — `_meta` pour l'annonce, les méthodes d'extension pour les appels. Un client qui n'en sait rien n'envoie rien, et rien ne change. | ||
| 10 | + | ||
| 11 | +La description d'un outil reprend volontairement celle de MCP (`name`, `description`, `inputSchema`), et la réponse aussi (blocs `content`, `isError`). Quiconque a écrit un outil MCP connaît déjà les deux formes, et mm savait déjà présenter un schéma MCP à un petit modèle (le même nettoyage de `required` et `enum` s'applique, pour les mêmes raisons : voir [Choix de conception MCP](mcp-decisions.md)). | ||
| 12 | + | ||
| 13 | +## Deux modes de découverte, et pourquoi `meta` par défaut | ||
| 14 | + | ||
| 15 | +- **`meta`** : la liste voyage dans `initialize`. Un seul message, rien à répondre, et la liste est connue avant qu'aucune session n'existe. La plupart des clients ont un jeu d'outils fixe, et pour eux c'est le moins de code à écrire. D'où le choix par défaut. | ||
| 16 | +- **`request`** : le client dit seulement qu'il a des outils, et mm demande la liste à chaque ouverture, chargement ou reprise de session. C'est pour un client dont les outils dépendent de la session — son répertoire de travail, l'utilisateur derrière — ou changent avec le temps. | ||
| 17 | + | ||
| 18 | +Le mode `request` a une contrainte à connaître : mm envoie la requête de liste pendant que le client attend encore sa réponse à `session/new`. Un client qui traiterait les requêtes une par une attendrait mm pendant que mm l'attend. Les SDK ACP (Go, TypeScript) traitent chaque requête entrante à part, donc cela ne leur coûte rien ; c'est écrit dans la spécification pour les clients qui font leur propre JSON-RPC. | ||
| 19 | + | ||
| 20 | +## Pourquoi la session voyage dans `_meta`, et les arguments au premier niveau | ||
| 21 | + | ||
| 22 | +Les `params` de l'appel sont exactement les arguments du modèle. Cela permet d'utiliser telle quelle une méthode d'extension existante : un client qui répond déjà à `_com.example/workspace/buffers {"language": "go"}` peut l'exposer comme outil sans adaptateur. L'id de session n'est pas un argument choisi par le modèle : il va là où ACP met les données hors bande — `_meta` — et le modèle ne peut pas le falsifier : un `_meta` qu'il aurait inventé est remplacé. | ||
| 23 | + | ||
| 24 | +## Pourquoi les outils sont construits par session et jamais enregistrés | ||
| 25 | + | ||
| 26 | +Genkit tient un registre d'outils nommés, et enregistrer deux fois le même nom est une erreur. Les outils du client changent selon la connexion (et selon la session en mode `request`) : mm les construit donc comme outils *dynamiques* (`ai.NewTool`), que Genkit enregistre dans un registre enfant le temps d'une génération. Deux sessions peuvent alors avoir un `client_open_buffers` défini différemment, et rien n'est à désenregistrer quand une session se termine. | ||
| 27 | + | ||
| 28 | +## Pourquoi les erreurs sont des réponses | ||
| 29 | + | ||
| 30 | +Un client qui ne répond pas, répond `Method not found` ou signale un échec donne au modèle un texte `[client tool error: …]`, et le tour continue. C'est la règle générale de mm (voir [Architecture](architecture.md)) : c'est le modèle qui doit réagir, c'est donc lui qui doit lire ce qui n'a pas marché. La même règle vaut pour une liste mal formée : une entrée inutilisable est ignorée avec une ligne sur stderr, et la session s'ouvre avec les autres. L'erreur d'un client ne doit pas coûter la conversation à l'utilisateur. | ||
| 31 | + | ||
| 32 | +## Permissions | ||
| 33 | + | ||
| 34 | +Les outils du client suivent la règle des outils venus d'ailleurs, avec leur propre clé (`acp.clientTools.permissions`) : `ask` demande toujours, `auto` ne demande que si `yolo` est désactivé. La valeur par défaut est `auto`, là où celle de MCP est `ask`, parce que la confiance n'est pas la même : un serveur MCP est du code tiers, un outil client est une fonction du programme même que l'utilisateur a lancé pour parler à l'agent. Voir [Mode yolo et permissions](yolo-and-permissions.md). | ||
| 35 | + | ||
| 36 | +## Alternatives écartées | ||
| 37 | + | ||
| 38 | +- **Utiliser les `mcpServers` de `session/new`.** ACP permet à un client de donner à l'agent des serveurs MCP auxquels se connecter. Cela aurait obligé le client à faire tourner un serveur MCP — un second protocole, un second transport — pour exposer une fonction qu'il détient déjà dans le même processus, au bout du tuyau qu'il partage déjà avec mm. | ||
| 39 | +- **Une méthode générique, `_org.mini-me/tools/call {name, arguments}`.** Plus simple à aiguiller, mais elle imposerait à chaque client le nom de méthode de mm ; une `method` par outil permet à un client d'exposer des méthodes d'extension qu'il a déjà. | ||
| 40 | +- **Déclarer les outils dans `agent.yaml`.** Le fichier d'agent ne sait pas quel client le lancera, et le même agent peut être piloté par un éditeur aujourd'hui et par un script demain. Les outils appartiennent au client : c'est au client de les annoncer. | ||
| 41 | +- **Un seul mode de découverte.** `meta` seul ne peut pas servir des outils qui dépendent de la session ; `request` seul impose à chaque client un handler de requête et un aller-retour pour une liste généralement fixe. | ||
| 42 | + | ||
| 43 | +## Lien avec le reste | ||
| 44 | + | ||
| 45 | +- Les outils n'existent qu'en mode ACP : le terminal et la fenêtre n'ont pas de client pour les prêter. | ||
| 46 | +- Une fois déclarés, ce sont des outils ordinaires pour le reste de mm : le [routage des outils](decision-models.md) peut les écarter d'un tour quand un de ses groupes correspond à leur nom (`^client_`), les [hooks](hooks.md) voient leurs appels, le détecteur de boucles les compte. | ||
| 47 | +- [`demo.acp.sdk/go`](../../../demo.acp.sdk/go/README.fr.md) (`Options.Tools`) et [`demo.acp.sdk/client-tools-nodejs`](../../../demo.acp.sdk/client-tools-nodejs/README.fr.md) sont deux implémentations client ; [Proposer des outils depuis votre client ACP](../how-to/offer-tools-from-an-acp-client.md) en détaille une. | ||
modified
docs/fr/explanation/architecture.md +1 -1 | @@ -29,7 +29,7 @@ Le point d'entrée, `main.go`, ne fait que du câblage : il charge les réglages | ||
| 29 | 29 | | `consent` | Si un appel d'outil peut s'exécuter : l'interrupteur `yolo` (`Gate`), les quatre réponses (`Answer`), ce qu'une session retient par outil (`Policy`), et le point d'accroche par lequel le mode ligne demande (`SetPrompter`). La fenêtre, la façade éditeur et `/ask` dessinent la question à leur manière et partagent ces règles. Voir [Le mode yolo et les permissions](yolo-and-permissions.md). | |
| 30 | 30 | | `hooks` | Le bloc `hooks:` (tickets 36 et 87) : lance chaque hook avec `sh -c`, écrit son entrée JSON, lit sa réponse, applique l'ordre de la chaîne et `onError`, et renvoie des décisions (`PreGeneration`, `PreToolCall`, `PostToolCall`, `PostGeneration`, et `PreAsk`, `PostAsk` pour `ask`). Ni Genkit ni front end : `engine/hooks.go` applique les décisions autour de `Generate` et dans le middleware `WrapTool`, si bien que tous les front ends en profitent. Voir [Les hooks](hooks.md). | |
| 31 | 31 | | `agent` | Le REPL du terminal. | |
| 32 | -| `acp` | La façade Agent Client Protocol. | | |
| 32 | +| `acp` | La façade Agent Client Protocol ; transforme aussi les outils qu'offre un client ACP en outils de session (`clienttools.go`, voir [Outils du client ACP](acp-client-tools.md)). | | |
| 33 | 33 | | `tui` | La façade fenêtre unique (`mm -tui`) : un `ui.Sink` qui dessine sur tcell, une valeur de transcription, une boucle d'événements réveillée par les goroutines du moteur et des outils. | |
| 34 | 34 | |
| 35 | 35 | Un diagramme draw.io de ces paquets et de leurs dépendances est conservé dans [`docs/diagrams/packages.drawio`](../../diagrams/packages.drawio). |
| @@ -29,7 +29,7 @@ Le point d'entrée, `main.go`, ne fait que du câblage : il charge les réglages | |||
| 29 | | `consent` | Si un appel d'outil peut s'exécuter : l'interrupteur `yolo` (`Gate`), les quatre réponses (`Answer`), ce qu'une session retient par outil (`Policy`), et le point d'accroche par lequel le mode ligne demande (`SetPrompter`). La fenêtre, la façade éditeur et `/ask` dessinent la question à leur manière et partagent ces règles. Voir [Le mode yolo et les permissions](yolo-and-permissions.md). | | 29 | | `consent` | Si un appel d'outil peut s'exécuter : l'interrupteur `yolo` (`Gate`), les quatre réponses (`Answer`), ce qu'une session retient par outil (`Policy`), et le point d'accroche par lequel le mode ligne demande (`SetPrompter`). La fenêtre, la façade éditeur et `/ask` dessinent la question à leur manière et partagent ces règles. Voir [Le mode yolo et les permissions](yolo-and-permissions.md). | |
| 30 | | `hooks` | Le bloc `hooks:` (tickets 36 et 87) : lance chaque hook avec `sh -c`, écrit son entrée JSON, lit sa réponse, applique l'ordre de la chaîne et `onError`, et renvoie des décisions (`PreGeneration`, `PreToolCall`, `PostToolCall`, `PostGeneration`, et `PreAsk`, `PostAsk` pour `ask`). Ni Genkit ni front end : `engine/hooks.go` applique les décisions autour de `Generate` et dans le middleware `WrapTool`, si bien que tous les front ends en profitent. Voir [Les hooks](hooks.md). | | 30 | | `hooks` | Le bloc `hooks:` (tickets 36 et 87) : lance chaque hook avec `sh -c`, écrit son entrée JSON, lit sa réponse, applique l'ordre de la chaîne et `onError`, et renvoie des décisions (`PreGeneration`, `PreToolCall`, `PostToolCall`, `PostGeneration`, et `PreAsk`, `PostAsk` pour `ask`). Ni Genkit ni front end : `engine/hooks.go` applique les décisions autour de `Generate` et dans le middleware `WrapTool`, si bien que tous les front ends en profitent. Voir [Les hooks](hooks.md). | |
| 31 | | `agent` | Le REPL du terminal. | | 31 | | `agent` | Le REPL du terminal. | |
| 32 | -| `acp` | La façade Agent Client Protocol. | | 32 | +| `acp` | La façade Agent Client Protocol ; transforme aussi les outils qu'offre un client ACP en outils de session (`clienttools.go`, voir [Outils du client ACP](acp-client-tools.md)). | |
| 33 | | `tui` | La façade fenêtre unique (`mm -tui`) : un `ui.Sink` qui dessine sur tcell, une valeur de transcription, une boucle d'événements réveillée par les goroutines du moteur et des outils. | | 33 | | `tui` | La façade fenêtre unique (`mm -tui`) : un `ui.Sink` qui dessine sur tcell, une valeur de transcription, une boucle d'événements réveillée par les goroutines du moteur et des outils. | |
| 34 | 34 | ||
| 35 | Un diagramme draw.io de ces paquets et de leurs dépendances est conservé dans [`docs/diagrams/packages.drawio`](../../diagrams/packages.drawio). | 35 | Un diagramme draw.io de ces paquets et de leurs dépendances est conservé dans [`docs/diagrams/packages.drawio`](../../diagrams/packages.drawio). |
modified
docs/fr/explanation/yolo-and-permissions.md +2 -0 | @@ -21,6 +21,8 @@ Ainsi, `yolo: true` avec `bashTool: false` est un agent qui modifie librement le | ||
| 21 | 21 | |
| 22 | 22 | Seuls les appels qui modifient quelque chose demandent : `bash`, `write_file`, `edit_file`, `fetch_content` quand il enregistre, `rename` de `lsp`, et les actions de `plan` qui modifient un plan. Les lectures (`read_file`, `read_skill`, les actions de navigation de `lsp`, `fetch_content` sans `output`) ne demandent jamais : une question à chaque lecture apprend aux gens à cliquer « autoriser » sans lire. |
| 23 | 23 | |
| 24 | +Les outils venus d'ailleurs ont plutôt leur propre clé : les outils MCP (`mcp.permissions`, par serveur) et les outils qu'offre un client ACP (`acp.clientTools.permissions`). Les deux lisent `ask` comme « toujours demander » et `auto` comme « ne demander que si `yolo` est désactivé ». Un outil client vaut `auto` par défaut parce que c'est le propre programme de l'utilisateur qui prête une fonction à l'agent ; un serveur MCP vaut `ask` par défaut parce que c'est du code tiers. | |
| 25 | + | |
| 24 | 26 | ## Quatre réponses, une mémoire |
| 25 | 27 | |
| 26 | 28 | Quand un appel demande, la réponse peut valoir *une fois* ou *pour la session*, et *oui* ou *non* : autoriser une fois, autoriser cet outil pour la session, refuser cet appel, ne jamais autoriser cet outil dans cette session. « Jamais » existe parce qu'un refus qui ne dure qu'un appel oblige à refuser la même nouvelle tentative encore et encore ; « refuser une fois » existe parce que sans lui, la seule façon de dire non serait définitive. Les réponses mémorisées d'une session le sont par nom d'outil, survivent à `/new` (l'éditeur voit toujours la même session) et disparaissent quand mm s'arrête. |
| @@ -21,6 +21,8 @@ Ainsi, `yolo: true` avec `bashTool: false` est un agent qui modifie librement le | |||
| 21 | 21 | ||
| 22 | Seuls les appels qui modifient quelque chose demandent : `bash`, `write_file`, `edit_file`, `fetch_content` quand il enregistre, `rename` de `lsp`, et les actions de `plan` qui modifient un plan. Les lectures (`read_file`, `read_skill`, les actions de navigation de `lsp`, `fetch_content` sans `output`) ne demandent jamais : une question à chaque lecture apprend aux gens à cliquer « autoriser » sans lire. | 22 | Seuls les appels qui modifient quelque chose demandent : `bash`, `write_file`, `edit_file`, `fetch_content` quand il enregistre, `rename` de `lsp`, et les actions de `plan` qui modifient un plan. Les lectures (`read_file`, `read_skill`, les actions de navigation de `lsp`, `fetch_content` sans `output`) ne demandent jamais : une question à chaque lecture apprend aux gens à cliquer « autoriser » sans lire. |
| 23 | 23 | ||
| 24 | +Les outils venus d'ailleurs ont plutôt leur propre clé : les outils MCP (`mcp.permissions`, par serveur) et les outils qu'offre un client ACP (`acp.clientTools.permissions`). Les deux lisent `ask` comme « toujours demander » et `auto` comme « ne demander que si `yolo` est désactivé ». Un outil client vaut `auto` par défaut parce que c'est le propre programme de l'utilisateur qui prête une fonction à l'agent ; un serveur MCP vaut `ask` par défaut parce que c'est du code tiers. | ||
| 25 | + | ||
| 24 | ## Quatre réponses, une mémoire | 26 | ## Quatre réponses, une mémoire |
| 25 | 27 | ||
| 26 | Quand un appel demande, la réponse peut valoir *une fois* ou *pour la session*, et *oui* ou *non* : autoriser une fois, autoriser cet outil pour la session, refuser cet appel, ne jamais autoriser cet outil dans cette session. « Jamais » existe parce qu'un refus qui ne dure qu'un appel oblige à refuser la même nouvelle tentative encore et encore ; « refuser une fois » existe parce que sans lui, la seule façon de dire non serait définitive. Les réponses mémorisées d'une session le sont par nom d'outil, survivent à `/new` (l'éditeur voit toujours la même session) et disparaissent quand mm s'arrête. | 28 | Quand un appel demande, la réponse peut valoir *une fois* ou *pour la session*, et *oui* ou *non* : autoriser une fois, autoriser cet outil pour la session, refuser cet appel, ne jamais autoriser cet outil dans cette session. « Jamais » existe parce qu'un refus qui ne dure qu'un appel oblige à refuser la même nouvelle tentative encore et encore ; « refuser une fois » existe parce que sans lui, la seule façon de dire non serait définitive. Les réponses mémorisées d'une session le sont par nom d'outil, survivent à `/new` (l'éditeur voit toujours la même session) et disparaissent quand mm s'arrête. |
modified
docs/fr/reference/acp-client-tools.md +1 -0 | @@ -200,6 +200,7 @@ Chaque résultat, en échec ou non, est coupé à `maxOutput` caractères (débu | ||
| 200 | 200 | |
| 201 | 201 | ## Voir aussi |
| 202 | 202 | |
| 203 | +- [Outils du client ACP — explication](../explanation/acp-client-tools.md) — pourquoi le protocole a cette forme | |
| 203 | 204 | - [Proposer des outils depuis votre client ACP](../how-to/offer-tools-from-an-acp-client.md) |
| 204 | 205 | - [Surface Agent Client Protocol](acp.md) |
| 205 | 206 | - [Fichier de configuration — clés de `acp`](configuration.md#clés-de-acp) |
| @@ -200,6 +200,7 @@ Chaque résultat, en échec ou non, est coupé à `maxOutput` caractères (débu | |||
| 200 | 200 | ||
| 201 | ## Voir aussi | 201 | ## Voir aussi |
| 202 | 202 | ||
| 203 | +- [Outils du client ACP — explication](../explanation/acp-client-tools.md) — pourquoi le protocole a cette forme | ||
| 203 | - [Proposer des outils depuis votre client ACP](../how-to/offer-tools-from-an-acp-client.md) | 204 | - [Proposer des outils depuis votre client ACP](../how-to/offer-tools-from-an-acp-client.md) |
| 204 | - [Surface Agent Client Protocol](acp.md) | 205 | - [Surface Agent Client Protocol](acp.md) |
| 205 | - [Fichier de configuration — clés de `acp`](configuration.md#clés-de-acp) | 206 | - [Fichier de configuration — clés de `acp`](configuration.md#clés-de-acp) |
modified
docs/fr/reference/tools.md +17 -0 | @@ -403,6 +403,23 @@ Une entrée avec `wasm:` au lieu de `command:` garde toutes les lignes ci-dessus | ||
| 403 | 403 | | ACP et fenêtre | Appel d'outil de type `execute`, titre `<nom>: <fichier>.wasm <JSON d'entrée>`. | |
| 404 | 404 | | Concurrence | Les appels d'un même module peuvent s'exécuter en même temps, chacun dans sa propre instance ; aucun état ne survit à un appel. | |
| 405 | 405 | |
| 406 | +## Outils du client (ACP) | |
| 407 | + | |
| 408 | +Outils proposés par le client ACP, en `mm -acp` seulement ; le protocole est spécifié dans [Outils du client ACP](acp-client-tools.md), les réglages sont [`acp.clientTools`](configuration.md#clés-de-acp). | |
| 409 | + | |
| 410 | +| Aspect | Comportement | | |
| 411 | +|--------|--------------| | |
| 412 | +| Nom | `client_<name>`, `<name>` étant celui du client, où chaque caractère hors de `[A-Za-z0-9]` devient `_`. | | |
| 413 | +| Déclaration | Par session, d'après la liste du client : à `initialize` (découverte `meta`) ou par `_org.mini-me/tools/list` à `session/new`, `session/load`, `session/resume` (découverte `request`). Une session sans liste n'a pas d'outil client. | | |
| 414 | +| Schéma envoyé au modèle | L'`inputSchema` du client sans `required`, `enum`, `additionalProperties: false` ni `$schema` ; les paramètres obligatoires terminent la description (`Required parameters: …`), les valeurs d'un `enum` terminent la description de la propriété (`One of: …`). | | |
| 415 | +| Exécution | Une requête ACP vers la `method` de l'outil (une méthode d'extension, `_…`), params = les arguments du modèle plus `_meta["org.mini-me/sessionId"]`. | | |
| 416 | +| Sortie | Les blocs texte du `content` de la réponse, concaténés ; toute autre réponse JSON sous forme de texte. | | |
| 417 | +| Échec | `[client tool error: <raison>]` : un paramètre obligatoire manquant (rien n'est envoyé), une erreur JSON-RPC, `isError: true`, pas de réponse dans le délai `acp.clientTools.timeout` (30 s par défaut). Le tour continue. | | |
| 418 | +| Permission | `acp.clientTools.permissions` : `auto` (par défaut) ne demande que si `yolo: false`, `ask` demande toujours. Un refus renvoie `[client_<name> rejected by user]`. | | |
| 419 | +| Troncature | `maxOutput`, début et fin conservés, comme `bash`. | | |
| 420 | +| ACP | Appel d'outil de genre `other`, titre `client: <name>`. | | |
| 421 | +| Terminal et fenêtre | Non disponibles. | | |
| 422 | + | |
| 406 | 423 | ## Détection de boucle |
| 407 | 424 | |
| 408 | 425 | Chaque outil enregistre `(outil, entrée, sortie)` dans le détecteur de boucle après chaque appel. |
| @@ -403,6 +403,23 @@ Une entrée avec `wasm:` au lieu de `command:` garde toutes les lignes ci-dessus | |||
| 403 | | ACP et fenêtre | Appel d'outil de type `execute`, titre `<nom>: <fichier>.wasm <JSON d'entrée>`. | | 403 | | ACP et fenêtre | Appel d'outil de type `execute`, titre `<nom>: <fichier>.wasm <JSON d'entrée>`. | |
| 404 | | Concurrence | Les appels d'un même module peuvent s'exécuter en même temps, chacun dans sa propre instance ; aucun état ne survit à un appel. | | 404 | | Concurrence | Les appels d'un même module peuvent s'exécuter en même temps, chacun dans sa propre instance ; aucun état ne survit à un appel. | |
| 405 | 405 | ||
| 406 | +## Outils du client (ACP) | ||
| 407 | + | ||
| 408 | +Outils proposés par le client ACP, en `mm -acp` seulement ; le protocole est spécifié dans [Outils du client ACP](acp-client-tools.md), les réglages sont [`acp.clientTools`](configuration.md#clés-de-acp). | ||
| 409 | + | ||
| 410 | +| Aspect | Comportement | | ||
| 411 | +|--------|--------------| | ||
| 412 | +| Nom | `client_<name>`, `<name>` étant celui du client, où chaque caractère hors de `[A-Za-z0-9]` devient `_`. | | ||
| 413 | +| Déclaration | Par session, d'après la liste du client : à `initialize` (découverte `meta`) ou par `_org.mini-me/tools/list` à `session/new`, `session/load`, `session/resume` (découverte `request`). Une session sans liste n'a pas d'outil client. | | ||
| 414 | +| Schéma envoyé au modèle | L'`inputSchema` du client sans `required`, `enum`, `additionalProperties: false` ni `$schema` ; les paramètres obligatoires terminent la description (`Required parameters: …`), les valeurs d'un `enum` terminent la description de la propriété (`One of: …`). | | ||
| 415 | +| Exécution | Une requête ACP vers la `method` de l'outil (une méthode d'extension, `_…`), params = les arguments du modèle plus `_meta["org.mini-me/sessionId"]`. | | ||
| 416 | +| Sortie | Les blocs texte du `content` de la réponse, concaténés ; toute autre réponse JSON sous forme de texte. | | ||
| 417 | +| Échec | `[client tool error: <raison>]` : un paramètre obligatoire manquant (rien n'est envoyé), une erreur JSON-RPC, `isError: true`, pas de réponse dans le délai `acp.clientTools.timeout` (30 s par défaut). Le tour continue. | | ||
| 418 | +| Permission | `acp.clientTools.permissions` : `auto` (par défaut) ne demande que si `yolo: false`, `ask` demande toujours. Un refus renvoie `[client_<name> rejected by user]`. | | ||
| 419 | +| Troncature | `maxOutput`, début et fin conservés, comme `bash`. | | ||
| 420 | +| ACP | Appel d'outil de genre `other`, titre `client: <name>`. | | ||
| 421 | +| Terminal et fenêtre | Non disponibles. | | ||
| 422 | + | ||
| 406 | ## Détection de boucle | 423 | ## Détection de boucle |
| 407 | 424 | ||
| 408 | Chaque outil enregistre `(outil, entrée, sortie)` dans le détecteur de boucle après chaque appel. | 425 | Chaque outil enregistre `(outil, entrée, sortie)` dans le détecteur de boucle après chaque appel. |