SkillAgentSearch skills...

Drush MCP

MCP server for Drupal via Drush. Lets AI agents (Claude Code, Gemini CLI, etc.) interact with any Drupal 10+/11+ site - locally, over SSH, or via Docker - by executing Drush commands.

Install / Use

/learn @Bloomidea/Drush MCP
About this skill

Quality Score

0/100

Supported Platforms

Claude Code
Claude Desktop
Gemini CLI
Cursor

README

drush-mcp

MCP server for Drupal via Drush. Lets AI agents (Claude Code, Gemini CLI, etc.) interact with any Drupal 10+/11+ site - locally, over SSH, or via Docker - by executing Drush commands.

Two packages:

  • @bloomidea/drush-mcp (npm) - TypeScript MCP server
  • bloomidea/drush-mcp-bridge (Composer) - PHP Drush bridge for structured entity operations

Requirements

  • Node.js 18+
  • PHP 8.1+
  • Drush 12+ or 13+
  • Drupal 10+ or 11+

Quick Start

Install the MCP server:

npm install -g @bloomidea/drush-mcp

Install the Drush bridge on your Drupal site:

composer require bloomidea/drush-mcp-bridge

Add the agent skill so your agent knows how to work with Drupal entities, fields, and groups:

npx skills add Bloomidea/drush-mcp

"create a task in the Atrium group" / "list all published articles" / "check Drupal status" / "add a comment to node 123"

Works with Claude Code, Cursor, Codex, Gemini, Windsurf, and 37+ agents.

Register in Claude Code (local):

claude mcp add drupal -- drush-mcp --local --command "drush"

Or with SSH:

claude mcp add drupal -- drush-mcp --ssh --host example.com --user deploy --root /var/www/html

Configuration

Three methods: CLI flags, YAML config file, or environment variables.

CLI Flags

# Local
drush-mcp --local --command "ddev drush"

# SSH
drush-mcp --ssh --host example.com --user deploy --root /var/www/html

# Docker (static container name)
drush-mcp --docker --host example.com --user deploy --container mycontainer

# Docker (dynamic container lookup via filter)
drush-mcp --docker --host example.com --user deploy --container-filter "label=coolify.serviceName=myapp"

Config File

Create drush-mcp.yml in your project root or home directory, or pass --config path:

sites:
  production:
    transport: ssh
    host: example.com
    user: deploy
    root: /var/www/html
  local:
    transport: local
    command: ddev drush

defaults:
  timeout: 30

Drush Site Aliases

If your project already uses Drush site aliases (drush/sites/self.site.yml), the fields map directly to drush-mcp.yml:

| Drush alias field | drush-mcp equivalent | |-------------------|---------------------| | host | host | | user | user | | root | root | | uri | uri | | docker.service | container |

So a Drush alias like:

# drush/sites/self.site.yml
live:
  host: example.com
  user: deploy
  root: /var/www/html
  uri: https://example.com

Becomes:

# drush-mcp.yml
sites:
  live:
    transport: ssh
    host: example.com
    user: deploy
    root: /var/www/html
    uri: https://example.com

The main difference is that drush-mcp requires an explicit transport field and supports additional options like containerFilter for dynamic Docker container lookup.

Environment Variables

| Variable | Description | |----------|-------------| | DRUSH_MCP_TRANSPORT | local, ssh, or docker | | DRUSH_MCP_HOST | SSH/Docker host | | DRUSH_MCP_USER | SSH/Docker user | | DRUSH_MCP_ROOT | Drupal root path | | DRUSH_MCP_COMMAND | Local command (e.g. ddev drush) | | DRUSH_MCP_CONTAINER | Docker container name | | DRUSH_MCP_CONTAINER_FILTER | Docker filter for dynamic container lookup |

Dynamic Container Resolution

When using Docker-based hosting platforms (Coolify, Docker Swarm, etc.), container names change on every deploy. Use --container-filter or the containerFilter config option with any valid docker ps --filter expression:

# Coolify: match by service name label
drush-mcp --docker --host example.com --user root --container-filter "label=coolify.serviceName=myapp-web"

# Docker Compose: match by compose service
drush-mcp --docker --host example.com --user root --container-filter "label=com.docker.compose.service=web"

# Match by image name
drush-mcp --docker --host example.com --user root --container-filter "ancestor=myimage:latest"

Or in drush-mcp.yml:

sites:
  production:
    transport: docker
    host: example.com
    user: root
    containerFilter: "label=coolify.serviceName=myapp-web"

The container name is resolved fresh on every command via docker ps --filter, so it automatically picks up new containers after deploys.

Coolify Setup

If your Drupal site runs on Coolify:

  1. Set a service name in Coolify: Configuration > General > Name (e.g., atrium-web)
  2. Use the label filter:
    claude mcp add drupal-production -- drush-mcp \
      --docker --host your-server.com --user root \
      --container-filter "label=coolify.serviceName=atrium-web"
    
  3. Install the bridge on your Drupal site: composer require bloomidea/drush-mcp-bridge
  4. Deploy - the bridge commands are available immediately

Tools

All 17 tools are available regardless of transport:

| Tool | Description | |------|-------------| | drupal_entity_create | Create entity from JSON fields | | drupal_entity_read | Load entity by type + ID | | drupal_entity_update | Update fields on existing entity | | drupal_entity_list | Query entities with filters | | drupal_entity_delete | Delete entity by type + ID | | drupal_introspect | Discover entity types, bundles, fields | | drupal_cache_rebuild | Clear all caches | | drupal_watchdog | View recent log entries | | drupal_status | Site info (version, DB, PHP) | | drupal_config_get | Read configuration value | | drupal_config_set | Write configuration value | | drupal_field_info | Field definitions for entity type | | drupal_user_create | Create user account | | drupal_user_block | Block user account | | drupal_drush | Run any Drush command | | drupal_php_eval | Execute PHP code | | drupal_sql_query | Run SQL query |

Multi-site

When multiple sites are configured, all tools accept a site parameter to target a specific site. With a single site configured, it is resolved automatically.

Drush Bridge Commands

The bloomidea/drush-mcp-bridge Composer package provides structured entity commands auto-discovered by Drush:

| Command | Description | |---------|-------------| | mcp:entity-create | Create an entity | | mcp:entity-read | Read an entity | | mcp:entity-update | Update an entity | | mcp:entity-list | Query entities | | mcp:introspect | Introspect entity types and fields |

Install via Composer and Drush picks them up automatically - no additional registration needed.

Important: The bridge command class file must be named *DrushCommands.php (not *Commands.php) for Drush 12's PSR-4 discovery. The class also provides a static create() factory method for dependency injection from Drupal's service container.

How is this different from the Drupal MCP Server module?

The MCP Server Drupal module takes a different approach: it runs the MCP server inside Drupal as a PHP module, exposing tools via HTTP with OAuth 2.1 authentication.

drush-mcp runs outside Drupal as a standalone TypeScript process that executes Drush commands over shell, SSH, or Docker. This leads to several practical differences:

| | drush-mcp | MCP Server module | |---|---|---| | Install on Drupal | Optional Composer package (bridge) | Required module + Tool API + Simple OAuth | | Runs as | External Node.js process | Inside Drupal's PHP runtime | | Transport | STDIO (local shell, SSH, Docker) | HTTP endpoint (/_mcp) | | Auth model | SSH keys / shell access | OAuth 2.1 tokens | | Config | YAML file or CLI flags | Drupal config entities | | Tools | 17 fixed tools + arbitrary Drush | Extensible via Tool API plugins | | Multi-site | Built-in (one server, many sites) | One instance per Drupal site | | Works without Drupal changes | Yes (built-in Drush commands work without the bridge) | No (module must be installed and configured) |

When to use drush-mcp: You already have SSH/shell access to your sites, want to connect multiple Drupal sites through one MCP server, or prefer not to install additional Drupal modules. Good for development workflows with DDEV/Lando and for ops teams managing multiple sites.

When to use MCP Server module: You need fine-grained OAuth-based access control, want to expose custom Tool API plugins, or prefer keeping everything within Drupal's ecosystem.

Security

There are no artificial capability tiers. If you have Drush access to a site, you have access to all tools.

The security boundary is transport access: SSH keys, Docker socket permissions, or local process access. Bridge commands (mcp:entity-*) enforce Drupal's entity access checks. Power tools (drupal_drush, drupal_php_eval, drupal_sql_query) do not - treat them accordingly.

License

MIT

View on GitHub
GitHub Stars3
CategoryDevelopment
Updated2d ago
Forks1

Languages

TypeScript

Security Score

90/100

Audited on Mar 21, 2026

No findings