Skip to main content

Drupal MCP Server

CI codecov PyPI version Python versions PyPI downloads License: MIT

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:

  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 (392 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

Codebase (12)

  • codebase_index_refresh, codebase_search, codebase_module_list, codebase_module_describe
  • codebase_unused_modules, codebase_redundancy_check, codebase_hooks_find, codebase_paths_list
  • codebase_files_list, codebase_directory_tree, codebase_file_read, codebase_function_read

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 (12)

  • module_search, module_get, module_versions, module_dependencies, module_compatibility
  • theme_search, theme_get, release_history, project_usage
  • module_issues_search, module_popular, module_recommendation

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 (17)

  • 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
  • create_migration_plan — Create a persisted migration manifest
  • get_migration_plan — Read a migration manifest and tracked state
  • list_migration_plans — List migration manifests
  • migration_status — Summarize migration progress
  • migrate_records — Copy a batch of source records to the target site
  • record_migration_result — Record externally executed migration outcomes
  • analyze_source_site — Analyze a source Drupal JSON:API resource type
  • reconcile_migration_counts — Compare source and target counts
  • run_migration_wave — Fetch and migrate one source resource type
  • run_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_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 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)

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

Built distribution (wheel)

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

Release 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

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.0

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