Skip to main content
Glama

MCP Dockhand

CI License: MIT Docker

An MCP (Model Context Protocol) server that exposes the Dockhand API as MCP tools. Manage your entire Docker infrastructure through AI assistants.

API coverage: 88.7% of in-scope Dockhand endpoints (282/318) have an MCP tool — see docs/coverage.md for the full, auto-updated breakdown by area.

Dockhand is a Docker management server that connects to multiple Docker hosts via Hawser agents. This MCP server provides full programmatic access to all Dockhand features.

Features

  • 280+ MCP Tools covering the Dockhand API — see docs/coverage.md for exact, auto-updated coverage

  • Streamable HTTP Transport (MCP Spec 2025-03-26) for Docker container hosting

  • Session-based Auth with auto-relogin on 401

  • SSE Support for deploy operations (start, stop, down, restart)

  • Environment Filter enforced on all container/stack/image/network/volume endpoints

  • Docker Ready with multi-stage build, non-root user, and health checks

Related MCP server: Portainer MCP Server

Quick Start

docker run -d \
  --name mcp-dockhand \
  -p 8080:8080 \
  -e DOCKHAND_URL=https://your-dockhand-server.com \
  -e DOCKHAND_USERNAME=your-username \
  -e DOCKHAND_PASSWORD=your-password \
  ghcr.io/strausmann/mcp-dockhand:latest

Docker Compose

services:
  mcp-dockhand:
    image: ghcr.io/strausmann/mcp-dockhand:latest
    container_name: mcp-dockhand
    restart: unless-stopped
    ports:
      - "8080:8080"
    environment:
      - DOCKHAND_URL=https://your-dockhand-server.com
      - DOCKHAND_USERNAME=your-username
      - DOCKHAND_PASSWORD=your-password

From Source

git clone https://github.com/strausmann/mcp-dockhand.git
cd mcp-dockhand
npm install
npm run build
DOCKHAND_URL=https://your-server.com DOCKHAND_USERNAME=admin DOCKHAND_PASSWORD=secret npm start

Configuration

Variable

Required

Default

Description

DOCKHAND_URL

Yes

-

Dockhand server URL

DOCKHAND_USERNAME

Yes

-

Dockhand username

DOCKHAND_PASSWORD

Yes

-

Dockhand password

MCP_PORT

No

8080

Port for the MCP server

MCP_SESSION_TTL_SECONDS

No

1800

Inactivity timeout before a retained MCP session is expired

MCP_SESSION_CLEANUP_INTERVAL_SECONDS

No

300

Interval for removing expired sessions (clamped to the session TTL)

MCP_MAX_SESSIONS

No

0

Maximum retained sessions; 0 keeps the existing unlimited behavior

MCP_HOST

No

0.0.0.0

Listen address. Kept as the wildcard address by default so the published Docker port (-p 8080:8080 / docker-compose.yml) keeps working; see Securing the transport for the recommended way to protect the endpoint instead of binding loopback-only

MCP_ALLOWED_HOSTS

No

(unset — Host check disabled)

Comma-separated Host header allowlist for /mcp (DNS-rebinding protection). Opt-in: unset means no Host check at all (pre-existing behavior, so existing deployments aren't broken by an update). Recommended once you set it up — see Securing the transport

MCP_ALLOWED_ORIGINS

No

(unset — Origin check disabled)

Comma-separated Origin header allowlist for /mcp. Opt-in, same as above. Only enforced when a caller actually sends an Origin header at all (non-browser MCP clients typically don't)

MCP_AUTH_TOKEN

No

(unset — endpoint unauthenticated)

Shared secret required as Authorization: Bearer <token> on every /mcp request. Opt-in; recommended once the endpoint is reachable beyond your own loopback — see Securing the transport

LOG_LEVEL

No

info

Log level

Securing the transport

/mcp binds 0.0.0.0:8080 by default (see MCP_HOST above), and out of the box — with none of MCP_ALLOWED_HOSTS, MCP_ALLOWED_ORIGINS, or MCP_AUTH_TOKEN set — it accepts any request with no Host/Origin check and no authentication. This is the same behavior mcp-dockhand has always had, kept as the default deliberately: enabling a check by default would reject requests from any client that doesn't reach the server as localhost/127.0.0.1 (a LAN IP, a reverse proxy, a Docker network alias), breaking existing deployments on a routine update.

You should turn this on once /mcp is reachable beyond your own machine's loopback interface — the server holds one Dockhand admin credential and every tool call acts with that identity, so anyone who can open an MCP session controls Docker (container exec, host bind-mounts via create_container, file read/write, stored git credentials). With no protection configured, the server logs a [security] WARNING at startup as a reminder. Three independent, all-opt-in layers are available:

  1. Host allowlist (MCP_ALLOWED_HOSTS). Once set to a non-empty value, every request to /mcpPOST, GET, and DELETE — is rejected with 403 unless its Host header matches the allowlist. This is the primary defense against DNS-rebinding: a malicious web page cannot make the operator's browser reach the server under a Host value the allowlist accepts. Set it to however your client actually reaches the server — localhost:8080/127.0.0.1:8080 for the documented local setup, or, if you connect directly by address rather than through localhost (including the mcp-proxy remote-server setup below), the exact host:port your client sends, e.g. 100.100.50.40:8222. Get this wrong and every request is rejected with 403 Invalid Host header — check the message, it echoes the Host value it saw.

  2. Origin allowlist (MCP_ALLOWED_ORIGINS). Once set, any request that does send an Origin header not in the list is rejected with 403. A missing Origin header always passes (the SDK's own MCP client and most non-browser tooling never send one), so this is only useful if a browser-based client talks to /mcp directly; the Host allowlist above is what actually stops DNS-rebinding.

  3. Bearer token (MCP_AUTH_TOKEN). Once set, every /mcp request must carry Authorization: Bearer <token> or is rejected with 401; the comparison is constant-time. Recommended alongside the Host allowlist for any deployment reachable from more than the operator's own machine.

# .env — recommended configuration once /mcp is reachable beyond loopback
MCP_ALLOWED_HOSTS=dock-mcp.internal.example.com
# or, connecting directly by address instead of a hostname:
#MCP_ALLOWED_HOSTS=100.100.50.40:8222
MCP_AUTH_TOKEN=<a long random secret, e.g. `openssl rand -hex 32`>

MCP Client Configuration

Claude Desktop / Claude Code

Add to your MCP settings:

{
  "mcpServers": {
    "dockhand": {
      "url": "http://localhost:8080/mcp"
    }
  }
}

If the server enforces a bearer token (MCP_AUTH_TOKEN set — see Securing the transport), the client must send it as an Authorization header, or every request is rejected with 401. In Claude Code's .mcp.json, add a headers block — reference an environment variable so the token never lives in the (often version-controlled) config file:

{
  "mcpServers": {
    "dockhand": {
      "type": "http",
      "url": "http://your-server:8080/mcp",
      "headers": { "Authorization": "Bearer ${DOCKHAND_MCP_TOKEN}" }
    }
  }
}

Send the token only over an encrypted transport. A bearer over plain http:// on a shared network can be sniffed — terminate TLS at a reverse proxy, or reach the server over a WireGuard/Tailscale/VPN link (the app-layer HTTP is then encrypted by the tunnel).

Export DOCKHAND_MCP_TOKEN in the environment Claude Code is launched from (e.g. from a gitignored .env you source before starting). The Host/host:port you connect to must also be in the server's MCP_ALLOWED_HOSTS if that allowlist is set. For Claude Desktop (native config has no headers field), pass the token through the mcp-proxy workaround below — mcp-proxy forwards an Authorization header via its own environment/args.

Claude Desktop with a remote server (mcp-proxy)

Claude Desktop can fail to connect to a remote mcp-dockhand server (not localhost) using the native "url" config above, even though the endpoint itself is reachable. The symptom is a generic "not a valid MCP server" error in Claude Desktop, while a plain browser/curl request to the same URL correctly returns {"error":"Invalid or missing session ID"}. This is a known limitation of Claude Desktop with remote Streamable HTTP servers, not a mcp-dockhand bug.

Workaround: wrap the connection with mcp-proxy, which translates Streamable HTTP to stdio — a transport Claude Desktop handles reliably:

{
  "mcpServers": {
    "dockhand": {
      "command": "/path/to/mcp-proxy",
      "args": ["--transport", "streamablehttp", "http://your-server:8080/mcp"]
    }
  }
}

All tools load and work correctly through the proxy. Thanks to @deadrubberboy for reporting this and sharing the workaround (#90).

Tool Reference

Containers (27 tools)

Tool

Description

list_containers

List all containers in an environment

get_container

Get container details

inspect_container

Docker inspect (full details)

get_container_logs

Get container logs

get_container_stats

Get resource usage stats

get_container_top

Get running processes

start_container

Start a container

stop_container

Stop a container

restart_container

Restart a container

pause_container

Pause a container

unpause_container

Unpause a container

rename_container

Rename a container

update_container

Update container settings

create_container

Create a new container

get_container_shells

List available shells

exec_container

Create a terminal exec session (execId + WS connectionInfo); does NOT run a one-shot command or return output — no such endpoint exists in the Dockhand API

list_container_files

Browse files inside container

get_container_file_content

Read file from container

create_container_file

Create an empty file or directory in container (no content — use write_container_file_content for that)

delete_container_file

Delete file in container

rename_container_file

Rename file in container

chmod_container_file

Change file permissions

check_container_updates

Check for image updates

get_pending_updates

Get pending updates

batch_update_containers

Batch update containers

execute_batch

Run a bulk lifecycle operation (start/stop/restart/remove/etc.) across containers, images, volumes, networks, or stacks

get_container_sizes

Get container disk sizes

get_containers_stats

Get aggregated stats

Stacks (21 tools)

Tool

Description

list_stacks

List all stacks

get_stack

Get stack details

create_stack

Create and optionally deploy a stack

start_stack

Start a stack (compose up)

stop_stack

Stop a stack (compose stop)

restart_stack

Restart a stack

down_stack

Take down a stack (compose down)

delete_stack

Delete a stack

get_stack_compose

Read compose file

update_stack_compose

Update compose file

get_stack_env

Read environment variables

update_stack_env

Update environment variables (merge by default — safe for partial updates; use mode="replace" to overwrite all)

get_stack_env_raw

Read raw .env file

validate_stack_env

Validate env variables

scan_stacks

Scan filesystem for stacks

adopt_stack

Adopt an untracked stack

relocate_stack

Move stack to new path

get_stack_sources

Get stack sources

get_stack_base_path

Get base path

get_stack_path_hints

Get path suggestions

validate_stack_path

Validate a stack path

Images (9 tools)

Tool

Description

list_images

List all images

get_image

Get image details

get_image_history

Get image layer history

tag_image

Tag an image

remove_image

Remove an image

pull_image

Pull an image

push_image

Push an image

scan_image

Vulnerability scan (Trivy/Grype)

export_image

Export image as tarball

Environments (18 tools)

Tool

Description

list_environments

List all environments

get_environment

Get environment details

create_environment

Create an environment

update_environment

Update an environment

delete_environment

Delete an environment

test_environment

Test connection

test_environment_connection

Test without saving

detect_docker_socket

Auto-detect socket

get_environment_timezone

Get timezone

set_environment_timezone

Set timezone

get_environment_update_check

Get update-check settings

set_environment_update_check

Set update-check settings

get_environment_image_prune

Get image prune settings

set_environment_image_prune

Set image prune settings

list_environment_notifications

List notifications

create_environment_notification

Create notification

get_environment_notification

Get notification

delete_environment_notification

Delete notification

Networks (7 tools)

Tool

Description

list_networks

List all networks

get_network

Get network details

inspect_network

Inspect network

create_network

Create a network

remove_network

Remove a network

connect_container_to_network

Connect container

disconnect_container_from_network

Disconnect container

Volumes (9 tools)

Tool

Description

list_volumes

List all volumes

get_volume

Get volume details

inspect_volume

Inspect volume

browse_volume

Browse files in volume

get_volume_file_content

Read file from volume

release_volume_browse

Release browse session

clone_volume

Clone a volume

export_volume

Export volume

remove_volume

Remove volume (destructive)

Git Stacks (15 tools)

Tool

Description

list_git_stacks

List Git-based stacks

get_git_stack

Get Git stack details

deploy_git_stack

Deploy a Git stack (SSE)

sync_git_stack

Sync with remote repo

test_git_stack

Test Git connection

get_git_stack_env_files

Get env files

trigger_git_webhook

Trigger webhook

get_git_webhook

Get webhook details

list_git_credentials

List Git credentials

create_git_credential

Create Git credential

get_git_credential

Get credential details

update_git_credential

Update credential

delete_git_credential

Delete credential

list_git_repositories

List Git repositories

create_git_repository

Create repository config

Dashboard & Activity (8 tools)

Tool

Description

get_dashboard_stats

Get dashboard statistics

get_dashboard_preferences

Get display preferences

set_dashboard_preferences

Set display preferences

get_activity_feed

Get activity feed

get_container_activity

Container activity

get_activity_events

Activity events

get_activity_stats

Activity statistics

get_merged_logs

Merged logs from containers

Auth & Hawser (12 tools)

Tool

Description

get_auth_session

Check session status

get_auth_providers

List auth providers

get_auth_settings

Get auth settings

create_oidc_provider

Create OIDC provider

get_oidc_provider

Get OIDC provider

test_oidc_provider

Test OIDC provider

create_ldap_provider

Create LDAP provider

get_ldap_provider

Get LDAP provider

test_ldap_provider

Test LDAP provider

list_hawser_tokens

List Hawser tokens

create_hawser_token

Create Hawser token

revoke_hawser_token

Revoke Hawser token

Audit (4 tools)

Tool

Description

get_audit_log

Get audit log

get_audit_events

Get audit event types

get_audit_users

Audit data by user

export_audit_log

Export audit log

Notifications (8 tools)

Tool

Description

list_notifications

List notifications

create_notification

Create notification

get_notification

Get notification

update_notification

Update notification

delete_notification

Delete notification

test_notification

Test notification

test_notification_config

Test without saving

trigger_test_notification

Trigger a real test event for a given event type + payload

Registries (10 tools)

Tool

Description

list_registries

List registries

create_registry

Add registry

get_registry

Get registry details

update_registry

Update registry

delete_registry

Delete registry

set_default_registry

Set as default

search_registry

Search registry

get_registry_catalog

Get catalog

get_registry_image

Get image from registry

get_registry_tags

Get image tags

System & Settings (19 tools)

Tool

Description

health_check

Server health

health_check_database

Database health

get_host_info

Host information

get_system_info

System information

get_system_disk

Disk usage

list_system_files

List system files

get_system_file_content

Read system file

get_changelog

Changelog

get_dependencies

Dependencies

get_general_settings

General settings

update_general_settings

Update settings

get_theme_settings

Theme settings

update_theme_settings

Update theme

get_scanner_settings

Scanner settings

update_scanner_settings

Update scanner

get_license

License info

activate_license

Activate license by name and key

get_prometheus_metrics

Prometheus metrics

prune_all

Prune all resources

Users, Roles & Preferences (20 tools)

Tool

Description

list_users

List users

create_user

Create user

get_user

Get user details

update_user

Update user

delete_user

Delete user

get_user_mfa_status

MFA status

enable_user_mfa

Enable MFA

disable_user_mfa

Disable MFA

get_user_roles

Get user roles

add_user_role

Assign one role to a user (no bulk-replace)

remove_user_role

Unassign one role from a user

list_roles

List roles

create_role

Create role with name + permissions object

get_role

Get role

update_role

Update role

delete_role

Delete role

get_profile

Get own profile

update_profile

Update own profile

get_favorites

Get favorites

set_favorites

Set favorites

list_config_sets

List config sets

Schedules (9 tools)

Tool

Description

list_schedules

List schedules

get_schedule_settings

Get settings

update_schedule_settings

Update settings

get_schedule_executions

Execution history

get_schedule_execution

Execution details

get_schedule

Get schedule

run_schedule_now

Run immediately

toggle_schedule

Enable/disable

toggle_system_schedule

Toggle system schedule

Auto-Update (3 tools)

Tool

Description

get_auto_update_settings

Get all auto-update settings

get_container_auto_update

Get container auto-update

set_container_auto_update

Set auto-update policy

Self-help / meta tools (6 tools)

Diagnostics for this MCP server itself, distinct from the Dockhand API tools above — useful for a client or operator asking "is this server healthy and correctly configured?" rather than "is Dockhand healthy?". None of these six take any input arguments, and none of them wrap a single Dockhand endpoint the way the tables above do (get_tool_manifest and get_runtime_stats call no Dockhand endpoint at all) — see src/tools/meta.ts.

Tool

Description

get_server_info

This server's own version, git SHA, build date, uptime, MCP protocol version, and the Dockhand URL/server version it's connected to

check_for_update

Compares this server's running version against the latest GitHub release (TTL-cached)

get_tool_manifest

Lists every registered tool with its Dockhand {method, path}, plus the pinned Dockhand OpenAPI commit/version this server's tools were generated against

self_check

End-to-end diagnostic: Dockhand reachability, credential validity, and a live, per-environment reachability check (POST /api/environments/{id}/test, run in parallel with a 5s per-environment timeout) plus Hawser-agent-connected status, in one call

validate_config

Checks that the required DOCKHAND_URL/DOCKHAND_USERNAME/DOCKHAND_PASSWORD env vars are present and that they authenticate successfully

get_runtime_stats

In-process counters for this server: total/per-tool call and error counts, uptime, and the last error's tool/message/timestamp

Notes:

  • check_for_update needs outbound network access to api.github.com (GitHub's releases API) — it will degrade to updateAvailable: null rather than fail if that's unreachable.

  • No meta tool exposes any secret value. validate_config reports only whether the required env vars are present (booleans) and whether they authenticate (a boolean + the raw HTTP status code, e.g. 200/401) — never the credential values themselves. self_check reports auth validity the same way. get_runtime_stats' lastError carries only a tool name, an error message, and a timestamp — never call arguments or response payloads. That error message is not fully opaque, though: for a failed Dockhand API call it can embed a slice of the upstream HTTP status and response body (via DockhandClient's own Dockhand API error: ... returned <status>: <body> message), and it is echoed to whichever MCP client next calls get_runtime_stats — not necessarily the one that hit the original error. It never includes request bodies or credential values, and it is truncated to 500 characters (with an ellipsis marker) before being stored, so an oversized upstream response is never echoed wholesale.

Important Notes

update_stack_env — Merge vs Replace Semantics

The Dockhand REST endpoint PUT /api/stacks/{name}/env has replace-semantics: submitting a partial list of variables silently deletes all other variables from the stack. A single-variable update would wipe everything else.

To prevent accidental data loss, this MCP tool defaults to merge mode:

  1. It fetches the current variable list via GET /api/stacks/{name}/env.

  2. It merges the incoming variables by key (new values overwrite existing ones on key collision).

  3. It writes the full combined list back via PUT.

# Safe partial update — only MY_VAR changes, all others preserved
update_stack_env(environmentId=1, name="my-stack", variables=[{key: "MY_VAR", value: "new"}])

# Explicit full replacement — all other variables are deleted
update_stack_env(environmentId=1, name="my-stack", variables=[...], mode="replace")

Use mode="replace" only when you intentionally want to replace the entire variable set.

Environment ID is Required

Most Docker resource endpoints (containers, stacks, images, networks, volumes) require an environmentId parameter. This maps to the ?env=<id> query parameter in the Dockhand API. Without it, endpoints return empty arrays.

SSE Responses

Deploy operations (start, stop, down, restart, compose update with restart) return Server-Sent Events. The MCP server automatically parses these and returns the final result.

Authentication

The server uses session-based cookie authentication. It automatically:

  • Logs in on first request

  • Stores the session cookie in memory

  • Re-authenticates on 401 responses

  • Handles session timeout (24h)

Development

# Install dependencies
npm install

# Type check
npm run typecheck

# Build
npm run build

# Run in development mode
DOCKHAND_URL=https://your-server.com \
DOCKHAND_USERNAME=admin \
DOCKHAND_PASSWORD=secret \
npm run dev

Linting

There is currently no npm run lint script. typescript-eslint (the only maintained ESLint/TypeScript integration) does not yet support the pinned typescript@^7.0.2 devDependency — it refuses to run at all against TS 7.0 (hard runtime error, not just a peer-dependency warning): see typescript-eslint#10940. Re-add eslint + typescript-eslint as devDependencies once that's resolved upstream — tsc --noEmit (via npm run typecheck) is the only static check enforced today.

License

MIT

A
license - permissive license
-
quality - not tested
A
maintenance

Maintenance

Maintainers
13hResponse time
5dRelease cycle
25Releases (12mo)
Commit activity
Issues opened vs closed

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    -
    quality
    D
    maintenance
    Enables AI assistants to manage Docker containers, deploy stacks, and monitor services across multiple Docker hosts from one centralized location. Supports container lifecycle management, Docker Compose operations, and infrastructure orchestration through natural language commands.
    6
    MIT
  • F
    license
    -
    quality
    D
    maintenance
    Enables AI assistants to manage Docker containers, stacks, images, volumes, and networks through Portainer's API. Supports listing resources, viewing logs, and performing container operations with configurable write permissions.
    1
  • A
    license
    -
    quality
    F
    maintenance
    Exposes the Dockhand Docker management API as tools for LLMs, enabling container, stack, image, volume, and network management via natural language.
    3
    MIT
  • A
    license
    B
    quality
    D
    maintenance
    Enables AI assistants to manage Docker containers, images, volumes, networks, and compose projects through Arcane's API. Provides comprehensive tools for environment, container, image, volume, network, project, and system operations.
    52
    15
    MIT

View all related MCP servers

Related MCP Connectors

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/strausmann/mcp-dockhand'

If you have feedback or need assistance with the MCP directory API, please join our Discord server