droost
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). - PHPStan-max clean, kernel-tested, and verified over STDIO.
Requirements
- Drupal 10.3+ or 11; PHP 8.3+.
-
MCP Server (
drupal/mcp_server)^2.0@alphaâ provides the MCP runtime and thedrush mcp:servercommand. -
Drush 12+ â Droost is served over STDIO by Drush and ships the
droost:installcommand. - The core Database Logging (
dblog) module â optional; only thedroost_logsanddroost_last_errortools 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 â 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.
â
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?").
â
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.settingsflag 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_getrecursively 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 returnssettings.phpvalues. -
Read-only SQL guard.
droost_db_queryaccepts a single statement, allows onlySELECT/WITH/SHOW/DESCRIBE/EXPLAIN, and rejects write or DDL keywords, stacked statements, and file functions (INTO OUTFILE,LOAD_FILE). -
Eval is doubly gated.
droost_evalis off by default behind its ownallow_evalflag (separate fromallow_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,security, andtesting.
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_generatecreates test content, users, or terms viadevel_generate. Destructive; gated byallow_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_listanddroost_profileread 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:
- Carry the
#[Tool]attribute (id, label, description,inputSchema,outputSchema, and thereadOnly/destructive/idempotent/openWorldannotations). - Extend
DroostToolBase(orDestructiveToolBasefor state-changing tools). The base classes provide thesucceed()/fail()envelope helpers, small typed-argument coercion helpers (stringArg(),intArg(),stringListArg(),toString(),toInt()), theenabled => TRUEdefault so the tool auto-registers, and â forDestructiveToolBaseâ thegate()check. - Implement
execute(array $arguments, ClientGateway $gateway): mixedand return a{success, message, data}array. - 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.
Tests
Kernel tests live under tests/src/Kernel/:
-
DroostToolsTestâ discovery, a representative execution, destructive/eval gating, annotations, the guideline provider, and the module doc reader. -
DroostIntrospectionToolsTestâ executes every read-only tool and asserts the success envelope, plus secret redaction and the SQL write guard. -
DroostOperationToolsTestâ the safe operations run unguarded; every gated tool is annotated destructive and refuses while its flag is off.
Run them with the project's PHPUnit configuration:
vendor/bin/phpunit web/modules/custom/droost
License
GPL-2.0-or-later.