PHPackages                             iqual/icms\_mcp - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. iqual/icms\_mcp

ActiveDrupal-module[Utility &amp; Helpers](/categories/utility)

iqual/icms\_mcp
===============

Drupal MCP tools for AI-powered, transactional ICMS content migration and import.

015↑2700%PHP

Since Jun 19Pushed todayCompare

[ Source](https://github.com/iqual-ch/icms_mcp)[ Packagist](https://packagist.org/packages/iqual/icms_mcp)[ RSS](/packages/iqual-icms-mcp/feed)WikiDiscussions main Synced today

READMEChangelogDependenciesVersions (2)Used By (0)

icms\_mcp — Drupal module
=========================

[](#icms_mcp--drupal-module)

Custom MCP plugin that exposes ICMS-specific tools to MCP clients (in our case, the iqual `drupal-bridge` ADK agent on Cloud Run).

What it ships
-------------

[](#what-it-ships)

Four tools. On the wire the names become `icms-mcp_`(drupal/mcp prepends the plugin id and `_`; note the hyphen — see "Plugin ID gotcha" below):

Tool (wire name)Purpose`icms-mcp_get_icms_catalog`Live `nodeTypes` / `paragraphTypes` / `allowedParagraphBundles` introspected from this site.`icms-mcp_validate_pivot`Drupal-side validation of an `icms-drupal-import-handoff-v1` pivot.`icms-mcp_import_pivot`Transactional create/update of node + paragraphs. Honours `strategy` and HITL gate.`icms-mcp_lookup_existing_node`Idempotency lookup by canonical source URL (matches against the configured source-key field).### Plugin ID gotcha

[](#plugin-id-gotcha)

drupal/mcp's `McpPluginManager::getAvailablePlugins()` enforces `/^[a-zA-Z0-9-]+$/` on plugin IDs — **underscores are forbidden**. That's why the `#[Mcp]` attribute uses `id: 'icms-mcp'` (hyphen) even though the module name is `icms_mcp` (underscore, as Drupal requires). If you ever see `tools/list` silently drop your plugin, check `/admin/reports/dblog` for `InvalidArgumentException: "Plugin ID must be made of letters, numbers, and hyphens"`.

### Wire perf gotcha

[](#wire-perf-gotcha)

drupal/mcp's bundled `drush` MCP plugin exposes ~230 tools and each one runs an access check that costs ~300–700ms in DDEV, which makes `tools/list` time out (~4 min wall-clock). Disable plugins you don't need:

```
ddev drush php:eval '$c=\Drupal::configFactory()->getEditable("mcp.settings"); $p=$c->get("plugins"); foreach (["drush","content","aif","jsonapi","tools","aia"] as $k) { $p[$k]["enabled"]=false; } $c->set("plugins",$p)->save();'
ddev drush cr
```

Behaviour summary
-----------------

[](#behaviour-summary)

- **Validation runs on every import.** `import_pivot` calls `validate_pivot`first and refuses to write a contract with any issues.
- **HITL gate.** If `metadata.review_decision == "review_required"`, the import returns `status: pending_review` unless the caller passes `approve: true`. `review_decision == "blocked"` always refuses.
- **Strategies** (`metadata.strategy`):
    - `skip` → if a node with the same `idempotence_key` exists, return `status: skipped` without writing.
    - `update` / `skip-or-update` → load and rewrite the existing node, replacing its paragraph children (old paragraphs are deleted after the node save commits, so a failure leaves the previous content intact). Cleanup uses paragraph entity IDs rather than historical revision IDs, allowing an update to repair a node that contains a stale paragraph revision reference.
    - `fail-if-exists` → returns `status: conflict` if a node with the same `idempotence_key` already exists.
- **Transactional.** All writes run inside a `database->startTransaction()`. Any throw inside the write path triggers a `rollBack()` and surfaces as `status: error`.
- **Audit journal.** Every outcome (`created` / `updated` / `skipped` / `conflict` / `error`) is logged to the `icms_mcp` logger channel with `idempotence_key`, `batch_id`, `run_id` in the context so you can correlate in `/admin/reports/dblog`.
- **Private pivot log.** Every payload received by `validate_pivot` or `import_pivot` is saved as formatted JSON under `private://icms_mcp/pivots`. The resulting URI is returned as `pivot_log_uri`. Disable this diagnostic log with `drush state:set icms_mcp.log_pivots 0`.

Prerequisites on the target site
--------------------------------

[](#prerequisites-on-the-target-site)

Two field machine names are configurable via `state` (defaults shown):

State keyDefaultPurpose`icms_mcp.source_key_field``field_icms_source_key`Plain string field (max 512) on `icms_page` storing the agent's `idempotence_key`.`icms_mcp.layouts_field``field_icms_paragraphs``entity_reference_revisions` field on `icms_page` that holds layout paragraphs.This module **does not auto-create those fields** — fields are part of your site configuration. If the defaults don't match your install:

```
ddev drush state:set icms_mcp.source_key_field field_my_source_key
ddev drush state:set icms_mcp.layouts_field field_my_paragraphs
```

`validate_pivot` and `get_icms_catalog` both surface a clear error when either field is missing, so misconfiguration is caught before the first import attempt.

Install
-------

[](#install)

Requires `drupal/mcp ^1.0`.

```
ddev composer require 'iqual/icms_mcp'
ddev drush en icms_mcp -y
```

Configure
---------

[](#configure)

1. `/admin/config/mcp` — enable token auth and the `icms-mcp` plugin (hyphen, not underscore — see "Plugin ID gotcha" above).
2. Create a dedicated Drupal user for the agent. Grant **only** the `Use MCP server` permission (from drupal/mcp) and the `Use ICMS MCP tools` permission (from this module). No other roles.

Why a plugin and not a custom REST/JSON:API endpoint?
-----------------------------------------------------

[](#why-a-plugin-and-not-a-custom-restjsonapi-endpoint)

Reuse + provenance. Every MCP-enabled client (Claude Desktop / Cursor / the ADK `McpToolset`) discovers the tools automatically, the auth + RBAC layer comes from `drupal/mcp`, and we get the streamable-HTTP transport, schema validation, and permission gating for free.

###  Health Score

23

—

LowBetter than 26% of packages

Maintenance65

Regular maintenance activity

Popularity8

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity13

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://www.gravatar.com/avatar/43082de1b2e2b2b2d48b7ff7dbc7c60ff1d0a015123d4062ac0b49ea0b3b725f?d=identicon)[iqual](/maintainers/iqual)

---

Top Contributors

[![youpixxl](https://avatars.githubusercontent.com/u/3398689?v=4)](https://github.com/youpixxl "youpixxl (9 commits)")

### Embed Badge

![Health badge](/badges/iqual-icms-mcp/health.svg)

```
[![Health](https://phpackages.com/badges/iqual-icms-mcp/health.svg)](https://phpackages.com/packages/iqual-icms-mcp)
```

###  Alternatives

[classpreloader/classpreloader

Helps class loading performance by generating a single PHP file containing all of the autoloaded files for a specific use case

37642.7M32](/packages/classpreloader-classpreloader)[ronanguilloux/php-gpio

GPIO-related utils &amp; toolkit PHP library

2678.0k](/packages/ronanguilloux-php-gpio)[elephox/mimey

PHP package for converting file extensions to MIME types and vice versa.

14240.0k5](/packages/elephox-mimey)[kicken/gearman-php

A PHP implementation of the Gearman protocol.

1662.8k3](/packages/kicken-gearman-php)[heyday/silverstripe-wkhtml

Provides Wkhtml functionality in SilverStripe

1539.2k1](/packages/heyday-silverstripe-wkhtml)[log1x/acf-move-wp-editor

A simple ACF Field that moves the WordPress content editor of a post or page to the location of this field.

361.1k](/packages/log1x-acf-move-wp-editor)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
