Skip to main content

pr-flow

Take an issue through to a PR in this repo and close it out after merge. Use when asked to open, create or submit a PR; when naming a branch; when signing off commits or repairing a missing Signed-off-by; when recording client evidence for a PR; when requesting a Copilot review, answering its comments or deciding whether to run another round; or when closing out after a merge.

Informations de source

Dépôt
modelcontextprotocol/servers
Dernière activité de la source
5 octobre 2026 à 05:28
Langue détectée de SKILL.md
anglais
Étoiles
91 295
Forks
11 788

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Explorateur de fichiers
2 fichiers

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
name
pr-flow
description
Take an issue through to a PR in this repo and close it out after merge. Use when asked to open, create or submit a PR; when naming a branch; when signing off commits or repairing a missing Signed-off-by; when recording client evidence for a PR; when requesting a Copilot review, answering its comments or deciding whether to run another round; or when closing out after a merge.
disable-model-invocation
false
# PR flow The rules this procedure carries out live in [`AGENTS.md`](../../../AGENTS.md) (**Contributing**, **Issue-driven work style**, **Responding to code reviews**, **Waiting on long-running work**). Board mechanics, including every board ID, are in `/board-ops`; creating a missing issue is `/issue-create`. This file names board columns and nothing else. **Pull requests against this repo are opened by the maintainers only.** Write access is not authorization to open one. Anyone else files a detailed issue (`/issue-create`) and a maintainer takes it from there. ## 1. Start from an issue **Every PR closes an issue.** A PR with no linked issue has no board card, so the work is invisible to the board. If there is no issue yet, create one first with `/issue-create`; never open the PR and backfill. **Read the issue in full, every comment included.** Scope changes, maintainer decisions and "not this, that" corrections are usually in the comments, not the body: ```sh gh issue view <N> --repo modelcontextprotocol/servers --comments ``` Then assign it to yourself and move its card to **In Progress** (the move-a-card recipe in `/board-ops`): ```sh gh issue edit <N> --repo modelcontextprotocol/servers --add-assignee @me ``` ## 2. Branch `v2/<type>/<N>-<slug>`, cut from **`origin/v2/main`**: ```sh git fetch origin v2/main git switch -c v2/fix/4810-filesystem-symlink-escape origin/v2/main ``` `<type>` follows the issue's type label: `bug` → `fix`, `enhancement` → `feat`, `documentation` → `docs`, `chore` → `chore`. A `question` has no branch type, because answering one ships nothing: if it turns out to need a change, relabel the issue with the type that change is, then branch. The `v2/` prefix matches the base branch and keeps the work legible in `git branch -a` next to `main`. **Never cut from `main`.** It is the release line and does not carry `v2/main`'s history, so the PR diff would show unrelated changes. **Stacking.** When one issue's work depends on another PR that has not merged yet, branch from that PR's branch and open the PR with `--base` set to it. Say in the body that it is stacked and on which PR. When the lower PR merges, retarget the stacked one to `v2/main` (`gh pr edit <PR> --base v2/main`) and rebase it if GitHub reports conflicts. ## 3. Sign off every commit (DCO) This repo adopted the [Developer Certificate of Origin](https://developercertificate.org/) (maintainer decision on #4861). Commit with **`git commit -s`**, every time. The rule is checked twice, by the same script (`scripts/verify-dco.mjs`): - **`npm run verify:dco`**, a stage of the pre-push gate (step 4), over `origin/v2/main..HEAD`: the commits you are about to push. Name another range with `-- --base <rev> --head <rev>` (a stacked branch's base, say). - **The `DCO signoff` job** (`.github/workflows/dco.yml`), on every pull request except into `main`, over the PR's `base.sha..head.sha`. What it checks: each commit carries a `Signed-off-by: Name <email>` trailer whose name **and** email match the commit's author or its committer. Merge commits and bot-authored commits are exempt. There is no partial credit: one unsigned commit out of six fails the whole check. The failure names each unsigned commit and prints the repair below. Two things that look like automation and are not: - ⚠️ **`git config format.signOff true` does nothing here.** Despite the name, it only defaults the `-s` flag for `git format-patch`. `git commit` never reads it, and there is no `commit.signoff` equivalent. - ⚠️ **A `prepare-commit-msg` hook works, but think before installing one.** The trailer is a certification, and a hook makes it on your behalf for _every_ commit, including work you merely cherry-picked. Inside that hook, `git var GIT_AUTHOR_IDENT` returns your config identity rather than the preserved author, so it cannot even tell it is signing for someone else. **Repairing commits already made** means rewriting them, onto the merge base the failure prints: ```sh git rebase --signoff <merge-base> git push --force-with-lease ``` `--signoff` signs each commit as you, the committer. Use `--force-with-lease` rather than `--force`, and rewrite only when you are the sole author and nobody has based work on the branch (a stacked PR above yours has). Nothing else clears the check: an empty "remediation" commit signs nothing that came before it, and the check reads every commit in the range. The signoff is a DCO assertion made in **your own name**. It does not claim you wrote the code, so signing off a cherry-pick is legitimate. Fabricating _someone else's_ certification never is. ## 4. Run the gate Format, then run the pre-push gate, and fix what fails before pushing: ```sh npm run format # TypeScript; in a Python server: uv run --frozen ruff format . npm run local:gate; echo "EXIT=$?" ``` `local:gate` runs every check CI runs, for both languages, whatever the change touched. Verify by the exit code, not by grepping the output. What each stage checks and how to fix a red one is `/pre-push-gate`; the stage list is [`docs/quality-gate.md`](../../../docs/quality-gate.md). One thing the gate deliberately leaves out: | Change touches | Also run | | --- | --- | | A skill's description, or a new skill | `npm run skills:eval` for the **whole** suite (spends model calls; not in the gate or CI). See `AGENTS.md` **Maintaining the skills** | ## 5. Client evidence A PR shows the change working, under the body's **How Has This Been Tested?** section (step 6). Here that means evidence from clients, not screenshots. **A change to server behavior** (a tool, resource, prompt, capability, transport or error it returns) records, for **both** the Inspector V2 and an LLM client, in **both** spec eras (a 2026-07-28 client and a 2025-11-25 client, per #4857): - what the client was asked to do (the Inspector action, or the prompt given to the LLM client), and - what it returned (the result, trimmed to what shows the change). A before/after pair is best when the change fixes a bug: the same request against `v2/main` and against the branch. **A change with no client-observable surface** (docs, skills, workflows, gate tooling, CI) carries a **targeted probe** instead: the one thing that proves it. For example, the guard made to fire on a planted defect and then pass, the workflow run that exercised the new job, the query that reads back the state a skill's recipe produced, or the `skills:eval` scores for a skill. Put the evidence in the PR body under **How Has This Been Tested?**. ## 6. Open the PR Base **`v2/main`** (or the lower branch, when stacked). **Never `main`.** Label it `v2`. The body's **first line is `Closes #<N>`**. The body carries these sections, in order. `.github/pull_request_template.md` is only the "issues, not PRs" banner that turns outside PRs away, so this list is where the structure lives: - **Description**: what changed and why. - **Server Details**: the server (or "none (repository-wide)") and what in it changed (tools, resources, prompts, docs, …). - **Motivation and Context**: the problem it solves; usually a pointer to the issue. - **How Has This Been Tested?**: the evidence from step 5. - **Breaking Changes**: whether users must change their client configuration. - **Types of changes**: tick each that applies: bug fix, new feature, breaking change, documentation update. - **Checklist**: answer every item. Tick each that holds, and tick and mark each that does not apply "(not applicable: <why>)"; leave none blank: - [ ] I have read the [MCP Protocol Documentation](https://modelcontextprotocol.io) - [ ] My changes follow MCP security best practices - [ ] I have updated the server's README accordingly - [ ] I have added a changeset (`npm run changeset`) if this changes what a TypeScript server publishes - [ ] I have tested this with an LLM client - [ ] My code follows the repository's style guidelines - [ ] New and existing tests pass locally - [ ] I have added appropriate error handling - [ ] I have documented all environment variables and configuration options - **Additional context** (optional): implementation notes or design decisions. Write the body to a file and pass it with `--body-file`. A body passed inline in double quotes goes through the shell, so every backtick in its Markdown runs as a command substitution and `$VAR` expands. Keep the file outside the worktree, where `git add -A` cannot pick it up. ```sh BODY=$(mktemp) cat > "$BODY" <<'EOF' Closes #<N> <the sections above, with the evidence> EOF gh pr create --repo modelcontextprotocol/servers \ --base v2/main --label v2 --title "<title>" --body-file "$BODY" ``` ⚠️ Closing keywords only link and auto-close for PRs that target the default branch (`main`). Against `v2/main`, `Closes #N` is only a cross-reference. Keep the line anyway, and **link the PR to the issue explicitly** right after opening it: ```sh PR=<PR_NUMBER>; N=<ISSUE_NUMBER> PR_ID=$(gh pr view "$PR" --repo modelcontextprotocol/servers --json id --jq .id) ISSUE_ID=$(gh issue view "$N" --repo modelcontextprotocol/servers --json id --jq .id) gh api graphql -f query=' mutation($issue:ID!,$pr:ID!) { addCloseIssueReferences(input:{issueId:$issue, pullRequestIds:[$pr]}) { issue { number } } }' -f issue="$ISSUE_ID" -f pr="$PR_ID" # Read the link back: the issue number must be listed. gh pr view "$PR" --repo modelcontextprotocol/servers \ --json closingIssuesReferences --jq '[.closingIssuesReferences[].number]' ``` The link does **not** make the issue close on merge into `v2/main`; that is still done by hand in step 9. Then move the card to **In Review** (`/board-ops`). The PR itself never goes on the board. ## 7. Request a Copilot review Only the GraphQL `requestReviews` mutation with the Copilot **bot id** works. REST, `gh pr edit --add-reviewer`, `userIds` and `copilot-swe-agent` all fail or silently drop the request. ```sh PR_ID=$(gh pr view <PR> --repo modelcontextprotocol/servers --json id --jq .id) gh api graphql -f query=' mutation($pr:ID!,$bot:[ID!]!) { requestReviews(input:{pullRequestId:$pr, botIds:$bot, union:true}) { pullRequest { id } } }' -f pr="$PR_ID" -f bot='BOT_kgDOCnlnWA' ``` A round usually lands in about five minutes. Wait for it with **one backgrounded loop that exits when the round lands, or when its deadline passes**, and wait for its notification rather than re-fetching once per turn. A review is remote state the harness cannot observe, which is the exception in `AGENTS.md` **Waiting on long-running work**, and this loop is where that poll belongs. ```sh PR=<PR> EXPECTED=1 # the review COUNT to reach; see below DEADLINE=$(( $(date +%s) + 20*60 )) while :; do # Capture first, so a gh failure stops the loop instead of being swallowed by # a pipeline. --slurp cannot be combined with --jq, hence the separate jq. raw=$(gh api --paginate --slurp \ repos/modelcontextprotocol/servers/pulls/$PR/reviews) || { echo "gh api failed ($?); not retrying blind" >&2; exit 1; } n=$(jq '[.[][] | select(.user.login | startswith("copilot-pull-request-reviewer"))] | length' <<<"$raw") || { echo "jq failed ($?) on an unexpected response shape" >&2; exit 1; } case $n in '' | *[!0-9]*) echo "not a count: '$n'" >&2; exit 1 ;; esac [ "$n" -ge "$EXPECTED" ] && { echo "round landed ($n reviews)"; break; } [ "$(date +%s)" -ge "$DEADLINE" ] && { echo "SILENT: no review by the deadline"; break; } sleep 30 done ``` `EXPECTED` is the review **count** to reach, so it is `1` only on the first round; on round two the first round's review is still there, and an existence check would return at once. Match the login with `startswith`: it carries a `[bot]` suffix. `sleep 30` is the remote-API floor `AGENTS.md` sets. **Every step that can fail exits the loop rather than retrying**: piping the count into `awk` would make an auth error read as `0`, and an empty count from a `jq` failure would make the comparison fail forever, so the job sleeps and retries without end. A background task that can never succeed is worse than none, because it looks like progress. Once the body lands, give the inline comments about another 60 seconds: they arrive after it (step 8). ## 8. Respond to the review Judge each finding against **the issue the PR closes**, as `AGENTS.md` **Responding to code reviews** says: fix defects in what the PR added; decline scope creep (pre-existing behavior, new capabilities, hardening the issue did not ask for) with a reason, and file an issue (`/issue-create`) for any that is worth doing on its own. Implementing a suggestion differently is fine. - **Reply to each review comment in its own thread**, saying what was done (with the commit) or why it was declined. That reply is the primary response, and it is not optional: each comment is a thread with its own resolve state, and a reply _in_ it is the only thing a reader of that thread sees. Replying does not resolve the thread; resolving is the reviewer's act. ```sh # The round's comments, by REVIEW id. The unpaginated /reviews listing hides # later rounds behind your own replies, and this endpoint returns 30 per page: # a round you only half fetch is a round you only half answer. gh api --paginate repos/modelcontextprotocol/servers/pulls/<PR>/reviews/<REVIEW_ID>/comments \ --jq '.[]|"\(.id) \(.path):\(.line)\n\(.body)\n"' # Reply into one thread, keyed by the comment id from above. gh api repos/modelcontextprotocol/servers/pulls/<PR>/comments/<COMMENT_ID>/replies \ -f body='Fixed in <sha>: …' ``` - ⚠️ **Then post a PR-level summary of the round, in addition, never instead.** Inline replies go hidden once the fix is pushed, because the threads become outdated, so the summary is what keeps the round readable afterwards. It does not replace the per-thread replies: a summary bullet cannot be connected back to the thread it answers. ```sh # Quoted heredoc, for the same reason as the PR body in step 6: Markdown # backticks inside a double-quoted --body run as commands. gh pr comment <PR> --repo modelcontextprotocol/servers --body-file - <<'EOF' Copilot round <k>: … EOF ``` Per-thread replies (`-f body=…` above) need the same care: single-quote the body, or read it from a file with `-F body=@<file>`. - ⚠️ **Read the "Suppressed comments" block in the review body.** Those findings have no comment id, so no thread to reply into. The PR-level summary is the only place to answer them. - ⚠️ **Inline comments lag the review body.** The body's "generated N comments" count lands first. Fetch by review id, and reconcile the count before answering. Push the fixes (signed off, step 3) before requesting the next round. ## 9. Another round, or stop Request another round (step 7) in one of two cases, and no other: - **You pushed a fix** in response to the last round. Raise `EXPECTED` by one. - **The last round was silent** (no review by the poll's deadline). Nothing was
Voir sur GitHub
Ce SKILL.md est tres volumineux, SkillsMP affiche donc ici seulement la premiere section. Voir sur GitHub