From 530f0d68898c4e31cdfd3b568bfa2179589d3d7e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 22 Apr 2026 14:23:12 +0100 Subject: [PATCH] chore(template): sync from mcp-ecosystem --- .github/dependabot.yml | 39 +++++++++++ .github/workflows/ci.yml | 34 ++++++++++ .github/workflows/dependabot-auto-merge.yml | 39 +++++++++++ .github/workflows/pr-title.yml | 35 ++++++++++ .github/workflows/release-please.yml | 73 +++++++++++++++++++++ .github/workflows/security.yml | 53 +++++++++++++++ .prettierrc | 7 ++ eslint.config.mjs | 26 ++++++++ package.json | 12 ++-- vitest.config.ts | 13 ++++ 10 files changed, 325 insertions(+), 6 deletions(-) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/dependabot-auto-merge.yml create mode 100644 .github/workflows/pr-title.yml create mode 100644 .github/workflows/release-please.yml create mode 100644 .github/workflows/security.yml create mode 100644 .prettierrc create mode 100644 eslint.config.mjs create mode 100644 vitest.config.ts diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..be20316 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,39 @@ +version: 2 +updates: + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "weekly" + groups: + runtime-dependencies: + dependency-type: "production" + toolchain: + dependency-type: "development" + patterns: + - "@eslint/js" + - "@types/*" + - "@vitest/coverage-v8" + - "eslint" + - "eslint-config-prettier" + - "prettier" + - "tsx" + - "typescript" + - "typescript-eslint" + - "vitest" + ignore: + # Major bumps for coupled toolchain packages need manual coordination + - dependency-name: "typescript" + update-types: ["version-update:semver-major"] + - dependency-name: "typescript-eslint" + update-types: ["version-update:semver-major"] + - dependency-name: "eslint" + update-types: ["version-update:semver-major"] + - dependency-name: "@eslint/*" + update-types: ["version-update:semver-major"] + - dependency-name: "vitest" + update-types: ["version-update:semver-major"] + - dependency-name: "@vitest/*" + update-types: ["version-update:semver-major"] + commit-message: + prefix: "chore(deps)" + open-pull-requests-limit: 10 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..8259e18 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,34 @@ +name: CI + +on: + pull_request: + branches: [main] + push: + branches: [main] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: "20" + cache: "npm" + + - name: Install dependencies + run: npm ci + + - name: Lint + run: npm run lint + + - name: Build + run: npm run build + + - name: Test + run: npm test + + - name: Test coverage + run: npm run test:coverage + continue-on-error: true diff --git a/.github/workflows/dependabot-auto-merge.yml b/.github/workflows/dependabot-auto-merge.yml new file mode 100644 index 0000000..8d205b8 --- /dev/null +++ b/.github/workflows/dependabot-auto-merge.yml @@ -0,0 +1,39 @@ +name: Dependabot auto-merge + +on: + pull_request_target: + types: + - opened + - reopened + - synchronize + - ready_for_review + +permissions: + contents: write + pull-requests: write + +jobs: + auto-merge: + if: github.actor == 'dependabot[bot]' + runs-on: ubuntu-latest + steps: + - name: Fetch Dependabot metadata + id: meta + uses: dependabot/fetch-metadata@v2 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Approve and enable auto-merge + if: | + steps.meta.outputs.update-type != 'version-update:semver-major' || + steps.meta.outputs.package-ecosystem == 'github_actions' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_URL: ${{ github.event.pull_request.html_url }} + run: | + CURRENT_LOGIN="$(gh api user --jq .login)" + ALREADY_APPROVED="$(gh api "repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/reviews" --jq '[.[] | select(.user.login == "'"$CURRENT_LOGIN"'" and .state == "APPROVED")] | length')" + if [ "$ALREADY_APPROVED" -eq 0 ]; then + gh pr review --approve "$PR_URL" + fi + gh pr merge --auto --squash "$PR_URL" diff --git a/.github/workflows/pr-title.yml b/.github/workflows/pr-title.yml new file mode 100644 index 0000000..93e1c34 --- /dev/null +++ b/.github/workflows/pr-title.yml @@ -0,0 +1,35 @@ +name: PR Title + +on: + pull_request_target: + types: + - opened + - edited + - reopened + - synchronize + +permissions: + pull-requests: read + +jobs: + conventional-pr-title: + if: ${{ github.event.pull_request.base.ref == 'main' }} + runs-on: ubuntu-latest + steps: + - name: Validate conventional PR title + env: + PR_TITLE: ${{ github.event.pull_request.title }} + run: | + pattern='^(feat|fix|docs|chore|refactor|test|perf|build|ci|revert)(\([[:alnum:]_.-]+\))?!?: .+' + + if [[ "$PR_TITLE" =~ $pattern ]]; then + echo "PR title is valid: $PR_TITLE" + exit 0 + fi + + echo "PR title must use Conventional Commits because squash merges become release-please input." + echo "Allowed prefixes: feat, fix, docs, chore, refactor, test, perf, build, ci, revert" + echo "Examples:" + echo " fix: harden push approval auth flow" + echo " feat(auth): add TOTP support" + exit 1 diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml new file mode 100644 index 0000000..8046b9c --- /dev/null +++ b/.github/workflows/release-please.yml @@ -0,0 +1,73 @@ +# Release Please - Automated versioning and npm publishing +# +# How it works: +# 1. Every push to main with conventional commits updates a "Release PR" +# 2. When you merge the Release PR, it: +# - Updates version in package.json +# - Updates CHANGELOG.md +# - Creates a GitHub Release with tag +# - Triggers the npm publish job below +# +# Commit message format: +# feat: add new feature → minor version bump +# fix: fix a bug → patch version bump +# feat!: breaking change → major version bump +# chore/docs: no version bump +# +# npm Publishing: +# Uses Trusted Publishing (OIDC) - no NPM_TOKEN needed! +# Requires Node 24+ for npm 11.5.1+ OIDC support. +# Configure at: https://www.npmjs.com/package/@verygoodplugins/mcp-{name}/access +# +# RELEASE_PLEASE_TOKEN: +# Uses org-level PAT so the Release PR triggers CI workflows. +# PRs created by GITHUB_TOKEN don't trigger other workflows (GitHub security). + +name: Release Please + +on: + push: + branches: + - main + +permissions: + contents: write + pull-requests: write + +jobs: + release-please: + runs-on: ubuntu-latest + outputs: + release_created: ${{ steps.release.outputs.release_created }} + tag_name: ${{ steps.release.outputs.tag_name }} + steps: + - uses: googleapis/release-please-action@v4 + id: release + with: + manifest-file: ".release-please-manifest.json" + config-file: "release-please-config.json" + token: ${{ secrets.RELEASE_PLEASE_TOKEN }} + + npm-publish: + needs: release-please + if: ${{ needs.release-please.outputs.release_created }} + runs-on: ubuntu-latest + environment: npm + permissions: + contents: read + id-token: write + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: "24" + registry-url: "https://registry.npmjs.org" + + # Use npm install for cross-version lockfile compatibility + - run: npm install + - run: npm run build + - run: npm test + + # Trusted Publishing: No NPM_TOKEN needed! + - run: npm publish --provenance --access public diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml new file mode 100644 index 0000000..6b12e14 --- /dev/null +++ b/.github/workflows/security.yml @@ -0,0 +1,53 @@ +name: Security + +on: + schedule: + - cron: "0 0 * * 0" # Weekly on Sunday + push: + branches: [main] + pull_request: + branches: [main] + workflow_dispatch: + +permissions: + contents: read + security-events: write + +jobs: + codeql: + name: CodeQL Analysis + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v4 + with: + languages: typescript + + - name: Autobuild + uses: github/codeql-action/autobuild@v4 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v4 + + audit: + name: Dependency Audit + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: "20" + cache: "npm" + + - run: npm ci + + - name: Audit dependencies + run: npm audit --audit-level=high + continue-on-error: true + + - name: Check for known vulnerabilities + run: npx better-npm-audit audit --level high + continue-on-error: true diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..1f4c4bb --- /dev/null +++ b/.prettierrc @@ -0,0 +1,7 @@ +{ + "semi": true, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "es5", + "printWidth": 100 +} diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..b1897b8 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,26 @@ +// ESLint flat config for VGP MCP servers +// Requires: eslint ^9.0.0, typescript-eslint ^8.0.0 +import eslint from '@eslint/js'; +import tseslint from 'typescript-eslint'; + +export default tseslint.config( + eslint.configs.recommended, + ...tseslint.configs.recommended, + { + files: ['src/**/*.ts'], + rules: { + // MCP stdio servers must not write to stdout outside the protocol. + 'no-console': ['error', { allow: ['error', 'warn'] }], + // Allow unused vars prefixed with underscore + '@typescript-eslint/no-unused-vars': [ + 'error', + { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }, + ], + // Allow explicit any in some cases (MCP tools often need flexibility) + '@typescript-eslint/no-explicit-any': 'warn', + }, + }, + { + ignores: ['dist/', 'node_modules/', '**/*.js', '**/*.mjs', '**/*.cjs'], + } +); diff --git a/package.json b/package.json index 8661ed9..99852bc 100644 --- a/package.json +++ b/package.json @@ -29,20 +29,20 @@ "author": "Claude", "license": "GPL-3.0-or-later", "dependencies": { - "@modelcontextprotocol/sdk": "^1.4.1", + "@modelcontextprotocol/sdk": "^1.25.1", "axios": "^1.6.7", - "dotenv": "^16.4.5", + "dotenv": "^17.2.3", "fs-extra": "^11.2.0", "marked": "^17.0.0", - "zod": "^3.23.8", + "zod": "^3.24.1", "zod-to-json-schema": "^3.24.1" }, "devDependencies": { "@types/fs-extra": "^11.0.4", - "@types/node": "^22.10.0", + "@types/node": "^22.10.5", "rimraf": "^5.0.5", - "tsx": "^4.7.1", - "typescript": "^5.3.3" + "tsx": "^4.19.2", + "typescript": "^5.7.3" }, "publishConfig": { "access": "public" diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000..82bfd07 --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,13 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + environment: 'node', + coverage: { + provider: 'v8', + reporter: ['text', 'json', 'html'], + exclude: ['node_modules/', 'dist/', 'tests/', '*.config.*'], + }, + }, +});