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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: efb62c57572aa0d7838b21fb503cc82d4d29b1b7a4b67a56a73403abf95569cd
4
- data.tar.gz: 659cee10a2d4bb77b2a4dabe5acf47c63415e17e67b34f3046eb5bbb149ca9a1
3
+ metadata.gz: 0b93e5e574efb910b42bf12e0fe976966fadf8671644d39427fc3e0c9123ddd8
4
+ data.tar.gz: 693331e50cf5a4232f71e6a1d4eff6ad9df4c23d7c844140e1b25029384dc80f
5
5
  SHA512:
6
- metadata.gz: f24eb8a997aa33b2a4389170fd3d938d9ac0e00fec6f437074497ddfc260d81a8390b61b883caf35901d19f34af271b1d8d32f3b5ff24ca51dea9c885cb9e7c7
7
- data.tar.gz: 68672e9b0c1432fa2575aea0034d4d4d74e872d83da97456d2fc0cc698249fd643d9325cfc9ed25223f3832b980926212fc98d228d135a10180c7cdd8ea8e0d5
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 from Dockerfile content.
4
+ # MCP tool for building Docker images.
5
5
  #
6
- # This tool provides the ability to build Docker images by providing Dockerfile
7
- # content as a string. It supports optional tagging of the resulting image for
8
- # easy identification and reuse.
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
- # Building Docker images can be potentially dangerous:
13
- # - Dockerfile commands execute with Docker daemon privileges
14
- # - Images can contain malicious software or backdoors
15
- # - Build process can access host resources (network, files)
16
- # - Base images may contain vulnerabilities
17
- # - Build context may expose sensitive information
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 recommendations:
20
- # - Review Dockerfile content carefully before building
21
- # - Use trusted base images from official repositories
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
- # - Limit network access during builds
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
- # == Features
39
+ # == Parameters
28
40
  #
29
- # - Build images from Dockerfile content strings
30
- # - Optional image tagging during build
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
- # # Simple image build
38
- # BuildImage.call(
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: "FROM alpine:latest\nRUN apk add --no-cache curl"
55
+ # dockerfile: dockerfile_content,
56
+ # tag: "my-curl:latest"
41
57
  # )
42
58
  #
43
- # # Build with custom tag
44
- # BuildImage.call(
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: "myapp:v1.0"
70
+ # tag: "custom-nginx:v1.0"
48
71
  # )
49
72
  #
50
- # @see Docker::Image.build
51
- # @see TagImage
73
+ # @see Docker::Image.build_from_dir
52
74
  # @since 0.1.0
53
- class BuildImage < MCP::Tool
75
+ BUILD_IMAGE_DEFINITION = ToolForge.define(:build_image) do
54
76
  description 'Build a Docker image'
55
77
 
56
- input_schema(
57
- properties: {
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
- # Build a Docker image from Dockerfile content.
71
- #
72
- # This method creates a Docker image by building from the provided Dockerfile
73
- # content string. The Dockerfile is processed by the Docker daemon and can
74
- # include any valid Dockerfile instructions. Optionally, the resulting image
75
- # can be tagged with a custom name for easy reference.
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
- MCP::Tool::Response.new([{
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 from host to Docker containers.
4
+ # MCP tool for copying files and directories to Docker containers.
5
5
  #
6
- # This tool provides the ability to copy files or entire directory trees from
7
- # the host filesystem into running Docker containers. It uses Docker's archive
8
- # streaming API to efficiently transfer files while preserving permissions and
9
- # directory structure.
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
- # == ⚠️ SECURITY WARNING ⚠️
11
+ # == Features
12
12
  #
13
- # This tool can be dangerous as it allows:
14
- # - Reading arbitrary files from the host filesystem
15
- # - Writing files into container filesystems
16
- # - Potentially overwriting critical container files
17
- # - Escalating privileges if used with setuid/setgid files
18
- # - Exposing sensitive host data to containers
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 recommendations:
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
- # == Features
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
- # - Copy individual files or entire directories
30
- # - Preserve file permissions and directory structure
31
- # - Optional ownership changes after copy
32
- # - Comprehensive error handling
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 a configuration file
38
- # CopyToContainer.call(
46
+ # # Copy single file
47
+ # response = CopyToContainer.call(
39
48
  # server_context: context,
40
49
  # id: "web-server",
41
- # source_path: "/host/config/nginx.conf",
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: "/host/app/src",
50
- # destination_path: "/app/",
51
- # owner: "appuser:appgroup"
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
- class CopyToContainer < MCP::Tool
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
- input_schema(
61
- properties: {
62
- id: {
63
- type: 'string',
69
+ param :id,
70
+ type: :string,
64
71
  description: 'Container ID or name'
65
- },
66
- source_path: {
67
- type: 'string',
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
- destination_path: {
71
- type: 'string',
76
+
77
+ param :destination_path,
78
+ type: :string,
72
79
  description: 'Path inside the container where the file/directory should be copied'
73
- },
74
- owner: {
75
- type: 'string',
76
- description: 'Owner for the copied files (optional, e.g., "1000:1000" or "username:group")'
77
- }
78
- },
79
- required: %w[id source_path destination_path]
80
- )
81
-
82
- # Copy files or directories from host filesystem to a Docker container.
83
- #
84
- # This method creates a tar archive of the source path and streams it into
85
- # the specified container using Docker's archive API. The operation preserves
86
- # file permissions and directory structure. Optionally, ownership can be
87
- # changed after the copy operation completes.
88
- #
89
- # The source path must exist on the host filesystem and be readable by the
90
- # process running the MCP server. The destination path must be a valid path
91
- # within the container.
92
- #
93
- # @param id [String] container ID or name to copy files into
94
- # @param source_path [String] path to file/directory on host filesystem
95
- # @param destination_path [String] destination path inside container
96
- # @param server_context [Object] MCP server context (unused but required)
97
- # @param owner [String, nil] ownership specification (e.g., "user:group", "1000:1000")
98
- #
99
- # @return [MCP::Tool::Response] success/failure message with operation details
100
- #
101
- # @raise [Docker::Error::NotFoundError] if container doesn't exist
102
- # @raise [StandardError] for file system or Docker API errors
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
- MCP::Tool::Response.new([{
165
- type: 'text',
166
- text: "Container #{id} not found"
167
- }])
142
+ "Container #{id} not found"
168
143
  rescue StandardError => e
169
- MCP::Tool::Response.new([{
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