The job's script calls jq; on a machine without it the latest-run case
posts error instead of success, failing the gate over a missing test
prerequisite. Copilot round 1 on #5179.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
dispatch-status checked for a newer dispatched run only in its
cancellation branch, so a superseded run whose job had already failed
posted `failure` over the newer run's status. The check now runs first,
in both python.yml and typescript.yml. A behavioral test runs each job's
script with a stub gh. Found by Copilot on #5166.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
A dispatched run cancelled for any reason other than supersession left
its "(dispatched)" status pending forever (Copilot). The result job now
runs under always(): it stays silent only when a newer dispatched run of
the same workflow exists on the commit, and otherwise reports the
cancellation as an error. scripts/version-pr-ci.test.mjs pins each link
of the version-PR CI path, since nothing exercises it before a release.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
An argument ending in a backslash was quoted as "...\\", which the CRT
parser reads as an escaped quote, swallowing the rest of the command line.
Backslash runs before an embedded quote or the closing quote are now doubled,
with tests including a round trip through a reference CRT parser.
Closes#5111
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
The ledger line came after every package's PR list, the Repository section
and GitHub's trailer, so in v1.0.0 it sat below 102 PR lines and was moved
by hand before publishing. assembleNotes() now emits it directly after the
Packages table; the file header, the tests and the release skill's step 5a
list follow.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
Adds scripts/verify-main-source.mjs and the main-source.yml workflow
(pull_request_target into main), which fails unless the PR's head commit
is on origin/v2/main. GitHub rulesets cannot restrict a PR's source
branch, so this check, made required on main, is what enforces the
milestone-merge-only rule in AGENTS.md.
Closes#5119
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
npm publish reads a bare dir/file argument as GitHub owner/repo shorthand and tries to clone it over SSH; the MCP Inspector's first release through the same split publish job failed that way (modelcontextprotocol/inspector#2551). release.yml already passes ./release-artifact/*.tgz; this asserts it, so the ./ cannot be lost in an edit. Reproduced locally with Node 24.21.0's npm: the bare path exits 128 on 'git ls-remote ssh://git@github.com/release-artifact/...', the ./ path dry-run-publishes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
scripts/release-npm-floor.test.mjs reads publish-npm out of release.yml, as release-dist-tag.test.mjs does, and runs the npm floor step with a stub npm on PATH: 11.5.1, 11.6.0, 11.19.0 and 12.0.0 pass, 11.5.0, 11.4.9, 10.9.9 and 9.0.0 fail. It also asserts an exact Node pin whose bundled npm meets the floor, the check ahead of the download and the publish, and no step that installs or runs a fetched package (comment lines ignored). The previous workflow fails all five; loosening the floor to 11.5.0 fails the refusal test (Copilot on #5105 and #5106).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
Copilot round 7 on #4975:
- Remove the dispatch `target_ref` input: a target that predates the
script has no `npm run interface-diff`, so the run failed. The head is
the branch chosen in "Use workflow from".
- A failed base install or build now reports the tail of both stdout and
stderr, so tsc diagnostics are not lost behind an npm footer.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
Copilot round 5 on #4975:
- A result with no boolean hasDifferences is an error, not "unchanged".
- On a probe failure, report the CLI's contextual message ("Base server
probe failed" / "Target probe failed") rather than the bare exception.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
Copilot round 3 on #4975 ("previously missed"):
- Skip the comment job on Dependabot PRs, which are same-repo but get a
read-only token; they keep the summary and artifact like fork PRs.
- Trigger on the root tsconfig.json, which everything's tsconfig extends.
- Spawn only npm through a shell on Windows, with its arguments quoted by
winShellArgs; git needs no shell.
- Silence the expected usage error in the script test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
Copilot round 1 on #4975:
- Check out github.sha rather than github.ref, so a push run diffs the
commit it was triggered for against that event's `before`, not a
branch tip a later push moved.
- When mcp-server-diff prints no JSON report, include its stderr (where
a fatal error goes) in the error, so the reason is not blank.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
Adapt #3260's workflow for v2/main:
- Run the check as `npm run interface-diff` (scripts/interface-diff.mjs),
driving the mcp-server-diff 3.0.0 CLI as an exact-pinned root
devDependency instead of the SHA-pinned v2.3.5 action. The same script
is a new last stage of local:gate, which workflow-gate.test.mjs now
requires, so the gate keeps running every check CI runs.
- Choose the base per event (the PR's base, the commit before a push, the
previous release tag, or a dispatch input) instead of the tool's
default merge-base with origin/main, which is wrong for v2/main PRs.
- Trigger pushes on main and v2/main, PRs on any base, and published
Releases instead of the retired typescript-servers-* tags.
- Run the PR's code only in a read-only job with no persisted
credentials; post the sticky comment from a separate job that holds
pull-requests: write, runs no PR code, and only runs for same-repo PRs.
Fork PRs keep the job summary and the report artifact.
- Give every job a timeout.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
The maintainers accepted the analysis job's residual credential channel on
#5069: every upstream the SDK watch reads is an MCP-org SDK, and those are
trusted, including their generated release notes' outside PR titles. The
workflow note now cites that decision instead of the Inspector's reasoning,
AGENTS.md states the rule, and a test fails on an SDK group outside the
modelcontextprotocol org, so the decision is taken again before one is added.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
Thanks is now a single `## Thanks for helping us improve` list at the end
of the notes, laid out as the Inspector's release notes are, rather than a
subsection per server. The maintainer-written `Credit: @login` convention
is removed: outside-PR authors are added to the draft's Thanks by hand, as
before, and the harvest docs are back to their v2/main wording.
From Copilot's review: refuse a milestone tag that already exists (GitHub
would use its commit, not --merge-sha), and count a rename's source path
when bucketing a PR by server.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
Three scheduled workflows replace Dependabot's PRs, ported from the MCP
Inspector and extended to this repo's second ecosystem:
- dependency-refresh (monthly): one tracking issue covering npm outdated
across the root and workspaces, each Python server's uv lock --upgrade
dry run, and every workflow uses: ref, with SHA pins ranked by their
# vX.Y.Z comment through scripts/lib/action-refs.mjs.
- dependabot-alerts (daily): one issue per vulnerable package per
lockfile, npm and pip, re-checked against v2/main, targeting the highest
patched version of the advisories that apply.
- sdk-watch (nightly): one issue per MCP SDK group behind its registry
(npm @modelcontextprotocol/*, PyPI mcp), with the Inspector's hardened
three-job Claude analysis kept unchanged.
Shared plumbing (the trust rule, milestone pick, scope labels and a writer
whose --dry-run writes nothing) lives in scripts/lib/sweep.mjs, with PEP 440
and uv.lock readers beside it. No sweep writes the board: GITHUB_TOKEN cannot
hold organization projects: write, so issues arrive labeled and milestoned
for issue-triage, and boarding from the workflows waits on a GitHub App
(#5061).
Deletes .github/dependabot.yml, removes the temporary Dependabot exception
from AGENTS.md and adds the issue-driven dependency rules, and adds
docs/ai-software-factory.md.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
Add scripts/release-notes.mjs (npm run release:notes), modeled on the
Inspector's helper and adapted to a seven-package release. It builds the
Packages table at the merge SHA (registry-checked), one section per
published package with its PRs and the community reporters it thanks, a
Repository catch-all so no generated PR is dropped, GitHub's trailer, the
ledger line and any known issues. Outside-PR authors are credited from a
maintainer-written `Credit: @login` line. Preview by default; --draft
creates the draft Release at the merge SHA; publishing stays manual.
The release skill's step 5a uses the helper in place of the shell
recipe, and 5b publishes the draft. The harvest instructions adopt the
Credit line.
Closes#5056
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
Run @modelcontextprotocol/codemod v1-to-v2 (2.3.1) on everything,
filesystem, memory and sequentialthinking, and finish what it leaves:
- Manifests: drop @modelcontextprotocol/sdk; depend on
@modelcontextprotocol/server (plus node and server-legacy in
everything), with client (and core, for everything's skipped task
tests) as devDependencies; zod ^4.2.0; engines node >=20. The root
devDependency for the boot smoke moves to @modelcontextprotocol/client.
- everything: wrap the three raw-shape prompt argsSchemas in z.object();
drop the result schemas from ctx.mcpReq.send(); keep HTTP+SSE on the
frozen server-legacy bridge.
- Experimental tasks are gone in v2 (SEP-2663): remove
simulate-research-query and the two trigger-*-async tools and the
tasks capability, pending #4852. Their tests stay, skipped, behind
typed stand-ins for the removed v1 surface.
- Re-baseline the Part 1 (#4854) assertions that SDK v2's documented
wire changes move: unknown tools reject with -32602, the
"MCP error <code>: " prefix is gone, validation issues read
"field: message", tool schemas declare JSON Schema 2020-12, tools
no longer advertise execution.taskSupport, and the resource-not-found
message reads "Resource not found: <uri>".
- Docs, skills and the smoke script follow; one major changeset per
server records the breaking changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
Copilot review on #5055: scanning every line of the message accepted a
`Signed-off-by:` quoted in the body, or written as the subject, so an
unsigned commit could pass. Read the signoffs with
`%(trailers:key=Signed-off-by,valueonly,unfold)`, git's own trailer
parser (the one `git commit -s` writes for), and add a regression test
for both cases plus a folded trailer that must still pass.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
The probot DCO app never ran on this repo, so nothing failed an unsigned
commit. Replace it with a check we own:
- scripts/verify-dco.mjs (+ tests): every commit in <base>..<head> needs a
Signed-off-by trailer matching its author or committer; merge and
bot-authored commits are exempt. A failure names each commit and the
`git rebase --signoff` repair.
- .github/workflows/dco.yml: runs it on pull requests over
base.sha..head.sha, contents: read only, with a timeout.
- `verify:dco` joins local:gate:stages, over origin/v2/main..HEAD.
- AGENTS.md states the signoff rule; pr-flow step 3 describes the job
instead of the app; quality-gate.md and pre-push-gate cover the stage;
the skills-index table is Prettier-clean.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
everything's build tsconfig had no exclude, so tsc emitted __tests__/**
and vitest.config.ts into dist/ and the package published them. Exclude
them as filesystem does. memory and sequentialthinking excluded only
*.test.ts and so packed __tests__/helpers.js; exclude __tests__/** there
too.
pack:verify now fails an npm tarball that ships a test, test helper or
test-runner config, so this cannot come back in any server.
Closes#4930
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
GitHub resolves expression contexts, property names and job ids without
regard to case, so `SECRETS.X`, `NEEDS.detect.outputs` and
`needs.DETECT.outputs` slipped past the guard. Match `secrets`, the
GITHUB_TOKEN exemption and `needs` case-insensitively, and resolve every job
id (in `needs:` and in expressions) to its key in `jobs:` in any case.
Closes#4934
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
fetch now refuses loopback addresses by default, and the boot smoke serves
fetch's page from 127.0.0.1, so the smoke opts out of the guard.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
coverage.py writes num_branches: 0 for every file when branch measurement
is off, which the per-file check counted as 100% on branches, so removing
`branch = true` would have let every file pass that dimension. Require
meta.branch_coverage === true and fail with a message pointing at
[tool.coverage.run] branch = true. Regression tests for false, a missing
flag and a missing meta.
Signed-off-by: cliffhall <cliff@futurescale.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add `npm run coverage:py` (scripts/coverage-py.mjs): for each Python
server, a locked sync, then `uv run --frozen pytest --cov
--cov-report=term-missing --cov-report=json`, then a per-file check of
coverage.json (scripts/lib/py-coverage.mjs) that fails any file below 90%
on lines or on branches. coverage.py's fail_under is global, hence the
script. Discovery, server selection and the carry-on-after-a-failure shape
are shared with validate-py.mjs.
Wire it in as a parallel `coverage` matrix job in python.yml and as a
stage of local:gate:stages after validate:py, and document it in
docs/quality-gate.md, AGENTS.md (the Python half of the coverage rule),
CONTRIBUTING.md and the pre-push-gate and testing skills.
Signed-off-by: cliffhall <cliff@futurescale.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds a parallel `coverage` job to typescript.yml (one leg per TypeScript
server, beside `validate`, as the Inspector's CI does) and a `coverage` stage
to `local:gate:stages` right after `validate`. The workflow guard derives the
stage from the new CI job, and its stage list now pins `coverage` by name.
Documents the stage in docs/quality-gate.md, the TypeScript half of the
coverage rule in AGENTS.md, how to diagnose a red coverage stage in the
pre-push-gate skill, and the in-process harness and coverage gate in the
testing skill. The docs describe the state after the four per-server
coverage PRs for #4854 land, which add the 90/90/90/90 per-file thresholds.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
Express 5 passes a listen error to the same callback it calls on success, and
sse.ts had no error listener, so on EADDRINUSE it printed its success line
while bound to nothing. Both HTTP transports now bind through listenOrExit
(transports/listen.ts), which prints the listening line only for a real bind
and otherwise names the port and exits 1. That also removes the false
listening line Streamable HTTP printed ahead of its error.
Closes#4923
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
- The release skill targets the merge PR's merge commit by SHA for the notes
and the Release, so the tag lands on the tree the ledger verified even if
main has moved since.
- release.yml uses queue: max, so a waiting release run is kept rather than
replaced by a later one.
- verify:action-pins counts any use of the needs context except a job's
result as reading that job's outputs, including toJSON(needs).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
- verify:action-pins counts a job as credentialed when a credentialed job
reads its outputs. release.yml's detect-packages supplies the name and
version an artifact must match, so it is now held to the pin too.
- release.yml runs one release at a time and never cancels one, so two runs
cannot each read the old latest and publish out of order.
- The release skill's Python audit counts a server as audited only when
pip-audit's output holds a verdict, and lists changesets recursively.
- The guard's failure message reads as sentences.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>