diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml index 7be3dc8..40997f3 100644 --- a/.github/workflows/pr-checks.yml +++ b/.github/workflows/pr-checks.yml @@ -24,10 +24,9 @@ jobs: - name: Checkout uses: actions/checkout@v7 + # Version comes from package.json "packageManager" (do not set "version" here). - name: Setup pnpm uses: pnpm/action-setup@v6 - with: - version: 10 - name: Setup Node.js uses: actions/setup-node@v7 @@ -53,8 +52,6 @@ jobs: - name: Setup pnpm uses: pnpm/action-setup@v6 - with: - version: 10 - name: Setup Node.js uses: actions/setup-node@v7 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..490dde8 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,132 @@ +# Publish @fqueis/openapi-contract to npm when a new version lands on main. +# +# Auth: npm Trusted Publishing (OIDC). Requires id-token: write, +# Node >= 22.14, npm CLI >= 11.5.1, and a Trusted Publisher on npmjs.com pointing +# at this workflow file (publish.yml) for repo fqueis/openapi-contract. +# +# Gate: skip if package.json version is already on the registry. +# Otherwise: lint + test + build, create GitHub Release (notes since previous tag), +# then pnpm publish. workflow_dispatch retries a failed publish without a new bump. +# +# Bootstrap: the first package version must exist on npm (local publish + 2FA) before +# Trusted Publisher can be configured; after that, CI owns subsequent publishes. +name: Publish + +on: + push: + branches: [main] + workflow_dispatch: + +permissions: + contents: write + id-token: write + +concurrency: + group: publish-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false + +jobs: + publish: + name: publish + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v7 + with: + fetch-depth: 0 + + - name: Setup pnpm + uses: pnpm/action-setup@v6 + + - name: Setup Node.js + uses: actions/setup-node@v7 + with: + node-version: '22.14' + cache: pnpm + registry-url: https://registry.npmjs.org + + - name: Ensure npm CLI supports trusted publishing + run: npm install -g npm@latest + + - name: Install dependencies + run: pnpm install --ignore-scripts --frozen-lockfile + + - name: Resolve version and check npm + id: version + run: | + VERSION="$(node -p "JSON.parse(require('fs').readFileSync('package.json','utf8')).version")" + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + echo "Package version: $VERSION" + + if npm view "@fqueis/openapi-contract@$VERSION" version >/dev/null 2>&1; then + echo "already_published=true" >> "$GITHUB_OUTPUT" + echo "Version $VERSION is already on npm — skipping publish." + else + echo "already_published=false" >> "$GITHUB_OUTPUT" + echo "Version $VERSION is not on npm — will publish." + fi + + - name: Lint + if: steps.version.outputs.already_published == 'false' + run: pnpm lint + + - name: Test + if: steps.version.outputs.already_published == 'false' + run: pnpm test + + - name: Build + if: steps.version.outputs.already_published == 'false' + run: pnpm build + + - name: Create GitHub Release + if: steps.version.outputs.already_published == 'false' + env: + GH_TOKEN: ${{ github.token }} + VERSION: ${{ steps.version.outputs.version }} + run: | + set -euo pipefail + TAG="v${VERSION}" + + if gh release view "$TAG" >/dev/null 2>&1; then + echo "Release $TAG already exists — skipping create." + exit 0 + fi + + PREV_TAG="$(git describe --tags --abbrev=0 2>/dev/null || true)" + NOTES_FILE="$(mktemp)" + + if [ -z "$PREV_TAG" ]; then + { + echo "## ${TAG}" + echo + echo "Initial release of \`@fqueis/openapi-contract\`." + } > "$NOTES_FILE" + else + { + echo "## ${TAG}" + echo + echo "Changes since ${PREV_TAG}:" + echo + git log "${PREV_TAG}..HEAD" --pretty=format:"- %s (%h)" --no-merges + echo + } > "$NOTES_FILE" + + if ! grep -q '^- ' "$NOTES_FILE"; then + { + echo "## ${TAG}" + echo + echo "Changes since ${PREV_TAG}:" + echo + echo "- No non-merge commits since ${PREV_TAG}." + } > "$NOTES_FILE" + fi + fi + + gh release create "$TAG" \ + --title "$TAG" \ + --notes-file "$NOTES_FILE" \ + --target "${{ github.sha }}" + + - name: Publish to npm + if: steps.version.outputs.already_published == 'false' + run: pnpm publish --access public --no-git-checks diff --git a/README.md b/README.md index bb34ee6..3d34095 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ MCP server that reads **OpenAPI contracts** from local (or remote) backends so agents can build frontends and mobile apps against the real API shape. This release is **read-only**: it inspects the OpenAPI document and never executes HTTP calls against your API. Backends are registered on demand; there is no env list of backends. +[![npm](https://img.shields.io/npm/v/@fqueis/openapi-contract.svg)](https://www.npmjs.com/package/@fqueis/openapi-contract) [![PR Checks](https://github.com/fqueis/openapi-contract/actions/workflows/pr-checks.yml/badge.svg)](https://github.com/fqueis/openapi-contract/actions/workflows/pr-checks.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE.md) @@ -23,12 +24,34 @@ Built with [`@modelcontextprotocol/sdk`](https://www.npmjs.com/package/@modelcon ## Requirements - Node.js >= 18 -- [pnpm](https://pnpm.io/) - A backend that exposes OpenAPI (Nest Swagger: `/docs-json`, etc.) +- [pnpm](https://pnpm.io/) — only if you develop or contribute to this repo --- -## Setup +## Usage in Cursor (`mcp.json`) + +Add to your Cursor MCP config (`~/.cursor/mcp.json` or Cursor Settings → MCP). + +**Recommended (npm):** + +```json +{ + "mcpServers": { + "openapi-contract": { + "type": "stdio", + "command": "npx", + "args": ["-y", "@fqueis/openapi-contract"] + } + } +} +``` + +You can run the server from a local clone for development, but for normal use prefer the published package above. + +--- + +## Local development / contributors ```bash cd /path/to/openapi-contract @@ -37,13 +60,7 @@ pnpm build pnpm test ``` ---- - -## Usage in Cursor (`mcp.json`) - -Add to your Cursor MCP config (`~/.cursor/mcp.json` or Cursor Settings → MCP). - -**Recommended (built JS):** +**Built JS:** ```json { diff --git a/package.json b/package.json index e1d0d3e..60c1d76 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "openapi-contract", + "name": "@fqueis/openapi-contract", "version": "1.0.0", "description": "MCP server that reads OpenAPI contracts from local backends for frontend/mobile agents", "type": "module", @@ -18,15 +18,27 @@ "lint:fix": "eslint \"{src,tests}/**/*.ts\" --fix", "test": "vitest run", "test:watch": "vitest", - "test:coverage": "vitest run --coverage" + "test:coverage": "vitest run --coverage", + "prepublishOnly": "pnpm run build" }, "keywords": [ "mcp", "openapi", "swagger" ], - "author": "", + "author": "fqueis", "license": "MIT", + "homepage": "https://github.com/fqueis/openapi-contract#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/fqueis/openapi-contract.git" + }, + "bugs": { + "url": "https://github.com/fqueis/openapi-contract/issues" + }, + "publishConfig": { + "access": "public" + }, "packageManager": "pnpm@10.33.2", "engines": { "node": ">=18"