mcp-docker-server 1.0.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 Ofer Shapira
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,124 @@
1
+ # mcp-server-docker
2
+
3
+ [![npm version](https://img.shields.io/npm/v/mcp-docker-server.svg)](https://www.npmjs.com/package/mcp-docker-server)
4
+ [![npm downloads](https://img.shields.io/npm/dm/mcp-docker-server.svg)](https://www.npmjs.com/package/mcp-docker-server)
5
+ [![CI](https://github.com/ofershap/mcp-server-docker/actions/workflows/ci.yml/badge.svg)](https://github.com/ofershap/mcp-server-docker/actions/workflows/ci.yml)
6
+ [![TypeScript](https://img.shields.io/badge/TypeScript-strict-blue.svg)](https://www.typescriptlang.org/)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
+
9
+ Control Docker containers, images, and services from your AI coding assistant. List, start, stop, read logs, run commands inside containers, and check resource usage.
10
+
11
+ ```bash
12
+ npx mcp-docker-server
13
+ ```
14
+
15
+ > Compatible with Claude Desktop, Cursor, VS Code Copilot, and any MCP-compatible client. No API keys needed. Connects to your local Docker socket automatically.
16
+
17
+ ![MCP server for Docker containers, logs, and resource monitoring](assets/demo.gif)
18
+
19
+ <sub>Demo built with <a href="https://github.com/ofershap/remotion-readme-kit">remotion-readme-kit</a></sub>
20
+
21
+ ## Why
22
+
23
+ If you work with Docker daily, you know the routine: switch to a terminal, type `docker ps`, scroll through logs, copy container IDs, restart services. It adds up. This MCP server lets your AI assistant handle those tasks for you while you stay focused on code. Ask it to check which containers are running, pull up logs from a failing service, or restart something that got stuck. It talks to Docker's API through the local socket, so there's nothing to configure and no credentials to manage.
24
+
25
+ ## Tools
26
+
27
+ | Tool | What it does |
28
+ | ------------------- | ------------------------------------------------------------------- |
29
+ | `list_containers` | List running (or all) containers with status, ports, and image info |
30
+ | `container_logs` | Get recent logs from a container |
31
+ | `start_container` | Start a stopped container |
32
+ | `stop_container` | Stop a running container |
33
+ | `restart_container` | Restart a container |
34
+ | `remove_container` | Remove a container (with optional force) |
35
+ | `exec_command` | Execute a command inside a running container |
36
+ | `container_stats` | Get live CPU, memory, and network stats |
37
+ | `list_images` | List all Docker images on the host |
38
+ | `remove_image` | Remove a Docker image |
39
+
40
+ ## Quick Start
41
+
42
+ ### Cursor
43
+
44
+ Add to `.cursor/mcp.json`:
45
+
46
+ ```json
47
+ {
48
+ "mcpServers": {
49
+ "docker": {
50
+ "command": "npx",
51
+ "args": ["-y", "mcp-docker-server"]
52
+ }
53
+ }
54
+ }
55
+ ```
56
+
57
+ ### Claude Desktop
58
+
59
+ Add to `claude_desktop_config.json`:
60
+
61
+ ```json
62
+ {
63
+ "mcpServers": {
64
+ "docker": {
65
+ "command": "npx",
66
+ "args": ["-y", "mcp-docker-server"]
67
+ }
68
+ }
69
+ }
70
+ ```
71
+
72
+ ### VS Code
73
+
74
+ Add to user settings or `.vscode/mcp.json`:
75
+
76
+ ```json
77
+ {
78
+ "mcp": {
79
+ "servers": {
80
+ "docker": {
81
+ "command": "npx",
82
+ "args": ["-y", "mcp-docker-server"]
83
+ }
84
+ }
85
+ }
86
+ }
87
+ ```
88
+
89
+ ## Examples
90
+
91
+ - "List all running Docker containers"
92
+ - "Show me the logs from the nginx container"
93
+ - "Restart the api-server container"
94
+ - "What's the CPU and memory usage of my postgres container?"
95
+ - "Execute `ls -la /app` inside the web container"
96
+ - "List all Docker images and their sizes"
97
+ - "Stop all containers that are using the old image"
98
+
99
+ ## Prerequisites
100
+
101
+ - **Docker** must be running on your machine
102
+ - The server connects to the Docker socket at `/var/run/docker.sock` (Linux/macOS) or the named pipe on Windows
103
+ - No API keys or tokens required
104
+
105
+ ## Development
106
+
107
+ ```bash
108
+ git clone https://github.com/ofershap/mcp-server-docker.git
109
+ cd mcp-server-docker
110
+ npm install
111
+ npm test
112
+ npm run build
113
+ ```
114
+
115
+ ## Author
116
+
117
+ **Ofer Shapira**
118
+
119
+ [![LinkedIn](https://img.shields.io/badge/LinkedIn-ofershap-blue?logo=linkedin)](https://linkedin.com/in/ofershap)
120
+ [![GitHub](https://img.shields.io/badge/GitHub-ofershap-black?logo=github)](https://github.com/ofershap)
121
+
122
+ ## License
123
+
124
+ MIT © 2026 Ofer Shapira
package/dist/index.js ADDED
@@ -0,0 +1,273 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
+ import { z } from "zod";
7
+
8
+ // src/docker.ts
9
+ import Dockerode from "dockerode";
10
+ var docker = new Dockerode();
11
+ function formatBytes(bytes) {
12
+ if (bytes === 0) return "0 B";
13
+ const k = 1024;
14
+ const sizes = ["B", "KB", "MB", "GB"];
15
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
16
+ return `${(bytes / Math.pow(k, i)).toFixed(1)} ${sizes[i]}`;
17
+ }
18
+ function formatPorts(ports) {
19
+ return ports.map((p) => {
20
+ if (p.PublicPort) return `${p.PublicPort}->${p.PrivatePort}/${p.Type}`;
21
+ return `${p.PrivatePort}/${p.Type}`;
22
+ }).join(", ");
23
+ }
24
+ async function listContainers(all) {
25
+ const containers = await docker.listContainers({ all });
26
+ return containers.map((c) => ({
27
+ id: c.Id.slice(0, 12),
28
+ name: c.Names[0]?.replace(/^\//, "") ?? "",
29
+ image: c.Image,
30
+ state: c.State,
31
+ status: c.Status,
32
+ ports: formatPorts(c.Ports),
33
+ created: new Date(c.Created * 1e3).toISOString()
34
+ }));
35
+ }
36
+ async function containerLogs(id, tail) {
37
+ const container = docker.getContainer(id);
38
+ const logs = await container.logs({
39
+ stdout: true,
40
+ stderr: true,
41
+ tail,
42
+ follow: false
43
+ });
44
+ return logs.toString("utf-8").replace(/[\x00-\x08]/g, "");
45
+ }
46
+ async function startContainer(id) {
47
+ const container = docker.getContainer(id);
48
+ await container.start();
49
+ return `Container ${id} started`;
50
+ }
51
+ async function stopContainer(id) {
52
+ const container = docker.getContainer(id);
53
+ await container.stop();
54
+ return `Container ${id} stopped`;
55
+ }
56
+ async function restartContainer(id) {
57
+ const container = docker.getContainer(id);
58
+ await container.restart();
59
+ return `Container ${id} restarted`;
60
+ }
61
+ async function removeContainer(id, force) {
62
+ const container = docker.getContainer(id);
63
+ await container.remove({ force });
64
+ return `Container ${id} removed`;
65
+ }
66
+ async function execCommand(id, command) {
67
+ const container = docker.getContainer(id);
68
+ const exec = await container.exec({
69
+ Cmd: command,
70
+ AttachStdout: true,
71
+ AttachStderr: true
72
+ });
73
+ const stream = await exec.start({ hijack: true, stdin: false });
74
+ return new Promise((resolve) => {
75
+ const chunks = [];
76
+ stream.on("data", (chunk) => chunks.push(chunk));
77
+ stream.on("end", () => {
78
+ resolve(
79
+ Buffer.concat(chunks).toString("utf-8").replace(/[\x00-\x08]/g, "")
80
+ // eslint-disable-line no-control-regex
81
+ );
82
+ });
83
+ });
84
+ }
85
+ async function containerStats(id) {
86
+ const container = docker.getContainer(id);
87
+ const stats = await container.stats({
88
+ stream: false
89
+ });
90
+ const cpuStats = stats.cpu_stats;
91
+ const precpuStats = stats.precpu_stats;
92
+ const cpuDelta = cpuStats.cpu_usage.total_usage - precpuStats.cpu_usage.total_usage;
93
+ const systemDelta = cpuStats.system_cpu_usage - precpuStats.system_cpu_usage;
94
+ const cpuCount = cpuStats.cpu_usage.percpu_usage?.length ?? 1;
95
+ const cpuPercent = systemDelta > 0 ? (cpuDelta / systemDelta * cpuCount * 100).toFixed(2) : "0.00";
96
+ const memUsage = stats.memory_stats.usage ?? 0;
97
+ const memLimit = stats.memory_stats.limit ?? 0;
98
+ const memPercent = memLimit > 0 ? (memUsage / memLimit * 100).toFixed(2) : "0.00";
99
+ const networks = stats.networks ?? {};
100
+ let rxBytes = 0;
101
+ let txBytes = 0;
102
+ for (const iface of Object.values(networks)) {
103
+ rxBytes += iface.rx_bytes ?? 0;
104
+ txBytes += iface.tx_bytes ?? 0;
105
+ }
106
+ return {
107
+ cpu_percent: `${cpuPercent}%`,
108
+ memory_usage: formatBytes(memUsage),
109
+ memory_limit: formatBytes(memLimit),
110
+ memory_percent: `${memPercent}%`,
111
+ network_rx: formatBytes(rxBytes),
112
+ network_tx: formatBytes(txBytes)
113
+ };
114
+ }
115
+ async function listImages() {
116
+ const images = await docker.listImages();
117
+ return images.map((img) => ({
118
+ id: img.Id.replace("sha256:", "").slice(0, 12),
119
+ tags: img.RepoTags ?? ["<none>"],
120
+ size: formatBytes(img.Size),
121
+ created: new Date(img.Created * 1e3).toISOString()
122
+ }));
123
+ }
124
+ async function removeImage(id, force) {
125
+ const image = docker.getImage(id);
126
+ await image.remove({ force });
127
+ return `Image ${id} removed`;
128
+ }
129
+
130
+ // src/index.ts
131
+ var server = new McpServer({
132
+ name: "mcp-server-docker",
133
+ version: "1.0.0"
134
+ });
135
+ server.tool(
136
+ "list_containers",
137
+ "List Docker containers. Set all=true to include stopped containers.",
138
+ {
139
+ all: z.boolean().optional().default(false).describe("Include stopped containers")
140
+ },
141
+ async ({ all }) => {
142
+ const containers = await listContainers(all);
143
+ return {
144
+ content: [
145
+ {
146
+ type: "text",
147
+ text: containers.length === 0 ? "No containers found." : containers.map(
148
+ (c) => `${c.id} ${c.name.padEnd(30)} ${c.image.padEnd(30)} ${c.state.padEnd(10)} ${c.status}`
149
+ ).join("\n")
150
+ }
151
+ ]
152
+ };
153
+ }
154
+ );
155
+ server.tool(
156
+ "container_logs",
157
+ "Get logs from a Docker container.",
158
+ {
159
+ id: z.string().describe("Container ID or name"),
160
+ tail: z.number().optional().default(100).describe("Number of lines from the end")
161
+ },
162
+ async ({ id, tail }) => {
163
+ const logs = await containerLogs(id, tail);
164
+ return { content: [{ type: "text", text: logs || "(no logs)" }] };
165
+ }
166
+ );
167
+ server.tool(
168
+ "start_container",
169
+ "Start a stopped Docker container.",
170
+ { id: z.string().describe("Container ID or name") },
171
+ async ({ id }) => {
172
+ const result = await startContainer(id);
173
+ return { content: [{ type: "text", text: result }] };
174
+ }
175
+ );
176
+ server.tool(
177
+ "stop_container",
178
+ "Stop a running Docker container.",
179
+ { id: z.string().describe("Container ID or name") },
180
+ async ({ id }) => {
181
+ const result = await stopContainer(id);
182
+ return { content: [{ type: "text", text: result }] };
183
+ }
184
+ );
185
+ server.tool(
186
+ "restart_container",
187
+ "Restart a Docker container.",
188
+ { id: z.string().describe("Container ID or name") },
189
+ async ({ id }) => {
190
+ const result = await restartContainer(id);
191
+ return { content: [{ type: "text", text: result }] };
192
+ }
193
+ );
194
+ server.tool(
195
+ "remove_container",
196
+ "Remove a Docker container. Use force=true to remove running containers.",
197
+ {
198
+ id: z.string().describe("Container ID or name"),
199
+ force: z.boolean().optional().default(false).describe("Force remove running container")
200
+ },
201
+ async ({ id, force }) => {
202
+ const result = await removeContainer(id, force);
203
+ return { content: [{ type: "text", text: result }] };
204
+ }
205
+ );
206
+ server.tool(
207
+ "exec_command",
208
+ "Execute a command inside a running Docker container.",
209
+ {
210
+ id: z.string().describe("Container ID or name"),
211
+ command: z.array(z.string()).describe("Command and arguments, e.g. ['ls', '-la']")
212
+ },
213
+ async ({ id, command }) => {
214
+ const output = await execCommand(id, command);
215
+ return {
216
+ content: [{ type: "text", text: output || "(no output)" }]
217
+ };
218
+ }
219
+ );
220
+ server.tool(
221
+ "container_stats",
222
+ "Get CPU, memory, and network stats for a running Docker container.",
223
+ { id: z.string().describe("Container ID or name") },
224
+ async ({ id }) => {
225
+ const stats = await containerStats(id);
226
+ return {
227
+ content: [
228
+ {
229
+ type: "text",
230
+ text: [
231
+ `CPU: ${stats.cpu_percent}`,
232
+ `Memory: ${stats.memory_usage} / ${stats.memory_limit} (${stats.memory_percent})`,
233
+ `Network: \u2193 ${stats.network_rx} \u2191 ${stats.network_tx}`
234
+ ].join("\n")
235
+ }
236
+ ]
237
+ };
238
+ }
239
+ );
240
+ server.tool("list_images", "List Docker images on the host.", {}, async () => {
241
+ const images = await listImages();
242
+ return {
243
+ content: [
244
+ {
245
+ type: "text",
246
+ text: images.length === 0 ? "No images found." : images.map(
247
+ (img) => `${img.id} ${img.tags.join(", ").padEnd(40)} ${img.size.padEnd(10)} ${img.created}`
248
+ ).join("\n")
249
+ }
250
+ ]
251
+ };
252
+ });
253
+ server.tool(
254
+ "remove_image",
255
+ "Remove a Docker image. Use force=true to force removal.",
256
+ {
257
+ id: z.string().describe("Image ID or tag"),
258
+ force: z.boolean().optional().default(false).describe("Force remove")
259
+ },
260
+ async ({ id, force }) => {
261
+ const result = await removeImage(id, force);
262
+ return { content: [{ type: "text", text: result }] };
263
+ }
264
+ );
265
+ async function main() {
266
+ const transport = new StdioServerTransport();
267
+ await server.connect(transport);
268
+ }
269
+ main().catch((error) => {
270
+ console.error("Fatal error:", error);
271
+ process.exit(1);
272
+ });
273
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/docker.ts"],"sourcesContent":["import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { z } from \"zod\";\nimport {\n listContainers,\n containerLogs,\n startContainer,\n stopContainer,\n restartContainer,\n removeContainer,\n execCommand,\n containerStats,\n listImages,\n removeImage,\n} from \"./docker.js\";\n\nconst server = new McpServer({\n name: \"mcp-server-docker\",\n version: \"1.0.0\",\n});\n\nserver.tool(\n \"list_containers\",\n \"List Docker containers. Set all=true to include stopped containers.\",\n {\n all: z\n .boolean()\n .optional()\n .default(false)\n .describe(\"Include stopped containers\"),\n },\n async ({ all }) => {\n const containers = await listContainers(all);\n return {\n content: [\n {\n type: \"text\",\n text:\n containers.length === 0\n ? \"No containers found.\"\n : containers\n .map(\n (c) =>\n `${c.id} ${c.name.padEnd(30)} ${c.image.padEnd(30)} ${c.state.padEnd(10)} ${c.status}`,\n )\n .join(\"\\n\"),\n },\n ],\n };\n },\n);\n\nserver.tool(\n \"container_logs\",\n \"Get logs from a Docker container.\",\n {\n id: z.string().describe(\"Container ID or name\"),\n tail: z\n .number()\n .optional()\n .default(100)\n .describe(\"Number of lines from the end\"),\n },\n async ({ id, tail }) => {\n const logs = await containerLogs(id, tail);\n return { content: [{ type: \"text\", text: logs || \"(no logs)\" }] };\n },\n);\n\nserver.tool(\n \"start_container\",\n \"Start a stopped Docker container.\",\n { id: z.string().describe(\"Container ID or name\") },\n async ({ id }) => {\n const result = await startContainer(id);\n return { content: [{ type: \"text\", text: result }] };\n },\n);\n\nserver.tool(\n \"stop_container\",\n \"Stop a running Docker container.\",\n { id: z.string().describe(\"Container ID or name\") },\n async ({ id }) => {\n const result = await stopContainer(id);\n return { content: [{ type: \"text\", text: result }] };\n },\n);\n\nserver.tool(\n \"restart_container\",\n \"Restart a Docker container.\",\n { id: z.string().describe(\"Container ID or name\") },\n async ({ id }) => {\n const result = await restartContainer(id);\n return { content: [{ type: \"text\", text: result }] };\n },\n);\n\nserver.tool(\n \"remove_container\",\n \"Remove a Docker container. Use force=true to remove running containers.\",\n {\n id: z.string().describe(\"Container ID or name\"),\n force: z\n .boolean()\n .optional()\n .default(false)\n .describe(\"Force remove running container\"),\n },\n async ({ id, force }) => {\n const result = await removeContainer(id, force);\n return { content: [{ type: \"text\", text: result }] };\n },\n);\n\nserver.tool(\n \"exec_command\",\n \"Execute a command inside a running Docker container.\",\n {\n id: z.string().describe(\"Container ID or name\"),\n command: z\n .array(z.string())\n .describe(\"Command and arguments, e.g. ['ls', '-la']\"),\n },\n async ({ id, command }) => {\n const output = await execCommand(id, command);\n return {\n content: [{ type: \"text\", text: output || \"(no output)\" }],\n };\n },\n);\n\nserver.tool(\n \"container_stats\",\n \"Get CPU, memory, and network stats for a running Docker container.\",\n { id: z.string().describe(\"Container ID or name\") },\n async ({ id }) => {\n const stats = await containerStats(id);\n return {\n content: [\n {\n type: \"text\",\n text: [\n `CPU: ${stats.cpu_percent}`,\n `Memory: ${stats.memory_usage} / ${stats.memory_limit} (${stats.memory_percent})`,\n `Network: ↓ ${stats.network_rx} ↑ ${stats.network_tx}`,\n ].join(\"\\n\"),\n },\n ],\n };\n },\n);\n\nserver.tool(\"list_images\", \"List Docker images on the host.\", {}, async () => {\n const images = await listImages();\n return {\n content: [\n {\n type: \"text\",\n text:\n images.length === 0\n ? \"No images found.\"\n : images\n .map(\n (img) =>\n `${img.id} ${img.tags.join(\", \").padEnd(40)} ${img.size.padEnd(10)} ${img.created}`,\n )\n .join(\"\\n\"),\n },\n ],\n };\n});\n\nserver.tool(\n \"remove_image\",\n \"Remove a Docker image. Use force=true to force removal.\",\n {\n id: z.string().describe(\"Image ID or tag\"),\n force: z.boolean().optional().default(false).describe(\"Force remove\"),\n },\n async ({ id, force }) => {\n const result = await removeImage(id, force);\n return { content: [{ type: \"text\", text: result }] };\n },\n);\n\nasync function main() {\n const transport = new StdioServerTransport();\n await server.connect(transport);\n}\n\nmain().catch((error) => {\n console.error(\"Fatal error:\", error);\n process.exit(1);\n});\n","import Dockerode from \"dockerode\";\n\nconst docker = new Dockerode();\n\nexport interface ContainerInfo {\n id: string;\n name: string;\n image: string;\n state: string;\n status: string;\n ports: string;\n created: string;\n}\n\nexport interface ContainerStats {\n cpu_percent: string;\n memory_usage: string;\n memory_limit: string;\n memory_percent: string;\n network_rx: string;\n network_tx: string;\n}\n\nexport interface ImageInfo {\n id: string;\n tags: string[];\n size: string;\n created: string;\n}\n\nfunction formatBytes(bytes: number): string {\n if (bytes === 0) return \"0 B\";\n const k = 1024;\n const sizes = [\"B\", \"KB\", \"MB\", \"GB\"];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return `${(bytes / Math.pow(k, i)).toFixed(1)} ${sizes[i]}`;\n}\n\nfunction formatPorts(ports: Dockerode.Port[]): string {\n return ports\n .map((p) => {\n if (p.PublicPort) return `${p.PublicPort}->${p.PrivatePort}/${p.Type}`;\n return `${p.PrivatePort}/${p.Type}`;\n })\n .join(\", \");\n}\n\nexport async function listContainers(all: boolean): Promise<ContainerInfo[]> {\n const containers = await docker.listContainers({ all });\n return containers.map((c) => ({\n id: c.Id.slice(0, 12),\n name: c.Names[0]?.replace(/^\\//, \"\") ?? \"\",\n image: c.Image,\n state: c.State,\n status: c.Status,\n ports: formatPorts(c.Ports),\n created: new Date(c.Created * 1000).toISOString(),\n }));\n}\n\nexport async function containerLogs(id: string, tail: number): Promise<string> {\n const container = docker.getContainer(id);\n const logs = await container.logs({\n stdout: true,\n stderr: true,\n tail,\n follow: false,\n });\n // Strip Docker log control characters (eslint: intentional for Docker output)\n return logs.toString(\"utf-8\").replace(/[\\x00-\\x08]/g, \"\"); // eslint-disable-line no-control-regex\n}\n\nexport async function startContainer(id: string): Promise<string> {\n const container = docker.getContainer(id);\n await container.start();\n return `Container ${id} started`;\n}\n\nexport async function stopContainer(id: string): Promise<string> {\n const container = docker.getContainer(id);\n await container.stop();\n return `Container ${id} stopped`;\n}\n\nexport async function restartContainer(id: string): Promise<string> {\n const container = docker.getContainer(id);\n await container.restart();\n return `Container ${id} restarted`;\n}\n\nexport async function removeContainer(\n id: string,\n force: boolean,\n): Promise<string> {\n const container = docker.getContainer(id);\n await container.remove({ force });\n return `Container ${id} removed`;\n}\n\nexport async function execCommand(\n id: string,\n command: string[],\n): Promise<string> {\n const container = docker.getContainer(id);\n const exec = await container.exec({\n Cmd: command,\n AttachStdout: true,\n AttachStderr: true,\n });\n const stream = await exec.start({ hijack: true, stdin: false });\n return new Promise((resolve) => {\n const chunks: Buffer[] = [];\n stream.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n stream.on(\"end\", () => {\n resolve(\n Buffer.concat(chunks)\n .toString(\"utf-8\")\n .replace(/[\\x00-\\x08]/g, \"\"), // eslint-disable-line no-control-regex\n );\n });\n });\n}\n\ninterface CpuStats {\n cpu_usage: { total_usage: number; percpu_usage?: number[] };\n system_cpu_usage: number;\n}\n\ninterface MemoryStats {\n usage?: number;\n limit?: number;\n}\n\ntype NetworkStats = Record<string, { rx_bytes?: number; tx_bytes?: number }>;\n\nexport async function containerStats(id: string): Promise<ContainerStats> {\n const container = docker.getContainer(id);\n const stats = (await container.stats({\n stream: false,\n })) as {\n cpu_stats: CpuStats;\n precpu_stats: CpuStats;\n memory_stats: MemoryStats;\n networks?: NetworkStats;\n };\n\n const cpuStats = stats.cpu_stats;\n const precpuStats = stats.precpu_stats;\n const cpuDelta =\n cpuStats.cpu_usage.total_usage - precpuStats.cpu_usage.total_usage;\n const systemDelta = cpuStats.system_cpu_usage - precpuStats.system_cpu_usage;\n const cpuCount = cpuStats.cpu_usage.percpu_usage?.length ?? 1;\n const cpuPercent =\n systemDelta > 0\n ? ((cpuDelta / systemDelta) * cpuCount * 100).toFixed(2)\n : \"0.00\";\n\n const memUsage = stats.memory_stats.usage ?? 0;\n const memLimit = stats.memory_stats.limit ?? 0;\n const memPercent =\n memLimit > 0 ? ((memUsage / memLimit) * 100).toFixed(2) : \"0.00\";\n\n const networks = stats.networks ?? {};\n let rxBytes = 0;\n let txBytes = 0;\n for (const iface of Object.values(networks)) {\n rxBytes += iface.rx_bytes ?? 0;\n txBytes += iface.tx_bytes ?? 0;\n }\n\n return {\n cpu_percent: `${cpuPercent}%`,\n memory_usage: formatBytes(memUsage),\n memory_limit: formatBytes(memLimit),\n memory_percent: `${memPercent}%`,\n network_rx: formatBytes(rxBytes),\n network_tx: formatBytes(txBytes),\n };\n}\n\nexport async function listImages(): Promise<ImageInfo[]> {\n const images = await docker.listImages();\n return images.map((img) => ({\n id: img.Id.replace(\"sha256:\", \"\").slice(0, 12),\n tags: img.RepoTags ?? [\"<none>\"],\n size: formatBytes(img.Size),\n created: new Date(img.Created * 1000).toISOString(),\n }));\n}\n\nexport async function removeImage(id: string, force: boolean): Promise<string> {\n const image = docker.getImage(id);\n await image.remove({ force });\n return `Image ${id} removed`;\n}\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "mcp-docker-server",
3
+ "version": "1.0.0",
4
+ "description": "MCP server for Docker — manage containers, images, volumes, and compose services from your IDE",
5
+ "type": "module",
6
+ "bin": {
7
+ "mcp-docker-server": "./dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "scripts": {
13
+ "build": "tsup",
14
+ "typecheck": "tsc --noEmit",
15
+ "test": "vitest run",
16
+ "test:watch": "vitest",
17
+ "test:coverage": "vitest run --coverage",
18
+ "lint": "eslint . && prettier --check .",
19
+ "format": "prettier --write .",
20
+ "prepare": "husky"
21
+ },
22
+ "keywords": [
23
+ "mcp",
24
+ "mcp-server",
25
+ "model-context-protocol",
26
+ "docker",
27
+ "containers",
28
+ "devops",
29
+ "docker-compose",
30
+ "ai",
31
+ "llm",
32
+ "claude",
33
+ "cursor"
34
+ ],
35
+ "author": "Ofer Shapira",
36
+ "license": "MIT",
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "https://github.com/ofershap/mcp-server-docker.git"
40
+ },
41
+ "bugs": {
42
+ "url": "https://github.com/ofershap/mcp-server-docker/issues"
43
+ },
44
+ "homepage": "https://github.com/ofershap/mcp-server-docker#readme",
45
+ "dependencies": {
46
+ "@modelcontextprotocol/sdk": "^1.26.0",
47
+ "dockerode": "^4.0.0",
48
+ "zod": "^3.25.0"
49
+ },
50
+ "devDependencies": {
51
+ "@eslint/js": "^9.0.0",
52
+ "@types/dockerode": "^3.3.0",
53
+ "@types/node": "^22.0.0",
54
+ "eslint": "^9.0.0",
55
+ "eslint-config-prettier": "^10.0.0",
56
+ "husky": "^9.0.0",
57
+ "lint-staged": "^15.0.0",
58
+ "prettier": "^3.0.0",
59
+ "tsup": "^8.0.0",
60
+ "typescript": "^5.7.0",
61
+ "typescript-eslint": "^8.0.0",
62
+ "vitest": "^3.2.0"
63
+ },
64
+ "lint-staged": {
65
+ "*.{ts,tsx,js}": "eslint --fix",
66
+ "*.{json,md,yml,yaml}": "prettier --write"
67
+ }
68
+ }