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.
@@ -1,179 +1,122 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'json'
4
- require 'open3'
5
-
6
3
  module DockerMCP
7
4
  # MCP tool for pushing Docker images to registries.
8
5
  #
9
- # This tool provides the ability to upload Docker images to Docker registries
10
- # such as Docker Hub, private registries, or cloud-based container registries.
11
- # It uses the Docker CLI to leverage native credential handling and push
12
- # capabilities.
6
+ # This tool provides the ability to upload Docker images to Docker
7
+ # registries such as Docker Hub, private registries, or cloud-based
8
+ # container registries. It supports various push configurations and
9
+ # authentication scenarios.
13
10
  #
14
11
  # == Features
15
12
  #
16
- # - Push images to any accessible Docker registry
17
- # - Flexible image and tag specification
18
- # - Native Docker credential handling
19
- # - Support for private registries
20
- # - Comprehensive error handling
21
- # - Validation of registry-compatible names
13
+ # - Push images to any Docker registry
14
+ # - Support for tagged and untagged pushes
15
+ # - Registry authentication integration
16
+ # - Comprehensive error handling and validation
17
+ # - Multi-registry support
18
+ # - Progress tracking and status reporting
19
+ # - Registry namespace validation
22
20
  #
23
- # == ⚠️ Security Considerations ⚠️
21
+ # == Security Considerations
24
22
  #
25
23
  # Pushing images involves significant security risks:
26
24
  # - **Credential Exposure**: Registry credentials may be exposed
27
- # - **Data Exfiltration**: Images may contain sensitive application data
28
- # - **Intellectual Property**: Source code and binaries may be exposed
29
- # - **Supply Chain Risk**: Malicious actors could access pushed images
30
- # - **Registry Access**: Unauthorized access to registry accounts
25
+ # - **Image Integrity**: Pushed images become publicly accessible
26
+ # - **Supply Chain Risk**: Malicious images can be distributed
27
+ # - **Registry Security**: Vulnerable registries can be compromised
28
+ # - **Network Exposure**: Push operations traverse networks
29
+ # - **Access Control**: Improper permissions can lead to unauthorized access
31
30
  #
32
- # Critical security measures:
33
- # - Verify registry authentication and authorization
34
- # - Scan images for secrets before pushing
35
- # - Use private registries for sensitive applications
31
+ # **Security Recommendations**:
32
+ # - Use secure registry authentication
33
+ # - Scan images for vulnerabilities before pushing
36
34
  # - Implement image signing and verification
37
- # - Monitor registry access and downloads
38
- # - Regularly audit pushed image contents
35
+ # - Use private registries for sensitive images
36
+ # - Monitor registry access and usage
37
+ # - Implement proper RBAC for registry operations
38
+ # - Validate image content before distribution
39
39
  #
40
- # == Registry Requirements
40
+ # == Parameters
41
41
  #
42
- # Images must be properly tagged for registry compatibility:
43
- # - Include registry hostname for private registries
44
- # - Include username/organization for Docker Hub
45
- # - Examples: `username/myapp`, `registry.company.com/team/app`
46
- # - Local image names (without `/`) cannot be pushed
42
+ # - **name**: Image name or ID to push (required)
43
+ # - **tag**: Tag to push (optional, pushes all tags if not specified)
44
+ # - **repo_tag**: Full repo:tag to push (optional, e.g., "registry/repo:tag")
47
45
  #
48
46
  # == Example Usage
49
47
  #
50
48
  # # Push to Docker Hub
51
- # PushImage.call(
49
+ # response = PushImage.call(
52
50
  # server_context: context,
53
- # name: "myusername/myapp",
54
- # tag: "v1.0"
51
+ # name: "username/myapp",
52
+ # tag: "v1.0.0"
55
53
  # )
56
54
  #
57
55
  # # Push to private registry
58
- # PushImage.call(
56
+ # response = PushImage.call(
59
57
  # server_context: context,
60
- # name: "registry.company.com/team/app",
61
- # tag: "latest"
58
+ # name: "myapp",
59
+ # repo_tag: "registry.company.com/team/myapp:latest"
62
60
  # )
63
61
  #
64
- # # Push with full repo specification
65
- # PushImage.call(
62
+ # # Push all tags
63
+ # response = PushImage.call(
66
64
  # server_context: context,
67
- # name: "myapp",
68
- # repo_tag: "myregistry.com/myuser/myapp:v2.0"
65
+ # name: "username/myapp"
69
66
  # )
70
67
  #
71
- # @see PullImage
72
- # @see TagImage
73
- # @see BuildImage
68
+ # @see Docker CLI push command
74
69
  # @since 0.1.0
75
- class PushImage < MCP::Tool
70
+ PUSH_IMAGE_DEFINITION = ToolForge.define(:push_image) do
76
71
  description 'Push a Docker image'
77
72
 
78
- input_schema(
79
- properties: {
80
- name: {
81
- type: 'string',
73
+ param :name,
74
+ type: :string,
82
75
  description: 'Image name or ID to push'
83
- },
84
- tag: {
85
- type: 'string',
86
- description: 'Tag to push (optional, pushes all tags if not specified)'
87
- },
88
- repo_tag: {
89
- type: 'string',
90
- description: 'Full repo:tag to push (e.g., "registry/repo:tag") (optional)'
91
- }
92
- },
93
- required: ['name']
94
- )
95
76
 
96
- # Push a Docker image to a registry.
97
- #
98
- # This method uploads the specified image to a Docker registry using
99
- # the Docker CLI for native credential handling. The image must be
100
- # properly tagged for registry compatibility.
101
- #
102
- # @param name [String] image name or ID to push
103
- # @param server_context [Object] MCP server context (unused but required)
104
- # @param tag [String, nil] specific tag to push
105
- # @param repo_tag [String, nil] complete repository:tag specification
106
- #
107
- # @return [MCP::Tool::Response] push operation results
108
- #
109
- # @raise [StandardError] for push failures or authentication issues
110
- #
111
- # @example Push tagged image to Docker Hub
112
- # response = PushImage.call(
113
- # server_context: context,
114
- # name: "myuser/webapp",
115
- # tag: "v1.2.3"
116
- # )
117
- #
118
- # @example Push to private registry
119
- # response = PushImage.call(
120
- # server_context: context,
121
- # name: "internal-registry.com/team/service",
122
- # tag: "latest"
123
- # )
124
- #
125
- # @see Docker::Image.get
126
- def self.call(name:, server_context:, tag: nil, repo_tag: nil)
77
+ param :tag,
78
+ type: :string,
79
+ description: 'Tag to push (optional, pushes all tags if not specified)',
80
+ required: false
81
+
82
+ param :repo_tag,
83
+ type: :string,
84
+ description: 'Full repo:tag to push (e.g., "registry/repo:tag") (optional)',
85
+ required: false
86
+
87
+ execute do |name:, tag: nil, repo_tag: nil|
127
88
  # Construct the full image identifier
128
89
  image_identifier = tag ? "#{name}:#{tag}" : name
129
90
 
130
91
  # Validate that the image name includes a registry/username
131
- # Images without a registry prefix will fail to push to Docker Hub
132
92
  unless name.include?('/') || repo_tag&.include?('/')
133
- error_msg = 'Error: Image name must include registry/username ' \
134
- "(e.g., 'username/#{name}'). Local images cannot be " \
135
- 'pushed without a registry prefix.'
136
- return MCP::Tool::Response.new([{
137
- type: 'text',
138
- text: error_msg
139
- }])
93
+ next 'Error: Image name must include registry/username ' \
94
+ "(e.g., 'username/#{name}'). Local images cannot be " \
95
+ 'pushed without a registry prefix.'
140
96
  end
141
97
 
142
98
  # Verify the image exists
143
99
  begin
144
100
  Docker::Image.get(image_identifier)
145
101
  rescue Docker::Error::NotFoundError
146
- return MCP::Tool::Response.new([{
147
- type: 'text',
148
- text: "Image #{image_identifier} not found"
149
- }])
102
+ next "Image #{image_identifier} not found"
150
103
  end
151
104
 
152
105
  # Use the Docker CLI to push the image
153
- # This way we leverage Docker's native credential handling
154
106
  push_target = repo_tag || image_identifier
155
107
  _, stderr, status = Open3.capture3('docker', 'push', push_target)
156
108
 
157
109
  if status.success?
158
- MCP::Tool::Response.new([{
159
- type: 'text',
160
- text: "Image #{push_target} pushed successfully"
161
- }])
110
+ "Image #{push_target} pushed successfully"
162
111
  else
163
- # Extract the error message from stderr
164
112
  error_msg = stderr.strip
165
113
  error_msg = 'Failed to push image' if error_msg.empty?
166
-
167
- MCP::Tool::Response.new([{
168
- type: 'text',
169
- text: "Error pushing image: #{error_msg}"
170
- }])
114
+ "Error pushing image: #{error_msg}"
171
115
  end
172
116
  rescue StandardError => e
173
- MCP::Tool::Response.new([{
174
- type: 'text',
175
- text: "Error pushing image: #{e.message}"
176
- }])
117
+ "Error pushing image: #{e.message}"
177
118
  end
178
119
  end
120
+
121
+ PushImage = PUSH_IMAGE_DEFINITION.to_mcp_tool
179
122
  end
@@ -1,123 +1,79 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DockerMCP
4
- # MCP tool for recreating Docker containers with the same configuration.
4
+ # MCP tool for recreating Docker containers.
5
5
  #
6
- # This tool provides a convenient way to recreate containers while preserving
7
- # their original configuration. It stops and removes the existing container,
8
- # then creates a new one with identical settings. This is useful for applying
9
- # image updates, clearing container state, or resolving container issues.
6
+ # This tool provides a complete container recreation process that stops
7
+ # the existing container, removes it, and creates a new container with
8
+ # the same configuration. This is useful for applying image updates,
9
+ # clearing container state, or resolving container corruption issues.
10
10
  #
11
11
  # == Features
12
12
  #
13
- # - Preserves complete container configuration
14
- # - Maintains original name and settings
15
- # - Handles running containers gracefully
16
- # - Restores running state after recreation
17
- # - Configurable stop timeout
18
- # - Comprehensive error handling
13
+ # - Complete container recreation with preserved configuration
14
+ # - Automatic stop, remove, and recreate sequence
15
+ # - Preserves original container configuration and settings
16
+ # - Configurable stop timeout for graceful shutdown
17
+ # - Handles both running and stopped containers
18
+ # - Maintains container networking and volume configurations
19
19
  #
20
- # == Process Overview
21
- #
22
- # 1. Retrieve existing container configuration
23
- # 2. Stop container gracefully (if running)
24
- # 3. Remove the old container
25
- # 4. Create new container with identical config
26
- # 5. Start new container (if original was running)
20
+ # == Security Considerations
27
21
  #
28
- # == ⚠️ Data Loss Warning ⚠️
22
+ # Container recreation involves several security considerations:
23
+ # - **Service Downtime**: Temporary service interruption during recreation
24
+ # - **Data Loss**: Container file system changes are lost (volumes preserved)
25
+ # - **Resource Allocation**: New container may have different resource usage
26
+ # - **Network Reconfiguration**: IP addresses may change
27
+ # - **State Reset**: Application state within container is lost
29
28
  #
30
- # **DESTRUCTIVE OPERATION - DATA LOSS POSSIBLE**
29
+ # Plan recreations carefully and coordinate with dependent services.
31
30
  #
32
- # This operation can cause permanent data loss:
33
- # - Container filesystem changes are lost
34
- # - Temporary data and logs are deleted
35
- # - Container state is reset completely
36
- # - Network connections are interrupted
37
- # - Anonymous volumes may be recreated empty
31
+ # == Parameters
38
32
  #
39
- # == Security Considerations
33
+ # - **id**: Container ID or name to recreate (required)
34
+ # - **timeout**: Seconds to wait before killing container when stopping (optional, default: 10)
40
35
  #
41
- # - Original configuration is preserved exactly
42
- # - Sensitive environment variables are maintained
43
- # - Port mappings and volume mounts are restored
44
- # - Network access patterns remain the same
36
+ # == Process Flow
45
37
  #
46
- # Ensure original configuration is still secure:
47
- # - Review exposed ports and volumes
48
- # - Validate environment variables
49
- # - Check image security updates
50
- # - Verify network policies
38
+ # 1. Inspect existing container to capture configuration
39
+ # 2. Stop the running container (if running)
40
+ # 3. Remove the stopped container
41
+ # 4. Create new container with captured configuration
42
+ # 5. Return new container information
51
43
  #
52
44
  # == Example Usage
53
45
  #
54
46
  # # Recreate with default timeout
55
- # RecreateContainer.call(
47
+ # response = RecreateContainer.call(
56
48
  # server_context: context,
57
49
  # id: "web-server"
58
50
  # )
59
51
  #
60
- # # Recreate with longer stop timeout
61
- # RecreateContainer.call(
52
+ # # Recreate with extended timeout
53
+ # response = RecreateContainer.call(
62
54
  # server_context: context,
63
55
  # id: "database",
64
56
  # timeout: 30
65
57
  # )
66
58
  #
67
- # @see CreateContainer
68
- # @see StopContainer
69
- # @see RemoveContainer
59
+ # @see Docker::Container#stop
60
+ # @see Docker::Container#remove
70
61
  # @see Docker::Container.create
71
62
  # @since 0.1.0
72
- class RecreateContainer < MCP::Tool
63
+ RECREATE_CONTAINER_DEFINITION = ToolForge.define(:recreate_container) do
73
64
  description 'Recreate a Docker container (stops, removes, and recreates with same configuration)'
74
65
 
75
- input_schema(
76
- properties: {
77
- id: {
78
- type: 'string',
66
+ param :id,
67
+ type: :string,
79
68
  description: 'Container ID or name to recreate'
80
- },
81
- timeout: {
82
- type: 'integer',
83
- description: 'Seconds to wait before killing the container when stopping (default: 10)'
84
- }
85
- },
86
- required: ['id']
87
- )
88
69
 
89
- # Recreate a Docker container with identical configuration.
90
- #
91
- # This method performs a complete container recreation cycle while
92
- # preserving all configuration settings. The new container will have
93
- # the same name, environment, port mappings, volumes, and other
94
- # settings as the original.
95
- #
96
- # @param id [String] container ID (full or short) or container name
97
- # @param server_context [Object] MCP server context (unused but required)
98
- # @param timeout [Integer] seconds to wait before force killing during stop (default: 10)
99
- #
100
- # @return [MCP::Tool::Response] recreation results with new container ID
101
- #
102
- # @raise [Docker::Error::NotFoundError] if container doesn't exist
103
- # @raise [StandardError] for recreation failures
104
- #
105
- # @example Recreate application container
106
- # response = RecreateContainer.call(
107
- # server_context: context,
108
- # id: "my-app"
109
- # )
110
- #
111
- # @example Recreate database with extended timeout
112
- # response = RecreateContainer.call(
113
- # server_context: context,
114
- # id: "postgres-main",
115
- # timeout: 60 # Allow time for DB shutdown
116
- # )
117
- #
118
- # @see Docker::Container.get
119
- # @see Docker::Container.create
120
- def self.call(id:, server_context:, timeout: 10)
70
+ param :timeout,
71
+ type: :integer,
72
+ description: 'Seconds to wait before killing the container when stopping (default: 10)',
73
+ required: false,
74
+ default: 10
75
+
76
+ execute do |id:, timeout: 10|
121
77
  # Get the existing container
122
78
  old_container = Docker::Container.get(id)
123
79
  config = old_container.json
@@ -149,20 +105,13 @@ module DockerMCP
149
105
  # Start if the old one was running
150
106
  new_container.start if config['State']['Running']
151
107
 
152
- MCP::Tool::Response.new([{
153
- type: 'text',
154
- text: "Container #{id} recreated successfully. New ID: #{new_container.id}"
155
- }])
108
+ "Container #{id} recreated successfully. New ID: #{new_container.id}"
156
109
  rescue Docker::Error::NotFoundError
157
- MCP::Tool::Response.new([{
158
- type: 'text',
159
- text: "Container #{id} not found"
160
- }])
110
+ "Container #{id} not found"
161
111
  rescue StandardError => e
162
- MCP::Tool::Response.new([{
163
- type: 'text',
164
- text: "Error recreating container: #{e.message}"
165
- }])
112
+ "Error recreating container: #{e.message}"
166
113
  end
167
114
  end
115
+
116
+ RecreateContainer = RECREATE_CONTAINER_DEFINITION.to_mcp_tool
168
117
  end
@@ -3,140 +3,88 @@
3
3
  module DockerMCP
4
4
  # MCP tool for removing Docker containers.
5
5
  #
6
- # This tool provides the ability to permanently delete Docker containers
7
- # from the system. It supports both graceful removal of stopped containers
8
- # and forced removal of running containers. Optionally, it can also remove
9
- # associated anonymous volumes.
6
+ # This tool permanently removes a Docker container from the system,
7
+ # including its file system, configuration, and metadata. This is a
8
+ # destructive operation that cannot be undone. The container must
9
+ # be stopped before removal unless force is specified.
10
10
  #
11
11
  # == Features
12
12
  #
13
- # - Remove stopped containers safely
14
- # - Force removal of running containers
13
+ # - Permanent container removal from system
14
+ # - Supports forced removal of running containers
15
15
  # - Optional removal of associated volumes
16
- # - Comprehensive error handling
17
- # - Works with containers by ID or name
16
+ # - Handles container identification by ID or name
17
+ # - Provides comprehensive error handling
18
+ # - Frees all associated system resources
18
19
  #
19
- # == Data Loss Warning
20
- #
21
- # ⚠️ **DESTRUCTIVE OPERATION** ⚠️
20
+ # == Security Considerations
22
21
  #
23
- # This operation permanently deletes containers and potentially data:
24
- # - Container filesystem changes are lost forever
25
- # - Running processes are killed immediately (with force)
26
- # - Associated volumes may be removed if specified
27
- # - Container logs and metadata are deleted
28
- # - Operation cannot be undone
22
+ # Container removal is a destructive operation with implications:
23
+ # - **Data Loss**: Permanently destroys container file system
24
+ # - **Configuration Loss**: Removes container settings and metadata
25
+ # - **Service Disruption**: Eliminates containerized services
26
+ # - **Resource Recovery**: Frees storage, memory, and system resources
27
+ # - **Audit Trail**: May remove forensic evidence if needed
29
28
  #
30
- # == Security Considerations
29
+ # Implement proper backup and approval workflows for production systems.
31
30
  #
32
- # - Forced removal can cause data corruption
33
- # - Volume removal may affect other containers
34
- # - Running container removal terminates services abruptly
35
- # - Sensitive data in container memory is not securely wiped
31
+ # == Parameters
36
32
  #
37
- # Best practices:
38
- # - Stop containers gracefully before removal
39
- # - Backup important data before removing
40
- # - Verify volume dependencies before volume removal
41
- # - Use force removal only when necessary
33
+ # - **id**: Container ID or name (required)
34
+ # - Accepts full container IDs
35
+ # - Accepts short container IDs (first 12+ characters)
36
+ # - Accepts custom container names
37
+ # - **force**: Force removal of running container (optional, default: false)
38
+ # - **volumes**: Remove associated volumes (optional, default: false)
42
39
  #
43
40
  # == Example Usage
44
41
  #
45
42
  # # Remove stopped container
46
- # RemoveContainer.call(
43
+ # response = RemoveContainer.call(
47
44
  # server_context: context,
48
- # id: "old-container"
45
+ # id: "old-web-server"
49
46
  # )
50
47
  #
51
48
  # # Force remove running container with volumes
52
- # RemoveContainer.call(
49
+ # response = RemoveContainer.call(
53
50
  # server_context: context,
54
51
  # id: "problematic-container",
55
52
  # force: true,
56
53
  # volumes: true
57
54
  # )
58
55
  #
59
- # @see StopContainer
60
- # @see CreateContainer
61
- # @see Docker::Container#delete
56
+ # @see Docker::Container#remove
62
57
  # @since 0.1.0
63
- class RemoveContainer < MCP::Tool
58
+ REMOVE_CONTAINER_DEFINITION = ToolForge.define(:remove_container) do
64
59
  description 'Remove a Docker container'
65
60
 
66
- input_schema(
67
- properties: {
68
- id: {
69
- type: 'string',
61
+ param :id,
62
+ type: :string,
70
63
  description: 'Container ID or name'
71
- },
72
- force: {
73
- type: 'boolean',
74
- description: 'Force removal of running container (default: false)'
75
- },
76
- volumes: {
77
- type: 'boolean',
78
- description: 'Remove associated volumes (default: false)'
79
- }
80
- },
81
- required: ['id']
82
- )
83
64
 
84
- # Remove a Docker container permanently.
85
- #
86
- # This method deletes a container from the Docker system. By default,
87
- # it only removes stopped containers. The force option allows removal
88
- # of running containers, and the volumes option removes associated
89
- # anonymous volumes.
90
- #
91
- # @param id [String] container ID (full or short) or container name
92
- # @param server_context [Object] MCP server context (unused but required)
93
- # @param force [Boolean] whether to force remove running containers (default: false)
94
- # @param volumes [Boolean] whether to remove associated volumes (default: false)
95
- #
96
- # @return [MCP::Tool::Response] removal operation results
97
- #
98
- # @raise [Docker::Error::NotFoundError] if container doesn't exist
99
- # @raise [StandardError] for other removal failures
100
- #
101
- # @example Remove stopped container
102
- # response = RemoveContainer.call(
103
- # server_context: context,
104
- # id: "finished-job"
105
- # )
106
- #
107
- # @example Force remove running container
108
- # response = RemoveContainer.call(
109
- # server_context: context,
110
- # id: "stuck-container",
111
- # force: true
112
- # )
113
- #
114
- # @example Remove container and its volumes
115
- # response = RemoveContainer.call(
116
- # server_context: context,
117
- # id: "temp-container",
118
- # volumes: true
119
- # )
120
- #
121
- # @see Docker::Container#delete
122
- def self.call(id:, server_context:, force: false, volumes: false)
65
+ param :force,
66
+ type: :boolean,
67
+ description: 'Force removal of running container (default: false)',
68
+ required: false,
69
+ default: false
70
+
71
+ param :volumes,
72
+ type: :boolean,
73
+ description: 'Remove associated volumes (default: false)',
74
+ required: false,
75
+ default: false
76
+
77
+ execute do |id:, force: false, volumes: false|
123
78
  container = Docker::Container.get(id)
124
79
  container.delete(force: force, v: volumes)
125
80
 
126
- MCP::Tool::Response.new([{
127
- type: 'text',
128
- text: "Container #{id} removed successfully"
129
- }])
81
+ "Container #{id} removed successfully"
130
82
  rescue Docker::Error::NotFoundError
131
- MCP::Tool::Response.new([{
132
- type: 'text',
133
- text: "Container #{id} not found"
134
- }])
83
+ "Container #{id} not found"
135
84
  rescue StandardError => e
136
- MCP::Tool::Response.new([{
137
- type: 'text',
138
- text: "Error removing container: #{e.message}"
139
- }])
85
+ "Error removing container: #{e.message}"
140
86
  end
141
87
  end
88
+
89
+ RemoveContainer = REMOVE_CONTAINER_DEFINITION.to_mcp_tool
142
90
  end