ADR 0193 says the node's runtime launches every served bundle over MCP stdio and knows no language, and ADR 0188 says one module may carry several bundles in any language. Nothing in the catalogue shows both at once: every tools bundle is TypeScript, and the only Go bundle is the runtime itself. netcheck is the smallest real module that does — read-only checks from a machine, worth having on their own: - tools-go (Go SDK go/v0.1.6): netcheck_tcp (one connect, nothing sent) and netcheck_dns (A/AAAA/CNAME/TXT/MX through the machine's resolver). - tools-typescript (@novox/mesh-sdk): netcheck_http (HEAD or GET, body neither sent nor read, redirects reported not followed, anything but http(s) refused). Both say loads; the module lists its tools. No container, no image, no env: nothing to be given, so the runtime's own words suffice.
73 lines
2.5 KiB
Go
73 lines
2.5 KiB
Go
// netcheck's Go tools bundle (novox/hq ADR 0188, ADR 0193): a process the node's runtime launches
|
|
// and speaks MCP over stdio to, through the Go SDK. It serves the two checks that are the machine's
|
|
// own sockets and resolver — a TCP connect and a DNS lookup — and nothing that changes anything.
|
|
// The module's HTTP check is its TypeScript bundle; the runtime serves both under one module.
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
|
|
stdio "git.novox.be/novox/mesh-sdk/go"
|
|
)
|
|
|
|
func main() {
|
|
// An empty name serves as the module the runtime names (MESH_SERVED_MODULE): netcheck.
|
|
if err := stdio.Serve("", tools()); err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
func tools() []stdio.Tool {
|
|
return []stdio.Tool{
|
|
{
|
|
Name: "netcheck_tcp",
|
|
Description: "Check whether a TCP port is reachable from this machine: opens one connection " +
|
|
"and closes it at once, sending nothing. Answers reachable, elapsed_ms and the error when not.",
|
|
Input: map[string]any{
|
|
"host": map[string]any{"type": "string", "description": "host name or IP address"},
|
|
"port": map[string]any{"type": "integer", "description": "TCP port, 1-65535"},
|
|
"timeout_ms": map[string]any{"type": "integer", "description": "give up after this long (default 3000, at most 30000)"},
|
|
},
|
|
Run: func(args map[string]any) (any, error) {
|
|
host, err := text(args, "host")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
port, err := whole(args, "port", 0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
timeout, err := timeoutOf(args)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return CheckTCP(host, port, timeout)
|
|
},
|
|
},
|
|
{
|
|
Name: "netcheck_dns",
|
|
Description: "Look a name up with this machine's resolver (its /etc/resolv.conf and /etc/hosts). " +
|
|
"type is A, AAAA, CNAME, TXT or MX; answers the records found, or the error.",
|
|
Input: map[string]any{
|
|
"name": map[string]any{"type": "string", "description": "the name to look up"},
|
|
"type": map[string]any{"type": "string", "enum": []string{"A", "AAAA", "CNAME", "TXT", "MX"}, "description": "record type (default A)"},
|
|
"timeout_ms": map[string]any{"type": "integer", "description": "give up after this long (default 3000, at most 30000)"},
|
|
},
|
|
Run: func(args map[string]any) (any, error) {
|
|
name, err := text(args, "name")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
kind, _ := args["type"].(string)
|
|
timeout, err := timeoutOf(args)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return CheckDNS(name, kind, timeout)
|
|
},
|
|
},
|
|
}
|
|
}
|