Skip to main content

Drupal MCP Server

CI codecov PyPI version Python versions License: MIT

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:

  1. OAuth2 — Set DRUPAL_OAUTH_CLIENT_ID, DRUPAL_OAUTH_CLIENT_SECRET, DRUPAL_OAUTH_TOKEN_URL
  2. Bearer Token — Set DRUPAL_API_TOKEN, sent as Authorization: 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)
  • openai
  • anthropic
  • nvidia_nim
  • google

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 scan
  • accessibility_wcag_check — WCAG compliance check
  • accessibility_color_contrast — Color contrast validation
  • accessibility_aria_validate — ARIA attribute validation
  • accessibility_report_generate — Generate accessibility report

AI (4)

  • ai_analyze_content_quality — Analyze content quality with AI
  • ai_suggest_field_mappings — Suggest field mappings between bundles
  • ai_generate_migration_plan — Generate migration plan with AI
  • ai_translate_content — Translate content with AI

Analysis (6)

  • analyze_d7_site — Analyze D7 site for D10 readiness
  • analyze_d9_site — Analyze D9+ for deprecated APIs
  • inventory_content_types — List all content types
  • inventory_modules — List all modules
  • map_modules_d7_to_d10 — AI-powered module mapping D7→D10
  • find_deprecated_code — Scan for deprecated APIs

Code Audit (7)

  • code_audit_phpstan — Run PHPStan static analysis
  • drupal_check — Run Drupal Check
  • code_sniffer — Run PHP_CodeSniffer
  • deprecation_scan — Scan for deprecated code
  • security_audit — Security-focused code audit
  • composer_audit — Composer dependency audit
  • code_quality_report — Combined code quality report

Comments (10)

  • comment_list, comment_get, comment_create, comment_update, comment_delete
  • comment_thread_list, comment_approve, comment_unapprove
  • comment_field_settings, comment_field_configure

Commerce (18)

  • commerce_store_list, commerce_store_get, commerce_store_create, commerce_store_update
  • commerce_product_list, commerce_product_get, commerce_product_create, commerce_product_update
  • commerce_order_list, commerce_order_get, commerce_order_create, commerce_order_update
  • commerce_payment_gateway_list, commerce_payment_gateway_get
  • commerce_promotion_list, commerce_promotion_create
  • commerce_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_delete
  • contact_message_list, contact_message_get, contact_message_delete, contact_spam_check

Content (14)

  • create_node, get_node, update_node, delete_node, query_nodes
  • upload_media, create_media
  • clean_nodes_ai, map_fields_ai, bulk_update_nodes
  • fix_media_references, bulk_update_aliases
  • analyze_content_quality, generate_test_content

Database (4)

  • db_query, db_tables, db_table_info, db_count

DevOps (20)

  • drush_command — Execute any Drush command
  • ddev_setup, ddev_start, ddev_stop, ddev_exec
  • lando_start, lando_stop, lando_exec, lando_setup
  • acquia_deploy, backup_database, cache_rebuild
  • composer_install, composer_require, composer_update
  • drush_cron, drush_watchdog, cache_clear, drush_updb, drush_php_eval

Drupal.org (9)

  • module_search, module_get, module_versions, module_dependencies, module_compatibility
  • theme_search, theme_get, release_history, project_usage

Feeds (12)

  • feeds_feed_list, feeds_feed_get, feeds_feed_create, feeds_feed_update, feeds_feed_delete
  • feeds_import, feeds_import_status, feeds_log_list
  • feeds_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_delete
  • field_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_types
  • graphql_schema_get, graphql_query, graphql_schema_extend
  • nextjs_helpers_generate, gatsby_helpers_generate

Layout Builder (15)

  • block_list, block_get, block_create, block_update, block_delete, block_place
  • layout_builder_list, layout_builder_get, layout_builder_create, layout_builder_update
  • layout_builder_sections_list, layout_builder_section_add
  • block_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_create
  • media_create_from_url, media_create_from_base64, media_derivative_generate
  • media_bulk_import, media_library_list

Menus (10)

  • menu_list, menu_get, menu_create, menu_update, menu_delete
  • menu_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_update
  • metatag_defaults_get, metatag_defaults_set, metatag_token_list
  • metatag_canonical_set, metatag_jsonld_generate

Migration (7)

  • generate_migration_yaml — Generate migration YAML
  • generate_module_migration — Generate migration module
  • run_migration_test — Test migration with limited records
  • validate_migration — Validate migration config
  • rollback_migration — Rollback a migration
  • sync_media_files — Sync media between paths
  • migrate_taxonomy — Migrate taxonomy vocabularies

Moderation (13)

  • moderation_state_list, moderation_state_get, moderation_state_create, moderation_state_update, moderation_state_delete
  • moderation_transition_list, moderation_transition_get, moderation_transition_create, moderation_transition_execute, moderation_transition_delete
  • content_moderation_dashboard, workflow_list, workflow_get

Module Development (12)

  • module_create, module_service_create, module_plugin_create, module_form_create
  • module_controller_create, module_event_subscriber_create, module_hook_implement
  • module_info_generate, module_permission_create, module_config_schema_create
  • module_test_create, module_dependencies_analyze

Multilingual (18)

  • language_list, language_get, language_add, language_enable, language_disable, language_delete
  • content_translation_enable, translation_status, translation_add, translation_update
  • interface_translation_list, interface_translation_add, interface_translation_update, interface_translation_delete
  • config_translation_list, config_translation_add
  • language_negotiation_get, language_negotiation_set

Paragraphs (10)

  • paragraphs_type_list, paragraphs_type_get, paragraphs_type_create, paragraphs_type_update, paragraphs_type_delete
  • paragraphs_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_delete
  • pathauto_generate, pathauto_token_list, redirect_pattern_list

Performance (7)

  • performance_query_log, performance_cache_stats, performance_redis_info
  • performance_bigpipe_status, performance_bigpipe_enable, performance_memcached_stats
  • performance_page_load_analyze

Redirects (7)

  • redirect_list, redirect_get, redirect_create, redirect_update, redirect_delete
  • redirect_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_disable
  • search_api_index_list, search_api_index_get, search_api_index_create, search_api_index_update
  • search_api_index_reindex, search_api_index_clear, search_api_facet_list, search_api_facet_create

Security Audit (8)

  • security_scan — PHPStan with security extensions
  • dependency_scan — Composer audit
  • config_security_check — Drupal config security
  • permission_audit — Role and permission audit
  • sql_injection_scan — SQL injection detection
  • xss_scan — XSS vulnerability detection
  • csp_audit — Content Security Policy audit
  • file_permissions_audit — File permissions audit

Taxonomy (10)

  • create_vocabulary, get_vocabulary, update_vocabulary, delete_vocabulary, list_vocabularies
  • create_term, get_term, update_term, delete_term, list_terms

Testing (9)

  • test_run_phpunit, test_run_kernel, test_run_behat, test_run_nightwatch
  • test_scaffold_phpunit, test_scaffold_behat, test_scaffold_nightwatch
  • test_coverage_report, test_list_suites

Theme Development (11)

  • theme_create, theme_library_create, theme_region_create, theme_template_override
  • theme_preprocess_function, theme_breakpoint_create, theme_base_change
  • theme_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_users
  • assign_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_delete
  • webform_submission_list, webform_submission_get, webform_submission_delete
  • webform_element_list, webform_element_create
  • webform_handler_list, webform_handler_create, webform_handler_update, webform_handler_delete
  • webform_token_list, webform_export_submissions

Documentation

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 the layout_builder.sections third-party setting on entity_view_display, and per-entity overrides live in the entity's layout_builder__layout field — so reading, writing and reverting layouts all work. The API has no display-level lock, so the lock argument 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, so revision_list and revision_delete return an honest limitation error rather than a 404.
  • Drush offers no per-run queue limit. queue:run drains the whole queue and has neither --limit nor --count. queue_run runs without one and reports that the requested limit could not be applied.
  • Drush only knows registered queues. queue:list and queue:run resolve queue workers that a module registered as a service, so queue_list and queue_info cannot see a queue nothing has registered, and queue_run answers "Queue not found" for one — including a queue queue_create just made. queue_create and queue_delete deliberately go around this and work on any queue name.
  • Composer is its own binary. Drush ships no composer command, so composer_install / composer_require / composer_update / composer_audit run composer directly and need it on PATH (or at ./vendor/bin/composer). They also do not require Drush. When the project is a DDEV or Lando project the command runs as ddev composer / lando composer instead: the host binary resolves against the host's PHP, which once locked in a doctrine/collections release 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 /graphql exists. The module installs no default, so graphql_query and graphql_schema_get answer a bare 404 on a fresh site. They now explain that, naming the graphql_server config entity, the execute default arbitrary graphql requests permission, and the fact that basic_auth has to be enabled before credentials reach Drupal at all.
  • langcode is not always writable. With content_translation installed and no bundle configured for translation, JSON:API refuses any write to langcode with 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_history for 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, not webroot. DDEV 1.24 silently drops an unrecognised key, so ddev_setup writes docroot: — 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)

Source distribution for drupal-mcp-server 0.1.0
File Size Uploaded
drupal_mcp_server-0.1.0.tar.gz 228.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for drupal-mcp-server 0.1.0
File Interpreter ABI Platform
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}

Release history Release notifications | RSS feed

0.2.0

2 release files

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page