Proposal: optional content-integrity fields for npm / PyPI / OCI package entries
メンテナーはふだん 1 日以内に返信
まだ誰も着手していません。
評価
- 難易度
- 5/5
- 見積もり時間
- 1週間以上
- 初心者へのやさしさ
- 12/100
調査の方向性
This is a design proposal, not a ready task: the open questions ask whether the registry computes the fields or accepts them from publishers, and how unpublished upstream versions are handled. Start from the fileSha256 handling for MCPB packages in the Go registry code, and read the measurements in bench/drift/summary.json and bench/drift/DRIFT.md. Done means maintainers settle those questions and agree on the field shapes for npm, PyPI and OCI entries.
索引モデルが issue の本文から書いたものです。
説明
Summary
server.json already lets MCPB packages carry a content hash (fileSha256). For npm and PyPI
package entries there is no equivalent, and OCI entries can only embed a digest in the image
reference, so a client that installs a server from a registry entry cannot check that the bytes it
receives are the bytes that were published under that entry. This issue proposes optional integrity
fields for those package types and shares measurements from the public registry API that may help
scope the change.
Related: #82 tracks fingerprints of the declared tool surface (it consolidates #1720 and #1681);
this issue is only about the package content, not the tool surface. #1706 proposes signalling when
the target behind a server name changes between versions; integrity fields would let a client
detect a change of content under an unchanged version and package name. #1715 tracks scheduled re-validation of published entries — the registry currently checks a package only at publish time and again when the publisher edits. A declared digest is complementary rather than a substitute: re-validation catches drift in the listing, while a declared digest lets a client catch drift in the bytes it receives, from any mirror or proxy, without waiting for the registry to re-check.
What we measured
Metadata only, from the public API (/v0.1/servers?version=latest) and npm and PyPI registry
metadata; collected on 2026-10-31. No package was downloaded and no server was run.
- Hashes by package type. Among the 53,449 latest server entries, MCPB package entries carry a
content hash in 2,089 of 2,089 cases. npm (14,682 entries) and PyPI (4,964) entries carry none,
and OCI entries (1,337) have no separate hash field — an image reference may embed a digest, which
this count does not look at [D1]. - Versions are almost always exact. 14,667 of 14,682 npm entries and 4,962 of 4,964 PyPI
entries specify an exact version [D2]. The version is pinned, but the content behind it is not
recorded. - The upstream registries do have the hashes. For the 30 most-downloaded npm-backed entries,
6,236 of 6,237 published versions have a sha512dist.integrityon npm (one older version has only
a sha1shasum); all files of the 200 releases in a random sample of 10 PyPI-backed entries have a
sha256 digest [D3]. Recording them at publish time would therefore be a copy, not a computation
over the package. - Versions move quickly and sometimes disappear. Those 30 npm packages published 1,151 versions
in the last 90 days, and 12 versions of 5 of them have been unpublished since [D4]. For 13 of the
30 entries, the registry's latest entry points to an older version than npm'slatest[D4].
Proposal
Optional fields per package entry, filled by the publisher or by the registry at publish time:
- npm:
integrity— the SRI string from the npm packument for that version (sha512-…). - PyPI:
files— a list of{filename, sha256}for the release, so that wheels added later are
visible. - OCI:
digest— the manifest digest the tag resolved to at publish time (an identifier that
already embeds@sha256:would carry it implicitly).
Clients could then compare what they are about to launch with what the registry entry recorded,
and report a mismatch instead of silently running different content under the same version.
Validation at publish. When one of these fields is present, the publish-time validator can compare the declared value against the upstream registry's own metadata for that exact version — npm's dist.integrity, PyPI's digests.sha256 — which it already fetches as part of the existing package check. A wrong declared value then fails at publish instead of surfacing later as a client-side mismatch. The fields stay optional; the cross-check applies only when a value is declared.
Non-goals
These fields identify content; they say nothing about what a server does at runtime and do not
attest that a server is safe. Fingerprints of the declared tool surface are out of scope here (#82).
Open questions
- Should the registry also compute the fields from the upstream registry when the publisher declares none, or only validate declared values?
- For PyPI, should
filescover all distribution files or only the ones a launcher would pick? - How should a client treat an entry whose upstream version was unpublished after registration?
Disclosure
The measurements were made with CapLock, a lockfile verifier for MCP configurations that I maintain:
https://github.com/NagaYu/caplock . The survey method and limits are in bench/drift/DRIFT.md.
Contact: https://nagayu-portfolio.nagayu.workers.dev/en
Sources
| ref | figure | source in the repository |
|---|---|---|
| D1 | latest entries 53,449; separate hash field present: MCPB 2,089/2,089, npm 0/14,682, PyPI 0/4,964, OCI 0/1,337 (digests embedded in OCI identifiers not counted) | bench/drift/summary.json → registry.latestEntries, registry.byRegistryType |
| D2 | exact version specs: npm 14,667/14,682, PyPI 4,962/4,964 | bench/drift/summary.json → registry.versionSpec |
| D3 | npm top 30: 6,236/6,237 versions with sha512 integrity, 1 sha1-only; PyPI sample: 200 releases, 0 files without sha256 | bench/drift/summary.json → npmTop30.versionsWithIntegritySha512, npmTop30.versionsShasumOnly, pypiSample.releasesTotal, pypiSample.filesWithoutSha256 |
| D4 | 1,151 versions in 90 days; 12 unpublished versions in 5 packages; registry entry older than npm latest: 13/30 |
bench/drift/summary.json → npmTop30.releasesLast90Days.total, npmTop30.unpublishedVersionsTotal, npmTop30.packagesWithUnpublishedVersions, npmTop30.registryBehindNpmLatest |
Selection: the 30 npm packages are the most downloaded (last month) among active latest registry
entries with exactly one npm or PyPI package; the PyPI entries are a random sample of 10 (PyPI has no
first-party download statistics). Details: bench/drift/summary.json → selection.
- 主要言語
- Go
- スター
- 7.4k
- フォーク
- 1k
- 平均マージ
- 13時間 43分
- マージ済み PR(30日)
- 51
環境構築
- Dockerfile または Docker Compose ファイルあり
- プルリクエストのテンプレートなし
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
modelcontextprotocol/registry のほかの issue
-
3 registry records that cannot resolve for any client, and a caution about single-pass dead countsオープン
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
modelcontextprotocol/registry#1692 ·
メンテナーはふだん 1 日以内に返信
-
IsValidRemoteURL only blocks literal localhost/127.0.0.1 — misses [::1], 127.0.0.0/8, 0.0.0.0, and private/link-local addresses対応中かも @amitvijapur が 104 日前に担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 74/100
modelcontextprotocol/registry#1465 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1時間未満 初心者へのやさしさ 8/100
modelcontextprotocol/registry#1785 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1時間未満 初心者へのやさしさ 35/100
modelcontextprotocol/registry#1784 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1時間未満 初心者へのやさしさ 8/100
modelcontextprotocol/registry#1780 ·
メンテナーはふだん 1 日以内に返信
modelcontextprotocol/registry の issue をすべて見る
似ている issue
-
agent-butler-finding chore
難易度 1/5 1時間未満 初心者へのやさしさ 78/100
jordansmall/spindrift#6318 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
Eyevinn/moqlivemock#166 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
JakeChampion/lang#14091 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 84/100
hyperledger/fabric-x-orderer#1409 ·
メンテナーはふだん 3 日以内に返信
-
get actions doesn't show the approval flag, and describe action shows it unformatted対応中かも @kenokech が今日担当しました。 オープンbug
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
メンテナーはふだん 1 日以内に返信