From 61cc56ec6eec1c8ce6edaec6ac147a3acd8e4d11 Mon Sep 17 00:00:00 2001 From: CD Cabrera Date: Mon, 27 Apr 2026 09:56:19 -0400 Subject: [PATCH 1/2] build(node.js)!: pf-3843 remove 20 support * audit, move dep audit, publishing baselines moved to node.js 24 * build, engine, moved to node.js 22 minimum * cli, node.js version copy, pin server version to support older node.js * docs, references moved to node.js 22 * package, min engine version, cleaned test scripts * src, drop node.js 20 reference * e2e, cleaned node.js 20 checks, left itSkip, envNodeVersion BREAKING CHANGE: Minimum supported Node.js is now 22, previously 20. MCP clients and integrations running Node.js 20 will fail to start on `@latest`. To migrate: - Either upgrade Node.js to a minimum of 22 LTS. - Or pin the server configuration to a Node.js 20-compatible release See usage docs for pinned configuration examples. --- .github/workflows/audit.yml | 26 ++++++++++-- .github/workflows/integration.yml | 19 +++------ .github/workflows/publishing.yml | 8 +--- README.md | 4 +- docs/development.md | 7 ++-- docs/usage.md | 37 +++++++++++++++-- guidelines/agent_behaviors.md | 2 +- jest.config.ts | 6 +-- package-lock.json | 2 +- package.json | 8 ++-- .../options.defaults.test.ts.snap | 2 +- .../__snapshots__/server.tools.test.ts.snap | 16 ++++---- src/__tests__/server.tools.test.ts | 12 +++--- src/cli.ts | 2 +- src/options.defaults.ts | 4 +- tests/e2e/stdioTransport.test.ts | 40 +------------------ 16 files changed, 98 insertions(+), 97 deletions(-) diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml index 4da0ea41..5a8a73e3 100644 --- a/.github/workflows/audit.yml +++ b/.github/workflows/audit.yml @@ -1,16 +1,19 @@ -name: Data Audit +name: Security & Data Audit on: pull_request: paths: - 'src/docs.json' - 'tests/audit/**' + - 'package.json' + - 'package-lock.json' schedule: - cron: '0 0 * * *' # Daily at midnight workflow_dispatch: jobs: - audit-links: + documentation-audit: + name: Documentation Audit runs-on: ubuntu-latest permissions: contents: read @@ -19,7 +22,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v6.3.0 with: - node-version: 22.x + node-version: '24' cache: npm - name: Install dependencies run: npm ci @@ -30,3 +33,20 @@ jobs: DOCS_AUDIT_MAX_TOTAL: ${{ github.event_name == 'schedule' && '0' || '50' }} # Advisories are non-blocking for PRs continue-on-error: ${{ github.event_name == 'pull_request' }} + + dependency-audit: + name: Dependency Audit + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@v6 + - name: Setup Node.js + uses: actions/setup-node@v6.3.0 + with: + node-version: '24' + cache: npm + - name: Run npm audit + run: npm audit --omit=dev --audit-level=critical + # Advisories are non-blocking for PRs + continue-on-error: ${{ github.event_name == 'pull_request' }} diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 93d1dfc7..0a52b8d9 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -13,30 +13,21 @@ jobs: contents: read strategy: matrix: - node-version: [20.x, 22.x, 24.x] + node-version: [22.x, 24.x] steps: - uses: actions/checkout@v6 + - name: Setup Node.js ${{ matrix.node-version }} uses: actions/setup-node@v6.3.0 with: node-version: ${{ matrix.node-version }} cache: npm - - name: Node.js modules cache - uses: actions/cache@v5 - id: modules-cache - with: - path: ${{ github.workspace }}/node_modules - key: ${{ runner.os }}-${{ matrix.node-version }}-modules-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - ${{ runner.os }}-${{ matrix.node-version }}-modules + - name: Install Node.js packages - if: ${{ steps.modules-cache.outputs.cache-hit != 'true' }} run: npm ci - - name: Audit packages - run: npm audit --audit-level=high - continue-on-error: true + - name: Lint and test run: npm run test:ci + - name: Confirm integration - if: ${{ success() }} run: npm run test:integration diff --git a/.github/workflows/publishing.yml b/.github/workflows/publishing.yml index 34ed9da4..f9a0671f 100644 --- a/.github/workflows/publishing.yml +++ b/.github/workflows/publishing.yml @@ -10,16 +10,12 @@ jobs: permissions: contents: read id-token: write - strategy: - matrix: - node: [22.x] steps: - uses: actions/checkout@v6 - - name: Setup Node.js ${{ matrix.node-version }} + - name: Setup Node.js uses: actions/setup-node@v6.3.0 with: - node-version: ${{ matrix.node-version }} - cache: npm + node-version: '24' registry-url: 'https://registry.npmjs.org' - name: Install dependencies run: npm ci diff --git a/README.md b/README.md index 0a7aeeba..a9831aab 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ It is intended to be extensible to meet the needs of different teams and project [Read more about our roadmap and how we've structured the server in our architecture docs](./docs/architecture.md). ## Requirements -- [Node.js 20+](https://nodejs.org/) +- [Node.js 22+](https://nodejs.org/) - NPM (or equivalent package manager) ## Quick start @@ -18,6 +18,8 @@ The PatternFly MCP Server supports multiple configurations; see the [usage docum #### Set a basic MCP configuration +> Unable to update to the required Node.js version? [See pinned MCP configuration examples for earlier Node.js versions.](./docs/usage.md#pinned-mcp-package-version) + Minimal configuration ```json { diff --git a/docs/development.md b/docs/development.md index 9ff85916..768d990c 100644 --- a/docs/development.md +++ b/docs/development.md @@ -158,8 +158,7 @@ The documentation catalog `src/docs.json` pins remote resources to specific comm #### Programmatic runtime requirements -- **Node.js 20+**: Required to run the core MCP server. -- **Node.js 22+**: Required for loading external tool plugins (`--tool`) and for developers working on advanced process isolation features. +- **Node.js 22+**: Required to run the MCP server, load external tool plugins (`--tool`), and use advanced process isolation features. **Example: Programmatic test mode** ```typescript @@ -411,7 +410,7 @@ These terms describe **how tools and their related properties are represented** ### Tool plugins -- **Plugins don't appear**: Verify the Node version (requires Node.js >= 20; >= 22 for tool plugins) and check logs (enable `--log-stderr`). +- **Plugins don't appear**: Verify the Node version (requires Node.js >= 22 for tool plugins) and check logs (enable `--log-stderr`). - **Startup warnings/errors**: Startup `load:ack` warnings/errors from tool plugins are logged when stderr/protocol logging is enabled. - **Schema errors**: If `tools/call` rejects with schema errors, ensure `inputSchema` is valid. See [Authoring tools](#authoring-tools) for details. - **Network access issues**: If the tool is having network access issues, you may need to configure `--plugin-isolation none`. This is generally discouraged for security reasons but may be necessary in some cases. @@ -424,7 +423,7 @@ These terms describe **how tools and their related properties are represented** ### General issues -- **Server won't start**: Check Node.js version (requires Node.js >= 20; >= 22 for tool plugins). +- **Server won't start**: Check Node.js version (requires Node.js >= 22 for tool plugins). - **Missing tools/resources**: Verify the server started successfully and check logs with `--log-stderr`. - **Type errors**: Ensure TypeScript types are installed: `npm install --save-dev @types/node` diff --git a/docs/usage.md b/docs/usage.md index ce8d919a..b4123334 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -97,6 +97,33 @@ Access specific component documentation or technical specifications using the fo Most MCP clients use JSON configuration to specify how the server is started. Below are examples you can adapt for your client. +### Pinned MCP package version + +Depending on your environment, you may have to delay updating to the minimum Node.js version required by the server. If you are unable to upgrade your Node.js version and must remain on a previous Node.js version, you can pin your MCP configuration to the last compatible version of the server. + +> **Note**: Currently, pinning to an older PatternFly MCP version means you will not receive updated documentation or new features until you "update" your pinned version. In the future, pinning a version may still make an allowance for documentation updates. [See our planned architecture.](./architecture.md#hybrid-documentation-in-progress) + +#### When to choose `@latest` or a pinned version for configuration + +| Node.js version | Package spec | Feature notes | +|-----------------|-------------------------------------|------------------------------------------------------------------------------------------------------------------------| +| **>=22** | `@patternfly/patternfly-mcp@latest` | Newest PatternFly features and rules. Includes **enhanced security isolation** for custom tool plugins. | +| **>=20** | `@patternfly/patternfly-mcp@1.1.0` | Standard features and rules. Lacks **advanced tool isolation**; compatible with all default PatternFly configurations. | + +##### Node.js 20 compatible pin + +```json +{ + "mcpServers": { + "patternfly-mcp": { + "command": "npx", + "args": ["-y", "@patternfly/patternfly-mcp@1.1.0"], + "description": "PatternFly rules and documentation (Node.js 20 compatible)" + } + } +} +``` + ### Minimal client config (stdio) ```json @@ -182,14 +209,16 @@ These are **first-step checks** for common setup problems, not full diagnostics. > **Agents**: PatternFly MCP server information is available internally through the `patternfly://context` MCP resource. -### 1. Verify Node.js Version -The PatternFly MCP server requires **Node.js 20 or higher**. +### 1. Verify Node.js version +The PatternFly MCP server requires **Node.js 22 or higher**. - **How to check**: - **macOS/Linux**: Open **Terminal** and type `node -v`. - **Windows**: Open **PowerShell** or **Command Prompt** and type `node -v`. -- **Requirement**: You should see a version starting with `v20`, `v22`, or higher. -- **Solution**: If your version is lower than 20, please download and install the latest "LTS" (Long Term Support) version from [nodejs.org](https://nodejs.org/). +- **Requirement**: You should see a version starting with `v22`, or higher. +- **Solution**: If your version is lower than 22, please download and install the latest "LTS" (Long Term Support) version from [nodejs.org](https://nodejs.org/). + +> **Unable to update your Node.js version?** [See pinned configuration examples for earlier Node.js versions.](#pinned-mcp-package-version) ### 2. Reset the npx Cache If you encounter an `ERR_MODULE_NOT_FOUND` error or don't see the latest features, your system may be using a "stale" or corrupted version in its cache. diff --git a/guidelines/agent_behaviors.md b/guidelines/agent_behaviors.md index 04a4da24..b609ddf7 100644 --- a/guidelines/agent_behaviors.md +++ b/guidelines/agent_behaviors.md @@ -45,7 +45,7 @@ For a detailed overview of the system design and roadmap, see [docs/architecture - **Validation Required**: Follow checklists; verify requirements; test thoroughly. Review [pull request warning signs](../CONTRIBUTING.md#pull-requests) to avoid common pitfalls. - **Confirmation Required**: Confirm success; summarize changes; explain impact; verify understanding. - **Guidance Review Scope**: Unless the user explicitly asks, do not make recommendations on improving guidance if all you're asked to do is review guidance. -- **Environment Awareness**: +- **Environment Awareness**: - Server and plugin execution requirements are defined in `package.json`. - Always verify environment compatibility by checking `patternfly://context` or `package.json`. - Proactively check for environment mismatches (e.g., Node.js version) if tools fail to load. diff --git a/jest.config.ts b/jest.config.ts index 8c02c24a..b97ead1d 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -36,7 +36,7 @@ export default { projects: [ { displayName: 'unit', - roots: ['src'], + roots: ['/src'], testMatch: ['/src/**/*.test.ts'], setupFilesAfterEnv: ['/jest.setupTests.ts'], ...baseConfig, @@ -64,7 +64,7 @@ export default { }, { displayName: 'e2e', - roots: ['tests/e2e'], + roots: ['/tests/e2e'], testMatch: ['/tests/e2e/**/*.test.ts'], setupFilesAfterEnv: ['/tests/e2e/jest.setupTests.ts'], transformIgnorePatterns: [ @@ -74,7 +74,7 @@ export default { }, { displayName: 'audit', - roots: ['tests/audit'], + roots: ['/tests/audit'], testMatch: ['/tests/audit/**/*.test.ts'], ...baseConfig } diff --git a/package-lock.json b/package-lock.json index f83e2f0d..55ca5e84 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41,7 +41,7 @@ "typescript-eslint": "^8.59.1" }, "engines": { - "node": ">=20.0.0" + "node": ">=22.0.0" } }, "node_modules/@babel/code-frame": { diff --git a/package.json b/package.json index c42e10f3..49ecfae3 100644 --- a/package.json +++ b/package.json @@ -31,11 +31,11 @@ "release": "changelog --non-cc --link-url https://github.com/patternfly/patternfly-mcp.git", "start": "node dist/cli.js --log-stderr", "start:dev": "tsx watch src/cli.ts --verbose --log-stderr", - "test": "npm run test:spell && npm run test:spell-docs && npm run test:lint && npm run test:types && jest --selectProjects unit --roots=src/", - "test:audit": "jest --selectProjects audit --roots=tests/audit/", + "test": "npm run test:spell && npm run test:spell-docs && npm run test:lint && npm run test:types && jest --selectProjects unit", + "test:audit": "jest --selectProjects audit", "test:ci": "npm test -- --coverage", "test:dev": "npm test -- --watchAll", - "test:integration": "npm run build && NODE_OPTIONS='--experimental-vm-modules' jest --selectProjects e2e --roots=tests/e2e/", + "test:integration": "npm run build && NODE_OPTIONS='--experimental-vm-modules' jest --selectProjects e2e", "test:integration-dev": "npm run test:integration -- --watchAll", "test:lint": "eslint .", "test:lint-fix": "eslint . --fix", @@ -84,7 +84,7 @@ "typescript-eslint": "^8.59.1" }, "engines": { - "node": ">=20.0.0" + "node": ">=22.0.0" }, "repository": { "type": "git", diff --git a/src/__tests__/__snapshots__/options.defaults.test.ts.snap b/src/__tests__/__snapshots__/options.defaults.test.ts.snap index 3bb1ee61..24cb856b 100644 --- a/src/__tests__/__snapshots__/options.defaults.test.ts.snap +++ b/src/__tests__/__snapshots__/options.defaults.test.ts.snap @@ -46,7 +46,7 @@ exports[`options defaults should return specific properties: defaults 1`] = ` "test": {}, }, "name": "@patternfly/patternfly-mcp", - "nodeEngine": ">=20.0.0", + "nodeEngine": ">=22.0.0", "nodeVersion": 22, "nodeVersionPreferred": 22, "patternflyOptions": { diff --git a/src/__tests__/__snapshots__/server.tools.test.ts.snap b/src/__tests__/__snapshots__/server.tools.test.ts.snap index cab35b52..0fe70b85 100644 --- a/src/__tests__/__snapshots__/server.tools.test.ts.snap +++ b/src/__tests__/__snapshots__/server.tools.test.ts.snap @@ -14,17 +14,14 @@ exports[`composeTools should attempt to setup creators, file package creators 1` } `; -exports[`composeTools should attempt to setup creators, file package creators, Node.js 20 1`] = ` +exports[`composeTools should attempt to setup creators, file package creators, Node.js 22 1`] = ` { "log": [ [ "Existing Tools Host session detected test-session-id. Shutting down the existing host before creating a new one.", ], - [ - "External tool plugins require Node >= 22; skipping file-based tools.", - ], ], - "toolsCount": 3, + "toolsCount": 5, } `; @@ -112,17 +109,20 @@ exports[`composeTools should attempt to setup creators, inline and file package } `; -exports[`composeTools should attempt to setup creators, inline and file package creators, duplicates, Node.js 20 1`] = ` +exports[`composeTools should attempt to setup creators, inline and file package creators, duplicates, Node.js 22 1`] = ` { "log": [ [ "Existing Tools Host session detected test-session-id. Shutting down the existing host before creating a new one.", ], [ - "External tool plugins require Node >= 22; skipping file-based tools.", + "Skipping tool plugin "@patternfly/tools" – name already used by built-in/inline tool.", + ], + [ + "Skipping tool plugin "DOLOR " – name already used by built-in/inline tool.", ], ], - "toolsCount": 5, + "toolsCount": 6, } `; diff --git a/src/__tests__/server.tools.test.ts b/src/__tests__/server.tools.test.ts index b2398c7c..cd5c1ad8 100644 --- a/src/__tests__/server.tools.test.ts +++ b/src/__tests__/server.tools.test.ts @@ -700,10 +700,10 @@ describe('composeTools', () => { expectedModuleCount: 5 }, { - description: 'file package creators, Node.js 20', - nodeVersion: 20, + description: 'file package creators, Node.js 22', + nodeVersion: 22, modules: ['file:///test/module.js', '@patternfly/tools'], - expectedModuleCount: 3 + expectedModuleCount: 5 }, { description: 'file package creators, Node.js 24', @@ -756,8 +756,8 @@ describe('composeTools', () => { expectedModuleCount: 6 }, { - description: 'inline and file package creators, duplicates, Node.js 20', - nodeVersion: 20, + description: 'inline and file package creators, duplicates, Node.js 22', + nodeVersion: 22, modules: [ { name: '@patternfly/tools', description: 'lorem ipsum', inputSchema: {}, handler: () => {} }, { name: 'dolor', description: 'sit amet', inputSchema: z.object({}), handler: () => {} }, @@ -765,7 +765,7 @@ describe('composeTools', () => { '@patternfly/tools', 'DOLOR ' ], - expectedModuleCount: 5 + expectedModuleCount: 6 } ])('should attempt to setup creators, $description', async ({ modules, nodeVersion, expectedModuleCount }) => { const mockChild = { diff --git a/src/cli.ts b/src/cli.ts index e7a963c0..d5bfd77e 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -52,7 +52,7 @@ const run = async (): Promise => { } else { // Everything else error = new Error( - `Node.js version ${envNodeMajorVersion} found but ${appMinNodeMajorVersion} or higher is required. Update Node.js and try again.` + `Node.js version ${envNodeMajorVersion} found but ${appMinNodeMajorVersion} or higher is required. Update Node.js or pin to an earlier PatternFly MCP version for compatibility and try again.` ); } diff --git a/src/options.defaults.ts b/src/options.defaults.ts index cad74b60..e6f84186 100644 --- a/src/options.defaults.ts +++ b/src/options.defaults.ts @@ -352,8 +352,8 @@ const MODE_OPTIONS: ModeOptions = { * The application's preferred Node.js major. Typically used for * unit testing. * - * @note Currently hardcoded, but once Node.js 20 support is removed - * we could consider populating this from package.json engine. + * @note Currently hardcoded due to potential differences in Node.js + * versions. Review populating this from the `package.json` engine. */ const NODE_VERSION_PREFERRED = 22; diff --git a/tests/e2e/stdioTransport.test.ts b/tests/e2e/stdioTransport.test.ts index 8907206c..52fcf856 100644 --- a/tests/e2e/stdioTransport.test.ts +++ b/tests/e2e/stdioTransport.test.ts @@ -443,7 +443,7 @@ describe('Tools', () => { afterEach(async () => CLIENT.stop()); - itSkip(envNodeVersion >= 22)('should access new tools', async () => { + it('should access new tools', async () => { const req = { method: 'tools/list', params: {} @@ -461,18 +461,7 @@ describe('Tools', () => { expect(names).toContain('echo_createMcp_tool'); }); - itSkip(envNodeVersion <= 20)('should fail to access a new tool', async () => { - const req = { - method: 'tools/list', - params: {} - }; - - await CLIENT.send(req); - - expect(CLIENT.logs().join(',')).toContain('External tool plugins require Node >= 22; skipping file-based tools.'); - }); - - itSkip(envNodeVersion >= 22).each([ + it.each([ { description: 'echo basic tool', name: 'echo_basic_tool', @@ -497,29 +486,4 @@ describe('Tools', () => { expect(resp.result).toMatchSnapshot(); expect(resp.result.isError).toBeUndefined(); }); - - itSkip(envNodeVersion <= 20).each([ - { - description: 'echo basic tool', - name: 'echo_basic_tool', - args: { type: 'echo', lorem: 'ipsum', dolor: 'sit amet' } - }, - { - description: 'echo create MCP tool', - name: 'echo_createMcp_tool', - args: { type: 'echo', lorem: 'ipsum', dolor: 'sit amet' } - } - ])('should fail to interact with a tool, $description', async ({ name, args }) => { - const req = { - method: 'tools/call', - params: { - name, - arguments: args - } - }; - - const resp: any = await CLIENT.send(req); - - expect(resp.result.isError).toBe(true); - }); }); From 65ed961807d3500e4aaa0275e249c3ceb10cc79f Mon Sep 17 00:00:00 2001 From: CD Cabrera Date: Thu, 21 May 2026 15:18:53 -0400 Subject: [PATCH 2/2] fix: review update --- docs/development.md | 2 +- docs/usage.md | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/development.md b/docs/development.md index 768d990c..865236ed 100644 --- a/docs/development.md +++ b/docs/development.md @@ -423,7 +423,7 @@ These terms describe **how tools and their related properties are represented** ### General issues -- **Server won't start**: Check Node.js version (requires Node.js >= 22 for tool plugins). +- **Server won't start**: Check Node.js version (requires Node.js >= 22 to run the server). - **Missing tools/resources**: Verify the server started successfully and check logs with `--log-stderr`. - **Type errors**: Ensure TypeScript types are installed: `npm install --save-dev @types/node` diff --git a/docs/usage.md b/docs/usage.md index b4123334..b3fdaa86 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -105,10 +105,10 @@ Depending on your environment, you may have to delay updating to the minimum Nod #### When to choose `@latest` or a pinned version for configuration -| Node.js version | Package spec | Feature notes | -|-----------------|-------------------------------------|------------------------------------------------------------------------------------------------------------------------| -| **>=22** | `@patternfly/patternfly-mcp@latest` | Newest PatternFly features and rules. Includes **enhanced security isolation** for custom tool plugins. | -| **>=20** | `@patternfly/patternfly-mcp@1.1.0` | Standard features and rules. Lacks **advanced tool isolation**; compatible with all default PatternFly configurations. | +| Node.js version | Package spec | Feature notes | +|-----------------|-------------------------------------|--------------------------------------------------------------------------------------------------------------------| +| **>=22** | `@patternfly/patternfly-mcp@latest` | Newest PatternFly features and rules. Includes **enhanced security isolation** for custom tool plugins. | +| **>=20** | `@patternfly/patternfly-mcp@1.1.0` | Standard features and rules. Lacks **custom tool plugins**; compatible with all default PatternFly configurations. | ##### Node.js 20 compatible pin