drupal-mcp-connector
v1.0.0
Published
A secure, multi-site Model Context Protocol (MCP) connector for Drupal — dual-protocol JSON:API and GraphQL.
Maintainers
Readme
drupal-mcp-connector
A secure, multi-site Model Context Protocol (MCP) connector for Drupal — dual-protocol JSON:API and GraphQL access, governed content tools, audit reports, and an SSH Drush bridge.
Built by Jeremy Michael Cerda ([email protected]). Maintained by Wilkes & Liberty, LLC.
What It Does
drupal-mcp-connector connects any Model Context Protocol client to one or more Drupal sites. It exposes Drupal content and configuration as a set of MCP tools, resources, and prompts, so an MCP client can read, audit, and (where permitted) write content through structured, governed operations instead of the admin UI:
"Find all articles missing a meta description and list them."
"Show me every user account that hasn't logged in for 90 days."
"Create 10 draft product nodes from this structured data."
"Run an SEO and accessibility audit on the article content type."
"What content types exist on the site and which are barely used?"The connector speaks two Drupal backends interchangeably — Drupal core's JSON:API and GraphQL (via GraphQL Compose) — selectable per site. It normalizes both into one canonical entity shape, so the same tools work whether a site exposes JSON:API, GraphQL, or both. An optional SSH Drush bridge adds administrative operations the HTTP APIs can't reach.
Dual-Protocol Backends
Each site declares which backend(s) it exposes via the api key:
"sites": {
"main": { "baseUrl": "https://example.com", "api": "jsonapi" },
"graphql_only": { "baseUrl": "https://api.example.com", "api": "graphql" },
"either": { "baseUrl": "https://example.com", "api": ["graphql", "jsonapi"] }
}apiaccepts"jsonapi","graphql", or a priority array like["graphql","jsonapi"]. Omit it to auto-detect (the connector probes both once and caches the result).- One canonical shape. Both backends return entities as
{ id, entityType, bundle, title, status, langcode, created, changed, url, fields, relationships, _backend }, so tool output is identical regardless of protocol. - Capability-aware. Each backend advertises what it supports (read, write, delete, server-side filter/sort, revisions). GraphQL via GraphQL Compose is read-only (no mutations) and has no server-side field filter, so filters are applied client-side over a bounded fetch and flagged
approximate/truncated. Write tools against a read-only backend return a clear capability error rather than failing silently. - Writes go through JSON:API. Use a JSON:API-enabled site as the write plane; keep GraphQL as a read plane where that suits your architecture.
See docs/architecture.md for the backend abstraction and docs/graphql-local-setup.md for the GraphQL specifics.
Features
89 Tools Across 20 Modules
| Module | Tools | |--------|-------| | Nodes | CRUD for any content type with arbitrary field support | | Taxonomy | Vocabulary listing + full term CRUD | | Users | List, get, create, update, block/unblock, role management (PII-gated) | | Media | List types, CRUD, file upload, orphaned-media detection | | GraphQL | Execute a query, schema introspection (mutation-gated) | | Entities | Generic CRUD for any Drupal entity type (paragraphs, commerce, webforms, …) | | Site | Site info, content-type discovery, configured-site listing | | Reports | Content summary, stale content, field completeness, SEO/accessibility audits, taxonomy usage, user activity, revision hotspots (10 read-only reports) | | Drush | Cache rebuild, cron, config sync, module management, DB updates via SSH | | Revisions | List/get entity revisions; governed revert to a prior revision | | Moderation | Set moderation state; list content by state; observed-state discovery (content_moderation) | | Scheduler | Set publish-on / unpublish-on dates (Scheduler module) | | Fields | Describe a bundle's fields (type/required/cardinality, best-effort) | | References | Resolve a human name/title to an entity UUID for relationship fields | | Bulk | Bulk create/update with per-item partial-failure reporting | | Translations | List + create entity translations | | Paragraphs | Create/get Paragraph components for embedding in host fields | | Structure | Menu links + custom blocks (list/create) | | Search | Best-effort content search (title match; Search API/Solr-ready) | | Reports (extra) | Orphaned references, unpublished content, missing-field audits |
MCP Resources
Browsable, always-fresh context the client can read without calling a tool:
drupal://sites— configured site profiles (no credentials)drupal://{site}/content-types— content types with field schemasdrupal://{site}/security-policy— the active security configuration
MCP Prompts
Workflow templates usable as slash-commands from any MCP client:
drupal-content-audit— walk through a full site content auditdrupal-create-article— guided article creation with all fieldsdrupal-seo-fix— find and fix SEO gapsdrupal-user-cleanup— identify and handle inactive accounts
Security Model
Defense-in-depth with four one-line presets, enforced connector-side and complemented by Drupal-side governance:
"security": { "preset": "auditor" }| Preset | What it does |
|--------|-------------|
| development | Everything allowed — local development only |
| content-editor | Create/edit nodes, media, terms; no deletes; no user access |
| auditor | Read-only, all entity types, PII fields redacted |
| production-strict | Read-only, no user entities, broad PII redaction |
Presets layer with entity allow/deny lists, per-bundle operation rules, and field-level redaction. Optional transport hardening (bearer-authenticated HTTPS, bind-address restriction, secrets-from-env) is covered in docs/security-hardening.md.
Requirements
- Node.js 20+
- Drupal 10 or 11 (JSON:API ships in core)
- For the GraphQL backend: GraphQL Compose
- For token auth (recommended): Simple OAuth
- For the Drush bridge: SSH key access to the Drupal server
Quick Start
# 1. Clone and install
git clone https://github.com/Wilkes-Liberty/drupal-mcp-connector
cd drupal-mcp-connector
npm install
# 2. Configure
cp config/config.example.json config/config.json
# Edit config/config.json — add your site's baseUrl, api backend, and auth
# 3. Run (stdio transport)
node src/index.jsRegister with an MCP client
Most desktop and CLI MCP clients launch the connector over stdio. Add an entry to your client's MCP server configuration:
{
"mcpServers": {
"drupal": {
"command": "node",
"args": ["/absolute/path/to/drupal-mcp-connector/src/index.js"],
"env": {
"DRUPAL_BASE_URL": "https://mysite.com",
"DRUPAL_API_TOKEN": "your-token-here"
}
}
}
}For multi-client or remote use, run the HTTPS transport and register the endpoint instead — see docs/getting-started.md.
Companion Drupal Module — MCP Sentinel
The connector works out of the box against Drupal core's JSON:API and a GraphQL Compose schema. For server-side governance, pair it with the MCP Sentinel module (drupal/mcp_sentinel), which enforces policy inside Drupal — independent of any connector configuration:
- Role-bound policy profiles (operation gates, entity allow/deny, field redaction)
- Tamper-evident audit log of every governed MCP operation, attributed to the acting account
- Content locks that prevent edits to content a human is actively editing
- OAuth scope enforcement (
mcp:read/mcp:write) per tool - HMAC-signed webhooks on MCP-driven entity changes
composer require drupal/mcp_sentinel drupal/mcp_server drupal/simple_oauth
drush en mcp_sentinel mcp_sentinel_server mcp_server_tool_bridge -y
drush mcp-sentinel:setupGovernance keys off the authenticated account's role and OAuth scopes — not request headers. The connector sends an X-MCP-Client identity header purely as a log label. See the MCP Sentinel project page for the full contract.
Documentation
| Doc | Description | |-----|-------------| | Getting Started | Full setup: DDEV/Lando, Simple OAuth, multi-site, transports | | MCP Clients | Wire the connector into Claude (Code/Desktop), Grok (Build/API), and OpenAI (Codex/ChatGPT) — copy-paste config per client | | OAuth client_credentials | Production OAuth deploy: scope→role mapping, JSON:API writes, config persistence, secret handling, troubleshooting | | Architecture | Backend abstraction, canonical model, and how to extend it | | GraphQL Setup | GraphQL Compose backend + local TLS notes | | Tools Reference | Full reference for all 89 tools | | Security Guide | Presets, entity access control, field redaction | | Security Hardening | Optional transport, identity, and secrets controls | | Threat Model | Trust boundaries, threats & mitigations, residual risks, and the security-pass results | | Deployment | Run the HTTPS transport in production: Docker, systemd, launchd, reverse proxy, pre-exposure checklist | | Integration Contract | The connector ↔ Drupal-governance contract (identity, OAuth scopes, compatibility) | | Versioning & Stability | Semver policy: the stable surface, deprecation process, MCP protocol + Node support | | Whitepaper | Vision, personas, and use cases |
Contributing
PRs welcome. See CONTRIBUTING.md for guidelines.
Security
Found a vulnerability? See SECURITY.md. Please do not open a public issue.
License
MIT © 2026 Jeremy Michael Cerda and Wilkes & Liberty, LLC
