Drupal MCP Server
A Model Context Protocol (MCP) server for Drupal operations. Provides 367 tools across 37 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"
}
}
}
}
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 (367 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
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 (9)
module_search,module_get,module_versions,module_dependencies,module_compatibilitytheme_search,theme_get,release_history,project_usage
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 (7)
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 vocabularies
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 367 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.1.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.1.0.tar.gz | 228.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| drupal_mcp_server-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 380.1 kB
Release files / drupal_mcp_server-0.1.0.tar.gz
| Download URL | drupal_mcp_server-0.1.0.tar.gz |
|---|---|
| Size | 228.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c1125235dca75febb0f8329adbae2607792c193f4ee0aa4a188bbcde83b12eed
|
|
BLAKE2b-256 checksum How to use checksums |
62f5f07b0b6825209eb7dfcb5d9cae784ab31d3e144c1bc87ff274cd170c0f79
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.19 {"installer":{"name":"uv","version":"0.12.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / drupal_mcp_server-0.1.0-py3-none-any.whl
| Download URL | drupal_mcp_server-0.1.0-py3-none-any.whl |
|---|---|
| Size | 151.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1913583ac9fd733390e7f1d6af6b449ad2da5e7b2aad55d30bb5bd20ea891601
|
|
BLAKE2b-256 checksum How to use checksums |
c7bfaf69670134a9688b4977e7a17b61fb4e53722a37954d3d5fa9abfb0f5402
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.19 {"installer":{"name":"uv","version":"0.12.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|