npm package: @ppippi-dev/batoniq
Description: Open-source Batoniq CLI and MCP workflow package
Latest version: 0.1.0

# @ppippi-dev/batoniq

Publishable Batoniq CLI and MCP package for kanban workflows.

## Features

- MCP stdio transport (`batoniq-kanban-mcp`)
- Direct CLI (`batoniq`)
- Tools:
  - `task.create`
  - `task.list`
  - `task.get`
  - `task.move`
  - `approval.request`
- Dual backend mode:
  - `in-memory` (default)
  - `api` (HTTP proxy to an explicitly configured Batoniq API)
- Project-scoped API mode with operator/reviewer auth separation
- Optional machine-readable workflow artifacts on write tools (`artifact` / `artifacts`)

## Use direct CLI

Install globally:

```bash
npm install -g @ppippi-dev/batoniq
batoniq info --json
```

Or run once with `npx`:

```bash
npx @ppippi-dev/batoniq info --json
```

From the repo root:

```bash
npm run batoniq:cli -- info --json
npm run batoniq:cli -- task list --json
npm run batoniq:cli -- task create --title "Ship release notes"
```

CLI env:

```bash
BATONIQ_BACKEND=api
BATONIQ_API_BASE_URL=http://localhost:4000
BATONIQ_API_PROJECT_ID=42
BATONIQ_API_KEY=
BATONIQ_API_OPERATOR_TOKEN=
BATONIQ_API_REVIEWER_TOKEN=
BATONIQ_UPSTREAM_TIMEOUT_MS=5000
BATONIQ_TOOL_TIMEOUT_MS=5000
```

- The direct CLI accepts the same project-scoped auth model as the MCP path.
- Published tarballs bundle `@batoniq/contracts`, so the installed `batoniq` binary works without a separate contracts package install.
- Legacy `BATONIQ_MCP_BACKEND`, `BATONIQ_MCP_UPSTREAM_TIMEOUT_MS`, and `BATONIQ_MCP_TOOL_TIMEOUT_MS` env names remain accepted for compatibility.

## Start MCP server (stdio)

Install package dependencies once:

```bash
npm install --prefix ./packages/oss-mcp-kanban-server
```

Start stdio server:

```bash
node ./packages/oss-mcp-kanban-server/src/cli.mjs stdio
```

## What MCP is and which path to choose

MCP lets AI clients talk to your Batoniq project using a project-scoped key.

- Start with `Claude Code` when you want the fastest project-scoped setup path.
- Use the direct `batoniq` CLI when you want repo-local command execution without registering an MCP client.
- Use `Local (stdio)` when your client can run commands on this machine.
- Use `Docker (stdio)` only when your client runs in a container or cannot use the local Node runtime.

Common setup mistakes:

- Replace `/ABSOLUTE/PATH/TO/batoniq` before saving the config.
- Copy the one-time secret immediately after creating a key, or rotate the key and generate a new one.
- If you see `<separate-key-required>`, create another project key for the missing permission and place it in the matching env var.
- If you use the Docker example, confirm `BATONIQ_API_BASE_URL` points to a reachable API host before saving the config.

For Claude Code, register the repo-local server with:

```bash
claude mcp add --transport stdio --scope project \
  --env BATONIQ_BACKEND=api \
  --env BATONIQ_API_BASE_URL=http://localhost:4000 \
  --env BATONIQ_API_PROJECT_ID=42 \
  --env BATONIQ_API_KEY=<api-key-with-project-42-operator-and-project-42-reviewer> \
  batoniq-kanban -- node /ABSOLUTE/PATH/TO/batoniq/packages/oss-mcp-kanban-server/src/cli.mjs stdio
```

Use `--scope project` when you want Claude Code to write the config into this repository's `.mcp.json`.
When one project key already carries both scopes, `BATONIQ_API_KEY` is the fastest onboarding path. Keep separate `BATONIQ_API_OPERATOR_TOKEN` and `BATONIQ_API_REVIEWER_TOKEN` values for advanced least-privilege automation when you intentionally want distinct keys per role.

```bash
BATONIQ_BACKEND=in-memory
```

```bash
BATONIQ_BACKEND=api
BATONIQ_API_BASE_URL=http://localhost:4000
BATONIQ_API_PROJECT_ID=42
BATONIQ_API_KEY=
BATONIQ_API_OPERATOR_TOKEN=
BATONIQ_API_REVIEWER_TOKEN=
BATONIQ_UPSTREAM_TIMEOUT_MS=5000
BATONIQ_TOOL_TIMEOUT_MS=5000
```

- When `BATONIQ_API_PROJECT_ID` is set, API mode uses project-scoped endpoints (`/v1/projects/{project_id}/...`).
- If you provide any `BATONIQ_API_*` variables, you must also set `BATONIQ_BACKEND=api` (or the legacy `BATONIQ_MCP_BACKEND=api`); otherwise startup fails instead of silently using `in-memory`.
- API mode requires an explicit `BATONIQ_API_BASE_URL`; it no longer falls back to `http://localhost:4000` implicitly.
- `BATONIQ_API_KEY` can be used as a shared fallback for both operator/reviewer requests only when that key carries both project scopes (for example `project:42:operator` and `project:42:reviewer`).
- If you prefer separate least-privilege keys, set `BATONIQ_API_OPERATOR_TOKEN` to a project-scoped key that can create/move tasks and `BATONIQ_API_REVIEWER_TOKEN` to a project-scoped key that can approve/reject.

Recommended onboarding setup (single key with both project scopes):

```text
BATONIQ_API_PROJECT_ID=42
BATONIQ_API_KEY=<api-key-with-project:42:operator-and-project:42:reviewer>
```

Advanced least-privilege setup (separate project keys):

```text
BATONIQ_API_PROJECT_ID=42
BATONIQ_API_OPERATOR_TOKEN=<api-key-with-project:42:operator>
BATONIQ_API_REVIEWER_TOKEN=<api-key-with-project:42:reviewer>
```

This MCP package only proxies workflow task/approval endpoints. Collaboration/session routes such as teams, invitations, and `/auth/me` stay on the private SaaS API/web surface.

## Tool payloads

Write tools accept optional machine-readable artifact payloads:

- `task.create`
- `task.move`
- `approval.request`

`task.create` also accepts optional stage-specific prompt overrides that Batoniq persists on the task:

```json
{
  "tool": "task.create",
  "input": {
    "title": "Harden OAuth callback",
    "description": "Close the replay gap in the OAuth callback and keep the session flow backward compatible.",
    "stagePromptOverrides": {
      "dev": "Audit callback state validation and session rotation.",
      "qa": "Verify replay and stale state rejection paths."
    }
  }
}
```

- `stagePromptOverrides` is optional and accepts only `pm`, `dev`, `qa`, and `approval` keys.
- Values are trimmed before request dispatch; blank entries are omitted.

Artifact shape:

```json
{
  "artifact": {
    "stage": "qa",
    "kind": "test_report",
    "status": "passed",
    "summary": "Regression suite green",
    "content": {
      "tests": {
        "passed": 18,
        "failed": 0
      }
    }
  }
}
```

- `stage` defaults by tool flow (`pm`, `dev`, `qa`, `approval`) when omitted.
- `artifact` adds one record; `artifacts` accepts an array of records.
- In API mode, persisted task payloads include `artifacts`, `latestQaArtifact`, and `approvalGate` from the upstream Batoniq API.
- If the latest QA artifact status is blocking (`failed`, `blocked`, `error`, `rejected`), `approval.request` returns an error instead of approving.

Audit logs:

- Tool invocation audit records are written to `stderr` as JSON lines (`tool_call_start`, `tool_call_success`, `tool_call_failure`).

## Client config example (local)

```json
{
  "mcpServers": {
    "batoniq-kanban": {
      "command": "node",
      "args": [
        "/ABSOLUTE/PATH/TO/batoniq/packages/oss-mcp-kanban-server/src/cli.mjs",
        "stdio"
      ],
      "env": {
        "BATONIQ_BACKEND": "api",
        "BATONIQ_API_BASE_URL": "http://localhost:4000",
        "BATONIQ_API_PROJECT_ID": "42",
        "BATONIQ_API_KEY": "api-key-with-project-42-operator-and-project-42-reviewer"
      }
    }
  }
}
```

You can copy templates from:

- `packages/oss-mcp-kanban-server/examples/claude_desktop_config.example.json`
- `packages/oss-mcp-kanban-server/examples/claude_desktop_config.docker.example.json`

## Local smoke flow

```bash
docker compose -f ./docker-compose.dev.yml up --build -d
BATONIQ_API_BASE_URL=http://localhost:4000 ./scripts/docker-mcp-smoke.sh
docker compose -f ./docker-compose.dev.yml down
```

## Package dry run

From the repo root:

```bash
npm run oss:pack:dry-run
npm run oss:install:smoke
```

Example MCP calls:

```json
{
  "tool": "task.create",
  "input": {
    "title": "Ship release notes",
    "description": "Prepare public changelog",
    "artifact": {
      "summary": "PM plan ready",
      "content": {
        "plan": ["collect highlights", "draft release notes"]
      }
    }
  }
}
```

```json
{
  "tool": "approval.request",
  "input": {
    "taskId": "task-0001",
    "decision": "approve",
    "reason": "QA passed and release manager signed off",
    "artifact": {
      "stage": "approval",
      "summary": "Release approved",
      "content": {
        "ticket": "REL-42"
      }
    }
  }
}
```