From 5022e04399793e40f678ce003e22f1a4b623ba41 Mon Sep 17 00:00:00 2001 From: Radoslav Dimitrov Date: Fri, 21 Aug 2026 01:43:05 +0300 Subject: [PATCH] ci: widen the publish-attempt path list (#1560) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #1524 tried to publish by adding `data/servers/io.decisionrules/server.json`. The detector ran fine and logged: ``` Non-publish file changed: data/servers/io.decisionrules/server.json PR #1524 match=false ``` The path list only knew `servers/**` at the repo root and exactly `data/seed.json`. Putting the file under `data/servers/` is a natural guess given `data/seed.json` exists, and it fell straight through. Adds `data/servers/**` plus root-level `server.json` / `servers.json`. `find` confirms nothing legitimate lives at any of those paths today. The list stays explicit rather than becoming a broad `**/server.json` glob. The classifier only flags a PR when *every* changed file matches, which makes widening asymmetric: too narrow silently misses spam, too broad auto-closes a legitimate single-file PR. Both copies of the list (stage 1 and stage 2) are updated identically, with a comment on each pointing at the other. ## Testing `actionlint` clean. macOS ships bash 3.2, so I extracted the literal `if [[ ... ]]` condition from each workflow file and ran it under bash 5 in Docker — testing the shipped text rather than a retyped copy. Both copies are byte-identical and agree: ``` MATCH data/servers/io.decisionrules/server.json # PR 1524 MATCH servers/com.aradia/x.json # PR 1549 MATCH data/seed.json MATCH server.json MATCH servers.json keep internal/api/handler.go keep internal/servers/service.go keep data/servers.md keep docs/README.md ``` Also dry-ran the full classifier against all 35 currently-open PRs from untrusted authors: **0 would be closed**. No legitimate open PR is affected. ## Known remaining gap (deliberately not fixed here) The detector does not run at all for authors new to GitHub. This repo's Actions approval policy is `first_time_contributors_new_to_github`, so their fork PRs sit in `action_required` until a maintainer approves the run. That is why #1549 was never flagged. I'm leaving that alone for now because the numbers don't justify the fix. Since the automation landed on 2026-07-09 it has had **71 detect runs: 70 ran, 1 was gated**. It has correctly closed 3 publish attempts (SAGE, Diagram Generation API, Kairos Signal) and missed 2 — one to the path bug this PR fixes, one to the gate. So the gate costs roughly one missed PR every six weeks, and those get closed manually within a day. The only ways around it are `pull_request_target` (runs untrusted PRs in a privileged context — one future `actions/checkout` from being a privilege-escalation hole) or a scheduled sweep (works, but trades the two-stage design for cron semantics and a ~15 min delay). Neither seems worth it at that rate. Happy to open a follow-up if you'd rather close the gap. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5 (1M context) --- .../workflows/close-invalid-publish-prs.yml | 9 +++++++-- .../workflows/detect-invalid-publish-prs.yml | 18 +++++++++++++++--- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/.github/workflows/close-invalid-publish-prs.yml b/.github/workflows/close-invalid-publish-prs.yml index 934421b..8666c3b 100644 --- a/.github/workflows/close-invalid-publish-prs.yml +++ b/.github/workflows/close-invalid-publish-prs.yml @@ -34,7 +34,7 @@ jobs: COMMENT_BODY: | Hi @__AUTHOR__ 👋 — thanks for your interest in the MCP Registry! - It looks like this PR is trying to publish an MCP server by adding or editing files in this repository (under `servers/` or in `data/seed.json`). That isn't how servers get published, so I'm closing this PR automatically. + It looks like this PR is trying to publish an MCP server by adding or editing server files in this repository (under `servers/`, `data/servers/`, or in `data/seed.json`). That isn't how servers get published, so I'm closing this PR automatically. **Servers are published with the [`mcp-publisher`](https://github.com/modelcontextprotocol/registry/blob/main/docs/modelcontextprotocol-io/quickstart.mdx) CLI**, not by opening a pull request against this repo. The CLI verifies that you own your namespace and submits your `server.json` directly to the live registry API. @@ -121,10 +121,15 @@ jobs: echo "No files for PR #$PR_NUMBER; skipping." exit 0 fi + # Keep this list in sync with the copy in stage 1. publish_attempt=0 other=0 for f in "${FILES[@]}"; do - if [[ "$f" == servers/* || "$f" == "data/seed.json" ]]; then + if [[ "$f" == servers/* \ + || "$f" == data/servers/* \ + || "$f" == "data/seed.json" \ + || "$f" == "server.json" \ + || "$f" == "servers.json" ]]; then publish_attempt=1 else other=1 diff --git a/.github/workflows/detect-invalid-publish-prs.yml b/.github/workflows/detect-invalid-publish-prs.yml index 355521b..c200f86 100644 --- a/.github/workflows/detect-invalid-publish-prs.yml +++ b/.github/workflows/detect-invalid-publish-prs.yml @@ -48,12 +48,24 @@ jobs: mapfile -t FILES < <(gh api --paginate "repos/$GH_REPO/pulls/$PR_NUMBER/files" --jq '.[].filename') if [ "${#FILES[@]}" -gt 0 ]; then # Flag only if the PR touches *exclusively* publish-attempt files - # (servers/** and/or data/seed.json) and nothing else, so that - # legit PRs touching seed data alongside real code are left alone. + # and nothing else, so that legit PRs touching seed data + # alongside real code are left alone. + # + # Keep this list in sync with the copy in stage 2. It stays an + # explicit list rather than a broad `**/server.json` glob: + # because we require *every* file to match, an over-broad + # pattern would auto-close a legitimate single-file PR. + # data/servers/** is here because #1524 put its server.json + # there - a natural guess next to data/seed.json - and slipped + # through the original servers/**-only list. publish_attempt=0 other=0 for f in "${FILES[@]}"; do - if [[ "$f" == servers/* || "$f" == "data/seed.json" ]]; then + if [[ "$f" == servers/* \ + || "$f" == data/servers/* \ + || "$f" == "data/seed.json" \ + || "$f" == "server.json" \ + || "$f" == "servers.json" ]]; then publish_attempt=1 else echo "Non-publish file changed: $f"