Proposal: optional content-integrity fields for npm / PyPI / OCI package entries

オープン
#1,783 コメント 7 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

まだ誰も着手していません。

評価

難易度
5/5
見積もり時間
1週間以上
初心者へのやさしさ
12/100
issue の種類
機能追加
明瞭さ
おおむね明確
活発さ
活発
技術スタック
go
領域
backend, security

調査の方向性

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.

  1. 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].
  2. 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.
  3. The upstream registries do have the hashes. For the 30 most-downloaded npm-backed entries,
    6,236 of 6,237 published versions have a sha512 dist.integrity on npm (one older version has only
    a sha1 shasum); 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.
  4. 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's latest [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 files cover 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

環境構築

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

modelcontextprotocol/registry のほかの issue

modelcontextprotocol/registry の issue をすべて見る

似ている issue

Go の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。