Skip to content

Contributing

github-actions[bot] edited this page Sep 1, 2026 · 2 revisions

Contributing

Setting up

git clone https://github.com/doublegate/CyberChef-MCP.git
cd CyberChef-MCP
npm install
npx grunt configTests    # REQUIRED -- see Installation

Node >=24 <27. Package manager is npm — not pnpm, not bun.

The gates

Everything here must pass before a pull request is ready. CI runs the same commands.

npm run lint            # ESLint via grunt -- zero errors
npm test                # core: 241 Node-API + 2,289 operation tests
npm run test:mcp        # MCP: 1,111 tests across 39 files (Vitest)
npm run test:coverage   # thresholds: 95 statements / 96 lines / 88 branches / 96 functions
npm run testnodeconsumer

Two suites, not interchangeable: the core tests are custom Node runners, the MCP tests are Vitest.

Conventions

  • 4-space indentation. Upstream CyberChef style, not 2.
  • No prettier. Formatting is enforced by ESLint.
  • Conventional Commitsfeat(scope):, fix(scope):, and so on.
  • JSON imports use with { type: "json" }, not assert.
  • Two lint rules interact awkwardly and are worth knowing before you fight them: camelcase with properties: "always" and dot-notation together make a snake_case property unassignable after an object exists. Build such objects as literals with quoted keys, or use Object.assign.

The rule that matters most

Never hand-edit anything under src/core/ or the six upstream-owned files in src/node/. Add a patch under patches/fork/ instead. See Fork & Upstream for the incident that made this a rule.

Testing philosophy

Test through a real MCP client, not hand-rolled JSON-RPC. This is the project's most expensive lesson. Every test before v2.1.0 spoke raw JSON-RPC or called handlers directly — and raw JSON-RPC does no schema validation, so three releases shipped in which every one of 524 tools carried an empty inputSchema while the suite stayed green. An official SDK client rejects that response outright. tests/mcp/stdio-client-contract.test.mjs exists to close that gap.

Pin a known answer before implementing. For anything with a correct answer — a hash format, a factorisation, a pattern offset — write the vector first and verify the implementation against it. And generate the vector or cite where it came from; never recall it. A sha512crypt vector written from memory during v2.4.0 was 84 characters where the format has 86, and the reflex on a red test is to change the code rather than count the string.

Ask what makes it confidently wrong. Tests that cover what a tool is for miss the cases that matter most. Three correctness bugs in v2.4.0 — a square modulus, a digest colliding with a structural pattern, a fragment below the uniqueness window — all lived in inputs nobody had thought to construct.

Examples are executable. examples/ holds nine self-asserting scripts that CI runs. Documentation that is not executed drifts.

Pull requests

Several bots review automatically — CodeQL, Trivy, CodeRabbit, Copilot, and a self-hosted Antigravity reviewer. Reply to every review thread, whether you adopt, reject or defer, and verify each claim against the code before answering. In v2.4.0 two suggestions would have introduced bugs if taken on trust, and several correct ones were nearly dismissed.

Changing the wiki

Edit docs/wiki/ in the repository, not the wiki itself. The wiki is published from that directory by .github/workflows/wiki-sync.yml on every push to master that touches it, so an edit made in the wiki UI is overwritten by the next sync. The footer on every page says so.

That indirection buys something: wiki content goes through review like code, and the pages cannot drift from a release without the diff being visible in the same pull request.

Two things about the mechanism are worth knowing before you debug it:

  • The wiki is a separate git repository (<repo>.wiki.git), so the built-in GITHUB_TOKEN cannot push to it. The workflow uses a WIKI_TOKEN secret — a classic PAT with repo scope, because a wiki is not covered by any fine-grained token permission.
  • The wiki repository does not exist until the first page is saved in the web UI. There is no API for creating it. Before that, a push fails with "Repository not found", which reads like a permissions problem and is not — the workflow's job summary names both causes rather than guessing between them.

Page filenames become URLs: Client-Setup.md is /wiki/Client-Setup. Link between pages with a plain relative link — [Installation](Installation) — and keep _Sidebar.md in step, since a page absent from it is reachable only by URL.

Where decisions are recorded

  • docs/adr/ — architecture decisions, Nygard format
  • docs/internal/*-findings-log.md — what was measured during each release, including what the plan got wrong. These are worth reading before changing anything they cover.
  • docs/releases/ — release notes
  • docs/planning/ROADMAP.md — where things are going

Clone this wiki locally