Skip to content

Conversation

@theodorosploumis
Copy link
Member

@theodorosploumis theodorosploumis commented Dec 13, 2025

User description

Summary

  • Provide a concise overview of the change.
  • Highlight any relevant context or background information.

Testing

  • Describe the tests you ran to validate your changes.
  • Include commands executed and their results, if applicable.

Screenshots (optional)

  • Add screenshots or videos to demonstrate the change, if it affects the UI.

Additional Notes (optional)

  • Add any follow-up tasks, dependencies, or other considerations.

PR Type

Enhancement, Documentation


Description

  • Create @drupaltools/mcp npm package with 186+ Drupal tools

    • Self-contained MCP server with embedded projects data
    • Support for npx installation and global npm install
    • Three tools: list_tools, search_tools, get_tool
  • Add GitHub Actions workflows for automated package updates and publishing

    • Auto-update MCP package when project data changes
    • Publish to npm on version tags with GitHub releases
  • Improve UI for deprecated tools with styled badges

    • Light red background (#ffebee) with pill-shaped styling
    • JavaScript to automatically wrap deprecated text
  • Update project configuration and documentation

    • Simplified root package.json scripts
    • Comprehensive MCP package README with installation and usage examples

Diagram Walkthrough

flowchart LR
  A["Project Data<br/>_data/projects/*.yml"] -->|Build Process| B["MCP Server<br/>mcp-package/"]
  B -->|Embed Data| C["projects.json"]
  B -->|Package| D["@drupaltools/mcp<br/>npm package"]
  E["GitHub Actions<br/>Workflows"] -->|Auto-update| B
  E -->|Publish| F["npm Registry"]
  G["UI Enhancement<br/>Deprecated Styling"] -->|Display| H["Web Interface"]
Loading

File Walkthrough

Relevant files
Enhancement
6 files
index.js
MCP server implementation with tool discovery                       
+285/-0 
index.js
Compiled MCP server distribution file                                       
+285/-0 
build.js
Build script to generate projects.json from YAML                 
+159/-0 
publish.sh
Manual npm publishing script with version management         
+51/-0   
scripts.js
Add deprecated item styling with JavaScript                           
+11/-0   
_layout.scss
Add CSS styling for deprecated badges                                       
+30/-0   
Configuration changes
5 files
package.json
NPM package configuration and scripts                                       
+50/-0   
package.json
Distribution package.json for npm publishing                         
+51/-0   
update-mcp-package.yml
Auto-update MCP package on data changes                                   
+85/-0   
publish-npm.yml
Automated npm publishing workflow on version tags               
+104/-0 
package.json
Simplify root package scripts and description                       
+2/-5     
Documentation
3 files
README.md
Comprehensive MCP package documentation                                   
+186/-0 
README.md
Distribution README with project overview                               
+81/-0   
LICENSE.md
Update copyright year to 2025                                                       
+1/-1     
Tests
1 files
test.js
Test script for MCP server validation                                       
+66/-0   
Additional files
101 files
build-api.yml +0/-47   
AUTOMATION.md +0/-107 
api_generator.rb +0/-57   
build-api-data.js +0/-91   
watch-api.js +0/-40   
client.html +0/-299 
acquia-coding-standards-php.json +0/-28   
acquia-desktop.json +0/-27   
aegir.json +0/-36   
amazee-silverback.json +0/-33   
anavarre-drucker.json +0/-28   
ansible-deploy-drupal.json +0/-24   
aquifer.json +0/-37   
aws-refarch-drupal.json +0/-24   
azure-kubernetes-service-drupal.json +0/-25   
backupscale-drupal-kubernetes.json +0/-26   
beetbox.json +0/-34   
behat-drupal-extension.json +0/-26   
bitnami-docker.json +0/-29   
blt.json +0/-35   
boa.json +0/-37   
boran-docker-drupal.json +0/-29   
buildsh.json +0/-33   
capistrano-drupal-deploy.json +0/-31   
ci-tests.json +0/-33   
cibox.json +0/-30   
cikit.json +0/-29   
cmseek.json +0/-25   
cmsscan.json +0/-25   
coding-standards-php.json +0/-24   
composer-drupal-lenient.json +0/-29   
composer-drupal-optimizations.json +0/-23   
composerize-drupal.json +0/-23   
contrib-kanban.json +0/-24   
d8githooks.json +0/-28   
dash.json +0/-29   
dcycle-starterkit-drupalsite.json +0/-26   
ddd.json +0/-29   
ddev.json +0/-34   
deployer.json +0/-29   
deployotron.json +0/-36   
devilbox.json +0/-37   
dgoto.json +0/-28   
distros-bid.json +0/-30   
dminca-drupal-docker.json +0/-29   
docker-drupal-project.json +0/-30   
docker4drupal.json +0/-37   
dockerized-drupal-starter.json +0/-35   
docksal.json +0/-35   
dockstack.json +0/-30   
dogit.json +0/-26   
dorgflow.json +0/-31   
drainpipe.json +0/-29   
drall.json +0/-26   
drec.json +0/-24   
drocker.json +0/-29   
droopescan.json +0/-23   
dropcat.json +0/-39   
dropfabrik.json +0/-35   
dropwhale.json +0/-27   
druact.json +0/-31   
drubs.json +0/-38   
drubuild.json +0/-28   
drucker.json +0/-26   
drudock.json +0/-28   
drumkit.json +0/-28   
druml.json +0/-34   
drupal-ansible-tools.json +0/-30   
drupal-api-client-js.json +0/-26   
drupal-api-client-php.json +0/-24   
drupal-auto-update.json +0/-29   
drupal-aws-ansible.json +0/-25   
drupal-behat.json +0/-25   
drupal-check.json +0/-23   
drupal-code-builder.json +0/-27   
drupal-code-generator.json +0/-32   
drupal-composer-init.json +0/-32   
drupal-critical.json +0/-23   
drupal-debug.json +0/-24   
drupal-dev.json +0/-28   
drupal-docker-lite.json +0/-29   
drupal-dockerized.json +0/-27   
drupal-driver.json +0/-25   
drupal-install-cli.json +0/-25   
drupal-js-build.json +0/-24   
drupal-mrn.json +0/-24   
drupal-nginx-recipe.json +0/-25   
drupal-operator.json +0/-25   
drupal-org-api.json +0/-25   
drupal-pre-commit.json +0/-24   
drupal-provision.json +0/-34   
drupal-quality-checker.json +0/-26   
drupal-scaffold-docker.json +0/-29   
drupal-scaffold.json +0/-33   
drupal-sdk.json +0/-27   
drupal-spec-tool.json +0/-25   
drupal-test-traits.json +0/-26   
drupal-testing.json +0/-22   
drupal-theme-init.json +0/-24   
drupal-ti.json +0/-24   
Additional files not shown

theodorosploumis and others added 2 commits December 13, 2025 22:32
- Create @drupaltools/mcp npm package for easy installation
  - Self-contained package with 186 Drupal tools data
  - Support for npx installation: npx @drupaltools/mcp@latest
  - GitHub Actions workflow for automated npm publishing
  - Interactive publish script for manual releases

- Remove obsolete REST API implementation
  - Deleted /api/ folder and all related files
  - Removed build-api.yml workflow and API generator plugin
  - Cleaned up _scripts/ folder and automation scripts

- Improve UI for deprecated tools
  - Add light red background (#ffebee) for deprecated tags
  - JavaScript to automatically wrap deprecated text
  - CSS styling with pill appearance

- Update documentation
  - Simplified MCP server README (removed API references)
  - Updated npm package documentation with clear installation instructions
  - Removed redundant local install section

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Created .github/workflows/update-mcp-package.yml
- Triggers on pushes/PRs that modify _data/projects/*.yml
- Properly checks for changes before committing (avoids empty commits)
- Updates MCP package with latest Drupal tools data
- Includes detailed summary in GitHub Actions output

Key improvements:
- Uses git status --porcelain to check for actual changes
- Only commits when MCP package data has been updated
- Provides clear feedback about what was changed
- Includes tool count in commit message

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@qodo-code-review
Copy link

qodo-code-review bot commented Dec 13, 2025

PR Compliance Guide 🔍

(Compliance updated until commit 4df1933)

Below is a summary of compliance checks for this PR:

Security Compliance
Hardcoded local paths

Description: The test script hardcodes absolute local filesystem paths for index.js and working
directory, which can leak developer environment details and break isolation when executed
in CI or other machines; use relative paths or dynamically resolve paths instead.
test.js [7-10]

Referred Code
const server = spawn('node', ['/var/www/html/NON-DRUPAL/drupaltools.github.io/mcp-package/dist/index.js'], {
  stdio: ['pipe', 'pipe', 'inherit'],
  cwd: '/var/www/html/NON-DRUPAL/drupaltools.github.io/mcp-package/dist'
});
CI secret exposure risk

Description: The workflow publishes to npm using NODE_AUTH_TOKEN without restricting the token’s
permissions in the workflow (e.g., using an npm automation token with least privilege and
environment protections), increasing risk if a malicious PR gains execution; enforce
least-privilege tokens and protected branches/environments.
publish-npm.yml [66-71]

Referred Code
- name: Publish to npm
  run: |
    cd mcp-package/dist
    npm publish
  env:
    NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
Verbose error disclosure

Description: The server returns error messages directly from error.message to clients, potentially
exposing internal details; sanitize or generalize error outputs to prevent information
disclosure.
index.js [141-170]

Referred Code
async handleListTools(args) {
  const { category = null, limit = 50 } = args;
  let tools = Object.values(PROJECTS_DATA);

  if (category) {
    tools = tools.filter(tool =>
      tool.category && tool.category.includes(category.toLowerCase())
    );
  }

  tools = tools.slice(0, limit);

  return {
    content: [
      {
        type: "text",
        text: JSON.stringify({
          tools: tools.map(tool => ({
            id: tool.id,
            name: tool.name,
            description: tool.description,



 ... (clipped 9 lines)
Verbose error disclosure

Description: The built server also returns raw error.message to clients, mirroring the information
disclosure risk in source; ensure errors are sanitized in production builds.
index.js [141-170]

Referred Code
async handleListTools(args) {
  const { category = null, limit = 50 } = args;
  let tools = Object.values(PROJECTS_DATA);

  if (category) {
    tools = tools.filter(tool =>
      tool.category && tool.category.includes(category.toLowerCase())
    );
  }

  tools = tools.slice(0, limit);

  return {
    content: [
      {
        type: "text",
        text: JSON.stringify({
          tools: tools.map(tool => ({
            id: tool.id,
            name: tool.name,
            description: tool.description,



 ... (clipped 9 lines)
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

🔴
Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Unhandled null cases: The search and list handlers access properties like tool.description and tool.name without
guarding against missing or null values, which can cause runtime errors.

Referred Code
async handleSearchTools(args) {
  const { query, limit = 10 } = args;
  const queryLower = query.toLowerCase();
  const tools = Object.values(PROJECTS_DATA);

  const scored = tools.map(tool => {
    let score = 0;

    // Title matches
    if (tool.name.toLowerCase().includes(queryLower)) score += 100;

    // Category matches
    if (tool.category) {
      tool.category.forEach(cat => {
        if (cat.toLowerCase().includes(queryLower)) score += 50;
      });
    }

    // Tag matches
    if (tool.tags) {
      tool.tags.forEach(tag => {



 ... (clipped 18 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status:
Missing audit logs: The MCP server handles tool queries and errors but does not emit structured audit logs for
critical actions such as requests served and outcomes, making post-incident reconstruction
unclear.

Referred Code
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
  const { name, arguments: args } = request.params;

  try {
    switch (name) {
      case "list_tools":
        return await this.handleListTools(args);
      case "search_tools":
        return await this.handleSearchTools(args);
      case "get_tool":
        return await this.handleGetTool(args);
      default:
        throw new Error(`Unknown tool: ${name}`);
    }
  } catch (error) {
    return {
      content: [
        {
          type: "text",
          text: `Error: ${error.message}`,
        },



 ... (clipped 5 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status:
Raw error message: User-facing tool responses return Error: ${error.message} which may expose internal
details depending on thrown error messages; consider generic client messages with internal
logging.

Referred Code
} catch (error) {
  return {
    content: [
      {
        type: "text",
        text: `Error: ${error.message}`,
      },
    ],
  };

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status:
Input validation gaps: Handlers accept arbitrary strings (e.g., category, query, tool_id) without validation or
normalization beyond toLowerCase, risking unexpected behavior with large inputs or special
characters.

Referred Code
async handleListTools(args) {
  const { category = null, limit = 50 } = args;
  let tools = Object.values(PROJECTS_DATA);

  if (category) {
    tools = tools.filter(tool =>
      tool.category && tool.category.includes(category.toLowerCase())
    );
  }

  tools = tools.slice(0, limit);

  return {
    content: [
      {
        type: "text",
        text: JSON.stringify({
          tools: tools.map(tool => ({
            id: tool.id,
            name: tool.name,
            description: tool.description,



 ... (clipped 12 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

Previous compliance checks

Compliance check up to commit fce6eee
Security Compliance
Path disclosure

Description: The test script hardcodes absolute local filesystem paths for index.js and cwd, which can
leak environment structure and is unsafe for published packages or CI environments; use
relative paths or build-time templating instead.
test.js [7-10]

Referred Code
const server = spawn('node', ['/var/www/html/NON-DRUPAL/drupaltools.github.io/mcp-package/dist/index.js'], {
  stdio: ['pipe', 'pipe', 'inherit'],
  cwd: '/var/www/html/NON-DRUPAL/drupaltools.github.io/mcp-package/dist'
});
CI write permissions

Description: The workflow commits and pushes directly to the default branch with a bot identity using
the repository GITHUB_TOKEN, which if misused could enable unauthorized modification of
release artifacts; restrict paths, require approvals, or use protected branches to
mitigate.
update-mcp-package.yml [64-71]

Referred Code
- Rebuilt projects.json with $(cd mcp-package && node -e "console.log(Object.keys(JSON.parse(require('fs').readFileSync('dist/projects.json', 'utf8'))).length)" tools
- Updated package when _data/projects changed

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>"
git push
Error detail exposure

Description: Tool execution errors are returned to clients with raw error.message, which may expose
internal details; return generic errors and log detailed messages server-side instead.
index.js [114-137]

Referred Code
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
  const { name, arguments: args } = request.params;

  try {
    switch (name) {
      case "list_tools":
        return await this.handleListTools(args);
      case "search_tools":
        return await this.handleSearchTools(args);
      case "get_tool":
        return await this.handleGetTool(args);
      default:
        throw new Error(`Unknown tool: ${name}`);
    }
  } catch (error) {
    return {
      content: [
        {
          type: "text",
          text: `Error: ${error.message}`,
        },



 ... (clipped 3 lines)
Error detail exposure

Description: Tool execution errors are sent back including error.message, risking leakage of internal
information to untrusted clients; prefer sanitized error responses.
index.js [128-137]

Referred Code
} catch (error) {
  return {
    content: [
      {
        type: "text",
        text: `Error: ${error.message}`,
      },
    ],
  };
}
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status:
Missing Audit Logs: New MCP server handlers perform critical actions (tool queries and lookups) without
emitting structured audit logs including user identity, timestamp, action, and outcome.

Referred Code
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
  const { name, arguments: args } = request.params;

  try {
    switch (name) {
      case "list_tools":
        return await this.handleListTools(args);
      case "search_tools":
        return await this.handleSearchTools(args);
      case "get_tool":
        return await this.handleGetTool(args);
      default:
        throw new Error(`Unknown tool: ${name}`);
    }
  } catch (error) {
    return {
      content: [
        {
          type: "text",
          text: `Error: ${error.message}`,
        },



 ... (clipped 39 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Generic Errors: Error handling returns generic text responses and may not validate null/empty inputs
(e.g., missing args fields) for handlers like list_tools/search_tools/get_tool, risking
unhandled edge cases.

Referred Code
          throw new Error(`Unknown tool: ${name}`);
      }
    } catch (error) {
      return {
        content: [
          {
            type: "text",
            text: `Error: ${error.message}`,
          },
        ],
      };
    }
  });
}

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status:
Error Detail Exposure: The user-facing error response includes the thrown error message (Error: ${error.message})
which could expose internal details depending on the exception content.

Referred Code
return {
  content: [
    {
      type: "text",
      text: `Error: ${error.message}`,
    },
  ],
};

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status:
Unstructured Logs: Logging via console.error and console statements is unstructured text and lacks safeguards
against leaking sensitive data, reducing auditability and control.

Referred Code
setupErrorHandling() {
  this.server.onerror = (error) => console.error("[MCP Error]", error);
  process.on("SIGINT", async () => {
    await this.server.close();
    process.exit(0);
  });
}

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status:
Weak Input Validation: Handlers rely on request params without explicit validation/sanitization (e.g., type
checks and bounds for limit, presence/type of query and tool_id), which may allow invalid
inputs or edge case failures.

Referred Code
async handleListTools(args) {
  const { category = null, limit = 50 } = args;
  let tools = Object.values(PROJECTS_DATA);

  if (category) {
    tools = tools.filter(tool =>
      tool.category && tool.category.includes(category.toLowerCase())
    );
  }

  tools = tools.slice(0, limit);

  return {
    content: [
      {
        type: "text",
        text: JSON.stringify({
          tools: tools.map(tool => ({
            id: tool.id,
            name: tool.name,
            description: tool.description,



 ... (clipped 12 lines)

Learn more about managing compliance generic rules or creating your own custom rules

@qodo-code-review
Copy link

qodo-code-review bot commented Dec 13, 2025

PR Code Suggestions ✨

Latest suggestions up to 4df1933

CategorySuggestion                                                                                                                                    Impact
General
Fetch full history for diffing

Configure the checkout action with fetch-depth: 0 to fetch the full git history,
ensuring reliable change detection in the workflow.

.github/workflows/update-mcp-package.yml [46-55]

+- name: Checkout repository
+  uses: actions/checkout@v4
+  with:
+    token: ${{ secrets.GITHUB_TOKEN }}
+    fetch-depth: 0
+
 - name: Check for changes
   id: changes
   run: |
     if [[ -n $(git status --porcelain mcp-package/) ]]; then
       echo "has_changes=true" >> $GITHUB_OUTPUT
       echo "✅ MCP package data updated"
     else
       echo "has_changes=false" >> $GITHUB_OUTPUT
       echo "ℹ️ No changes to MCP package"
     fi
  • Apply / Chat
Suggestion importance[1-10]: 8

__

Why: This suggestion correctly identifies a potential and common issue with shallow clones in CI when checking for file changes, and fetch-depth: 0 is the correct fix to ensure the workflow's reliability.

Medium
Add npm dependency caching

Add caching for npm dependencies in the GitHub Actions workflow and use npm ci
for installation to improve performance and reliability.

.github/workflows/update-mcp-package.yml [36-39]

+- name: Setup Node.js
+  uses: actions/setup-node@v4
+  with:
+    node-version: '18'
+    cache: 'npm'
+    cache-dependency-path: mcp-package/package-lock.json
+
+- name: Install dependencies
+  run: |
+    cd mcp-package
+    npm ci
+
 - name: Build the MCP package with latest data
   run: |
     cd mcp-package
     npm run build
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: This is a valuable performance and reliability improvement for the CI workflow, using npm ci and caching dependencies, which is a standard best practice.

Medium
Enforce strict CI test behavior

Set the CI=true environment variable for the npm install and npm test steps to
enforce strict, non-interactive behavior suitable for a CI environment.

.github/workflows/update-mcp-package.yml [41-44]

+- name: Install dependencies
+  env:
+    CI: true
+  run: |
+    cd mcp-package
+    npm ci
+
 - name: Run tests
+  env:
+    CI: true
   run: |
     cd mcp-package
     npm test
  • Apply / Chat
Suggestion importance[1-10]: 6

__

Why: Setting CI=true is a good practice for ensuring tests run in a strict, non-interactive mode, which can improve the reliability of the test step in the CI workflow.

Low
Prevent hard process exit on load

Instead of calling process.exit(1) when loading projects.json fails, return an
empty object to allow the server to start in a degraded state and handle the
error gracefully.

mcp-package/src/index.js [18-29]

 const PROJECTS_DATA = loadProjectsData();
 
 function loadProjectsData() {
   try {
-    // Try to load from the embedded JSON file first
     const projectsJson = readFileSync(join(__dirname, 'projects.json'), 'utf8');
     return JSON.parse(projectsJson);
   } catch (error) {
     console.error('Failed to load projects data:', error);
-    process.exit(1);
+    return {};
   }
 }

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 4

__

Why: While avoiding process.exit is generally good practice, for a standalone CLI server failing to load its essential data on startup, exiting with an error is an acceptable and clear failure mode.

Low
Possible issue
Validate and guard request params

Add checks for request.params and its arguments property to prevent crashes from
malformed requests. Use nullish coalescing for safe access and validate required
fields.

mcp-package/src/index.js [114-138]

 this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
-  const { name, arguments: args } = request.params;
+  const params = request?.params ?? {};
+  const name = params.name;
+  const args = params.arguments ?? {};
 
   try {
     switch (name) {
       case "list_tools":
-        return await this.handleListTools(args);
+        return await this.handleListTools(args ?? {});
       case "search_tools":
+        if (!args || typeof args.query !== "string") {
+          throw new Error("Missing required 'query' string");
+        }
         return await this.handleSearchTools(args);
       case "get_tool":
+        if (!args || typeof args.tool_id !== "string") {
+          throw new Error("Missing required 'tool_id' string");
+        }
         return await this.handleGetTool(args);
       default:
         throw new Error(`Unknown tool: ${name}`);
     }
   } catch (error) {
     return {
       content: [
         {
           type: "text",
-          text: `Error: ${error.message}`,
+          text: `Error: ${error?.message ?? String(error)}`,
         },
       ],
     };
   }
 });

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 7

__

Why: This is a good suggestion for improving robustness by adding defensive checks against malformed requests, which prevents potential runtime errors if request.params is missing.

Medium
Validate version before publishing

Add a validation step to ensure the VERSION variable contains a valid semantic
version string before proceeding with the publish steps.

.github/workflows/publish-npm.yml [47-57]

 - name: Determine version
   id: version
   run: |
     if [[ "${{ github.ref_type }}" == "tag" ]]; then
       VERSION=${{ github.ref_name }}
-      VERSION=${VERSION#v}  # Remove 'v' prefix if present
+      VERSION=${VERSION#v}
     else
       VERSION=${{ github.event.inputs.version }}
+    fi
+    if [[ -z "$VERSION" || ! "$VERSION" =~ ^[0-9]+(\.[0-9]+){2}(-[0-9A-Za-z.-]+)?$ ]]; then
+      echo "Invalid or empty version: '$VERSION'"; exit 1
     fi
     echo "version=$VERSION" >> $GITHUB_OUTPUT
     echo "Publishing version: $VERSION"
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: This suggestion adds a useful validation step to ensure the version string is a valid semantic version, which improves the workflow's robustness by failing early and preventing potential publishing errors.

Medium
Enable provenance and explicit access

Add the --provenance and --access public flags to the npm publish command to
leverage OIDC for publishing and ensure the package is public.

.github/workflows/publish-npm.yml [66-71]

 - name: Publish to npm
   run: |
     cd mcp-package/dist
-    npm publish
+    npm publish --provenance --access public
   env:
     NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
  • Apply / Chat
Suggestion importance[1-10]: 5

__

Why: The suggestion correctly identifies that npm publish --provenance should be used with id-token: write permission for enhanced security, but it fails to mention that the NODE_AUTH_TOKEN environment variable must be removed for it to work.

Low
General
Use relative paths in tests

Replace the hardcoded absolute paths in mcp-package/dist/test.js with relative
paths to make the test script portable and executable on different machines.

mcp-package/dist/test.js [7-10]

-const server = spawn('node', ['/var/www/html/NON-DRUPAL/drupaltools.github.io/mcp-package/dist/index.js'], {
+import { fileURLToPath } from 'url';
+import { dirname, join } from 'path';
+
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = dirname(__filename);
+
+const serverPath = join(__dirname, 'index.js');
+
+const server = spawn('node', [serverPath], {
   stdio: ['pipe', 'pipe', 'inherit'],
-  cwd: '/var/www/html/NON-DRUPAL/drupaltools.github.io/mcp-package/dist'
+  cwd: __dirname
 });

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies that the hardcoded absolute paths in the generated test file mcp-package/dist/test.js make the tests non-portable, which is a significant issue.

Medium
Possible issue
Validate request parameters defensively

Add defensive checks for request.params and tool-specific arguments within the
CallToolRequestSchema handler to prevent crashes from malformed client requests
and provide clearer error messages.

mcp-package/src/index.js [114-138]

 this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
-  const { name, arguments: args } = request.params;
+  const params = request?.params ?? {};
+  const name = params.name;
+  const args = params.arguments ?? {};
 
   try {
     switch (name) {
       case "list_tools":
-        return await this.handleListTools(args);
+        return await this.handleListTools(args ?? {});
       case "search_tools":
+        if (!args || typeof args.query !== "string") {
+          throw new Error("Missing required 'query' string");
+        }
         return await this.handleSearchTools(args);
       case "get_tool":
+        if (!args || typeof args.tool_id !== "string") {
+          throw new Error("Missing required 'tool_id' string");
+        }
         return await this.handleGetTool(args);
       default:
         throw new Error(`Unknown tool: ${name}`);
     }
   } catch (error) {
     return {
       content: [
         {
           type: "text",
-          text: `Error: ${error.message}`,
+          text: `Error: ${error?.message ?? String(error)}`,
         },
       ],
     };
   }
 });

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies a potential runtime error if request.params is missing and improves robustness by adding defensive checks for request parameters, which is good practice for a server.

Medium
Fix category typo for filtering

Correct the misspelled category guality-check to quality-check to ensure search
and filtering work correctly.

mcp-package/dist/projects.json [663-685]

 {
   "coding-standards-php": {
     "name": "Coding standards php",
     "year_created": 2018,
     "source": "https://github.com/acquia/coding-standards-php",
     "homepage": "https://github.com/acquia/coding-standards-php",
     "docs": null,
     "logo": null,
     "description": "Acquia Coding Standards for PHP is a collection of PHP_CodeSniffer rules (sniffs) for Acquia coding standards for PHP projects, including Drupal extensions.",
     "requires": [
       "composer",
       "phpcs"
     ],
     "drupal_versions": [
       9
     ],
     "category": [
-      "guality-check",
+      "quality-check",
       "coding-standards",
       "testing",
       "phpcs"
     ],
     "id": "coding-standards-php"
   }
 ...

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 7

__

Why: This suggestion correctly identifies a typo (guality-check) in the data that would negatively impact search and filtering functionality, improving data integrity and user experience.

Medium
Build dist automatically on install

Add a prepare script to package.json to automatically run the build step upon
npm install, ensuring the dist directory is created for local development or
git-based installs.

mcp-package/package.json [1-20]

 {
   "name": "@drupaltools/mcp",
   "version": "1.0.0",
   "description": "Model Context Protocol (MCP) server for discovering Drupal development tools, utilities, and plugins",
   "type": "module",
   "main": "dist/index.js",
   "bin": {
     "drupaltools-mcp": "dist/index.js"
   },
   "files": [
     "dist/",
     "README.md"
   ],
   "scripts": {
     "build": "node build.js",
+    "prepare": "npm run build",
     "prepublishOnly": "npm run build",
     "test": "node dist/test.js",
     "publish:npm": "cd dist && npm publish",
     "publish": "./publish.sh"
   },
-  ...
+  "keywords": [
+    "mcp",
+    "model-context-protocol",
+    "drupal",
+    "tools",
+    "static-analysis",
+    "development",
+    "cli"
+  ],
+  "author": "Drupal Tools Community",
+  "license": "MIT",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/drupaltools/drupaltools.github.io.git"
+  },
+  "bugs": {
+    "url": "https://github.com/drupaltools/drupaltools.github.io/issues"
+  },
+  "homepage": "https://github.com/drupaltools/drupaltools.github.io#readme",
+  "dependencies": {
+    "@modelcontextprotocol/sdk": "^1.24.3",
+    "js-yaml": "^4.1.1"
+  },
+  "engines": {
+    "node": ">=18.0.0"
+  },
+  "publishConfig": {
+    "access": "public"
+  }
 }

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies that installing the package from a git repository will fail without a prepare script to build the dist directory, which is a valid and important improvement for developers.

Medium
Null-safe scoring of tool fields

In handleSearchTools, add null-safety checks for tool properties like name and
description before calling methods like .toLowerCase() to prevent TypeError if
the data is incomplete.

mcp-package/src/index.js [175-235]

 async handleSearchTools(args) {
   const { query, limit = 10 } = args;
-  const queryLower = query.toLowerCase();
+  const queryLower = (query ?? "").toLowerCase();
   const tools = Object.values(PROJECTS_DATA);
 
   const scored = tools.map(tool => {
     let score = 0;
 
-    // Title matches
-    if (tool.name.toLowerCase().includes(queryLower)) score += 100;
+    const name = typeof tool?.name === "string" ? tool.name : "";
+    const description = typeof tool?.description === "string" ? tool.description : "";
+    const categories = Array.isArray(tool?.category) ? tool.category : [];
+    const tags = Array.isArray(tool?.tags) ? tool.tags : [];
+    const homepage = typeof tool?.homepage === "string" ? tool.homepage : "";
+    const source = typeof tool?.source === "string" ? tool.source : "";
 
-    // Category matches
-    if (tool.category) {
-      tool.category.forEach(cat => {
-        if (cat.toLowerCase().includes(queryLower)) score += 50;
-      });
-    }
+    if (name.toLowerCase().includes(queryLower)) score += 100;
 
-    // Tag matches
-    if (tool.tags) {
-      tool.tags.forEach(tag => {
-        if (tag.toLowerCase().includes(queryLower)) score += 30;
-      });
-    }
+    categories.forEach(cat => {
+      if (typeof cat === "string" && cat.toLowerCase().includes(queryLower)) score += 50;
+    });
 
-    // Description matches
-    if (tool.description.toLowerCase().includes(queryLower)) score += 20;
+    tags.forEach(tag => {
+      if (typeof tag === "string" && tag.toLowerCase().includes(queryLower)) score += 30;
+    });
 
-    // Homepage/source matches
-    if (tool.homepage && tool.homepage.toLowerCase().includes(queryLower)) score += 10;
-    if (tool.source && tool.source.toLowerCase().includes(queryLower)) score += 10;
+    if (description.toLowerCase().includes(queryLower)) score += 20;
+
+    if (homepage.toLowerCase().includes(queryLower)) score += 10;
+    if (source.toLowerCase().includes(queryLower)) score += 10;
 
     return { tool, score };
   })
   .filter(item => item.score > 0)
   .sort((a, b) => b.score - a.score)
   .slice(0, limit)
   .map(item => item.tool);
 
   return {
     content: [
       {
         type: "text",
         text: JSON.stringify({
           query,
           results: scored.map(tool => ({
             id: tool.id,
             name: tool.name,
             description: tool.description,
             categories: tool.category || [],
             tags: tool.tags || [],
             drupal_versions: tool.drupal_versions || [],
             homepage: tool.homepage,
             docs: tool.docs
           })),
           total: scored.length
         }, null, 2),
       },
     ],
   };
 }

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 6

__

Why: The suggestion correctly points out that missing properties in the PROJECTS_DATA could cause runtime errors. Adding null-safety checks improves the robustness of the search functionality.

Low
Ensure proper npm auth usage

Add caching and always-auth: true to the setup-node step to improve workflow
performance and ensure robust npm authentication.

.github/workflows/publish-npm.yml [25-30]

 - name: Setup Node.js
   uses: actions/setup-node@v4
   with:
     node-version: '18'
     registry-url: 'https://registry.npmjs.org'
     scope: '@drupaltools'
+    always-auth: true
+    cache: 'npm'
+    cache-dependency-path: mcp-package/package-lock.json
  • Apply / Chat
Suggestion importance[1-10]: 6

__

Why: The suggestion correctly proposes adding caching to speed up the workflow and always-auth: true for more robust authentication, which are best practices for a CI/CD pipeline.

Low
Safely quote commit message

Refactor the multiline commit message in the GitHub workflow to use a heredoc to
prevent potential shell interpolation errors.

.github/workflows/update-mcp-package.yml [57-71]

 - name: Commit and push changes
   if: steps.changes.outputs.has_changes == 'true'
   run: |
     git config --local user.email "action@github.com"
     git config --local user.name "GitHub Action"
     git add mcp-package/
-    git commit -m "chore: Update MCP package with latest Drupal tools data
+    TOOLS_COUNT=$(cd mcp-package && node -e "console.log(Object.keys(JSON.parse(require('fs').readFileSync('dist/projects.json','utf8'))).length)")
+    git commit -m "$(cat <<'EOF'
+chore: Update MCP package with latest Drupal tools data
 
-    - Rebuilt projects.json with $(cd mcp-package && node -e "console.log(Object.keys(JSON.parse(require('fs').readFileSync('dist/projects.json', 'utf8'))).length)" tools
-    - Updated package when _data/projects changed
+- Rebuilt projects.json with TOOLS_COUNT_PLACEHOLDER tools
+- Updated package when _data/projects changed
 
-    🤖 Generated with [Claude Code](https://claude.com/claude-code)
+🤖 Generated with [Claude Code](https://claude.com/claude-code)
 
-    Co-Authored-By: Claude <noreply@anthropic.com>"
+Co-Authored-By: Claude <noreply@anthropic.com>
+EOF
+)" | sed "s/TOOLS_COUNT_PLACEHOLDER/$TOOLS_COUNT/"
     git push

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 5

__

Why: The suggestion correctly identifies a potential shell interpolation issue in the multiline commit message and proposes a more robust solution, improving the reliability of the CI workflow.

Low
  • More

Previous suggestions

Suggestions up to commit fce6eee
CategorySuggestion                                                                                                                                    Impact
Possible issue
Prevent workflow failure on pull requests

Add a condition to the Commit and push changes step to ensure it only runs on
pushes to the main or master branch, preventing failures on pull request
triggers.

.github/workflows/update-mcp-package.yml [57-71]

 - name: Commit and push changes
-  if: steps.changes.outputs.has_changes == 'true'
+  if: steps.changes.outputs.has_changes == 'true' && github.event_name == 'push' && (github.ref_name == 'main' || github.ref_name == 'master')
   run: |
     git config --local user.email "action@github.com"
     git config --local user.name "GitHub Action"
     git add mcp-package/
     git commit -m "chore: Update MCP package with latest Drupal tools data
 
     - Rebuilt projects.json with $(cd mcp-package && node -e "console.log(Object.keys(JSON.parse(require('fs').readFileSync('dist/projects.json', 'utf8'))).length)" tools
     - Updated package when _data/projects changed
 
     🤖 Generated with [Claude Code](https://claude.com/claude-code)
 
     Co-Authored-By: Claude <noreply@anthropic.com>"
     git push
Suggestion importance[1-10]: 9

__

Why: This suggestion correctly identifies a critical flaw in the workflow logic that would cause it to fail on every pull request, making it a crucial bug fix.

High
Fix incorrect package entrypoint paths

Correct the main and bin paths in mcp-package/dist/package.json to be relative
to the dist directory (e.g., index.js) to fix package resolution after
installation.

mcp-package/dist/package.json [6-9]

-"main": "dist/index.js",
+"main": "index.js",
 "bin": {
-  "drupaltools-mcp": "dist/index.js"
+  "drupaltools-mcp": "index.js"
 },
Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies a critical packaging bug where incorrect paths in package.json would make the published npm package unusable.

High
Use relative paths in test script

Modify the build script to use relative paths instead of absolute paths when
generating the test script, ensuring the test is portable and can run in any
environment.

mcp-package/build.js [77-80]

-const server = spawn('node', ['${serverDest}'], {
+const server = spawn('node', ['index.js'], {
   stdio: ['pipe', 'pipe', 'inherit'],
-  cwd: '${distDir}'
+  cwd: __dirname
 });
Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies that the build script generates a non-portable test file with hardcoded absolute paths, which would fail in any other environment.

Medium
Fix case-sensitive category filtering bug

Fix a bug in the category filtering logic to make it case-insensitive. This is
done by converting the tool's categories to lowercase before comparing them
against the user's input.

mcp-package/src/index.js [145-149]

 if (category) {
+  const lowerCategory = category.toLowerCase();
   tools = tools.filter(tool =>
-    tool.category && tool.category.includes(category.toLowerCase())
+    tool.category && tool.category.some(c => c.toLowerCase() === lowerCategory)
   );
 }
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies a case-sensitivity bug in the category filtering logic and provides a valid fix, improving the feature's correctness.

Medium
Refactor the release publishing steps

Refactor the publishing script to update the version number before the build
step, ensuring the dist directory is rebuilt with the correct version before
publishing.

mcp-package/publish.sh [29-39]

-# Update version
+# Update version in root package.json
 echo "📝 Updating version to $VERSION..."
-npm version $VERSION --no-git-tag-version
-cd dist
-npm version $VERSION --no-git-tag-version
-cd ..
+npm version "$VERSION" --no-git-tag-version
 
-# Publish to npm
+# Re-build the package to include the new version
+echo "🔨 Re-building the package with new version..."
+npm run build
+
+# Publish to npm from the dist folder
 echo "🚀 Publishing to npm..."
 cd dist
 npm publish
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies a flaw in the publishing logic and proposes a more robust workflow, ensuring the published package is consistent with the build artifacts.

Medium
Fix incorrect command-line flag typo

In the README.md, correct the Jekyll command by replacing the incorrect --server
flag with the correct --port flag to specify the server port.

mcp-package/dist/README.md [48]

-bundle exec jekyll serve --server 8080
+bundle exec jekyll serve --port 8080
Suggestion importance[1-10]: 5

__

Why: The suggestion correctly identifies a typo in a command within the README.md file, which improves the quality and accuracy of the documentation for local setup.

Low
Security
Prevent a potential XSS vulnerability

Refactor the code to prevent a potential Cross-Site Scripting (XSS)
vulnerability by using a callback function with jQuery's .html() method for
safer content replacement.

js/scripts.js [10-18]

 // Style deprecated items
 $(".filter-category").each(function() {
   const $this = $(this);
   const text = $this.text();
   if (text.toLowerCase().includes("deprecated")) {
     // Find and wrap the "deprecated" text
-    const deprecatedText = $this.text().replace(/deprecated/gi, '<span class="deprecated">deprecated</span>');
-    $this.html(deprecatedText);
+    $this.html(function(index, oldHtml) {
+      return oldHtml.replace(/deprecated/gi, '<span class="deprecated">deprecated</span>');
+    });
   }
 });
Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies a potential XSS vulnerability and provides a safer implementation using a callback function with .html(), which is a best practice.

Medium
General
Update action to a supported version

Update the actions/create-release action from the outdated v1 to a more recent
version like v1.1.4 to ensure it runs on a supported Node.js environment.

.github/workflows/publish-npm.yml [73-104]

 - name: Create GitHub Release
   if: github.ref_type == 'tag'
-  uses: actions/create-release@v1
+  uses: actions/create-release@v1.1.4
   env:
     GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
   with:
     tag_name: ${{ github.ref_name }}
     release_name: Release ${{ github.ref_name }}
     draft: false
     prerelease: false
     body: |
       ## Changes in ${{ github.ref_name }}
 
       This release updates the @drupaltools/mcp npm package with the latest Drupal tools data.
 
       ### Installation
       ```bash
       npx @drupaltools/mcp@${{ steps.version.outputs.version }}
       ```
 
       ### Claude Desktop Configuration
       ```json
       {
         "mcpServers": {
           "drupaltools": {
             "type": "stdio",
             "command": "npx",
             "args": ["@drupaltools/mcp@${{ steps.version.outputs.version }}"]
           }
         }
       }
       ```
Suggestion importance[1-10]: 6

__

Why: The suggestion correctly identifies the use of an outdated actions/create-release@v1 and recommends updating to a supported version, which is important for security and long-term maintainability.

Low
Remove redundant package version update

Remove the redundant npm version command that runs in the mcp-package/
directory, as only the version in the dist/ directory is used for publishing.

.github/workflows/publish-npm.yml [59-64]

 - name: Update package version
   run: |
-    cd mcp-package
-    npm version ${{ steps.version.outputs.version }} --no-git-tag-version
-    cd dist
+    cd mcp-package/dist
     npm version ${{ steps.version.outputs.version }} --no-git-tag-version
Suggestion importance[1-10]: 5

__

Why: The suggestion correctly points out a redundant npm version command, and removing it simplifies the workflow and eliminates a potentially confusing and useless step.

Low

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

- Document required GitHub secrets (NPM_TOKEN)
- Clarify npm package name: @drupaltools/mcp
- Add step-by-step publishing instructions
- Include first-time publishing guide
- Add verification steps after publishing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants