@soyrageagency/docker-mcp 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +882 -0
  3. package/dist/ai/credentials.d.ts +72 -0
  4. package/dist/ai/credentials.js +157 -0
  5. package/dist/ai/credentials.js.map +1 -0
  6. package/dist/ai/login.d.ts +22 -0
  7. package/dist/ai/login.js +189 -0
  8. package/dist/ai/login.js.map +1 -0
  9. package/dist/ai/provider.d.ts +29 -0
  10. package/dist/ai/provider.js +113 -0
  11. package/dist/ai/provider.js.map +1 -0
  12. package/dist/assets.d.ts +20 -0
  13. package/dist/assets.js +56 -0
  14. package/dist/assets.js.map +1 -0
  15. package/dist/branding.d.ts +31 -0
  16. package/dist/branding.js +61 -0
  17. package/dist/branding.js.map +1 -0
  18. package/dist/config.d.ts +65 -0
  19. package/dist/config.js +128 -0
  20. package/dist/config.js.map +1 -0
  21. package/dist/docker/client.d.ts +66 -0
  22. package/dist/docker/client.js +161 -0
  23. package/dist/docker/client.js.map +1 -0
  24. package/dist/docker/compose.d.ts +74 -0
  25. package/dist/docker/compose.js +126 -0
  26. package/dist/docker/compose.js.map +1 -0
  27. package/dist/entry.d.ts +15 -0
  28. package/dist/entry.js +33 -0
  29. package/dist/entry.js.map +1 -0
  30. package/dist/index.d.ts +14 -0
  31. package/dist/index.js +100 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/logger.d.ts +25 -0
  34. package/dist/logger.js +68 -0
  35. package/dist/logger.js.map +1 -0
  36. package/dist/panel/index.d.ts +16 -0
  37. package/dist/panel/index.js +53 -0
  38. package/dist/panel/index.js.map +1 -0
  39. package/dist/panel/public/app.js +1004 -0
  40. package/dist/panel/public/index.html +243 -0
  41. package/dist/panel/public/styles.css +428 -0
  42. package/dist/panel/server.d.ts +23 -0
  43. package/dist/panel/server.js +355 -0
  44. package/dist/panel/server.js.map +1 -0
  45. package/dist/panel/service.d.ts +327 -0
  46. package/dist/panel/service.js +1356 -0
  47. package/dist/panel/service.js.map +1 -0
  48. package/dist/plugins.d.ts +50 -0
  49. package/dist/plugins.js +121 -0
  50. package/dist/plugins.js.map +1 -0
  51. package/dist/ragedocker.d.ts +21 -0
  52. package/dist/ragedocker.js +164 -0
  53. package/dist/ragedocker.js.map +1 -0
  54. package/dist/tools/about.d.ts +15 -0
  55. package/dist/tools/about.js +55 -0
  56. package/dist/tools/about.js.map +1 -0
  57. package/dist/tools/compose.d.ts +19 -0
  58. package/dist/tools/compose.js +145 -0
  59. package/dist/tools/compose.js.map +1 -0
  60. package/dist/tools/containers.d.ts +13 -0
  61. package/dist/tools/containers.js +119 -0
  62. package/dist/tools/containers.js.map +1 -0
  63. package/dist/tools/context.d.ts +37 -0
  64. package/dist/tools/context.js +13 -0
  65. package/dist/tools/context.js.map +1 -0
  66. package/dist/tools/images.d.ts +11 -0
  67. package/dist/tools/images.js +38 -0
  68. package/dist/tools/images.js.map +1 -0
  69. package/dist/tools/index.d.ts +15 -0
  70. package/dist/tools/index.js +34 -0
  71. package/dist/tools/index.js.map +1 -0
  72. package/dist/tools/lifecycle.d.ts +16 -0
  73. package/dist/tools/lifecycle.js +165 -0
  74. package/dist/tools/lifecycle.js.map +1 -0
  75. package/dist/tools/logs.d.ts +15 -0
  76. package/dist/tools/logs.js +110 -0
  77. package/dist/tools/logs.js.map +1 -0
  78. package/dist/tools/system.d.ts +14 -0
  79. package/dist/tools/system.js +108 -0
  80. package/dist/tools/system.js.map +1 -0
  81. package/dist/tui/ansi.d.ts +66 -0
  82. package/dist/tui/ansi.js +118 -0
  83. package/dist/tui/ansi.js.map +1 -0
  84. package/dist/tui/app.d.ts +160 -0
  85. package/dist/tui/app.js +1339 -0
  86. package/dist/tui/app.js.map +1 -0
  87. package/dist/tui/box.d.ts +18 -0
  88. package/dist/tui/box.js +38 -0
  89. package/dist/tui/box.js.map +1 -0
  90. package/dist/tui/index.d.ts +19 -0
  91. package/dist/tui/index.js +66 -0
  92. package/dist/tui/index.js.map +1 -0
  93. package/dist/update/channel.d.ts +82 -0
  94. package/dist/update/channel.js +178 -0
  95. package/dist/update/channel.js.map +1 -0
  96. package/dist/updates.json +42 -0
  97. package/dist/utils/format.d.ts +24 -0
  98. package/dist/utils/format.js +69 -0
  99. package/dist/utils/format.js.map +1 -0
  100. package/dist/utils/result.d.ts +35 -0
  101. package/dist/utils/result.js +31 -0
  102. package/dist/utils/result.js.map +1 -0
  103. package/package.json +90 -0
  104. package/scripts/install.mjs +132 -0
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Docker Compose tools.
3
+ *
4
+ * Read-only:
5
+ * • compose_ps — show the services and health of a stack.
6
+ * • compose_config — validate & render the effective configuration.
7
+ *
8
+ * State-changing (skipped in read-only mode):
9
+ * • deploy_stack — `compose up -d` (optionally --build).
10
+ * • compose_down — tear a stack down.
11
+ * • compose_restart — restart all/selected services.
12
+ * • compose_pull — pull the latest images.
13
+ *
14
+ * Part of Docker MCP Server.
15
+ * Crafted by SoyRage Agency — https://soyrage.es/
16
+ * Licensed under the MIT License.
17
+ */
18
+ import { z } from "zod";
19
+ import { fail, guard, ok } from "../utils/result.js";
20
+ /** Shared input fields identifying a Compose project. */
21
+ const targetShape = {
22
+ file: z
23
+ .string()
24
+ .min(1)
25
+ .describe("Path to a compose file, or a directory containing one " +
26
+ "(compose.yaml / docker-compose.yml). Relative paths resolve against " +
27
+ "the server's configured Compose working directory."),
28
+ project: z
29
+ .string()
30
+ .optional()
31
+ .describe("Explicit Compose project name (defaults to the folder name)."),
32
+ };
33
+ /** Turn a ComposeResult into a readable tool result. */
34
+ function present(action, result) {
35
+ const body = [result.stdout.trim(), result.stderr.trim()]
36
+ .filter(Boolean)
37
+ .join("\n");
38
+ if (result.code !== 0) {
39
+ return fail(`${action} failed (exit ${result.code}).\n\n${body || "(no output)"}`);
40
+ }
41
+ return ok(`${action} succeeded.\n\n\`\`\`\n${body || "(no output)"}\n\`\`\``);
42
+ }
43
+ export function registerComposeTools(ctx) {
44
+ const { server, compose, config, logger } = ctx;
45
+ // ---------- Read-only tools (always available) --------------------------
46
+ server.registerTool("compose_ps", {
47
+ title: "Compose: list services",
48
+ description: "List the services defined by a Compose stack and their current " +
49
+ "state/health. Point `file` at the compose file or its directory.",
50
+ inputSchema: targetShape,
51
+ }, async (args) => guard(async () => {
52
+ const result = await compose.ps(args);
53
+ if (result.code !== 0)
54
+ return present("compose ps", result);
55
+ // `--format json` yields one JSON object per line; render compactly.
56
+ const services = result.stdout
57
+ .split(/\r?\n/)
58
+ .map((line) => line.trim())
59
+ .filter(Boolean)
60
+ .map((line) => {
61
+ try {
62
+ return JSON.parse(line);
63
+ }
64
+ catch {
65
+ return null;
66
+ }
67
+ })
68
+ .filter((entry) => entry !== null);
69
+ if (services.length === 0) {
70
+ return ok("No services are currently running for this stack.");
71
+ }
72
+ const lines = services.map((svc) => {
73
+ const name = svc.Service ?? svc.Name ?? "?";
74
+ const state = svc.State ?? "?";
75
+ const status = svc.Status ?? "";
76
+ return `• ${name}: ${state} ${status}`.trimEnd();
77
+ });
78
+ return ok(`${services.length} service(s):\n\n${lines.join("\n")}`);
79
+ }));
80
+ server.registerTool("compose_config", {
81
+ title: "Compose: validate config",
82
+ description: "Validate a Compose file and render its fully-resolved configuration " +
83
+ "(interpolated variables, merged overrides). A non-zero result means " +
84
+ "the file has errors.",
85
+ inputSchema: targetShape,
86
+ }, async (args) => guard(async () => present("compose config", await compose.config(args))));
87
+ // ---------- State-changing tools (respect read-only mode) ---------------
88
+ if (config.readOnly) {
89
+ logger.info("Read-only mode: Compose deploy/down/restart tools are disabled.");
90
+ return;
91
+ }
92
+ server.registerTool("deploy_stack", {
93
+ title: "Compose: deploy stack",
94
+ description: "Deploy or update a Compose stack in the background (`compose up -d`). " +
95
+ "Optionally rebuild images and/or scope to specific services. This is " +
96
+ "the one-shot 'ship it' action.",
97
+ inputSchema: {
98
+ ...targetShape,
99
+ build: z
100
+ .boolean()
101
+ .optional()
102
+ .describe("Rebuild images before starting (default: false)."),
103
+ services: z
104
+ .array(z.string())
105
+ .optional()
106
+ .describe("Limit the action to these services (default: all)."),
107
+ },
108
+ }, async ({ file, project, build, services }) => guard(async () => present("deploy_stack", await compose.up({ file, project }, { build, services }))));
109
+ server.registerTool("compose_down", {
110
+ title: "Compose: tear down stack",
111
+ description: "Stop and remove a Compose stack (`compose down`). Set " +
112
+ "`removeVolumes` to also delete named volumes — destructive.",
113
+ inputSchema: {
114
+ ...targetShape,
115
+ removeVolumes: z
116
+ .boolean()
117
+ .optional()
118
+ .describe("Also remove named volumes (default: false)."),
119
+ },
120
+ }, async ({ file, project, removeVolumes }) => guard(async () => present("compose_down", await compose.down({ file, project }, { volumes: removeVolumes }))));
121
+ server.registerTool("compose_restart", {
122
+ title: "Compose: restart services",
123
+ description: "Restart all services in a stack, or only the ones you name.",
124
+ inputSchema: {
125
+ ...targetShape,
126
+ services: z
127
+ .array(z.string())
128
+ .optional()
129
+ .describe("Services to restart (default: all)."),
130
+ },
131
+ }, async ({ file, project, services }) => guard(async () => present("compose_restart", await compose.restart({ file, project }, services ?? []))));
132
+ server.registerTool("compose_pull", {
133
+ title: "Compose: pull images",
134
+ description: "Pull the latest images referenced by a stack without starting it. " +
135
+ "Pair with `deploy_stack` to perform a rolling update.",
136
+ inputSchema: {
137
+ ...targetShape,
138
+ services: z
139
+ .array(z.string())
140
+ .optional()
141
+ .describe("Services to pull (default: all)."),
142
+ },
143
+ }, async ({ file, project, services }) => guard(async () => present("compose_pull", await compose.pull({ file, project }, services ?? []))));
144
+ }
145
+ //# sourceMappingURL=compose.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compose.js","sourceRoot":"","sources":["../../src/tools/compose.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Read-only container insight tools.
3
+ *
4
+ * • list_containers — the "what's running?" overview
5
+ * • inspect_container — full low-level configuration for one container
6
+ * • container_stats — a one-shot CPU / memory / network snapshot
7
+ *
8
+ * Part of Docker MCP Server.
9
+ * Crafted by SoyRage Agency — https://soyrage.es/
10
+ * Licensed under the MIT License.
11
+ */
12
+ import type { ToolContext } from "./context.js";
13
+ export declare function registerContainerTools({ server, docker }: ToolContext): void;
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Read-only container insight tools.
3
+ *
4
+ * • list_containers — the "what's running?" overview
5
+ * • inspect_container — full low-level configuration for one container
6
+ * • container_stats — a one-shot CPU / memory / network snapshot
7
+ *
8
+ * Part of Docker MCP Server.
9
+ * Crafted by SoyRage Agency — https://soyrage.es/
10
+ * Licensed under the MIT License.
11
+ */
12
+ import { z } from "zod";
13
+ import { normalizeName } from "../docker/client.js";
14
+ import { asJsonBlock, formatBytes, formatRelativeTime, renderTable, truncate, } from "../utils/format.js";
15
+ import { guard, ok } from "../utils/result.js";
16
+ /** Compute a percentage from Docker's raw CPU accounting deltas. */
17
+ function cpuPercent(stats) {
18
+ const cpuDelta = stats.cpu_stats.cpu_usage.total_usage -
19
+ stats.precpu_stats.cpu_usage.total_usage;
20
+ const systemDelta = (stats.cpu_stats.system_cpu_usage ?? 0) -
21
+ (stats.precpu_stats.system_cpu_usage ?? 0);
22
+ const cores = stats.cpu_stats.online_cpus ??
23
+ stats.cpu_stats.cpu_usage.percpu_usage?.length ??
24
+ 1;
25
+ if (systemDelta <= 0 || cpuDelta <= 0)
26
+ return 0;
27
+ return (cpuDelta / systemDelta) * cores * 100;
28
+ }
29
+ export function registerContainerTools({ server, docker }) {
30
+ server.registerTool("list_containers", {
31
+ title: "List containers",
32
+ description: "List Docker containers with their status, image, ports and uptime. " +
33
+ "By default only running containers are shown; set `all` to include " +
34
+ "stopped ones. This is the best starting point to understand what is " +
35
+ "deployed on the host.",
36
+ inputSchema: {
37
+ all: z
38
+ .boolean()
39
+ .optional()
40
+ .describe("Include stopped/exited containers (default: false)."),
41
+ },
42
+ }, async ({ all }) => guard(async () => {
43
+ const containers = await docker.listContainers(all ?? false);
44
+ const rows = containers.map((c) => {
45
+ const name = normalizeName(c.Names[0] ?? c.Id);
46
+ const ports = (c.Ports ?? [])
47
+ .filter((p) => p.PublicPort)
48
+ .map((p) => `${p.PublicPort}→${p.PrivatePort}/${p.Type}`)
49
+ .join(", ");
50
+ return [
51
+ truncate(name, 24),
52
+ truncate(c.Image, 28),
53
+ c.State,
54
+ truncate(c.Status, 22),
55
+ ports || "—",
56
+ ];
57
+ });
58
+ const table = renderTable(["NAME", "IMAGE", "STATE", "STATUS", "PORTS"], rows);
59
+ const summary = `${containers.length} container(s)${all ? "" : " running"}.`;
60
+ return ok(`${summary}\n\n${table}`);
61
+ }));
62
+ server.registerTool("inspect_container", {
63
+ title: "Inspect container",
64
+ description: "Return the full low-level configuration of a single container " +
65
+ "(environment, mounts, network settings, restart policy, health, …). " +
66
+ "Accepts a container name, short id or full id.",
67
+ inputSchema: {
68
+ container: z
69
+ .string()
70
+ .min(1)
71
+ .describe("Container name, short id or full id."),
72
+ },
73
+ }, async ({ container }) => guard(async () => {
74
+ const handle = await docker.resolveContainer(container);
75
+ const details = await handle.inspect();
76
+ // Surface the most useful fields as a readable header, then the raw
77
+ // payload for anything the model wants to dig into.
78
+ const header = [
79
+ `Name: ${normalizeName(details.Name)}`,
80
+ `Image: ${details.Config.Image}`,
81
+ `State: ${details.State.Status} (health: ${details.State.Health?.Status ?? "n/a"})`,
82
+ `Created: ${details.Created}`,
83
+ `Restart: ${details.HostConfig.RestartPolicy?.Name || "no"}`,
84
+ ].join("\n");
85
+ return ok(`${header}\n\n${asJsonBlock(details)}`);
86
+ }));
87
+ server.registerTool("container_stats", {
88
+ title: "Container resource stats",
89
+ description: "Take a one-shot snapshot of a container's live CPU %, memory usage " +
90
+ "and network I/O. Useful to answer 'why is my server slow?'.",
91
+ inputSchema: {
92
+ container: z
93
+ .string()
94
+ .min(1)
95
+ .describe("Container name, short id or full id."),
96
+ },
97
+ }, async ({ container }) => guard(async () => {
98
+ const handle = await docker.resolveContainer(container);
99
+ // `stream: false` returns a single sample instead of an endless stream.
100
+ const stats = (await handle.stats({
101
+ stream: false,
102
+ }));
103
+ const memUsage = stats.memory_stats.usage ?? 0;
104
+ const memLimit = stats.memory_stats.limit ?? 0;
105
+ const memPercent = memLimit > 0 ? (memUsage / memLimit) * 100 : 0;
106
+ const networks = stats.networks ?? {};
107
+ const rx = Object.values(networks).reduce((sum, n) => sum + n.rx_bytes, 0);
108
+ const tx = Object.values(networks).reduce((sum, n) => sum + n.tx_bytes, 0);
109
+ const lines = [
110
+ `Container: ${normalizeName(stats.name ?? container)}`,
111
+ `CPU: ${cpuPercent(stats).toFixed(2)} %`,
112
+ `Memory: ${formatBytes(memUsage)} / ${formatBytes(memLimit)} (${memPercent.toFixed(1)} %)`,
113
+ `Net RX/TX: ${formatBytes(rx)} / ${formatBytes(tx)}`,
114
+ `Sampled: ${formatRelativeTime(Math.floor(new Date(stats.read).getTime() / 1000))}`,
115
+ ];
116
+ return ok(lines.join("\n"));
117
+ }));
118
+ }
119
+ //# sourceMappingURL=containers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"containers.js","sourceRoot":"","sources":["../../src/tools/containers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Shared tool context.
3
+ *
4
+ * Every tool-registration function receives one of these. It bundles the
5
+ * collaborators tools need (Docker client, Compose driver, config, logger) so
6
+ * individual modules stay free of global state and are trivial to test.
7
+ *
8
+ * Part of Docker MCP Server.
9
+ * Crafted by SoyRage Agency — https://soyrage.es/
10
+ * Licensed under the MIT License.
11
+ */
12
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
13
+ import type { AppConfig } from "../config.js";
14
+ import type { Logger } from "../logger.js";
15
+ import type { DockerClient } from "../docker/client.js";
16
+ import type { ComposeDriver } from "../docker/compose.js";
17
+ /**
18
+ * Lightweight plugin metadata surfaced to tools (e.g. `list_plugins`) without
19
+ * pulling in the plugin module, keeping the dependency graph acyclic.
20
+ */
21
+ export interface PluginInfo {
22
+ readonly name: string;
23
+ readonly title: string;
24
+ readonly category: string;
25
+ readonly mutating: boolean;
26
+ readonly enabled: boolean;
27
+ }
28
+ /** Dependencies handed to each tool group at registration time. */
29
+ export interface ToolContext {
30
+ readonly server: McpServer;
31
+ readonly docker: DockerClient;
32
+ readonly compose: ComposeDriver;
33
+ readonly config: AppConfig;
34
+ readonly logger: Logger;
35
+ /** The full plugin catalogue with per-plugin enabled state. */
36
+ readonly plugins: readonly PluginInfo[];
37
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Shared tool context.
3
+ *
4
+ * Every tool-registration function receives one of these. It bundles the
5
+ * collaborators tools need (Docker client, Compose driver, config, logger) so
6
+ * individual modules stay free of global state and are trivial to test.
7
+ *
8
+ * Part of Docker MCP Server.
9
+ * Crafted by SoyRage Agency — https://soyrage.es/
10
+ * Licensed under the MIT License.
11
+ */
12
+ export {};
13
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/tools/context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Image inventory tools.
3
+ *
4
+ * • list_images — show locally cached images with size and age.
5
+ *
6
+ * Part of Docker MCP Server.
7
+ * Crafted by SoyRage Agency — https://soyrage.es/
8
+ * Licensed under the MIT License.
9
+ */
10
+ import type { ToolContext } from "./context.js";
11
+ export declare function registerImageTools({ server, docker }: ToolContext): void;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Image inventory tools.
3
+ *
4
+ * • list_images — show locally cached images with size and age.
5
+ *
6
+ * Part of Docker MCP Server.
7
+ * Crafted by SoyRage Agency — https://soyrage.es/
8
+ * Licensed under the MIT License.
9
+ */
10
+ import { formatBytes, formatRelativeTime, renderTable, truncate, } from "../utils/format.js";
11
+ import { guard, ok } from "../utils/result.js";
12
+ export function registerImageTools({ server, docker }) {
13
+ server.registerTool("list_images", {
14
+ title: "List images",
15
+ description: "List Docker images cached on the host, including repository:tag, " +
16
+ "size and how long ago they were created. Handy for spotting stale " +
17
+ "or oversized images before a cleanup.",
18
+ inputSchema: {},
19
+ }, async () => guard(async () => {
20
+ const images = await docker.listImages();
21
+ // Flatten one row per repo tag; untagged images show as <none>.
22
+ const rows = images.flatMap((image) => {
23
+ const tags = image.RepoTags && image.RepoTags.length > 0
24
+ ? image.RepoTags
25
+ : ["<none>:<none>"];
26
+ return tags.map((tag) => [
27
+ truncate(tag, 40),
28
+ (image.Id ?? "").replace("sha256:", "").slice(0, 12),
29
+ formatBytes(image.Size),
30
+ formatRelativeTime(image.Created),
31
+ ]);
32
+ });
33
+ const table = renderTable(["REPOSITORY:TAG", "IMAGE ID", "SIZE", "CREATED"], rows);
34
+ const total = images.reduce((sum, image) => sum + image.Size, 0);
35
+ return ok(`${images.length} image(s), ${formatBytes(total)} total on disk.\n\n${table}`);
36
+ }));
37
+ }
38
+ //# sourceMappingURL=images.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"images.js","sourceRoot":"","sources":["../../src/tools/images.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,WAAW,EACX,QAAQ,GACT,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,oBAAoB,CAAC;AAE/C,MAAM,UAAU,kBAAkB,CAAC,EAAE,MAAM,EAAE,MAAM,EAAe;IAChE,MAAM,CAAC,YAAY,CACjB,aAAa,EACb;QACE,KAAK,EAAE,aAAa;QACpB,WAAW,EACT,mEAAmE;YACnE,oEAAoE;YACpE,uCAAuC;QACzC,WAAW,EAAE,EAAE;KAChB,EACD,KAAK,IAAI,EAAE,CACT,KAAK,CAAC,KAAK,IAAI,EAAE;QACf,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,UAAU,EAAE,CAAC;QAEzC,gEAAgE;QAChE,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;YACpC,MAAM,IAAI,GACR,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC;gBACzC,CAAC,CAAC,KAAK,CAAC,QAAQ;gBAChB,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC;YACxB,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC;gBACvB,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC;gBACjB,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;gBACpD,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC;gBACvB,kBAAkB,CAAC,KAAK,CAAC,OAAO,CAAC;aAClC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,WAAW,CACvB,CAAC,gBAAgB,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,CAAC,EACjD,IAAI,CACL,CAAC;QACF,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,GAAG,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAEjE,OAAO,EAAE,CACP,GAAG,MAAM,CAAC,MAAM,cAAc,WAAW,CAAC,KAAK,CAAC,sBAAsB,KAAK,EAAE,CAC9E,CAAC;IACJ,CAAC,CAAC,CACL,CAAC;AACJ,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Tool registry.
3
+ *
4
+ * Registers every tool group against the MCP server in one place. The order is
5
+ * purely cosmetic (it influences how clients that preserve order list tools).
6
+ * Read-only and opt-in gating is handled inside each group.
7
+ *
8
+ * Part of Docker MCP Server.
9
+ * Crafted by SoyRage Agency — https://soyrage.es/
10
+ * Licensed under the MIT License.
11
+ */
12
+ import type { ToolContext } from "./context.js";
13
+ /** Wire up all tools. Called once from the entry point. */
14
+ export declare function registerAllTools(context: ToolContext): void;
15
+ export type { ToolContext };
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Tool registry.
3
+ *
4
+ * Registers every tool group against the MCP server in one place. The order is
5
+ * purely cosmetic (it influences how clients that preserve order list tools).
6
+ * Read-only and opt-in gating is handled inside each group.
7
+ *
8
+ * Part of Docker MCP Server.
9
+ * Crafted by SoyRage Agency — https://soyrage.es/
10
+ * Licensed under the MIT License.
11
+ */
12
+ import { registerAboutTool } from "./about.js";
13
+ import { registerContainerTools } from "./containers.js";
14
+ import { registerLogTools } from "./logs.js";
15
+ import { registerLifecycleTools } from "./lifecycle.js";
16
+ import { registerImageTools } from "./images.js";
17
+ import { registerSystemTools } from "./system.js";
18
+ import { registerComposeTools } from "./compose.js";
19
+ /** Wire up all tools. Called once from the entry point. */
20
+ export function registerAllTools(context) {
21
+ // Identity first so it heads the tool list and the assistant can greet.
22
+ registerAboutTool(context);
23
+ registerContainerTools(context);
24
+ registerLogTools(context);
25
+ registerImageTools(context);
26
+ registerSystemTools(context);
27
+ registerComposeTools(context);
28
+ // Registered last so mutating actions appear after insight in tool lists.
29
+ registerLifecycleTools(context);
30
+ context.logger.info(context.config.readOnly
31
+ ? "Tools registered in READ-ONLY mode."
32
+ : "Tools registered in full read/write mode.");
33
+ }
34
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEpD,2DAA2D;AAC3D,MAAM,UAAU,gBAAgB,CAAC,OAAoB;IACnD,wEAAwE;IACxE,iBAAiB,CAAC,OAAO,CAAC,CAAC;IAC3B,sBAAsB,CAAC,OAAO,CAAC,CAAC;IAChC,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAC1B,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAC5B,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC7B,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAC9B,0EAA0E;IAC1E,sBAAsB,CAAC,OAAO,CAAC,CAAC;IAEhC,OAAO,CAAC,MAAM,CAAC,IAAI,CACjB,OAAO,CAAC,MAAM,CAAC,QAAQ;QACrB,CAAC,CAAC,qCAAqC;QACvC,CAAC,CAAC,2CAA2C,CAChD,CAAC;AACJ,CAAC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Container lifecycle tools (state-changing).
3
+ *
4
+ * • start_container • stop_container
5
+ * • restart_container • remove_container
6
+ * • exec_in_container (opt-in via DOCKER_MCP_ALLOW_EXEC)
7
+ *
8
+ * The entire group is skipped when the server runs in read-only mode, so a
9
+ * demo deployment can safely expose insight without risk of mutation.
10
+ *
11
+ * Part of Docker MCP Server.
12
+ * Crafted by SoyRage Agency — https://soyrage.es/
13
+ * Licensed under the MIT License.
14
+ */
15
+ import type { ToolContext } from "./context.js";
16
+ export declare function registerLifecycleTools(ctx: ToolContext): void;
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Container lifecycle tools (state-changing).
3
+ *
4
+ * • start_container • stop_container
5
+ * • restart_container • remove_container
6
+ * • exec_in_container (opt-in via DOCKER_MCP_ALLOW_EXEC)
7
+ *
8
+ * The entire group is skipped when the server runs in read-only mode, so a
9
+ * demo deployment can safely expose insight without risk of mutation.
10
+ *
11
+ * Part of Docker MCP Server.
12
+ * Crafted by SoyRage Agency — https://soyrage.es/
13
+ * Licensed under the MIT License.
14
+ */
15
+ import { z } from "zod";
16
+ import { normalizeName } from "../docker/client.js";
17
+ import { guard, ok } from "../utils/result.js";
18
+ export function registerLifecycleTools(ctx) {
19
+ const { server, docker, config, logger } = ctx;
20
+ // Respect read-only mode: register nothing state-changing.
21
+ if (config.readOnly) {
22
+ logger.info("Read-only mode: lifecycle tools are disabled.");
23
+ return;
24
+ }
25
+ server.registerTool("start_container", {
26
+ title: "Start container",
27
+ description: "Start a stopped container. No-op if it is already running. " +
28
+ "Returns the resulting state.",
29
+ inputSchema: {
30
+ container: z.string().min(1).describe("Container name or id."),
31
+ },
32
+ }, async ({ container }) => guard(async () => {
33
+ const handle = await docker.resolveContainer(container);
34
+ const info = await handle.inspect();
35
+ if (info.State.Running) {
36
+ return ok(`Container "${normalizeName(container)}" is already running.`);
37
+ }
38
+ await handle.start();
39
+ return ok(`Started "${normalizeName(container)}".`);
40
+ }));
41
+ server.registerTool("stop_container", {
42
+ title: "Stop container",
43
+ description: "Gracefully stop a running container (SIGTERM, then SIGKILL after a " +
44
+ "grace period). No-op if it is already stopped.",
45
+ inputSchema: {
46
+ container: z.string().min(1).describe("Container name or id."),
47
+ timeout: z
48
+ .number()
49
+ .int()
50
+ .positive()
51
+ .max(300)
52
+ .optional()
53
+ .describe("Seconds to wait before force-killing (default: 10)."),
54
+ },
55
+ }, async ({ container, timeout }) => guard(async () => {
56
+ const handle = await docker.resolveContainer(container);
57
+ const info = await handle.inspect();
58
+ if (!info.State.Running) {
59
+ return ok(`Container "${normalizeName(container)}" is already stopped.`);
60
+ }
61
+ await handle.stop({ t: timeout ?? 10 });
62
+ return ok(`Stopped "${normalizeName(container)}".`);
63
+ }));
64
+ server.registerTool("restart_container", {
65
+ title: "Restart container",
66
+ description: "Restart a container. This is the go-to fix for 'turn it off and on " +
67
+ "again' style requests.",
68
+ inputSchema: {
69
+ container: z.string().min(1).describe("Container name or id."),
70
+ timeout: z
71
+ .number()
72
+ .int()
73
+ .positive()
74
+ .max(300)
75
+ .optional()
76
+ .describe("Seconds to wait before force-killing (default: 10)."),
77
+ },
78
+ }, async ({ container, timeout }) => guard(async () => {
79
+ const handle = await docker.resolveContainer(container);
80
+ await handle.restart({ t: timeout ?? 10 });
81
+ return ok(`Restarted "${normalizeName(container)}".`);
82
+ }));
83
+ server.registerTool("remove_container", {
84
+ title: "Remove container",
85
+ description: "Remove a container. Requires `force` to remove a running one. This " +
86
+ "is destructive and cannot be undone — prefer stopping first.",
87
+ inputSchema: {
88
+ container: z.string().min(1).describe("Container name or id."),
89
+ force: z
90
+ .boolean()
91
+ .optional()
92
+ .describe("Remove even if running (default: false)."),
93
+ removeVolumes: z
94
+ .boolean()
95
+ .optional()
96
+ .describe("Also remove anonymous volumes (default: false)."),
97
+ },
98
+ }, async ({ container, force, removeVolumes }) => guard(async () => {
99
+ const handle = await docker.resolveContainer(container);
100
+ await handle.remove({ force: force ?? false, v: removeVolumes ?? false });
101
+ return ok(`Removed "${normalizeName(container)}".`);
102
+ }));
103
+ // ---- Optional, powerful: run a command inside a container ---------------
104
+ if (config.allowExec) {
105
+ logger.info("exec_in_container tool is ENABLED (DOCKER_MCP_ALLOW_EXEC).");
106
+ server.registerTool("exec_in_container", {
107
+ title: "Execute command in container",
108
+ description: "Run a one-off command inside a running container and return its " +
109
+ "combined output. The command is passed as an argument array (no " +
110
+ "shell), e.g. [\"ls\", \"-la\", \"/app\"]. Disabled unless the " +
111
+ "server operator opts in.",
112
+ inputSchema: {
113
+ container: z.string().min(1).describe("Container name or id."),
114
+ command: z
115
+ .array(z.string())
116
+ .min(1)
117
+ .describe('Argument array, e.g. ["cat", "/etc/hostname"].'),
118
+ workdir: z
119
+ .string()
120
+ .optional()
121
+ .describe("Working directory inside the container."),
122
+ },
123
+ }, async ({ container, command, workdir }) => guard(async () => {
124
+ const handle = await docker.resolveContainer(container);
125
+ const exec = await handle.exec({
126
+ Cmd: command,
127
+ AttachStdout: true,
128
+ AttachStderr: true,
129
+ WorkingDir: workdir,
130
+ });
131
+ const stream = await exec.start({});
132
+ const output = await collectStream(stream);
133
+ const inspect = await exec.inspect();
134
+ const header = `exit code ${inspect.ExitCode ?? "?"}`;
135
+ return ok(`\`${command.join(" ")}\` → ${header}\n\n\`\`\`\n${output.trimEnd()}\n\`\`\``);
136
+ }));
137
+ }
138
+ }
139
+ /** Read a dockerode exec stream to completion, demultiplexing frame headers. */
140
+ function collectStream(stream) {
141
+ return new Promise((resolveStream, rejectStream) => {
142
+ const chunks = [];
143
+ stream.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
144
+ stream.on("end", () => {
145
+ const buffer = Buffer.concat(chunks);
146
+ const out = [];
147
+ let offset = 0;
148
+ // Best-effort demultiplex; fall back to raw text if unframed.
149
+ while (offset + 8 <= buffer.length) {
150
+ const type = buffer[offset];
151
+ if (type > 2 || buffer[offset + 1] !== 0) {
152
+ resolveStream(buffer.toString("utf8"));
153
+ return;
154
+ }
155
+ const size = buffer.readUInt32BE(offset + 4);
156
+ const start = offset + 8;
157
+ out.push(buffer.subarray(start, start + size).toString("utf8"));
158
+ offset = start + size;
159
+ }
160
+ resolveStream(out.length ? out.join("") : buffer.toString("utf8"));
161
+ });
162
+ stream.on("error", rejectStream);
163
+ });
164
+ }
165
+ //# sourceMappingURL=lifecycle.js.map