Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@ All notable changes to this project will be documented in this file.

## [Unreleased]

### Documentation

- **Install matrix + distribution doc (D6)** — the README now lists npm / npx / pnpm /
**mise** (`mise use -g npm:@getmcpm/cli`, via mise's built-in `npm:` backend — no
registry entry needed) as install channels, all resolving the scoped `@getmcpm/cli`
package. New `docs/DISTRIBUTION.md` documents the deliberate **name-collision
decision**: the `mcpm` Homebrew formula (and PyPI name) belong to the unrelated
[mcpm.sh](https://mcpm.sh) project, so mcpm ships through the collision-free scoped
channels rather than fighting for `brew install mcpm`. A Homebrew tap + mise
registry short-name are deferred (documented).

### Added

- **`mcpm audit --sarif` (D3)** — emit a SARIF 2.1.0 report for GitHub
Expand Down
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,7 @@ the same entry shape).
| 2026-07-03 | v0.17.0 released (Wave-0 adoption credibility floor) + started Wave 1 with the Claude Code adapter (D1) | Following the new **docs/ROADMAP-ADOPTION.md**: v0.17.0 shipped the four Wave-0 items (E3 supply-chain evidence pack, B1a macOS CI leg, E11 `docs/CONTRACTS.md`, E9a registry-delisting gate). Then D1 (#117) added **Claude Code** as a 5th first-class client (`~/.claude.json`, user-global `mcpServers`) — a rootKey-only `BaseAdapter` subclass; **user-global scope only**, per-project `projects[<path>].mcpServers` deliberately deferred. D1 is on `main`, ships next tag. |
| 2026-07-03 | Publish workflow: attach the release SBOM at `gh release create` time, never via a follow-up `gh release upload` | The repo has GitHub **immutable releases** enabled: uploading an asset to an already-created release returns HTTP 422. v0.17.0's publish succeeded on npm but failed the release-asset step (SBOM), so its GitHub release is asset-less and can't be amended. Fix (#116): pass `mcpm.cdx.json` as a positional asset to `gh release create` so it's sealed atomically. Correct from v0.18.0 on. GOTCHA for any future release-pipeline work. |
| 2026-07-03 | Wave-1 D4a: Gemini CLI as a 6th first-class client (`~/.gemini/settings.json`, user-global `mcpServers`) | Structural clone of D1 — another rootKey-only `BaseAdapter` subclass; verified the format against Google's gemini-cli docs (top-level `mcpServers`, home-relative on all platforms). Detector auto-enumerates it, so every client-iterating command works day one. **User-global scope only**, per-project `.gemini/settings.json` deliberately out of scope (same as D1). Caveat: Gemini reads `url`=SSE / `httpUrl`=HTTP; mcpm writes `url`, the existing non-Cursor URL-transport caveat. Merged to `main` (#120), ships next tag. |
| 2026-07-03 | Wave-1 D6: distribution — install matrix + `docs/DISTRIBUTION.md`; name-collision resolved by NOT fighting for `mcpm` | Verified with data: `mcpm` is ALREADY the homebrew-core formula for the unrelated **mcpm.sh** (Path Integral, v2.15.0, homepage mcpm.sh) + it squats PyPI; its bin is also `mcpm`. DECISION: don't publish a colliding `brew install mcpm` / claim the `mcpm` mise short-name (would fight a name we don't own + conflict on the `mcpm` bin). Distribute via collision-free SCOPED channels: README install matrix = npm/npx/pnpm + **mise `npm:@getmcpm/cli`** (mise's built-in npm backend, verified vs mise docs — NO registry PR needed). `docs/DISTRIBUTION.md` documents it. DEFERRED (external repos / outward-facing → need owner auth, NOT blocking): `getmcpm/homebrew-mcpm` tap (non-colliding formula name + bin decision) + mise registry short-name PR + devcontainer (needs project-scope adapter). Docs-only. On `main`, ships next tag. |
| 2026-07-03 | Wave-1 D3: `mcpm audit --sarif` — SARIF 2.1.0 for GitHub code-scanning | Pure `src/output/sarif.ts` mapper beside the `--json` branch. One rule per real `Finding.type` (the union is **8** now, not the roadmap's "7"; the rule catalog is a TS-exhaustive `Record<Finding["type"],...>` so a new type fails the build). Each finding → a SARIF result anchored **file-level** to `mcpm.yaml` (audit scans INSTALLED servers → no source line; a fake line number would be a lie), + a `logicalLocation` naming the server, severity→`error`/`warning`/`note`, and a stable `partialFingerprints` (`server:type:sha256(msg)[:12]`) so GitHub tracks the same alert across runs. Report-only (never fixes, even with `--fix`); exit matches `audit` (risky→1) — README documents the `upload-sarif` + `if: always()` pattern. `__PKG_VERSION__` (tsup define) guarded with a `typeof` fallback since it's undefined under vitest. On `main`, ships next tag. |
| 2026-07-03 | Wave-1 D2: `mcpm verify` (repo-only, client-free CI gate) + a composite GitHub Action; `classifyIntegrity`/`frozenVerdict` extracted to `src/stack/frozen-verify.ts` | The critique premise held: NO existing command runs on a hosted runner (`up` hard-fails at Step 3 on zero detected clients; `sync --check`/`audit` are vacuously green). So `mcpm verify` runs the SAME fail-closed integrity pass as `up --frozen` — extracted `classifyIntegrity` + a NEW pure `frozenVerdict` (structured pass/block decision) into a shared module both consume — but client-free (no detection, no `~/.mcpm`, no writes). Same BLOCK semantics + exit codes (1 on drift/unverifiable/format-mismatch/mixed-missing-baseline; benign refuse on lock-wide no-baseline; pypi/oci/url = unenforceable notice; exit 1 on no-lock, and verify NEVER auto-locks). `up`'s output/block-matrix is byte-identical (13 frozen tests unchanged — the refactor's safety net). `--json` emits a VerifyModel (`schemaVersion:1`). Composite Action `.github/actions/mcpm-verify` (step summary from `--json`, static shields badge, pre-commit snippet). **ONE verb**: B3 later EXTENDS `mcpm verify` with Sigstore provenance, never forks it. v1 = npm `dist.integrity` only; stack-vs-lock staleness deferred. On `main`, ships next tag. |
| 2026-07-03 | Wave-1 D7: `doctor --json` / `--report` via a pure `DoctorModel` builder + redacted report; fixes the `mcpm_doctor` `issues: []` bug | `doctorHandler` split into `buildDoctorModel` (pure, structured — `schemaVersion:1`, clients w/ server+guarded counts, runtimes, advisory drift, typed issues, `ok`) → renderers. `--json` emits the model; the MCP `handleDoctor` now REUSES the model (was returning hardcoded `issues: []` + detected-clients-only). `--report` = a redacted paste-for-bug-reports snapshot: OS/arch, mcpm+node versions, per-client server **counts**, runtime availability, confine + secret-store backend, issue **counts** — deliberately **no server names/args** (issue messages embed names → reduced to counts; the tested security invariant). New `.github/ISSUE_TEMPLATE/bug.yml` requires a pasted report (telemetry-free friction channel). D7 is the FIRST of the four structured-output mappers (D3/D7/E2/E6) to land: the shared "one model" is the `schemaVersion` + JSON convention, NOT a monolithic type — doctor issues stayed a doctor-specific typed list, deliberately NOT forced into the audit `Finding` shape (different domain). doctor `--json`/`--report` shapes UNSTABLE (added to CONTRACTS). On `main`, ships next tag. |
Expand Down
17 changes: 15 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,22 @@ The risky part of an MCP server doesn't show up at install -- it shows up while

## Quick start

```bash
npm install -g @getmcpm/cli
Install with the package manager you already use:

| Method | Command |
|---|---|
| **npm** (global) | `npm install -g @getmcpm/cli` |
| **npx** (no install) | `npx @getmcpm/cli <command>` |
| **pnpm** | `pnpm add -g @getmcpm/cli` |
| **mise** | `mise use -g npm:@getmcpm/cli` |

The binary is `mcpm`. **Heads up:** the `mcpm` Homebrew formula is a *different,
unrelated* project ([mcpm.sh](https://mcpm.sh)) — install this mcpm via
npm/npx/pnpm/mise above (all resolve the scoped `@getmcpm/cli` package, so there's
no name collision). A dedicated Homebrew tap is deferred; see
[`docs/DISTRIBUTION.md`](docs/DISTRIBUTION.md).

```bash
mcpm search filesystem
mcpm info io.github.domdomegg/filesystem-mcp
mcpm install io.github.domdomegg/filesystem-mcp
Expand Down
46 changes: 46 additions & 0 deletions docs/DISTRIBUTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Distribution

mcpm ships as the scoped npm package **`@getmcpm/cli`** (bin: `mcpm`). Every install
channel below resolves that one package, so there is no ambiguity about what you get.

## Install matrix

| Method | Command | Notes |
|---|---|---|
| **npm** | `npm install -g @getmcpm/cli` | global bin `mcpm` |
| **npx** | `npx @getmcpm/cli <command>` | no install; always latest |
| **pnpm** | `pnpm add -g @getmcpm/cli` | |
| **mise** | `mise use -g npm:@getmcpm/cli` | via mise's built-in `npm:` backend — no registry entry needed |

`npx @getmcpm/cli` is what the CI surfaces (`mcpm verify`, `mcpm audit --sarif`) and
the GitHub Action use, so it is the most-exercised path.

## The `mcpm` name collision (why no `brew install mcpm`)

An **unrelated** project — Path Integral's [mcpm.sh](https://mcpm.sh) — already owns
the short name `mcpm`:

- it is the **`mcpm` formula in Homebrew core** ("Open source, community-driven MCP
server and client manager", homepage `mcpm.sh`), and
- it squats the `mcpm` name on PyPI.

Its bin is also called `mcpm`. So we deliberately **do not** publish a colliding
`brew install mcpm` or claim the `mcpm` short-name on registries that already point at
mcpm.sh — that would fight over a name we don't own and would conflict on the `mcpm`
binary if a user had both installed.

**Resolution:** distribute through the scoped, collision-free channels above
(`@getmcpm/cli` on npm; `npm:@getmcpm/cli` on mise). The `getmcpm` scope and the
`getmcpm/cli` repo are the canonical identity.

## Deferred

- **Homebrew tap** (`getmcpm/homebrew-mcpm`) — a tap could ship a non-colliding
formula (e.g. `getmcpm`), but it needs a separate repo under the org and a decision
on the installed bin name (it can't shadow core `mcpm`). Deferred until there's
demand the npm/npx/mise channels don't already cover.
- **mise registry short-name** (`mise use mcpm`) — would require a PR to the mise
registry for a name that resolves to mcpm.sh's world; the `npm:@getmcpm/cli` backend
form works today without it.
- **devcontainer feature** — deferred until a project-scope adapter exists (inside a
container there are no host GUI client configs to manage).
16 changes: 12 additions & 4 deletions docs/ROADMAP-ADOPTION.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,11 +202,19 @@ The completeness critic's verdict: the candidate set over-indexed enterprise for
`mcpm.yaml` (file-level, no fake line numbers) + a `logicalLocation` for the server
name + a stable `partialFingerprints`. Report-only, exit matches `audit` (risky→1).
GitHub code-scanning upload documented in the README. **Cut:** guard-events SARIF.
- **D6 · Distribution (trimmed).** mise registry PR (near-zero — npm backend alias) +
`getmcpm/homebrew-mcpm` tap + README install matrix. Framed honestly as
convenience, not enterprise unblock. **Cut:** devcontainer feature until a
- **D6 · Distribution (trimmed). ✅ SHIPPED** (the in-repo slice; on `main`, ships next
tag). **Name-collision blocker RESOLVED with data**: `mcpm` is already the
homebrew-core formula for the unrelated [mcpm.sh](https://mcpm.sh) (v2.15.0) and it
squats PyPI — its bin is also `mcpm`. **Decision:** do NOT fight for `brew install
mcpm` / the `mcpm` mise short-name; distribute through the collision-free scoped
channels. Shipped: README install matrix (npm / npx / pnpm / **mise** via the
built-in `npm:@getmcpm/cli` backend — verified against mise docs, **no registry PR
needed**) + `docs/DISTRIBUTION.md` documenting the decision. **Deferred (external
repos / outward-facing, need owner action):** the `getmcpm/homebrew-mcpm` tap (a
non-colliding formula name + bin decision) and a mise registry short-name PR — both
documented as deferred, not blocking. **Cut:** devcontainer feature until a
project-scope adapter exists (inside a container there are no host GUI configs to
manage). **Blocker:** resolve the mcpm.sh name collision first.
manage).

**Awareness (non-code, runs alongside Wave 1):** submit to the MCP-security
roundups/awesome-lists that currently omit mcpm; a comparison page (mcpm vs
Expand Down
Loading