drupal-mcp-connector 2.7.4 → 2.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{.claude → .agents}/commands/drupal-audit-config-best-practices.md +3 -4
- package/{.claude → .agents}/commands/drupal-audit-site-health.md +3 -4
- package/{.claude → .agents}/commands/drupal-block-user.md +3 -4
- package/{.claude → .agents}/commands/drupal-bulk-create.md +3 -4
- package/{.claude → .agents}/commands/drupal-bulk-update.md +3 -4
- package/{.claude → .agents}/commands/drupal-config-get.md +3 -4
- package/{.claude → .agents}/commands/drupal-config-list.md +3 -4
- package/{.claude → .agents}/commands/drupal-config-set.md +3 -4
- package/{.claude → .agents}/commands/drupal-content-by-moderation-state.md +3 -4
- package/{.claude → .agents}/commands/drupal-create-block.md +3 -4
- package/{.claude → .agents}/commands/drupal-create-media.md +3 -4
- package/{.claude → .agents}/commands/drupal-create-menu-link.md +3 -4
- package/{.claude → .agents}/commands/drupal-create-node.md +6 -7
- package/{.claude → .agents}/commands/drupal-create-paragraph.md +3 -4
- package/{.claude → .agents}/commands/drupal-create-redirect.md +3 -4
- package/{.claude → .agents}/commands/drupal-create-taxonomy-term.md +3 -4
- package/{.claude → .agents}/commands/drupal-create-translation.md +3 -4
- package/{.claude → .agents}/commands/drupal-create-user.md +3 -4
- package/{.claude → .agents}/commands/drupal-delete-media.md +3 -4
- package/{.claude → .agents}/commands/drupal-delete-node.md +3 -4
- package/{.claude → .agents}/commands/drupal-delete-taxonomy-term.md +3 -4
- package/{.claude → .agents}/commands/drupal-describe-fields.md +3 -4
- package/{.claude → .agents}/commands/drupal-drush-cache-rebuild.md +3 -4
- package/{.claude → .agents}/commands/drupal-drush-config-export.md +3 -4
- package/{.claude → .agents}/commands/drupal-drush-config-import.md +3 -4
- package/{.claude → .agents}/commands/drupal-drush-config-status.md +3 -4
- package/{.claude → .agents}/commands/drupal-drush-cron.md +3 -4
- package/{.claude → .agents}/commands/drupal-drush-module-disable.md +3 -4
- package/{.claude → .agents}/commands/drupal-drush-module-enable.md +3 -4
- package/{.claude → .agents}/commands/drupal-drush-module-list.md +3 -4
- package/{.claude → .agents}/commands/drupal-drush-security-updates.md +3 -4
- package/{.claude → .agents}/commands/drupal-drush-sql-query.md +3 -4
- package/{.claude → .agents}/commands/drupal-drush-status.md +3 -4
- package/{.claude → .agents}/commands/drupal-drush-updatedb.md +3 -4
- package/{.claude → .agents}/commands/drupal-drush-user-create.md +3 -4
- package/{.claude → .agents}/commands/drupal-drush-user-list.md +3 -4
- package/{.claude → .agents}/commands/drupal-drush-watchdog.md +3 -4
- package/{.claude → .agents}/commands/drupal-entity-create.md +3 -4
- package/{.claude → .agents}/commands/drupal-entity-delete.md +3 -4
- package/{.claude → .agents}/commands/drupal-entity-get.md +3 -4
- package/{.claude → .agents}/commands/drupal-entity-list.md +3 -4
- package/{.claude → .agents}/commands/drupal-entity-update.md +6 -7
- package/{.claude → .agents}/commands/drupal-find-orphaned-media.md +3 -4
- package/{.claude → .agents}/commands/drupal-get-entity-schema.md +3 -4
- package/{.claude → .agents}/commands/drupal-get-media.md +3 -4
- package/{.claude → .agents}/commands/drupal-get-node.md +3 -4
- package/{.claude → .agents}/commands/drupal-get-paragraph.md +3 -4
- package/{.claude → .agents}/commands/drupal-get-revision.md +3 -4
- package/{.claude → .agents}/commands/drupal-get-taxonomy-term.md +3 -4
- package/{.claude → .agents}/commands/drupal-get-taxonomy-terms.md +3 -4
- package/{.claude → .agents}/commands/drupal-get-user-by-name.md +3 -4
- package/{.claude → .agents}/commands/drupal-get-user.md +3 -4
- package/{.claude → .agents}/commands/drupal-governance-status.md +3 -4
- package/{.claude → .agents}/commands/drupal-graphql-introspect.md +3 -4
- package/{.claude → .agents}/commands/drupal-graphql.md +3 -4
- package/{.claude → .agents}/commands/drupal-list-blocks.md +3 -4
- package/{.claude → .agents}/commands/drupal-list-content-types.md +3 -4
- package/{.claude → .agents}/commands/drupal-list-entity-types.md +3 -4
- package/{.claude → .agents}/commands/drupal-list-media-types.md +3 -4
- package/{.claude → .agents}/commands/drupal-list-media.md +3 -4
- package/{.claude → .agents}/commands/drupal-list-menu-links.md +3 -4
- package/{.claude → .agents}/commands/drupal-list-moderation-states.md +3 -4
- package/{.claude → .agents}/commands/drupal-list-nodes.md +3 -4
- package/{.claude → .agents}/commands/drupal-list-revisions.md +3 -4
- package/{.claude → .agents}/commands/drupal-list-roles.md +3 -4
- package/{.claude → .agents}/commands/drupal-list-sites.md +2 -3
- package/{.claude → .agents}/commands/drupal-list-translations.md +3 -4
- package/{.claude → .agents}/commands/drupal-list-users.md +3 -4
- package/{.claude → .agents}/commands/drupal-list-vocabularies.md +3 -4
- package/{.claude → .agents}/commands/drupal-mcp-whoami.md +3 -4
- package/{.claude → .agents}/commands/drupal-report-404-log.md +3 -4
- package/{.claude → .agents}/commands/drupal-report-accessibility-audit.md +3 -4
- package/{.claude → .agents}/commands/drupal-report-alias-coverage.md +3 -4
- package/{.claude → .agents}/commands/drupal-report-broken-embeds.md +3 -4
- package/{.claude → .agents}/commands/drupal-report-broken-links.md +3 -4
- package/{.claude → .agents}/commands/drupal-report-cache-config.md +3 -4
- package/{.claude → .agents}/commands/drupal-report-config-drift.md +3 -4
- package/{.claude → .agents}/commands/drupal-report-content-by-author.md +3 -4
- package/{.claude → .agents}/commands/drupal-report-content-summary.md +3 -4
- package/{.claude → .agents}/commands/drupal-report-duplicate-content.md +3 -4
- package/{.claude → .agents}/commands/drupal-report-field-completeness.md +3 -4
- package/{.claude → .agents}/commands/drupal-report-menu-integrity.md +3 -4
- package/{.claude → .agents}/commands/drupal-report-missing-field.md +3 -4
- package/{.claude → .agents}/commands/drupal-report-module-audit.md +3 -4
- package/{.claude → .agents}/commands/drupal-report-orphan-pages.md +3 -4
- package/{.claude → .agents}/commands/drupal-report-orphaned-references.md +3 -4
- package/{.claude → .agents}/commands/drupal-report-permission-audit.md +3 -4
- package/{.claude → .agents}/commands/drupal-report-pii-exposure.md +3 -4
- package/{.claude → .agents}/commands/drupal-report-readability.md +3 -4
- package/{.claude → .agents}/commands/drupal-report-recently-published.md +3 -4
- package/{.claude → .agents}/commands/drupal-report-redirect-health.md +3 -4
- package/{.claude → .agents}/commands/drupal-report-revision-hotspots.md +3 -4
- package/{.claude → .agents}/commands/drupal-report-scheduled-content.md +3 -4
- package/{.claude → .agents}/commands/drupal-report-seo-audit.md +3 -4
- package/{.claude → .agents}/commands/drupal-report-seo-meta-coverage.md +3 -4
- package/{.claude → .agents}/commands/drupal-report-stale-content.md +3 -4
- package/{.claude → .agents}/commands/drupal-report-status-report.md +3 -4
- package/{.claude → .agents}/commands/drupal-report-taxonomy-usage.md +3 -4
- package/{.claude → .agents}/commands/drupal-report-text-format-audit.md +3 -4
- package/{.claude → .agents}/commands/drupal-report-translation-coverage.md +3 -4
- package/{.claude → .agents}/commands/drupal-report-unpublished.md +3 -4
- package/{.claude → .agents}/commands/drupal-report-user-activity.md +3 -4
- package/{.claude → .agents}/commands/drupal-report-workflow-bottlenecks.md +3 -4
- package/{.claude → .agents}/commands/drupal-resolve-reference.md +3 -4
- package/{.claude → .agents}/commands/drupal-revert-revision.md +3 -4
- package/{.claude → .agents}/commands/drupal-schedule-publish.md +3 -4
- package/{.claude → .agents}/commands/drupal-search-content.md +3 -4
- package/{.claude → .agents}/commands/drupal-search.md +3 -4
- package/{.claude → .agents}/commands/drupal-security-info.md +3 -4
- package/{.claude → .agents}/commands/drupal-set-moderation-state.md +3 -4
- package/{.claude → .agents}/commands/drupal-site-info.md +3 -4
- package/{.claude → .agents}/commands/drupal-update-media.md +3 -4
- package/{.claude → .agents}/commands/drupal-update-menu-link.md +3 -4
- package/{.claude → .agents}/commands/drupal-update-node.md +9 -10
- package/{.claude → .agents}/commands/drupal-update-paragraph.md +3 -4
- package/{.claude → .agents}/commands/drupal-update-redirect.md +3 -4
- package/{.claude → .agents}/commands/drupal-update-taxonomy-term.md +3 -4
- package/{.claude → .agents}/commands/drupal-update-user.md +3 -4
- package/{.claude → .agents}/commands/drupal-upload-file-and-create-media.md +3 -4
- package/{.claude → .agents}/commands/drupal-upload-file.md +3 -4
- package/CHANGELOG.md +56 -0
- package/README.md +21 -11
- package/config/config.example.json +1 -1
- package/package.json +4 -2
- package/scripts/generate-commands.js +42 -21
- package/scripts/install-commands.js +157 -0
- package/src/lib/backends/backend-interface.js +20 -1
- package/src/lib/backends/jsonapi.js +47 -4
- package/src/lib/body-summary.js +60 -0
- package/src/lib/data-flow.js +437 -0
- package/src/lib/dispatch.js +36 -15
- package/src/lib/drupal-fetch.js +52 -6
- package/src/lib/field-definition.js +285 -0
- package/src/lib/patch-preflight.js +157 -66
- package/src/lib/security.js +14 -0
- package/src/lib/server-tools.js +9 -0
- package/src/lib/write-revision.js +82 -2
- package/src/tools/bulk.js +4 -3
- package/src/tools/entities.js +11 -7
- package/src/tools/index.js +3 -2
- package/src/tools/nodes.js +55 -43
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: "User activity summary: active vs blocked accounts, never-logged-in users, and users inactive beyond a threshold. Useful for security audits and account hygiene."
|
|
3
3
|
argument-hint: "[site] [inactiveDays] [limit]"
|
|
4
|
-
allowed-tools: mcp__drupal__drupal_report_user_activity
|
|
5
4
|
---
|
|
6
5
|
|
|
7
|
-
Call the
|
|
6
|
+
Call the MCP tool `drupal_report_user_activity`.
|
|
8
7
|
|
|
9
8
|
User activity summary: active vs blocked accounts, never-logged-in users, and users inactive beyond a threshold. Useful for security audits and account hygiene.
|
|
10
9
|
|
|
11
|
-
Parse the
|
|
10
|
+
Parse the arguments supplied with this command into this tool's parameters:
|
|
12
11
|
|
|
13
12
|
**Optional:**
|
|
14
13
|
- `site` (string): Named site from connector config. Omit only on reads: multi-site configs fall back to defaultSite (often local/dev, not production). Writes require an explicit site when more than one site is configured. Every response includes `_target` { name, baseUrl, source } (`hint` when you passed site, `default` when you did not).
|
|
15
14
|
- `inactiveDays` (number): Days without login to flag as inactive
|
|
16
15
|
- `limit` (number)
|
|
17
16
|
|
|
18
|
-
If a required parameter is missing
|
|
17
|
+
If a required parameter is missing, ask before calling — do not invent values. Coerce each value to its JSON type (booleans → true/false, numbers → numeric, object/array → parse JSON), then make the single tool call and summarize the result.
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: "Find content stuck in a non-published moderation state (draft/needs_review) beyond N days — editorial bottlenecks. Reads moderation_state; gated when content_moderation isn't exposed."
|
|
3
3
|
argument-hint: "[site] [type] [days] [states] [sampleSize]"
|
|
4
|
-
allowed-tools: mcp__drupal__drupal_report_workflow_bottlenecks
|
|
5
4
|
---
|
|
6
5
|
|
|
7
|
-
Call the
|
|
6
|
+
Call the MCP tool `drupal_report_workflow_bottlenecks`.
|
|
8
7
|
|
|
9
8
|
Find content stuck in a non-published moderation state (draft/needs_review) beyond N days — editorial bottlenecks. Reads moderation_state; gated when content_moderation isn't exposed.
|
|
10
9
|
|
|
11
|
-
Parse the
|
|
10
|
+
Parse the arguments supplied with this command into this tool's parameters:
|
|
12
11
|
|
|
13
12
|
**Optional:**
|
|
14
13
|
- `site` (string): Named site from connector config. Omit only on reads: multi-site configs fall back to defaultSite (often local/dev, not production). Writes require an explicit site when more than one site is configured. Every response includes `_target` { name, baseUrl, source } (`hint` when you passed site, `default` when you did not).
|
|
@@ -17,4 +16,4 @@ Parse the request in `$ARGUMENTS` into this tool's parameters:
|
|
|
17
16
|
- `states` (array (pass as JSON)): Moderation states to treat as bottlenecks
|
|
18
17
|
- `sampleSize` (number)
|
|
19
18
|
|
|
20
|
-
If a required parameter is missing
|
|
19
|
+
If a required parameter is missing, ask before calling — do not invent values. Coerce each value to its JSON type (booleans → true/false, numbers → numeric, object/array → parse JSON), then make the single tool call and summarize the result.
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: "Resolve a human name or title to a Drupal entity UUID. Use this before creating or updating an entity reference when you only know the label (e.g. a taxonomy term name, a user name, or a node title) and not its UUID. Read-only: returns the best match { id, title } plus any ambiguous candidates. Filters on 'title' for nodes and 'name' for taxonomy_term / user."
|
|
3
3
|
argument-hint: "<entityType> <bundle> <name> [site] [limit]"
|
|
4
|
-
allowed-tools: mcp__drupal__drupal_resolve_reference
|
|
5
4
|
---
|
|
6
5
|
|
|
7
|
-
Call the
|
|
6
|
+
Call the MCP tool `drupal_resolve_reference`.
|
|
8
7
|
|
|
9
8
|
Resolve a human name or title to a Drupal entity UUID. Use this before creating or updating an entity reference when you only know the label (e.g. a taxonomy term name, a user name, or a node title) and not its UUID. Read-only: returns the best match { id, title } plus any ambiguous candidates. Filters on 'title' for nodes and 'name' for taxonomy_term / user.
|
|
10
9
|
|
|
11
|
-
Parse the
|
|
10
|
+
Parse the arguments supplied with this command into this tool's parameters:
|
|
12
11
|
|
|
13
12
|
**Required:**
|
|
14
13
|
- `entityType` (string): Entity type machine name, e.g. 'node', 'taxonomy_term', 'user'
|
|
@@ -19,4 +18,4 @@ Parse the request in `$ARGUMENTS` into this tool's parameters:
|
|
|
19
18
|
- `site` (string): Named site from connector config. Omit only on reads: multi-site configs fall back to defaultSite (often local/dev, not production). Writes require an explicit site when more than one site is configured. Every response includes `_target` { name, baseUrl, source } (`hint` when you passed site, `default` when you did not).
|
|
20
19
|
- `limit` (number): Maximum candidates to consider
|
|
21
20
|
|
|
22
|
-
If a required parameter is missing
|
|
21
|
+
If a required parameter is missing, ask before calling — do not invent values. Coerce each value to its JSON type (booleans → true/false, numbers → numeric, object/array → parse JSON), then make the single tool call and summarize the result.
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: "Revert a content node to a prior revision (GOVERNED WRITE). Reads the target revision and replays its editable content as a NEW current revision (history is preserved, nothing is deleted). Internal ids, revision metadata, and computed paths are not written back. Subject to the site's write security policy. Confirm with the user before calling."
|
|
3
3
|
argument-hint: "<type> <id> <version> [site]"
|
|
4
|
-
allowed-tools: mcp__drupal__drupal_revert_revision
|
|
5
4
|
---
|
|
6
5
|
|
|
7
|
-
Call the
|
|
6
|
+
Call the MCP tool `drupal_revert_revision`.
|
|
8
7
|
|
|
9
8
|
Revert a content node to a prior revision (GOVERNED WRITE). Reads the target revision and replays its editable content as a NEW current revision (history is preserved, nothing is deleted). Internal ids, revision metadata, and computed paths are not written back. Subject to the site's write security policy. Confirm with the user before calling.
|
|
10
9
|
|
|
11
|
-
Parse the
|
|
10
|
+
Parse the arguments supplied with this command into this tool's parameters:
|
|
12
11
|
|
|
13
12
|
**Required:**
|
|
14
13
|
- `type` (string): Content type machine name
|
|
@@ -18,4 +17,4 @@ Parse the request in `$ARGUMENTS` into this tool's parameters:
|
|
|
18
17
|
**Optional:**
|
|
19
18
|
- `site` (string): Named site from connector config. Omit only on reads: multi-site configs fall back to defaultSite (often local/dev, not production). Writes require an explicit site when more than one site is configured. Every response includes `_target` { name, baseUrl, source } (`hint` when you passed site, `default` when you did not).
|
|
20
19
|
|
|
21
|
-
If a required parameter is missing
|
|
20
|
+
If a required parameter is missing, ask before calling — do not invent values. Coerce each value to its JSON type (booleans → true/false, numbers → numeric, object/array → parse JSON), then make the single tool call and summarize the result.
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: "Schedule a content node to publish and/or unpublish at a future time using the Drupal Scheduler module. Sets the publish_on and unpublish_on fields on the node. Requires the Scheduler module to be installed and enabled for the content type, with the publish_on / unpublish_on fields present on the bundle — otherwise the call fails with a clear capability error. Timestamps accept ISO 8601 (e.g. '2026-07-01T12:00:00Z') or a Unix epoch and are passed through unchanged. Provide at least one of publishOn or unpublishOn."
|
|
3
3
|
argument-hint: "<type> <id> [site] [publishOn] [unpublishOn]"
|
|
4
|
-
allowed-tools: mcp__drupal__drupal_schedule_publish
|
|
5
4
|
---
|
|
6
5
|
|
|
7
|
-
Call the
|
|
6
|
+
Call the MCP tool `drupal_schedule_publish`.
|
|
8
7
|
|
|
9
8
|
Schedule a content node to publish and/or unpublish at a future time using the Drupal Scheduler module. Sets the publish_on and unpublish_on fields on the node. Requires the Scheduler module to be installed and enabled for the content type, with the publish_on / unpublish_on fields present on the bundle — otherwise the call fails with a clear capability error. Timestamps accept ISO 8601 (e.g. '2026-07-01T12:00:00Z') or a Unix epoch and are passed through unchanged. Provide at least one of publishOn or unpublishOn.
|
|
10
9
|
|
|
11
|
-
Parse the
|
|
10
|
+
Parse the arguments supplied with this command into this tool's parameters:
|
|
12
11
|
|
|
13
12
|
**Required:**
|
|
14
13
|
- `type` (string): Content type machine name, e.g. 'article'
|
|
@@ -19,4 +18,4 @@ Parse the request in `$ARGUMENTS` into this tool's parameters:
|
|
|
19
18
|
- `publishOn` (string): When to publish — ISO 8601 datetime or Unix epoch. Sets the Scheduler publish_on field.
|
|
20
19
|
- `unpublishOn` (string): When to unpublish — ISO 8601 datetime or Unix epoch. Sets the Scheduler unpublish_on field.
|
|
21
20
|
|
|
22
|
-
If a required parameter is missing
|
|
21
|
+
If a required parameter is missing, ask before calling — do not invent values. Coerce each value to its JSON type (booleans → true/false, numbers → numeric, object/array → parse JSON), then make the single tool call and summarize the result.
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: "Search nodes by title substring. Returns title, path alias, and body summary."
|
|
3
3
|
argument-hint: "<query> [site] [type] [status] [limit]"
|
|
4
|
-
allowed-tools: mcp__drupal__drupal_search_content
|
|
5
4
|
---
|
|
6
5
|
|
|
7
|
-
Call the
|
|
6
|
+
Call the MCP tool `drupal_search_content`.
|
|
8
7
|
|
|
9
8
|
Search nodes by title substring. Returns title, path alias, and body summary.
|
|
10
9
|
|
|
11
|
-
Parse the
|
|
10
|
+
Parse the arguments supplied with this command into this tool's parameters:
|
|
12
11
|
|
|
13
12
|
**Required:**
|
|
14
13
|
- `query` (string): Search term to match against node titles
|
|
@@ -19,4 +18,4 @@ Parse the request in `$ARGUMENTS` into this tool's parameters:
|
|
|
19
18
|
- `status` (boolean (true/false)): Filter by publish status
|
|
20
19
|
- `limit` (number)
|
|
21
20
|
|
|
22
|
-
If a required parameter is missing
|
|
21
|
+
If a required parameter is missing, ask before calling — do not invent values. Coerce each value to its JSON type (booleans → true/false, numbers → numeric, object/array → parse JSON), then make the single tool call and summarize the result.
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: "Search content by a query string. Best-effort title match over a content type (mode:'fallback'); relevance-ranked search requires a Search API/Solr endpoint."
|
|
3
3
|
argument-hint: "<query> [site] [type] [limit]"
|
|
4
|
-
allowed-tools: mcp__drupal__drupal_search
|
|
5
4
|
---
|
|
6
5
|
|
|
7
|
-
Call the
|
|
6
|
+
Call the MCP tool `drupal_search`.
|
|
8
7
|
|
|
9
8
|
Search content by a query string. Best-effort title match over a content type (mode:'fallback'); relevance-ranked search requires a Search API/Solr endpoint.
|
|
10
9
|
|
|
11
|
-
Parse the
|
|
10
|
+
Parse the arguments supplied with this command into this tool's parameters:
|
|
12
11
|
|
|
13
12
|
**Required:**
|
|
14
13
|
- `query` (string): Search term
|
|
@@ -18,4 +17,4 @@ Parse the request in `$ARGUMENTS` into this tool's parameters:
|
|
|
18
17
|
- `type` (string): Content type machine name (default: article)
|
|
19
18
|
- `limit` (number)
|
|
20
19
|
|
|
21
|
-
If a required parameter is missing
|
|
20
|
+
If a required parameter is missing, ask before calling — do not invent values. Coerce each value to its JSON type (booleans → true/false, numbers → numeric, object/array → parse JSON), then make the single tool call and summarize the result.
|
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: "Show the active security configuration for a site — what's allowed, what's blocked, what fields are redacted. Run this to understand the current access policy."
|
|
3
3
|
argument-hint: "[site]"
|
|
4
|
-
allowed-tools: mcp__drupal__drupal_security_info
|
|
5
4
|
---
|
|
6
5
|
|
|
7
|
-
Call the
|
|
6
|
+
Call the MCP tool `drupal_security_info`.
|
|
8
7
|
|
|
9
8
|
Show the active security configuration for a site — what's allowed, what's blocked, what fields are redacted. Run this to understand the current access policy.
|
|
10
9
|
|
|
11
|
-
Parse the
|
|
10
|
+
Parse the arguments supplied with this command into this tool's parameters:
|
|
12
11
|
|
|
13
12
|
**Optional:**
|
|
14
13
|
- `site` (string): Named site from connector config. Omit only on reads: multi-site configs fall back to defaultSite (often local/dev, not production). Writes require an explicit site when more than one site is configured. Every response includes `_target` { name, baseUrl, source } (`hint` when you passed site, `default` when you did not).
|
|
15
14
|
|
|
16
|
-
If a required parameter is missing
|
|
15
|
+
If a required parameter is missing, ask before calling — do not invent values. Coerce each value to its JSON type (booleans → true/false, numbers → numeric, object/array → parse JSON), then make the single tool call and summarize the result.
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: "Transition a content node to a moderation state (content_moderation), e.g. 'draft', 'needs_review', 'published', 'archived'. Governed write."
|
|
3
3
|
argument-hint: "<type> <id> <state> [site]"
|
|
4
|
-
allowed-tools: mcp__drupal__drupal_set_moderation_state
|
|
5
4
|
---
|
|
6
5
|
|
|
7
|
-
Call the
|
|
6
|
+
Call the MCP tool `drupal_set_moderation_state`.
|
|
8
7
|
|
|
9
8
|
Transition a content node to a moderation state (content_moderation), e.g. 'draft', 'needs_review', 'published', 'archived'. Governed write.
|
|
10
9
|
|
|
11
|
-
Parse the
|
|
10
|
+
Parse the arguments supplied with this command into this tool's parameters:
|
|
12
11
|
|
|
13
12
|
**Required:**
|
|
14
13
|
- `type` (string): Content type machine name
|
|
@@ -18,4 +17,4 @@ Parse the request in `$ARGUMENTS` into this tool's parameters:
|
|
|
18
17
|
**Optional:**
|
|
19
18
|
- `site` (string): Named site from connector config. Omit only on reads: multi-site configs fall back to defaultSite (often local/dev, not production). Writes require an explicit site when more than one site is configured. Every response includes `_target` { name, baseUrl, source } (`hint` when you passed site, `default` when you did not).
|
|
20
19
|
|
|
21
|
-
If a required parameter is missing
|
|
20
|
+
If a required parameter is missing, ask before calling — do not invent values. Coerce each value to its JSON type (booleans → true/false, numbers → numeric, object/array → parse JSON), then make the single tool call and summarize the result.
|
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: "Get the base URL and the list of available resource/query types for a configured site (works for JSON:API and GraphQL backends)."
|
|
3
3
|
argument-hint: "[site]"
|
|
4
|
-
allowed-tools: mcp__drupal__drupal_site_info
|
|
5
4
|
---
|
|
6
5
|
|
|
7
|
-
Call the
|
|
6
|
+
Call the MCP tool `drupal_site_info`.
|
|
8
7
|
|
|
9
8
|
Get the base URL and the list of available resource/query types for a configured site (works for JSON:API and GraphQL backends).
|
|
10
9
|
|
|
11
|
-
Parse the
|
|
10
|
+
Parse the arguments supplied with this command into this tool's parameters:
|
|
12
11
|
|
|
13
12
|
**Optional:**
|
|
14
13
|
- `site` (string): Named site from connector config. Omit only on reads: multi-site configs fall back to defaultSite (often local/dev, not production). Writes require an explicit site when more than one site is configured. Every response includes `_target` { name, baseUrl, source } (`hint` when you passed site, `default` when you did not).
|
|
15
14
|
|
|
16
|
-
If a required parameter is missing
|
|
15
|
+
If a required parameter is missing, ask before calling — do not invent values. Coerce each value to its JSON type (booleans → true/false, numbers → numeric, object/array → parse JSON), then make the single tool call and summarize the result.
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: "Update a media entity's name, status, or field values. Partial: omitted fields (status included) are left untouched."
|
|
3
3
|
argument-hint: "<type> <id> [site] [name] [status] [fields]"
|
|
4
|
-
allowed-tools: mcp__drupal__drupal_update_media
|
|
5
4
|
---
|
|
6
5
|
|
|
7
|
-
Call the
|
|
6
|
+
Call the MCP tool `drupal_update_media`.
|
|
8
7
|
|
|
9
8
|
Update a media entity's name, status, or field values. Partial: omitted fields (status included) are left untouched.
|
|
10
9
|
|
|
11
|
-
Parse the
|
|
10
|
+
Parse the arguments supplied with this command into this tool's parameters:
|
|
12
11
|
|
|
13
12
|
**Required:**
|
|
14
13
|
- `type` (string)
|
|
@@ -20,4 +19,4 @@ Parse the request in `$ARGUMENTS` into this tool's parameters:
|
|
|
20
19
|
- `status` (boolean (true/false)): Published flag. Only sent when provided; requires allowPublish when true.
|
|
21
20
|
- `fields` (object (pass as JSON)): Field values. Entity-reference values in JSON:API linkage shape ({ data: { type, id } }) are sent as relationships automatically.
|
|
22
21
|
|
|
23
|
-
If a required parameter is missing
|
|
22
|
+
If a required parameter is missing, ask before calling — do not invent values. Coerce each value to its JSON type (booleans → true/false, numbers → numeric, object/array → parse JSON), then make the single tool call and summarize the result.
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: "Update a custom menu link by UUID (rename, re-weight, re-target, re-parent, enable/disable). Only the fields you pass change. The link's enabled state is preserved across edits — an unrelated change will not disable a live link — unless you pass 'enabled' explicitly. Checked against the site security config."
|
|
3
3
|
argument-hint: "<id> [site] [title] [link] [menu] [weight] [parent] [enabled]"
|
|
4
|
-
allowed-tools: mcp__drupal__drupal_update_menu_link
|
|
5
4
|
---
|
|
6
5
|
|
|
7
|
-
Call the
|
|
6
|
+
Call the MCP tool `drupal_update_menu_link`.
|
|
8
7
|
|
|
9
8
|
Update a custom menu link by UUID (rename, re-weight, re-target, re-parent, enable/disable). Only the fields you pass change. The link's enabled state is preserved across edits — an unrelated change will not disable a live link — unless you pass 'enabled' explicitly. Checked against the site security config.
|
|
10
9
|
|
|
11
|
-
Parse the
|
|
10
|
+
Parse the arguments supplied with this command into this tool's parameters:
|
|
12
11
|
|
|
13
12
|
**Required:**
|
|
14
13
|
- `id` (string): Menu link UUID
|
|
@@ -22,4 +21,4 @@ Parse the request in `$ARGUMENTS` into this tool's parameters:
|
|
|
22
21
|
- `parent` (string): New parent link plugin id (e.g. 'menu_link_content:<uuid>'), or '' for top level. Omit to leave unchanged.
|
|
23
22
|
- `enabled` (boolean (true/false)): Enable/disable the link. Omit to preserve the current state.
|
|
24
23
|
|
|
25
|
-
If a required parameter is missing
|
|
24
|
+
If a required parameter is missing, ask before calling — do not invent values. Coerce each value to its JSON type (booleans → true/false, numbers → numeric, object/array → parse JSON), then make the single tool call and summarize the result.
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: "Update an existing node. Only include fields you want to change. For moderated content types, use moderationState (e.g. 'published') rather than status. When the target is published and moderated and you omit moderationState, the connector defaults the write to moderation_state 'draft' (forward revision) so live default revisions are not mutated by accident. Entity-reference fields go in `relationships`, not `fields`. Paragraph / ERR identifiers are resolved to include meta.target_revision_id before PATCH; the write fails if any ref cannot be resolved (an unresolved identifier persists as an empty field). On moderated targets an id-mismatch PATCH preflight runs first — including on dryRun —
|
|
2
|
+
description: "Update an existing node. Only include fields you want to change. For moderated content types, use moderationState (e.g. 'published') rather than status. When the target is published and moderated and you omit moderationState, the connector defaults the write to moderation_state 'draft' (forward revision) so live default revisions are not mutated by accident. Entity-reference fields go in `relationships`, not `fields`. Paragraph / ERR identifiers are resolved to include meta.target_revision_id before PATCH; the write fails if any ref cannot be resolved (an unresolved identifier persists as an empty field). On moderated targets an id-mismatch PATCH preflight runs first — including on dryRun — against the same URL the write will hit. An addressable working copy is PATCHed via ?resourceVersion=rel:working-copy (#166); dryRun uses that same target. workingCopy:null from drupal_list_revisions is not proof the node is writable (possiblyPatchBlocked / #201). Preflight here does not un-orphan paragraphs already created; probe the host before creating dependents."
|
|
3
3
|
argument-hint: "<type> <id> [site] [title] [body] [summary] [format] [status] [moderationState] [fields] [relationships] [dryRun] [returning]"
|
|
4
|
-
allowed-tools: mcp__drupal__drupal_update_node
|
|
5
4
|
---
|
|
6
5
|
|
|
7
|
-
Call the
|
|
6
|
+
Call the MCP tool `drupal_update_node`.
|
|
8
7
|
|
|
9
|
-
Update an existing node. Only include fields you want to change. For moderated content types, use moderationState (e.g. 'published') rather than status. When the target is published and moderated and you omit moderationState, the connector defaults the write to moderation_state 'draft' (forward revision) so live default revisions are not mutated by accident. Entity-reference fields go in `relationships`, not `fields`. Paragraph / ERR identifiers are resolved to include meta.target_revision_id before PATCH; the write fails if any ref cannot be resolved (an unresolved identifier persists as an empty field). On moderated targets an id-mismatch PATCH preflight runs first — including on dryRun —
|
|
8
|
+
Update an existing node. Only include fields you want to change. For moderated content types, use moderationState (e.g. 'published') rather than status. When the target is published and moderated and you omit moderationState, the connector defaults the write to moderation_state 'draft' (forward revision) so live default revisions are not mutated by accident. Entity-reference fields go in `relationships`, not `fields`. Paragraph / ERR identifiers are resolved to include meta.target_revision_id before PATCH; the write fails if any ref cannot be resolved (an unresolved identifier persists as an empty field). On moderated targets an id-mismatch PATCH preflight runs first — including on dryRun — against the same URL the write will hit. An addressable working copy is PATCHed via ?resourceVersion=rel:working-copy (#166); dryRun uses that same target. workingCopy:null from drupal_list_revisions is not proof the node is writable (possiblyPatchBlocked / #201). Preflight here does not un-orphan paragraphs already created; probe the host before creating dependents.
|
|
10
9
|
|
|
11
|
-
Parse the
|
|
10
|
+
Parse the arguments supplied with this command into this tool's parameters:
|
|
12
11
|
|
|
13
12
|
**Required:**
|
|
14
13
|
- `type` (string)
|
|
@@ -18,13 +17,13 @@ Parse the request in `$ARGUMENTS` into this tool's parameters:
|
|
|
18
17
|
- `site` (string): Named site from connector config. Omit only on reads: multi-site configs fall back to defaultSite (often local/dev, not production). Writes require an explicit site when more than one site is configured. Every response includes `_target` { name, baseUrl, source } (`hint` when you passed site, `default` when you did not).
|
|
19
18
|
- `title` (string)
|
|
20
19
|
- `body` (string)
|
|
21
|
-
- `summary` (string): Body summary/teaser — writes
|
|
22
|
-
- `format` (string): Text format machine name for the body, e.g. 'basic_html'.
|
|
20
|
+
- `summary` (string): Body summary/teaser — writes body.summary on core text_with_summary only. Refused when the sampled body field has no summary property (text_long / text_formatted) or the schema cannot be determined. Prefer the site's dedicated deck/summary field via `fields`.
|
|
21
|
+
- `format` (string): Text format machine name for the body, e.g. 'basic_html'. When the body field's allowed_formats lists exactly one format, that is the default. A caller format outside that list is refused before write. When allowed_formats cannot be resolved, defaults to the site config's `defaultTextFormat`, then 'full_html'.
|
|
23
22
|
- `status` (boolean (true/false)): Published flag for NON-moderated types: true = publish, false = unpublish. Ignored if moderationState is set.
|
|
24
23
|
- `moderationState` (string): Moderation state transition for content_moderation types, e.g. 'draft', 'published', 'archived'. Takes precedence over status. Required to keep or re-publish a live node — omitting it on a published moderated node defaults the write to 'draft'.
|
|
25
|
-
- `fields` (object (pass as JSON)): Scalar/attribute field values keyed by machine name. Entity-reference fields go in `relationships`, not here.
|
|
24
|
+
- `fields` (object (pass as JSON)): Scalar/attribute field values keyed by machine name. Formatted text: a string or { value, format?, summary? }. format must be in the field's allowed_formats; a single allowed format is used when omitted. Entity-reference fields go in `relationships`, not here.
|
|
26
25
|
- `relationships` (object (pass as JSON)): Entity-reference fields as JSON:API relationships, keyed by field machine name. Single-value uses { data: { type, id } }; multi-value uses { data: [{ type, id }, …] }. Paragraph / ERR items must carry meta.target_revision_id — the connector injects it when missing, and fails the write if it cannot.
|
|
27
|
-
- `dryRun` (boolean (true/false)): Validate, resolve ERR identifiers, and (on moderated targets) run the core PATCH-guard probe against Drupal, then return a preview without the real write. The probe uses a non-matching data.id so Drupal does not save. A working-copy 400 fails the dryRun.
|
|
26
|
+
- `dryRun` (boolean (true/false)): Validate, resolve ERR identifiers, and (on moderated targets) run the core PATCH-guard probe against Drupal, then return a preview without the real write. The probe uses a non-matching data.id so Drupal does not save, and hits the same URL as the real write (canonical, or ?resourceVersion=rel:working-copy when a draft is addressable). A working-copy 400 fails the dryRun.
|
|
28
27
|
- `returning` (string): Response verbosity. "full" (default) returns the complete saved entity; "minimal" returns just identity + state (id, type, bundle, title, status, changed, url) — much smaller, recommended for bulk writes where the echoed body would dominate the response.
|
|
29
28
|
|
|
30
|
-
If a required parameter is missing
|
|
29
|
+
If a required parameter is missing, ask before calling — do not invent values. Coerce each value to its JSON type (booleans → true/false, numbers → numeric, object/array → parse JSON), then make the single tool call and summarize the result.
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: "Update an existing Paragraph entity's field values by paragraph type (bundle) and UUID. Only the attributes you pass are changed (partial update); the host entity's reference to the paragraph is unchanged (same UUID), so this maintains a component paragraph in place without re-embedding. Returns relationshipData including meta.target_revision_id for a later host attach. Use drupal_get_entity_schema (entityType 'paragraph', the bundle) to discover fields. Governed by the site security policy."
|
|
3
3
|
argument-hint: "<paragraphType> <id> [site] [attributes]"
|
|
4
|
-
allowed-tools: mcp__drupal__drupal_update_paragraph
|
|
5
4
|
---
|
|
6
5
|
|
|
7
|
-
Call the
|
|
6
|
+
Call the MCP tool `drupal_update_paragraph`.
|
|
8
7
|
|
|
9
8
|
Update an existing Paragraph entity's field values by paragraph type (bundle) and UUID. Only the attributes you pass are changed (partial update); the host entity's reference to the paragraph is unchanged (same UUID), so this maintains a component paragraph in place without re-embedding. Returns relationshipData including meta.target_revision_id for a later host attach. Use drupal_get_entity_schema (entityType 'paragraph', the bundle) to discover fields. Governed by the site security policy.
|
|
10
9
|
|
|
11
|
-
Parse the
|
|
10
|
+
Parse the arguments supplied with this command into this tool's parameters:
|
|
12
11
|
|
|
13
12
|
**Required:**
|
|
14
13
|
- `paragraphType` (string): Paragraph type / bundle machine name, e.g. 'text', 'image', 'cta'
|
|
@@ -18,4 +17,4 @@ Parse the request in `$ARGUMENTS` into this tool's parameters:
|
|
|
18
17
|
- `site` (string): Named site from connector config. Omit only on reads: multi-site configs fall back to defaultSite (often local/dev, not production). Writes require an explicit site when more than one site is configured. Every response includes `_target` { name, baseUrl, source } (`hint` when you passed site, `default` when you did not).
|
|
19
18
|
- `attributes` (object (pass as JSON)): Paragraph field values to change, keyed by Drupal machine name, e.g. { field_body: { value: '<p>..</p>', format: 'full_html' } }
|
|
20
19
|
|
|
21
|
-
If a required parameter is missing
|
|
20
|
+
If a required parameter is missing, ask before calling — do not invent values. Coerce each value to its JSON type (booleans → true/false, numbers → numeric, object/array → parse JSON), then make the single tool call and summarize the result.
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: "Update an existing redirect by UUID: repoint its source or target, or change its status code (e.g. 301↔302). Only the fields you pass are changed (partial update). Use this to activate/fix a redirect that isn't firing (e.g. one created with a stale source). Governed by the site security policy."
|
|
3
3
|
argument-hint: "<id> [site] [source] [target] [statusCode]"
|
|
4
|
-
allowed-tools: mcp__drupal__drupal_update_redirect
|
|
5
4
|
---
|
|
6
5
|
|
|
7
|
-
Call the
|
|
6
|
+
Call the MCP tool `drupal_update_redirect`.
|
|
8
7
|
|
|
9
8
|
Update an existing redirect by UUID: repoint its source or target, or change its status code (e.g. 301↔302). Only the fields you pass are changed (partial update). Use this to activate/fix a redirect that isn't firing (e.g. one created with a stale source). Governed by the site security policy.
|
|
10
9
|
|
|
11
|
-
Parse the
|
|
10
|
+
Parse the arguments supplied with this command into this tool's parameters:
|
|
12
11
|
|
|
13
12
|
**Required:**
|
|
14
13
|
- `id` (string): Redirect entity UUID
|
|
@@ -19,4 +18,4 @@ Parse the request in `$ARGUMENTS` into this tool's parameters:
|
|
|
19
18
|
- `target` (string): New destination path/URI. Omit to leave unchanged.
|
|
20
19
|
- `statusCode` (number): New HTTP redirect status code (301/302/303/307/308). Omit to leave unchanged.
|
|
21
20
|
|
|
22
|
-
If a required parameter is missing
|
|
21
|
+
If a required parameter is missing, ask before calling — do not invent values. Coerce each value to its JSON type (booleans → true/false, numbers → numeric, object/array → parse JSON), then make the single tool call and summarize the result.
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: "Update an existing taxonomy term's name, description, or weight."
|
|
3
3
|
argument-hint: "<vocabulary> <id> [site] [name] [description] [weight]"
|
|
4
|
-
allowed-tools: mcp__drupal__drupal_update_taxonomy_term
|
|
5
4
|
---
|
|
6
5
|
|
|
7
|
-
Call the
|
|
6
|
+
Call the MCP tool `drupal_update_taxonomy_term`.
|
|
8
7
|
|
|
9
8
|
Update an existing taxonomy term's name, description, or weight.
|
|
10
9
|
|
|
11
|
-
Parse the
|
|
10
|
+
Parse the arguments supplied with this command into this tool's parameters:
|
|
12
11
|
|
|
13
12
|
**Required:**
|
|
14
13
|
- `vocabulary` (string)
|
|
@@ -20,4 +19,4 @@ Parse the request in `$ARGUMENTS` into this tool's parameters:
|
|
|
20
19
|
- `description` (string)
|
|
21
20
|
- `weight` (number)
|
|
22
21
|
|
|
23
|
-
If a required parameter is missing
|
|
22
|
+
If a required parameter is missing, ask before calling — do not invent values. Coerce each value to its JSON type (booleans → true/false, numbers → numeric, object/array → parse JSON), then make the single tool call and summarize the result.
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: "Update a Drupal user account. Only include fields you want to change. Can reassign roles by providing a full replacement role list."
|
|
3
3
|
argument-hint: "<id> [site] [name] [mail] [password] [status] [roles] [timezone]"
|
|
4
|
-
allowed-tools: mcp__drupal__drupal_update_user
|
|
5
4
|
---
|
|
6
5
|
|
|
7
|
-
Call the
|
|
6
|
+
Call the MCP tool `drupal_update_user`.
|
|
8
7
|
|
|
9
8
|
Update a Drupal user account. Only include fields you want to change. Can reassign roles by providing a full replacement role list.
|
|
10
9
|
|
|
11
|
-
Parse the
|
|
10
|
+
Parse the arguments supplied with this command into this tool's parameters:
|
|
12
11
|
|
|
13
12
|
**Required:**
|
|
14
13
|
- `id` (string): User UUID
|
|
@@ -22,4 +21,4 @@ Parse the request in `$ARGUMENTS` into this tool's parameters:
|
|
|
22
21
|
- `roles` (array (pass as JSON)): Full replacement role UUID list
|
|
23
22
|
- `timezone` (string)
|
|
24
23
|
|
|
25
|
-
If a required parameter is missing
|
|
24
|
+
If a required parameter is missing, ask before calling — do not invent values. Coerce each value to its JSON type (booleans → true/false, numbers → numeric, object/array → parse JSON), then make the single tool call and summarize the result.
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: "Convenience tool: upload a local file and immediately create a Media entity in one step. Best for the common 'add an image' workflow. Media defaults to unpublished."
|
|
3
3
|
argument-hint: "<filePath> <mediaType> <fieldName> [site] [mediaName] [altText] [status]"
|
|
4
|
-
allowed-tools: mcp__drupal__drupal_upload_file_and_create_media
|
|
5
4
|
---
|
|
6
5
|
|
|
7
|
-
Call the
|
|
6
|
+
Call the MCP tool `drupal_upload_file_and_create_media`.
|
|
8
7
|
|
|
9
8
|
Convenience tool: upload a local file and immediately create a Media entity in one step. Best for the common 'add an image' workflow. Media defaults to unpublished.
|
|
10
9
|
|
|
11
|
-
Parse the
|
|
10
|
+
Parse the arguments supplied with this command into this tool's parameters:
|
|
12
11
|
|
|
13
12
|
**Required:**
|
|
14
13
|
- `filePath` (string): Local path to the file (must resolve under MCP_UPLOAD_ROOT or the connector working directory)
|
|
@@ -21,4 +20,4 @@ Parse the request in `$ARGUMENTS` into this tool's parameters:
|
|
|
21
20
|
- `altText` (string): Alt text for image media
|
|
22
21
|
- `status` (boolean (true/false)): Published flag. Defaults to false. Requires allowPublish when true.
|
|
23
22
|
|
|
24
|
-
If a required parameter is missing
|
|
23
|
+
If a required parameter is missing, ask before calling — do not invent values. Coerce each value to its JSON type (booleans → true/false, numbers → numeric, object/array → parse JSON), then make the single tool call and summarize the result.
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: "Upload a local file to Drupal and create a File entity. Returns the file UUID to use when creating a Media entity. For images, the typical flow is: drupal_upload_file → drupal_create_media."
|
|
3
3
|
argument-hint: "<filePath> <bundle> <fieldName> [site] [entityType]"
|
|
4
|
-
allowed-tools: mcp__drupal__drupal_upload_file
|
|
5
4
|
---
|
|
6
5
|
|
|
7
|
-
Call the
|
|
6
|
+
Call the MCP tool `drupal_upload_file`.
|
|
8
7
|
|
|
9
8
|
Upload a local file to Drupal and create a File entity. Returns the file UUID to use when creating a Media entity. For images, the typical flow is: drupal_upload_file → drupal_create_media.
|
|
10
9
|
|
|
11
|
-
Parse the
|
|
10
|
+
Parse the arguments supplied with this command into this tool's parameters:
|
|
12
11
|
|
|
13
12
|
**Required:**
|
|
14
13
|
- `filePath` (string): Local path to the file (must resolve under MCP_UPLOAD_ROOT or the connector working directory)
|
|
@@ -19,4 +18,4 @@ Parse the request in `$ARGUMENTS` into this tool's parameters:
|
|
|
19
18
|
- `site` (string): Named site from connector config. Omit only on reads: multi-site configs fall back to defaultSite (often local/dev, not production). Writes require an explicit site when more than one site is configured. Every response includes `_target` { name, baseUrl, source } (`hint` when you passed site, `default` when you did not).
|
|
20
19
|
- `entityType` (string): Drupal entity type (usually 'media' or 'node')
|
|
21
20
|
|
|
22
|
-
If a required parameter is missing
|
|
21
|
+
If a required parameter is missing, ask before calling — do not invent values. Coerce each value to its JSON type (booleans → true/false, numbers → numeric, object/array → parse JSON), then make the single tool call and summarize the result.
|
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,62 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [2.8.0] - 2026-08-25
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- **Iterative updates PATCH an existing working copy (#166).**
|
|
14
|
+
`drupal_update_node`, `drupal_entity_update`, and `drupal_bulk_update`
|
|
15
|
+
resolve `rel:working-copy` before the write. When that alias is
|
|
16
|
+
addressable, both the dryRun probe and the real PATCH use
|
|
17
|
+
`?resourceVersion=rel:working-copy` instead of the canonical URL, so a
|
|
18
|
+
second edit lands on the same forward revision. dryRun can no longer
|
|
19
|
+
succeed when that write would 400. An addressable draft is edited in
|
|
20
|
+
place — the connector does not discard it or tell the caller to publish
|
|
21
|
+
first. The stray-revision case (alias does not resolve, core still
|
|
22
|
+
blocks) still refuses with revision-surgery language (#201). A stale or
|
|
23
|
+
concurrent working-copy 400 is refused without retrying the canonical
|
|
24
|
+
URL. Successful writes include `_revisions: { live, working }` when both
|
|
25
|
+
vids can be read.
|
|
26
|
+
- **`summary` is refused when body has no summary property (#163).**
|
|
27
|
+
`drupal_create_node` and `drupal_update_node` introspect the sampled body
|
|
28
|
+
field before writing `summary`. A `text_long` / `text_formatted` body (or
|
|
29
|
+
an undetermined schema) fails closed with an actionable message to set the
|
|
30
|
+
site's deck field via `fields`. Core `text_with_summary` still accepts
|
|
31
|
+
`summary` and returns `_warnings` with `summary_parameter_deprecated`.
|
|
32
|
+
dryRun uses the same check.
|
|
33
|
+
- **Node writes honor field `allowed_formats` (#168).** Create/update (including
|
|
34
|
+
`dryRun`) resolve Field API `allowed_formats` from JSON:API `field_config`
|
|
35
|
+
and, when that is unavailable, Drush `config:get`. A single allowed format
|
|
36
|
+
is the default when the caller omits `format`. A caller format outside the
|
|
37
|
+
list is refused before mutation. The historical `defaultTextFormat` /
|
|
38
|
+
`full_html` fallback applies only while the list cannot be resolved — never
|
|
39
|
+
when `full_html` is excluded by field config.
|
|
40
|
+
|
|
41
|
+
### Added
|
|
42
|
+
- **Northbound data-flow budgets bind to principal and target (#179).** Every
|
|
43
|
+
governed tool call that resolves a site now carries request-scoped principal
|
|
44
|
+
and authoritative-target context. Row, byte, page, request, and chained-action
|
|
45
|
+
counters use the same finite defaults as mcp_sentinel (500 results / 8 MiB /
|
|
46
|
+
600 req/60s / 120 pages/60s) and are keyed by inbound principal + target —
|
|
47
|
+
not by MCP session — so pagination, retries, batching, and a new chain id
|
|
48
|
+
cannot reset them. JSON:API, GraphQL, and the server-tool bridge send the
|
|
49
|
+
source wire contract (`X-MCP-Declared-Ceiling`, narrow-only;
|
|
50
|
+
`X-MCP-Declared-Destination` from the entitlement pair). Denials name a
|
|
51
|
+
stable reason plus a correlation id and do not echo restricted payload.
|
|
52
|
+
Optional `security.declaredCeiling` and `security.readBudgets` override the
|
|
53
|
+
defaults. Stdio / local-operator traffic still sends the declared headers;
|
|
54
|
+
connector-side counters enforce when an inbound OAuth principal is present.
|
|
55
|
+
|
|
56
|
+
### Changed
|
|
57
|
+
- **Slash-command stubs moved to `.agents/commands/` (#218).** Generated
|
|
58
|
+
`/drupal-*` files are harness-agnostic (protocol tool names, no
|
|
59
|
+
`mcp__drupal__*` `allowed-tools`). `.claude/` is no longer tracked or
|
|
60
|
+
published. `npm run install:commands` copies stubs into operator home
|
|
61
|
+
directories (`~/.claude/commands` with the Claude Code adapter,
|
|
62
|
+
`~/.grok/commands` as-is) so the bare `/drupal-*` form still works
|
|
63
|
+
without a vendor folder in this repo or in a consuming project. The npm
|
|
64
|
+
`files` list now ships `.agents/commands/` instead of `.claude/commands/`.
|
|
65
|
+
|
|
10
66
|
## [2.7.4] - 2026-08-18
|
|
11
67
|
|
|
12
68
|
### Fixed
|
package/README.md
CHANGED
|
@@ -49,7 +49,9 @@ Each site declares which backend(s) it exposes via the `api` key:
|
|
|
49
49
|
- **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.
|
|
50
50
|
- **`defaultTextFormat` sets the body text format** used by the `body` convenience
|
|
51
51
|
parameter on node writes, e.g. `{ "baseUrl": "…", "defaultTextFormat": "basic_html" }`.
|
|
52
|
-
Individual calls can override it with `format`.
|
|
52
|
+
Individual calls can override it with `format`. Field API `allowed_formats` wins
|
|
53
|
+
when it can be resolved: a single allowed format is the default, and a format
|
|
54
|
+
outside the list is refused. Without a resolved list, the connector falls
|
|
53
55
|
back to `full_html`, which many governed sites deliberately do not define — and which
|
|
54
56
|
is the most permissive core format, so setting this per site is the safer posture. A
|
|
55
57
|
text format is Drupal's HTML-filtering boundary, so it is worth choosing deliberately
|
|
@@ -119,20 +121,27 @@ governed call to that tool. These are protocol-native, so they work everywhere t
|
|
|
119
121
|
prompts capability is supported — the client renders them per its own convention
|
|
120
122
|
(e.g. Claude Code shows `/mcp__drupal__drupal-create-node`).
|
|
121
123
|
|
|
122
|
-
####
|
|
123
|
-
|
|
124
|
-
connector
|
|
125
|
-
|
|
126
|
-
|
|
124
|
+
#### Filesystem slash commands (`/drupal-*`)
|
|
125
|
+
MCP prompts (above) are the protocol-native slash surface. For the literal bare
|
|
126
|
+
`/drupal-<tool>` form, the connector ships generated stubs under
|
|
127
|
+
**`.agents/commands/`** — a harness-agnostic tree, not a vendor rule directory.
|
|
128
|
+
Clients that scan `.agents/commands/` (including Grok Build when this repo is
|
|
129
|
+
the project) pick them up automatically.
|
|
130
|
+
|
|
131
|
+
Clients that only scan a vendor home path (Claude Code `~/.claude/commands`,
|
|
132
|
+
Grok `~/.grok/commands`) install from that tree into the **operator home**, so
|
|
133
|
+
no consuming project has to commit `.claude/` or `.grok/`:
|
|
127
134
|
|
|
128
135
|
```bash
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
#
|
|
136
|
+
npm run generate:commands # refresh .agents/commands/ (from a clone)
|
|
137
|
+
npm run install:commands # copies into ~/.claude/commands and ~/.grok/commands
|
|
138
|
+
# npm run install:commands -- --clients claude
|
|
139
|
+
# npm run install:commands -- --home /path/to/fake-home # tests / CI
|
|
132
140
|
```
|
|
133
141
|
|
|
134
|
-
|
|
135
|
-
|
|
142
|
+
From an npm install, the same files ship at
|
|
143
|
+
`node_modules/drupal-mcp-connector/.agents/commands/`. Do not copy them into an
|
|
144
|
+
application repo's `.claude/` or `.grok/` directory.
|
|
136
145
|
|
|
137
146
|
### Security Model
|
|
138
147
|
|
|
@@ -161,6 +170,7 @@ Additional connector-side gates (2.1+ / 2.2+):
|
|
|
161
170
|
- **Uploads** only from `MCP_UPLOAD_ROOT` (or the process cwd); sensitive paths (`.env*`, `.ssh`, connector `config.json`) are refused.
|
|
162
171
|
- **HTTPS:** non-loopback binds require an inbound OAuth resource server (`auth.issuer` + `auth.audience`), not `MCP_AUTH_TOKEN`. `MCP_AUTH_TOKEN` remains valid on loopback. `MCP_ALLOW_UNAUTHENTICATED=1` is only for a trusted proxy. Non-loopback TLS defaults to 120 req/min rate limiting. Inbound JWT scopes (and optional `auth.grants`) filter which tools, resources, prompts, and sites a principal can discover or invoke.
|
|
163
172
|
- **GraphQL is off by default.** `drupal_graphql` / introspect require `security.allowGraphql` (true only on the `development` preset). Raw GraphQL results still bypass entity allowlists and field redaction — prefer JSON:API entity tools when connector policy must hold. Mutations also need `allowGraphqlMutations`.
|
|
173
|
+
- **Northbound data-flow budgets (2.8+):** row, byte, page, request, and chained-action counters keyed by inbound principal + authoritative target (same finite defaults as MCP Sentinel), so pagination, retries, batching, or a new chain id cannot reset them. Governed reads carry the declared-ceiling / declared-destination wire contract; tune with `security.declaredCeiling` and `security.readBudgets`.
|
|
164
174
|
|
|
165
175
|
Full detail: **[docs/security.md](docs/security.md)** and **[docs/security-hardening.md](docs/security-hardening.md)**.
|
|
166
176
|
|