Drupal MCP Server
A Model Context Protocol (MCP) server for Drupal operations. Provides 392 tools across 38 modules for site analysis, migration, content management, configuration, commerce, headless, DevOps, security, and more.
Why Use This?
- Unified interface — Access JSON:API, GraphQL, Drush, and AI through one MCP server
- AI-powered — Content analysis, field mapping, and migration planning with your choice of LLM
- Multi-transport — Stdio for desktop apps, HTTP/SSE for remote deployments
- Secure — Input validation, OAuth 2.1, no shell injection
- Comprehensive — 37 tool modules covering every major Drupal subsystem
Features
- JSON:API & GraphQL — Full support for Drupal's REST endpoints, schema introspection, sparse fieldsets
- Drush Integration — Execute any Drush command (cache, cron, updb, watchdog, php-eval, etc.)
- DDEV/Lando/Acquia Support — Local and cloud development environment management
- AI-Powered Tools — Multi-provider AI (Ollama, OpenAI, Anthropic, NVIDIA NIM, Google)
- Migration Assistance — Generate and validate migration configurations, D7→D10 upgrades
- Security Audits — PHPStan, Drupal Check, dependency scanning, SQL injection, XSS, CSP, file permissions
- Content Management — CRUD for nodes, media, users, taxonomy, comments, revisions, paragraphs
- Commerce — Stores, products, orders, payments, promotions, shipping (18 tools)
- Configuration — Export/import, sync, diff, split (7 tools)
- DevOps — Database queries, Composer, cache, cron, queues, backups (20 tools)
- Headless — Next.js/Gatsby helpers, JSON:API optimization, GraphQL schema (10 tools)
- Search — Search API servers, indexes, facets, reindex (13 tools)
- Multilingual — Languages, content translation, interface translation, config translation (18 tools)
- Module Development — Scaffolding for modules, services, plugins, forms, controllers, tests (12 tools)
- Theme Development — Theme creation, libraries, regions, templates, preprocessing, assets (11 tools)
- Webform — Forms, submissions, elements, handlers, tokens, export (16 tools)
Installation
pip install drupal-mcp-server
Or with uv:
uv pip install drupal-mcp-server
Quickstart
1. Configure
cp examples/.env.example .env
Minimal .env:
DRUPAL_BASE_URL=https://your-drupal-site.com
DRUPAL_USERNAME=admin
DRUPAL_PASSWORD=secret
2. Run
drupal-mcp
3. Use with Claude Desktop
Add to ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"drupal": {
"command": "drupal-mcp",
"env": {
"DRUPAL_BASE_URL": "https://your-drupal-site.com",
"DRUPAL_USERNAME": "admin",
"DRUPAL_PASSWORD": "secret"
}
}
}
}
AI Coding Agents
Register the server with any MCP client in one command:
drupal-mcp install
It detects the agents on your machine — Claude Code, Claude Desktop, Cursor,
Codex CLI, OpenCode, Windsurf, Pi, Hermes Agent and OpenClaw — gathers
DRUPAL_* settings from ./.env or the environment, and writes each client's
config (preferring the client's own mcp add command when it has one).
drupal-mcp install --client claude-code --print # preview without writing
drupal-mcp install --command 'uvx drupal-mcp-server' # run from PyPI, no install
See the AI Coding Agents guide for per-client copy-paste snippets, options, and caveats.
Configuration
Drupal connection
| Variable | Default | Description |
|---|---|---|
DRUPAL_BASE_URL |
http://localhost |
Base URL of the Drupal site |
DRUPAL_USERNAME / DRUPAL_PASSWORD |
— | Credentials (see below) |
DRUPAL_VERIFY_SSL |
true |
Set false for self-signed dev certs (DDEV, Lando) |
Authentication
Set DRUPAL_USERNAME and DRUPAL_PASSWORD. Drupal rejects basic auth on its
write endpoints with 403, so the server logs in to /user/login, reuses the
session cookie, and attaches the CSRF token that mutating JSON:API requests
require. Basic auth headers are still sent when a session cannot be
established, so reads keep working on sites without /user/login.
Other methods, used when the username/password pair is not set:
- OAuth2 — Set
DRUPAL_OAUTH_CLIENT_ID,DRUPAL_OAUTH_CLIENT_SECRET,DRUPAL_OAUTH_TOKEN_URL - Bearer Token — Set
DRUPAL_API_TOKEN, sent asAuthorization: Bearer
Drush
Drush tools shell out to DRUSH_PATH (default drush). It may contain
arguments, so container-based setups work:
DRUSH_PATH="ddev drush" # start the MCP server from the project directory
AI Providers
Set DEFAULT_AI_PROVIDER to one of:
ollama(default, local)openaianthropicnvidia_nimgoogle
Transport
Set MCP_TRANSPORT to one of:
stdio(default, for desktop apps)http(for remote HTTP deployment)sse(for Server-Sent Events)streamable-http(for Streamable HTTP)
Tools (392 total)
Accessibility (5)
accessibility_axe_scan— Run axe-core accessibility scanaccessibility_wcag_check— WCAG compliance checkaccessibility_color_contrast— Color contrast validationaccessibility_aria_validate— ARIA attribute validationaccessibility_report_generate— Generate accessibility report
AI (4)
ai_analyze_content_quality— Analyze content quality with AIai_suggest_field_mappings— Suggest field mappings between bundlesai_generate_migration_plan— Generate migration plan with AIai_translate_content— Translate content with AI
Analysis (6)
analyze_d7_site— Analyze D7 site for D10 readinessanalyze_d9_site— Analyze D9+ for deprecated APIsinventory_content_types— List all content typesinventory_modules— List all modulesmap_modules_d7_to_d10— AI-powered module mapping D7→D10find_deprecated_code— Scan for deprecated APIs
Code Audit (7)
code_audit_phpstan— Run PHPStan static analysisdrupal_check— Run Drupal Checkcode_sniffer— Run PHP_CodeSnifferdeprecation_scan— Scan for deprecated codesecurity_audit— Security-focused code auditcomposer_audit— Composer dependency auditcode_quality_report— Combined code quality report
Codebase (12)
codebase_index_refresh,codebase_search,codebase_module_list,codebase_module_describecodebase_unused_modules,codebase_redundancy_check,codebase_hooks_find,codebase_paths_listcodebase_files_list,codebase_directory_tree,codebase_file_read,codebase_function_read
Comments (10)
comment_list,comment_get,comment_create,comment_update,comment_deletecomment_thread_list,comment_approve,comment_unapprovecomment_field_settings,comment_field_configure
Commerce (18)
commerce_store_list,commerce_store_get,commerce_store_create,commerce_store_updatecommerce_product_list,commerce_product_get,commerce_product_create,commerce_product_updatecommerce_order_list,commerce_order_get,commerce_order_create,commerce_order_updatecommerce_payment_gateway_list,commerce_payment_gateway_getcommerce_promotion_list,commerce_promotion_createcommerce_shipping_method_list,commerce_shipping_method_create
Config (7)
config_get,config_set,export_config,import_config,sync_config,diff_config,split_config
Contact (9)
contact_form_list,contact_form_get,contact_form_create,contact_form_update,contact_form_deletecontact_message_list,contact_message_get,contact_message_delete,contact_spam_check
Content (14)
create_node,get_node,update_node,delete_node,query_nodesupload_media,create_mediaclean_nodes_ai,map_fields_ai,bulk_update_nodesfix_media_references,bulk_update_aliasesanalyze_content_quality,generate_test_content
Database (4)
db_query,db_tables,db_table_info,db_count
DevOps (20)
drush_command— Execute any Drush commandddev_setup,ddev_start,ddev_stop,ddev_execlando_start,lando_stop,lando_exec,lando_setupacquia_deploy,backup_database,cache_rebuildcomposer_install,composer_require,composer_updatedrush_cron,drush_watchdog,cache_clear,drush_updb,drush_php_eval
Drupal.org (12)
module_search,module_get,module_versions,module_dependencies,module_compatibilitytheme_search,theme_get,release_history,project_usagemodule_issues_search,module_popular,module_recommendation
Feeds (12)
feeds_feed_list,feeds_feed_get,feeds_feed_create,feeds_feed_update,feeds_feed_deletefeeds_import,feeds_import_status,feeds_log_listfeeds_tamper_list,feeds_parser_list,feeds_fetcher_list,feeds_processor_list
Fields (10)
field_config_list,field_config_get,field_config_create,field_config_update,field_config_deletefield_storage_list,field_storage_get,field_storage_create,field_storage_update,field_storage_delete
Headless (10)
jsonapi_schema_get,jsonapi_include_optimize,jsonapi_sparse_fieldsets,jsonapi_resources_list,jsonapi_resource_typesgraphql_schema_get,graphql_query,graphql_schema_extendnextjs_helpers_generate,gatsby_helpers_generate
Layout Builder (15)
block_list,block_get,block_create,block_update,block_delete,block_placelayout_builder_list,layout_builder_get,layout_builder_create,layout_builder_updatelayout_builder_sections_list,layout_builder_section_addblock_plugin_list,block_region_list,layout_builder_override_remove
The block_* tools work normally. Layout Builder has no JSON:API resource, so
the layout_builder_* tools go through Drush to the real API instead:
entity_view_display holds the sections, and per-entity overrides live in the
entity's layout_builder__layout field. layout_builder_list reports which
displays have Layout Builder enabled; layout_builder_get,
layout_builder_sections_list, layout_builder_section_add,
layout_builder_create and layout_builder_update read and write the layout;
layout_builder_override_remove reverts an entity's override.
Media Enhanced (8)
media_type_list,media_type_get,media_type_createmedia_create_from_url,media_create_from_base64,media_derivative_generatemedia_bulk_import,media_library_list
Menus (10)
menu_list,menu_get,menu_create,menu_update,menu_deletemenu_link_list,menu_link_get,menu_link_create,menu_link_update,menu_link_delete
Metatag (9)
metatag_config_list,metatag_config_get,metatag_config_create,metatag_config_updatemetatag_defaults_get,metatag_defaults_set,metatag_token_listmetatag_canonical_set,metatag_jsonld_generate
Migration (17)
generate_migration_yaml— Generate migration YAMLgenerate_module_migration— Generate migration modulerun_migration_test— Test migration with limited recordsvalidate_migration— Validate migration configrollback_migration— Rollback a migrationsync_media_files— Sync media between pathsmigrate_taxonomy— Migrate taxonomy vocabulariescreate_migration_plan— Create a persisted migration manifestget_migration_plan— Read a migration manifest and tracked statelist_migration_plans— List migration manifestsmigration_status— Summarize migration progressmigrate_records— Copy a batch of source records to the target siterecord_migration_result— Record externally executed migration outcomesanalyze_source_site— Analyze a source Drupal JSON:API resource typereconcile_migration_counts— Compare source and target countsrun_migration_wave— Fetch and migrate one source resource typerun_migration— Run an ordered end-to-end migration for all planned types
Moderation (13)
moderation_state_list,moderation_state_get,moderation_state_create,moderation_state_update,moderation_state_deletemoderation_transition_list,moderation_transition_get,moderation_transition_create,moderation_transition_execute,moderation_transition_deletecontent_moderation_dashboard,workflow_list,workflow_get
Module Development (12)
module_create,module_service_create,module_plugin_create,module_form_createmodule_controller_create,module_event_subscriber_create,module_hook_implementmodule_info_generate,module_permission_create,module_config_schema_createmodule_test_create,module_dependencies_analyze
Multilingual (18)
language_list,language_get,language_add,language_enable,language_disable,language_deletecontent_translation_enable,translation_status,translation_add,translation_updateinterface_translation_list,interface_translation_add,interface_translation_update,interface_translation_deleteconfig_translation_list,config_translation_addlanguage_negotiation_get,language_negotiation_set
Paragraphs (10)
paragraphs_type_list,paragraphs_type_get,paragraphs_type_create,paragraphs_type_update,paragraphs_type_deleteparagraphs_item_list,paragraphs_item_get,paragraphs_item_create,paragraphs_item_update,paragraphs_item_delete
Pathauto (8)
pathauto_pattern_list,pathauto_pattern_get,pathauto_pattern_create,pathauto_pattern_update,pathauto_pattern_deletepathauto_generate,pathauto_token_list,redirect_pattern_list
Performance (7)
performance_query_log,performance_cache_stats,performance_redis_infoperformance_bigpipe_status,performance_bigpipe_enable,performance_memcached_statsperformance_page_load_analyze
Redirects (7)
redirect_list,redirect_get,redirect_create,redirect_update,redirect_deleteredirect_bulk_create,redirect_bulk_delete
Revisions (5)
revision_list,revision_get,revision_revert,revision_delete,revision_diff
Scheduler (7)
cron_run,cron_list,queue_list,queue_run,queue_delete,queue_info,queue_create
Search API (13)
search_api_server_list,search_api_server_get,search_api_server_create,search_api_server_enable,search_api_server_disablesearch_api_index_list,search_api_index_get,search_api_index_create,search_api_index_updatesearch_api_index_reindex,search_api_index_clear,search_api_facet_list,search_api_facet_create
Security Audit (8)
security_scan— PHPStan with security extensionsdependency_scan— Composer auditconfig_security_check— Drupal config securitypermission_audit— Role and permission auditsql_injection_scan— SQL injection detectionxss_scan— XSS vulnerability detectioncsp_audit— Content Security Policy auditfile_permissions_audit— File permissions audit
Taxonomy (10)
create_vocabulary,get_vocabulary,update_vocabulary,delete_vocabulary,list_vocabulariescreate_term,get_term,update_term,delete_term,list_terms
Testing (9)
test_run_phpunit,test_run_kernel,test_run_behat,test_run_nightwatchtest_scaffold_phpunit,test_scaffold_behat,test_scaffold_nightwatchtest_coverage_report,test_list_suites
Theme Development (11)
theme_create,theme_library_create,theme_region_create,theme_template_overridetheme_preprocess_function,theme_breakpoint_create,theme_base_changetheme_settings_form,theme_asset_optimize,theme_debug_enable,theme_lint_twig
Users (12)
create_user,get_user,get_user_by_email,update_user,delete_user,list_usersassign_role,remove_role,list_roles,get_user_roles,block_user,unblock_user
Views (7)
views_list,views_get,views_create,views_update,views_delete,views_execute,views_export
Webform (16)
webform_list,webform_get,webform_create,webform_update,webform_deletewebform_submission_list,webform_submission_get,webform_submission_deletewebform_element_list,webform_element_createwebform_handler_list,webform_handler_create,webform_handler_update,webform_handler_deletewebform_token_list,webform_export_submissions
Documentation
- Contributing Guide
- Changelog
- Security Policy
- Code of Conduct
- Architecture
- Migration Guide
- Docstring Standard
Known limitations
These are real constraints of Drupal and its APIs, not TODOs. Each was confirmed against a live Drupal 11 site under DDEV, and the affected tools say so at runtime rather than returning an opaque error.
- Layout Builder has no JSON:API resource. The six
layout_builder_*tools originally asked JSON:API for a resource that has never existed and returned an opaque 404. They now go through Drush to the real API — sections are thelayout_builder.sectionsthird-party setting onentity_view_display, and per-entity overrides live in the entity'slayout_builder__layoutfield — so reading, writing and reverting layouts all work. The API has no display-level lock, so thelockargument marks components rather than the display. - Revisions have no collection endpoint. JSON:API addresses a single revision via
?resourceVersion=id:<vid>but exposes no revision listing or deletion, sorevision_listandrevision_deletereturn an honest limitation error rather than a 404. - Drush offers no per-run queue limit.
queue:rundrains the whole queue and has neither--limitnor--count.queue_runruns without one and reports that the requested limit could not be applied. - Drush only knows registered queues.
queue:listandqueue:runresolve queue workers that a module registered as a service, soqueue_listandqueue_infocannot see a queue nothing has registered, andqueue_runanswers "Queue not found" for one — including a queuequeue_createjust made.queue_createandqueue_deletedeliberately go around this and work on any queue name. - Composer is its own binary. Drush ships no
composercommand, socomposer_install/composer_require/composer_update/composer_auditruncomposerdirectly and need it onPATH(or at./vendor/bin/composer). They also do not require Drush. When the project is a DDEV or Lando project the command runs asddev composer/lando composerinstead: the host binary resolves against the host's PHP, which once locked in adoctrine/collectionsrelease needing 8.4 for a container running 8.3 — Composer's platform check then aborted every subsequent request and took the site down. - GraphQL needs a server before
/graphqlexists. The module installs no default, sographql_queryandgraphql_schema_getanswer a bare 404 on a fresh site. They now explain that, naming thegraphql_serverconfig entity, theexecute default arbitrary graphql requestspermission, and the fact thatbasic_authhas to be enabled before credentials reach Drupal at all. langcodeis not always writable. Withcontent_translationinstalled and no bundle configured for translation, JSON:API refuses any write tolangcodewith a 403 regardless of the value sent —"en"as much as a nonsense one. The create tools omit it unless a language was asked for, so a create succeeds; one that does pass it gets Drupal's own explanation.- Security scanning needs PHPStan 1.x. Older PHPStan has no
--exclude-path; the tools probe for it and fall back rather than failing. release_historyfor Drupal 7 projects. packages.drupal.org is the live source for releases, dependencies, and core compatibility. D7-era projects fall back to updates.drupal.org's XML feed, which has been frozen since Drupal 8.- DDEV's key is
docroot, notwebroot. DDEV 1.24 silently drops an unrecognised key, soddev_setupwritesdocroot:— an empty docroot makes nginx serve the project root and every request 404s.
Modules the tools target are installed in the test site — Commerce, Paragraphs,
Webform, Search API, Feeds, Metatag, Pathauto — so a missing module is no longer
the usual explanation for a tool that does nothing. Two resources stay out of
reach by design: content_moderation_state is never exposed through JSON:API,
and feeds_feed needs a feed type with a feeds_item field configured on its
target bundle. Both are reported as not exposed rather than as failures.
Development
# Create venv
python3 -m venv venv
source venv/bin/activate
# Install dev dependencies
pip install -e ".[dev]"
# Run tests
pytest
# Lint
ruff check src/ tests/
# Type check
mypy src/drupal_mcp_server/
The offline suite cannot catch most of the bugs that matter in this package —
they need a real Drupal. Live runs do, and tests/live/README.md lists what
each one has found so far:
python tests/live/smoke_test.py # 9 end-to-end cases, ~30s
python tests/live/layout_builder_test.py # 10 Layout Builder cases, ~60s
python tests/live/queue_test.py # 18 cron/queue cases, ~60s
python tests/live/filter_check.py # every hard-coded JSON:API filter
python tests/live/resource_check.py # every resource name, asked of Drupal
python tests/live/install_modules.py # the modules the tools assume, ~10 min
python tests/live/sweep.py # all 392 tools, ~30 min
python tests/live/mcp_call.py content_list '{}' # one call, for spot checks
They need ddev running and the site in drupal-live/; tests/live/README.md
covers setup.
License
MIT
Metadata
Release files for drupal-mcp-server 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| drupal_mcp_server-0.2.0.tar.gz | 287.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| drupal_mcp_server-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 478.6 kB
Release files / drupal_mcp_server-0.2.0.tar.gz
| Download URL | drupal_mcp_server-0.2.0.tar.gz |
|---|---|
| Size | 287.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4e172c627cd086e2046aaa059ecb4a5aab5af5963a343092fcdb70d61a8b585a
|
|
BLAKE2b-256 checksum How to use checksums |
0a194478a1ae613ace0018b810c41873dcde5fb3b2137d1818fd6282e09df031
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.
Transparency logRelease files / drupal_mcp_server-0.2.0-py3-none-any.whl
| Download URL | drupal_mcp_server-0.2.0-py3-none-any.whl |
|---|---|
| Size | 190.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2bd2d55bd5e710d3d85001830e113ae742e4f7ab1c999935eaa135ab66ed235a
|
|
BLAKE2b-256 checksum How to use checksums |
99a3e35262999ffbf7f5efeec5f63f0fd38e1d17364d173099698423e100c1b5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.
Transparency log