package console // The mesh's tools found by address, not announced whole (novox/hq ADR 0195, to-be 34 §3a). // // The console announces six tools — five to find and call, and mesh_runtimes to say which runtimes // discovery heard. Everything the mesh answers is reached through them by one address per layer: // // . a seat held once for the mesh — its holder answers // /. a seat held once per machine — that machine's holder answers // /. a module assigned to a machine — that assignment answers // . also, for a module whose instances are interchangeable (ADR 0160) // // A module that is not interchangeable is called with its machine or refused, naming the machines // it runs on: "whichever answers" is no answer for state a machine holds. // // Every discovery verb asks the mesh when it is called — kept a few seconds at most, never for a // session — so a tool that arrived a minute ago is found without the client reconnecting. import ( "encoding/json" "fmt" "sort" "strings" "sync" "time" "github.com/novox/mesh-tools/node-tools/internal/announce" "github.com/novox/mesh-tools/node-tools/internal/bus" ) // IndexKept is how long what the mesh answered is kept before it is asked again: long enough that // one agent turn's search, describe and call ask once, short enough that nothing goes stale. var IndexKept = 5 * time.Second // The six tools the console announces. Names of the API's kind — letters, digits, `_`, `-`. const ( verbOverview = "mesh_overview" verbMachine = "mesh_machine" verbSearch = "mesh_search" verbDescribe = "mesh_describe" verbCall = "mesh_call" verbRuntimes = "mesh_runtimes" ) // searchCap is how many matches a search answers before it says how many more there were. const searchCap = 25 const grammar = "Addresses: `.` for a seat held once for the mesh (e.g. `mesh-controller.nodes`); " + "`/.` for a seat every machine holds (e.g. `ace/node-packet-filter.rules`); " + "`/.` for a module on one machine (e.g. `novox/postgres.postgres_list_databases`); " + "and `.` also for a module whose instances are interchangeable." // discovery is the six tools as tools/list announces them. func discovery() []map[string]any { str := func(desc string) map[string]any { return map[string]any{"type": "string", "description": desc} } obj := func(props map[string]any, required ...string) map[string]any { s := map[string]any{"type": "object", "properties": props} if len(required) > 0 { s["required"] = required } return s } return []map[string]any{ {"name": verbOverview, "inputSchema": obj(map[string]any{}), "description": "The mesh at a glance: the seats it holds once for the whole mesh with their verbs, the seats " + "every machine holds, and its machines. Start here, then `mesh_machine` for one machine. " + grammar}, {"name": verbMachine, "inputSchema": obj(map[string]any{"node": str("the machine, as mesh_overview names it")}, "node"), "description": "One machine: the seats it holds with their verbs, and the modules assigned to it with their tools — " + "each with the address to describe or call it by. " + grammar}, {"name": verbSearch, "inputSchema": obj(map[string]any{"query": str("words to find in tool names and descriptions, e.g. `postgres databases`")}, "query"), "description": "Find tools anywhere in the mesh by words: every match's address and a line of what it does, across the " + "mesh's seats, the machines' seats and every module on every machine. " + grammar}, {"name": verbDescribe, "inputSchema": obj(map[string]any{"address": str("the tool's address")}, "address"), "description": "What one tool does and the arguments it takes, as a JSON schema. The machine is in the address, " + "never an argument. " + grammar}, {"name": verbCall, "inputSchema": obj(map[string]any{ "address": str("the tool's address"), "arguments": map[string]any{"type": "object", "description": "the tool's arguments, as mesh_describe gives its schema"}, }, "address"), "description": "Call one tool by its address with its arguments; the answer says which machine gave it. " + grammar}, {"name": verbRuntimes, "inputSchema": obj(map[string]any{}), "description": "Which runtimes answered discovery, asked now: for each, its machine, how long its answer took to " + "arrive, its size in bytes, how many modules and tools it announced, whether it was shortened to fit the " + "bus, and when it was last heard — and every runtime or machine expected that was not heard. Read-only; " + "for when an address is said to be missing or on another machine."}, } } func isDiscovery(name string) bool { switch name { case verbOverview, verbMachine, verbSearch, verbDescribe, verbCall, verbRuntimes: return true } return false } // seatInfo is a seat as the mesh's records define it, and who holds it where. type seatInfo struct { Seat string Scope string // "mesh" or "node" Verbs []Tool Holders []holder } type holder struct { Module string `json:"module"` Node string `json:"node"` } // moduleInfo is a module that answers tools: where it runs, whether any instance will do, and its // tools as one of its instances described them. type moduleInfo struct { Module string On []string Interchangeable bool Tools []Tool } // index is what the mesh answered about itself, at one moment. type index struct { Seats []seatInfo Machines []string Modules map[string]*moduleInfo NotAnswering []string Listing *Listing // the flat catalogue the call path resolves subjects with Discovery announce.Discovery // Recorded is where the controller's records place each module that declares tools. Recorded map[string][]string // Unheard is every runtime whose answer this index lacks: it said it was there and did not say // what it serves in time, or it answered before and not now. An address it might answer is never // called missing while it is here (2026-10-05). Unheard []unheard } // unheard is one runtime discovery did not hear in full, and why. type unheard struct { Runtime string `json:"runtime"` Machine string `json:"machine,omitempty"` Why string `json:"why"` } // unheardOn is what is unheard on one machine; with no machine, everything unheard. func (x *index) unheardOn(node string) []unheard { var out []unheard for _, u := range x.Unheard { if node == "" || u.Machine == node || u.Machine == "" { out = append(out, u) } } return out } // sayUnheard is the unheard, as a sentence's end. func sayUnheard(us []unheard) string { var parts []string for _, u := range us { who := u.Runtime if u.Machine != "" { who += " on " + u.Machine } parts = append(parts, who+" ("+u.Why+")") } return strings.Join(parts, "; ") } func (x *index) seat(name string) *seatInfo { for i := range x.Seats { if x.Seats[i].Seat == name { return &x.Seats[i] } } return nil } func findTool(tools []Tool, name string) *Tool { for i := range tools { if tools[i].Name == name { return &tools[i] } } return nil } // controllerOutput is a controller seat verb's answer: the command's printed output. func controllerOutput(conn *bus.Conn, verb string) (string, error) { got, err := conn.Ask("seat:mesh-controller."+verb, map[string]any{}, "") if err != nil { return "", err } var r struct { Output string `json:"output"` OK *bool `json:"ok"` } if json.Unmarshal(got.Result, &r) != nil { return "", fmt.Errorf("mesh-controller.%s answered something that is not its output", verb) } if r.OK != nil && !*r.OK { return "", fmt.Errorf("mesh-controller.%s: %s", verb, strings.TrimSpace(r.Output)) } return r.Output, nil } // jsonIn is the JSON document a command printed, after any lines it said first: a seat verb runs // the controller's command, and a command may warn before it answers. func jsonIn(output string) string { t := strings.TrimSpace(output) if strings.HasPrefix(t, "{") || strings.HasPrefix(t, "[") { return t } for _, open := range []string{"\n{", "\n["} { if i := strings.Index(output, open); i >= 0 { return strings.TrimSpace(output[i+1:]) } } return "" } // recordedModule is a module as the controller's records hold it (`module list --json`): where the // mesh assigned it, and whether it declares tools — what should announce itself, and where. type recordedModule struct { Module string `json:"module"` On []string `json:"on"` Tools bool `json:"tools"` } // recordedMachine is a machine as the controller's records hold it (`node list --json`). type recordedMachine struct { Name string `json:"name"` } // indexOn asks the mesh what it holds (novox/hq ADR 0197): what answers, from every runtime's own // announcement on the bus — one `$SRV.INFO` request — and what should, from the controller's records // read as JSON. Nothing is inferred from a roster and nothing is parsed from print. func indexOn(conn *bus.Conn) (*index, error) { var wg sync.WaitGroup var nodesOut, modulesOut string wg.Add(2) go func() { defer wg.Done(); nodesOut, _ = controllerOutput(conn, "nodes") }() go func() { defer wg.Done(); modulesOut, _ = controllerOutput(conn, "modules") }() d, err := announce.Gather(conn) wg.Wait() if err != nil { return nil, err } var endpoints []announce.Endpoint for _, h := range d.Heard { endpoints = append(endpoints, announce.Endpoints(h.Info)...) } x, announced, machines := indexOfEndpoints(endpoints) l := x.Listing x.Discovery = d for _, s := range d.Silent { x.Unheard = append(x.Unheard, unheard{Runtime: s.Name, Machine: s.Machine, Why: fmt.Sprintf("it answered PING; what it serves did not arrive within %s", announce.Patience)}) } // What should have answered: every assignment of a module that declares tools. Silence is named; // a module with no tools is never a name here. var recorded []recordedModule if json.Unmarshal([]byte(jsonIn(modulesOut)), &recorded) == nil { for _, m := range recorded { if !m.Tools { continue } x.Recorded[m.Module] = append([]string{}, m.On...) for _, n := range m.On { // A holder of a seat held once for the mesh announces no machine — the seat is the mesh's, // not a machine's — so what it announced without one answers for wherever it is assigned. if !announced[m.Module][n] && !announced[m.Module][""] { l.NotAnswering = append(l.NotAnswering, m.Module+" on "+n) } } } } else { l.NotAnswering = append(l.NotAnswering, "mesh-controller (its records of the modules did not answer, so what is missing cannot be said)") } for _, u := range x.Unheard { l.NotAnswering = append(l.NotAnswering, "the runtime "+sayUnheard([]unheard{u})) } sort.Strings(l.NotAnswering) x.NotAnswering = l.NotAnswering var known []recordedMachine if json.Unmarshal([]byte(jsonIn(nodesOut)), &known) == nil { for _, n := range known { if n.Name != "" { machines[n.Name] = true } } } for n := range machines { x.Machines = append(x.Machines, n) } sort.Strings(x.Machines) return x, nil } func containsHolder(hs []holder, h holder) bool { for _, x := range hs { if x == h { return true } } return false } func (s *Surface) index() (*index, error) { return s.indexAsked(false) } // indexAsked is what the mesh answered, asked again when `fresh` or when the kept answer is old. func (s *Surface) indexAsked(fresh bool) (*index, error) { s.mu.Lock() if !fresh && s.idx != nil && time.Since(s.idxAt) <= IndexKept { x := s.idx s.mu.Unlock() return x, nil } s.mu.Unlock() x, err := indexOn(s.conn) if err != nil { return nil, err } s.mu.Lock() s.remember(x) s.idx, s.idxAt = x, time.Now() s.mu.Unlock() return x, nil } // indexOfEndpoints is what the announced endpoints say: every seat with its verbs and holders, every // module with its tools and machines, the flat listing, and which module announced on which machine. func indexOfEndpoints(endpoints []announce.Endpoint) (*index, map[string]map[string]bool, map[string]bool) { l := &Listing{Tools: []Tool{}, NotAnswering: []string{}} x := &index{Modules: map[string]*moduleInfo{}, Listing: l, Recorded: map[string][]string{}} announced := map[string]map[string]bool{} // module → node → announced something seats := map[string]*seatInfo{} // ., or seat:., → index in l.Tools. **Apart**, because a seat and a module may // share a name — mesh-delivery is the delivery's seat and the module holding it (novox/hq ADR 0239), and // the module answers the seat's verbs with tools of the same names: in one namespace the module's tool // took the key first, and the seat was listed with no verb at all (novox/hq issue 287). toolAt := map[string]int{} machines := map[string]bool{} for _, e := range endpoints { if e.Node != "" { machines[e.Node] = true } if announced[e.Module] == nil { announced[e.Module] = map[string]bool{} } announced[e.Module][e.Node] = true switch e.Kind { case announce.KindSeat: st := seats[e.Seat] if st == nil { st = &seatInfo{Seat: e.Seat, Scope: e.Scope} seats[e.Seat] = st } if st.Scope != "node" && e.Scope == "node" { st.Scope = "node" } h := holder{Module: e.Module, Node: e.Node} if !containsHolder(st.Holders, h) { st.Holders = append(st.Holders, h) } key := "seat:" + e.Seat + "." + e.Tool if _, have := toolAt[key]; !have { toolAt[key] = len(l.Tools) t := Tool{Module: e.Seat, Name: e.Tool, Description: e.Description, Input: e.Schema, Seat: true, Scope: st.Scope} l.Tools = append(l.Tools, t) st.Verbs = append(st.Verbs, t) } case announce.KindTool: m := x.Modules[e.Module] if m == nil { m = &moduleInfo{Module: e.Module} x.Modules[e.Module] = m } if e.Node != "" && !contains(m.On, e.Node) { m.On = append(m.On, e.Node) } m.Interchangeable = m.Interchangeable || e.Interchangeable key := e.Module + "." + e.Tool i, have := toolAt[key] if !have { i = len(l.Tools) toolAt[key] = i l.Tools = append(l.Tools, Tool{Module: e.Module, Name: e.Tool, Description: e.Description, Input: e.Schema}) } if !contains(l.Tools[i].Subjects, e.Subject) { l.Tools[i].Subjects = append(l.Tools[i].Subjects, e.Subject) } } } // A tool's subjects as a call looks them up: the plain one any instance answers first, then each // machine's. for i := range l.Tools { t := &l.Tools[i] if t.Seat { continue } plain := "mesh.mod." + t.Module + ".tool." + t.Name sort.SliceStable(t.Subjects, func(a, b int) bool { if (t.Subjects[a] == plain) != (t.Subjects[b] == plain) { return t.Subjects[a] == plain } return t.Subjects[a] < t.Subjects[b] }) } for _, t := range l.Tools { if !t.Seat { x.Modules[t.Module].Tools = append(x.Modules[t.Module].Tools, t) } } for _, m := range x.Modules { sort.Strings(m.On) } for _, st := range seats { sort.Slice(st.Holders, func(a, b int) bool { return st.Holders[a].Node < st.Holders[b].Node }) x.Seats = append(x.Seats, *st) } sort.Slice(x.Seats, func(i, j int) bool { return x.Seats[i].Seat < x.Seats[j].Seat }) sort.SliceStable(l.Tools, func(i, j int) bool { return l.Tools[i].Module+"."+l.Tools[i].Name < l.Tools[j].Module+"."+l.Tools[j].Name }) return x, announced, machines } // target is what an address resolves to. type target struct { Address string Key string // the call key the existing path takes: seat:.[@node] or .[@node] Name string // . or ., for the listing's subject lookup Node string Tool Tool Seat bool } // resolve turns an address into exactly one target, or says why it cannot. func resolve(x *index, address string) (target, error) { address = strings.TrimSpace(address) node, rest, hasNode := strings.Cut(address, "/") if !hasNode { rest, node = address, "" } dot := strings.Index(rest, ".") if dot <= 0 || dot == len(rest)-1 || strings.Contains(rest, "/") { return target{}, fmt.Errorf("%q is not an address. %s", address, grammar) } prefix, name := rest[:dot], rest[dot+1:] s := x.seat(prefix) if s != nil && x.Modules[prefix] != nil && findTool(x.Modules[prefix].Tools, name) != nil { // **A seat and a module of one name** (novox/hq issue 287): mesh-delivery is the delivery's seat and // the module holding it. The module's tool is meant when the seat has no such verb, or when a machine // is named for a seat held once for the mesh — `/.` is a module on one machine. if findTool(s.Verbs, name) == nil || (node != "" && s.Scope != "node") { s = nil } } if s != nil { verb := findTool(s.Verbs, name) if verb == nil { if len(s.Verbs) == 0 { return target{}, fmt.Errorf("the seat %s has no verb %s: its holder announced none", prefix, name) } return target{}, fmt.Errorf("the seat %s has no verb %s; it has %s", prefix, name, toolNames(s.Verbs)) } if s.Scope == "node" { if node == "" { return target{}, fmt.Errorf("%s is held once per machine: write /%s — it is held on %s", prefix, rest, orNobody(nodesOf(s.Holders))) } return target{Address: node + "/" + rest, Key: "seat:" + rest + "@" + node, Name: rest, Node: node, Tool: *verb, Seat: true}, nil } if node != "" { return target{}, fmt.Errorf("%s is held once for the whole mesh: write %s, without a machine", prefix, rest) } return target{Address: rest, Key: "seat:" + rest, Name: rest, Tool: *verb, Seat: true}, nil } m := x.Modules[prefix] if m == nil { if on := x.Recorded[prefix]; len(on) > 0 { why := "it did not announce itself on the bus" if us := x.unheardOnAny(on); len(us) > 0 { why = "discovery did not hear in full from " + sayUnheard(us) } return target{}, fmt.Errorf("%s is assigned to %s in the mesh's records, but nothing that answered discovery serves it: %s. "+ "Ask again in a moment; mesh_runtimes shows which runtimes answered", prefix, strings.Join(on, ", "), why) } if len(x.Unheard) > 0 { return target{}, fmt.Errorf("nothing that answered discovery is called %s, but not every runtime answered: %s — "+ "%s may be theirs. Ask again in a moment; mesh_runtimes shows which runtimes answered", prefix, sayUnheard(x.Unheard), prefix) } return target{}, fmt.Errorf("nothing in the mesh is called %s: no seat, and no module that answers tools. "+ "mesh_search finds a tool by words", prefix) } tool := findTool(m.Tools, name) if tool == nil { return target{}, fmt.Errorf("%s has no tool %s; it has %s", prefix, name, toolNames(m.Tools)) } if node == "" { if !m.Interchangeable { return target{}, fmt.Errorf("%s keeps state on each machine it runs on, so a call names the machine: "+ "write /%s — it runs on %s", prefix, rest, orNobody(m.On)) } return target{Address: rest, Key: rest, Name: rest, Tool: *tool}, nil } if len(m.On) > 0 && !contains(m.On, node) { if us := x.unheardOn(node); len(us) > 0 || contains(x.Recorded[prefix], node) { why := "it did not announce itself there" if len(us) > 0 { why = "discovery did not hear in full from " + sayUnheard(us) } return target{}, fmt.Errorf("%s on %s did not answer discovery: %s. It was heard on %s. "+ "Ask again in a moment; mesh_runtimes shows which runtimes answered", prefix, node, why, orNobody(m.On)) } return target{}, fmt.Errorf("%s does not run on %s; it runs on %s", prefix, node, orNobody(m.On)) } return target{Address: node + "/" + rest, Key: rest + "@" + node, Name: rest, Node: node, Tool: *tool}, nil } // unheardOnAny is what is unheard on any of these machines. func (x *index) unheardOnAny(nodes []string) []unheard { var out []unheard for _, u := range x.Unheard { if u.Machine == "" || contains(nodes, u.Machine) { out = append(out, u) } } return out } func contains(xs []string, s string) bool { for _, x := range xs { if x == s { return true } } return false } func toolNames(ts []Tool) string { names := make([]string, 0, len(ts)) for _, t := range ts { names = append(names, t.Name) } sort.Strings(names) return strings.Join(names, ", ") } func nodesOf(hs []holder) []string { var out []string for _, h := range hs { if h.Node != "" && !contains(out, h.Node) { out = append(out, h.Node) } } sort.Strings(out) return out } func orNobody(nodes []string) string { if len(nodes) == 0 { return "no machine the mesh knows of" } return strings.Join(nodes, ", ") } // firstLine is a description's first line, for a list. func firstLine(s string) string { s = strings.TrimSpace(s) if i := strings.IndexAny(s, "\n"); i >= 0 { s = s[:i] } if len(s) > 160 { s = s[:157] + "…" } return s } // schemaAsPassed is a tool's schema as an agent passes it at this address. Where the address names // the machine, `node` is taken out: the machine is in the address. Where it does not — a seat held // once for the mesh, or an interchangeable module — `node` is the tool's own argument, and is kept. // // **Taking it out everywhere made the mesh's own verbs lie** (novox/hq issue 244): the controller's // `push`, `plan`, `assign`, `pin` and `settings` take the machine they act on as `node`, and were // described without it — `push` as taking nothing at all, so a push naming one machine arrived as a // push of every machine behind. func schemaAsPassed(t target) map[string]any { if t.Node == "" { return asSchema(t.Tool.Input) } return schemaWithoutNode(t.Tool.Input) } // argumentsAsSent are a call's arguments as the tool at this address receives them, or why the call // is refused. **Nothing given is dropped without a word** (novox/hq issue 244): // - where the address names the machine, a `node` naming the same machine is redundant and taken // out; one naming another machine is refused — which of the two was meant is not guessed; // - where it does not, `node` is the tool's own argument and goes to it like any other; // - a seat's schema is the mesh's record of the verb, so an argument a seat's verb does not // declare is refused here, naming it, rather than sent to be passed over. func argumentsAsSent(t target, given map[string]any) (map[string]any, error) { args := map[string]any{} for k, v := range given { args[k] = v } if n, has := args["node"]; has && t.Node != "" { if named, _ := n.(string); strings.TrimSpace(named) != t.Node { return nil, fmt.Errorf("%s names the machine %s in its address and %v in its arguments: say it once, in the address", t.Address, t.Node, n) } delete(args, "node") } if !t.Seat { return args, nil } declared := map[string]bool{} var names []string if p, ok := schemaAsPassed(t)["properties"].(map[string]any); ok { for k := range p { declared[k] = true names = append(names, k) } } sort.Strings(names) var strangers []string for k := range args { if !declared[k] { strangers = append(strangers, k) } } if len(strangers) > 0 { sort.Strings(strangers) takes := "nothing" if len(names) > 0 { takes = strings.Join(names, ", ") } return nil, fmt.Errorf("%s takes no argument %s — it takes %s (mesh_describe %s); nothing was sent", t.Address, strings.Join(strangers, ", "), takes, t.Address) } return args, nil } // schemaWithoutNode is a tool's schema with `node` taken out, for an address that names the machine. func schemaWithoutNode(raw json.RawMessage) map[string]any { schema := asSchema(raw) out := map[string]any{} for k, v := range schema { out[k] = v } if p, ok := schema["properties"].(map[string]any); ok { props := map[string]any{} for k, v := range p { if k != "node" { props[k] = v } } out["properties"] = props } if r, ok := schema["required"].([]any); ok { var keep []any for _, k := range r { if k != "node" { keep = append(keep, k) } } if len(keep) == 0 { delete(out, "required") } else { out["required"] = keep } } return out } // answerText is a discovery verb's answer as MCP content: JSON, indented. func answerText(v any) map[string]any { var b strings.Builder enc := json.NewEncoder(&b) enc.SetEscapeHTML(false) // `/…` is read by an agent, not embedded in a page enc.SetIndent("", " ") _ = enc.Encode(v) return map[string]any{"content": []map[string]any{{"type": "text", "text": strings.TrimRight(b.String(), "\n")}}} } func failure(text string) map[string]any { return map[string]any{"content": []map[string]any{{"type": "text", "text": text}}, "isError": true} } // discover answers one of the discovery verbs. func (s *Surface) discover(name string, args map[string]any) map[string]any { if name == verbRuntimes { return s.runtimesAnswer() } x, err := s.index() if err != nil { return failure("the mesh's discovery failed: " + err.Error()) } str := func(k string) string { v, _ := args[k].(string); return strings.TrimSpace(v) } switch name { case verbOverview: type verbLine struct { Address string `json:"address"` Description string `json:"description"` } var mesh, node []map[string]any for _, st := range x.Seats { var verbs []verbLine for _, v := range st.Verbs { addr := st.Seat + "." + v.Name if st.Scope == "node" { addr = "/" + addr } verbs = append(verbs, verbLine{addr, firstLine(v.Description)}) } entry := map[string]any{"seat": st.Seat, "verbs": verbs} if st.Scope == "node" { entry["held on"] = nodesOf(st.Holders) node = append(node, entry) } else { if h := nodesOf(st.Holders); len(h) > 0 { entry["held on"] = h } mesh = append(mesh, entry) } } modules := 0 tools := 0 for _, m := range x.Modules { modules++ tools += len(m.Tools) } return answerText(map[string]any{ "seats of the mesh": mesh, "seats every machine": node, "machines": x.Machines, "modules with tools": fmt.Sprintf("%d modules, %d tools — mesh_machine lists a machine's, mesh_search finds one", modules, tools), "not answering": x.NotAnswering, }) case verbMachine: node := str("node") if node == "" { return failure("mesh_machine needs `node`: one of " + orNobody(x.Machines)) } if !contains(x.Machines, node) { return failure(fmt.Sprintf("the mesh knows no machine %q; it has %s", node, orNobody(x.Machines))) } var seats []map[string]any for _, st := range x.Seats { if st.Scope != "node" || !contains(nodesOf(st.Holders), node) { continue } var verbs []string for _, v := range st.Verbs { verbs = append(verbs, node+"/"+st.Seat+"."+v.Name) } var by string for _, h := range st.Holders { if h.Node == node { by = h.Module } } seats = append(seats, map[string]any{"seat": st.Seat, "held by": by, "verbs": verbs}) } var modules []map[string]any names := make([]string, 0, len(x.Modules)) for n := range x.Modules { names = append(names, n) } sort.Strings(names) for _, n := range names { m := x.Modules[n] if !contains(m.On, node) { continue } var tools []map[string]string for _, t := range m.Tools { tools = append(tools, map[string]string{"address": node + "/" + m.Module + "." + t.Name, "does": firstLine(t.Description)}) } entry := map[string]any{"module": m.Module, "tools": tools} if m.Interchangeable { entry["interchangeable"] = "any instance answers . as well" } modules = append(modules, entry) } out := map[string]any{"machine": node, "seats": seats, "modules": modules} if seats == nil { out["seats"] = []map[string]any{} } if modules == nil { out["modules"] = []map[string]any{} } if us := x.unheardOn(node); len(us) > 0 { out["incomplete"] = "discovery did not hear in full from " + sayUnheard(us) + "; what is listed may be missing what it serves. Ask again in a moment; mesh_runtimes shows which runtimes answered" } return answerText(out) case verbSearch: query := strings.ToLower(str("query")) if query == "" { return failure("mesh_search needs `query`: words to find, e.g. `postgres databases`") } words := strings.Fields(query) type hit struct { Address string `json:"address"` Does string `json:"does"` Also string `json:"also,omitempty"` } var hits []hit matches := func(parts ...string) bool { hay := strings.ToLower(strings.Join(parts, " ")) for _, w := range words { if !strings.Contains(hay, w) { return false } } return true } for _, st := range x.Seats { for _, v := range st.Verbs { if !matches(st.Seat, v.Name, v.Description) { continue } if st.Scope == "node" { on := nodesOf(st.Holders) first := "" also := "" if len(on) > 0 { first = on[0] if len(on) > 1 { also = "also on " + strings.Join(on[1:], ", ") } } hits = append(hits, hit{first + "/" + st.Seat + "." + v.Name, firstLine(v.Description), also}) } else { hits = append(hits, hit{st.Seat + "." + v.Name, firstLine(v.Description), ""}) } } } names := make([]string, 0, len(x.Modules)) for n := range x.Modules { names = append(names, n) } sort.Strings(names) for _, n := range names { m := x.Modules[n] for _, t := range m.Tools { if !matches(m.Module, t.Name, t.Description) { continue } switch { case m.Interchangeable: hits = append(hits, hit{m.Module + "." + t.Name, firstLine(t.Description), "any instance; on " + orNobody(m.On)}) case len(m.On) == 0: hits = append(hits, hit{"/" + m.Module + "." + t.Name, firstLine(t.Description), "the mesh places it on no machine"}) default: also := "" if len(m.On) > 1 { also = "also on " + strings.Join(m.On[1:], ", ") } hits = append(hits, hit{m.On[0] + "/" + m.Module + "." + t.Name, firstLine(t.Description), also}) } } } out := map[string]any{"matches": hits} if len(hits) > searchCap { out["matches"] = hits[:searchCap] out["more"] = fmt.Sprintf("%d more; narrow the words", len(hits)-searchCap) } if len(hits) == 0 { out["matches"] = []hit{} out["hint"] = "nothing matched every word; try fewer words, or mesh_overview and mesh_machine to browse" } if len(x.Unheard) > 0 { out["incomplete"] = "discovery did not hear in full from " + sayUnheard(x.Unheard) + "; what they serve is not searched. mesh_runtimes shows which runtimes answered" } return answerText(out) case verbDescribe: t, err := resolve(x, str("address")) if err != nil { return failure(err.Error()) } if !t.Seat && len(t.Tool.Input) == 0 { // Discovery carries a seat's schema, not a module tool's: asked of the runtime serving it. t.Tool.Input = inputOf(s.conn, t.Tool.Module, t.Tool.Name) } description := t.Tool.Description if description == "" { description = t.Name } return answerText(map[string]any{"address": t.Address, "description": description, "arguments": schemaAsPassed(t)}) case verbCall: t, err := resolve(x, str("address")) if err != nil { return failure(err.Error()) } given, _ := args["arguments"].(map[string]any) callArgs, err := argumentsAsSent(t, given) if err != nil { return failure(err.Error()) } var got bus.Answered if t.Seat { got, err = s.conn.Ask(t.Key, callArgs, "") } else { got, err = callTool(s.conn, t.Key, callArgs, nil, x.Listing) } if err != nil { return failure(whyItFailed(t.Address, err)) } content := []map[string]any{{"type": "text", "text": pretty(got.Result)}} if got.Node != "" { content = append(content, map[string]any{"type": "text", "text": "answered by " + got.Node}) } return map[string]any{"content": content} } return failure("no discovery verb " + name) } // inputOf is a module tool's input schema, asked of a runtime that serves the module — its `tools` verb, // answered on the module's plain subject by any instance — because discovery no longer carries it: every // tool's schema made a machine's one discovery answer outgrow the bus (2026-10-04). Nil when nothing // answers, which describes the tool as taking nothing rather than failing the description. func inputOf(conn *bus.Conn, module, tool string) json.RawMessage { got, err := conn.Ask(module+".tools", map[string]any{}, "") if err != nil { return nil } var answer struct { Tools []struct { Name string `json:"name"` Input json.RawMessage `json:"input"` } `json:"tools"` } if json.Unmarshal(got.Result, &answer) != nil { return nil } for _, t := range answer.Tools { if t.Name == tool { return t.Input } } return nil }