Skip to content

Latest commit

 

History

History
121 lines (95 loc) · 5.68 KB

CONTRIBUTING.md

File metadata and controls

121 lines (95 loc) · 5.68 KB

Contributing to mcpm

Thanks for your interest. mcpm is a security tool, so the bar is correctness and determinism over feature breadth.

Agent-assisted contributions

AGENTS.md is the shared contributor entry point; Claude Code imports it through CLAUDE.md. Repo-owned development/release skills and current project context are indexed in maintainer workflow. The separately distributed public mcpm usage skill is for CLI consumers; installing the npm package does not install contributor skills.

Development setup

Requires Node.js ^22.22.2 || ^24.15.0 || >=26.0.0 and pnpm 10. That range is the intersection of every runtime dependency's own engines, not a preference — src/__tests__/engines-invariant.test.ts fails the build if package.json promises a Node a dependency does not support.

pnpm install --frozen-lockfile
pnpm run typecheck      # tsc --noEmit + tooling configs (this is also `lint`)
pnpm run build          # tsup -> dist/
pnpm test               # vitest
pnpm run test:coverage  # with coverage

CI uses pnpm install --frozen-lockfile. package.json carries pnpm.overrides; Dependabot's regenerated pnpm-lock.yaml drops that block, so npm bump PRs fail with ERR_PNPM_LOCKFILE_CONFIG_MISMATCH before tests run. Refresh the lockfile locally with pnpm update <pkg> (not frozen) so the overrides survive, rather than merging Dependabot's lockfile. GitHub Actions bumps are unaffected.

@types/node is pinned to the engines floor (22), so a local typecheck only proves the code compiles against Node 22's API. CI re-runs it per matrix leg against that leg's own typings. To reproduce a Typecheck against @types/node <major> failure, use an isolated clean checkout so restoring the two experiment files cannot erase existing work:

pnpm add -D @types/node@26   # or 24 — the major CI failed on
pnpm run typecheck
git checkout -- package.json pnpm-lock.yaml

Dogfooding a release

pnpm dogfood:release packs the tarball, clean-installs it, and smoke-runs the real binary. The supported tag-release workflow requires this gate before publishing. A direct local pnpm publish only runs the package's build lifecycle hook; it does not automatically run this smoke. The smoke uses your local build and needs a Node inside engines.node.

To smoke an already-published version instead, set MCPM_DOGFOOD_SPEC — the same script, same assertions, no build and no pnpm:

MCPM_DOGFOOD_SPEC=@getmcpm/cli@latest ./scripts/dogfood-release.sh

Three places to run that, in increasing order of "not your machine":

where how good for
your machine the command above fastest, but needs a conforming Node, and it is your machine
a container docker run --rm -v "$PWD/scripts:/w/scripts:ro" -w /w -e MCPM_DOGFOOD_SPEC=@getmcpm/cli@latest node:26 ./scripts/dogfood-release.sh any Node major, disposable, no local Node needed
GitHub Actions → Dogfood → Run workflow Node 22/24/26 + macOS, zero local setup

The release script sets a throwaway $HOME for its CLI smoke commands, so doctor sees synthetic client state. Build, pack and npm installation happen before that switch and use the normal home/caches; this is not an OS sandbox for package installation. dogfood-confine.sh also uses a disposable home for its fixture commands; pnpm dogfood:confine builds before running that script.

macOS-only end-to-end check for the --confine sandbox:

pnpm dogfood:confine    # hermetic; must print "✓ confine dogfood PASSED"

For offline direct/guarded stdio comparisons using the pinned published release and official SDK v1/v2 peers, run bash scripts/dogfood-protocol.sh /tmp/mcpm-protocol-evidence. Docker provisions the test dependencies, then runs with no network or host mounts. See the matrix, evidence and limits.

For installed VS Code/Claude Code checks inside disposable macOS VMs, see native client validation, including its account/VM-free self-check command. Native VM runs require the separately pinned public client/runtime payload. Candidate changes must use the explicit candidate mode and a fresh packed CLI; a passing published-baseline run does not validate changed production code. Use the separate --approvals native suite for default tool consent; the ordinary lifecycle suite preapproves fixture tools and cannot establish that permission behavior.

Ground rules

  • Tests first, and they must pass. New behaviour needs a test; bug fixes need a failing repro test that your change turns green. Keep the suite and typecheck green — CI runs them on Node 22/24/26.
  • Local-first, deterministic, no telemetry. The default path makes no model-API calls and sends no usage data. Don't add a hosted backend or a network call on a path the user didn't ask for. New runtime dependencies are avoided; if one is genuinely needed, call it out explicitly in the PR.
  • Validate at the boundary. External data (registry responses, config files, tool traffic) is parsed through Zod before use. Fail closed on integrity/security boundaries; state boundaries honestly in user-facing copy.
  • Conventional commits. feat:, fix:, refactor:, docs:, test:, chore:, perf:, ci:. Update CHANGELOG.md under ## [Unreleased] for user-visible changes.

Pull requests

Keep PRs single-purpose and small enough to review. Describe the problem, the change, and how you verified it. Changes under .github/workflows/ require CODEOWNERS review.

Reporting security issues

See SECURITY.md — please do not open a public issue for a vulnerability.