// portainer's tools — its own code (novox/hq ADR 0039), importing portainer's own client. They // return structured data; the mesh serves them through the sdk's tool harness. portainer is // tools-only (no events entrypoint): a container starting or stopping is the host's signal to emit, // not Portainer's to re-announce. import { registerModuleTools, type ToolDefinition } from "@novox/mesh-sdk/tools"; import { PortainerClient } from "../client.js"; export function getPortainerTools(portainer: PortainerClient): ToolDefinition[] { return [ { name: "portainer_endpoints", description: "List the environments (endpoints) Portainer manages — each a Docker host or cluster.", input: {}, run: async () => { const endpoints = await portainer.listEndpoints(); return { count: endpoints.length, endpoints }; }, }, { name: "portainer_stacks", description: "List the stacks (compose/swarm deployments) Portainer knows about.", input: {}, run: async () => { const stacks = await portainer.listStacks(); return { count: stacks.length, stacks }; }, }, { name: "portainer_containers", description: "List the containers on one Portainer endpoint, including stopped ones.", input: { endpoint: { type: "number", description: "the endpoint id (see portainer_endpoints)" } }, run: async (args) => { const endpointId = Number(args.endpoint); const containers = await portainer.listContainers(endpointId); return { endpointId, count: containers.length, containers }; }, }, ]; } // The tools exist only when a token is configured; without one, portainer contributes none rather // than failing the whole runtime. registerModuleTools("portainer", (env) => { try { return getPortainerTools(PortainerClient.fromEnv(env)); } catch { return []; } });