From 5485103b40d39924c8a2bf75ca8fdf11ed1f3792 Mon Sep 17 00:00:00 2001 From: "Kent C. Dodds" Date: Thu, 30 Jul 2026 08:34:09 -0400 Subject: [PATCH] Add GitHub Actions workflow for MCP agent session backfill. Lets operators dry-run and execute the production sweep with existing CF secrets, and documents the correct kody-production worker script. Co-authored-by: Cursor --- .../workflows/backfill-mcp-agent-sessions.yml | 125 ++++++++++++++++++ .../mcp-agent-session-backfill.md | 31 ++++- 2 files changed, 149 insertions(+), 7 deletions(-) create mode 100644 .github/workflows/backfill-mcp-agent-sessions.yml diff --git a/.github/workflows/backfill-mcp-agent-sessions.yml b/.github/workflows/backfill-mcp-agent-sessions.yml new file mode 100644 index 0000000000..41dfbb89c9 --- /dev/null +++ b/.github/workflows/backfill-mcp-agent-sessions.yml @@ -0,0 +1,125 @@ +name: 🧭 Backfill MCP agent sessions + +on: + workflow_dispatch: + inputs: + mode: + description: Dry-run audits only; execute writes index rows and the completion marker. + type: choice + options: + - dry-run + - execute + default: dry-run + origin: + description: Deployed Worker origin that serves /__maintenance/* + type: string + default: https://heykody.dev + worker_script: + description: Cloudflare Workers script name that owns the MCP Durable Object namespace + type: string + default: kody-production + +permissions: + contents: read + +concurrency: + group: backfill-mcp-agent-sessions-${{ inputs.origin }}-${{ inputs.worker_script }} + cancel-in-progress: false + +jobs: + backfill: + runs-on: ubuntu-latest + name: 🧭 Backfill MCP agent sessions (${{ inputs.mode }}) + timeout-minutes: 180 + steps: + - name: 📦 Checkout + uses: actions/checkout@v6.0.2 + with: + persist-credentials: false + + - name: 🟢 Setup Node + uses: actions/setup-node@v6 + with: + node-version: 26 + cache: npm + + - name: 📥 Install Dependencies + run: npm ci + + - name: 🧭 Run MCP agent session backfill + env: + CLOUDFLARE_ACCOUNT_ID: ${{ vars.CLOUDFLARE_ACCOUNT_ID }} + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CAPABILITY_REINDEX_SECRET: ${{ secrets.CAPABILITY_REINDEX_SECRET }} + BACKFILL_MODE: ${{ inputs.mode }} + BACKFILL_ORIGIN: ${{ inputs.origin }} + BACKFILL_WORKER_SCRIPT: ${{ inputs.worker_script }} + run: | + set -euo pipefail + if [ -z "${CLOUDFLARE_ACCOUNT_ID:-}" ] || [ -z "${CLOUDFLARE_API_TOKEN:-}" ] || [ -z "${CAPABILITY_REINDEX_SECRET:-}" ]; then + echo "CLOUDFLARE_ACCOUNT_ID, CLOUDFLARE_API_TOKEN, and CAPABILITY_REINDEX_SECRET are required." >&2 + exit 1 + fi + + args=( + --origin "${BACKFILL_ORIGIN}" + --worker-script "${BACKFILL_WORKER_SCRIPT}" + --audit-out "${RUNNER_TEMP}/mcp-agent-session-backfill-${BACKFILL_MODE}.json" + ) + if [ "${BACKFILL_MODE}" = "execute" ]; then + args+=(--execute) + fi + + echo "Running backfill mode=${BACKFILL_MODE} origin=${BACKFILL_ORIGIN} worker_script=${BACKFILL_WORKER_SCRIPT}" + npm run backfill:mcp-agent-sessions -- "${args[@]}" + echo "AUDIT_PATH=${RUNNER_TEMP}/mcp-agent-session-backfill-${BACKFILL_MODE}.json" >> "$GITHUB_ENV" + + - name: 📊 Summarize audit + if: always() && env.AUDIT_PATH != '' + run: | + set -euo pipefail + if [ ! -f "${AUDIT_PATH}" ]; then + echo "No audit artifact was written." + exit 0 + fi + node <<'NODE' + const fs = require('node:fs') + const audit = JSON.parse(fs.readFileSync(process.env.AUDIT_PATH, 'utf8')) + const actions = {} + for (const row of audit.rows ?? []) { + const action = String(row.action ?? 'unknown') + actions[action] = (actions[action] ?? 0) + 1 + } + const failureErrors = {} + for (const failure of audit.failures ?? []) { + const error = String(failure.error ?? 'unknown') + failureErrors[error] = (failureErrors[error] ?? 0) + 1 + } + console.log( + JSON.stringify( + { + dryRun: audit.dryRun, + workerScript: audit.workerScript, + namespaceId: audit.namespaceId, + pages: audit.pages, + listedWithStoredData: audit.listedWithStoredData, + rowCount: (audit.rows ?? []).length, + failureCount: (audit.failures ?? []).length, + actions, + failureErrors, + completedAt: audit.completedAt, + }, + null, + 2, + ), + ) + NODE + + - name: 📤 Upload audit artifact + if: always() && env.AUDIT_PATH != '' + uses: actions/upload-artifact@v4 + with: + name: mcp-agent-session-backfill-${{ inputs.mode }} + path: ${{ env.AUDIT_PATH }} + if-no-files-found: ignore + retention-days: 30 diff --git a/docs/contributing/mcp-agent-session-backfill.md b/docs/contributing/mcp-agent-session-backfill.md index 18a2aea80c..193caf0b29 100644 --- a/docs/contributing/mcp-agent-session-backfill.md +++ b/docs/contributing/mcp-agent-session-backfill.md @@ -8,18 +8,35 @@ The operator sweep uses Cloudflare's Durable Objects list API and the existing `CAPABILITY_REINDEX_SECRET` maintenance authentication. Never pass secrets as CLI flags or commit audit output. -## Required environment +## Preferred: GitHub Actions + +Use the `🧭 Backfill MCP agent sessions` workflow +(`.github/workflows/backfill-mcp-agent-sessions.yml`): + +```bash +gh workflow run backfill-mcp-agent-sessions.yml \ + -f mode=dry-run \ + -f origin=https://heykody.dev \ + -f worker_script=kody-production +``` + +Review the uploaded audit artifact, then re-run with `-f mode=execute` only when +there are no `no_owner` rows, ownership conflicts, or other failures. Production +inventory is large (thousands of MCP Durable Objects); the job timeout is three +hours. + +## Required environment (local CLI) - `CLOUDFLARE_ACCOUNT_ID` -- `CLOUDFLARE_API_TOKEN` with Workers/Durable Objects read access -- `CAPABILITY_REINDEX_SECRET` +- `CLOUDFLARE_API_TOKEN` with Workers Scripts Read (Durable Objects list) +- `CAPABILITY_REINDEX_SECRET` matching the deployed Worker secret -## Dry run +## Dry run (local) ```bash npm run backfill:mcp-agent-sessions -- \ --origin https://heykody.dev \ - --worker-script kody \ + --worker-script kody-production \ --audit-out /tmp/mcp-agent-session-backfill-dry-run.json ``` @@ -27,12 +44,12 @@ The command resolves the `MCP` namespace, cursor-pages every object with stored data, and calls the maintenance ownership RPC in batches of 50 without writing index rows. Review every `no_owner`, conflict, and failure before execution. -## Execute +## Execute (local) ```bash npm run backfill:mcp-agent-sessions -- \ --origin https://heykody.dev \ - --worker-script kody \ + --worker-script kody-production \ --audit-out /tmp/mcp-agent-session-backfill-execute.json \ --execute ```