Repository navigation
Update MCP Coverage #8
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: Update MCP Coverage | |
| on: | |
| workflow_run: | |
| workflows: [Publish NPM] | |
| types: [completed] | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: update-mcp-coverage | |
| cancel-in-progress: false | |
| jobs: | |
| sdk-version: | |
| if: >- | |
| github.repository == 'kernel/kernel-node-sdk' && | |
| (github.event_name == 'workflow_dispatch' || | |
| (github.event.workflow_run.conclusion == 'success' && | |
| github.event.workflow_run.head_repository.full_name == github.repository)) | |
| runs-on: ubuntu-latest | |
| outputs: | |
| version: ${{ steps.version.outputs.version }} | |
| sha: ${{ steps.version.outputs.sha }} | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| with: | |
| ref: ${{ github.event.workflow_run.head_sha || github.sha }} | |
| persist-credentials: false | |
| - name: Resolve published SDK version | |
| id: version | |
| run: | | |
| version=$(jq -er 'select(.name == "@onkernel/sdk") | .version' package.json) | |
| if [[ ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then | |
| echo "Skipping non-stable SDK version: $version" | |
| exit 0 | |
| fi | |
| # A just-published version can take a few minutes to become installable. | |
| for attempt in $(seq 1 30); do | |
| if [[ "$(npm view "@onkernel/sdk@$version" version 2>/dev/null)" == "$version" ]]; then | |
| break | |
| fi | |
| if [[ "$attempt" == 30 ]]; then | |
| echo "::error::@onkernel/sdk@$version is not available on npm after 15 minutes." | |
| exit 1 | |
| fi | |
| sleep 30 | |
| done | |
| echo "version=$version" >> "$GITHUB_OUTPUT" | |
| echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" | |
| generate: | |
| needs: sdk-version | |
| if: needs.sdk-version.outputs.version != '' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 55 | |
| outputs: | |
| changed: ${{ steps.snapshot.outputs.changed }} | |
| main_sha: ${{ steps.prepare.outputs.main_sha }} | |
| update_sha: ${{ steps.prepare.outputs.update_sha }} | |
| rebased: ${{ steps.prepare.outputs.rebased }} | |
| agent_base_tree: ${{ steps.prepare.outputs.agent_base_tree }} | |
| env: | |
| SDK_VERSION: ${{ needs.sdk-version.outputs.version }} | |
| SDK_SHA: ${{ needs.sdk-version.outputs.sha }} | |
| UPDATE_BRANCH: mcp-coverage-update | |
| services: | |
| redis: | |
| image: redis:7-alpine | |
| ports: | |
| - 6379:6379 | |
| options: >- | |
| --health-cmd "redis-cli ping" | |
| --health-interval 5s | |
| --health-timeout 3s | |
| --health-retries 20 | |
| steps: | |
| - name: Checkout updater instructions | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| with: | |
| persist-credentials: false | |
| - name: Checkout published SDK | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| with: | |
| ref: ${{ env.SDK_SHA }} | |
| path: sdk | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 | |
| with: | |
| node-version: '22' | |
| - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 | |
| with: | |
| bun-version: '1.3.3' | |
| - name: Checkout MCP server | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| with: | |
| repository: kernel/kernel-mcp-server | |
| path: mcp | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - name: Prepare update branch and SDK diff | |
| id: prepare | |
| working-directory: mcp | |
| run: | | |
| git config user.name 'kernel-internal[bot]' | |
| git config user.email '260533166+kernel-internal[bot]@users.noreply.github.com' | |
| echo "main_sha=$(git rev-parse origin/main)" >> "$GITHUB_OUTPUT" | |
| if git show-ref --verify --quiet "refs/remotes/origin/$UPDATE_BRANCH"; then | |
| echo "update_sha=$(git rev-parse "origin/$UPDATE_BRANCH")" >> "$GITHUB_OUTPUT" | |
| git switch --track "origin/$UPDATE_BRANCH" | |
| if ! git rebase origin/main; then | |
| git rebase --abort | |
| # The agent covers everything since main's SDK version, so a bot-only | |
| # branch can be regenerated from main. Human commits must be kept. | |
| if git log --no-merges --format=%ae "origin/main..origin/$UPDATE_BRANCH" \ | |
| | grep -qvxF '260533166+kernel-internal[bot]@users.noreply.github.com'; then | |
| echo "::error::$UPDATE_BRANCH has human commits that conflict with main. Resolve the conflicts on that branch, then rerun this workflow." | |
| exit 1 | |
| fi | |
| echo "::warning::$UPDATE_BRANCH conflicts with main; regenerating it from main." | |
| git switch -C "$UPDATE_BRANCH" origin/main | |
| else | |
| echo "rebased=true" >> "$GITHUB_OUTPUT" | |
| fi | |
| else | |
| git switch -c "$UPDATE_BRANCH" origin/main | |
| fi | |
| # The agent's changes are published on top of this head, so human | |
| # commits on the update branch keep their content and authorship. | |
| echo "agent_base=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" | |
| echo "agent_base_tree=$(git rev-parse 'HEAD^{tree}')" >> "$GITHUB_OUTPUT" | |
| old_version=$(jq -er '.dependencies["@onkernel/sdk"]' package.json) | |
| if [[ "$old_version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] && | |
| [[ "$old_version" != "$SDK_VERSION" ]] && | |
| [[ "$(printf '%s\n' "$old_version" "$SDK_VERSION" | sort -V | tail -1)" == "$old_version" ]]; then | |
| echo "MCP already uses newer SDK $old_version; skipping stale run." | |
| exit 0 | |
| fi | |
| if git -C ../sdk rev-parse --verify "refs/tags/v$old_version" >/dev/null 2>&1; then | |
| git -C ../sdk diff "refs/tags/v$old_version" "$SDK_SHA" -- api.md src/resources > "$RUNNER_TEMP/sdk-diff.patch" | |
| echo "old_date=$(TZ=UTC git -C ../sdk log -1 --date=format-local:%Y-%m-%dT%H:%M:%SZ --format=%cd "refs/tags/v$old_version")" >> "$GITHUB_OUTPUT" | |
| else | |
| printf 'Baseline %s unavailable; audit all of api.md and src/resources.\n' "$old_version" > "$RUNNER_TEMP/sdk-diff.patch" | |
| fi | |
| echo "new_date=$(TZ=UTC git -C ../sdk log -1 --date=format-local:%Y-%m-%dT%H:%M:%SZ --format=%cd "$SDK_SHA")" >> "$GITHUB_OUTPUT" | |
| echo "update=true" >> "$GITHUB_OUTPUT" | |
| - name: Generate API read token | |
| if: steps.prepare.outputs.update == 'true' | |
| id: api-read-token | |
| uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1 | |
| with: | |
| app-id: ${{ secrets.ADMIN_APP_ID }} | |
| private-key: ${{ secrets.ADMIN_APP_PRIVATE_KEY }} | |
| owner: kernel | |
| repositories: kernel | |
| permission-contents: read | |
| - name: Fetch API spec | |
| if: steps.prepare.outputs.update == 'true' | |
| env: | |
| GH_TOKEN: ${{ steps.api-read-token.outputs.token }} | |
| OLD_DATE: ${{ steps.prepare.outputs.old_date }} | |
| NEW_DATE: ${{ steps.prepare.outputs.new_date }} | |
| run: | | |
| main_at() { | |
| gh api -X GET repos/kernel/kernel/commits -f sha=main -f until="$1" -f per_page=1 --jq '.[0].sha // empty' | |
| } | |
| file_at() { | |
| gh api -H 'Accept: application/vnd.github.raw' "repos/kernel/kernel/contents/$1?ref=$2" | |
| } | |
| spec="$RUNNER_TEMP/kernel-api/packages/api/openapi.yaml" | |
| head=$(main_at "$NEW_DATE") | |
| test -n "$head" | |
| mkdir -p "$(dirname "$spec")" | |
| file_at packages/api/openapi.yaml "$head" > "$spec" | |
| base="" | |
| if [[ -n "$OLD_DATE" ]]; then | |
| base=$(main_at "$OLD_DATE") | |
| fi | |
| if [[ -n "$base" ]]; then | |
| file_at packages/api/openapi.yaml "$base" > "$RUNNER_TEMP/openapi-base.yaml" | |
| diff -u --label "openapi.yaml@$base" --label "openapi.yaml@$head" \ | |
| "$RUNNER_TEMP/openapi-base.yaml" "$spec" \ | |
| > "$RUNNER_TEMP/openapi-diff.patch" || [[ $? -eq 1 ]] | |
| rm "$RUNNER_TEMP/openapi-base.yaml" | |
| else | |
| printf 'Spec baseline unavailable; compare all relevant descriptions in openapi.yaml.\n' > "$RUNNER_TEMP/openapi-diff.patch" | |
| fi | |
| gh api --method DELETE /installation/token | |
| - name: Install Pi | |
| if: steps.prepare.outputs.update == 'true' | |
| env: | |
| PI_TELEMETRY: '0' | |
| run: | | |
| npm install -g --ignore-scripts @earendil-works/pi-coding-agent | |
| pi --version | |
| - name: Update SDK dependency and MCP coverage | |
| if: steps.prepare.outputs.update == 'true' | |
| working-directory: mcp | |
| env: | |
| ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} | |
| OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} | |
| PI_PREFERRED_MODEL: ${{ vars.PI_PREFERRED_MODEL }} | |
| PI_SKIP_VERSION_CHECK: '1' | |
| PI_TELEMETRY: '0' | |
| run: | | |
| : "${PI_PREFERRED_MODEL:?Set the PI_PREFERRED_MODEL organization variable}" | |
| bun add --exact "@onkernel/sdk@$SDK_VERSION" | |
| pi --print --no-session --approve \ | |
| --model "$PI_PREFERRED_MODEL" --thinking max \ | |
| @../.github/prompts/update-mcp-coverage.md | |
| - name: Snapshot publishable changes | |
| if: steps.prepare.outputs.update == 'true' | |
| id: snapshot | |
| working-directory: mcp | |
| env: | |
| AGENT_BASE: ${{ steps.prepare.outputs.agent_base }} | |
| run: | | |
| git reset -q --soft "$AGENT_BASE" | |
| git add -A | |
| git diff --cached --name-only --no-renames -z \ | |
| | bash ../.github/scripts/mcp-update-disallowed-paths.sh > "$RUNNER_TEMP/excluded-paths" | |
| if [[ -s "$RUNNER_TEMP/excluded-paths" ]]; then | |
| echo "::warning::Discarding changes outside the MCP updater allowlist:" | |
| tr '\0' '\n' < "$RUNNER_TEMP/excluded-paths" | |
| GIT_LITERAL_PATHSPECS=1 git reset -q --pathspec-from-file="$RUNNER_TEMP/excluded-paths" --pathspec-file-nul | |
| fi | |
| git diff --cached --binary --no-renames --no-ext-diff --no-textconv > "$RUNNER_TEMP/changes.patch" | |
| git reset -q --hard | |
| git clean -fdq | |
| if [[ -s "$RUNNER_TEMP/changes.patch" ]]; then | |
| git apply --index "$RUNNER_TEMP/changes.patch" | |
| echo "changed=true" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Verify MCP changes | |
| if: steps.prepare.outputs.update == 'true' | |
| working-directory: mcp | |
| run: | | |
| test -s "$RUNNER_TEMP/mcp-coverage-report.md" | |
| jq -e --arg version "$SDK_VERSION" '.dependencies["@onkernel/sdk"] == $version and | |
| ([.overrides, .resolutions, .patchedDependencies] | map((. // {}) | has("@onkernel/sdk")) | any | not)' package.json | |
| grep -qF "\"@onkernel/sdk\": [\"@onkernel/sdk@$SDK_VERSION\", \"\"," bun.lock | |
| bun install --frozen-lockfile | |
| bun run check:managed-auth-app | |
| bunx tsc --noEmit --incremental false | |
| bun test | |
| bun scripts/record-oauth-redis-contract.ts | |
| git diff --exit-code -- fixtures/oauth-redis-recordings.json | |
| git diff --cached --check | |
| - name: Upload validated changes | |
| if: steps.snapshot.outputs.changed == 'true' | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | |
| with: | |
| name: mcp-coverage-update | |
| path: | | |
| ${{ runner.temp }}/changes.patch | |
| ${{ runner.temp }}/mcp-coverage-report.md | |
| if-no-files-found: error | |
| retention-days: 7 | |
| publish: | |
| needs: [sdk-version, generate] | |
| if: needs.generate.outputs.changed == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| env: | |
| SDK_VERSION: ${{ needs.sdk-version.outputs.version }} | |
| MAIN_SHA: ${{ needs.generate.outputs.main_sha }} | |
| UPDATE_SHA: ${{ needs.generate.outputs.update_sha }} | |
| REBASED: ${{ needs.generate.outputs.rebased }} | |
| AGENT_BASE_TREE: ${{ needs.generate.outputs.agent_base_tree }} | |
| UPDATE_BRANCH: mcp-coverage-update | |
| steps: | |
| - name: Checkout updater scripts | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| with: | |
| persist-credentials: false | |
| - name: Download validated changes | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 | |
| with: | |
| name: mcp-coverage-update | |
| path: ${{ runner.temp }}/mcp-coverage-update | |
| - name: Checkout MCP server | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 | |
| with: | |
| repository: kernel/kernel-mcp-server | |
| path: mcp | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - name: Apply validated changes | |
| working-directory: mcp | |
| run: | | |
| git config user.name 'kernel-internal[bot]' | |
| git config user.email '260533166+kernel-internal[bot]@users.noreply.github.com' | |
| if [[ "$REBASED" == true ]]; then | |
| git switch -c "$UPDATE_BRANCH" "$UPDATE_SHA" | |
| git rebase -q "$MAIN_SHA" | |
| else | |
| git switch -c "$UPDATE_BRANCH" "$MAIN_SHA" | |
| fi | |
| if [[ "$(git rev-parse 'HEAD^{tree}')" != "$AGENT_BASE_TREE" ]]; then | |
| echo "::error::The rebuilt update branch differs from the one the changes were validated on." | |
| exit 1 | |
| fi | |
| git apply --index "$RUNNER_TEMP/mcp-coverage-update/changes.patch" | |
| git diff --cached --name-only --no-renames -z \ | |
| | bash ../.github/scripts/mcp-update-disallowed-paths.sh > "$RUNNER_TEMP/disallowed-paths" | |
| if [[ -s "$RUNNER_TEMP/disallowed-paths" ]]; then | |
| echo "::error::Validated changes include paths outside the MCP updater allowlist:" | |
| tr '\0' '\n' < "$RUNNER_TEMP/disallowed-paths" | |
| exit 1 | |
| fi | |
| if git diff --cached --raw --no-renames | awk '$2 !~ /^(100644|100755|000000)$/ { found = 1 } END { exit !found }'; then | |
| echo "::error::Validated changes may only contain regular files." | |
| exit 1 | |
| fi | |
| git commit -q -m "Update Node SDK to $SDK_VERSION and MCP coverage" | |
| - name: Generate MCP repository token | |
| id: app-token | |
| uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1 | |
| with: | |
| app-id: ${{ secrets.ADMIN_APP_ID }} | |
| private-key: ${{ secrets.ADMIN_APP_PRIVATE_KEY }} | |
| owner: kernel | |
| repositories: kernel-mcp-server | |
| permission-contents: write | |
| permission-pull-requests: write | |
| - name: Open or update MCP coverage PR | |
| working-directory: mcp | |
| env: | |
| GH_TOKEN: ${{ steps.app-token.outputs.token }} | |
| run: | | |
| if git diff --quiet origin/main HEAD; then | |
| echo "MCP coverage is already up to date." | |
| exit 0 | |
| fi | |
| gh auth setup-git | |
| git push --force-with-lease="$UPDATE_BRANCH:$UPDATE_SHA" origin "$UPDATE_BRANCH" | |
| body="$RUNNER_TEMP/mcp-coverage-pr.md" | |
| { | |
| printf '## Summary\n\n- Update `@onkernel/sdk` to `%s`.\n' "$SDK_VERSION" | |
| printf '\n## Coverage\n\n' | |
| head -c 60000 "$RUNNER_TEMP/mcp-coverage-update/mcp-coverage-report.md" | |
| printf '\n## Validation\n\n- Frozen Bun install\n- Managed-auth App bundle check\n- TypeScript type check\n- Bun tests\n- OAuth Redis recording check\n' | |
| } > "$body" | |
| pr=$(gh pr list --repo kernel/kernel-mcp-server --head "$UPDATE_BRANCH" --base main \ | |
| --state open --json number --jq '.[0].number // empty') | |
| title="MCP: Update Node SDK to $SDK_VERSION" | |
| if [[ -n "$pr" ]]; then | |
| gh pr edit "$pr" --repo kernel/kernel-mcp-server --title "$title" --body-file "$body" | |
| else | |
| gh pr create --repo kernel/kernel-mcp-server --head "$UPDATE_BRANCH" --base main \ | |
| --title "$title" --body-file "$body" | |
| fi |