All notable changes to the MCP Tools module will be documented in this file.
The format is based on Keep a Changelog.
- ApiKeyManagerTest failures on Drupal 11: The beta3 frozen REQUEST_TIME fix changed
DrupalClock::now()to usegetCurrentTime(), but the test only mockedgetRequestTime(), causing 4 failures on Drupal 11 - CI dependency resolution: Consolidated
composer requiresteps with-Wflag to resolve transitive conflicts betweenmcp/sdkand Drupal core packages
- Frozen timestamps in server mode:
getRequestTime()returns the process start time in long-runningdrush mcp-tools:servesessions, causing all entities to share the same timestamp. Replaced withgetCurrentTime()across 7 services: ContentService, MediaService, BatchService, TaxonomyManagementService, ModerationService, SchedulerService, and DrupalClock (#3575317, reported by guillaumeg)
- Bump dependencies:
mcp-error-codes ^1.2,mcp-schema-builder ^1.1,mcp-tool-gateway ^1.1 - Refactor DefaultToolErrorHandler to use McpError fluent builder
- Migrate all module services from hardcoded error strings to
ErrorCode::*constants - Fix list schema rejecting complex objects in batch tools
- Remove dev-only
DRUPAL_ORG_DESCRIPTION.htmlfrom public release
- DrupalToolProvider: New adapter implementing
ToolProviderInterfacefrom mcp-tool-gateway for standardized tool discovery and execution patterns - Full MCP PHP ecosystem integration: Now leverages 5 standalone Composer packages:
code-wheel/mcp-error-codes: ^1.2- ErrorCode constants + McpError fluent buildercode-wheel/mcp-schema-builder: ^1.1- TypeMapper, SchemaValidator, McpSchema presetscode-wheel/mcp-tool-gateway: ^1.1- ToolProviderInterface, middleware pipelinecode-wheel/mcp-http-security: ^1.0- API key validation, IP allowlist, scopescode-wheel/mcp-events: ^2.0- Tool execution events
- DefaultToolErrorHandler: Refactored to use
McpErrorfluent builder for cleaner, more maintainable error responses - ErrorCode standardization: All 24+ service files now use
ErrorCode::*constants instead of hardcoded strings - ToolApiGateway: Now uses DrupalToolProvider internally for consistent tool discovery
- 223 tools across 35 submodules (up from 154)
- 741 unit tests passing on Drupal 11 + PHP 8.4
- Full CI pipeline with Drupal 10.3 and 11.0 matrix testing
- New standalone packages for the PHP MCP ecosystem:
- code-wheel/mcp-error-codes v1.1 - Standardized error codes with helper methods
- code-wheel/mcp-events v2.1 - Tool execution events with JsonSerializable support
- Service interfaces: Added
AccessManagerInterface,AuditLoggerInterface,RateLimiterInterfacefor better testability - MenuManagementService: Renamed from MenuService with proper interface
- TaxonomyManagementService: Renamed from TaxonomyService (in mcp_tools_structure) with proper interface
- External package dependencies: Now uses external Composer packages instead of bundled code:
code-wheel/mcp-error-codes: ^1.1- Error code constantscode-wheel/mcp-events: ^2.0- Tool execution events (decoupled from mcp/sdk)
- Service architecture: Core services now implement interfaces for dependency injection
- TemplateService: Refactored for cleaner architecture with ComponentFactory
- WriteAccessTrait: Improved dry-run handling and confirmation flow
- Bundled event classes: Removed
src/Mcp/Event/Tool*Event.php(now in external package) - Duplicate services: Removed duplicate TaxonomyService from mcp_tools_structure (uses core TaxonomyService)
- McpToolsRemoteController: Improved error handling and response formatting
- SchedulerService: Fixed service injection and method signatures
mcp_tools_jsonapisubmodule: Generic entity CRUD via JSON:API (6 new tools)mcp_jsonapi_discover_types- Discover all available entity types/bundlesmcp_jsonapi_get_entity- Retrieve entity by UUIDmcp_jsonapi_list_entities- List entities with filtering and paginationmcp_jsonapi_create_entity- Create any entity typemcp_jsonapi_update_entity- Update entity by UUIDmcp_jsonapi_delete_entity- Delete entity by UUID- Configurable allowlist/blocklist for entity types
- Security: user, shortcut, shortcut_set always blocked
- Settings form at
/admin/config/services/mcp-tools/jsonapi
mcp_search_api_searchtool: Search content via Search API indexes with keywords, filters, and pagination (added tomcp_tools_search_api)mcp:dev-profilecommand: One-command setup for development - applies development preset and enables recommended submodules- Grouped submodule display: Status page and
mcp:statusnow show all 35 submodules grouped by category (core-only, contrib-dependent, infrastructure)
- UserService: Added admin role protection to
blockUser()- users with administrator role cannot be blocked via MCP - UserService: Added input validation (username max 60 chars, email max 254 chars, email format validation)
- RoleService: Expanded
DANGEROUS_PERMISSIONSfrom 10 to 19 entries includingadminister blocks,administer views,create url aliases - MenuService: Enhanced URI validation to decode URL-encoded characters and prevent bypass attempts (e.g.,
%6Aavascript:)
- ContentTypeService: Fixed critical infinite recursion bug in
getEntityFieldManager()that called itself instead of the service container - TaxonomyService: Fixed N+1 query issues with new
getTermCountsByVocabulary()andbatchLoadParents()methods - mcp:status: Tool counts now queried dynamically instead of hardcoded
- Bloat cleanup: Removed Policy engine, Component system, telemetry module, workflows module, and example module
- Config cleanup: Removed
policysection frommcp_tools.settings.ymland schema - Documentation: Removed WordPress references, updated tool counts, removed COMPONENTS.md
- Lifecycle consistency: Added
lifecycle: experimentalto 9 submodules missing it - Package standardization: Standardized
package: MCP Toolsacross all submodules - Test updates: Updated ToolSchemaKernelTest tool count (156 → 154), removed deleted modules from test lists
- Execution user configuration for remote HTTP endpoint:
- Added "Use site admin (uid 1)" checkbox for simple development setup
- Added "Create MCP Executor Account" button to create dedicated service user/role
- Removed automatic user creation on module install (now user-initiated)
- Removed runtime block on uid 1 execution (now allowed via checkbox)
- Updated documentation to reflect execution user options
- Release workflow now installs
mcp-http-securitypackage before tests
- Restored 4 unit tests that were incorrectly marked as testing debt:
RedirectServiceTest- fixed assertion to match actual API (success=true, found=false)ContentTypeServiceTest- fixed with proper DI for EntityFieldManagerInterfaceFieldServiceTest- updated for new addField() signature and getFieldTypes() formatWebformServiceTest- fixed method names (listSubmissions→getSubmissions), removed dead tests
ContentTypeServicenow uses proper dependency injection instead of static\Drupal::service()calls
- New standalone package: code-wheel/mcp-http-security extracted for the PHP MCP ecosystem
- API key management with secure hashing (SHA-256 + pepper)
- IP allowlisting with CIDR support (IPv4/IPv6)
- Origin/hostname allowlisting with wildcard subdomains
- PSR-15 SecurityMiddleware for any PHP framework
- Multiple storage backends: Array, File, PDO
- Drupal adapters for the extracted package:
DrupalStateStorage- bridges Drupal State API to StorageInterfaceDrupalClock- bridges Drupal TimeInterface to PSR-20 ClockInterface
mcp_tools_remotenow delegates tocode-wheel/mcp-http-securitypackage- Backward compatible: existing API keys continue to work unchanged
- Cleaner architecture with proper separation of concerns
- Added
code-wheel/mcp-http-security: ^1.0requirement
- 100% service test coverage: All 48 services now have unit or kernel tests
- New kernel tests for contrib-dependent services: EntityClone, Metatag, Pathauto, Scheduler, SearchApi, Sitemap, UltimateCron
- Extended StructureServicesKernelTest with TaxonomyService coverage
- Added kernel tests for Redirect, Webform services
- ToolSchemaKernelTest expanded with MCP contract validations
- Admin UI improvements:
- Permissions tab showing tool access by scope/category
- Remote settings tab for HTTP endpoint configuration
- Docker development environment:
docker-compose.yml+ setup scripts for contributors - Strategic roadmap: Community-driven roadmap items for post-adoption phase
date()type errors in AnalyzePerformance and other analysis tools- Unit tests no longer require contrib module dependencies
- Clarified contrib module extraction plans (separate module, not submodule)
- Configuration mode presets: Development, Staging, Production modes with sensible defaults
- Development: Full access, no rate limiting
- Staging: Config-only mode, rate limited, audit logging
- Production: Read-only mode, strict limits, full audit
- Compound operations in
mcp_tools_structure:mcp_structure_scaffold_content_type- Create content type + fields in one callmcp_structure_setup_taxonomy- Create vocabulary + terms with hierarchy
- Text format tools in base module:
mcp_list_text_formats- List available text formatsmcp_get_text_format- Get format details including allowed HTML
- Architecture documentation: New
docs/ARCHITECTURE.mdwith design patterns, security model
- idempotentHint now set for read operations (TRUE for safe-to-retry ops)
- ROADMAP.md updated with completed P1 items
- 6 new schema discovery tools in
mcp_tools_structurefor AI introspection:mcp_structure_list_content_types- List all content types with field countsmcp_structure_get_content_type- Get full field schema with types, cardinality, and allowed valuesmcp_structure_list_vocabularies- List all vocabularies with term countsmcp_structure_get_vocabulary- Get vocabulary details with terms and hierarchymcp_structure_list_roles- List all roles with permission/user countsmcp_structure_get_role_permissions- Get permissions grouped by provider module
destructive: TRUEflag added to 6 more tools: DisableTheme, DisableView, DisableJob, DisableLayoutBuilder, CancelSchedule, RevokePermissions (38 total destructive tools)- Remediation hints added to 32 error messages across 12 services, guiding Claude to the correct discovery tools
- 181 tool files updated with rich, meaningful descriptions for all inputs and outputs
- All empty
TranslatableMarkup('')entries replaced with helpful descriptions explaining:- Expected formats and valid values
- Field type documentation (entity_reference, text_with_summary, etc.)
- When to use each tool and relationships between tools
- ROADMAP.md cleaned up and simplified, removing completed phases
- Error messages now consistently reference discovery tools (e.g., "Content type 'blog' not found. Use mcp_structure_list_content_types to see available types.")
- Expanded unit test coverage for analysis/config/templates/batch/migration services to improve reliability and raise overall coverage.
- Batch and migration operations now create entities via injected storages (instead of static
::create()calls) to improve testability and consistency.
- Template application now logs audit events with the correct parameters (avoids runtime errors when audit logging is enabled).
- Optional Origin/Host allowlist for the remote HTTP endpoint (
mcp_tools_remote.settings.allowed_origins) as defense-in-depth against DNS rebinding. - JS MCP SDK STDIO smoke test (
scripts/mcp_js_sdk_compat.mjs) wired into CI to catch strict-client schema regressions early. - HTTP transport E2E coverage for Origin/Host allowlist behavior.
- Regression coverage for tools with no inputs to ensure MCP JSON Schemas encode empty
propertiesas{}.
- STDIO usage docs/examples now include
--uidand recommend a dedicated execution user for shared environments.
- Tools with no inputs now return an MCP
inputSchema.propertiesvalue that encodes as an object ({}), improving compatibility with strict MCP clients (Codex/Claude Code).
- DrupalCI (
.gitlab-ci.yml) configuration to test across a broader core/PHP matrix. drush mcp-tools:remote-setupto create a dedicated remote execution user/role and configuremcp_tools_remote.settings.uid.- Expanded config preview support: role create/delete, grant/revoke permissions, delete content type, and delete field preview operations.
DRUPALCI.mdand local testing notes to make CI failures easier to triage.
- Audit logs now include a per-tool-call correlation ID, transport, client identifier, and active scopes.
- Status page now shows the remote HTTP endpoint (when enabled) and warns on risky remote settings (uid 1, empty allowlist, include-all-tools).
- CI "full tool registration" now validates schema conversion for all tools (not just registration count).
- STDIO E2E sets a known baseline for read-only/config-only so local reruns are deterministic.
- Remote HTTP transport now refuses to execute as uid 1 (runtime enforcement, not just UI validation).
- HTTP transport E2E now validates IP allowlist enforcement and runs as a dedicated service user with only the required
mcp_tools use …permissions. - Drupal.org description and README now highlight read-only default scopes and include recommended starter bundles.
- Remote HTTP hardening: optional IP allowlist (
mcp_tools_remote.settings.allowed_ips) and API key TTL support (drush mcp-tools:remote-key-create --ttl=...). - Kernel coverage to ensure all core tool definitions convert cleanly to MCP tool annotations + JSON schemas.
- HTTP transport E2E now validates config-only mode (config writes allowed, ops writes denied).
- Default connection scopes are now read-only by default (
access.default_scopes: [read]). - Admin-scope tools are now declared as
ToolOperation::Triggerso they can be gated by admin scope at the Tool API access layer (recipes/templates/config export). - Rate limiting now ignores the client-provided
X-MCP-Client-Idheader by default; opt-in viarate_limiting.trust_client_id_header.
- MCP-scoped config-change tracking via a tool-call context and a
config.save/delete/renamesubscriber (enablesmcp_config_mcp_changesto reflect real tool activity). - Unit/kernel coverage for previously untested core services: Analysis (broken links), Recipes, and Structure (content types + fields).
codecov.ymlto enforce high patch coverage while overall coverage ramps up.
mcp_analysis_broken_linksadds an optionalbase_urlinput for STDIO/CLI usage.- Broken-link scanner now enforces the
allowed_hostsallowlist and blocks redirects to non-allowlisted hosts. - CI no longer runs Drupal 11 on PHP 8.3 (Composer now requires PHP 8.4+ for current Drupal 11 releases).
- Additional unit coverage for Tool API access gating, cron/cache/image styles, and moderation services.
- Refactored multiple services to use dependency injection (improves testability and reduces static
\Drupal::*usage). - Entity creation in write services now uses storage
->create()instead of static entity::create()helpers.
- Cron job discovery now uses
ModuleHandlerInterface::invokeAllWith()(prevents calling a non-existentgetImplementations()method). mcp_tools_recipesnow registerslogger.channel.mcp_tools_recipesso the service container compiles cleanly in kernel tests.
- Tool metadata lint test to prevent read/write operation regressions.
- Kernel coverage for config-only mode gating by write kind.
mcp_upload_filenow correctly declares a write operation (requires write scope).- Status page now shows config-only mode status.
- Config-only mode (restrict write tools to configuration changes) with configurable allowed write kinds.
- Corrected multiple Tool API operation declarations so write tools are not incorrectly exposed as read operations.
- Enforced correct write-scope gating for tools that mutate configuration or operational state (e.g., block placement, pathauto alias generation, Search API indexing).
- Unit coverage for schema conversion, error formatting, config management previews/exports, and remote controller failure paths.
- Additional unit coverage for batch/migration helpers and watchdog message formatting.
- Code coverage job now runs unit + kernel + functional suites and excludes contrib-dependent submodules (unless their dependencies are installed).
- Release workflow now runs submodule unit/kernel/functional tests (not just base module tests).
- Base64 uploads are capped and block dangerous executable extensions by default.
- Serialized watchdog/metatag parsing now enforces size limits to prevent memory exhaustion.
- End-to-end MCP transport checks (STDIO + HTTP) wired into CI and release workflows.
drush mcp-tools:serveno longer writes human output to STDOUT (prevents corrupting the STDIO JSON-RPC stream).mcp_tools_remoteHTTP transport now uses a persistent session store (prevents 404 "session not found" afterinitialize).- Drush command registration moved to per-submodule
drush.services.ymlfiles (avoids runtime dependency on Drush and fixes command discovery). - Tool execution output handling now tolerates incomplete Tool API output contexts (prevents MCP tool calls failing with "provided context ... is not valid").
- Unit coverage for
TemplateServiceandWebhookNotifier(signature + redaction + SSRF blocking).
- Functional UI tests now grant
access administration pagesand assert anonymous access denial robustly across login/403 configurations. - CI now installs Drupal dev dependencies with
--with-all-dependenciesto avoid Drupal 11 Composer lock conflicts (e.g.sebastian/diffvs PHPUnit).
mcp_tools_mcp_serveroptional bridge submodule (depends ondrupal/mcp_server) with a Drush sync command to generate MCP Server tool configs for MCP Tools.
mcp_tools_stdiosubmodule to run an MCP server over STDIO via Drush (drush mcp-tools:serve).mcp_tools_remotesubmodule to expose an HTTP MCP endpoint at/_mcp_toolsusing API key authentication.- MCP server bridge classes for exposing Tool API tools via the official
mcp/sdk. - Unit test to ensure every tool category has a corresponding permission, plus unit coverage for remote API key management.
- Added missing
mcp_tools use {category}permissions so non-uid1 roles can be granted access to all tool categories.
- Kernel smoke coverage to instantiate all core tools (144) and ensure
access()never throws.
- CI tool registration check now enforces a minimum of 144 MCP Tools tools (core-only install).
SystemStatusServiceno longer references non-existentSystemManager::REQUIREMENT_INFO(Drupal 10/11).
- Tool API plugin implementations for all MCP Tools (205 tools) using PHP attributes (
#[Tool(...)]) andPlugin/tool/Tooldiscovery. Drupal\\mcp_tools\\Tool\\McpToolsToolBasewrapper to adapt legacy MCP Tools responses into Tool APIExecutableResultobjects and enforce category permissions + MCP scopes.- Access configuration hardening:
access.allowed_scopesplus trust toggles for reading scopes from headers, query params, and environment variables. - Webhook host allowlist via
webhooks.allowed_hosts. - Read-operation rate limiting helpers and expanded kernel coverage.
- Kernel coverage for Tool API discovery/registration.
- Minimum requirements: Drupal
^10.3 || ^11, PHP>=8.3. - MCP Server is optional (only required when exposing tools over MCP).
- Drupal 11 / Symfony 7 test compatibility and Tool API listing compatibility.
- Scope override values are intersected with
access.allowed_scopesto prevent accidental privilege escalation via scope injection. - Configuration analysis and audit logging redact sensitive values by default.
- Site Health: GetSiteStatus, GetSystemStatus, CheckSecurityUpdates, CheckCronStatus, AnalyzeWatchdog, GetQueueStatus, GetFileSystemStatus
- Content: ListContentTypes, GetRecentContent, SearchContent, GetVocabularies, GetTerms, GetFiles, FindOrphanedFiles
- Configuration: GetConfigStatus, GetConfig, ListConfig
- Users: GetRoles, GetUsers, GetPermissions
- Structure: GetMenus, GetMenuTree
- AccessManager - Three-layer access control (module-based, global read-only mode, connection scopes)
- RateLimiter - Per-client, per-operation-type rate limiting with configurable limits
- AuditLogger - Operation logging with sensitive data redaction
- WebhookNotifier - HMAC-signed webhook notifications for external systems
- Settings form at
/admin/config/services/mcp-tools - Status page at
/admin/config/services/mcp-tools/status - Configurable access control, rate limiting, and webhook settings
| Submodule | Tools | Description |
|---|---|---|
| mcp_tools_content | 4 | Content CRUD (create, update, delete, publish) |
| mcp_tools_structure | 12 | Content types, fields, taxonomy, roles, permissions |
| mcp_tools_users | 5 | User management (create, update, block, roles) |
| mcp_tools_menus | 5 | Menu management (menus and links) |
| mcp_tools_views | 6 | Views creation and management |
| mcp_tools_blocks | 5 | Block placement and configuration |
| mcp_tools_media | 6 | Media types, uploads, entities |
| mcp_tools_webform | 7 | Webform creation and submissions |
| mcp_tools_theme | 8 | Theme settings and management |
| mcp_tools_layout_builder | 9 | Layout Builder sections and blocks |
| mcp_tools_recipes | 6 | Drupal Recipes (10.3+) |
| mcp_tools_config | 5 | Configuration export and tracking |
| mcp_tools_paragraphs | 6 | Paragraphs type and field management |
| mcp_tools_moderation | 6 | Content Moderation workflows and states |
| mcp_tools_scheduler | 5 | Scheduled publish/unpublish (contrib) |
| mcp_tools_metatag | 5 | SEO meta tags management (contrib) |
| mcp_tools_image_styles | 7 | Image styles and effects |
| mcp_tools_cache | 6 | Cache management and invalidation |
| mcp_tools_cron | 5 | Cron execution and queue processing |
| mcp_tools_ultimate_cron | 6 | Ultimate Cron job management (contrib) |
| mcp_tools_pathauto | 6 | URL alias patterns (contrib) |
| mcp_tools_redirect | 7 | URL redirects (contrib) |
| mcp_tools_sitemap | 7 | XML sitemap management (contrib) |
| mcp_tools_search_api | 8 | Search API index management (contrib) |
| mcp_tools_entity_clone | 4 | Entity cloning (contrib) |
| mcp_tools_analysis | 8 | Site analysis (SEO, a11y, security, performance) |
| mcp_tools_batch | 6 | Bulk operations (create, update, delete, publish) |
| mcp_tools_templates | 5 | Site templates (blog, portfolio, business, docs) |
| mcp_tools_migration | 7 | Content import/export (CSV, JSON) |
- Three-layer access control - Modules, global toggle, connection scopes
- Rate limiting - Configurable per-minute/hour limits by operation type (now includes admin operations)
- Audit logging - All operations logged with user info
- SSRF protection - Webhook URLs validated against private IPs and cloud metadata services
- Role escalation protection - Pattern-based blocking of admin/super roles
- Import field protection - Protected fields (uid, nid, moderation_state, etc.) cannot be set via import
- Menu URI validation - Strict scheme whitelist prevents javascript:/data: XSS attacks
- Secure session identification - Rate limiter uses system-level identifiers resistant to spoofing
- Sensitive data redaction - Passwords and secrets never logged or sent
- Protected entities - uid 1, administrator role, system menus, core views
- Dangerous permissions blocked - Cannot grant admin permissions via MCP
- Webhook signatures - HMAC-SHA256 signed webhook payloads
- Unit tests for AccessManager, RateLimiter, AuditLogger
- Kernel tests for access control and rate limiting integration
- Security-focused tests for bypass prevention
| Category | Tools |
|---|---|
| Base module (read-only) | 22 |
| mcp_tools_content | 4 |
| mcp_tools_structure | 12 |
| mcp_tools_users | 5 |
| mcp_tools_menus | 5 |
| mcp_tools_views | 6 |
| mcp_tools_blocks | 5 |
| mcp_tools_media | 6 |
| mcp_tools_webform | 7 |
| mcp_tools_theme | 8 |
| mcp_tools_layout_builder | 9 |
| mcp_tools_recipes | 6 |
| mcp_tools_config | 5 |
| mcp_tools_paragraphs | 6 |
| mcp_tools_moderation | 6 |
| mcp_tools_scheduler | 5 |
| mcp_tools_metatag | 5 |
| mcp_tools_image_styles | 7 |
| mcp_tools_cache | 6 |
| mcp_tools_cron | 5 |
| mcp_tools_ultimate_cron | 6 |
| mcp_tools_pathauto | 6 |
| mcp_tools_redirect | 7 |
| mcp_tools_sitemap | 7 |
| mcp_tools_search_api | 8 |
| mcp_tools_entity_clone | 4 |
| mcp_tools_analysis | 8 |
| mcp_tools_batch | 6 |
| mcp_tools_templates | 5 |
| mcp_tools_migration | 7 |
| Total | 205 |