drupal-mcp-connector 1.1.1 → 1.2.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/CHANGELOG.md +26 -0
- package/config/config.example.json +3 -3
- package/package.json +1 -1
- package/src/lib/security.js +73 -9
- package/src/lib/server-tools.js +9 -4
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.2.0] - 2026-06-27
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- Widened the content/developer security presets so the connector supports full content
|
|
14
|
+
building and management. `content-editor` and `write-plane` now allow `paragraph`,
|
|
15
|
+
`block_content`, `menu_link_content`, `redirect`, `path_alias`, and `file` in addition
|
|
16
|
+
to `node`/`taxonomy_term`/`media`. `config-editor` (developer tier) additionally allows
|
|
17
|
+
the site-building config entities (`node_type`, `paragraphs_type`, `block_content_type`,
|
|
18
|
+
`media_type`, `field_config`, `field_storage_config`, `entity_form_display`,
|
|
19
|
+
`entity_view_display`, `taxonomy_vocabulary`) for read/introspection — content-model
|
|
20
|
+
changes go through the governed config bridge / `drush config:import`, not JSON:API
|
|
21
|
+
entity create.
|
|
22
|
+
- Corrected the server-tool bridge tool names: `mcp_server_tool_bridge` exposes Tool-API
|
|
23
|
+
tools as `tool_api.<id>`, so the governed config tools are
|
|
24
|
+
`tool_api.mcp_sentinel_config_get` / `_list` / `_set` (previously documented as bare
|
|
25
|
+
`config_get` / `_list` / `_set`, which never resolved). Updated `SERVER_TOOLS` and
|
|
26
|
+
`docs/integration-contract.md` accordingly. These tools must be registered as enabled
|
|
27
|
+
`mcp_tool_config` entities on the Drupal site; they are not exposed by default.
|
|
28
|
+
|
|
29
|
+
### Security
|
|
30
|
+
- Deny-hardened the content/developer presets: `oauth2_token`, `key`, `consumer`,
|
|
31
|
+
`encryption_profile`, `mcp_tool_config`, and `mcp_policy_profile` are now in
|
|
32
|
+
`deniedEntityTypes` alongside `user`, so secrets, the agent's own governance config,
|
|
33
|
+
and account data stay blocked even if an allowlist is later widened. PII-bearing
|
|
34
|
+
`webform_submission` and `profile` are intentionally left off the allowlists.
|
|
35
|
+
|
|
10
36
|
## [1.1.1] - 2026-06-26
|
|
11
37
|
|
|
12
38
|
### Fixed
|
|
@@ -100,11 +100,11 @@
|
|
|
100
100
|
|
|
101
101
|
"_security_presets": {
|
|
102
102
|
"development": "All operations allowed, incl. config read/write. Local dev / break-glass only.",
|
|
103
|
-
"content-editor": "Create/edit
|
|
104
|
-
"config-editor": "content-editor + governed config read/write (Developer tier).",
|
|
103
|
+
"content-editor": "Create/edit the full content set (node, media, taxonomy_term, paragraph, block_content, menu_link_content, redirect, path_alias, file). No deletes. Config read-only. Denies user + secrets/governance types.",
|
|
104
|
+
"config-editor": "content-editor + site-building config entities (read/introspection) + governed config read/write (Developer tier). Model changes go via the config bridge, not JSON:API.",
|
|
105
105
|
"auditor": "Read-only. All entity types. User PII fields redacted. Config read-only.",
|
|
106
106
|
"production-strict": "Read-only. No user entities. Broad PII redaction. No config access.",
|
|
107
|
-
"write-plane": "Governed writes (no delete/mutations) on node, taxonomy_term, media.
|
|
107
|
+
"write-plane": "Governed writes (no delete/mutations) on the content set (node, taxonomy_term, media + structural content entities). Denies user + secrets/governance types. Redacts pass/mail. Config read-only."
|
|
108
108
|
},
|
|
109
109
|
|
|
110
110
|
"_server_tools": {
|
package/package.json
CHANGED
package/src/lib/security.js
CHANGED
|
@@ -13,11 +13,17 @@ import { parse } from "graphql";
|
|
|
13
13
|
* ─── Quick presets ────────────────────────────────────────────────────────
|
|
14
14
|
*
|
|
15
15
|
* "preset": "development" Everything allowed. Default if no security key.
|
|
16
|
-
* "preset": "content-editor" Create/edit nodes
|
|
17
|
-
*
|
|
16
|
+
* "preset": "content-editor" Create/edit content (nodes, media, terms, paragraphs, blocks,
|
|
17
|
+
* menu links, redirects, aliases, files). No deletes. Config read-only.
|
|
18
|
+
* "preset": "config-editor" content-editor + site-building config READ + governed config
|
|
19
|
+
* read/write (Developer tier). Model changes go via the config bridge.
|
|
18
20
|
* "preset": "auditor" Read-only. All entity types. User fields redacted.
|
|
19
21
|
* "preset": "production-strict" Read-only. Explicit allowlist required. Redacts PII.
|
|
20
|
-
* "preset": "write-plane" Governed writes (no delete/mutations) on
|
|
22
|
+
* "preset": "write-plane" Governed writes (no delete/mutations) on the content set
|
|
23
|
+
* (node, term, media + structural content entities).
|
|
24
|
+
*
|
|
25
|
+
* Secrets, the agent's own governance config, and account data (see SENSITIVE_DENY)
|
|
26
|
+
* are always denied on the content/developer tiers, regardless of the allowlist.
|
|
21
27
|
*
|
|
22
28
|
* Presets can be overridden by adding explicit keys alongside them.
|
|
23
29
|
*
|
|
@@ -51,6 +57,57 @@ import { parse } from "graphql";
|
|
|
51
57
|
* The connector never writes these fields either when redaction is active.
|
|
52
58
|
*/
|
|
53
59
|
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
// Shared entity-type groups
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
//
|
|
64
|
+
// The connector allowlist is deliberately safe-by-default: the Drupal site
|
|
65
|
+
// exposes secret-, governance-, and PII-bearing entity types over JSON:API
|
|
66
|
+
// (oauth2_token, key, consumer, mcp_tool_config, profile, webform_submission,
|
|
67
|
+
// …), so anything not explicitly listed stays denied. Widen these groups to
|
|
68
|
+
// grant capability; never flip a content/developer tier to allowedEntityTypes:
|
|
69
|
+
// null.
|
|
70
|
+
|
|
71
|
+
// Content (fieldable) entities used to build and manage page content. All are
|
|
72
|
+
// JSON:API-writable, so the standard entity tools create/update them directly.
|
|
73
|
+
const CONTENT_STRUCTURAL = [
|
|
74
|
+
"paragraph",
|
|
75
|
+
"block_content",
|
|
76
|
+
"menu_link_content",
|
|
77
|
+
"redirect",
|
|
78
|
+
"path_alias",
|
|
79
|
+
"file",
|
|
80
|
+
];
|
|
81
|
+
|
|
82
|
+
// Content-model *config* entities. Allowlisted for READ / introspection only —
|
|
83
|
+
// they are config entities, so building/changing them goes through the governed
|
|
84
|
+
// config bridge (config_set → mcp_sentinel) or `drush config:import`, NOT
|
|
85
|
+
// drupal_entity_create. Granted to the developer tier only.
|
|
86
|
+
const SITE_BUILDER_CONFIG = [
|
|
87
|
+
"node_type",
|
|
88
|
+
"paragraphs_type",
|
|
89
|
+
"block_content_type",
|
|
90
|
+
"media_type",
|
|
91
|
+
"field_config",
|
|
92
|
+
"field_storage_config",
|
|
93
|
+
"entity_form_display",
|
|
94
|
+
"entity_view_display",
|
|
95
|
+
"taxonomy_vocabulary",
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
// Always-blocked: secrets, the agent's own governance config, and account data.
|
|
99
|
+
// Belt-and-suspenders denylist — these stay blocked even if a future change
|
|
100
|
+
// widens an allowlist. (deniedEntityTypes takes priority over allowedEntityTypes.)
|
|
101
|
+
const SENSITIVE_DENY = [
|
|
102
|
+
"user",
|
|
103
|
+
"oauth2_token",
|
|
104
|
+
"key",
|
|
105
|
+
"consumer",
|
|
106
|
+
"encryption_profile",
|
|
107
|
+
"mcp_tool_config",
|
|
108
|
+
"mcp_policy_profile",
|
|
109
|
+
];
|
|
110
|
+
|
|
54
111
|
// ---------------------------------------------------------------------------
|
|
55
112
|
// Preset definitions
|
|
56
113
|
// ---------------------------------------------------------------------------
|
|
@@ -74,8 +131,10 @@ const PRESETS = {
|
|
|
74
131
|
allowGraphqlMutations: false,
|
|
75
132
|
allowConfigRead: true, // config read-only
|
|
76
133
|
allowConfigWrite: false,
|
|
77
|
-
|
|
78
|
-
|
|
134
|
+
// Full content building: base content types + structural content entities
|
|
135
|
+
// (paragraphs, custom blocks, menu links, redirects, aliases, files).
|
|
136
|
+
allowedEntityTypes: ["node", "media", "taxonomy_term", ...CONTENT_STRUCTURAL],
|
|
137
|
+
deniedEntityTypes: [...SENSITIVE_DENY],
|
|
79
138
|
entityRules: {
|
|
80
139
|
node: { allowedOperations: ["read", "create", "update"] },
|
|
81
140
|
media: { allowedOperations: ["read", "create", "update"] },
|
|
@@ -93,8 +152,11 @@ const PRESETS = {
|
|
|
93
152
|
allowGraphqlMutations: false,
|
|
94
153
|
allowConfigRead: true,
|
|
95
154
|
allowConfigWrite: true, // governed config writes via drupal_config_set
|
|
96
|
-
|
|
97
|
-
|
|
155
|
+
// content-editor's content set PLUS site-building config entities, the
|
|
156
|
+
// latter for READ / introspection only — model changes go through the
|
|
157
|
+
// governed config bridge (drupal_config_set) / drush config:import.
|
|
158
|
+
allowedEntityTypes: ["node", "media", "taxonomy_term", ...CONTENT_STRUCTURAL, ...SITE_BUILDER_CONFIG],
|
|
159
|
+
deniedEntityTypes: [...SENSITIVE_DENY],
|
|
98
160
|
entityRules: {
|
|
99
161
|
node: { allowedOperations: ["read", "create", "update"] },
|
|
100
162
|
media: { allowedOperations: ["read", "create", "update"] },
|
|
@@ -141,8 +203,10 @@ const PRESETS = {
|
|
|
141
203
|
allowGraphqlMutations: false, // writes go through the JSON:API plane
|
|
142
204
|
allowConfigRead: true, // config read-only
|
|
143
205
|
allowConfigWrite: false,
|
|
144
|
-
|
|
145
|
-
|
|
206
|
+
// Full content building on the content tier: base content + structural
|
|
207
|
+
// content entities. No site-building config entities (developer tier only).
|
|
208
|
+
allowedEntityTypes: ["node", "taxonomy_term", "media", ...CONTENT_STRUCTURAL],
|
|
209
|
+
deniedEntityTypes: [...SENSITIVE_DENY],
|
|
146
210
|
entityRules: {},
|
|
147
211
|
globalRedactedFields: ["pass", "mail"],
|
|
148
212
|
},
|
package/src/lib/server-tools.js
CHANGED
|
@@ -25,12 +25,17 @@ import { clearToken } from "./oauth.js";
|
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
* Canonical server-side tool names for governed config operations.
|
|
28
|
-
*
|
|
28
|
+
*
|
|
29
|
+
* Drupal's mcp_server_tool_bridge exposes every Tool-API tool through the MCP
|
|
30
|
+
* protocol under the derivative name `tool_api.<mcp_tool_config id>`, so the
|
|
31
|
+
* governed config tools registered against mcp_sentinel's McpConfigGet/List/Set
|
|
32
|
+
* plugins surface as `tool_api.mcp_sentinel_config_*`. Keep the mapping here so
|
|
33
|
+
* a server-side rename is a one-line change.
|
|
29
34
|
*/
|
|
30
35
|
export const SERVER_TOOLS = {
|
|
31
|
-
configGet: "
|
|
32
|
-
configList: "
|
|
33
|
-
configSet: "
|
|
36
|
+
configGet: "tool_api.mcp_sentinel_config_get",
|
|
37
|
+
configList: "tool_api.mcp_sentinel_config_list",
|
|
38
|
+
configSet: "tool_api.mcp_sentinel_config_set",
|
|
34
39
|
};
|
|
35
40
|
|
|
36
41
|
// Monotonic JSON-RPC request id. A simple counter keeps ids unique per process
|