This project is not covered by Drupal’s security advisory policy.

Droost gives AI coding agents structured, first-hand knowledge of this Drupal application. It is the Drupal analog of Laravel Boost: a developer-acceleration toolkit that exposes the application and codebase itself - versions, the entity and configuration model, routes, services, logs, database schema, and a set of guarded operations - as Model Context Protocol (MCP) tools.

It is built entirely on MCP Server and the official mcp/sdk: MCP Server provides the runtime and the #[Tool] plugin system; Droost provides the tools.

The problem it solves

An AI agent asked to write Drupal code is usually working blind. It does not know which modules and versions are installed, what entity types and fields exist, which services it should inject instead of calling \Drupal::, what the routes and permissions are, or whether the last request errored. It guesses - and guesses age badly as a project evolves.

The common workaround is to tell the agent to run Drush and parse the free-text output. Droost takes a different path: it exposes that information as explicit, schema-described tools that return structured JSON. The agent gets reliable, version-correct answers about the real site, and capabilities become discoverable and individually gateable rather than hidden behind a shell.

Features

  • 22 MCP tools spanning read-only introspection and guarded operations, each returning a uniform {success, message, data} envelope.
  • Read-only by default. State-changing tools are registered so an agent can discover them, but refuse to act until explicitly opted in.
  • A real security model (see below): per-flag gating, secret redaction in config reads, and a read-only SQL guard.
  • Bundled, version-stamped guidelines - composable Drupal conventions plus on-demand deep-dive topics, served over MCP and writable to AGENTS.md.
  • On-disk documentation search across core, contrib, and custom modules, so answers match the exact versions installed.
  • One-command adoption with drush droost:install (auto-detects DDEV).

Requirements

  • Drupal 10.3+ or 11; PHP 8.3+.
  • MCP Server (drupal/mcp_server) ^2.0@alpha - provides the MCP runtime and the drush mcp:server command.
  • Drush 12+ - Droost is served over STDIO by Drush and ships the droost:install command.
  • The core Database Logging (dblog) module - optional; only the droost_logs and droost_last_error tools depend on it, and they degrade gracefully when it is absent.

Installation

Install with Composer (recommended) so the MCP Server dependency is resolved:

composer require drupal/droost
drush en droost -y

Note: MCP Server is currently an alpha release, so a site on the default stable minimum-stability cannot resolve it without opting in. Either require the dependency explicitly first:

composer require drupal/mcp_server:^2.0@alpha
composer require drupal/droost

or set "minimum-stability": "alpha" with "prefer-stable": true in your root composer.json before requiring Droost.

Usage

1. Wire up your editor

drush droost:install

droost:install (alias droost) registers the Droost MCP server in the project's .mcp.json, writes the bundled guidelines to AGENTS.md, and prints ready-to-paste registration commands for common clients. It auto-detects DDEV and, in that case, uses ddev drush mcp:server as the launch command.

Options:

Option Values Effect
--launcher auto (default), ddev, drush, vendor How editors launch the server. auto picks ddev when a .ddev directory is present, otherwise drush.
--guidelines true (default) / false Whether to also (re)write the Droost block in AGENTS.md.

The command prints per-editor hints, for example:

Claude Code:  claude mcp add -s local -t stdio droost ddev drush mcp:server
Gemini CLI:   gemini mcp add -s project -t stdio droost ddev drush mcp:server
Codex:        codex mcp add droost -- ddev drush mcp:server

2. Serve the tools

MCP Server serves Droost's enabled tools over STDIO with drush mcp:server (run as ddev drush mcp:server under DDEV); your editor launches this and speaks JSON-RPC to it. After enabling or adding tools, rebuild caches and reload MCP servers in your editor:

drush cache:rebuild

A quick manual handshake, list, and call check:

printf '%s\n' \
  '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"c","version":"0"}}}' \
  '{"jsonrpc":"2.0","method":"notifications/initialized"}' \
  '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
  | drush mcp:server

Configuration

Droost ships read-only. State-changing tools are gated by two boolean flags in droost.settings, both off by default. Configure them at Admin > Configuration > Development > Droost (/admin/config/development/droost, requires the Administer Droost permission), or from the CLI:

drush config:set droost.settings allow_destructive true
drush config:set droost.settings allow_eval true
Flag Unlocks Default
allow_destructive droost_config_set, droost_module_install, droost_module_uninstall false
allow_eval droost_eval (arbitrary PHP - the Tinker analog) false

Each flag is independent: enabling destructive tools does not enable the eval tool.

Tool reference

Every tool returns {success, message, data}. The columns mirror the MCP annotations Droost sets on each tool.

Introspection (read-only)

Tool Purpose Key arguments
droost_app_info Drupal/PHP versions, install profile, DB engine, themes, and enabled contrib/custom modules with versions. None
droost_entities Entity types > bundles > field definitions. entity_type_id, bundle
droost_entity_load One entity's raw field values, loaded by ID or UUID. entity_type, id / uuid, fields
droost_config_get Read a config object's values (sensitive keys redacted) or list config names. name, prefix, limit
droost_config_status Configuration drift between active config and the sync directory. name, limit
droost_db_schema List tables, or one table's columns. table, limit
droost_db_query Run a single read-only SQL statement (SELECT / WITH / SHOW / DESCRIBE / EXPLAIN only). query, limit
droost_routes Routes with path, methods, controller, and access requirements. pattern, limit
droost_services Registered dependency-injection service IDs. pattern, limit
droost_logs Recent dblog entries, newest first. Requires dblog. type, severity, limit
droost_last_error The most recent error-level dblog entries. Requires dblog. limit
droost_url Resolve an internal path or a route (+ parameters) to an absolute URL. path, route, parameters
droost_permissions Permission catalog and role grants; or one role's permissions; or which roles grant a permission. role, permission
droost_update_status Pending hook_update_N and post-update functions ("do I need drush updatedb?"). None
droost_guidelines Curated Drupal guidelines: core conventions + a list of deep-dive topics, or one topic. topic
droost_module_docs Read or search README / *.md / *.api.php / docs/*.md / info descriptions across modules. query, module, file, scope, limit

Operations

Tool Purpose Gating
droost_cache_rebuild Rebuild all caches (drush cr). Ungated (safe, idempotent).
droost_cron_run Run cron: process queues and scheduled maintenance. Ungated (safe).
droost_config_set Set a single value on an existing config object. allow_destructive
droost_module_install Install (enable) modules and their dependencies. allow_destructive
droost_module_uninstall Uninstall (disable) modules; deletes their config and data. allow_destructive
droost_eval Execute PHP in the bootstrapped Drupal context (the Tinker analog). High risk. allow_eval

Security model

Droost is explicit about runtime safety:

  • Read-only by default. Every tool sets accurate MCP annotations (readOnly, destructive, idempotent, openWorld). These are advisory hints to the client; the real gate is enforced in code.
  • Opt-in gating. State-changing tools are registered (so an agent can discover them) but call a gate at the top of execution and refuse unless the relevant droost.settings flag is on. The refusal message explains exactly how to enable the tool.
  • Why a config flag, not a permission. Over STDIO there is no web session or authenticated user, so a Drupal permission cannot gate the call; the config flag does. The module still ships the matching use droost * permissions for the case where MCP Server is additionally exposed over HTTP at /_mcp - in which case destructive tools should remain STDIO-only.
  • Secret redaction. droost_config_get recursively redacts values whose keys look sensitive (*pass*, *secret*, *token*, *credential*, *hmac*, *salt*, key / *_key, API/client keys), so credentials never reach the agent. It never returns settings.php values.
  • Read-only SQL guard. droost_db_query accepts a single statement, allows only SELECT / WITH / SHOW / DESCRIBE / EXPLAIN, and rejects write or DDL keywords, stacked statements, and file functions (INTO OUTFILE, LOAD_FILE).
  • Eval is doubly gated. droost_eval is off by default behind its own allow_eval flag (separate from allow_destructive) and is documented as trusted-local-development only.

Guidelines

Droost bundles composable Drupal know-how under guidelines/:

  • Core conventions (guidelines/core/) - always-on guidance an agent should follow on every change: dependency injection over \Drupal::, hooks, the entity and config APIs, render arrays, plugins, coding standards, and security.
  • Deep-dive topics (guidelines/topics/) - pulled on demand by name: entity-api, plugins, mcp-tools, and security.

The droost_guidelines tool serves the core conventions (version-stamped to the live Drupal and PHP versions) plus the topic list; pass topic for one topic's full content. drush droost:install writes the same composed guidelines into a delimited block in AGENTS.md so non-MCP tooling can use them too.

Submodules

  • droost_devel (requires Devel) - droost_generate creates content, users, or terms via devel_generate. Destructive; gated by allow_destructive.
  • droost_profiler (self-contained, no external dependency) - a lightweight request profiler. Opt-in middleware records DB queries, wall-clock time, and peak memory per HTTP request; droost_profile_list and droost_profile read them back over MCP, surfacing the slowest and duplicate queries.

Extending Droost

A Droost tool is a plugin class under src/Plugin/Tool/. To add one:

  1. Carry the #[Tool] attribute (id, label, description, inputSchema, outputSchema, and the readOnly / destructive / idempotent / openWorld annotations).
  2. Extend DroostToolBase (or DestructiveToolBase for state-changing tools). The base classes provide the succeed() / fail() envelope helpers, small typed-argument coercion helpers (stringArg(), intArg(), stringListArg(), toString(), toInt()), the enabled => TRUE default so the tool auto-registers, and - for DestructiveToolBase - the gate() check.
  3. Implement execute(array $arguments, ClientGateway $gateway): mixed and return a {success, message, data} array.
  4. Inject services through create() rather than calling \Drupal::.

A minimal read-only tool:

#[Tool(
  id: 'droost_example',
  label: new TranslatableMarkup('Droost: Example'),
  description: new TranslatableMarkup('Returns a greeting. Read-only.'),
  inputSchema: ['type' => 'object'],
  readOnly: TRUE,
  destructive: FALSE,
  idempotent: TRUE,
  openWorld: FALSE,
)]
final class Example extends DroostToolBase {

  public function execute(array $arguments, ClientGateway $gateway): mixed {
    return $this->succeed('Hello from Droost.', ['greeting' => 'hello']);
  }

}

A state-changing tool calls the gate first:

public function execute(array $arguments, ClientGateway $gateway): mixed {
  if (($blocked = $this->gate()) !== NULL) {
    return $blocked;
  }
  // perform the guarded operation
}

See src/Plugin/Tool/AppInfo.php for a complete read-only example, src/Plugin/Tool/ConfigSet.php for a gated one, and the mcp-tools guideline topic for the full contract. Run drush cache:rebuild and the tool appears in tools/list.

Project information

Releases