# Nimbus BCI (pypi · nimbus-mcp)

AI agents build, train, and analyze BCI/EEG pipelines: data, models, experiments, live sessions.

- Trust score: 75/100 (medium)
- Change this week: +4
- Registry status: active
- Liveness: live
- Owner verified: no
- Last scored: 2026-10-24

## Components

- remote · `nimbus-mcp.fly.dev`: 36/100, [markdown](https://verifymcp.io/servers/nimbusbci-nimbus-mcp/nimbus-mcp-2.md), [page](https://verifymcp.io/servers/nimbusbci-nimbus-mcp/nimbus-mcp-2)
- pypi · `nimbus-mcp`: 75/100 (this document), [markdown](https://verifymcp.io/servers/nimbusbci-nimbus-mcp/nimbus-mcp.md), [page](https://verifymcp.io/servers/nimbusbci-nimbus-mcp/nimbus-mcp)

## Channel facts

- Registry: `pypi`
- Package: `nimbus-mcp`
- Version: `0.14.17`
- Transport: `stdio`

## Trust breakdown

How this component scores in each security and reliability category. Every signal is checked automatically from public evidence about the published package, including repeated runs of it in an isolated sandbox, and we only credit what we can confirm. Scores are 0–100 per category. Scoring method: https://verifymcp.io/docs/scoring (what has changed: https://verifymcp.io/docs/scoring/changelog)

Scored 2026-10-24.

- **Supply Chain Security**: 100/100
  - No malware found by supply-chain analysis.
  - No known CVEs affecting this package version or its production dependencies.
  - Runs hatchling.build at install time, a recognised build step with no custom scripting around it.
  - 1 of 22 dependencies flagged as unhealthy.
- **Provenance & Transparency**: 32/100
  - Source repository is publicly reachable at the declared URL.
  - Provenance check failed: no build-provenance attestation is published.
  - Licence could not be checked: the licence is declared in free text we could not match to a standard (SPDX) licence identifier, so we cannot tell whether it is OSI-approved.
  - Actively maintained (last published 1 days ago).
  - Disclosure check failed: no security disclosure policy was found in the source repository.
- **Schema Quality & AI Usability**: 81/100
  - 100% of prompts and resources have a non-trivial description (not blank, and not just the item's name).
  - AI-judged instruction clarity (excellent).
  - Context-footprint check failed: tool/resource definitions use about 10014 tokens (~158/item across 63 items; 63 tools + 0 resources), over budget; trim descriptions and params.
  - Usage-examples check failed: none of the tools include examples.
- **Stability & Change Management**: 46/100
  - Stability check failed: the tool surface changed between 0.11.0 and 0.14.17: 0 tool removals, 1 breaking changes, 20 additions.
- **Tool Coverage**: 100/100
  - 100% of tools have a non-trivial description (not blank, and not just the tool's name).
  - 100% of tool parameters carry a description.
  - Structured output schemas are declared (100% of tools); any adoption earns full credit.
- **Tool Safety**: 100/100
  - No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.
  - All 4 tool(s) whose name or description implies an irreversible operation declare an MCP destructiveHint annotation.
  - An AI judge read all 64 captured unit(s) of tool text and found none that tries to manipulate the model reading it.
- **Capabilities**: 100/100
  - Implements a current MCP spec version (2026-07-28).

## Install

### How do I install the Nimbus BCI MCP server?

Nimbus BCI runs locally as a PyPI package, launched with uvx nimbus-mcp. Ready-made configuration for Claude, Cursor, VS Code, Codex and 5 more is on this page, copied from each client's own documentation.

### Claude

```bash
claude mcp add nimbusbci-nimbus-mcp -- uvx nimbus-mcp
```

### Cursor

```json
{
  "mcpServers": {
    "nimbusbci-nimbus-mcp": {
      "command": "uvx",
      "args": [
        "nimbus-mcp"
      ]
    }
  }
}
```

### VS Code

```json
{
  "servers": {
    "nimbusbci-nimbus-mcp": {
      "command": "uvx",
      "args": [
        "nimbus-mcp"
      ]
    }
  }
}
```

### Codex

```bash
codex mcp add nimbusbci-nimbus-mcp -- uvx nimbus-mcp
```

### opencode

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "nimbusbci-nimbus-mcp": {
      "type": "local",
      "command": [
        "uvx",
        "nimbus-mcp"
      ],
      "enabled": true
    }
  }
}
```

### OpenClaw

```bash
openclaw mcp add nimbusbci-nimbus-mcp --command uvx --arg nimbus-mcp
```

### Hermes

```yaml
mcp_servers:
  nimbusbci-nimbus-mcp:
    command: "uvx"
    args: ["nimbus-mcp"]
```

### Netclaw

```json
{
  "McpServers": {
    "nimbusbci-nimbus-mcp": {
      "Transport": "stdio",
      "Command": "uvx",
      "Arguments": [
        "nimbus-mcp"
      ]
    }
  }
}
```

### Vellum

```bash
assistant mcp add nimbusbci-nimbus-mcp -t stdio -c uvx -a nimbus-mcp
```

### Other

```json
{
  "mcpServers": {
    "nimbusbci-nimbus-mcp": {
      "command": "uvx",
      "args": [
        "nimbus-mcp"
      ]
    }
  }
}
```

## Changelog

Every change recorded for this component, newest first. Days that predate change tracking, or that we cannot explain, say so: "we were watching and nothing happened" and "we were not watching" are different claims.

### 2026-10-24 (score 75, +15)

- [security improvement] Malware scan: unverified → pass

### 2026-10-23 (score 60, −12)

- [security regression] Malware scan: pass → unverified
- [functional improvement] Schema quality: unverified → 100
- [functional] Package version: 0.14.15 → 0.14.17

### 2026-10-22 (score 72, +15)

- [security improvement] Malware scan: unverified → pass

### 2026-10-21 (score 57, +1)

- [security improvement] Malware scan: unverified → pass
- [functional] Package version: 0.14.11 → 0.14.15

### 2026-10-20 (score 56, 0)

- [security improvement] Malware scan: unverified → pass
- [functional] Package version: 0.14.8 → 0.14.11

### 2026-10-19 (score 56, −15)

- [security regression] Malware scan: pass → unverified
- [security regression] Stability: 0.27 → fail
- [functional] Package version: 0.14.4 → 0.14.8

### 2026-10-18 (score 71, 0)

- [security regression] Tool safety: pass → unverified
- [security regression] Stability: 0.23 → unverified
- [functional regression] Tool coverage: 100 → unverified
- [functional regression] Capabilities: pass → unverified
- [functional] Destructive annotations: pass → 100
- [functional] Package version: 0.14.2 → 0.14.4

### 2026-10-17 (score 71, +1)

No change was recorded against any check on this day. Stability & Change Management went from 20 to 23. That category is still filling its 30-day observation window: 6 days of observed history at the previous scan, 7 at this one. The score rises as the window fills, whether or not the server changes.

## MCP tools (63)

### `account.whoami` (~95 tokens)

Account.Whoami

Who you are authenticated as: account email, plan (isPro / pioneer),
this month's free-run quota, and — with a hosted token — the token name
and days until it expires. Call this first when setup guidance appears
or to check which credential a session uses. In anonymous Local Free
mode there is no account: the response says so (mode/tier) instead of
demanding a login.

### `catalog.nodes` (~73 tokens)

Catalog.Nodes

List Nimbus pipeline node types (data, preprocessing, features, models...).

Use catalog.node_schema(node_type) for one node's full config schema and ports.

Input parameters:

- `category`: Optional filter — e.g. "data", "preprocessing", "features", "models" (exact category ids from the unfiltered list).

### `catalog.node_schema` (~48 tokens)

Catalog.Node Schema

Full config JSON schema + input/output ports for one node type.

Input parameters:

- `node_type` (string, required): Node id from catalog.nodes (e.g. "csp", "nimbus_lda").

### `catalog.templates` (~31 tokens)

Catalog.Templates

List built-in starter pipelines (MI/P300/SSVEP...).
catalog.template(id) returns the graph.

### `catalog.template` (~49 tokens)

Catalog.Template

Full template incl. the 'train' execGraph needed by execution.run/pipeline.validate.

Input parameters:

- `template_id` (string, required): Template id from catalog.templates (e.g. "mi_csp_lda").

### `catalog.datasets` (~56 tokens)

Catalog.Datasets

Curated public EEG datasets (MOABB packs) available to pipelines.

Input parameters:

- `only_on_disk` (boolean): Only return datasets whose data packs are present on this backend (True by default; False also lists known-but-missing sets).

### `catalog.leaderboard` (~269 tokens)

Catalog.Leaderboard

Public benchmark leaderboard: pipeline rankings per dataset.

Two tracks: the top-level ``within_session`` block (curated suite
pipelines) and ``crossSubject`` (leave-one-subject-out — curated
pipelines plus BYO ``python_model`` submissions; BYO rows carry
\``kind: "byo"`` and an ``owner`` name). Within each dataset, ``rows``
sort desc by ``meanAccuracyPct`` (95% CI in ``ciLoPct``/``ciHiPct``).
\``packFingerprint`` identifies the exact dataset pack behind the
scores; ``updated`` marks each dataset's most recent run. The
\``pretrainedFewTrial`` efficiency track means: accuracy at a
protocol-fixed 100-trials-per-subject budget — the frozen-backbone /
few-parameter rows that matter when calibration data is scarce
(``paramsTrained`` shown per row so cost is readable next to
accuracy). ``submittableDatasets`` is the set open for
\``leaderboard.submit``; a dataset can appear under ``crossSubject``
and still be closed for new submissions.

Input parameters:

- `dataset_id`: Optional dataset id. When set, every track is filtered to that dataset. Omit it for the full board.

### `leaderboard.submit` (~629 tokens)

Leaderboard.Submit

Submit a BYO python_model to a leaderboard track for server-side scoring.

Nimbus scores it server-side under the track's fixed protocol — you
submit code, never results. Poll ``leaderboard.status`` with the
returned ``submissionId``; scoring takes minutes (classical) to tens
of minutes (deep). Requirements must be plain PyPI names/specs (max
20, no URLs). Identical code resubmission is idempotent per track
(``deduplicated: true``).

Verification contract (tier model X10): ``verification="verified"``
(default) is the server-side-scored submission above — on the hosted
backend it is Pro-only; free accounts get a structured tier response.
\``verification="unverified_local"`` names the designed Account-Free
lane for locally-computed results; the backend has no unverified
submission kind yet (see the response it returns — nothing is
stored, and no row is faked). Anonymous callers get a structured
login prompt: a submission needs an identity. Local (desktop/dev)
backends keep their existing server-side quota behaviour.

Input parameters:

- `backbone_kind`: Checkpoint family for the BYO backbone lane — one of the zoo's supported kinds (e.g. "cbramod", "labram").
- `backbone_license`: License declared for the backbone checkpoint.
- `backbone_repo`: HF 'org/name' repo id for the BYO backbone lane.
- `backbone_revision`: Full 40-char commit SHA pinning the backbone.
- `class_name`: Contract class to instantiate; omit to auto-detect.
- `code`: Python source defining a class with fit(X, y, info) and predict(X) (<=256KB; validate first with python.validate). Required unless backbone_repo is set.
- `dataset_id` (string, required): A dataset id from catalog.leaderboard ``submittableDatasets`` (e.g. "BNCI2014_001"). A dataset can appear under crossSubject and still be closed for submission (BNCI2014_008 is scored there and is no…
- `name` (string, required): Display name on the board (<=100 chars).
- `requirements`: Optional PyPI requirements (e.g. ["sklearn>=1.4"]).
- `track` (string): "cross_subject" (default — full-data leave-one-subject-out, seed 42) or "pretrained_few_trial" (efficiency track: LOSO with a protocol-fixed 100-trials-per-subject cap; code that imports torch must d…
- `verification` (string): "verified" (default; server-side scored, Pro on the hosted backend) or "unverified_local" (Account-Free locally-computed lane; see the tool description for its current backend status).

### `leaderboard.status` (~68 tokens)

Leaderboard.Status

Fetch one of your leaderboard submissions (owner-only).

Returns status (queued | scoring | done | failed), metrics when done
(meanAccuracyPct + CI, same shape as leaderboard rows), or the error
that failed it.

Input parameters:

- `submission_id` (string, required): The submissionId returned by leaderboard.submit.

### `leaderboard.mine` (~20 tokens)

Leaderboard.Mine

List your leaderboard submissions (newest first), all datasets.

### `pipeline.validate` (~70 tokens)

Pipeline.Validate

Validate a pipeline graph before running.
ExecGraphSnapshot: {nodes: [{id, type, config}], connections: [{from, to}]}.
Build it from catalog.template(id).train or from scratch using catalog.nodes().

Input parameters:

- `train_graph` (object, required): The graph to validate ({nodes, connections}).

### `pipeline.validate_node` (~141 tokens)

Pipeline.Validate Node

Validate one node's config (schema + semantic checks).

Catches the same value errors as pipeline.validate for a single node
(inverted bandpass, CSP components vs channel count when dataset is set).

Input parameters:

- `config` (object, required): The node's config object to check.
- `dataset`: Optional MOABB pack id (e.g. "BNCI2014_001") so CSP/Nyquist checks can use channel count and sampling rate. Omitted → those context checks are skipped; inverted-band checks still run.
- `node_type` (string, required): Node type id from catalog.nodes (e.g. "csp", "bandpass_filter").

### `execution.run` (~239 tokens)

Execution.Run

Start a pipeline run (NON-BLOCKING). Returns executionId — poll with
execution.get() until status is completed/failed, then execution.results().
If an identical graph is already running for you, the run still starts but
the response carries a ``warning`` + ``duplicateOf`` ids (cancel one with
execution.cancel to avoid paying twice).
The server runs a limited number of executions at once (3 by default); when
all slots are busy the call fails immediately with 429
nimbus.execution.queue_full and a retryAfter (seconds) — wait, then retry.

Input parameters:

- `description`: Optional longer description of the experiment.
- `layout`: Optional canvas positions {nodes: {id: {x, y}}}; a deterministic grid is synthesized when omitted.
- `name`: Display name for the run (shown in the Runs list).
- `subject`: Optional dataset subject code (e.g. "S01") recorded with the run.
- `train_graph` (object, required): Pipeline graph {nodes: [{id, type, config}], connections: [{from, to}]} as built by catalog.template/pipeline.validate.

### `execution.cancel` (~32 tokens)

Execution.Cancel

Cancel a running execution.

Input parameters:

- `execution_id` (string, required): The run to terminate (from execution.run/execution.list).

### `execution.delete` (~85 tokens)

Execution.Delete

Permanently delete one of your finished runs (completed/failed/cancelled)
with its results. Running runs must be cancelled first (409
nimbus.execution.delete_not_terminal). Owner only.

Input parameters:

- `confirm` (boolean): MUST be true to delete — the explicit user go-ahead.
- `execution_id` (string, required): The run to delete (from execution.run/execution.list).

### `execution.get` (~109 tokens)

Execution.Get

Execution status summary (status: running/completed/failed/cancelled).

Summary fields: ``dataset`` is the human-readable display label
(e.g. "BCIIV2a"); ``datasetId`` is the catalog id (e.g. "BNCI2014_001")
accepted by other tools (data.inspect_dataset, public_data.dataset).
\``title`` is the run name shown in Studio.

Input parameters:

- `execution_id` (string, required): The run to check (from execution.run/execution.list).

### `execution.list` (~127 tokens)

Execution.List

Recent executions. Optional status filter (running/completed/failed/cancelled).

Per-run fields: ``dataset`` is the human-readable display label
(e.g. "BCIIV2a"); ``datasetId`` is the catalog id (e.g. "BNCI2014_001")
accepted by other tools (data.inspect_dataset, public_data.dataset).
\``title`` is the run name shown in Studio.

Input parameters:

- `limit` (integer): Maximum number of runs to return (default 20).
- `status`: Filter by run status: running/completed/failed/cancelled.

### `execution.results` (~74 tokens)

Execution.Results

Metrics for a completed run. Trimmed by default (accuracy, kappa, ITR,
confusion matrix, per-class); full=True returns the complete result object.

Input parameters:

- `execution_id` (string, required): The completed run to fetch metrics for.
- `full` (boolean): Return the backend's complete result object (all fields).

### `calibration.start` (~311 tokens)

Calibration.Start

Start a guided subject calibration session (NON-BLOCKING; confirm-gated —
the device goes on a human's head). The Nimbus Studio app shows the cues on
its calibration dashboard automatically; poll calibration.status.
Requires a Pro plan (hosted token or Pro session): calibration nodes and
custom-data training are gated by the freemium node policy; local
X-MCP-Key principals pass only when the desktop app flags the signed-in
Pro session (MCP_LOCAL_IS_PRO).

Input parameters:

- `classes`: Override class list [{id,label,cue}] (MI default: left/right hand).
- `confirm` (boolean): MUST be true — explicit user go-ahead for a session on their head.
- `connection_type`: Device selector when several exist (e.g. serial vs wifi).
- `device_type`: Device id from device.list (omit → template default synthetic).
- `ip_address`: Device IP for network/wifi devices.
- `ip_port`: Port for network devices.
- `mac_address`: Bluetooth MAC (BT devices).
- `name`: Display name recorded on the execution.
- `paradigm` (string): mi | p300 | sart | target_hit.
- `port`: Serial/COM port for wired devices.
- `serial_number`: Device serial (some BLE stacks).
- `source_id`: LSL source id.
- `stream_name`: LSL stream name (LSL devices).
- `trials_per_class`: Override the template's trial count (minimum 5).

### `calibration.status` (~64 tokens)

Calibration.Status

Live snapshot of a calibration session (phase, current trial, progress,
paused). Once complete, carries the recorded upload — call
calibration.train to turn it into the subject's own classifier.

Input parameters:

- `execution_id` (string, required): The calibration run to inspect (from calibration.start).

### `calibration.pause` (~35 tokens)

Calibration.Pause

Pause a running calibration between trials (cues hold; resume anytime).

Input parameters:

- `execution_id` (string, required): The calibration run to pause.

### `calibration.resume` (~27 tokens)

Calibration.Resume

Resume a paused calibration session.

Input parameters:

- `execution_id` (string, required): The calibration run to resume.

### `calibration.train` (~247 tokens)

Calibration.Train

Train the subject's own classifier from a COMPLETED calibration session
(NON-BLOCKING). Fetches the recorded upload, wires it into a train
pipeline as a custom_data source, and starts the run. Requires a Pro
plan (custom_data training is freemium-gated). The calibrate→train
handoff resolves on every backend, desktop-local included; only
recordings made before the desktop execution-linkage build resolve
no upload — rerun calibration.start for those.

Input parameters:

- `execution_id` (string, required): The COMPLETED calibration run (from calibration.start).
- `name`: Display name recorded on the training run.
- `paradigm` (string): Paradigm of the recording (mi | p300 | sart | target_hit); mi and p300 have default train templates.
- `template_id`: Train template to use (e.g. from catalog.templates); required for sart/target_hit (mi defaults to mi_headband_csp_lda, p300 to p300_hardware_xdawn_lda).
- `train_graph`: Explicit train graph instead of a template; its first data node (custom_data/public_data) is rewired onto the recording.

### `experiment.run` (~236 tokens)

Experiment.Run

Run 1-25 pipelines as ONE paced experiment (NON-BLOCKING). Returns an
experimentId immediately; a background thread submits at most 2 runs at
a time (min(max_concurrent, 2)), retries queue-full up to 3 times per
run, and polls each execution to completion. Poll experiment.get() for
per-run status and, once finished, aggregated metrics.

PORTABILITY: the experiment record is written through to the
Nimbus server under your account (at start and at completion), so
experiment.get works from ANY entry point — local stdio, hosted
gateway, another machine — signed in with the same account/token.
If the server is unreachable when the experiment starts it still
runs and polls locally, but other entry points cannot read it
until a write-through succeeds (#518).

Input parameters:

- `max_concurrent` (integer): Parallel submissions cap, clamped to 1-2 (default 2).
- `runs` (array, required): 1-25 entries, each {name: str, train_graph: {nodes, connections}} (same graph shape as execution.run's train_graph).

### `experiment.delete` (~79 tokens)

Experiment.Delete

Delete one of your experiment records (server + this machine's local
cache). Its individual runs are kept — delete them with execution.delete.
A still-running experiment cannot be deleted.

Input parameters:

- `confirm` (boolean): MUST be true to delete — the explicit user go-ahead.
- `experiment_id` (string, required): The experiment to delete (from experiment.run).

### `experiment.get` (~265 tokens)

Experiment.Get

Experiment snapshot: status (running/completed/failed), per-run rows
({name, executionId, status, error?, metrics?, evaluationKind?}) and,
once finished, aggregates over completed runs only: per-metric
{mean, std, best, n}, plus ``pooledExecutionIds`` / ``pooledRunNames``
/ ``kindBreakdown`` (per-subject vs LOSO etc. — each row has kind, n,
mean, std, executionIds, runNames; #470). Pooled headline means are
unchanged; kindBreakdown labels what was pooled. std is population;
None below 2 values. kindBreakdown is always present when aggregates
exist (including single-kind experiments).

PORTABILITY: experiment records are stored on the Nimbus server
under your account, so any entry point (local stdio, hosted
gateway, another machine) signed in with the same account/token
can read them; the serving process and the local
\~/.nimbus/experiments store remain as fast-path caches.
Experiments run before server-side storage landed exist only in
the origin machine's local store and read as unknown elsewhere
(#518).

Input parameters:

- `experiment_id` (string, required): The experiment to inspect (from experiment.run).

### `execution.artifacts` (~42 tokens)

Execution.Artifacts

Trained artifacts (models/filters, e.g. *.pkl) saved by an execution.

Input parameters:

- `execution_id` (string, required): Run whose artifacts to list.

### `execution.download_artifact` (~110 tokens)

Execution.Download Artifact

Download one artifact file.

Local/stdio: saved to NIMBUS_EXPORT_DIR/executions/<id>/ and its path returned.
Hosted gateway: returned inline as ``contentBase64`` (up to 1 MB of base64);
larger files point to Nimbus Studio. Nothing is written on the gateway.

Input parameters:

- `artifact_name` (string, required): File name from execution.artifacts (e.g. "nimbus_lda.pkl").
- `execution_id` (string, required): Run that produced the artifact.

### `pipeline.export` (~113 tokens)

Pipeline.Export

Export the pipeline as a standalone runnable Python bundle (zip).

Local/stdio: the zip is saved under NIMBUS_EXPORT_DIR/exports and its path
returned. Hosted gateway: the zip is returned inline as ``contentBase64``
(up to 1 MB of base64). UI-only nodes (interactive_application) are left out and
listed in ``exportNotes``.

Input parameters:

- `name`: Optional name recorded inside the bundle.
- `train_graph` (object, required): Pipeline graph {nodes, connections} to export.

### `bids.export_dataset` (~241 tokens)

Bids.Export Dataset

Export a Nimbus dataset as a BIDS-layout zip (local: saved to
NIMBUS_EXPORT_DIR/bids/; hosted gateway: inline ``contentBase64``, up to 1 MB).

Continuous sources (EDF/BDF uploads, stream recordings) export as BIDS-raw
(EDF byte-passthrough; recordings converted HDF5→EDF with events.tsv).
Epoched sources (MOABB packs, trial-table uploads) export as BIDS-derivatives
(data passthrough + descriptive trial_index/label/session/run events — no
fabricated onsets). The zip embeds nimbus_export_manifest.json with sha256s.

Input parameters:

- `dataset` (string, required): Dataset id — a MOABB pack id (e.g. "BNCI2014_001"), an upload path (as returned by data.upload), or a recording name. Auto-detected.
- `kind`: Optional explicit source kind: pack|upload|recording (default auto).
- `subjects`: Pack exports only — subject filter (e.g. ["S01", "S03"]) to bound zip size; default exports all installed subjects.

### `bids.export_execution` (~108 tokens)

Bids.Export Execution

Export an execution's results as a BIDS-derivative zip (metrics.json,
per-subject participants.tsv, protocol.json, full pipeline_snapshot.json,
predictions.tsv when stored, provenance/execution.json). Local: saved to
NIMBUS_EXPORT_DIR/bids/. Hosted gateway: inline ``contentBase64`` (up to
1 MB of base64); larger zips point to Nimbus Studio.

Input parameters:

- `execution_id` (string, required): Run whose results to export (from execution.run).

### `device.list` (~32 tokens)

Device.List

EEG devices supported by this backend (OpenBCI, Muse, BrainBit, LSL, PiEEG...).

### `device.test` (~148 tokens)

Device.Test

Test a device connection WITHOUT starting a stream (safe, no confirm needed).

Input parameters:

- `connection_type`: Device-specific selector when several exist (e.g. serial vs wifi).
- `device_type` (string, required): Device id from device.list (e.g. "brainbit", "muse").
- `ip_address`: Device IP for network/wifi devices.
- `ip_port`: Port for network devices.
- `mac_address`: Bluetooth MAC (BT devices).
- `port`: Serial/COM port for wired devices.
- `serial_number`: Device serial (some BLE stacks).
- `source_id`: LSL source id.
- `stream_name`: LSL stream name (LSL devices).

### `stream.start` (~331 tokens)

Stream.Start

Connect an EEG device and START a live streaming session on the user's head.
Requires confirm=True; call device.test first. Track with stream.status().
Idle watchdog: if no stream.status()/stream.telemetry() poll happens for
idle_timeout_sec (default 900), the session is stopped and the device
disconnected automatically — an abandoned stream never keeps running on
the user's head. Any poll resets the timer; idle_timeout_sec=0 disables
the watchdog.

Input parameters:

- `chunk_size` (integer): Samples per streamed chunk (default 125).
- `confirm` (boolean): MUST be true to start — the explicit user go-ahead for a live session on their head; anything else is refused with zero requests.
- `connection_type`: Device-specific selector (e.g. serial vs wifi).
- `device_type` (string, required): Device id from device.list (e.g. "brainbit").
- `idle_timeout_sec` (integer): Watchdog: stop+disconnect after this many seconds without a status poll (default 900; 0 disables).
- `ip_address`: Device IP for network/wifi devices.
- `ip_port`: Port for network devices.
- `mac_address`: Bluetooth MAC (BT devices).
- `n_channels` (integer): Channel count to open (default 8).
- `port`: Serial/COM port for wired devices.
- `serial_number`: Device serial (some BLE stacks).
- `session_id`: Optional existing session to resume/reuse.
- `source_id`: LSL source id.
- `stream_name`: LSL stream name (LSL devices).

### `stream.status` (~59 tokens)

Stream.Status

Live snapshot of a streaming session (running, deviceConnected).
Polling this also feeds the idle watchdog: each call resets the
session's idle timer (see stream.start's idle_timeout_sec).

Input parameters:

- `session_id` (string, required): The streaming session to inspect.

### `stream.stop` (~53 tokens)

Stream.Stop

Stop a streaming session and disconnect the device (always safe to call).
Also removes the session from the idle watchdog so it cannot fire after
an explicit stop.

Input parameters:

- `session_id` (string, required): The streaming session to stop.

### `plots.confusion` (~163 tokens)

Plots.Confusion

Confusion-matrix figure for one execution: heatmap with trial
counts + per-class accuracy bars, rendered as a PNG the assistant can
see. Use it right after execution.results to eyeball WHICH classes are
confused, not just the accuracy number.

Input parameters:

- `execution_id` (string, required): Run to plot (from execution.run / execution.results).
- `matrix` (string): "eval" (held-out, default) or "train" (fit-set / cross-validation). CV and evaluation-plan runs often have no eval matrix — use "train".
- `save` (boolean): Local/stdio: also write the PNG to NIMBUS_EXPORT_DIR/plots/ and return its path. Ignored on the hosted gateway (the image is returned inline).

Output parameters:

- `meta` (object): Figure sidecar. foundation.compare includes results/best/skipped (also as a JSON text content block).
- `mimeType` (string)
- `ok` (boolean)
- `savedPath` (string): Set when save=true.
- `sizeBytes` (integer): Set when save=true.
- `title` (string|null)

### `plots.dataset` (~321 tokens)

Plots.Dataset

Figure over an EEG data source — the visual companion to
data.inspect_dataset. kind=psd (Welch, channel mean bold),
kind=bandpower (channel × band dB heatmap), kind=topomap (band-power
scalp map), kind=spectrogram (time-frequency for one channel). Sources
are the same ones inspect uses: MOABB packs, uploads, local files.

Input parameters:

- `band` (string): Band for kind=topomap: delta|theta|alpha|beta|gamma (default alpha).
- `channel_idx` (integer): 0-based channel for kind=spectrogram (default 0).
- `dataset`: MOABB dataset id, e.g. "BNCI2014_001" (source=dataset).
- `kind` (string, required): Figure type: psd | topomap | bandpower | spectrogram.
- `mode` (string): Pack split to load: training | evaluation | all (source=dataset).
- `path`: Upload path (source=upload) or absolute local path (source=local).
- `save` (boolean): Local/stdio: also write the PNG to NIMBUS_EXPORT_DIR/plots/ and return its path. Ignored on the hosted gateway (the image is returned inline).
- `source` (string, required): "dataset" (MOABB pack), "upload" (path from data.upload), or "local" (absolute path, local backend only).
- `subject`: Subject id, e.g. "S01" (source=dataset).

Output parameters:

- `meta` (object): Figure sidecar. foundation.compare includes results/best/skipped (also as a JSON text content block).
- `mimeType` (string)
- `ok` (boolean)
- `savedPath` (string): Set when save=true.
- `sizeBytes` (integer): Set when save=true.
- `title` (string|null)

### `plots.erp` (~243 tokens)

Plots.Erp

ERP butterfly figure from epoched data: one subplot per class, all
channel means (thin) + across-channel mean (bold), time-locked to the
trial event. Shows whether class-conditioned evoked structure exists
BEFORE training anything.

Input parameters:

- `dataset`: MOABB dataset id, e.g. "BNCI2014_001" (source=dataset).
- `max_channels` (integer): Channels drawn per class subplot (default 16, max 64).
- `mode` (string): Pack split to load: training | evaluation | all (source=dataset).
- `path`: Upload path (source=upload) or absolute local path (source=local).
- `save` (boolean): Local/stdio: also write the PNG to NIMBUS_EXPORT_DIR/plots/ and return its path. Ignored on the hosted gateway (the image is returned inline).
- `source` (string, required): "dataset" (MOABB pack), "upload" (path from data.upload), or "local" (absolute path, local backend only).
- `subject`: Subject id, e.g. "S01" (source=dataset).

Output parameters:

- `meta` (object): Figure sidecar. foundation.compare includes results/best/skipped (also as a JSON text content block).
- `mimeType` (string)
- `ok` (boolean)
- `savedPath` (string): Set when save=true.
- `sizeBytes` (integer): Set when save=true.
- `title` (string|null)

### `plots.leaderboard` (~230 tokens)

Plots.Leaderboard

Leaderboard bar chart for one dataset: accuracy with 95% CI
whiskers, best first; BYO python_model submissions highlighted in a
distinct color. The visual companion to catalog.leaderboard. On the
pretrained_few_trial track the figure also includes the classical
LOSO baseline as a muted side panel for comparison.

Input parameters:

- `dataset_id` (string, required): Dataset to chart, e.g. "BNCI2014_001" (see catalog.leaderboard).
- `save` (boolean): Local/stdio: also write the PNG to NIMBUS_EXPORT_DIR/plots/ and return its path. Ignored on the hosted gateway (the image is returned inline).
- `track` (string): "within_session" (curated 5-fold), "crossSubject" (LOSO, curated + BYO submissions), or "pretrained_few_trial" (the efficiency track: LOSO at the protocol's fixed per-subject trial budget — the figur…

Output parameters:

- `meta` (object): Figure sidecar. foundation.compare includes results/best/skipped (also as a JSON text content block).
- `mimeType` (string)
- `ok` (boolean)
- `savedPath` (string): Set when save=true.
- `sizeBytes` (integer): Set when save=true.
- `title` (string|null)

### `stream.telemetry` (~197 tokens)

Stream.Telemetry

Live snapshot of a streaming session: latest prediction + recent window,
signal quality (meanChannelQuality, snrDb, artifactProbability), indicators,
running stats. Poll this while a session runs. Works with OR without a
deployed model: modelless hardware streams (no classifier) report live
signal quality + focus/relaxation band-power indicators with
latestPrediction null; deployed sessions additionally carry predictions
and confidence stats. Expect low
confidence during filter/ASR warm-up (first seconds); quality < 0.5 or high
artifactProbability means the signal is poor. 404 => session not active in
this backend. Each poll also feeds the idle watchdog (see stream.start's
idle_timeout_sec), keeping an actively watched session alive.

Input parameters:

- `session_id` (string, required): The streaming session to read telemetry for.
- `window` (integer): How many recent predictions/chunks to include (default 50).

### `data.upload` (~234 tokens)

Data.Upload

Upload an EEG file (.edf/.bdf/.mat/.csv/.txt/.tsv/.h5/.hdf5, <=500MB) to the
backend and get the registered path for a custom_data node.

sampling_rate (Hz, e.g. 250.0) is REQUIRED for plain CSV/TSV/TXT files
without embedded metadata — uploads without it are rejected (no silent
250 Hz assumption). format overrides extension-based detection
(auto, mat, csv, tsv, txt, edf, bdf, h5, hdf5).

Input parameters:

- `dataset_name`: Optional label for the uploaded dataset.
- `file_path` (string, required): Local file to upload (.edf/.bdf/.mat/.csv/.txt/.tsv/.h5/.hdf5, <=500MB).
- `format`: Override extension-based detection (auto|mat|csv|tsv|txt|edf|bdf|h5|hdf5).
- `sampling_rate`: Hz for headerless CSV/TSV/TXT (REQUIRED there, e.g. 250.0).

### `data.delete_upload` (~92 tokens)

Data.Delete Upload

Delete an uploaded file from your account (disk + registry).

Requires confirm=True. Pass the path returned by data.upload
(file.path). Only your own uploads can be deleted; missing paths
return not-found.

Input parameters:

- `confirm` (boolean): MUST be true to delete — the explicit user go-ahead.
- `path` (string, required): Upload path from data.upload (e.g. user_…/file.csv).

### `data.inspect_dataset` (~239 tokens)

Data.Inspect Dataset

Exploratory summary of a public EEG dataset (MOABB pack): channels,
sampling rate, trial/class balance, per-channel µV stats, band powers
and a PSD overview.

Look at the data BEFORE building pipelines: class balance drives
stratification choices (imbalanced classes skew accuracy), and
flatlined channels mean a montage/reference problem worth fixing
first. subject defaults to the dataset's catalog ``defaultSubject``
(see catalog.datasets), e.g. "S01"; a comma-list like
"S01,S03" loads a cohort. mode: training | evaluation | all.
Units note: values are ASSUMED volts by the loader — a µV-native file
reads 1e6x too large; set unitsScale in a pipeline's custom_data
config when needed.

Input parameters:

- `dataset` (string, required): Dataset id from catalog.datasets (e.g. "BNCI2014_001").
- `mode` (string): Which split to summarize — training | evaluation | all.
- `subject`: Subject code ("S01") or comma-list cohort ("S01,S03"); defaults to the catalog defaultSubject.

### `data.inspect_file` (~336 tokens)

Data.Inspect File

Exploratory summary of an EEG file (.edf/.bdf/.mat/.csv/.tsv/.txt/.h5):
channels, sampling rate, trial/class balance, per-channel µV stats,
band powers and a PSD overview.

The path shape picks the source: ABSOLUTE path → read the file from
disk (only on a LOCAL backend: desktop app / MCP local mode — no
upload needed); RELATIVE path (the one data.upload returns) → describe
the uploaded file, which works on ANY backend (hosted or local).

Look at the data BEFORE building pipelines: class balance drives
stratification choices, and flatlined channels mean a
montage/reference problem worth fixing first. Units note: values are
ASSUMED volts by the loader — a µV-native CSV reads 1e6x too large;
set unitsScale in a pipeline's custom_data config when needed. On a
hosted backend absolute paths are refused and this returns guidance
(upload the file first or switch to a local backend).

CSV/TSV/TXT files have no embedded sampling rate: pass sampling_rate
(Hz) for them — the backend never assumes 250 Hz. Formats that embed
their rate (EDF/BDF/MAT/H5) ignore it.

Input parameters:

- `path` (string, required): ABSOLUTE filesystem path (`~` is expanded; local backends only) or the RELATIVE upload path returned by data.upload (works on any backend).
- `sampling_rate`: Hz for CSV/TSV/TXT files (REQUIRED there, e.g. 250.0).

### `project.create` (~54 tokens)

Project.Create

Create a project (container for one pipeline document). Returns projectId.

Input parameters:

- `description`: Optional longer description shown in the studio.
- `name` (string, required): Project display name (e.g. "MI CSP-LDA sweep").

### `project.list` (~20 tokens)

Project.List

List projects owned by the current principal (agent work included).

### `project.save` (~174 tokens)

Project.Save

Save a pipeline graph into a project (visible on the studio canvas).

Always pass expected_revision from the last project.load (or a prior
save/create response). When it does not match the current server
revision, the save fails with a conflict — it does NOT silently
overwrite concurrent edits. Omitting it is rejected so a stale
single-writer convenience path cannot wipe another client's work.

Input parameters:

- `expected_revision` (integer, required): Optimistic-concurrency base from project.load / a prior save. Required.
- `name`: Optional pipeline name stored on the document.
- `project_id` (string, required): Target project (from project.create/project.list).
- `subject`: Optional dataset subject code (e.g. "S01") for this pipeline.
- `train_graph` (object, required): Pipeline graph {nodes, connections} to persist.

### `project.load` (~38 tokens)

Project.Load

Load a project's saved pipeline (train graph + meta) for editing/re-running.

Input parameters:

- `project_id` (string, required): Project whose pipeline document to load.

### `project.rename` (~46 tokens)

Project.Rename

Rename a project you own (max 200 characters).

Input parameters:

- `name` (string, required): New display name.
- `project_id` (string, required): Project to rename (from project.create/project.list).

### `project.delete` (~91 tokens)

Project.Delete

Delete a project (and its saved pipeline document) from the studio workspace.

Requires confirm=True: this removes the project from the user's project
list. Executions already run from it are not affected. Find ids with
project.list.

Input parameters:

- `confirm` (boolean): MUST be true to delete — the explicit user go-ahead.
- `project_id` (string, required): Project to delete (from project.create/project.list).

### `protocol.validate` (~116 tokens)

Protocol.Validate

Validate a neurofeedback protocol document WITHOUT saving it.
Returns {valid: true, spec} on success, or {valid: false, errors: [...]}
with the per-violation list (loc/msg) when the document is invalid —
the verdict is data, never an exception.

Input parameters:

- `spec` (object, required): Wire-format protocol document (camelCase): id, name, indicator {name, transform}, armDefaults, blocks [{id, kind: assessment|training, durationSec, feedback?}], threshold, sessions, breakCriteria.

### `protocol.save` (~84 tokens)

Protocol.Save

Validate and persist the NEXT VERSION of a protocol document
(confirm-gated: each call appends a version row; accidental calls
clutter the version history). Edits create a new version — the
assignment pins the version it runs.

Input parameters:

- `confirm` (boolean): MUST be true to write.
- `spec` (object, required): Wire-format protocol document (validate first to iterate).

### `protocol.list` (~17 tokens)

Protocol.List

Every saved protocol id at its latest version.

### `protocol.start_assignment` (~381 tokens)

Protocol.Start Assignment

Enroll a subject and prepare their neurofeedback deploy session
(confirm-gated — a session runs on a human). Creates the assignment
(yoked sham arms REQUIRE yoked_donor_upload_id: a recorded simulated
session's upload — mint one by running an active-arm session and
data.upload-ing its recording), then persists the protocol graph as a
deployable execution. Requires a Pro plan for LIVE hardware sessions;
simulated-source sessions (the default template) are free.

Graph source: execution_id reuses THAT run's stored graph (it must
contain a protocol_session node; no device is pre-connected — the
deploy session's data source stays yours). Without execution_id the
built-in template graph is cloned — simulated source → bandpass →
bandpower_indicator → protocol_session stamped with the protocol refs —
and a simulated device is connected to the returned session id.

The session starts when a client opens {statusUrl} (the deploy WS),
sends init_deployment with modelExecutionId={executionId}, then start;
the Studio app shows the feedback panel automatically. Poll
protocol.session_status; stop with protocol.stop (the recording's
block annotations + assignment progression are written on stop).

Input parameters:

- `arm` (string, required): active | sham.
- `confirm` (boolean): MUST be true — explicit go-ahead for a session on a human.
- `execution_id`: Optional completed execution whose stored graph carries a protocol_session node; default clones the template.
- `protocol_id` (string, required): Saved protocol id (protocol.save).
- `protocol_version` (integer, required): Immutable version the assignment pins.
- `subject_label` (string, required): Subject identifier (e.g. "S01").
- `yoked_donor_upload_id`: Donor recording upload (required when the arm's blocks resolve to yoked — the backend validates coverage).

### `protocol.session_status` (~84 tokens)

Protocol.Session Status

Live snapshot of a protocol deploy session: the scheduler block /
session progress + threshold state (under ``protocol``) beside the
telemetry window (recent indicators, quality, stats).

Input parameters:

- `session_id` (string, required): The deploy session to inspect (from start_assignment).
- `window` (integer): How many recent predictions to include (1-200, default 50).

### `protocol.pause` (~59 tokens)

Protocol.Pause

Pause a running protocol session (blocks freeze; the session and
device stay alive — resume with protocol.resume). Simulated and
playback sessions only while paused: no data is recorded.

Input parameters:

- `session_id` (string, required): The deploy session to pause.

### `protocol.resume` (~98 tokens)

Protocol.Resume

Resume a paused protocol session (restarts the server-driven device
loop). Live-hardware resume requires a Pro plan; simulated sessions are
free. Pass the session's channel count (start_assignment reports it).

Input parameters:

- `chunk_size` (integer): Samples per streamed chunk (default 125).
- `n_channels` (integer): Session channel count (the built-in template opens 4).
- `session_id` (string, required): The deploy session to resume.

### `protocol.stop` (~74 tokens)

Protocol.Stop

Stop a protocol session (always safe to call): stops streaming,
saves the block-annotated recording, advances the assignment when the
scheduler reached its natural end, and disconnects the device
(best-effort — a failed disconnect is reported, never raised).

Input parameters:

- `session_id` (string, required): The deploy session to stop.

### `protocol.export_stats` (~133 tokens)

Protocol.Export Stats

Paired pre/post analysis for one assignment's recorded sessions:
per-block aggregates (descriptive + CSV), paired t / Wilcoxon / dz
(per-statistic minimums: paired t and Cohen's dz need ≥ 3 completed
sessions, Wilcoxon needs ≥ 5), specificity notes, the
adaptive-threshold trajectory, and a pre/post figure (base64 PNG —
render or save it to look at the effect). An assignment with no
recordings yet returns the empty payload with a note, never an error.

Input parameters:

- `assignment_id` (string, required): The assignment to analyze (from start_assignment).

### `python.validate` (~141 tokens)

Python.Validate

Statically check a python_model script (BYO model class) without running it.

Compiles the source and verifies the contract — a class with
fit(X, y, info) and predict(X); optional predict_proba(X) unlocks
confidence panels. Never executes the code, so it is safe against the
hosted backend. Use it before execution.run to catch syntax errors,
missing methods, or the wrong class name cheaply.

Input parameters:

- `class_name`: Contract class to instantiate; omit to auto-detect the first class defining fit+predict.
- `code` (string, required): Python source of the model class (<=256KB UTF-8).

### `stats.grouped` (~352 tokens)

Stats.Grouped

Cluster-aware group statistics across executions.

REQUIREMENT (read first): each execution must have been run with an
evaluation plan that STORED per-SUBJECT accuracies for at least 3
subjects — e.g. leave-one-subject-out (LOSO) or grouped CV over
\>= 3 subjects. Session groups within one subject (multi-session
single-subject runs) are refused with
\``nimbus.stats.non_subject_groups`` — those are not independent
subjects. A plain single-subject CV run with no subject/session
grouping stores no group stats and fails with
\``nimbus.stats.insufficient_groups``.

What it does: pools the per-subject accuracies each run's evaluation
plan stored, then reports the between-group summary — mean accuracy
with a t-interval and a one-sample t-test vs chance. Responses label
\``groupKind``, real ``groupKeys`` (subject ids like S01 when stamped
or recoverable), ``perGroup`` (key + accuracy pairs),
\``overall.pooledExecutionIds``, and ``kindBreakdown`` /
\``overall.kindBreakdown`` (per_subject vs loso etc. with n + mean —
\#470; between-group pooled mean is unchanged).

Input parameters:

- `chance`: Chance level in [0, 1]; default 1/nClasses per execution.
- `execution_ids` (array, required): 1–25 executions (from execution.run) to pool. Runs without subject group stats are reported with nulls but do not fail the request — unless NONE qualifies (``nimbus.stats.insufficient_groups``) or on…

### `foundation.compare` (~254 tokens)

Foundation.Compare

Run every available pretrained backbone on YOUR data and see which wins —
a ranked bar chart with CI whiskers, params, and wall time per backbone.
Backbones that can't process your data (window size, channel count) are
noted, not silently skipped. Each backbone embeds your
trials frozen (the exact foundation_features preprocessing), a LDA is
cross-validated on the embeddings. Returns a PNG image PLUS a JSON text
block (and structuredContent.meta.results) with per-backbone accuracy,
CI, embed dim, params, and time — pick the foundation_features backbone
by measurement, not vibes.

Input parameters:

- `dataset`: MOABB dataset id, e.g. "BNCI2014_001" (source=dataset).
- `mode` (string): Pack split to load: training | evaluation | all (source=dataset).
- `path`: Upload path (source=upload) or absolute local path (source=local).
- `source` (string, required): "dataset" (MOABB pack), "upload" (path from data.upload), or "local" (absolute path, local backend only).
- `subject`: Subject id, e.g. "S01" (source=dataset).

Output parameters:

- `meta` (object): Figure sidecar. foundation.compare includes results/best/skipped (also as a JSON text content block).
- `mimeType` (string)
- `ok` (boolean)
- `savedPath` (string): Set when save=true.
- `sizeBytes` (integer): Set when save=true.
- `title` (string|null)

## Diagnostics

Captured diagnostic sections: Provenance, Install scripts, Dependencies. The full working is on the page: https://verifymcp.io/servers/nimbusbci-nimbus-mcp/nimbus-mcp#diagnostics

## Score history

- 2026-10-24: 75
- 2026-10-23: 60
- 2026-10-22: 72
- 2026-10-21: 57
- 2026-10-20: 56
- 2026-10-19: 56
- 2026-10-18: 71
- 2026-10-17: 71
- 2026-10-16: 70
- 2026-10-15: 70
- 2026-10-14: 36
- 2026-10-13: 36
- 2026-10-12: 36
- 2026-10-04: 36

## Common questions

### What is the Nimbus BCI MCP server?

Nimbus BCI is an MCP server listed in the public MCP registry as io.github.nimbusbci/nimbus-mcp. AI agents build, train, and analyze BCI/EEG pipelines: data, models, experiments, live sessions. This page covers its PyPI package (nimbus-mcp).

### Is the Nimbus BCI MCP server safe to use?

Nimbus BCI scores 75 out of 100 on VerifyMCP. We found no known CVEs affecting it as of 24 October 2026. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.

### What tools does the Nimbus BCI MCP server expose?

Nimbus BCI exposes 63 tools: account.whoami, catalog.nodes, catalog.node_schema, catalog.templates, catalog.template, and 58 more. Their descriptions and schemas cost roughly 8,987 tokens of context every time the server is loaded.

### Is the Nimbus BCI MCP server still maintained?

Nimbus BCI is still listed as active in the MCP registry. We last reached this channel on 24 October 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.

## Links

- PyPI project: https://pypi.org/project/nimbus-mcp/
- Socket report: https://socket.dev/pypi/package/nimbus-mcp
- Repository: https://github.com/nimbusbci/nimbus-mcp
- Changelog RSS feed: https://verifymcp.io/servers/nimbusbci-nimbus-mcp/nimbus-mcp.xml
- Changelog JSON feed: https://verifymcp.io/servers/nimbusbci-nimbus-mcp/nimbus-mcp.json
- HTML version of this page: https://verifymcp.io/servers/nimbusbci-nimbus-mcp/nimbus-mcp
