Proposer des outils depuis votre client ACP
Ce guide montre comment un programme Go qui pilote mm -acp donne à l'agent ses propres outils : des fonctions de votre programme que le modèle appelle comme ses outils intégrés (les buffers ouverts d'un éditeur, les données d'une application, une liste de tâches…). Il suppose que votre programme est déjà un client ACP de mm, écrit avec github.com/coder/acp-go-sdk (v0.13.5, la version qu'utilise mm). Le protocole exact est dans la spécification des outils du client.
Étapes
1. Décrire vos outils
Une description par outil : un nom, une description pour le modèle, la méthode d'extension que mm appellera (elle doit commencer par _), et le schéma JSON des arguments.
type toolSpec struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
Method string `json:"method"`
InputSchema map[string]any `json:"inputSchema,omitempty"`
}
var tools = []toolSpec{{
Name: "add_todo",
Description: "Add one item to the user's to-do list.",
Method: "_com.example/todo/add",
InputSchema: map[string]any{
"type": "object",
"properties": map[string]any{"item": map[string]any{"type": "string"}},
"required": []any{"item"},
},
}}
Le modèle verra l'outil sous le nom client_add_todo.
2. Les annoncer dans initialize
Mettez la liste dans clientCapabilities._meta, sous org.mini-me/tools :
_, err := conn.Initialize(ctx, sdk.InitializeRequest{
ProtocolVersion: sdk.ProtocolVersionNumber,
ClientInfo: &sdk.Implementation{Name: "my-client", Version: "1.0.0"},
ClientCapabilities: sdk.ClientCapabilities{
Meta: map[string]any{"org.mini-me/tools": tools},
},
})
C'est tout ce dont mm a besoin avec la configuration par défaut (acp.clientTools.discovery: meta) : chaque session ouverte ensuite a les outils.
3. Répondre aux appels
Votre type client (celui passé à sdk.NewClientSideConnection) implémente sdk.ExtensionMethodHandler. Le SDK lui passe chaque requête dont la méthode commence par _ :
var _ sdk.ExtensionMethodHandler = (*myClient)(nil)
func (c *myClient) HandleExtensionMethod(ctx context.Context, method string, params json.RawMessage) (any, error) {
switch method {
case "_com.example/todo/add":
var args struct {
Item string `json:"item"`
Meta struct {
SessionID string `json:"org.mini-me/sessionId"`
} `json:"_meta"`
}
if err := json.Unmarshal(params, &args); err != nil {
return nil, sdk.NewInvalidParams(map[string]any{"error": err.Error()})
}
c.todos = append(c.todos, args.Item)
return textResult(fmt.Sprintf("added %q", args.Item), false), nil
}
return nil, sdk.NewMethodNotFound(method)
}
// textResult is the answer shape mm reads first.
func textResult(text string, isError bool) map[string]any {
return map[string]any{
"content": []map[string]any{{"type": "text", "text": text}},
"isError": isError,
}
}
- Les arguments du modèle sont au premier niveau de
params;_meta["org.mini-me/sessionId"]indique quelle session a appelé. - Répondez à un échec par
isError: trueet un texte dont le modèle peut se servir, plutôt que par une erreur JSON-RPC : les deux arrivent au modèle, mais le texte, c'est vous qui l'écrivez. - Gardez le handler sûr en accès concurrent : le SDK traite chaque requête dans sa propre goroutine.
4. Vérifier que mm les voit
Lancez mm et lisez son stderr. Après initialize, il affiche :
[acp] client tools (meta): client_add_todo
Une entrée qu'il n'a pas pu utiliser est signalée sur sa propre ligne ([acp] client tools: entry 0 skipped: …), avec la raison.
Variantes
-
La liste dépend de la session (son
cwd, un utilisateur) : mettezdiscovery: requestdans le fichier d'agent, annoncez seulement"org.mini-me/tools": true, et répondez à_org.mini-me/tools/listdansHandleExtensionMethodavecmap[string]any{"tools": tools}. mm l'envoie pendantsession/new,session/loadetsession/resume, avant d'y répondre ; le SDK vous permet de répondre pendant que votre appel àNewSessionattend.acp: clientTools: discovery: request -
Chaque appel doit être confirmé : mettez
acp.clientTools.permissions: ask. mm envoie alorssession/request_permissionavant chaque appel, avec le titreclient: <name>; répondez-y dans votre méthodeRequestPermission. Avec la valeur par défautauto, cela n'arrive que si le fichier d'agent dityolo: false. -
Outils lents : augmentez
acp.clientTools.timeout(30spar défaut). Au-delà, le modèle lit une erreur et le tour continue. -
Avec le paquet
minimededemo.acp.sdk/go: les étapes 1 à 3 se résument àminime.Options{Tools: []minime.Tool{…}}, chaqueToolayant une fonctionHandle;ToolDiscovery: minime.DiscoveryRequestchoisit le second mode.go run ./examples/clienttoolslance un exemple complet.
Voir aussi
- Spécification des outils du client — le JSON exact, la validation d'une liste, les erreurs
- Fichier de configuration — clés de
acp - Piloter mini-me depuis un programme
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 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
|