Build: Making Drupal Sites AI-Discoverable with MCP
I built Project Context Connector, a Drupal module that makes site configuration programmatically accessible to AI agents, automation scripts, and monitoring tools through multiple interfaces including native Model Context Protocol support.
The Problem: Opaque Drupal Sites
When building AI assistants or DevOps automation for Drupal sites, there's no standard way to ask "What version of Drupal is running?" or "Which modules need security updates?" Traditional approaches require SSH access, manual Drush commands, or parsing HTML pages.
For agencies managing dozens of client sites, this creates friction:
- Support bots can't answer environment questions
- CI/CD pipelines can't adapt to site configuration
- AI assistants lack real-time context about the stack
- Security monitoring requires custom scripting for each site
The Solution: Multiple Access Patterns
Project Context Connector exposes a read-only JSON snapshot of Drupal site metadata through four interfaces:
1. REST API (permission-gated):
curl -u user:pass https://site.com/project-context-connector/snapshot
2. HMAC Signed Endpoint (no Drupal user needed):
curl -H "X-PCC-Timestamp: $(date +%s)" \
-H "X-PCC-Signature: $(echo -n "$(date +%s)" | \
openssl dgst -sha256 -hmac 'secret' -binary | base64)" \
https://site.com/project-context-connector/snapshot/signed
3. Drush Command (local/CI):
drush pcc:snapshot --format=json
4. Model Context Protocol Tool (AI assistants):
{
"name": "project_context_snapshot",
"description": "Get Drupal site context",
"input": {}
}
What Gets Exposed
The snapshot includes exactly what external tools need:
{
"generated_at": "2026-02-16T18:00:00Z",
"drupal": {
"core_version": "10.3.0",
"php": {"version": "8.3.0"},
"database": {"driver": "mysql", "version": "8.0.36"},
"active_modules": [
{
"name": "views",
"version": "10.3.0",
"origin": "core",
"security_status": "current"
}
],
"themes": {
"default": "olivero",
"admin": "claro"
}
}
}
Security update status comes from Drupal's Update module cache (no outbound requests).
MCP Integration: Native AI Access
The newest feature is Model Context Protocol (MCP) support via Drupal's Tool API. When you install the mcp_server module alongside Project Context Connector, AI assistants like Claude Desktop can query your site directly:
Installation:
composer require drupal/mcp_server drupal/project_context_connector
drush en -y mcp_server project_context_connector
drush cr
Claude Desktop Config:
{
"mcpServers": {
"drupal-prod": {
"command": "drush",
"args": ["mcp:server"],
"cwd": "/var/www/drupal"
}
}
}
Now Claude can answer questions like "What PHP version is the production site running?" by calling the project_context_snapshot tool directly, no HTTP requests needed.
Real-World Use Case: Slack Bot Integration
I built a companion Slack bot that uses these endpoints to answer team questions:
/drupal-env production
Response:
Production Environment:
- Drupal: 10.3.0
- PHP: 8.3.0
- Database: MySQL 8.0.36
- Modules needing updates: 0
- Security status: All current
The bot authenticates via HMAC signatures, runs on AWS Lambda, and costs pennies per month. No SSH access required, no credentials stored in the bot code.
Security First
The module follows secure-by-default principles:
- Read-only operations (no write endpoints)
- Zero PII exposure (no user data, emails, or credentials)
- Rate limiting via Drupal's Flood API (default: 60 req/min)
- CORS allow-lists for browser clients
- Proper HTTP caching with cache tags
- HMAC-SHA256 authentication with replay protection
- Optional database version hiding (OWASP recommendation)
Technical Decisions
Why multiple interfaces? Different use cases need different access patterns. MCP is ideal for local AI assistants, REST APIs work for remote bots, Drush fits CI/CD pipelines, and HMAC signatures enable serverless functions without managing Drupal sessions.
Why not expose everything? The snapshot is deliberately minimal. It includes versions, security status, and configuration flags, but excludes sensitive data (database credentials, API keys, user information). Even the optional database version is off by default.
PHPStan Challenge:
The MCP integration uses an optional dependency (drupal/tool). PHPStan fails if the module isn't installed, but we can't require it since HTTP/Drush features work fine without it. Solution: phpstan.neon with excludePaths to skip the MCP plugin during analysis when the tool module is absent.
Performance Notes
- Cached responses: Full Drupal render cache with proper contexts
- No database queries: Uses cached module/theme lists from Drupal's extension discovery
- Security updates: Reads from Update module cache (already warmed by cron)
- Response size: ~50KB typical for 100+ modules
Installation (2 minutes)
composer require drupal/project_context_connector
drush en -y project_context_connector
drush cr
For MCP support, add mcp_server:
composer require drupal/mcp_server
drush en -y mcp_server
Grant the "access project context snapshot" permission to service users or configure HMAC authentication.
Next Steps
The module is production-ready on Drupal.org with full test coverage (unit, kernel, functional) and GitLab CI pipelines. Future directions:
- Custom field plugins for site-specific metadata
- GraphQL endpoint alongside REST
- Webhook notifications on security updates
- Multi-site aggregation dashboard
For agencies managing multiple Drupal sites, this creates a standard interface for site metadata, enabling centralized monitoring, AI-powered support, and DevOps automation without custom scripts per client.
View Code
