@supernova123/docker-mcp-server 0.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Nova
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,128 @@
1
+ # @supernova123/docker-mcp-server
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@supernova123/docker-mcp-server)](https://www.npmjs.com/package/@supernova123/docker-mcp-server)
4
+ [![npm downloads](https://img.shields.io/npm/dm/@supernova123/docker-mcp-server)](https://www.npmjs.com/package/@supernova123/docker-mcp-server)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
6
+ [![MCP](https://img.shields.io/badge/MCP-compatible-green)](https://modelcontextprotocol.io)
7
+ [![Claude Desktop](https://img.shields.io/badge/Claude%20Desktop-compatible-purple)](https://claude.ai)
8
+
9
+ **The Docker MCP server designed for agents that need their containers to stay running.**
10
+
11
+ > **Without this:** Your agent deploys a container, it crashes at 3am, and nobody notices until the user complains. Compose stacks drift. Health checks are manual. Logs are scattered across terminals.
12
+ >
13
+ > **With this:** Your agent checks health, watches for readiness, restarts crashed containers, and follows logs, all through a single MCP interface. Containers stay running because your agent knows how to keep them running.
14
+
15
+ ## Why This Server?
16
+
17
+ There are 11+ Docker MCP servers on npm. Most are stale, GPL-licensed, or only cover basic CRUD. This one is different:
18
+
19
+ | | This server | ckreiling/mcp-server-docker | docker/hub-mcp |
20
+ |---|---|---|---|
21
+ | **License** | MIT | GPL-3.0 | Apache-2.0 |
22
+ | **Last updated** | Active | Jun 2025 (stale) | Active |
23
+ | **Health checks** | ✅ HTTP/TCP/exec probes | ❌ | ❌ |
24
+ | **Auto-restart** | ✅ set_restart_policy | ❌ | ❌ |
25
+ | **Compose lifecycle** | ✅ up/down/ps/logs/restart | ❌ | ❌ |
26
+ | **Log streaming** | ✅ tail + timestamp filter | Basic | Basic |
27
+ | **Agent positioning** | ✅ Built for agents | Generic Docker | Registry API |
28
+
29
+ ## Quick Start
30
+
31
+ One command to run:
32
+
33
+ ```bash
34
+ npx @supernova123/docker-mcp-server
35
+ ```
36
+
37
+ ### Claude Desktop / Cursor / VS Code Config
38
+
39
+ Add to your MCP settings:
40
+
41
+ ```json
42
+ {
43
+ "mcpServers": {
44
+ "docker": {
45
+ "command": "npx",
46
+ "args": ["-y", "@supernova123/docker-mcp-server"]
47
+ }
48
+ }
49
+ }
50
+ ```
51
+
52
+ ### Claude Code
53
+
54
+ ```bash
55
+ claude mcp add docker -- npx -y @supernova123/docker-mcp-server
56
+ ```
57
+
58
+ ## Tools
59
+
60
+ ### Container Lifecycle
61
+ | Tool | Description |
62
+ |------|-------------|
63
+ | `list_containers` | List containers with filters (state, label, name) |
64
+ | `inspect_container` | Get detailed container config and state |
65
+ | `start_container` | Start a stopped container |
66
+ | `stop_container` | Stop a running container |
67
+ | `restart_container` | Restart a container |
68
+ | `remove_container` | Remove a container (with force option) |
69
+ | `recreate_container` | Stop, remove, and re-create a container with same config |
70
+ | `run_container` | Create + start a container in one call |
71
+
72
+ ### Image Management
73
+ | Tool | Description |
74
+ |------|-------------|
75
+ | `list_images` | List images with optional filters |
76
+ | `pull_image` | Pull an image from a registry |
77
+ | `build_image` | Build an image from a Dockerfile |
78
+ | `remove_image` | Remove an image |
79
+
80
+ ### Docker Compose
81
+ | Tool | Description |
82
+ |------|-------------|
83
+ | `compose_up` | Bring up a Compose stack |
84
+ | `compose_down` | Tear down a Compose stack |
85
+ | `compose_ps` | List service states |
86
+ | `compose_logs` | Tail Compose service logs |
87
+ | `compose_restart` | Restart Compose services |
88
+
89
+ ### Health & Self-Healing
90
+ | Tool | Description |
91
+ |------|-------------|
92
+ | `check_health` | Run a health probe (HTTP, TCP, exec) |
93
+ | `watch_health` | Poll health until healthy or timeout |
94
+ | `set_restart_policy` | Change restart policy on a running container |
95
+
96
+ ### Logs & Observability
97
+ | Tool | Description |
98
+ |------|-------------|
99
+ | `stream_logs` | Get container logs with tail/timestamp filtering |
100
+ | `container_stats` | CPU, memory, network, block I/O snapshot |
101
+
102
+ ### Exec
103
+ | Tool | Description |
104
+ |------|-------------|
105
+ | `exec_in_container` | Run a command inside a running container |
106
+
107
+ ### Networks & Volumes
108
+ | Tool | Description |
109
+ |------|-------------|
110
+ | `list_networks` | List Docker networks |
111
+ | `list_volumes` | List Docker volumes |
112
+
113
+ ## Requirements
114
+
115
+ - Node.js 18+
116
+ - Docker daemon running locally (or remote via DOCKER_HOST)
117
+ - Docker socket accessible at `/var/run/docker.sock`
118
+
119
+ ## Security
120
+
121
+ - **Read-only by default**: all container and image tools read state; write operations (start/stop/remove) require explicit tool calls
122
+ - **No API keys needed**: connects to local Docker daemon via socket
123
+ - **No network access**: all operations are local Docker API calls
124
+ - **MIT License**: fully auditable
125
+
126
+ ## License
127
+
128
+ MIT
@@ -0,0 +1,12 @@
1
+ import Dockerode from "dockerode";
2
+ export interface DockerClientOptions {
3
+ socketPath?: string;
4
+ host?: string;
5
+ port?: number;
6
+ }
7
+ export declare function createDockerClient(options?: DockerClientOptions): Dockerode;
8
+ export declare function formatError(error: unknown): string;
9
+ export declare function formatContainer(container: Dockerode.ContainerInfo): Record<string, unknown>;
10
+ export declare function formatImage(image: Dockerode.ImageInfo): Record<string, unknown>;
11
+ export declare function formatBytes(bytes: number): string;
12
+ //# sourceMappingURL=docker.d.ts.map
package/dist/docker.js ADDED
@@ -0,0 +1,58 @@
1
+ import Dockerode from "dockerode";
2
+ export function createDockerClient(options) {
3
+ if (options?.socketPath) {
4
+ return new Dockerode({ socketPath: options.socketPath });
5
+ }
6
+ if (options?.host && options?.port) {
7
+ return new Dockerode({ host: options.host, port: options.port });
8
+ }
9
+ // Default: local socket
10
+ return new Dockerode({ socketPath: "/var/run/docker.sock" });
11
+ }
12
+ export function formatError(error) {
13
+ if (error instanceof Error)
14
+ return error.message;
15
+ if (typeof error === "string")
16
+ return error;
17
+ return String(error);
18
+ }
19
+ export function formatContainer(container) {
20
+ return {
21
+ id: container.Id.substring(0, 12),
22
+ name: container.Names[0]?.replace(/^\//, ""),
23
+ image: container.Image,
24
+ state: container.State,
25
+ status: container.Status,
26
+ created: new Date(container.Created * 1000).toISOString(),
27
+ ports: container.Ports.map((p) => ({
28
+ private: p.PrivatePort,
29
+ public: p.PublicPort,
30
+ type: p.Type,
31
+ })),
32
+ labels: container.Labels,
33
+ mounts: container.Mounts.map((m) => ({
34
+ type: m.Type,
35
+ source: m.Source,
36
+ destination: m.Destination,
37
+ mode: m.Mode,
38
+ rw: m.RW,
39
+ })),
40
+ };
41
+ }
42
+ export function formatImage(image) {
43
+ return {
44
+ id: image.Id.substring(0, 19),
45
+ tags: image.RepoTags || ["<none>:<none>"],
46
+ size: image.Size,
47
+ created: new Date(image.Created).toISOString(),
48
+ };
49
+ }
50
+ export function formatBytes(bytes) {
51
+ if (bytes === 0)
52
+ return "0 B";
53
+ const k = 1024;
54
+ const sizes = ["B", "KB", "MB", "GB", "TB"];
55
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
56
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
57
+ }
58
+ //# sourceMappingURL=docker.js.map
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
package/dist/index.js ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
+ import { createDockerClient } from "./docker.js";
4
+ import { createServer } from "./server.js";
5
+ async function main() {
6
+ const docker = createDockerClient();
7
+ const server = createServer(docker);
8
+ const transport = new StdioServerTransport();
9
+ await server.connect(transport);
10
+ process.stderr.write("Docker MCP Server running on stdio\n");
11
+ }
12
+ main().catch((error) => {
13
+ process.stderr.write(`Fatal error: ${error}\n`);
14
+ process.exit(1);
15
+ });
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,4 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import Dockerode from "dockerode";
3
+ export declare function createServer(docker: Dockerode): McpServer;
4
+ //# sourceMappingURL=server.d.ts.map
package/dist/server.js ADDED
@@ -0,0 +1,24 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { registerContainerTools } from "./tools/container.js";
3
+ import { registerImageTools } from "./tools/image.js";
4
+ import { registerComposeTools } from "./tools/compose.js";
5
+ import { registerHealthTools } from "./tools/health.js";
6
+ import { registerLogsTools } from "./tools/logs.js";
7
+ import { registerExecTools } from "./tools/exec.js";
8
+ import { registerNetworkTools } from "./tools/network.js";
9
+ export function createServer(docker) {
10
+ const server = new McpServer({
11
+ name: "docker-mcp-server",
12
+ version: "0.1.0",
13
+ });
14
+ // Register all tool categories
15
+ registerContainerTools(server, docker);
16
+ registerImageTools(server, docker);
17
+ registerComposeTools(server);
18
+ registerHealthTools(server, docker);
19
+ registerLogsTools(server, docker);
20
+ registerExecTools(server, docker);
21
+ registerNetworkTools(server, docker);
22
+ return server;
23
+ }
24
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1,3 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerComposeTools(server: McpServer): void;
3
+ //# sourceMappingURL=compose.d.ts.map
@@ -0,0 +1,95 @@
1
+ import { execSync, exec as execCb } from "child_process";
2
+ import { promisify } from "util";
3
+ import { ComposeUpSchema, ComposeDownSchema, ComposePsSchema, ComposeLogsSchema, ComposeRestartSchema, } from "../types.js";
4
+ import { formatError } from "../docker.js";
5
+ const execAsync = promisify(execCb);
6
+ function runCompose(path, args) {
7
+ try {
8
+ const result = execSync(`docker compose -f ${path} ${args.join(" ")}`, {
9
+ encoding: "utf-8",
10
+ timeout: 30000,
11
+ });
12
+ return result.trim();
13
+ }
14
+ catch (error) {
15
+ const err = error;
16
+ throw new Error(err.stderr || err.stdout || formatError(error));
17
+ }
18
+ }
19
+ export function registerComposeTools(server) {
20
+ server.tool("compose_up", "Bring up Docker Compose services from a docker-compose.yml file. Optionally build images first.", ComposeUpSchema.shape, async (params) => {
21
+ try {
22
+ const args = ["up", "-d"];
23
+ if (params.build)
24
+ args.push("--build");
25
+ if (params.services?.length)
26
+ args.push(...params.services);
27
+ const output = runCompose(params.path, args);
28
+ return { content: [{ type: "text", text: output || "Compose services started." }] };
29
+ }
30
+ catch (error) {
31
+ return { content: [{ type: "text", text: `Error: ${formatError(error)}` }], isError: true };
32
+ }
33
+ });
34
+ server.tool("compose_down", "Tear down Docker Compose services. Optionally remove named volumes.", ComposeDownSchema.shape, async (params) => {
35
+ try {
36
+ const args = ["down"];
37
+ if (params.volumes)
38
+ args.push("-v");
39
+ if (params.timeout)
40
+ args.push(`--timeout ${params.timeout}`);
41
+ const output = runCompose(params.path, args);
42
+ return { content: [{ type: "text", text: output || "Compose services stopped." }] };
43
+ }
44
+ catch (error) {
45
+ return { content: [{ type: "text", text: `Error: ${formatError(error)}` }], isError: true };
46
+ }
47
+ });
48
+ server.tool("compose_ps", "List service states across a Docker Compose stack.", ComposePsSchema.shape, async (params) => {
49
+ try {
50
+ const output = runCompose(params.path, ["ps", "--format", "json"]);
51
+ const lines = output.split("\n").filter(Boolean);
52
+ const services = lines.map((line) => {
53
+ try {
54
+ return JSON.parse(line);
55
+ }
56
+ catch {
57
+ return { raw: line };
58
+ }
59
+ });
60
+ return { content: [{ type: "text", text: JSON.stringify(services, null, 2) }] };
61
+ }
62
+ catch (error) {
63
+ return { content: [{ type: "text", text: `Error: ${formatError(error)}` }], isError: true };
64
+ }
65
+ });
66
+ server.tool("compose_logs", "Tail logs from Docker Compose services. Supports filtering by service and line count.", ComposeLogsSchema.shape, async (params) => {
67
+ try {
68
+ const args = ["logs", "--tail", String(params.tail ?? 100)];
69
+ if (params.follow)
70
+ args.push("-f");
71
+ if (params.services?.length)
72
+ args.push(...params.services);
73
+ const output = runCompose(params.path, args);
74
+ return { content: [{ type: "text", text: output || "No logs found." }] };
75
+ }
76
+ catch (error) {
77
+ return { content: [{ type: "text", text: `Error: ${formatError(error)}` }], isError: true };
78
+ }
79
+ });
80
+ server.tool("compose_restart", "Restart Docker Compose services. Restart specific services or the entire stack.", ComposeRestartSchema.shape, async (params) => {
81
+ try {
82
+ const args = ["restart"];
83
+ if (params.timeout)
84
+ args.push(`--timeout ${params.timeout}`);
85
+ if (params.services?.length)
86
+ args.push(...params.services);
87
+ const output = runCompose(params.path, args);
88
+ return { content: [{ type: "text", text: output || "Compose services restarted." }] };
89
+ }
90
+ catch (error) {
91
+ return { content: [{ type: "text", text: `Error: ${formatError(error)}` }], isError: true };
92
+ }
93
+ });
94
+ }
95
+ //# sourceMappingURL=compose.js.map
@@ -0,0 +1,4 @@
1
+ import Dockerode from "dockerode";
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ export declare function registerContainerTools(server: McpServer, docker: Dockerode): void;
4
+ //# sourceMappingURL=container.d.ts.map
@@ -0,0 +1,140 @@
1
+ import { ListContainersSchema, InspectContainerSchema, StartContainerSchema, StopContainerSchema, RestartContainerSchema, RemoveContainerSchema, RecreateContainerSchema, RunContainerSchema, } from "../types.js";
2
+ import { formatContainer, formatError } from "../docker.js";
3
+ export function registerContainerTools(server, docker) {
4
+ server.tool("list_containers", "List Docker containers with optional filters (state, label, name). Returns container IDs, names, images, states, ports, and labels.", ListContainersSchema.shape, async (params) => {
5
+ try {
6
+ const containers = await docker.listContainers({
7
+ all: params.all ?? false,
8
+ filters: JSON.stringify({
9
+ ...(params.label ? { label: params.label } : {}),
10
+ ...(params.name ? { name: [`/${params.name}`] } : {}),
11
+ ...(params.state ? { status: [params.state] } : {}),
12
+ }),
13
+ });
14
+ const results = containers.map(formatContainer);
15
+ return { content: [{ type: "text", text: JSON.stringify(results, null, 2) }] };
16
+ }
17
+ catch (error) {
18
+ return { content: [{ type: "text", text: `Error: ${formatError(error)}` }], isError: true };
19
+ }
20
+ });
21
+ server.tool("inspect_container", "Get detailed configuration and state of a Docker container by ID or name.", InspectContainerSchema.shape, async (params) => {
22
+ try {
23
+ const container = docker.getContainer(params.container_id);
24
+ const info = await container.inspect();
25
+ return { content: [{ type: "text", text: JSON.stringify(info, null, 2) }] };
26
+ }
27
+ catch (error) {
28
+ return { content: [{ type: "text", text: `Error: ${formatError(error)}` }], isError: true };
29
+ }
30
+ });
31
+ server.tool("start_container", "Start a stopped Docker container by ID or name.", StartContainerSchema.shape, async (params) => {
32
+ try {
33
+ const container = docker.getContainer(params.container_id);
34
+ await container.start();
35
+ return { content: [{ type: "text", text: `Container ${params.container_id} started.` }] };
36
+ }
37
+ catch (error) {
38
+ return { content: [{ type: "text", text: `Error: ${formatError(error)}` }], isError: true };
39
+ }
40
+ });
41
+ server.tool("stop_container", "Stop a running Docker container by ID or name with optional timeout.", StopContainerSchema.shape, async (params) => {
42
+ try {
43
+ const container = docker.getContainer(params.container_id);
44
+ await container.stop({ t: params.timeout ?? 10 });
45
+ return { content: [{ type: "text", text: `Container ${params.container_id} stopped.` }] };
46
+ }
47
+ catch (error) {
48
+ return { content: [{ type: "text", text: `Error: ${formatError(error)}` }], isError: true };
49
+ }
50
+ });
51
+ server.tool("restart_container", "Restart a Docker container by ID or name with optional timeout.", RestartContainerSchema.shape, async (params) => {
52
+ try {
53
+ const container = docker.getContainer(params.container_id);
54
+ await container.restart({ t: params.timeout ?? 10 });
55
+ return { content: [{ type: "text", text: `Container ${params.container_id} restarted.` }] };
56
+ }
57
+ catch (error) {
58
+ return { content: [{ type: "text", text: `Error: ${formatError(error)}` }], isError: true };
59
+ }
60
+ });
61
+ server.tool("remove_container", "Remove a Docker container by ID or name. Use force to remove running containers.", RemoveContainerSchema.shape, async (params) => {
62
+ try {
63
+ const container = docker.getContainer(params.container_id);
64
+ await container.remove({ force: params.force ?? false });
65
+ return { content: [{ type: "text", text: `Container ${params.container_id} removed.` }] };
66
+ }
67
+ catch (error) {
68
+ return { content: [{ type: "text", text: `Error: ${formatError(error)}` }], isError: true };
69
+ }
70
+ });
71
+ server.tool("recreate_container", "Recreate a container with the same configuration (stop, remove, re-create). Useful for applying config changes.", RecreateContainerSchema.shape, async (params) => {
72
+ try {
73
+ const container = docker.getContainer(params.container_id);
74
+ const info = await container.inspect();
75
+ // Stop and remove
76
+ try {
77
+ await container.stop({ t: params.timeout ?? 10 });
78
+ }
79
+ catch { /* already stopped */ }
80
+ await container.remove({ force: true });
81
+ // Re-create with same config
82
+ const createOpts = {
83
+ name: info.Name.replace(/^\//, ""),
84
+ Image: info.Config.Image,
85
+ Env: info.Config.Env,
86
+ Cmd: info.Config.Cmd,
87
+ WorkingDir: info.Config.WorkingDir,
88
+ Labels: info.Config.Labels || {},
89
+ HostConfig: {
90
+ Binds: info.HostConfig?.Binds,
91
+ PortBindings: info.HostConfig?.PortBindings,
92
+ RestartPolicy: info.HostConfig?.RestartPolicy,
93
+ NetworkMode: info.HostConfig?.NetworkMode,
94
+ },
95
+ };
96
+ const newContainer = await docker.createContainer(createOpts);
97
+ await newContainer.start();
98
+ return {
99
+ content: [{ type: "text", text: `Container recreated and started. New ID: ${newContainer.id.substring(0, 12)}` }],
100
+ };
101
+ }
102
+ catch (error) {
103
+ return { content: [{ type: "text", text: `Error: ${formatError(error)}` }], isError: true };
104
+ }
105
+ });
106
+ server.tool("run_container", "Create and start a new Docker container with one command. Supports image, env, ports, volumes, restart policy, and command override.", RunContainerSchema.shape, async (params) => {
107
+ try {
108
+ const createOpts = {
109
+ Image: params.image,
110
+ name: params.name,
111
+ Env: params.env ? Object.entries(params.env).map(([k, v]) => `${k}=${v}`) : undefined,
112
+ Cmd: params.command,
113
+ ExposedPorts: params.ports
114
+ ? Object.fromEntries(Object.keys(params.ports).map((k) => [k, {}]))
115
+ : undefined,
116
+ HostConfig: {
117
+ PortBindings: params.ports
118
+ ? Object.fromEntries(Object.entries(params.ports).map(([containerPort, hostBinding]) => [
119
+ containerPort,
120
+ [{ HostPort: hostBinding }],
121
+ ]))
122
+ : undefined,
123
+ Binds: params.volumes,
124
+ RestartPolicy: params.restart_policy
125
+ ? { Name: params.restart_policy, MaximumRetryCount: 0 }
126
+ : undefined,
127
+ },
128
+ };
129
+ const container = await docker.createContainer(createOpts);
130
+ await container.start();
131
+ return {
132
+ content: [{ type: "text", text: `Container created and started. ID: ${container.id.substring(0, 12)}` }],
133
+ };
134
+ }
135
+ catch (error) {
136
+ return { content: [{ type: "text", text: `Error: ${formatError(error)}` }], isError: true };
137
+ }
138
+ });
139
+ }
140
+ //# sourceMappingURL=container.js.map
@@ -0,0 +1,4 @@
1
+ import Dockerode from "dockerode";
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ export declare function registerExecTools(server: McpServer, docker: Dockerode): void;
4
+ //# sourceMappingURL=exec.d.ts.map
@@ -0,0 +1,39 @@
1
+ import { ExecInContainerSchema } from "../types.js";
2
+ import { formatError } from "../docker.js";
3
+ export function registerExecTools(server, docker) {
4
+ server.tool("exec_in_container", "Execute a command inside a running Docker container. Returns stdout, stderr, and exit code.", ExecInContainerSchema.shape, async (params) => {
5
+ try {
6
+ const container = docker.getContainer(params.container_id);
7
+ const exec = await container.exec({
8
+ Cmd: params.command,
9
+ AttachStdout: true,
10
+ AttachStderr: true,
11
+ WorkingDir: params.working_dir,
12
+ Env: params.env ? Object.entries(params.env).map(([k, v]) => `${k}=${v}`) : undefined,
13
+ });
14
+ const stream = await exec.start({});
15
+ const output = await new Promise((resolve) => {
16
+ let data = "";
17
+ stream.on("data", (chunk) => { data += chunk.toString(); });
18
+ stream.on("end", () => resolve(data));
19
+ });
20
+ const inspect = await exec.inspect();
21
+ const cleanOutput = output.replace(/^[\x00-\x0f]{8}/gm, "");
22
+ return {
23
+ content: [{
24
+ type: "text",
25
+ text: JSON.stringify({
26
+ exitCode: inspect.ExitCode,
27
+ stdout: cleanOutput,
28
+ stderr: "",
29
+ }, null, 2),
30
+ }],
31
+ isError: inspect.ExitCode !== 0,
32
+ };
33
+ }
34
+ catch (error) {
35
+ return { content: [{ type: "text", text: `Error: ${formatError(error)}` }], isError: true };
36
+ }
37
+ });
38
+ }
39
+ //# sourceMappingURL=exec.js.map
@@ -0,0 +1,4 @@
1
+ import Dockerode from "dockerode";
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ export declare function registerHealthTools(server: McpServer, docker: Dockerode): void;
4
+ //# sourceMappingURL=health.d.ts.map