docker_mcp 0.2.5 → 0.3.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.
- checksums.yaml +4 -4
- data/lib/docker_mcp/build_image.rb +65 -84
- data/lib/docker_mcp/copy_to_container.rb +87 -161
- data/lib/docker_mcp/create_container.rb +63 -115
- data/lib/docker_mcp/create_network.rb +63 -102
- data/lib/docker_mcp/create_volume.rb +57 -100
- data/lib/docker_mcp/exec_container.rb +94 -147
- data/lib/docker_mcp/fetch_container_logs.rb +64 -117
- data/lib/docker_mcp/list_containers.rb +44 -47
- data/lib/docker_mcp/list_images.rb +32 -56
- data/lib/docker_mcp/list_networks.rb +33 -60
- data/lib/docker_mcp/list_volumes.rb +33 -67
- data/lib/docker_mcp/pull_image.rb +24 -68
- data/lib/docker_mcp/push_image.rb +61 -118
- data/lib/docker_mcp/recreate_container.rb +48 -99
- data/lib/docker_mcp/remove_container.rb +49 -101
- data/lib/docker_mcp/remove_image.rb +53 -119
- data/lib/docker_mcp/remove_network.rb +42 -96
- data/lib/docker_mcp/remove_volume.rb +52 -106
- data/lib/docker_mcp/run_container.rb +72 -82
- data/lib/docker_mcp/start_container.rb +36 -76
- data/lib/docker_mcp/stop_container.rb +41 -86
- data/lib/docker_mcp/tag_image.rb +67 -121
- data/lib/docker_mcp/version.rb +1 -1
- data/lib/docker_mcp.rb +2 -0
- metadata +15 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0b93e5e574efb910b42bf12e0fe976966fadf8671644d39427fc3e0c9123ddd8
|
4
|
+
data.tar.gz: 693331e50cf5a4232f71e6a1d4eff6ad9df4c23d7c844140e1b25029384dc80f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bc6c784c575e29641377539be524b108bb6cf36535470eed017daf5e8df06b12254d62f91c62f6e471a9060fdfb0365e08d08b1252c2d7e05287bf82a93d981d
|
7
|
+
data.tar.gz: c968b9d93685216e023ccfd88fe95fffb93ba64d7305d671f0e3b8e970d4775cd66593dd582c8f4ea7ed4a2f4cd7cf6383a4d49441e7468ed89ffcc81c3e9b0d
|
@@ -1,104 +1,90 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module DockerMCP
|
4
|
-
# MCP tool for building Docker images
|
4
|
+
# MCP tool for building Docker images.
|
5
5
|
#
|
6
|
-
# This tool provides the ability to build Docker images
|
7
|
-
# content
|
8
|
-
#
|
6
|
+
# This tool provides the ability to build Docker images from Dockerfile
|
7
|
+
# content. It creates custom images by executing Dockerfile instructions
|
8
|
+
# and supports comprehensive build configuration including tagging and
|
9
|
+
# build arguments.
|
10
|
+
#
|
11
|
+
# == Features
|
12
|
+
#
|
13
|
+
# - Build images from Dockerfile content strings
|
14
|
+
# - Support for custom image tagging
|
15
|
+
# - Comprehensive build output and error reporting
|
16
|
+
# - Handles all standard Dockerfile instructions
|
17
|
+
# - Build context management
|
18
|
+
# - Progress tracking and logging
|
9
19
|
#
|
10
20
|
# == Security Considerations
|
11
21
|
#
|
12
|
-
#
|
13
|
-
# - Dockerfile commands execute
|
14
|
-
# -
|
15
|
-
# -
|
16
|
-
# -
|
17
|
-
# -
|
22
|
+
# Image building involves significant security risks:
|
23
|
+
# - **Code Execution**: Dockerfile RUN commands execute arbitrary code
|
24
|
+
# - **Network Access**: Build process can access networks and repositories
|
25
|
+
# - **File System Access**: Can read local files and directories
|
26
|
+
# - **Credential Exposure**: May expose build-time secrets and credentials
|
27
|
+
# - **Supply Chain Risk**: Downloaded packages may contain malware
|
28
|
+
# - **Resource Consumption**: Builds can consume significant CPU, memory, and storage
|
18
29
|
#
|
19
|
-
# Security
|
20
|
-
# - Review Dockerfile content
|
21
|
-
# - Use trusted base images
|
30
|
+
# **Security Recommendations**:
|
31
|
+
# - Review all Dockerfile content before building
|
32
|
+
# - Use trusted base images only
|
33
|
+
# - Avoid embedding secrets in image layers
|
34
|
+
# - Implement build isolation and sandboxing
|
35
|
+
# - Monitor build resource consumption
|
22
36
|
# - Scan built images for vulnerabilities
|
23
|
-
# -
|
24
|
-
# - Avoid including secrets in Dockerfile instructions
|
25
|
-
# - Use multi-stage builds to minimize final image size
|
37
|
+
# - Use multi-stage builds to minimize attack surface
|
26
38
|
#
|
27
|
-
# ==
|
39
|
+
# == Parameters
|
28
40
|
#
|
29
|
-
# -
|
30
|
-
# -
|
31
|
-
# - Comprehensive error handling
|
32
|
-
# - Support for all standard Dockerfile instructions
|
33
|
-
# - Returns image ID and build status
|
41
|
+
# - **dockerfile**: Dockerfile content as a string (required)
|
42
|
+
# - **tag**: Tag for the built image (optional, e.g., "myimage:latest")
|
34
43
|
#
|
35
44
|
# == Example Usage
|
36
45
|
#
|
37
|
-
# #
|
38
|
-
#
|
46
|
+
# # Build simple image
|
47
|
+
# dockerfile_content = <<~DOCKERFILE
|
48
|
+
# FROM alpine:latest
|
49
|
+
# RUN apk add --no-cache curl
|
50
|
+
# CMD ["curl", "--version"]
|
51
|
+
# DOCKERFILE
|
52
|
+
#
|
53
|
+
# response = BuildImage.call(
|
39
54
|
# server_context: context,
|
40
|
-
# dockerfile:
|
55
|
+
# dockerfile: dockerfile_content,
|
56
|
+
# tag: "my-curl:latest"
|
41
57
|
# )
|
42
58
|
#
|
43
|
-
# # Build
|
44
|
-
#
|
59
|
+
# # Build web server image
|
60
|
+
# dockerfile_content = <<~DOCKERFILE
|
61
|
+
# FROM nginx:alpine
|
62
|
+
# COPY nginx.conf /etc/nginx/nginx.conf
|
63
|
+
# EXPOSE 80
|
64
|
+
# CMD ["nginx", "-g", "daemon off;"]
|
65
|
+
# DOCKERFILE
|
66
|
+
#
|
67
|
+
# response = BuildImage.call(
|
45
68
|
# server_context: context,
|
46
69
|
# dockerfile: dockerfile_content,
|
47
|
-
# tag: "
|
70
|
+
# tag: "custom-nginx:v1.0"
|
48
71
|
# )
|
49
72
|
#
|
50
|
-
# @see Docker::Image.
|
51
|
-
# @see TagImage
|
73
|
+
# @see Docker::Image.build_from_dir
|
52
74
|
# @since 0.1.0
|
53
|
-
|
75
|
+
BUILD_IMAGE_DEFINITION = ToolForge.define(:build_image) do
|
54
76
|
description 'Build a Docker image'
|
55
77
|
|
56
|
-
|
57
|
-
|
58
|
-
dockerfile: {
|
59
|
-
type: 'string',
|
78
|
+
param :dockerfile,
|
79
|
+
type: :string,
|
60
80
|
description: 'Dockerfile content as a string'
|
61
|
-
},
|
62
|
-
tag: {
|
63
|
-
type: 'string',
|
64
|
-
description: 'Tag for the built image (e.g., "myimage:latest")'
|
65
|
-
}
|
66
|
-
},
|
67
|
-
required: ['dockerfile']
|
68
|
-
)
|
69
81
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
#
|
77
|
-
# @param dockerfile [String] the complete Dockerfile content as a string
|
78
|
-
# @param server_context [Object] MCP server context (unused but required)
|
79
|
-
# @param tag [String, nil] optional tag to apply to the built image
|
80
|
-
#
|
81
|
-
# @return [MCP::Tool::Response] build results including image ID and tag info
|
82
|
-
#
|
83
|
-
# @raise [Docker::Error] for Docker daemon communication errors
|
84
|
-
# @raise [StandardError] for build failures or other errors
|
85
|
-
#
|
86
|
-
# @example Build simple image
|
87
|
-
# dockerfile = <<~DOCKERFILE
|
88
|
-
# FROM alpine:latest
|
89
|
-
# RUN apk add --no-cache nginx
|
90
|
-
# EXPOSE 80
|
91
|
-
# CMD ["nginx", "-g", "daemon off;"]
|
92
|
-
# DOCKERFILE
|
93
|
-
#
|
94
|
-
# response = BuildImage.call(
|
95
|
-
# server_context: context,
|
96
|
-
# dockerfile: dockerfile,
|
97
|
-
# tag: "my-nginx:latest"
|
98
|
-
# )
|
99
|
-
#
|
100
|
-
# @see Docker::Image.build
|
101
|
-
def self.call(dockerfile:, server_context:, tag: nil)
|
82
|
+
param :tag,
|
83
|
+
type: :string,
|
84
|
+
description: 'Tag for the built image (e.g., "myimage:latest")',
|
85
|
+
required: false
|
86
|
+
|
87
|
+
execute do |dockerfile:, tag: nil|
|
102
88
|
# Build the image
|
103
89
|
image = Docker::Image.build(dockerfile)
|
104
90
|
|
@@ -112,16 +98,11 @@ module DockerMCP
|
|
112
98
|
|
113
99
|
response_text = "Image built successfully. ID: #{image.id}"
|
114
100
|
response_text += ", Tag: #{tag}" if tag
|
115
|
-
|
116
|
-
MCP::Tool::Response.new([{
|
117
|
-
type: 'text',
|
118
|
-
text: response_text
|
119
|
-
}])
|
101
|
+
response_text
|
120
102
|
rescue StandardError => e
|
121
|
-
|
122
|
-
type: 'text',
|
123
|
-
text: "Error building image: #{e.message}"
|
124
|
-
}])
|
103
|
+
"Error building image: #{e.message}"
|
125
104
|
end
|
126
105
|
end
|
106
|
+
|
107
|
+
BuildImage = BUILD_IMAGE_DEFINITION.to_mcp_tool
|
127
108
|
end
|
@@ -1,135 +1,117 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module DockerMCP
|
4
|
-
# MCP tool for copying files and directories
|
4
|
+
# MCP tool for copying files and directories to Docker containers.
|
5
5
|
#
|
6
|
-
# This tool provides the ability to copy files
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
6
|
+
# This tool provides the ability to copy files and directories from the
|
7
|
+
# local host filesystem into running Docker containers. It supports both
|
8
|
+
# individual files and entire directory trees, with optional ownership
|
9
|
+
# modification within the container.
|
10
10
|
#
|
11
|
-
# ==
|
11
|
+
# == Features
|
12
12
|
#
|
13
|
-
#
|
14
|
-
# -
|
15
|
-
# -
|
16
|
-
# -
|
17
|
-
# -
|
18
|
-
# -
|
13
|
+
# - Copy files and directories from host to container
|
14
|
+
# - Supports recursive directory copying
|
15
|
+
# - Preserves file permissions and metadata
|
16
|
+
# - Optional ownership modification after copy
|
17
|
+
# - Works with running containers
|
18
|
+
# - Comprehensive error handling and validation
|
19
19
|
#
|
20
|
-
# Security
|
21
|
-
# - Validate source paths to prevent directory traversal
|
22
|
-
# - Ensure containers run with minimal privileges
|
23
|
-
# - Monitor file copy operations for sensitive paths
|
24
|
-
# - Use read-only filesystems where possible
|
25
|
-
# - Implement proper access controls on source files
|
20
|
+
# == Security Considerations
|
26
21
|
#
|
27
|
-
#
|
22
|
+
# File copying operations have significant security implications:
|
23
|
+
# - **File System Access**: Reads local host file system content
|
24
|
+
# - **Container Modification**: Alters container file system state
|
25
|
+
# - **Data Injection**: Can introduce malicious files into containers
|
26
|
+
# - **Permission Escalation**: May affect container security context
|
27
|
+
# - **Resource Consumption**: Large copies can consume storage and I/O
|
28
|
+
# - **Path Traversal**: Improper paths could access unintended locations
|
29
|
+
#
|
30
|
+
# **Security Recommendations**:
|
31
|
+
# - Validate and sanitize all file paths
|
32
|
+
# - Implement access controls for source file locations
|
33
|
+
# - Monitor file copy operations and sizes
|
34
|
+
# - Use read-only mounts where possible
|
35
|
+
# - Apply resource limits to prevent abuse
|
36
|
+
#
|
37
|
+
# == Parameters
|
28
38
|
#
|
29
|
-
# -
|
30
|
-
# -
|
31
|
-
# -
|
32
|
-
# -
|
33
|
-
# - Support for both absolute and relative paths
|
39
|
+
# - **id**: Container ID or name (required)
|
40
|
+
# - **source_path**: Path to file/directory on local filesystem (required)
|
41
|
+
# - **destination_path**: Path inside container for copied content (required)
|
42
|
+
# - **owner**: Owner for copied files (optional, e.g., "1000:1000" or "username:group")
|
34
43
|
#
|
35
44
|
# == Example Usage
|
36
45
|
#
|
37
|
-
# # Copy
|
38
|
-
# CopyToContainer.call(
|
46
|
+
# # Copy single file
|
47
|
+
# response = CopyToContainer.call(
|
39
48
|
# server_context: context,
|
40
49
|
# id: "web-server",
|
41
|
-
# source_path: "/
|
42
|
-
# destination_path: "/etc/nginx/"
|
50
|
+
# source_path: "/local/config.conf",
|
51
|
+
# destination_path: "/etc/nginx/nginx.conf"
|
43
52
|
# )
|
44
53
|
#
|
45
54
|
# # Copy directory with ownership change
|
46
|
-
# CopyToContainer.call(
|
55
|
+
# response = CopyToContainer.call(
|
47
56
|
# server_context: context,
|
48
57
|
# id: "app-container",
|
49
|
-
# source_path: "/
|
50
|
-
# destination_path: "/app
|
51
|
-
# owner: "
|
58
|
+
# source_path: "/local/app-data",
|
59
|
+
# destination_path: "/var/lib/app",
|
60
|
+
# owner: "app:app"
|
52
61
|
# )
|
53
62
|
#
|
54
63
|
# @see Docker::Container#archive_in_stream
|
55
64
|
# @since 0.1.0
|
56
|
-
|
65
|
+
COPY_TO_CONTAINER_DEFINITION = ToolForge.define(:copy_to_container) do
|
57
66
|
description 'Copy a file or directory from the local filesystem into a running Docker container. ' \
|
58
67
|
'The source path is on the local machine, and the destination path is inside the container.'
|
59
68
|
|
60
|
-
|
61
|
-
|
62
|
-
id: {
|
63
|
-
type: 'string',
|
69
|
+
param :id,
|
70
|
+
type: :string,
|
64
71
|
description: 'Container ID or name'
|
65
|
-
|
66
|
-
|
67
|
-
type:
|
72
|
+
|
73
|
+
param :source_path,
|
74
|
+
type: :string,
|
68
75
|
description: 'Path to the file or directory on the local filesystem to copy'
|
69
|
-
|
70
|
-
|
71
|
-
type:
|
76
|
+
|
77
|
+
param :destination_path,
|
78
|
+
type: :string,
|
72
79
|
description: 'Path inside the container where the file/directory should be copied'
|
73
|
-
|
74
|
-
|
75
|
-
type:
|
76
|
-
description: 'Owner for the copied files (optional, e.g., "1000:1000" or "username:group")'
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
# @example Copy configuration file
|
105
|
-
# response = CopyToContainer.call(
|
106
|
-
# server_context: context,
|
107
|
-
# id: "nginx-container",
|
108
|
-
# source_path: "/etc/nginx/sites-available/default",
|
109
|
-
# destination_path: "/etc/nginx/sites-enabled/"
|
110
|
-
# )
|
111
|
-
#
|
112
|
-
# @example Copy directory with ownership
|
113
|
-
# response = CopyToContainer.call(
|
114
|
-
# server_context: context,
|
115
|
-
# id: "app-container",
|
116
|
-
# source_path: "/local/project",
|
117
|
-
# destination_path: "/app/",
|
118
|
-
# owner: "www-data:www-data"
|
119
|
-
# )
|
120
|
-
#
|
121
|
-
# @see Docker::Container#archive_in_stream
|
122
|
-
# @see #add_to_tar
|
123
|
-
def self.call(id:, source_path:, destination_path:, server_context:, owner: nil)
|
80
|
+
|
81
|
+
param :owner,
|
82
|
+
type: :string,
|
83
|
+
description: 'Owner for the copied files (optional, e.g., "1000:1000" or "username:group")',
|
84
|
+
required: false
|
85
|
+
|
86
|
+
# Helper method for adding files/directories to tar
|
87
|
+
class_helper :add_to_tar do |tar, path, archive_path|
|
88
|
+
if File.directory?(path)
|
89
|
+
# Add directory entry
|
90
|
+
tar.mkdir(archive_path, File.stat(path).mode)
|
91
|
+
|
92
|
+
# Add directory contents
|
93
|
+
Dir.entries(path).each do |entry|
|
94
|
+
next if ['.', '..'].include?(entry)
|
95
|
+
|
96
|
+
full_path = File.join(path, entry)
|
97
|
+
archive_entry_path = File.join(archive_path, entry)
|
98
|
+
add_to_tar(tar, full_path, archive_entry_path)
|
99
|
+
end
|
100
|
+
else
|
101
|
+
# Add file
|
102
|
+
File.open(path, 'rb') do |file|
|
103
|
+
tar.add_file_simple(archive_path, File.stat(path).mode, file.size) do |tar_file|
|
104
|
+
IO.copy_stream(file, tar_file)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
execute do |id:, source_path:, destination_path:, owner: nil|
|
124
111
|
container = Docker::Container.get(id)
|
125
112
|
|
126
113
|
# Verify source path exists
|
127
|
-
unless File.exist?(source_path)
|
128
|
-
return MCP::Tool::Response.new([{
|
129
|
-
type: 'text',
|
130
|
-
text: "Source path not found: #{source_path}"
|
131
|
-
}])
|
132
|
-
end
|
114
|
+
next "Source path not found: #{source_path}" unless File.exist?(source_path)
|
133
115
|
|
134
116
|
# Create a tar archive of the source
|
135
117
|
tar_io = StringIO.new
|
@@ -155,69 +137,13 @@ module DockerMCP
|
|
155
137
|
file_type = File.directory?(source_path) ? 'directory' : 'file'
|
156
138
|
response_text = "Successfully copied #{file_type} from #{source_path} to #{id}:#{destination_path}"
|
157
139
|
response_text += "\nOwnership changed to #{owner}" if owner
|
158
|
-
|
159
|
-
MCP::Tool::Response.new([{
|
160
|
-
type: 'text',
|
161
|
-
text: response_text
|
162
|
-
}])
|
140
|
+
response_text
|
163
141
|
rescue Docker::Error::NotFoundError
|
164
|
-
|
165
|
-
type: 'text',
|
166
|
-
text: "Container #{id} not found"
|
167
|
-
}])
|
142
|
+
"Container #{id} not found"
|
168
143
|
rescue StandardError => e
|
169
|
-
|
170
|
-
type: 'text',
|
171
|
-
text: "Error copying to container: #{e.message}"
|
172
|
-
}])
|
173
|
-
end
|
174
|
-
|
175
|
-
# Recursively add files and directories to a tar archive.
|
176
|
-
#
|
177
|
-
# This helper method builds a tar archive by recursively traversing
|
178
|
-
# the filesystem starting from the given path. It preserves file
|
179
|
-
# permissions and handles both files and directories appropriately.
|
180
|
-
#
|
181
|
-
# For directories, it creates directory entries in the tar and then
|
182
|
-
# recursively processes all contained files and subdirectories.
|
183
|
-
# For files, it reads the content and adds it to the tar with
|
184
|
-
# preserved permissions.
|
185
|
-
#
|
186
|
-
# @param tar [Gem::Package::TarWriter] the tar writer instance
|
187
|
-
# @param path [String] the filesystem path to add to the archive
|
188
|
-
# @param archive_path [String] the path within the tar archive
|
189
|
-
#
|
190
|
-
# @return [void]
|
191
|
-
#
|
192
|
-
# @example Add single file
|
193
|
-
# add_to_tar(tar_writer, "/host/file.txt", "file.txt")
|
194
|
-
#
|
195
|
-
# @example Add directory tree
|
196
|
-
# add_to_tar(tar_writer, "/host/mydir", "mydir")
|
197
|
-
#
|
198
|
-
# @see Gem::Package::TarWriter#mkdir
|
199
|
-
# @see Gem::Package::TarWriter#add_file_simple
|
200
|
-
def self.add_to_tar(tar, path, archive_path)
|
201
|
-
if File.directory?(path)
|
202
|
-
# Add directory entry
|
203
|
-
tar.mkdir(archive_path, File.stat(path).mode)
|
204
|
-
|
205
|
-
# Add directory contents
|
206
|
-
Dir.entries(path).each do |entry|
|
207
|
-
next if ['.', '..'].include?(entry)
|
208
|
-
|
209
|
-
full_path = File.join(path, entry)
|
210
|
-
archive_entry_path = File.join(archive_path, entry)
|
211
|
-
add_to_tar(tar, full_path, archive_entry_path)
|
212
|
-
end
|
213
|
-
else
|
214
|
-
# Add file
|
215
|
-
File.open(path, 'rb') do |file|
|
216
|
-
tar.add_file_simple(archive_path, File.stat(path).mode, file.size) do |tar_file|
|
217
|
-
IO.copy_stream(file, tar_file)
|
218
|
-
end
|
219
|
-
end
|
220
|
-
end
|
144
|
+
"Error copying to container: #{e.message}"
|
221
145
|
end
|
222
146
|
end
|
147
|
+
|
148
|
+
CopyToContainer = COPY_TO_CONTAINER_DEFINITION.to_mcp_tool
|
223
149
|
end
|