Skip to main content

Ninchi CLI and MCP server

Run Ninchi checks from a terminal, Git hook, CI job, or MCP-capable editor. The package is a thin HTTP client: it does not import the Ninchi backend or question engine.

A programmatic check records evidence of demonstrated understanding tied to the submitted artifact. It is a separate evidence class from pull-request verification, not a measurement of latent ability, mastery, or competency.

Install

Install the published package from PyPI:

pipx install ninchi-cli

Use ninchi-cli 0.2.1 or later. Version 0.2.0 predates an API change in how a check reports that its question is still being generated. Against the current API, 0.2.0 shows an empty question instead of waiting for it. Check the installed version with pipx list and upgrade with pipx upgrade ninchi-cli.

Development install from a checkout

From the repository root:

pipx install ./cli

For editable development with test dependencies:

cd cli
python3.12 -m venv .venv
.venv/bin/pip install -e ".[dev]"
.venv/bin/pytest -q

The package requires Python 3.11 or newer.

PATH and GUI editors

After the verified public pipx install ... command:

pipx ensurepath

Close and reopen the shell, then verify:

command -v ninchi
ninchi --help

If an MCP client reports the literal error spawn ninchi ENOENT, it cannot find the executable. GUI editors often do not inherit the PATH used by an interactive shell. Run command -v ninchi (or which ninchi) in a terminal and use the printed absolute path in the MCP configuration.

Prerequisites

GitHub is not required. A check is submitted directly to an organization. It needs no repository, pull request, or source-control connection.

  1. Have a Ninchi account. Any sign-in method works: email and password (with optional TOTP MFA), your organization's SAML single sign-on, or a GitHub, GitLab, or Bitbucket account.
  2. Join the target Ninchi organization. To start an organization without a source-control connection, create a Direct organization at /dashboard/create-org and invite members by email. GitHub, GitLab, and Bitbucket organizations work the same way for programmatic checks.
  3. Find its ID in the dashboard URL — the segment after /dashboard/orgs/. Both identifier forms work: a numeric id (/dashboard/orgs/42 → --org-id 42) or the org's opaque UUID (/dashboard/orgs/1b671a64-… → --org-id 1b671a64-…). New dashboard URLs use the UUID form; numeric ids in existing configs keep working indefinitely. The CLI does not currently have an org-list command.
  4. Ask an org admin to open org settings, choose the Challenges tab, find the Programmatic Checks (CLI & MCP) card, and turn on Enable CLI & MCP check submission. The same card sets Answers submitted by AI agents, which defaults to blocked (see Answering-actor semantics).
  5. Make sure the organization's plan includes programmatic checks. The Hobbyist / Free plan has a quota of zero, including for a new Direct organization.

Programmatic checks have a hard quota per developer per org in a trailing 30-day window. The limit comes from the org's plan, and checks you submit to one org do not count against another org's quota. A developer who belongs to two orgs has a separate allowance in each.

Plan Checks per developer, per org
Hobbyist / Free 0
Standard 20
Pro 100
Enterprise 100+ (operator-configured, never below 100)

When the quota is exhausted, the API returns HTTP 429. On a plan that allows some checks, the response also states the UTC time when the next check becomes available. At that time, enough checks have left the window to make room for another check. A plan downgrade can leave you above the cap. Several checks must then leave the window, and the stated time moves later.

A zero-allowance plan never frees a slot, so the response carries no reset time. Waiting does not help. The org needs a plan with a quota above zero.

Authentication

Device login

ninchi login

The CLI:

  1. prints a verification URL and a code such as XXXX-XXXX;
  2. waits while you sign in and approve the code at /dashboard/device; and
  3. saves the issued personal access token to ~/.config/ninchi/credentials.

The credentials file is mode 0600; its parent directory is mode 0700. NINCHI_CONFIG_DIR overrides the directory. Otherwise the CLI honors XDG_CONFIG_HOME before falling back to ~/.config/ninchi.

Login flags:

--timeout SECONDS   approval timeout (default: 600)
--api-url URL       API override (default: https://api.ninchi.ai)
--json              machine-readable output

CI token

CI should use an environment variable rather than the credentials file:

export NINCHI_API_TOKEN="ninchi_pat_..."
export NINCHI_ORG_ID=42

Create and revoke PATs at /dashboard/profile. A token is shown once, is stored by Ninchi as an HMAC-SHA256 digest, and stops working immediately after revocation. The available scopes are:

  • checks:read — read challenges, results, and personal history.
  • checks:write — submit checks and answers.

Device login requests both scopes by default. Manually created tokens also default to both scopes.

NINCHI_API_URL or --api-url can target another API environment.

CLI usage

ninchi check

With no source flag, check submits the working-tree diff from git diff HEAD:

ninchi check --org-id 42

Choose exactly one alternate subject source:

git diff | ninchi check --stdin --org-id 42
ninchi check --staged --org-id 42
ninchi check --commit HEAD --org-id 42
ninchi check --file src/app.py --language python --org-id 42

Set the org once for subsequent commands:

export NINCHI_ORG_ID=42
ninchi check --staged

All check flags:

--stdin
--staged
--commit SHA
--file PATH
--language LANGUAGE
--org-id ID
--answer TEXT
--actor human_attested|agent_assisted|agent_autonomous
--poll-interval SECONDS
--timeout SECONDS
--api-url URL
--json

--language is a hint for --file. --poll-interval defaults to 2 seconds; --timeout defaults to 600 seconds.

Automation can answer non-interactively and emit JSON:

ninchi check --staged \
  --answer "$ANSWER" \
  --actor agent_assisted \
  --json

If --json is used without --answer, the CLI returns the generated challenge and exits 2 with guidance to pass an answer.

Answering-actor semantics

  • An answer typed interactively defaults to human_attested.
  • --answer, or a process with no terminal, defaults to agent_assisted.
  • --actor overrides the default. Use the value that honestly describes who authored the answer:
    • human_attested: the human wrote the answer.
    • agent_assisted: a human meaningfully directed the answer's content.
    • agent_autonomous: an agent authored the answer.

The server enforces the organization's agent-answer policy. Never relabel an answer to bypass that policy.

ninchi result

ninchi result CHALLENGE_ID
ninchi result CHALLENGE_ID --json

ninchi history

ninchi history
ninchi history --limit 50
ninchi history --limit 50 --json

The default limit is 20. The server accepts limits from 1 to 100.

Exit codes

  • 0: challenge passed.
  • 1: challenge failed.
  • 2: pending state, configuration error, authentication error, API error, or another operational error.

Pre-push hook

Example .git/hooks/pre-push:

#!/usr/bin/env bash
set -euo pipefail

base="${NINCHI_BASE_REF:-origin/main}"
git diff --patch "$base"...HEAD | ninchi check \
  --stdin \
  --org-id "${NINCHI_ORG_ID:?set NINCHI_ORG_ID}"

Make it executable:

chmod +x .git/hooks/pre-push

This form is human-attested: it omits --answer and prompts in the terminal.

CI

The CLI needs only two environment variables, NINCHI_API_TOKEN and NINCHI_ORG_ID, so it runs in any CI system: GitLab CI, Bitbucket Pipelines, Jenkins, and others. The GitHub Actions job below is one example. The same pipeline works elsewhere: install with pipx, pipe the diff against the target branch into ninchi check --stdin --json, and store the token as a masked secret.

GitHub Actions

name: Ninchi check
on:
  pull_request:

jobs:
  ninchi:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
      - run: pipx install ninchi-cli
      - name: Submit Ninchi check and surface the challenge
        run: |
          git diff --patch origin/${{ github.base_ref }}...HEAD |
            ninchi check --stdin --json | tee ninchi-challenge.json || true
        env:
          NINCHI_API_TOKEN: ${{ secrets.NINCHI_API_TOKEN }}
          NINCHI_ORG_ID: ${{ vars.NINCHI_ORG_ID }}

Store the PAT in GitHub Secrets. This job stops at submission by design: with --json and no --answer, the CLI prints the challenge JSON — including the question — and exits 2 (the || true keeps the step green). The answer must come from whoever authored the change; do not wire a canned or scripted answer into CI. The question is generated per submission, and the answer window (the org-configured TTL) starts once the challenge is first viewed — so have authors answer interactively where they work, such as the pre-push hook above or an MCP-capable editor.

MCP server (ninchi mcp)

Authenticate once with ninchi login, then configure the stdio server in an MCP-capable client.

Run:

command -v ninchi

Use that output as command:

{
  "mcpServers": {
    "ninchi": {
      "command": "/absolute/path/from/command-v-ninchi",
      "args": ["mcp"]
    }
  }
}

Cursor accepts this shape in .cursor/mcp.json or its global MCP settings. Other MCP clients use the same command and argument, though their config-file location differs.

Alternative when the editor inherits PATH

{
  "mcpServers": {
    "ninchi": {
      "command": "ninchi",
      "args": ["mcp"]
    }
  }
}

If this produces spawn ninchi ENOENT, use the absolute-path form.

Five MCP tools

Tool Purpose
submit_check Submit exactly one diff, content block, or file region and start question generation.
get_challenge Fetch the question, optionally waiting while generation finishes.
submit_answer Submit an answer with a required, honest actor claim.
get_result Fetch score and feedback, optionally waiting while evaluation finishes.
list_history List the authenticated developer's recent challenges.

submit_answer requires actor. When the org policy is blocked, the tool returns guidance to hand the question to the human and relay only the human's own words as human_attested. When the policy is recorded, agent_assisted and agent_autonomous answers are accepted and stored with that provenance.

MCP check submissions include:

{
  "client_attribution": "mcp:<client>"
}

This names the submitting channel and feeds AI-authorship analytics as a detection-floor signal. It does not, by itself, assert that AI assisted the submitted work. When the client name is unavailable, the server uses mcp.

Evidence and score semantics

Programmatic checks use origin="api" and are stored as a distinct, lower-weight evidence class. Their question, answer, score, artifact context, answering-actor claim, client attribution, and timestamps remain inspectable.

By default, this evidence does not count toward Ninchi Scores. An org admin may opt in with Count programmatic checks toward Ninchi Scores in the Programmatic Checks card. Pull-request evidence is unaffected either way.

Troubleshooting

Symptom Resolution
spawn ninchi ENOENT The editor cannot find the executable. Run pipx ensurepath, open a new shell, run command -v ninchi, and use the resulting absolute path in the MCP config.
403: programmatic checks are not enabled An org admin must enable Enable CLI & MCP check submission in Programmatic Checks (CLI & MCP) on the Challenges tab of org settings.
429 The trailing-30-day quota for this developer in this org is exhausted. When the plan allows some checks, the response gives the UTC time the window next frees a slot. On a zero-allowance plan (Hobbyist / Free) there is no reset time to report; the org needs a plan that includes programmatic checks.
403 on an agent answer The org's programmatic_agent_answers policy is blocked. Hand the question to the human; submit only their own words as human_attested.
Challenge expired The answer window starts when a ready challenge is first viewed. The org configures the TTL; the shipped default is 5 minutes.
Status generating or answered Generation and grading are asynchronous. generating means the question is not ready; answered means grading is still running. Keep polling get_challenge or get_result until a terminal result or client timeout. The MCP tools report a timed-out wait as generating or evaluating.
Empty question, then an error on answer You are running ninchi-cli 0.2.0, which does not wait for generation against the current API. Run pipx upgrade ninchi-cli.

CLI errors are printed to stderr, or as JSON with --json, and exit 2.

Metadata

Release files for ninchi-cli 0.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ninchi-cli 0.2.1
File Size Uploaded
ninchi_cli-0.2.1.tar.gz 31.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for ninchi-cli 0.2.1
File Interpreter ABI Platform
ninchi_cli-0.2.1-py3-none-any.whl Python 3 none any Details

Total release size: 51.0 kB

Release files / ninchi_cli-0.2.1.tar.gz

Download URL ninchi_cli-0.2.1.tar.gz
Size 31.0 kB
Tags Source
SHA-256 checksum
How to use checksums
d9e276a358ec3819750d77ea7f182e0639718c3fe784b83bb083b7438fc8f767
BLAKE2b-256 checksum
How to use checksums
e3c84aedf9ccb7790f8ae43853c51f71ff4f5a7d8dbdbc4e60a5a3d013d98d86
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 15, 2026.

Transparency log

Release files / ninchi_cli-0.2.1-py3-none-any.whl

Download URL ninchi_cli-0.2.1-py3-none-any.whl
Size 20.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
36f4dfe45dcc31caeb617efe40d46b5b1c2b9325c0539b16d79a0d4e631b65f4
BLAKE2b-256 checksum
How to use checksums
321d7409071ccfda0ae330b1874f6359fba20dfe2a5c2390180eeece2519d2e5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 15, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.1 This release

2 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page