## Summary
- Bumps `golang.org/x/net` from v0.53.0 to v0.55.0 to fix
[GO-2026-5026](https://pkg.go.dev/vuln/GO-2026-5026) (failure to reject
ASCII-only Punycode-encoded labels in `golang.org/x/net/idna`).
- govulncheck flagged the vuln via `auth.DefaultHTTPKeyFetcher.FetchKey`
→ `http.Client.Do` → `idna.ToASCII` at
`internal/api/handlers/v0/auth/http.go:191`.
- Also brings in associated `x/crypto`, `x/sys`, `x/text` bumps via `go
mod tidy`.
This unblocks open dependabot PRs (e.g. #1298) that are failing CI on
the same govulncheck finding despite not touching the root module.
## Test plan
- [x] `go build ./...`
- [x] `govulncheck` no longer reports GO-2026-5026 locally
- [ ] CI passes
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bumps the actions group with 1 update:
[goreleaser/goreleaser-action](https://github.com/goreleaser/goreleaser-action).
Updates `goreleaser/goreleaser-action` from 7.2.1 to 7.2.2
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/goreleaser/goreleaser-action/releases">goreleaser/goreleaser-action's
releases</a>.</em></p>
<blockquote>
<h2>v7.2.2</h2>
<h2>What's Changed</h2>
<ul>
<li>ci(deps): bump the actions group with 3 updates by <a
href="https://github.com/dependabot"><code>@dependabot</code></a>[bot]
in <a
href="https://redirect.github.com/goreleaser/goreleaser-action/pull/560">goreleaser/goreleaser-action#560</a></li>
<li>fix: nightly resolution to select newest published release by <a
href="https://github.com/Copilot"><code>@Copilot</code></a> in <a
href="https://redirect.github.com/goreleaser/goreleaser-action/pull/562">goreleaser/goreleaser-action#562</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/Copilot"><code>@Copilot</code></a> made
their first contribution in <a
href="https://redirect.github.com/goreleaser/goreleaser-action/pull/562">goreleaser/goreleaser-action#562</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/goreleaser/goreleaser-action/compare/v7...v7.2.2">https://github.com/goreleaser/goreleaser-action/compare/v7...v7.2.2</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="5daf1e915a"><code>5daf1e9</code></a>
fix: nightly resolution to select newest published release (<a
href="https://redirect.github.com/goreleaser/goreleaser-action/issues/562">#562</a>)</li>
<li><a
href="5cc7ebb73d"><code>5cc7ebb</code></a>
ci: update actions</li>
<li><a
href="702f5f91c9"><code>702f5f9</code></a>
ci(deps): bump the actions group with 3 updates (<a
href="https://redirect.github.com/goreleaser/goreleaser-action/issues/560">#560</a>)</li>
<li>See full diff in <a
href="1a80836c5c...5daf1e915a">compare
view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps
[github.com/pulumi/pulumi/sdk/v3](https://github.com/pulumi/pulumi) from
3.238.0 to 3.242.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/pulumi/pulumi/releases">github.com/pulumi/pulumi/sdk/v3's
releases</a>.</em></p>
<blockquote>
<h2>v3.242.0</h2>
<h2>3.242.0 (2026-05-19)</h2>
<h3>Features</h3>
<ul>
<li>
<p>[cli] Add a <code>pulumi</code> package for npx support</p>
</li>
<li>
<p>[cli] Add the <code>pulumi org member edit</code> command
<a
href="https://redirect.github.com/pulumi/pulumi/pull/23235">#23235</a></p>
</li>
<li>
<p>[cli] Add the <code>pulumi org member remove</code> command
<a
href="https://redirect.github.com/pulumi/pulumi/pull/23237">#23237</a></p>
</li>
<li>
<p>[cli/cloud] Add <code>pulumi deployment get</code> to retrieve
details for a specific deployment
<a
href="https://redirect.github.com/pulumi/pulumi/pull/23238">#23238</a></p>
</li>
<li>
<p>[cli/cloud] Add <code>pulumi insights account scan get
<account> <scan-id></code> to show the full workflow run for
a single Insights scan
<a
href="https://redirect.github.com/pulumi/pulumi/pull/23255">#23255</a></p>
</li>
<li>
<p>[cli/cloud] Add <code>pulumi insights account scan list
<account></code> to discover recent scan IDs to feed into
<code>pulumi insights account scan log</code>
<a
href="https://redirect.github.com/pulumi/pulumi/pull/23255">#23255</a></p>
</li>
<li>
<p>[cli/deployment] Add dedicated flags for each deployment setting
<a
href="https://redirect.github.com/pulumi/pulumi/pull/23236">#23236</a></p>
</li>
<li>
<p>[cli/do] Add the start of <code>pulumi do</code>
<a
href="https://redirect.github.com/pulumi/pulumi/pull/23176">#23176</a></p>
</li>
<li>
<p>[cli/neo] Add <code>--print</code>/<code>-p</code> to <code>pulumi
neo</code> to run a single prompt non-interactively and print the
agent's final response to stdout
<a
href="https://redirect.github.com/pulumi/pulumi/pull/23245">#23245</a></p>
</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li>
<p>[cli/cloud] Fix <code>pulumi insights account scan log --all</code>
to follow the server's pagination cursor through the end of the log, and
render <code>--job/--step</code> mode as structured lines instead of an
empty raw-string blob
<a
href="https://redirect.github.com/pulumi/pulumi/pull/23256">#23256</a></p>
</li>
<li>
<p>[sdk] Close gzip.Writer in archiveTarGZIP to produce valid tar.gz
output
<a
href="https://redirect.github.com/pulumi/pulumi/pull/23240">#23240</a></p>
</li>
<li>
<p>[sdkgen/python] Fix usage of ArgsDict types in typed dictionaries
<a
href="https://redirect.github.com/pulumi/pulumi/pull/23253">#23253</a></p>
</li>
</ul>
<h2>v3.241.0</h2>
<h2>3.241.0 (2026-05-18)</h2>
<h3>Features</h3>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/pulumi/pulumi/blob/master/CHANGELOG.md">github.com/pulumi/pulumi/sdk/v3's
changelog</a>.</em></p>
<blockquote>
<h2>3.242.0 (2026-05-19)</h2>
<h3>Features</h3>
<ul>
<li>
<p>[cli] Add a <code>pulumi</code> package for npx support</p>
</li>
<li>
<p>[cli] Add the <code>pulumi org member edit</code> command
<a
href="https://redirect.github.com/pulumi/pulumi/pull/23235">#23235</a></p>
</li>
<li>
<p>[cli] Add the <code>pulumi org member remove</code> command
<a
href="https://redirect.github.com/pulumi/pulumi/pull/23237">#23237</a></p>
</li>
<li>
<p>[cli/cloud] Add <code>pulumi deployment get</code> to retrieve
details for a specific deployment
<a
href="https://redirect.github.com/pulumi/pulumi/pull/23238">#23238</a></p>
</li>
<li>
<p>[cli/cloud] Add <code>pulumi insights account scan get
<account> <scan-id></code> to show the full workflow run for
a single Insights scan
<a
href="https://redirect.github.com/pulumi/pulumi/pull/23255">#23255</a></p>
</li>
<li>
<p>[cli/cloud] Add <code>pulumi insights account scan list
<account></code> to discover recent scan IDs to feed into
<code>pulumi insights account scan log</code>
<a
href="https://redirect.github.com/pulumi/pulumi/pull/23255">#23255</a></p>
</li>
<li>
<p>[cli/deployment] Add dedicated flags for each deployment setting
<a
href="https://redirect.github.com/pulumi/pulumi/pull/23236">#23236</a></p>
</li>
<li>
<p>[cli/do] Add the start of <code>pulumi do</code>
<a
href="https://redirect.github.com/pulumi/pulumi/pull/23176">#23176</a></p>
</li>
<li>
<p>[cli/neo] Add <code>--print</code>/<code>-p</code> to <code>pulumi
neo</code> to run a single prompt non-interactively and print the
agent's final response to stdout
<a
href="https://redirect.github.com/pulumi/pulumi/pull/23245">#23245</a></p>
</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li>
<p>[cli/cloud] Fix <code>pulumi insights account scan log --all</code>
to follow the server's pagination cursor through the end of the log, and
render <code>--job/--step</code> mode as structured lines instead of an
empty raw-string blob
<a
href="https://redirect.github.com/pulumi/pulumi/pull/23256">#23256</a></p>
</li>
<li>
<p>[sdk] Close gzip.Writer in archiveTarGZIP to produce valid tar.gz
output
<a
href="https://redirect.github.com/pulumi/pulumi/pull/23240">#23240</a></p>
</li>
<li>
<p>[sdkgen/python] Fix usage of ArgsDict types in typed dictionaries
<a
href="https://redirect.github.com/pulumi/pulumi/pull/23253">#23253</a></p>
</li>
</ul>
<h2>3.241.0 (2026-05-18)</h2>
<h3>Features</h3>
<ul>
<li>[cli] Add <code>--output json</code> to <code>pulumi preview</code>
for a structured JSON summary of the operation result
<a
href="https://redirect.github.com/pulumi/pulumi/pull/22927">#22927</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="f4db8b5f55"><code>f4db8b5</code></a>
Upgrade Pulumi HCL to v0.3.2 (<a
href="https://redirect.github.com/pulumi/pulumi/issues/23261">#23261</a>)</li>
<li><a
href="cd682b849c"><code>cd682b8</code></a>
cli: add a <code>pulumi</code> package for npx support (<a
href="https://redirect.github.com/pulumi/pulumi/issues/23145">#23145</a>)</li>
<li><a
href="e8364996b7"><code>e836499</code></a>
cli/neo: set AI_AGENT=neo for shell-tool child processes (<a
href="https://redirect.github.com/pulumi/pulumi/issues/23251">#23251</a>)</li>
<li><a
href="2560b9015f"><code>2560b90</code></a>
unhide <code>org usage</code> command tree (<a
href="https://redirect.github.com/pulumi/pulumi/issues/23259">#23259</a>)</li>
<li><a
href="4588160dba"><code>4588160</code></a>
unhide <code>org member</code> commands (<a
href="https://redirect.github.com/pulumi/pulumi/issues/23257">#23257</a>)</li>
<li><a
href="f821c0dfc7"><code>f821c0d</code></a>
unhide <code>org role</code> commands (<a
href="https://redirect.github.com/pulumi/pulumi/issues/23258">#23258</a>)</li>
<li><a
href="9356340a67"><code>9356340</code></a>
docs(contributing): minor grammar fixes (<code>PR</code> ->
<code>PRs</code>, <code>Here's</code> -> `Here are...</li>
<li><a
href="3779d16c8b"><code>3779d16</code></a>
unhide <code>org member</code> (<a
href="https://redirect.github.com/pulumi/pulumi/issues/23241">#23241</a>)</li>
<li><a
href="c28bf15134"><code>c28bf15</code></a>
unhide a couple <code>stack</code> commands (<a
href="https://redirect.github.com/pulumi/pulumi/issues/23243">#23243</a>)</li>
<li><a
href="90caf84946"><code>90caf84</code></a>
cli/cloud: fix <code>pulumi insights account scan log</code> pagination
(<a
href="https://redirect.github.com/pulumi/pulumi/issues/23256">#23256</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/pulumi/pulumi/compare/v3.238.0...v3.242.0">compare
view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
## Summary
- Dependency update PRs were arriving daily across gomod (root +
`/deploy`), github-actions, and docker — creating significant review
noise.
- Switch all four update entries in `.github/dependabot.yml` from
`interval: daily` to `interval: weekly` so they batch on a weekly
cadence (Dependabot defaults to Mondays).
## Test plan
- [ ] Confirm `.github/dependabot.yml` parses correctly (Dependabot will
report a config error on the repo if not)
- [ ] Observe that new dependency PRs stop appearing daily after merge
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
## Summary
When the OCI validator's anonymous manifest fetch returns HTTP 429,
return a retryable error instead of `nil`. This aligns OCI with the
fail-closed behavior of the npm / PyPI / NuGet / MCPB validators, and
ensures the `io.modelcontextprotocol.server.name` label-match always
runs before a publish is accepted.
## Test plan
- [x] `go test ./internal/validators/registries/ -run TestValidateOCI`
passes locally
- [x] `gofmt` + `golangci-lint` clean on the changed file
- [ ] CI green
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bumps [github.com/go-git/go-git/v5](https://github.com/go-git/go-git)
from 5.18.0 to 5.19.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/go-git/go-git/releases">github.com/go-git/go-git/v5's
releases</a>.</em></p>
<blockquote>
<h2>v5.19.0</h2>
<h2>What's Changed</h2>
<ul>
<li>build: Update module github.com/go-git/go-git/v5 to v5.18.0
[SECURITY] (releases/v5.x) by <a
href="https://github.com/go-git-renovate"><code>@go-git-renovate</code></a>[bot]
in <a
href="https://redirect.github.com/go-git/go-git/pull/2010">go-git/go-git#2010</a></li>
<li>v5: Bump sha1cd and go-billy by <a
href="https://github.com/pjbgf"><code>@pjbgf</code></a> in <a
href="https://redirect.github.com/go-git/go-git/pull/2060">go-git/go-git#2060</a></li>
<li>v5: Align object encoding with upstream by <a
href="https://github.com/pjbgf"><code>@pjbgf</code></a> in <a
href="https://redirect.github.com/go-git/go-git/pull/2065">go-git/go-git#2065</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/go-git/go-git/compare/v5.18.0...v5.19.0">https://github.com/go-git/go-git/compare/v5.18.0...v5.19.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="bc930f4cbe"><code>bc930f4</code></a>
Merge pull request <a
href="https://redirect.github.com/go-git/go-git/issues/2065">#2065</a>
from go-git/commit-v5</li>
<li><a
href="d315264343"><code>d315264</code></a>
plumbing: object, Reset object before decode</li>
<li><a
href="6e1d34890a"><code>6e1d348</code></a>
plumbing: object, Align Tree handling with upstream</li>
<li><a
href="e134ba34cf"><code>e134ba3</code></a>
tests: Skip double checks in Git v2.11</li>
<li><a
href="1971422f6b"><code>1971422</code></a>
tests: Add git conformance tests for signing verification</li>
<li><a
href="a387aa8857"><code>a387aa8</code></a>
plumbing: object, Add ErrMalformedTag</li>
<li><a
href="f415670d90"><code>f415670</code></a>
plumbing: object, Decode Tag headers via a state machine</li>
<li><a
href="5b0cd38a62"><code>5b0cd38</code></a>
plumbing: object, Reject multi-signature commits at Verify</li>
<li><a
href="fe8ed6223a"><code>fe8ed62</code></a>
plumbing: object, Align Tag.EncodeWithoutSignature with Commit</li>
<li><a
href="98e337d5bd"><code>98e337d</code></a>
plumbing: object, Add support for Tag.SignatureSHA256</li>
<li>Additional commits viewable in <a
href="https://github.com/go-git/go-git/compare/v5.18.0...v5.19.0">compare
view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/modelcontextprotocol/registry/network/alerts).
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
## Summary
`complete.md` documents three flags for `mcp-publisher publish` that the
CLI does not actually parse:
- `--file=PATH` — Path to server.json
- `--registry=URL` — Registry URL override
- `--dry-run` — Validate without publishing
Reading
[`cmd/publisher/commands/publish.go`](https://github.com/modelcontextprotocol/registry/blob/main/cmd/publisher/commands/publish.go)
and
[`cmd/publisher/main.go`](https://github.com/modelcontextprotocol/registry/blob/main/cmd/publisher/main.go)
confirms the actual interface is a single positional `PATH` argument
(default `./server.json`):
```go
serverFile := \"server.json\"
if len(args) > 0 && !strings.HasPrefix(args[0], \"-\") {
serverFile = args[0]
}
```
Registry URL is bound at login time via the saved token, and \"validate
without publishing\" is a separate `mcp-publisher validate` subcommand.
The bug user in #767 hit \"server.json not found\" precisely because
`--file .registry/server.json` is filtered out by the
`HasPrefix(args[0], \"-\")` check, leaving the default lookup in place.
## Background
The canonical reference at
[`docs/reference/cli/commands.md`](https://github.com/modelcontextprotocol/registry/blob/main/docs/reference/cli/commands.md)
was already updated to the correct positional shape. `complete.md` (the
concatenated single-file mirror of `docs/` used for LLM/quickstart
consumption) had drifted out of sync — these were the only remaining
`--file` references in the repo outside of test fixtures.
Per the discussion on the prior flag-implementation attempt in #771,
maintainers preferred fixing the documentation over adding the
unimplemented flags:
> @domdomegg: I'd lean towards removing the options from the docs.
This PR closes that loop on the last stale doc surface.
## Changes
`complete.md`: the `mcp-publisher publish` block now matches
`docs/reference/cli/commands.md` line-for-line (positional `PATH`, no
spurious flags, no dry-run example).
```diff
-mcp-publisher publish [options]
+mcp-publisher publish [PATH]
-- \`--file=PATH\` - Path to server.json (default: \`./server.json\`)
-- \`--registry=URL\` - Registry URL override
-- \`--dry-run\` - Validate without publishing
+- \`PATH\` - Path to server.json (default: \`./server.json\`)
-# Dry run validation
-mcp-publisher publish --dry-run
-
# Custom file location
-mcp-publisher publish --file=./config/server.json
+mcp-publisher publish ./config/server.json
```
Closes#767.
## Out of scope
- `cmd/publisher/README.md:17` still shows `./bin/mcp-publisher publish
--registry=http://localhost:8080` in the contributor quickstart. The
flag is silently ignored at runtime (caught by the same
`HasPrefix(\"-\")` filter). Happy to fold that fix into this PR or send
a follow-up — flagging here in case a maintainer prefers one option.
- `complete.md` is missing `mcp-publisher validate` and `mcp-publisher
status` sections that exist in `docs/reference/cli/commands.md`. Out of
scope for this single-concern PR; if there's interest, a broader sync
would be a separate PR.
## Test plan
- [x] `grep -r -- '--file' .` outside test fixtures returns 0 hits after
the change
- [x] `grep -r 'mcp-publisher publish --' docs/ complete.md` shows only
legitimate flags (`--registry` on `login`)
- [x] No code changes — `make check` not exercised since this is
doc-only
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: d 🔹 <258577966+voidborne-d@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bumps [golang.org/x/mod](https://github.com/golang/mod) from 0.35.0 to
0.36.0.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="643da9ba74"><code>643da9b</code></a>
go.mod: update golang.org/x dependencies</li>
<li><a
href="ccc3cdf529"><code>ccc3cdf</code></a>
zip: include 'but content has correct sum' note in TestVCS</li>
<li><a
href="ab30318032"><code>ab30318</code></a>
zip: update zip hashes for new flate compression</li>
<li>See full diff in <a
href="https://github.com/golang/mod/compare/v0.35.0...v0.36.0">compare
view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps
[github.com/pulumi/pulumi/sdk/v3](https://github.com/pulumi/pulumi) from
3.236.0 to 3.237.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/pulumi/pulumi/releases">github.com/pulumi/pulumi/sdk/v3's
releases</a>.</em></p>
<blockquote>
<h2>v3.237.0</h2>
<h2>3.237.0 (2026-05-08)</h2>
<h3>Features</h3>
<ul>
<li>
<p>[cli] Include the running command name and detected AI agent (when
present) in the User-Agent header on Pulumi Cloud API requests
<a
href="https://redirect.github.com/pulumi/pulumi/pull/22908">#22908</a></p>
</li>
<li>
<p>[engine] Include <code>result</code> on the summary engine event
<a
href="https://redirect.github.com/pulumi/pulumi/pull/22883">#22883</a></p>
</li>
<li>
<p>[sdkgen] Eagerly error on schemas with unconstructable types
<a
href="https://redirect.github.com/pulumi/pulumi/pull/22890">#22890</a></p>
</li>
<li>
<p>[cli/cloud] Auto-fill <code>lang</code> and <code>os</code> query
parameters on <code>pulumi cloud api</code> GET/HEAD requests when the
matched OpenAPI operation declares them and the caller hasn't supplied
them
<a
href="https://redirect.github.com/pulumi/pulumi/pull/22726">#22726</a></p>
</li>
<li>
<p>[cli/package] Add <code>pulumi package new</code> to bootstrap a
Pulumi package from a template
<a
href="https://redirect.github.com/pulumi/pulumi/pull/22837">#22837</a></p>
</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li>
<p>[cli] Add blank-line gaps between <code>pulumi neo</code> TUI
conversation blocks
<a
href="https://redirect.github.com/pulumi/pulumi/pull/22846">#22846</a></p>
</li>
<li>
<p>[cli/import] Preserve <code>__</code>-prefixed keys when generating
PCL for imported resource state, so provider-defined payloads round-trip
correctly
<a
href="https://redirect.github.com/pulumi/pulumi/pull/22856">#22856</a></p>
</li>
<li>
<p>[cli/neo] Render <code>ux__ask_user</code> clarifying questions as
questions instead of approval prompts
<a
href="https://redirect.github.com/pulumi/pulumi/pull/22862">#22862</a></p>
</li>
<li>
<p>[cli/neo] Fix a panic when cancelling a <code>pulumi neo</code>
session
<a
href="https://redirect.github.com/pulumi/pulumi/pull/22898">#22898</a></p>
</li>
<li>
<p>[cli/neo] Render every assistant message in the TUI scrollback so
multi-turn commentary no longer disappears between tool calls</p>
</li>
<li>
<p>[cli/neo] Return the bare stack name and canonical project name from
<code>pulumi_preview</code> and <code>pulumi_up</code> tool results
instead of echoing the raw input
<a
href="https://redirect.github.com/pulumi/pulumi/pull/22891">#22891</a></p>
</li>
<li>
<p>[codegen/pcl] Stop reporting spurious circular references when an
<code>ignoreChanges</code>, <code>hideDiffs</code>,
<code>replaceOnChanges</code>, or <code>additionalSecretOutputs</code>
entry shares a name with a top-level node
<a
href="https://redirect.github.com/pulumi/pulumi/pull/22916">#22916</a></p>
</li>
<li>
<p>[programgen/pcl] Fix PCL binder panic when a conditional mixes a
Promise-typed branch with a try() branch
<a
href="https://redirect.github.com/pulumi/pulumi/pull/22907">#22907</a></p>
</li>
<li>
<p>[sdk/python] Support <code>NotRequired</code>, <code>Required</code>
and <code>total=False</code> in TypedDicts for component resource arg
types
<a
href="https://redirect.github.com/pulumi/pulumi/pull/22858">#22858</a></p>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/pulumi/pulumi/blob/master/CHANGELOG.md">github.com/pulumi/pulumi/sdk/v3's
changelog</a>.</em></p>
<blockquote>
<h2>3.237.0 (2026-05-08)</h2>
<h3>Features</h3>
<ul>
<li>
<p>[cli] Include the running command name and detected AI agent (when
present) in the User-Agent header on Pulumi Cloud API requests
<a
href="https://redirect.github.com/pulumi/pulumi/pull/22908">#22908</a></p>
</li>
<li>
<p>[engine] Include <code>result</code> on the summary engine event
<a
href="https://redirect.github.com/pulumi/pulumi/pull/22883">#22883</a></p>
</li>
<li>
<p>[sdkgen] Eagerly error on schemas with unconstructable types
<a
href="https://redirect.github.com/pulumi/pulumi/pull/22890">#22890</a></p>
</li>
<li>
<p>[cli/cloud] Auto-fill <code>lang</code> and <code>os</code> query
parameters on <code>pulumi cloud api</code> GET/HEAD requests when the
matched OpenAPI operation declares them and the caller hasn't supplied
them
<a
href="https://redirect.github.com/pulumi/pulumi/pull/22726">#22726</a></p>
</li>
<li>
<p>[cli/package] Add <code>pulumi package new</code> to bootstrap a
Pulumi package from a template
<a
href="https://redirect.github.com/pulumi/pulumi/pull/22837">#22837</a></p>
</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li>
<p>[cli] Add blank-line gaps between <code>pulumi neo</code> TUI
conversation blocks
<a
href="https://redirect.github.com/pulumi/pulumi/pull/22846">#22846</a></p>
</li>
<li>
<p>[cli/import] Preserve <code>__</code>-prefixed keys when generating
PCL for imported resource state, so provider-defined payloads round-trip
correctly
<a
href="https://redirect.github.com/pulumi/pulumi/pull/22856">#22856</a></p>
</li>
<li>
<p>[cli/neo] Render <code>ux__ask_user</code> clarifying questions as
questions instead of approval prompts
<a
href="https://redirect.github.com/pulumi/pulumi/pull/22862">#22862</a></p>
</li>
<li>
<p>[cli/neo] Fix a panic when cancelling a <code>pulumi neo</code>
session
<a
href="https://redirect.github.com/pulumi/pulumi/pull/22898">#22898</a></p>
</li>
<li>
<p>[cli/neo] Render every assistant message in the TUI scrollback so
multi-turn commentary no longer disappears between tool calls</p>
</li>
<li>
<p>[cli/neo] Return the bare stack name and canonical project name from
<code>pulumi_preview</code> and <code>pulumi_up</code> tool results
instead of echoing the raw input
<a
href="https://redirect.github.com/pulumi/pulumi/pull/22891">#22891</a></p>
</li>
<li>
<p>[codegen/pcl] Stop reporting spurious circular references when an
<code>ignoreChanges</code>, <code>hideDiffs</code>,
<code>replaceOnChanges</code>, or <code>additionalSecretOutputs</code>
entry shares a name with a top-level node
<a
href="https://redirect.github.com/pulumi/pulumi/pull/22916">#22916</a></p>
</li>
<li>
<p>[programgen/pcl] Fix PCL binder panic when a conditional mixes a
Promise-typed branch with a try() branch
<a
href="https://redirect.github.com/pulumi/pulumi/pull/22907">#22907</a></p>
</li>
<li>
<p>[sdk/python] Support <code>NotRequired</code>, <code>Required</code>
and <code>total=False</code> in TypedDicts for component resource arg
types
<a
href="https://redirect.github.com/pulumi/pulumi/pull/22858">#22858</a></p>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="dc35aff826"><code>dc35aff</code></a>
only don't run CI if changelog/config.yaml changes (<a
href="https://redirect.github.com/pulumi/pulumi/issues/22937">#22937</a>)</li>
<li><a
href="a38b7dce73"><code>a38b7dc</code></a>
cli/neo: keep prior streaming text when the agent emits a new turn (<a
href="https://redirect.github.com/pulumi/pulumi/issues/22897">#22897</a>)</li>
<li><a
href="6d84bb5ed7"><code>6d84bb5</code></a>
add continue-on-error to upload code coverage and upload codecov test
results...</li>
<li><a
href="8377d1277e"><code>8377d12</code></a>
Eagerly error on schemas with unconstructable types (<a
href="https://redirect.github.com/pulumi/pulumi/issues/22890">#22890</a>)</li>
<li><a
href="9d209a07ad"><code>9d209a0</code></a>
bump java and yaml dependencies (<a
href="https://redirect.github.com/pulumi/pulumi/issues/22932">#22932</a>)</li>
<li><a
href="dd82f69c24"><code>dd82f69</code></a>
LocalWorkspace.nested_config: use a new stack per test (<a
href="https://redirect.github.com/pulumi/pulumi/issues/22926">#22926</a>)</li>
<li><a
href="c5180568c9"><code>c518056</code></a>
Fix flaky TestFuzz: targeted update with refresh + deleted parent (<a
href="https://redirect.github.com/pulumi/pulumi/issues/22925">#22925</a>)</li>
<li><a
href="46486da224"><code>46486da</code></a>
Thread workspace.Context to ProviderFromSource (<a
href="https://redirect.github.com/pulumi/pulumi/issues/22880">#22880</a>)</li>
<li><a
href="ad42ef389e"><code>ad42ef3</code></a>
increase nodejs automation API timeout (<a
href="https://redirect.github.com/pulumi/pulumi/issues/22920">#22920</a>)</li>
<li><a
href="0b8a78bd77"><code>0b8a78b</code></a>
Test on Node.js 26 and drop 20 (<a
href="https://redirect.github.com/pulumi/pulumi/issues/22872">#22872</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/pulumi/pulumi/compare/v3.236.0...v3.237.0">compare
view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps the actions group with 1 update:
[sigstore/cosign-installer](https://github.com/sigstore/cosign-installer).
Updates `sigstore/cosign-installer` from 4.1.1 to 4.1.2
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/sigstore/cosign-installer/releases">sigstore/cosign-installer's
releases</a>.</em></p>
<blockquote>
<h2>v4.1.2</h2>
<h2>What's Changed</h2>
<ul>
<li>Bump cosign to 3.0.6 in <a
href="https://redirect.github.com/sigstore/cosign-installer/pull/232">sigstore/cosign-installer#232</a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="6f9f177880"><code>6f9f177</code></a>
Bump cosign to 3.0.6 (<a
href="https://redirect.github.com/sigstore/cosign-installer/issues/232">#232</a>)</li>
<li><a
href="b5e753ae2d"><code>b5e753a</code></a>
Bump actions/github-script from 8.0.0 to 9.0.0 (<a
href="https://redirect.github.com/sigstore/cosign-installer/issues/230">#230</a>)</li>
<li><a
href="115e4ce455"><code>115e4ce</code></a>
Bump actions/setup-go from 6.3.0 to 6.4.0 (<a
href="https://redirect.github.com/sigstore/cosign-installer/issues/226">#226</a>)</li>
<li>See full diff in <a
href="cad07c2e89...6f9f177880">compare
view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Radoslav Dimitrov <radoslav@stacklok.com>
## Summary
Bumps `golang.org/x/net` from `v0.52.0` to `v0.53.0` to address
`GO-2026-4918` (HTTP/2 transport infinite loop on bad
`SETTINGS_MAX_FRAME_SIZE`). `go mod tidy` also pulls
`golang.org/x/crypto` to v0.50.0 and `golang.org/x/text` to v0.36.0.
## Why now
`govulncheck` is failing on `main` and every open Dependabot PR (#1263,
#1264, #1265) after the 2026-05 Go security release. The findings split
into:
- **One we control** — `golang.org/x/net@v0.52.0` → fixed in `v0.53.0`.
Addressed here.
- **Six stdlib findings** (`GO-2026-4986`, `4982`, `4980`, `4977`,
`4971`, and the stdlib half of `4918`) — all fixed in `go1.26.3`. We
deliberately do **not** pin a Go patch in the workflow: `go-version:
'stable'` will pick up `1.26.3` automatically once the
`actions/go-versions` manifest catches up, with no workflow maintenance.
## Test plan
- [x] `go build ./...`
- [x] `go vet ./...`
- [x] `GOTOOLCHAIN=go1.26.3 govulncheck ./...` → "No vulnerabilities
found."
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
## Summary
The DNS and HTTP authentication guides currently run `openssl genpkey
-algorithm Ed25519 -out key.pem` without caveat. macOS ships LibreSSL as
the system `openssl` binary, and LibreSSL doesn't implement Ed25519 in
`genpkey` — so the very first command in the Ed25519 codepath errors out
with:
```
Algorithm Ed25519 not found
```
A first-time integrator on macOS hits this immediately, with no path to
a fix from the docs alone. Cost me ~5 minutes earlier today before I
figured out I needed `brew install openssl@3` and an explicit binary
path.
This PR adds a `<Note>` callout to the **DNS Authentication** section
right above the Ed25519 codepath, and a shorter cross-reference Note in
the **HTTP Authentication** section since the same `openssl genpkey
-algorithm Ed25519` commands appear there. The ECDSA P-384 codepath
works on LibreSSL, so it's untouched.
## Test plan
- [x] Verified locally that `/opt/homebrew/opt/openssl@3/bin/openssl
genpkey -algorithm Ed25519 -out key.pem` works on macOS Darwin 25.4.0
- [x] Verified the system `/usr/bin/openssl genpkey -algorithm Ed25519
-out key.pem` fails with `Algorithm Ed25519 not found` on the same
machine (LibreSSL 3.3.6)
- [x] Verified the published `ai.ravenmcp/raven-mcp` flow end-to-end
with the OpenSSL 3 binary
- [ ] Markdown rendering of the `<Note>` callouts — relying on the
existing `<Note>` pattern in this file (line 6) to validate the syntax
Co-authored-by: Radoslav Dimitrov <radoslav@stacklok.com>
## Summary
- Add a new **Registry Implementations** section to
`docs/community-projects.md` for self-hostable servers that implement
the MCP Registry API spec.
- Move existing **ToolSDK MCP Registry** entry from "Other" into the new
section.
- Add **ToolHive Registry Server**
(https://github.com/stacklok/toolhive-registry-server) to the new
section.
## Test plan
- [x] `docs/community-projects.md` renders correctly (markdown only, no
build step).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
## Summary
- Bump `mcp-registry:imageTag` from `1.7.7` → `1.7.8` in
`deploy/Pulumi.gcpProd.yaml`
- v1.7.8 ships the metrics fix that stops counting client-cancelled
requests as server errors (#1255). Under scraper-driven load, clients
with short timeouts close the socket while PG is still iterating; the
handler converts `context.Canceled` to a 500 and the metric middleware
was bumping `mcp_registry_http_errors_total` even though NGINX recorded
499 and never delivered a response. This is what's been firing the daily
~17:00 UTC `Availability dropped below 95%` alert.
- Also includes a CI-only `pulumi/actions` 6.6.1 → 7.0.0 bump (#1254).
No registry runtime impact.
- Staging is on `4e14707` (the v1.7.8 commit) and
`staging.registry.modelcontextprotocol.io/v0.1/version` reports it.
## Test plan
- [ ] Confirm release workflow's `ko-push` job has published
`ghcr.io/modelcontextprotocol/registry:1.7.8` before merging
- [ ] Watch deploy-production roll the registry pods to `1.7.8`
- [ ] Hit `https://registry.modelcontextprotocol.io/v0.1/version` and
confirm `git_commit` matches `4e147072f0`
- [ ] Through the next ~17:00 UTC scraper burst, watch in Prometheus:
- `mcp_registry_http_errors_total{status_code="500",
path="/v0.1/servers"}` should stay flat
- `mcp_registry_http_requests_total{status_code="499",
path="/v0.1/servers"}` should grow during the burst
- The `Availability dropped below 95%` alert should not fire on
cancellation-only bursts
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
## Summary
- Remap recorded status to `499` (NGINX-style "client closed") when
`ctx.Context().Err()` is `context.Canceled` after the handler returns
- Skip the `http_errors_total` increment for `499`s so the availability
metric reflects server-visible errors only
## Why
Under scraper-driven load, clients with short timeouts paginate
`/v0.1/servers`, give up while PG is busy, and close the TCP connection.
The handler's DB iteration returns `context canceled`, the handler
converts that to `huma.Error500InternalServerError`, and tries to write
a response to the now-closed socket. NGINX records `499` (client closed)
and never delivers anything to the client — but the registry's
middleware records `status_code=500` and bumps
`mcp_registry_http_errors_total`.
That's what's been firing the `Availability dropped below 95%` alert
during the daily 17:00 UTC bursts. Recent prod numbers from one pod
(~19h uptime):
```
mcp_registry_http_errors_total{path="/v0.1/servers", status_code="500"} 7396
mcp_registry_http_errors_total{path="/v0.1/servers/{serverName}/versions", status_code="500"} 283
mcp_registry_http_errors_total{path="/v0/servers", status_code="500"} 352
```
…against zero 5xx in NGINX ingress logs over the same window. The alert
was correct *given the data it had*; the data was misclassifying client
cancellations as server errors.
## What this does
In `internal/api/router/router.go` (the metric middleware): after
`next(ctx)`, check `ctx.Context().Err()`. If it's `context.Canceled`,
override `statusCode` to `499` and skip the error counter. Requests
counter and duration histogram still record so the cancellation rate
stays visible.
`context.DeadlineExceeded` is intentionally **not** remapped — that
would indicate a server-side timeout if we ever add per-request
deadlines, and should still count as a server error.
## What this does *not* do
- Doesn't reduce the underlying load (ServiceNow + notion catalog
scrapers driving the daily peak — separate work, see the `updated_since`
outreach we're doing).
- Doesn't change handler behaviour — handlers still call
`huma.Error500…` on `context.Canceled`. We could short-circuit earlier
(return without writing) to save the work, but that's a bigger refactor
and the metric fix unblocks the alert immediately.
## Companion change (no code, just a Grafana annotation tweak)
The `Availability dropped below 95%` alert (UID `bexrc60etvhmoa`)
currently has the annotation:
> No. of 5xx are increased from acceptable limit i.e. 5% of total
throughput
The query is on `mcp_registry_http_errors_total`, not 5xx specifically.
After this change the count is meaningful again, but the wording is
still misleading. Suggest updating to:
> Internal error rate exceeded 5% of total throughput
(no PromQL change required.)
## Test plan
- [x] `go test ./internal/api/router/...` — new test passes
- [x] `golangci-lint run ./internal/...` — clean
- [ ] After merge, watch a daily 17:00 UTC burst —
`mcp_registry_http_errors_total{status_code="500",
path="/v0.1/servers"}` should stay flat while
`mcp_registry_http_requests_total{status_code="499",
path="/v0.1/servers"}` grows. Availability alert should not fire on
cancellation-only bursts.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
## Summary
- Bump `mcp-registry:imageTag` from `1.7.6` → `1.7.7` in
`deploy/Pulumi.gcpProd.yaml`
- v1.7.7 is a security patch covering three findings (validator
hardening, attribute-context UI escaping, IPv6 SSRF blocklist
extension); see the v1.7.7 release notes
- Staging is on `f5f40bd` (the v1.7.7 commit) and verified end-to-end:
- `/v0.1/version` reports the new commit
- `/v0.1/validate` rejects a poisoned `websiteUrl` with
`website-url-invalid-characters`
- `/` HTML serves the `escapeAttr` helper
## Test plan
- [ ] Confirm release workflow's `ko-push` job has finished publishing
`ghcr.io/modelcontextprotocol/registry:1.7.7` before merging
- [ ] Watch deploy-production workflow rolls the registry pods to
`1.7.7`
- [ ] Hit `https://registry.modelcontextprotocol.io/v0.1/version` and
confirm `git_commit` matches `f5f40bd...`
- [ ] Spot-check the catalogue UI loads cleanly
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The HTTP namespace-verification fetcher's `isBlockedIP` composed its
blocklist from Go stdlib's `Is*` helpers plus a manual CGNAT range. That
covers IPv4 cloud metadata, RFC1918, ULA, and link-local — but Go's
classification helpers don't recognise IPv6 prefix families that embed
or tunnel to arbitrary IPv4:
- 2002::/16 RFC 3056 6to4 — bits 16-47 are an arbitrary IPv4
- 64:ff9b::/96 RFC 6052 NAT64 well-known — low 32 bits are IPv4
- 64:ff9b:1::/48 RFC 8215 NAT64 local-use — same IPv4-embedding shape
- fec0::/10 RFC 3879 site-local (deprecated, still routed by
some stacks)
On hosts with 6to4 routing or a NAT64 next-hop (the default on
IPv6-only GKE node pools, AWS IPv6-only EC2, Azure IPv6 VMs with
NAT64, and many corporate DNS64/NAT64 networks), an attacker who
controls DNS for a publisher domain can cause the registry to dial
`169.254.169.254`, `10.0.0.0/8`, etc. via the IPv6 wrapper. Same
SSRF class as GHSA-56c3-vfp2-5qqj on n8n-mcp.
Define the four prefixes via a new `mustCIDR` helper (panic on
malformed CIDR at init so a typo can't fail-open silently) and iterate
them in `isBlockedIP` after the stdlib gates. Refactor the existing
CGNAT IIFE to use the same helper for consistency. IPv4-mapped IPv6
(::ffff:0:0/96) needs no special-casing: Go's `Is*` helpers honour the
`To4()` fast-path, so e.g. `::ffff:10.0.0.1` is already classified as
`IsPrivate`.
Adds 17 test cases to `TestIsBlockedIP` covering each new prefix
family, IPv4-mapped IPv6 sanity checks, and just-outside-the-range
positive controls (`2001::1`, `2003::1`, `64:ff9c::1`).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`validateWebsiteURL` only checked that the URL parses, is absolute, and
uses the `https` scheme. It accepted literal `"`, `'`, `<`, `>`, and
ASCII space — none of which are valid in a URI per RFC 3986, and all of
which broke rendering when the value flowed into the catalogue UI's
`<a href="...">` template.
Add a `strings.IndexAny` check after the scheme check that rejects any
of these characters with a clear `website-url-invalid-characters` issue
that points at the offending byte position. Control characters (`\t`,
`\n`, `\r`) are also covered: they're caught one step earlier by Go's
`url.Parse`, but the bytes are listed in the rejection set so the
behaviour is explicit and survives any future relaxation of `url.Parse`.
Already-percent-encoded URLs (e.g. `?q=hello%20world`) continue to
validate cleanly.
Adds table-driven cases for `"`, `'`, `<>`, space, newline, and a
positive case for percent-encoded special characters.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The catalogue page interpolates publisher-supplied URL fields
(`server.repository.url`, `server.websiteUrl`) inside `href="..."`
attributes via `innerHTML`, escaping them with a
`textContent`→`innerHTML`
helper. Per the HTML5 fragment-serialisation algorithm, that round-trip
encodes only `&`, `<`, `>`, and U+00A0 inside text nodes — it does not
encode `"` or `'`. The helper is therefore safe in element-text
contexts (where it is used for `name`, `version`, `description`, etc.)
but unsafe inside an attribute value.
Add an attribute-safe `escapeAttr` helper that follows the OWASP
attribute-encoding rule (also encodes `"` and `'`) and use it for the
two `href` interpolations. Element-text usages keep the existing
`escapeHtml` and remain unchanged. A short comment on each helper
documents the contexts they cover so future fields are wired up
correctly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
## Summary
Follow-up to #1241. Two small tweaks to the `readBody` helper:
- **Rename `readBody` → `readErrorBody`.** The helper is only ever
called on the error path (after a non-2xx status check), where we want a
small string for diagnostics. The generic name made it look reusable on
the success path, where streaming JSON decoding is the right approach.
The new name pins the intended use down.
- **Cap the read with `io.LimitReader` (8 KiB).** Defense in depth —
error diagnostics never need more than a few KB, and we shouldn't buffer
an arbitrarily large body from an upstream just to format an error
message. 8 KiB is well above any realistic GitHub API or JWKS error
response.
No behavior change on the happy path. On the unhappy path, error
messages are now bounded.
## Test plan
- [x] `go build ./...`
- [x] `go test ./internal/api/handlers/v0/auth/...`
🤖 Generated with [Claude Code](https://claude.com/claude-code)
The same pattern for reading an error response body appeared in three
places across the auth handlers:
- `internal/api/handlers/v0/auth/github_at.go` (twice)
- `internal/api/handlers/v0/auth/github_oidc.go` (once)
A shared `readBody` helper function added to `common.go` now handles
this in one place.
The `io` import is removed from both files since it is no longer used
directly there.
---------
Co-authored-by: Radoslav Dimitrov <radoslav@stacklok.com>
## Summary
Follow-up to #1238 (closes#988). Adds direct unit-test coverage for the
`claimMatches` helper, which was the actual change in #1238.
`claimMatches` and `toAnySlice` are unexported, so the tests live in
`oidc_internal_test.go` (`package auth`) — same pattern as
`http_internal_test.go`. Cases covered:
- scalar/scalar match and mismatch
- scalar actual against array expected (both directions of overlap)
- array actual against scalar expected
- **array/array** — pre-#1238 this combination panicked with `comparing
uncomparable type []interface {}`
- `[]string` typed slice (exercises the typed-slice branch of
`toAnySlice`, which `[]any` skips)
- non-string scalars (int, bool)
- empty actual array
Test values use single letters (`x`, `y`, `p`, `q`) rather than role
labels — only the relationships matter, and short tokens keep `goconst`
quiet.
## Test plan
- [x] `go test ./internal/api/handlers/v0/auth/...` — all 13 subtests
pass
- [x] `golangci-lint run ./internal/api/handlers/v0/auth/...` — no new
issues vs main
Closes#988. Direct `actualValue != expectedValue` fails when the token
claim is an array (groups, roles, scp, aud). Switch to a slice-aware
any-of overlap so OIDC list claims are validated correctly.
Bumps
[github.com/pulumi/pulumi/sdk/v3](https://github.com/pulumi/pulumi) from
3.233.0 to 3.234.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/pulumi/pulumi/releases">github.com/pulumi/pulumi/sdk/v3's
releases</a>.</em></p>
<blockquote>
<h2>v3.234.0</h2>
<h2>3.234.0 (2026-05-01)</h2>
<h3>Features</h3>
<ul>
<li>
<p>[cli/cloud] Add <code>pulumi cloud api describe</code> for inspecting
the parameters, request
body, and response schema of any Pulumi Cloud API operation, with
text, markdown, and JSON output</p>
<p><a
href="https://redirect.github.com/pulumi/pulumi/pull/22770">#22770</a></p>
</li>
<li>
<p>[cli/cloud] Add <code>pulumi cloud api list</code> for browsing every
endpoint exposed by the Pulumi
Cloud OpenAPI spec, with table and JSON output</p>
<p><a
href="https://redirect.github.com/pulumi/pulumi/pull/22769">#22769</a></p>
</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li>[engine] Revert changes to property path parsing that resulted in
errors for currently valid paths</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/pulumi/pulumi/blob/master/CHANGELOG.md">github.com/pulumi/pulumi/sdk/v3's
changelog</a>.</em></p>
<blockquote>
<h2>3.234.0 (2026-05-01)</h2>
<h3>Features</h3>
<ul>
<li>
<p>[cli/cloud] Add <code>pulumi cloud api describe</code> for inspecting
the parameters, request
body, and response schema of any Pulumi Cloud API operation, with
text, markdown, and JSON output</p>
<p><a
href="https://redirect.github.com/pulumi/pulumi/pull/22770">#22770</a></p>
</li>
<li>
<p>[cli/cloud] Add <code>pulumi cloud api list</code> for browsing every
endpoint exposed by the Pulumi
Cloud OpenAPI spec, with table and JSON output</p>
<p><a
href="https://redirect.github.com/pulumi/pulumi/pull/22769">#22769</a></p>
</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li>[engine] Revert changes to property path parsing that resulted in
errors for currently valid paths</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="e802ef2c5b"><code>e802ef2</code></a>
Revert "Tighten parsing on new property paths (<a
href="https://redirect.github.com/pulumi/pulumi/issues/22695">#22695</a>)"
(<a
href="https://redirect.github.com/pulumi/pulumi/issues/22801">#22801</a>)</li>
<li><a
href="f5deaa1b20"><code>f5deaa1</code></a>
cli/cloud: add <code>pulumi cloud api describe</code> (<a
href="https://redirect.github.com/pulumi/pulumi/issues/22770">#22770</a>)</li>
<li><a
href="bccf53d1fb"><code>bccf53d</code></a>
cli/cloud: add <code>pulumi cloud api list</code> (<a
href="https://redirect.github.com/pulumi/pulumi/issues/22769">#22769</a>)</li>
<li><a
href="5575172e07"><code>5575172</code></a>
Changelog and go.mod updates for v3.233.0 (<a
href="https://redirect.github.com/pulumi/pulumi/issues/22795">#22795</a>)</li>
<li><a
href="caf13d6d14"><code>caf13d6</code></a>
Cancel in-flight fuzz iterations when the 20s cap fires (<a
href="https://redirect.github.com/pulumi/pulumi/issues/22725">#22725</a>)</li>
<li><a
href="324811c0a7"><code>324811c</code></a>
Freeze v2.233.0 (<a
href="https://redirect.github.com/pulumi/pulumi/issues/22793">#22793</a>)</li>
<li>See full diff in <a
href="https://github.com/pulumi/pulumi/compare/v3.233.0...v3.234.0">compare
view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [github.com/caarlos0/env/v11](https://github.com/caarlos0/env)
from 11.4.0 to 11.4.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/caarlos0/env/releases">github.com/caarlos0/env/v11's
releases</a>.</em></p>
<blockquote>
<h2>v11.4.1</h2>
<h2>Changelog</h2>
<h3>Bug fixes</h3>
<ul>
<li>6daeb06ac7e3c9532a7eaa5768d45917393b7480: fix: use Key instead of
OwnKey in rawEnvVars to properly reference existing defaults in complex
structs (<a
href="https://redirect.github.com/caarlos0/env/issues/412">#412</a>) (<a
href="https://github.com/AndrewChubatiuk"><code>@AndrewChubatiuk</code></a>)</li>
</ul>
<h3>Documentation updates</h3>
<ul>
<li>1ff55e6c9733833ce75d62b345916a407dbe4158: docs: adaptive starchart
(<a href="https://redirect.github.com/caarlos0/env/issues/416">#416</a>)
(<a
href="https://github.com/alexandear"><code>@alexandear</code></a>)</li>
</ul>
<h3>Other work</h3>
<ul>
<li>cf4a9682420b025e44b4773fd7ee0be48bb68b2f: ci(deps): bump the actions
group with 3 updates (<a
href="https://redirect.github.com/caarlos0/env/issues/411">#411</a>) (<a
href="https://github.com/dependabot"><code>@dependabot</code></a>[bot])</li>
<li>33faf6f333c2c3dacef2991825f8e03960a2c0cb: ci(deps): bump the actions
group with 3 updates (<a
href="https://redirect.github.com/caarlos0/env/issues/417">#417</a>) (<a
href="https://github.com/dependabot"><code>@dependabot</code></a>[bot])</li>
<li>0cb635b62488f4d310809a82a8832cedc9aa94eb: ci(deps): bump the actions
group with 6 updates (<a
href="https://redirect.github.com/caarlos0/env/issues/415">#415</a>) (<a
href="https://github.com/dependabot"><code>@dependabot</code></a>[bot])</li>
<li>a72d89a8930fc800372a6a338a1acf33e5cc3a56: ci: update release config
(<a href="https://github.com/caarlos0"><code>@caarlos0</code></a>)</li>
</ul>
<hr />
<p><em>Released with <a href="https://goreleaser.com/pro">GoReleaser
Pro</a>!</em></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="a72d89a893"><code>a72d89a</code></a>
ci: update release config</li>
<li><a
href="6daeb06ac7"><code>6daeb06</code></a>
fix: use Key instead of OwnKey in rawEnvVars to properly reference
existing d...</li>
<li><a
href="33faf6f333"><code>33faf6f</code></a>
ci(deps): bump the actions group with 3 updates (<a
href="https://redirect.github.com/caarlos0/env/issues/417">#417</a>)</li>
<li><a
href="1ff55e6c97"><code>1ff55e6</code></a>
docs: adaptive starchart (<a
href="https://redirect.github.com/caarlos0/env/issues/416">#416</a>)</li>
<li><a
href="0cb635b624"><code>0cb635b</code></a>
ci(deps): bump the actions group with 6 updates (<a
href="https://redirect.github.com/caarlos0/env/issues/415">#415</a>)</li>
<li><a
href="cf4a968242"><code>cf4a968</code></a>
ci(deps): bump the actions group with 3 updates (<a
href="https://redirect.github.com/caarlos0/env/issues/411">#411</a>)</li>
<li>See full diff in <a
href="https://github.com/caarlos0/env/compare/v11.4.0...v11.4.1">compare
view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps
[github.com/pulumi/pulumi/sdk/v3](https://github.com/pulumi/pulumi) from
3.232.0 to 3.233.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/pulumi/pulumi/releases">github.com/pulumi/pulumi/sdk/v3's
releases</a>.</em></p>
<blockquote>
<h2>v3.233.0</h2>
<h2>3.233.0 (2026-04-30)</h2>
<h3>Features</h3>
<ul>
<li>
<p>[auto/python] Expose the auto-generated Pulumi CLI interface as
<code>workspace.cli_api</code>
<a
href="https://redirect.github.com/pulumi/pulumi/pull/22638">#22638</a></p>
</li>
<li>
<p>[cli] Add encrypted logging to ~/.pulumi/logs; use the
PULUMI_ENABLE_AUTOMATIC_LOGGING feature flag to turn it on
<a
href="https://redirect.github.com/pulumi/pulumi/pull/22494">#22494</a></p>
</li>
<li>
<p>[cli] Implement the <code>filesystem__grep</code> and
<code>filesystem__content_replace</code> local tools
for <code>pulumi neo</code>. <code>grep</code> runs a regex search
across files in the project root
with an optional <code>include</code> glob filter and returns results in
<code>path:lineno: line</code>
form. <code>content_replace</code> performs a literal multi-file
search-and-replace with a
<code>file_pattern</code> glob and <code>dry_run</code> preview mode.
Both tools skip binary files,
hidden directories, and <code>node_modules</code>, and reject paths
outside the project
root. Their input schemas match the cloud-side tool definitions.</p>
<p><a
href="https://redirect.github.com/pulumi/pulumi/pull/22655">#22655</a></p>
</li>
<li>
<p>[cli] Add <code>pulumi_preview</code> and <code>pulumi_up</code> as
local tools for the experimental <code>pulumi neo</code>
agent. The Neo TUI renders a persistent bordered block for each
operation that
streams changed resources and diagnostics as the engine runs and
finalizes with a
summary of the op counts. Hidden behind PULUMI_EXPERIMENTAL.</p>
<p><a
href="https://redirect.github.com/pulumi/pulumi/pull/22697">#22697</a></p>
</li>
<li>
<p>[engine] Add <code>List</code> to the provider protocol and schema
<a
href="https://redirect.github.com/pulumi/pulumi/pull/22693">#22693</a></p>
</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li>
<p>[engine] Return a clear error when two installed plugins claim the
same default provider package name (for example, a native
<code>scaleway</code> provider alongside a
<code>terraform-provider</code> bridge parameterized as
<code>scaleway</code>) instead of panicking with "Should not have
seen an older plugin if sorting is correct!"
<a
href="https://redirect.github.com/pulumi/pulumi/pull/22679">#22679</a></p>
</li>
<li>
<p>[programgen] Do not wrap a <code>call(...)</code> on a method whose
return type is marked plain in an Output. Previously
PCL bound every method call's return type as
<code>Output<T></code>, which caused downstream program-gen
to emit broken <code>.apply(...)</code>/<code>.ApplyT(...)</code>
traversals against plain struct returns (e.g.
methods with <code>liftSingleValueMethodReturns=true</code> or
<code>ReturnTypePlain=true</code>).</p>
<p><a
href="https://redirect.github.com/pulumi/pulumi/pull/22696">#22696</a></p>
</li>
<li>
<p>[backend/diy] When using a backend url containing creds (e.g.
PostgreSQL conn string), mask user:pass as in lock-related error
messages
<a
href="https://redirect.github.com/pulumi/pulumi/pull/22701">#22701</a></p>
</li>
<li>
<p>[codegen/go] Generate unqualified <code>Provider</code> references
for the package's own provider resource. Previously
the Go codegen always emitted <code><pkg>.Provider</code> even
when the reference appeared inside <code><pkg></code></p>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/pulumi/pulumi/blob/master/CHANGELOG.md">github.com/pulumi/pulumi/sdk/v3's
changelog</a>.</em></p>
<blockquote>
<h2>3.233.0 (2026-04-30)</h2>
<h3>Features</h3>
<ul>
<li>
<p>[auto/python] Expose the auto-generated Pulumi CLI interface as
<code>workspace.cli_api</code>
<a
href="https://redirect.github.com/pulumi/pulumi/pull/22638">#22638</a></p>
</li>
<li>
<p>[cli] Add encrypted logging to ~/.pulumi/logs; use the
PULUMI_ENABLE_AUTOMATIC_LOGGING feature flag to turn it on
<a
href="https://redirect.github.com/pulumi/pulumi/pull/22494">#22494</a></p>
</li>
<li>
<p>[cli] Implement the <code>filesystem__grep</code> and
<code>filesystem__content_replace</code> local tools
for <code>pulumi neo</code>. <code>grep</code> runs a regex search
across files in the project root
with an optional <code>include</code> glob filter and returns results in
<code>path:lineno: line</code>
form. <code>content_replace</code> performs a literal multi-file
search-and-replace with a
<code>file_pattern</code> glob and <code>dry_run</code> preview mode.
Both tools skip binary files,
hidden directories, and <code>node_modules</code>, and reject paths
outside the project
root. Their input schemas match the cloud-side tool definitions.</p>
<p><a
href="https://redirect.github.com/pulumi/pulumi/pull/22655">#22655</a></p>
</li>
<li>
<p>[cli] Add <code>pulumi_preview</code> and <code>pulumi_up</code> as
local tools for the experimental <code>pulumi neo</code>
agent. The Neo TUI renders a persistent bordered block for each
operation that
streams changed resources and diagnostics as the engine runs and
finalizes with a
summary of the op counts. Hidden behind PULUMI_EXPERIMENTAL.</p>
<p><a
href="https://redirect.github.com/pulumi/pulumi/pull/22697">#22697</a></p>
</li>
<li>
<p>[engine] Add <code>List</code> to the provider protocol and schema
<a
href="https://redirect.github.com/pulumi/pulumi/pull/22693">#22693</a></p>
</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li>
<p>[engine] Return a clear error when two installed plugins claim the
same default provider package name (for example, a native
<code>scaleway</code> provider alongside a
<code>terraform-provider</code> bridge parameterized as
<code>scaleway</code>) instead of panicking with "Should not have
seen an older plugin if sorting is correct!"
<a
href="https://redirect.github.com/pulumi/pulumi/pull/22679">#22679</a></p>
</li>
<li>
<p>[programgen] Do not wrap a <code>call(...)</code> on a method whose
return type is marked plain in an Output. Previously
PCL bound every method call's return type as
<code>Output<T></code>, which caused downstream program-gen
to emit broken <code>.apply(...)</code>/<code>.ApplyT(...)</code>
traversals against plain struct returns (e.g.
methods with <code>liftSingleValueMethodReturns=true</code> or
<code>ReturnTypePlain=true</code>).</p>
<p><a
href="https://redirect.github.com/pulumi/pulumi/pull/22696">#22696</a></p>
</li>
<li>
<p>[backend/diy] When using a backend url containing creds (e.g.
PostgreSQL conn string), mask user:pass as in lock-related error
messages
<a
href="https://redirect.github.com/pulumi/pulumi/pull/22701">#22701</a></p>
</li>
<li>
<p>[codegen/go] Generate unqualified <code>Provider</code> references
for the package's own provider resource. Previously
the Go codegen always emitted <code><pkg>.Provider</code> even
when the reference appeared inside <code><pkg></code>
itself, producing identifiers that would not compile. Affects generated
code for method return</p>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="fa313013d0"><code>fa31301</code></a>
don't stamp vcs information into binaries (<a
href="https://redirect.github.com/pulumi/pulumi/issues/22788">#22788</a>)</li>
<li><a
href="17fd40453d"><code>17fd404</code></a>
double timeout in "does not hang" test (<a
href="https://redirect.github.com/pulumi/pulumi/issues/22789">#22789</a>)</li>
<li><a
href="eb6e31d841"><code>eb6e31d</code></a>
split tests module on macos further (<a
href="https://redirect.github.com/pulumi/pulumi/issues/22790">#22790</a>)</li>
<li><a
href="94dbe6401b"><code>94dbe64</code></a>
Remove runInStack from run-policy-pack (<a
href="https://redirect.github.com/pulumi/pulumi/issues/22783">#22783</a>)</li>
<li><a
href="a35ec7e9d5"><code>a35ec7e</code></a>
retry pip install a few times (<a
href="https://redirect.github.com/pulumi/pulumi/issues/22785">#22785</a>)</li>
<li><a
href="7a4beeaa44"><code>7a4beea</code></a>
Reject schemas with duplicate tokens after applying
<code>moduleFormat</code> (<a
href="https://redirect.github.com/pulumi/pulumi/issues/22765">#22765</a>)</li>
<li><a
href="0b914cee46"><code>0b914ce</code></a>
unflake TestNodejsComponentProviderRun (<a
href="https://redirect.github.com/pulumi/pulumi/issues/22782">#22782</a>)</li>
<li><a
href="5cd1646706"><code>5cd1646</code></a>
start logging encrypted logs for all pulumi commands (<a
href="https://redirect.github.com/pulumi/pulumi/issues/22494">#22494</a>)</li>
<li><a
href="a81ef07c09"><code>a81ef07</code></a>
pin more plugin versions in integration tests (<a
href="https://redirect.github.com/pulumi/pulumi/issues/22758">#22758</a>)</li>
<li><a
href="0d2b8bf114"><code>0d2b8bf</code></a>
Update list doc comment (<a
href="https://redirect.github.com/pulumi/pulumi/issues/22777">#22777</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/pulumi/pulumi/compare/v3.232.0...v3.233.0">compare
view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
## Summary
Follow-up to #1229. Three small doc additions so the new audience
requirement is discoverable without reading the source:
- `.env.example` lists `MCP_REGISTRY_GITHUB_OIDC_AUDIENCE` alongside the
other registry env vars, with a note that the value must match the
scheme+host that publishers pass via `--registry`.
- The publisher CLI reference (`docs/reference/cli/commands.md`)
explains that the CLI derives the audience from `--registry` and that
self-hosters must set the matching env var on the registry side.
- The GitHub Actions troubleshooting table
(`docs/modelcontextprotocol-io/github-actions.mdx`) maps the new
`invalid audience` error to the user-facing fix (upgrade
`mcp-publisher`).
No code changes — docs only.
## Test plan
- [ ] CI green (markdown lint / link checks if any)
- [ ] Eyeball-render the troubleshooting table on the
modelcontextprotocol.io preview to confirm the new row formats correctly
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
## What this does
The publisher CLI now derives the OIDC audience to request from the
`--registry` URL instead of using a fixed constant, and the server
validates the token's `aud` claim against a new per-deployment env var
(`MCP_REGISTRY_GITHUB_OIDC_AUDIENCE`).
Production sets this env var to
`https://registry.modelcontextprotocol.io` (and per-environment URL
elsewhere), so the deployment only accepts tokens that were requested
for it.
## Compatibility
This is a breaking change for older `mcp-publisher` binaries that still
request the legacy hardcoded audience. After this rolls to a deployment,
users on old `mcp-publisher` will see a clear `invalid audience:
expected …, got […]` error and need to upgrade. Users following the
documented `releases/latest` install pattern get the new binary
automatically.
## Test plan
- [ ] CI green
- [ ] Run a synthetic publish from a real GitHub Actions workflow
against staging after deploy
- [ ] Confirm an older `mcp-publisher` against the same staging deploy
returns the expected `invalid audience` error
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
## What this does
Fixes an open redirect plus a handful of small, mostly-invisible
hardening changes around it.
## What's in it
- **The fix**: trailing-slash redirects no longer let `//evil.com/`
redirect off-host.
- **SSRF block** on the DNS/HTTP namespace verification — refuses to
dial loopback, RFC1918, link-local, multicast, CGNAT, or
IP-literal/single-label \"domains\".
- **Slow-client guard**: `ReadTimeout` and `IdleTimeout` on the HTTP
server (no `WriteTimeout` — would risk cutting off legitimate
multi-package publishes).
- **Defence-in-depth headers** on the UI (`/`) and 404 responses.
- **5xx responses** stop leaking raw DB error text to clients (logged
server-side instead).
- **Search filter** stops treating `%` and `_` as wildcards.
- **Validators**: PyPI/NuGet identifiers URL-escaped before fetching;
MCPB no longer follows redirects.
- **Misc**: `Version` field bounded to 255 chars; logs quote user input;
`BlockedNamespaces` denylist now catches subdomain claimants.
Each change is its own commit so they can be reviewed (or reverted)
independently.
## Test plan
- [ ] CI green
- [ ] Synthetic publish in staging for each package type (npm, pypi,
nuget, oci, mcpb) — confirms validator changes don't regress
- [ ] Quick browser check of `/` after deploy to confirm UI still loads
with the new CSP
- [ ] `curl https://<host>//evil.com/` — confirm the open redirect is
closed
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
## Summary
Promotes
[v1.7.4](https://github.com/modelcontextprotocol/registry/releases/tag/v1.7.4)
(#1225) to production: registry pod memory request `256Mi → 512Mi`,
limit `512Mi → 1Gi`.
## Why
#1221 doubled pgxpool `MaxConns` 30→60 per pod without bumping the
registry pod memory limit. pgxpool keeps per-connection state in the Go
process (prepared statement cache, scratch buffers, row iteration state)
so peak per-pod memory shifted from ~330 MB to ~450 MB against the 512
MiB limit. Pod `tx49f` was OOMKilled at 17:01 UTC on 2026-04-29.
## Validation so far
- Staging deployed cleanly via `deploy-staging.yml` after #1225 merged
- Both staging pods running with `req=512Mi limit=1Gi` ✓
- Rolling update was clean — no failed health checks during the
transition
- Both pods spread across nodes (q716 + 7exo)
## Deployment
**Zero downtime** — Deployment template change only, rolling update with
`maxSurge: 1, maxUnavailable: 0`. No PG involvement (no
`max_connections` change, no shared_preload_libraries change).
## Test plan
- [x] v1.7.4 release built and pushed
(`ghcr.io/modelcontextprotocol/registry:1.7.4`)
- [x] Staging deployed cleanly with new limits
- [ ] Prod Pulumi run applies cleanly; rolling update completes
- [ ] Prod pods come up with `req=512Mi limit=1Gi`
- [ ] After 24h: no OOMKills on either pod, peak memory stays under ~600
MB
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
## Summary
Follow-up to #1221. That PR doubled pgxpool `MaxConns` from 30 to 60 per
pod without bumping the registry pod memory limit. pgxpool keeps
per-connection state in the Go process (prepared statement cache,
scratch buffers, row iteration state), so memory grew roughly
proportionally — peak per-pod usage shifted from ~330 MB to ~450 MB
against a 512 MiB limit.
## What surfaced it
Pod `tx49f` was OOMKilled at 17:01 UTC on 2026-04-29 (`exitCode 137`,
`reason: OOMKilled`). Memory history from Cloud Monitoring confirms the
regression:
| Pods | Peak 30-min memory |
|------|-------------------:|
| Pre-#1221 (`MaxConns=30`) | 178–333 MB |
| **Post-#1221** (`MaxConns=60`) | **437–449 MB** |
| Old limit | **512 MiB** ← right at the edge |
Other pod was at 394 MB at the time of inspection — could OOM again.
## Fix
`deploy/pkg/k8s/registry.go:217-225`:
| | Before | After |
|---|-------:|------:|
| `requests.memory` | 256 MiB | **512 MiB** |
| `limits.memory` | 512 MiB | **1 GiB** |
1 GiB is ~2× the current peak under load — gives room for traffic spikes
without immediate OOM risk. Either node has 4–5 GiB free allocatable
memory, so the new request fits comfortably.
## Test plan
- [x] `go build ./...` clean
- [x] `golangci-lint run` clean
- [ ] After release + prod promotion: pod memory stabilises around 450
MB peak with no OOMKills
- [ ] After release + prod promotion: tx49f restartCount stops climbing
(currently at 5)
## Deployment
Zero downtime — Deployment template change only, rolling update with
`maxSurge: 1, maxUnavailable: 0`. **No PG involvement** (no
`max_connections` change, no shared_preload_libraries change). Same
shape as a normal v1.7.x image promotion.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
## Summary
Promotes
[v1.7.3](https://github.com/modelcontextprotocol/registry/releases/tag/v1.7.3)
to production. Contents:
- **#1220** — `RemoteURL` filter SQL rewritten to use JSONB containment
(`@>`) instead of `EXISTS jsonb_array_elements`. The new form is
GIN-indexable; the old form forced a full table scan. Local benchmark:
40,398 → 294 buffer reads, 17 ms → 1.6 ms. Maps to prod's 10,005 ms
cold-cache observation.
- **#1221** — pgxpool `MaxConns 30→60`, `MinConns 5→10`. PG
`max_connections 100→200`. Explicit PG `resources:` block (was unset).
## What this addresses
- **#1220**: yesterday's 17:08 UTC `Publish Endpoint Latency` alert
(`dev.storage/mcp` publish took 14.8s with `remotes_ms=10980` —
pinpointed by the per-phase slog from #1215).
- **#1221**: yesterday's 17:35–17:40 UTC `Availability dropped below
95%` alert and 18:17 UTC `Publish Endpoint Latency` re-fire. Both were
scraper-driven concurrency on `/v0/servers` (~15 req/s sustained from
ServiceNow + others). With the bumped pool, the queue at the Go HTTP
layer should clear faster instead of blowing up to 20–35s nginx-level
latencies.
## Deployment caveat
The PG `max_connections` change is a postmaster-level setting → CNPG
triggers a PG restart on the next prod Pulumi run. With `instances: 1`
this is brief downtime — staging took **~30s** during the equivalent
restart, with **one registry pod bouncing once** on its 8-attempt
DB-retry budget before recovering on the next kubelet restart.
**Time the merge for a low-traffic UTC window.** Alert history suggests
very early UTC (02:00–04:00) is quietest.
## Resource impact
PG node memory is currently 39% (~2.4 GiB / 6 GiB allocatable).
Worst-case PG memory growth with `max_connections=200` lands around 3–4
GiB, putting the node at ~65% — fits with headroom. Empirically, prod PG
has peaked at **413 MiB** in the last 30h of incident data, so the
proposed 4 GiB limit is ~10× the historical max — guardrail not
constraint.
## Post-merge
CNPG handles `pg_stat_statements` extension creation automatically (no
manual `CREATE EXTENSION` step needed — it was already done in v1.7.2's
deploy).
Verify after deploy:
```bash
PATH=/opt/homebrew/share/google-cloud-sdk/bin:$PATH
# max_connections actually changed
kubectl exec -i registry-pg-1 -c postgres \
--context gke_mcp-registry-prod_us-central1-b_mcp-registry-prod \
-- psql -U postgres -tAc "SHOW max_connections" # expect: 200
# resources block applied
kubectl get pod registry-pg-1 \
--context gke_mcp-registry-prod_us-central1-b_mcp-registry-prod \
-o jsonpath='{.spec.containers[?(@.name=="postgres")].resources}{"\n"}'
# pgxpool MaxConns reflected (registry app uses 60 per pod after restart)
kubectl exec -i registry-pg-1 -c postgres \
--context gke_mcp-registry-prod_us-central1-b_mcp-registry-prod \
-- psql -U postgres -tAc "SELECT count(*) FROM pg_stat_activity WHERE datname='app'"
```
## Test plan
- [x] v1.7.3 release built and pushed
(`ghcr.io/modelcontextprotocol/registry:1.7.3`)
- [x] Staging deployed cleanly; PG restarted and came back with
`max_connections=200`; one staging pod bounced as expected
- [ ] Prod Pulumi run applies cleanly; brief PG restart
- [ ] Confirm `SHOW max_connections` returns 200 on prod
- [ ] Confirm `publish complete` events show `remotes_ms` < 10ms
- [ ] Watch for any "too many connections" errors during the rollout
window (none expected — Pulumi orders CNPG cluster before Deployment, so
PG accepts the new conn limit before pgxpool tries to use it)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
## Summary
Follow-up to #1215 and #1220. Both of those addressed *individual* slow
queries; this PR addresses *concurrency* — under today's scraper load
the cursor query is fast on average (mean 42ms) but the connection pool
saturates and queue depth blows up at the Go HTTP layer.
## Diagnosis
`pg_stat_statements` (added in #1215) made this possible to see:
```
max_ms mean_ms calls template
10,755 41.8 94,009 plain cursor pagination, no filter
7,277 214.7 14,770 ILIKE substring filter
7,638 330.8 7,656 ILIKE substring + is_latest filter
```
Mean times are healthy. But `max_exec_time` of 7–10s on the cursor
query, combined with sustained ~15 req/s from scrapers (ServiceNow's
148.139.x.x range, anonymous `node` user-agent, etc) saturates
`MaxConns=30 × 2 pods = 60`. New requests queue at the Go HTTP layer;
nginx-side p99 hits 35s; eventually scrapers time out at 60s, retry, and
amplify the queue.
Today's two ongoing alerts are both this pattern:
- 17:35–17:40 UTC `Availability dropped below 95%` — 4,526 GET requests
in 5 min, hundreds of 504s
- 18:17 UTC `Publish Endpoint Latency` re-fire — same scraper
concurrency dragging the publish path
Critically, the symptoms today were also visible during yesterday's
incident, but yesterday's broken cursor (#1215) was the dominant cause.
After #1215 the cursor is fast individually; concurrency now becomes the
next bottleneck.
## Changes
### pgxpool (`internal/database/postgres.go`)
| Setting | Before | After |
|---------|-------:|------:|
| `MaxConns` | 30 | **60** per pod |
| `MinConns` | 5 | **10** per pod |
| `MaxConnIdleTime` | 30 min | (unchanged) |
| `MaxConnLifetime` | 2 h | (unchanged) |
2 pods × 60 = 120 total app connections. Cuts queue depth roughly in
half at current scraper load.
### PG cluster (`deploy/pkg/k8s/postgres.go`)
- `max_connections: 100 → 200` — required to support the larger pool.
120 (app) + ~10 (PG internals: autovacuum, replication, admin) + 70
headroom.
- Added explicit `resources:` block — previously the pod had no resource
limits, making node-level OOM behaviour unpredictable.
| | Request | Limit |
|---|--------:|------:|
| memory | 512Mi | **4Gi** |
| cpu | 200m | 1500m |
## Resource budget
| Node | Now | After |
|------|----:|------:|
| dy89 (PG node) | 39% mem (~2.4 GiB / 6 GiB) | ~65% mem worst-case
(~3.9 GiB) |
| 2yxm | 36% mem | unchanged |
Both nodes well within capacity. CPU usage <50% on both, plenty of
headroom.
PG worst-case memory math:
- 200 conns × ~15 MB per backend = 3 GiB
- + `shared_buffers` 128 MiB
- + `maintenance_work_mem`, OS overhead, etc.
- ≈ 3–4 GiB total
## Deployment caveat
`max_connections` is a postmaster-level setting → CNPG triggers a PG
restart on the change. Same in-pod restart shape as the
pg_stat_statements deploy yesterday — registry pods see ~30s of DB
unavailability, covered by v1.7.1's retry-with-backoff (8 attempts, 1→8s
capped). One registry pod may bounce once before recovering, like
yesterday.
**Time the merge for a low-traffic UTC window.**
Order of operations matters within the deploy itself:
1. Pulumi applies the new CNPG spec → PG restarts with
`max_connections=200`
2. Rolling deploy of registry pods picks up `MaxConns=60` config
3. New conns are accepted up to 200 limit
Pulumi's standard ordering does step 1 before step 2 in this scenario
(Pulumi resource graph: CNPG cluster precedes Deployment). If for any
reason it doesn't, the worst case is `too many connections` errors
during a small window — Self-correcting once the rollout completes.
## Test plan
- [x] `go build ./...` clean for app + deploy
- [x] `make lint` clean
- [x] `go test -race ./internal/database/...` green
- [ ] On merge: staging deploy applies the spec change cleanly; PG
restarts; pool reaches 60 max
- [ ] On prod deploy: same; verify no `too many connections` errors
during the rollout window
- [ ] After deploy: query `SHOW max_connections` returns 200; query
pg_stat_statements after a scraper burst, confirm `max_exec_time` for
cursor query no longer hits 10s
## Out of scope
- **ILIKE substring search** (`server_name ILIKE '%foo%'`) is
unindexable. Three pg_stat_statements variants run with means 141–333ms
and max 4–7s. Worth replacing with a `pg_trgm` GIN index or full-text
search in a separate PR.
- **Per-IP nginx rate limiting** — defends against scraper retry storms
regardless of pool size.
- **`Cache-Control` on `/v0/servers`** — let nginx absorb
scraper-repeated cursors.
- **Pre-existing `superfluous WriteHeader` warnings** — separate Huma
framework issue.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
## Summary
Same shape of bug as #1215's cursor fix, different filter. The RemoteURL
filter built its WHERE predicate using `jsonb_array_elements` + `->>`
extraction, which the planner can't translate into a GIN search.
Replaced with a JSONB containment predicate `@>` that uses
`idx_servers_json_remotes`.
## Diagnosis (from the per-phase publish slog #1215 added)
Today's first publish-latency alert at 17:08 UTC pointed straight at
this:
```
publish complete server_name=dev.storage/mcp version=1.10.1 total_ms=14822
validate_ms=0 lock_ms=238 remotes_ms=10980 version_checks_ms=535 unmark_ms=3014 create_ms=53
```
`remotes_ms=10,980` on a server with **one** remote URL pointed at
`validateNoDuplicateRemoteURLs`, which runs the RemoteURL filter against
the database. EXPLAIN ANALYZE on prod confirmed:
```
Filter: (status != 'deleted') AND EXISTS(SubPlan 1)
Rows Removed by Filter: 21,092 ← scans the whole table
Buffers: shared hit=20,028
Execution Time: 10,005 ms
```
## Fix
`internal/database/postgres.go:99` rewritten:
```sql
-- before (planner can't use GIN index for this)
EXISTS (SELECT 1 FROM jsonb_array_elements(value->'remotes') AS remote
WHERE remote->>'url' = $1)
-- after (GIN-indexable)
value -> 'remotes' @> jsonb_build_array(jsonb_build_object('url', $1::text))
```
Semantically equivalent for our schema (`url` is always a string, NOT
NULL).
## Local benchmark — 20K rows, prod-shaped JSONB (~1KB/row)
| Form | Buffer hits | Index used | Time |
|------|------------:|------------|-----:|
| OLD | 40,398 | none — full scan | 17.0 ms |
| NEW | **294** | **`idx_servers_json_remotes` GIN bitmap** | **1.6 ms**
|
Maps to prod's 10,005 ms cold-cache observation — same shape of speedup
we got from the cursor fix.
## Scope honesty
This PR addresses the **publish slowness path**
(`validateNoDuplicateRemoteURLs` in the publish transaction). It
directly resolves today's 17:08 UTC `Publish Endpoint Latency` alert.
It does **not** address:
- Today's 17:35–17:40 UTC `Availability dropped below 95%` alert.
Per-investigation: only 4 publishes happened in the entire 17:00–17:50
window and zero during the burst minutes — so my initial "concurrent
slow publishes starved the pool" hypothesis was wrong. The actual cause
was scraper-driven concurrency (4,500+ requests in 5 min) on plain
cursor pagination + ILIKE substring search, where individual queries are
fast (mean 42 ms) but tail latency under concurrency reaches 10 s
server-side and 20–35 s nginx-level due to pool queueing.
- The 18:17 UTC `Publish Endpoint Latency` re-fire (same scraper-driven
concurrency).
A follow-up PR will raise pool size + PG `max_connections` + add
explicit PG resource limits to absorb concurrency without queue blowup.
That fix has a different shape (config) and a different risk profile (PG
restart for `max_connections`); kept separate so this surgical SQL
change can ship cleanly.
## Test plan
- [x] `go build ./...` clean
- [x] `make lint` clean
- [x] `go test -race ./internal/database/... ./internal/service/...`
green — existing `TestPostgreSQL_ListServers/filter by remote URL`
covers semantic correctness
- [x] EXPLAIN ANALYZE comparison locally on 20K rows + prod-shaped JSONB
- [ ] After merge + deploy: verify on prod via `EXPLAIN ANALYZE`;
confirm `remotes_ms` in `publish complete` slog drops to single-digit ms
## Out of scope (separate follow-ups)
- pgxpool `MaxConns` / `MinConns` bump + PG `max_connections` raise + PG
`resources` limits — addresses the cursor-tail and ILIKE-search
concurrency that this PR doesn't touch
- Per-IP rate limiting at nginx — defends against scraper retry storms
regardless of query speed
- `Cache-Control` headers on `/v0/servers` — let nginx absorb scraper
repeats
- Pre-existing `superfluous WriteHeader` warnings — separate Huma
framework issue
- ILIKE substring search on `server_name` is unindexable — could be
replaced with `pg_trgm` GIN index or full-text search
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
## Summary
Promotes
[v1.7.2](https://github.com/modelcontextprotocol/registry/releases/tag/v1.7.2)
(PR #1215) to production:
- **Row-constructor cursor pagination** — fixes the 760ms list query
that was the root cause of today's pool-exhaustion alerts. Local
benchmark shows /v0/servers paginated requests dropping from ~30ms (cold
cache, 100K rows) to ~0.05ms.
- **Per-phase publish slog** — `publish complete` events now include
validate / lock / remotes / version_checks / unmark / db_create timings
so the next slow publish is self-diagnostic.
- **pg_stat_statements** — aggregate query visibility we didn't have
during today's incident.
## Deployment caveat
The CNPG cluster spec change for `pg_stat_statements` triggers an in-pod
postgres restart on the next prod Pulumi run. Staging took **~45–70s of
registry unavailability** during the equivalent restart. The DB
retry-with-backoff in v1.7.1 covers most of it, but registry pods may
bounce once before they reconnect (one staging pod hit `Failed to
connect after 8 attempts` then recovered on its next restart).
**Time the merge for a low-traffic UTC window** — the alert history
suggests very early UTC is quietest.
## Post-merge action
Once the prod Pulumi run completes, run once on the existing cluster:
```bash
PATH=/opt/homebrew/share/google-cloud-sdk/bin:$PATH
kubectl exec -i registry-pg-1 -c postgres \
--context gke_mcp-registry-prod_us-central1-b_mcp-registry-prod \
-- psql -U postgres -c "CREATE EXTENSION IF NOT EXISTS pg_stat_statements"
```
Verify:
```bash
kubectl exec -i registry-pg-1 -c postgres \
--context gke_mcp-registry-prod_us-central1-b_mcp-registry-prod \
-- psql -U postgres -d app -c "
SELECT round(mean_exec_time::numeric, 2) AS mean_ms, calls,
substring(query, 1, 80) AS q
FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 10;"
```
## Test plan
- [x] v1.7.2 release built and pushed
(`ghcr.io/modelcontextprotocol/registry:1.7.2`)
- [x] Staging deployed cleanly; `pg_stat_statements` is collecting on
staging
- [ ] Prod Pulumi run applies cleanly; brief PG restart
- [ ] Run `CREATE EXTENSION` on prod
- [ ] Confirm `publish complete` events contain per-phase timings on the
next publish
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
## Summary
Three changes from the 2026-04-27 incident:
1. **Cursor SQL fix** — `addCursorCondition` rewritten to use a
row-constructor comparison `(server_name, version) > ($1, $2)` instead
of the OR-decomposed form. Postgres can index-seek on the row
constructor; it can't on the OR form, which scanned from the start of
the index and filtered rows before the cursor. Cost grew linearly with
cursor depth.
2. **Per-phase publish timings restored** — `createServerInTransaction`
was simplified during #1211 review to log only `validate_ms`. The
incident showed publishes spending 50+ s in `acquire_lock` /
`version_checks` / `db_create` while `validate_ms` reported a few
hundred ms — the diagnostic signal was hidden. Restored timings for
every phase, refactored into a small `runPhase` helper.
3. **`pg_stat_statements` enabled** in the CNPG cluster spec. We had no
aggregate query visibility during the incident — could only EXPLAIN
queries we happened to suspect.
## Cursor fix evidence
Local benchmark, 100K rows, cold cache:
| Form | Rows filtered | Buffer reads | Time |
|------|--------------:|-------------:|-----:|
| OLD | 80,001 | 7,679 | 31.6 ms |
| NEW | 2 | 1 | **0.05 ms** |
Maps to prod's measured 8,911 buffer hits → 760 ms. End-to-end API:
`/v0/servers?limit=100&cursor=…` returns in 4–7 ms regardless of depth.
Risk check: confirmed `(server_name, version)` index exists in that
column order (both `servers_pkey` and `idx_servers_name_version`), and
both columns are `NOT NULL` so row-constructor comparison can't silently
drop rows.
## Tests
- New
`TestPostgreSQL_PerformanceScenarios/compound_cursor_across_versions_of_same_server`
pins multi-version pagination semantics. The existing cursor tests only
exercised the fallback (single-component cursor) and degenerate (one
version per server) cases.
- `make lint` clean, `go test -race ./internal/... ./cmd/...` green.
## Deployment
The cursor + slog changes are zero-downtime. The CNPG spec change
triggers a brief PG restart (single-instance cluster). After the
restart, run once on prod:
```bash
kubectl exec -i registry-pg-1 -c postgres \
--context gke_mcp-registry-prod_us-central1-b_mcp-registry-prod \
-- psql -U postgres -c "CREATE EXTENSION IF NOT EXISTS pg_stat_statements"
```
Time the merge for a low-traffic UTC window. v1.7.1's DB
retry-with-backoff covers the brief PG restart.
## Out of scope
`MaxConns` bump, per-IP rate limiting at nginx, response caching, the
pre-existing `superfluous WriteHeader` warnings — separate follow-ups.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>