drupal-mcp-connector 2.18.0 → 2.19.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.
@@ -1,11 +1,11 @@
1
1
  ---
2
- description: "Compare the live graphql_compose schema to the last gqcc:generate snapshot (`drush graphql-compose-codegen:diff`). Requires the module and drushSsh. Missing command fails loud. If allowedCommands is set, include graphql-compose-codegen:diff."
2
+ description: "Compare the live graphql_compose schema to the last gqcc:generate snapshot (`drush graphql-compose-codegen:diff`). Uses the configured module binding; unbound sites require the module and drushSsh. Missing command fails loud. If allowedCommands is set, include graphql-compose-codegen:diff."
3
3
  argument-hint: "[site] [bundles] [skipFields]"
4
4
  ---
5
5
 
6
6
  Call the MCP tool `drupal_codegen_diff`.
7
7
 
8
- Compare the live graphql_compose schema to the last gqcc:generate snapshot (`drush graphql-compose-codegen:diff`). Requires the module and drushSsh. Missing command fails loud. If allowedCommands is set, include graphql-compose-codegen:diff.
8
+ Compare the live graphql_compose schema to the last gqcc:generate snapshot (`drush graphql-compose-codegen:diff`). Uses the configured module binding; unbound sites require the module and drushSsh. Missing command fails loud. If allowedCommands is set, include graphql-compose-codegen:diff.
9
9
 
10
10
  Parse the arguments supplied with this command into this tool's parameters:
11
11
 
@@ -1,11 +1,11 @@
1
1
  ---
2
- description: "Return TypeScript/GraphQL scaffold artefacts from graphql_compose_codegen as text (`drush graphql-compose-codegen:generate --dry-run`). Never writes on the Drupal host (no --output-dir). Copy artefacts locally. Requires the module and drushSsh. Missing command fails loud. If allowedCommands is set, include graphql-compose-codegen:generate."
2
+ description: "Return TypeScript/GraphQL scaffold artefacts from graphql_compose_codegen as text (`drush graphql-compose-codegen:generate --dry-run`). Never writes on the Drupal host (no --output-dir). Copy artefacts locally. Uses the configured module binding; unbound sites require the module and drushSsh. Missing command fails loud. If allowedCommands is set, include graphql-compose-codegen:generate."
3
3
  argument-hint: "[site] [bundles] [skipFields]"
4
4
  ---
5
5
 
6
6
  Call the MCP tool `drupal_codegen_generate`.
7
7
 
8
- Return TypeScript/GraphQL scaffold artefacts from graphql_compose_codegen as text (`drush graphql-compose-codegen:generate --dry-run`). Never writes on the Drupal host (no --output-dir). Copy artefacts locally. Requires the module and drushSsh. Missing command fails loud. If allowedCommands is set, include graphql-compose-codegen:generate.
8
+ Return TypeScript/GraphQL scaffold artefacts from graphql_compose_codegen as text (`drush graphql-compose-codegen:generate --dry-run`). Never writes on the Drupal host (no --output-dir). Copy artefacts locally. Uses the configured module binding; unbound sites require the module and drushSsh. Missing command fails loud. If allowedCommands is set, include graphql-compose-codegen:generate.
9
9
 
10
10
  Parse the arguments supplied with this command into this tool's parameters:
11
11
 
@@ -1,11 +1,11 @@
1
1
  ---
2
- description: "List node and paragraph bundles and extra fields from graphql_compose_codegen (`drush graphql-compose-codegen:inspect`). Requires the module and drushSsh. Missing command fails loud. If allowedCommands is set, include graphql-compose-codegen:inspect. Does not write files."
2
+ description: "List node and paragraph bundles and extra fields from graphql_compose_codegen (`drush graphql-compose-codegen:inspect`). Uses the configured module binding; unbound sites require the module and drushSsh. Missing command fails loud. If allowedCommands is set, include graphql-compose-codegen:inspect. Does not write files."
3
3
  argument-hint: "[site] [bundles] [skipFields]"
4
4
  ---
5
5
 
6
6
  Call the MCP tool `drupal_codegen_inspect`.
7
7
 
8
- List node and paragraph bundles and extra fields from graphql_compose_codegen (`drush graphql-compose-codegen:inspect`). Requires the module and drushSsh. Missing command fails loud. If allowedCommands is set, include graphql-compose-codegen:inspect. Does not write files.
8
+ List node and paragraph bundles and extra fields from graphql_compose_codegen (`drush graphql-compose-codegen:inspect`). Uses the configured module binding; unbound sites require the module and drushSsh. Missing command fails loud. If allowedCommands is set, include graphql-compose-codegen:inspect. Does not write files.
9
9
 
10
10
  Parse the arguments supplied with this command into this tool's parameters:
11
11
 
@@ -1,11 +1,11 @@
1
1
  ---
2
- description: "Run a single read-only SELECT through mcp_sentinel's governed command (`drush mcp-sentinel:sql-query`). Requires the site to set drushSsh.rawSql=\"governed\" AND the site's policy profile to set allow_raw_sql; both are off by default. The server refuses statements touching a denied entity type, a non-entity table, or a redacted field, and records every attempt in the tamper-evident audit log. Use the site-context or entity-schema tools for schema introspection."
2
+ description: "Run a single read-only SELECT through the configured sqlQuery module binding, or the legacy governed Drush command on unbound sites. Requires the site to set drushSsh.rawSql=\"governed\" AND the site's policy profile to set allow_raw_sql; both are off by default. The server refuses statements touching a denied entity type, a non-entity table, or a redacted field, and records every attempt in the tamper-evident audit log. Use the site-context or entity-schema tools for schema introspection."
3
3
  argument-hint: "<query> [site]"
4
4
  ---
5
5
 
6
6
  Call the MCP tool `drupal_drush_sql_query`.
7
7
 
8
- Run a single read-only SELECT through mcp_sentinel's governed command (`drush mcp-sentinel:sql-query`). Requires the site to set drushSsh.rawSql="governed" AND the site's policy profile to set allow_raw_sql; both are off by default. The server refuses statements touching a denied entity type, a non-entity table, or a redacted field, and records every attempt in the tamper-evident audit log. Use the site-context or entity-schema tools for schema introspection.
8
+ Run a single read-only SELECT through the configured sqlQuery module binding, or the legacy governed Drush command on unbound sites. Requires the site to set drushSsh.rawSql="governed" AND the site's policy profile to set allow_raw_sql; both are off by default. The server refuses statements touching a denied entity type, a non-entity table, or a redacted field, and records every attempt in the tamper-evident audit log. Use the site-context or entity-schema tools for schema introspection.
9
9
 
10
10
  Parse the arguments supplied with this command into this tool's parameters:
11
11
 
package/CHANGELOG.md CHANGED
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [2.19.0] - 2026-09-16
11
+
12
+ ### Added
13
+ - Codegen and governed SQL compatibility commands can use module-owned Tool API
14
+ actions through explicit bindings (#317). Configured calls validate fresh
15
+ schemas and never fall back to SSH on failure. Core and third-party commands
16
+ remain available; unbound sites retain their existing migration path.
17
+
10
18
  ## [2.18.0] - 2026-09-16
11
19
 
12
20
  ### Fixed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "drupal-mcp-connector",
3
- "version": "2.18.0",
3
+ "version": "2.19.0",
4
4
  "description": "Drupal MCP Connector — multi-site MCP server for Drupal with JSON:API and GraphQL, governed writes, draft translations, content tools, audit reports, and an SSH Drush bridge.",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * Tool group: GraphQL Compose Codegen (TypeScript / Next.js scaffolds).
3
3
  *
4
- * Thin Drush wrappers around `drupal/graphql_compose_codegen`. The generator
4
+ * Compatibility adapters through configured module bindings, or legacy Drush.
5
+ * Thin wrappers around `drupal/graphql_compose_codegen`. The generator
5
6
  * lives on the Drupal site; this connector does not reimplement it and does
6
7
  * not run GraphQL Code Generator against the SDL.
7
8
  *
@@ -16,6 +17,7 @@
16
17
 
17
18
  import { getSiteConfig } from "../lib/config.js";
18
19
  import { sshDrush } from "./drush.js";
20
+ import { callBoundModuleTool, toolResultData } from "../lib/server-tools.js";
19
21
  import { SecurityError } from "../lib/security.js";
20
22
  import { validateMachineName } from "../lib/validate.js";
21
23
 
@@ -75,6 +77,18 @@ async function runGqcc(
75
77
  const site = getSiteConfig(siteName);
76
78
  const names = parseMachineNameList(bundles, "bundles");
77
79
  const skip = parseMachineNameList(skipFields, "skipFields");
80
+ if (site.serverTools?.bindings !== undefined) {
81
+ const binding = new Map([
82
+ [INSPECT, "codegenInspect"], [DIFF, "codegenDiff"], [GENERATE, "codegenPreview"],
83
+ ]).get(subcommand);
84
+ const result = toolResultData(await callBoundModuleTool(site, binding, {
85
+ bundles: names, skip_fields: skip,
86
+ }, { operation: "read", scope: "mcp_config", capabilities: ["configRead"] }));
87
+ if (result?.success !== true || !result.data || typeof result.data !== "object" || Array.isArray(result.data)) {
88
+ throw new SecurityError("Codegen module returned an invalid result. No SSH fallback was attempted.");
89
+ }
90
+ return { output: JSON.stringify(result.data), command: `module:${binding}`, wroteFiles: false };
91
+ }
78
92
  const args = [subcommand];
79
93
  if (names.length) args.push(`--bundles=${names.join(",")}`);
80
94
  if (skip.length) args.push(`--skip-fields=${skip.join(",")}`);
@@ -126,19 +140,19 @@ export const definitions = [
126
140
  {
127
141
  name: "drupal_codegen_inspect",
128
142
  description:
129
- "List node and paragraph bundles and extra fields from graphql_compose_codegen (`drush graphql-compose-codegen:inspect`). Requires the module and drushSsh. Missing command fails loud. If allowedCommands is set, include graphql-compose-codegen:inspect. Does not write files.",
143
+ "List node and paragraph bundles and extra fields from graphql_compose_codegen (`drush graphql-compose-codegen:inspect`). Uses the configured module binding; unbound sites require the module and drushSsh. Missing command fails loud. If allowedCommands is set, include graphql-compose-codegen:inspect. Does not write files.",
130
144
  inputSchema: { type: "object", properties: LIST_PROPS },
131
145
  },
132
146
  {
133
147
  name: "drupal_codegen_diff",
134
148
  description:
135
- "Compare the live graphql_compose schema to the last gqcc:generate snapshot (`drush graphql-compose-codegen:diff`). Requires the module and drushSsh. Missing command fails loud. If allowedCommands is set, include graphql-compose-codegen:diff.",
149
+ "Compare the live graphql_compose schema to the last gqcc:generate snapshot (`drush graphql-compose-codegen:diff`). Uses the configured module binding; unbound sites require the module and drushSsh. Missing command fails loud. If allowedCommands is set, include graphql-compose-codegen:diff.",
136
150
  inputSchema: { type: "object", properties: LIST_PROPS },
137
151
  },
138
152
  {
139
153
  name: "drupal_codegen_generate",
140
154
  description:
141
- "Return TypeScript/GraphQL scaffold artefacts from graphql_compose_codegen as text (`drush graphql-compose-codegen:generate --dry-run`). Never writes on the Drupal host (no --output-dir). Copy artefacts locally. Requires the module and drushSsh. Missing command fails loud. If allowedCommands is set, include graphql-compose-codegen:generate.",
155
+ "Return TypeScript/GraphQL scaffold artefacts from graphql_compose_codegen as text (`drush graphql-compose-codegen:generate --dry-run`). Never writes on the Drupal host (no --output-dir). Copy artefacts locally. Uses the configured module binding; unbound sites require the module and drushSsh. Missing command fails loud. If allowedCommands is set, include graphql-compose-codegen:generate.",
142
156
  inputSchema: { type: "object", properties: LIST_PROPS },
143
157
  },
144
158
  ];
@@ -20,6 +20,7 @@
20
20
  * Requires per-site "drushSsh" config block — tools fail gracefully if absent.
21
21
  */
22
22
 
23
+ import { callBoundModuleTool, toolResultData } from "../lib/server-tools.js";
23
24
  import { Client } from "ssh2";
24
25
  import { readFileSync } from "fs";
25
26
  import { homedir } from "os";
@@ -456,6 +457,15 @@ async function sqlQuery({ site: siteName, query }) {
456
457
  assertGovernedRawSql(sshCfg, site._name);
457
458
  // Fast local reject only; mcp_sentinel's guard is the authority.
458
459
  validateSqlQuery(query);
460
+ if (site.serverTools?.bindings !== undefined) {
461
+ const result = toolResultData(await callBoundModuleTool(site, "sqlQuery", { query }, {
462
+ operation: "read", scope: "mcp_admin", capabilities: ["rawSql"],
463
+ }));
464
+ if (result?.success !== true || !result.data || !Array.isArray(result.data.rows)) {
465
+ throw new SecurityError("SQL module returned an invalid governed result. No SSH fallback was attempted.");
466
+ }
467
+ return result.data;
468
+ }
459
469
  const out = await sshDrush(site, ["mcp-sentinel:sql-query", query]);
460
470
  // The command emits a JSON object; a non-JSON reply means the command was
461
471
  // not found (mcp_sentinel absent or too old), which must not be reported as
@@ -534,7 +544,7 @@ export const definitions = [
534
544
  },
535
545
  {
536
546
  name: "drupal_drush_sql_query",
537
- description: "Run a single read-only SELECT through mcp_sentinel's governed command (`drush mcp-sentinel:sql-query`). Requires the site to set drushSsh.rawSql=\"governed\" AND the site's policy profile to set allow_raw_sql; both are off by default. The server refuses statements touching a denied entity type, a non-entity table, or a redacted field, and records every attempt in the tamper-evident audit log. Use the site-context or entity-schema tools for schema introspection.",
547
+ description: "Run a single read-only SELECT through the configured sqlQuery module binding, or the legacy governed Drush command on unbound sites. Requires the site to set drushSsh.rawSql=\"governed\" AND the site's policy profile to set allow_raw_sql; both are off by default. The server refuses statements touching a denied entity type, a non-entity table, or a redacted field, and records every attempt in the tamper-evident audit log. Use the site-context or entity-schema tools for schema introspection.",
538
548
  inputSchema: { type: "object", required: ["query"], properties: { site: { type: "string" }, query: { type: "string" } } },
539
549
  },
540
550
  {