refactor!: MCP-only toolset — every tool over tools/call, with search/execute sessions and submitFeedback() #937
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| pull_request: | |
| branches: | |
| - main | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| pages: write | |
| id-token: write | |
| jobs: | |
| gitleaks: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup Nix | |
| uses: ./.github/actions/setup-nix | |
| with: | |
| tools: gitleaks | |
| skip-pnpm-install: 'true' | |
| - name: Run Gitleaks | |
| run: gitleaks detect --source . --config .gitleaks.toml | |
| lint: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Setup Nix | |
| uses: ./.github/actions/setup-nix | |
| with: | |
| tools: nodejs_24 pnpm_10 oxlint oxfmt | |
| - name: Run Lint | |
| run: pnpm run lint | |
| build-and-test: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Setup Nix | |
| uses: ./.github/actions/setup-nix | |
| - name: Run Build | |
| run: pnpm run build | |
| - name: Run Tests | |
| run: pnpm test | |
| # The `ai` peer range spans three majors whose tool types differ, so the | |
| # single build-and-test job above only ever proves whichever version the dev | |
| # catalog pins. This job covers the declared range independently of that pin. | |
| # | |
| # Every entry is an exact version, not a range. A range resolves to whatever | |
| # the lockfile already holds unless the lockfile is deleted, and deleting it | |
| # re-resolves every dependency to its newest in-range version, which trips | |
| # this repo's `trustPolicy: no-downgrade` on unrelated packages. Each pin is | |
| # therefore a boundary worth holding still: | |
| # v5 - the lower bound of the declared peer range | |
| # v6 - the last v6 the dev catalog shipped | |
| # v7 - the major boundary where the AI SDK tool types changed shape | |
| # The newest v7 is not covered here; build-and-test covers whatever the dev | |
| # catalog pins, which is the only v7 that moves. | |
| ai-peer-range: | |
| name: ai-peer-range (${{ matrix.ai }}) | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| ai: | |
| - 5.0.108 # lower bound of the peer range | |
| - 6.0.7 # last v6 the dev catalog shipped | |
| - 7.0.0 # first v7, where Tool became a union | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Setup Nix | |
| uses: ./.github/actions/setup-nix | |
| with: | |
| skip-pnpm-install: 'true' | |
| # `--filter .` keeps the examples workspace out of resolution; it pulls a | |
| # dependency that `blockExoticSubdeps` rejects on any lockfile update. | |
| # | |
| # Assert the resolved version, not just that the edit looked applied. A | |
| # `sed` that stops matching would otherwise leave the catalog untouched | |
| # and the job would pass while silently testing the version it was meant | |
| # to replace. | |
| - name: Install with ai@${{ matrix.ai }} | |
| run: | | |
| sed -i -E "/^ dev:/,/^ [a-z]+:/ s|^ ai: .*| ai: '${{ matrix.ai }}'|" pnpm-workspace.yaml | |
| pnpm install --no-frozen-lockfile --filter . | |
| resolved=$(node -p "require('./node_modules/ai/package.json').version") | |
| echo "resolved ai@$resolved" | |
| if [ "$resolved" != "${{ matrix.ai }}" ]; then | |
| echo "::error::expected ai@${{ matrix.ai }} but resolved ai@$resolved" | |
| exit 1 | |
| fi | |
| # Type errors are the failure mode here, and vitest type-checks the suite | |
| # via the root project's `typecheck` config. | |
| - name: Run Tests | |
| run: pnpm exec vitest run --project root | |
| # Enforces the wire contract shared with the Python SDK (StackOneHQ/sdk-conformance). | |
| # StackOneHQ/sdk-conformance is private and requires CONFORMANCE_REPO_TOKEN. | |
| # Fork PRs and Dependabot runs cannot access this secret and skip this gate; | |
| # internal branches and PRs require the secret and fail if it is absent or expired. | |
| check-secret: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| can-skip: ${{ steps.check.outputs.can-skip }} | |
| steps: | |
| - id: check | |
| env: | |
| TOKEN: ${{ secrets.CONFORMANCE_REPO_TOKEN }} | |
| IS_FORK: ${{ github.event.pull_request.head.repo.fork == true }} | |
| IS_DEPENDABOT: ${{ github.actor == 'dependabot[bot]' }} | |
| run: | | |
| if [ -z "$TOKEN" ] && { [ "$IS_FORK" = "true" ] || [ "$IS_DEPENDABOT" = "true" ]; }; then | |
| echo "can-skip=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "can-skip=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| conformance: | |
| needs: check-secret | |
| if: needs.check-secret.outputs.can-skip != 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check conformance token is configured | |
| env: | |
| TOKEN: ${{ secrets.CONFORMANCE_REPO_TOKEN }} | |
| run: | | |
| if [ -z "$TOKEN" ]; then | |
| echo "::error::CONFORMANCE_REPO_TOKEN is not set or has expired." | |
| exit 1 | |
| fi | |
| - name: Checkout SDK | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| path: sdk | |
| - name: Checkout conformance suite | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| repository: StackOneHQ/sdk-conformance | |
| ref: 9b5605a520150c55724fdf9236871bc95e52b0cb | |
| token: ${{ secrets.CONFORMANCE_REPO_TOKEN }} | |
| path: sdk-conformance | |
| persist-credentials: false | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 | |
| with: | |
| version: 10.26.0 | |
| package_json_file: sdk-conformance/package.json | |
| # 24, not 22: the SDK's devEngines pins Node ^24.11.0, and the suite runs on it too. | |
| - name: Setup Node | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | |
| with: | |
| node-version: 24 | |
| - name: Install SDK dependencies | |
| working-directory: sdk | |
| run: pnpm install --frozen-lockfile | |
| # The runner imports the built dist, not src, so the harness grades the | |
| # publishable artifact. It refuses a dist older than src. | |
| - name: Build SDK | |
| working-directory: sdk | |
| run: pnpm run build | |
| - name: Install conformance dependencies | |
| working-directory: sdk-conformance | |
| run: pnpm install --frozen-lockfile | |
| - name: Run conformance suite | |
| working-directory: sdk-conformance | |
| env: | |
| NODE_SDK_DIST: ${{ github.workspace }}/sdk/dist/index.mjs | |
| run: pnpm test:node -- --strict-schema | |
| # Make this the one required status check. Individually required checks go | |
| # missing when a job is renamed or a matrix entry is added, and nothing notices. | |
| ci-ok: | |
| if: always() | |
| needs: [check-secret, gitleaks, lint, build-and-test, ai-peer-range, conformance] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Require every job to have passed | |
| run: | | |
| if [ "${{ needs.gitleaks.result != 'success' || needs.lint.result != 'success' || needs.build-and-test.result != 'success' || needs.ai-peer-range.result != 'success' }}" = "true" ]; then | |
| echo "::error::A required job did not pass (gitleaks: ${{ needs.gitleaks.result }}, lint: ${{ needs.lint.result }}, build-and-test: ${{ needs.build-and-test.result }}, ai-peer-range: ${{ needs.ai-peer-range.result }})" | |
| exit 1 | |
| fi | |
| if [ "${{ needs.conformance.result != 'success' && needs.check-secret.outputs.can-skip != 'true' }}" = "true" ]; then | |
| echo "::error::Conformance check did not pass (result: ${{ needs.conformance.result }})" | |
| exit 1 | |
| fi | |
| coverage: | |
| runs-on: ubuntu-latest | |
| if: github.ref == 'refs/heads/main' | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Setup Nix | |
| uses: ./.github/actions/setup-nix | |
| - name: Run Tests with Coverage | |
| run: pnpm run coverage | |
| - name: Create Coverage Badge | |
| uses: jaywcjlove/coverage-badges-cli@4e8975aa2628e3329126e7eee36724d07ed86fda # v2.2.0 | |
| with: | |
| source: coverage/coverage-summary.json | |
| output: coverage/badges.svg | |
| - name: Upload coverage artifact | |
| uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4.0.0 | |
| with: | |
| path: coverage | |
| deploy-coverage: | |
| needs: coverage | |
| runs-on: ubuntu-latest | |
| environment: | |
| name: github-pages | |
| url: ${{ steps.deployment.outputs.page_url }} | |
| steps: | |
| - name: Deploy to GitHub Pages | |
| id: deployment | |
| uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5 |