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,97 +1,97 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DockerMCP
4
- # MCP tool for running Docker containers (create and start in one operation).
4
+ # MCP tool for running Docker containers.
5
5
  #
6
- # This tool provides a convenient way to create and immediately start Docker
7
- # containers from images. It combines the functionality of container creation
8
- # and startup into a single operation, which is the most common use case for
9
- # Docker containers.
6
+ # This tool creates and immediately starts a Docker container from a
7
+ # specified image in a single operation. It combines the functionality
8
+ # of create_container and start_container for convenience when immediate
9
+ # execution is desired.
10
10
  #
11
11
  # == Features
12
12
  #
13
- # - Create and start containers in one operation
14
- # - Full support for container configuration options
15
- # - Port mapping and network configuration
16
- # - Volume mounting capabilities
17
- # - Environment variable configuration
18
- # - Custom command execution
19
- # - Comprehensive error handling
13
+ # - Creates and starts containers in one operation
14
+ # - Supports all container configuration options
15
+ # - Configures command execution and environment variables
16
+ # - Sets up port exposure and network configuration
17
+ # - Applies advanced host configurations and volume mounts
18
+ # - Handles container naming and labeling
20
19
  #
21
20
  # == Security Considerations
22
21
  #
23
- # - Containers inherit Docker daemon privileges
24
- # - Port mappings expose services to host network
25
- # - Volume mounts provide filesystem access
26
- # - Environment variables may contain sensitive data
27
- # - Custom commands can execute arbitrary code
22
+ # Running containers involves significant security considerations:
23
+ # - **Immediate Execution**: Starts processes immediately upon creation
24
+ # - **Resource Consumption**: Consumes CPU, memory, and storage resources
25
+ # - **Network Exposure**: Creates active network endpoints
26
+ # - **File System Access**: Potentially accesses host directories
27
+ # - **Process Isolation**: Runs processes with configured privileges
28
28
  #
29
- # Use appropriate security measures:
30
- # - Run containers with minimal required privileges
31
- # - Limit port exposure to necessary services only
32
- # - Use read-only volumes where possible
33
- # - Avoid mounting sensitive host directories
34
- # - Validate environment variables for secrets
29
+ # Implement strict access controls and resource monitoring.
30
+ #
31
+ # == Parameters
32
+ #
33
+ # - **image**: Docker image to use (required)
34
+ # - **name**: Custom container name (optional)
35
+ # - **cmd**: Command to execute as space-separated string (optional)
36
+ # - **env**: Environment variables as comma-separated KEY=VALUE pairs (optional)
37
+ # - **exposed_ports**: Port exposure configuration as JSON object (optional)
38
+ # - **host_config**: Advanced host configuration as JSON object (optional)
35
39
  #
36
40
  # == Example Usage
37
41
  #
38
- # # Simple container run
39
- # RunContainer.call(
42
+ # # Simple container execution
43
+ # response = RunContainer.call(
40
44
  # server_context: context,
41
- # image: "nginx:latest",
42
- # name: "web-server"
45
+ # image: "alpine:latest",
46
+ # cmd: "echo 'Hello World'"
43
47
  # )
44
48
  #
45
- # # Advanced configuration with ports and volumes
46
- # RunContainer.call(
49
+ # # Web server with port binding
50
+ # response = RunContainer.call(
47
51
  # server_context: context,
48
- # image: "postgres:13",
49
- # name: "database",
50
- # env: "POSTGRES_PASSWORD=secret,POSTGRES_DB=myapp",
52
+ # image: "nginx:latest",
53
+ # name: "web-server",
54
+ # exposed_ports: {"80/tcp" => {}},
51
55
  # host_config: {
52
- # "PortBindings" => {"5432/tcp" => [{"HostPort" => "5432"}]},
53
- # "Binds" => ["/host/data:/var/lib/postgresql/data"]
56
+ # "PortBindings" => {"80/tcp" => [{"HostPort" => "8080"}]}
54
57
  # }
55
58
  # )
56
59
  #
57
- # @see CreateContainer
58
- # @see StartContainer
59
60
  # @see Docker::Container.create
60
61
  # @since 0.1.0
61
- class RunContainer < MCP::Tool
62
+ RUN_CONTAINER_DEFINITION = ToolForge.define(:run_container) do
62
63
  description 'Run a Docker container (create and start)'
63
64
 
64
- input_schema(
65
- properties: {
66
- image: {
67
- type: 'string',
65
+ param :image,
66
+ type: :string,
68
67
  description: 'Image name to use (e.g., "ubuntu:22.04")'
69
- },
70
- name: {
71
- type: 'string',
72
- description: 'Container name (optional)'
73
- },
74
- cmd: {
75
- type: 'string',
76
- description: 'Command to run as space-separated string (optional, e.g., "npm start" or "python app.py")'
77
- },
78
- env: {
79
- type: 'string',
80
- description: 'Environment variables as comma-separated KEY=VALUE pairs (optional)'
81
- },
82
- exposed_ports: {
83
- type: 'object',
84
- description: 'Exposed ports as {"port/protocol": {}} (optional)'
85
- },
86
- host_config: {
87
- type: 'object',
88
- description: 'Host configuration including port bindings, volumes, etc. (optional)'
89
- }
90
- },
91
- required: ['image']
92
- )
93
68
 
94
- def self.call(image:, server_context:, name: nil, cmd: nil, env: nil, exposed_ports: nil, host_config: nil)
69
+ param :name,
70
+ type: :string,
71
+ description: 'Container name (optional)',
72
+ required: false
73
+
74
+ param :cmd,
75
+ type: :string,
76
+ description: 'Command to run as space-separated string (optional, e.g., "npm start" or "python app.py")',
77
+ required: false
78
+
79
+ param :env,
80
+ type: :string,
81
+ description: 'Environment variables as comma-separated KEY=VALUE pairs (optional)',
82
+ required: false
83
+
84
+ param :exposed_ports,
85
+ type: :object,
86
+ description: 'Exposed ports as {"port/protocol": {}} (optional)',
87
+ required: false
88
+
89
+ param :host_config,
90
+ type: :object,
91
+ description: 'Host configuration including port bindings, volumes, etc. (optional)',
92
+ required: false
93
+
94
+ execute do |image:, name: nil, cmd: nil, env: nil, exposed_ports: nil, host_config: nil|
95
95
  config = { 'Image' => image }
96
96
  config['name'] = name if name
97
97
 
@@ -108,25 +108,15 @@ module DockerMCP
108
108
  container.start
109
109
  container_name = container.info['Names']&.first&.delete_prefix('/')
110
110
 
111
- MCP::Tool::Response.new([{
112
- type: 'text',
113
- text: "Container started successfully. ID: #{container.id}, Name: #{container_name}"
114
- }])
111
+ "Container started successfully. ID: #{container.id}, Name: #{container_name}"
115
112
  rescue Docker::Error::NotFoundError
116
- MCP::Tool::Response.new([{
117
- type: 'text',
118
- text: "Image #{image} not found"
119
- }])
113
+ "Image #{image} not found"
120
114
  rescue Docker::Error::ConflictError
121
- MCP::Tool::Response.new([{
122
- type: 'text',
123
- text: "Container with name #{name} already exists"
124
- }])
115
+ "Container with name #{name} already exists"
125
116
  rescue StandardError => e
126
- MCP::Tool::Response.new([{
127
- type: 'text',
128
- text: "Error running container: #{e.message}"
129
- }])
117
+ "Error running container: #{e.message}"
130
118
  end
131
119
  end
120
+
121
+ RunContainer = RUN_CONTAINER_DEFINITION.to_mcp_tool
132
122
  end
@@ -1,112 +1,72 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DockerMCP
4
- # MCP tool for starting existing Docker containers.
4
+ # MCP tool for starting Docker containers.
5
5
  #
6
- # This tool provides the ability to start Docker containers that have been
7
- # created but are currently stopped. It's the counterpart to StopContainer
8
- # and is commonly used after CreateContainer or when restarting stopped
9
- # containers.
6
+ # This tool starts a previously created Docker container that is currently
7
+ # in a "created" or "stopped" state. It transitions the container to a
8
+ # "running" state and begins executing the configured command or entrypoint.
10
9
  #
11
10
  # == Features
12
11
  #
13
- # - Start stopped containers by ID or name
14
- # - Simple and reliable container lifecycle management
15
- # - Comprehensive error handling
16
- # - Works with containers in any stopped state
12
+ # - Starts containers by ID or name
13
+ # - Supports both short and full container IDs
14
+ # - Works with custom container names
15
+ # - Provides clear success/failure feedback
16
+ # - Handles container state transitions
17
+ # - Preserves all container configuration
17
18
  #
18
19
  # == Security Considerations
19
20
  #
20
- # Starting containers can have security implications:
21
- # - Containers resume with their original configuration
22
- # - Services become accessible on mapped ports
23
- # - Processes resume execution with previous privileges
24
- # - Network connections are re-established
21
+ # Starting containers involves security implications:
22
+ # - **Process Execution**: Begins running container processes
23
+ # - **Resource Activation**: Activates CPU, memory, and I/O usage
24
+ # - **Network Activation**: Brings network interfaces online
25
+ # - **Service Exposure**: Makes configured services accessible
25
26
  #
26
- # Ensure containers are properly configured before starting:
27
- # - Review port mappings and network exposure
28
- # - Verify volume mounts and file permissions
29
- # - Check environment variables for sensitive data
30
- # - Validate container image integrity
27
+ # Ensure proper monitoring and access controls are in place.
28
+ #
29
+ # == Parameters
30
+ #
31
+ # - **id**: Container ID or name (required)
32
+ # - Accepts full container IDs
33
+ # - Accepts short container IDs (first 12+ characters)
34
+ # - Accepts custom container names
31
35
  #
32
36
  # == Example Usage
33
37
  #
34
- # # Start container by name
35
- # StartContainer.call(
38
+ # # Start by container name
39
+ # response = StartContainer.call(
36
40
  # server_context: context,
37
41
  # id: "web-server"
38
42
  # )
39
43
  #
40
- # # Start container by ID
41
- # StartContainer.call(
44
+ # # Start by container ID
45
+ # response = StartContainer.call(
42
46
  # server_context: context,
43
47
  # id: "a1b2c3d4e5f6"
44
48
  # )
45
49
  #
46
- # @see CreateContainer
47
- # @see StopContainer
48
- # @see RunContainer
49
50
  # @see Docker::Container#start
50
51
  # @since 0.1.0
51
- class StartContainer < MCP::Tool
52
+ START_CONTAINER_DEFINITION = ToolForge.define(:start_container) do
52
53
  description 'Start a Docker container'
53
54
 
54
- input_schema(
55
- properties: {
56
- id: {
57
- type: 'string',
55
+ param :id,
56
+ type: :string,
58
57
  description: 'Container ID or name'
59
- }
60
- },
61
- required: ['id']
62
- )
63
58
 
64
- # Start an existing Docker container.
65
- #
66
- # This method starts a container that is currently in a stopped state.
67
- # The container must already exist and be in a startable state. If the
68
- # container is already running, Docker will typically ignore the start
69
- # command without error.
70
- #
71
- # @param id [String] container ID (full or short) or container name
72
- # @param server_context [Object] MCP server context (unused but required)
73
- #
74
- # @return [MCP::Tool::Response] start operation results
75
- #
76
- # @raise [Docker::Error::NotFoundError] if container doesn't exist
77
- # @raise [StandardError] for other start failures
78
- #
79
- # @example Start by container name
80
- # response = StartContainer.call(
81
- # server_context: context,
82
- # id: "my-app-container"
83
- # )
84
- #
85
- # @example Start by container ID
86
- # response = StartContainer.call(
87
- # server_context: context,
88
- # id: "abc123def456"
89
- # )
90
- #
91
- # @see Docker::Container#start
92
- def self.call(id:, server_context:)
59
+ execute do |id:|
93
60
  container = Docker::Container.get(id)
94
61
  container.start
95
62
 
96
- MCP::Tool::Response.new([{
97
- type: 'text',
98
- text: "Container #{id} started successfully"
99
- }])
63
+ "Container #{id} started successfully"
100
64
  rescue Docker::Error::NotFoundError
101
- MCP::Tool::Response.new([{
102
- type: 'text',
103
- text: "Container #{id} not found"
104
- }])
65
+ "Container #{id} not found"
105
66
  rescue StandardError => e
106
- MCP::Tool::Response.new([{
107
- type: 'text',
108
- text: "Error starting container: #{e.message}"
109
- }])
67
+ "Error starting container: #{e.message}"
110
68
  end
111
69
  end
70
+
71
+ StartContainer = START_CONTAINER_DEFINITION.to_mcp_tool
112
72
  end
@@ -1,126 +1,81 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DockerMCP
4
- # MCP tool for stopping running Docker containers.
4
+ # MCP tool for stopping Docker containers.
5
5
  #
6
- # This tool provides the ability to gracefully stop running Docker containers
7
- # with configurable timeout handling. It sends a SIGTERM signal to the main
8
- # process and waits for the specified timeout before forcibly killing the
9
- # container with SIGKILL.
6
+ # This tool gracefully stops a running Docker container by sending a
7
+ # SIGTERM signal to the main process, allowing it to shut down cleanly.
8
+ # If the container doesn't stop within the specified timeout, it will
9
+ # be forcefully killed with SIGKILL.
10
10
  #
11
11
  # == Features
12
12
  #
13
13
  # - Graceful container shutdown with SIGTERM
14
- # - Configurable timeout before force kill
15
- # - Works with containers by ID or name
16
- # - Comprehensive error handling
17
- # - Safe for already stopped containers
18
- #
19
- # == Shutdown Process
20
- #
21
- # 1. Send SIGTERM to container's main process
22
- # 2. Wait for graceful shutdown up to timeout
23
- # 3. Send SIGKILL if container hasn't stopped
24
- # 4. Return success when container is stopped
14
+ # - Configurable timeout for forced termination
15
+ # - Supports container identification by ID or name
16
+ # - Handles both running and already-stopped containers
17
+ # - Provides clear feedback on operation status
18
+ # - Preserves container and data integrity
25
19
  #
26
20
  # == Security Considerations
27
21
  #
28
22
  # Stopping containers affects service availability:
29
- # - Services become unavailable immediately
30
- # - Network connections are terminated
31
- # - Data may be lost if not properly persisted
32
- # - Dependent services may be affected
23
+ # - **Service Disruption**: Terminates running services and processes
24
+ # - **Data Integrity**: May interrupt ongoing operations
25
+ # - **Resource Release**: Frees CPU, memory, and network resources
26
+ # - **State Preservation**: Maintains container state for future restart
27
+ #
28
+ # Coordinate container stops with dependent services and users.
33
29
  #
34
- # Best practices:
35
- # - Ensure data persistence before stopping
36
- # - Consider impact on dependent services
37
- # - Use appropriate timeout for graceful shutdown
38
- # - Monitor application logs during shutdown
30
+ # == Parameters
31
+ #
32
+ # - **id**: Container ID or name (required)
33
+ # - Accepts full container IDs
34
+ # - Accepts short container IDs (first 12+ characters)
35
+ # - Accepts custom container names
36
+ # - **timeout**: Seconds to wait before killing container (optional, default: 10)
39
37
  #
40
38
  # == Example Usage
41
39
  #
42
- # # Stop with default timeout (10 seconds)
43
- # StopContainer.call(
40
+ # # Stop with default timeout
41
+ # response = StopContainer.call(
44
42
  # server_context: context,
45
43
  # id: "web-server"
46
44
  # )
47
45
  #
48
46
  # # Stop with custom timeout
49
- # StopContainer.call(
47
+ # response = StopContainer.call(
50
48
  # server_context: context,
51
49
  # id: "database",
52
50
  # timeout: 30
53
51
  # )
54
52
  #
55
- # @see StartContainer
56
- # @see RemoveContainer
57
53
  # @see Docker::Container#stop
58
54
  # @since 0.1.0
59
- class StopContainer < MCP::Tool
55
+ STOP_CONTAINER_DEFINITION = ToolForge.define(:stop_container) do
60
56
  description 'Stop a Docker container'
61
57
 
62
- input_schema(
63
- properties: {
64
- id: {
65
- type: 'string',
58
+ param :id,
59
+ type: :string,
66
60
  description: 'Container ID or name'
67
- },
68
- timeout: {
69
- type: 'integer',
70
- description: 'Seconds to wait before killing the container (default: 10)'
71
- }
72
- },
73
- required: ['id']
74
- )
75
61
 
76
- # Stop a running Docker container gracefully.
77
- #
78
- # This method stops a container by sending SIGTERM to the main process
79
- # and waiting for the specified timeout before sending SIGKILL. The
80
- # operation is idempotent - stopping an already stopped container
81
- # typically succeeds without error.
82
- #
83
- # @param id [String] container ID (full or short) or container name
84
- # @param server_context [Object] MCP server context (unused but required)
85
- # @param timeout [Integer] seconds to wait before force killing (default: 10)
86
- #
87
- # @return [MCP::Tool::Response] stop operation results
88
- #
89
- # @raise [Docker::Error::NotFoundError] if container doesn't exist
90
- # @raise [StandardError] for other stop failures
91
- #
92
- # @example Stop with default timeout
93
- # response = StopContainer.call(
94
- # server_context: context,
95
- # id: "nginx-server"
96
- # )
97
- #
98
- # @example Stop database with longer timeout
99
- # response = StopContainer.call(
100
- # server_context: context,
101
- # id: "postgres-db",
102
- # timeout: 60 # Allow more time for DB shutdown
103
- # )
104
- #
105
- # @see Docker::Container#stop
106
- def self.call(id:, server_context:, timeout: 10)
62
+ param :timeout,
63
+ type: :integer,
64
+ description: 'Seconds to wait before killing the container (default: 10)',
65
+ required: false,
66
+ default: 10
67
+
68
+ execute do |id:, timeout: 10|
107
69
  container = Docker::Container.get(id)
108
70
  container.stop('timeout' => timeout)
109
71
 
110
- MCP::Tool::Response.new([{
111
- type: 'text',
112
- text: "Container #{id} stopped successfully"
113
- }])
72
+ "Container #{id} stopped successfully"
114
73
  rescue Docker::Error::NotFoundError
115
- MCP::Tool::Response.new([{
116
- type: 'text',
117
- text: "Container #{id} not found"
118
- }])
74
+ "Container #{id} not found"
119
75
  rescue StandardError => e
120
- MCP::Tool::Response.new([{
121
- type: 'text',
122
- text: "Error stopping container: #{e.message}"
123
- }])
76
+ "Error stopping container: #{e.message}"
124
77
  end
125
78
  end
79
+
80
+ StopContainer = STOP_CONTAINER_DEFINITION.to_mcp_tool
126
81
  end