# Ephys (pypi · ephys-mcp)

Read-only analysis of intracortical BCI recordings (NWB, DANDI, WAV): spikes, PSTHs, decoding, plots

- Trust score: 80/100 (high trust)
- Change this week: +2
- Registry status: active
- Liveness: live
- Owner verified: no
- Last scored: 2026-10-24

## Components

- pypi · `ephys-mcp`: 80/100 (this document), [markdown](https://verifymcp.io/servers/happyc0der-ephys-mcp/ephys-mcp.md), [page](https://verifymcp.io/servers/happyc0der-ephys-mcp/ephys-mcp)

## Channel facts

- Registry: `pypi`
- Package: `ephys-mcp`
- Version: `0.5.1`
- 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 uv_build at install time, a recognised build step with no custom scripting around it.
  - 2 of 54 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.
  - License check failed: the license (CC0-1.0) isn't a recognized OSI-approved license.
  - Actively maintained (last published 31 days ago).
  - Disclosure check failed: no security disclosure policy was found in the source repository.
- **Schema Quality & AI Usability**: 80/100
  - 100% of prompts and resources have a non-trivial description (not blank, and not just the item's name).
  - AI-judged instruction clarity (good).
  - Tool/resource definitions use about 2000 tokens (~80/item across 25 items; 24 tools + 1 resources), lean.
  - Usage-examples check failed: none of the tools include examples.
- **Stability & Change Management**: 100/100
  - No destabilizing schema changes in the last 30 days.
- **Tool Coverage**: 67/100
  - 100% of tools have a non-trivial description (not blank, and not just the tool's name).
  - 0% of tool parameters carry a description.
- **Tool Safety**: 100/100
  - No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.
  - We read all 24 captured tool definition(s), and no name or description among them implies an irreversible operation.
  - An AI judge read all 26 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 Ephys MCP server?

Ephys runs locally as a PyPI package, launched with uvx ephys-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 happyc0der-ephys-mcp -- uvx ephys-mcp
```

### Cursor

```json
{
  "mcpServers": {
    "happyc0der-ephys-mcp": {
      "command": "uvx",
      "args": [
        "ephys-mcp"
      ]
    }
  }
}
```

### VS Code

```json
{
  "servers": {
    "happyc0der-ephys-mcp": {
      "command": "uvx",
      "args": [
        "ephys-mcp"
      ]
    }
  }
}
```

### Codex

```bash
codex mcp add happyc0der-ephys-mcp -- uvx ephys-mcp
```

### opencode

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

### OpenClaw

```bash
openclaw mcp add happyc0der-ephys-mcp --command uvx --arg ephys-mcp
```

### Hermes

```yaml
mcp_servers:
  happyc0der-ephys-mcp:
    command: "uvx"
    args: ["ephys-mcp"]
```

### Netclaw

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

### Vellum

```bash
assistant mcp add happyc0der-ephys-mcp -t stdio -c uvx -a ephys-mcp
```

### Other

```json
{
  "mcpServers": {
    "happyc0der-ephys-mcp": {
      "command": "uvx",
      "args": [
        "ephys-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-22 (score 80, 0)

- [security] Stability: 0.97 → pass

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

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

### 2026-10-19 (score 79, +1)

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

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

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

### 2026-10-15 (score 77, +1)

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

### 2026-10-13 (score 76, +12)

- [security improvement] Known CVEs: unverified → pass
- [functional improvement] Dependency health: unverified → 0.98

### 2026-10-12 (score 64, −10)

- [security regression] Known CVEs: pass → unverified
- [functional regression] Dependency health: 0.98 → unverified

### 2026-10-08 (score 74, +1)

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

## MCP tools (24)

### `list_sources` (~24 tokens)

List the data source types this server can open, with their parameters and status.

### `open_session` (~46 tokens)

Open a recording session and return its session_id and metadata.

\`params` overrides the source's default_params (see list_sources).

Input parameters:

- `params`
- `source` (string)

### `close_session` (~27 tokens)

Close a session and drop any decoder fitted on it.

Input parameters:

- `session_id` (string, required)

### `get_session_info` (~36 tokens)

Metadata for a session: duration, channels, sampling rates, behaviour signals, licence, citation.

Input parameters:

- `session_id` (string, required)

### `get_signal_quality` (~53 tokens)

Noise level, SNR and dead/noisy channels from a broadband snippet (max 10 s).

Input parameters:

- `duration_s` (number)
- `session_id` (string, required)
- `t0` (number)

### `detect_spikes` (~71 tokens)

Threshold-crossing spike detection on a broadband snippet (max 10 s).

Where the session has ground-truth spikes, precision and recall are reported.

Input parameters:

- `duration_s` (number)
- `session_id` (string, required)
- `t0` (number)
- `threshold_sigma` (number)

### `set_probe_geometry` (~162 tokens)

Tell the server where the recording's contacts are, for sessions whose file does not say.

Named layouts: 'utah' (square grid, e.g. 96 contacts at 400 um), 'grid' with n_columns,
'linear' (one column at pitch_um, e.g. a laminar probe), 'tetrode_linear' (groups of four).
'explicit' takes positions_um as [[x, y], ...] in channel order. Geometry is used by
sort_spikes and plot_probe. Contacts under 100 um apart are sorted jointly.

Input parameters:

- `layout` (string)
- `n_columns` (integer)
- `pitch_um` (number)
- `positions_um`
- `session_id` (string, required)

### `get_probe` (~63 tokens)

Contact geometry and brain-area labels per channel, where known. Use the `channel`
indices with the `units` argument of get_psth / fit_latent_factors to analyse one area.

Input parameters:

- `max_rows` (integer)
- `session_id` (string, required)

### `plot_probe` (~75 tokens)

Figure: the array map, each contact coloured by its firing rate over a window, with area
labels and sorted units marked when a sort has been run. Needs geometry from the file or
set_probe_geometry.

Input parameters:

- `duration_s` (number)
- `session_id` (string, required)
- `t0` (number)

### `sort_spikes` (~135 tokens)

Spike-sort a broadband window (needs the sort extra). Afterwards the session's spike times
are the sorted units, restricted to that window, so rates, PSTHs, rasters and decoders use them.

Uses the session's probe geometry (from the file or set_probe_geometry) so nearby contacts
are sorted jointly; without geometry each channel is treated as an independent electrode.
Sorting takes seconds to a few minutes depending on duration and channel count.

Input parameters:

- `channels`
- `duration_s` (number)
- `session_id` (string, required)
- `sorter` (string)
- `t0` (number)

### `get_firing_rates` (~47 tokens)

Population firing-rate summary over a time range (defaults to the whole session).

Input parameters:

- `session_id` (string, required)
- `t0` (number)
- `t1`

### `fit_decoder` (~136 tokens)

Fit a decoder from spike counts to a behaviour signal and score it on held-out data.

The first `train_fraction` of the session trains; the remainder tests. With no
\`target`, the first velocity-like behaviour signal is used. Unrecorded gaps are skipped.
\`mask` names a boolean behaviour signal (such as FALCON's eval_mask); only bins where it
is true are used for fitting and scoring.

Input parameters:

- `bin_s` (number)
- `kind` (string)
- `mask`
- `session_id` (string, required)
- `target`
- `train_fraction` (number)

### `evaluate_cross_session` (~148 tokens)

Fit a decoder on one session and score it, unchanged, on other sessions: the question
benchmarks such as FALCON ask (does a decoder survive to a later day?).

All sessions must be open and share the same units/channels. `mask` names a boolean
behaviour signal restricting which bins are scored (FALCON files carry `eval_mask`; pass
null when the sessions have none). Also reports same-session held-out R² for comparison.

Input parameters:

- `bin_s` (number)
- `kind` (string)
- `mask`
- `target`
- `test_session_ids` (array, required)
- `train_session_id` (string, required)

### `decode_window` (~60 tokens)

Decode a window with the fitted decoder; returns a downsampled decoded-vs-true preview.

Input parameters:

- `duration_s` (number)
- `max_points` (integer)
- `session_id` (string, required)
- `t0` (number, required)

### `get_psth` (~178 tokens)

Peri-stimulus time histogram: firing aligned to a trial event and averaged over trials.

\`event` is one of the session's event_columns (default: movement onset if present).
\`group_by` splits trials by one of its group_columns, e.g. reach direction.
\`units` restricts the analysis to some units; tuning that cancels in the population
average (such as direction preference) only shows up per unit. Unit numbers in the
result index into `units` when it is given.
Returns the population time course, its peak, and the units that change most.

Input parameters:

- `bin_s` (number)
- `event`
- `group_by`
- `session_id` (string, required)
- `t_after` (number)
- `t_before` (number)
- `units`

### `fit_latent_factors` (~170 tokens)

Fit low-dimensional latent factors to trial-aligned population activity.

GPFA (Yu et al. 2009) gives denoised single-trial trajectories and a timescale per factor;
PCA is the fast, noisier baseline. Look for an elbow in variance_explained_per_factor to
judge dimensionality. If the session has a velocity-like signal, reports how well the top
factors explain it. Then plot_latent_factors draws the trajectories.

Input parameters:

- `bin_s` (number)
- `event`
- `group_by`
- `method` (string)
- `n_factors` (integer)
- `session_id` (string, required)
- `t_after` (number)
- `t_before` (number)
- `units`

### `plot_latent_factors` (~73 tokens)

Figure: the top three latent factors over time (single trials thin, group means bold), the
trajectory in the plane of the first two factors, and variance explained per factor.
Requires fit_latent_factors first.

Input parameters:

- `max_trials_drawn` (integer)
- `session_id` (string, required)

### `plot_psth` (~99 tokens)

Figure: population PSTH per group (mean ± SEM over trials) above a unit-by-time heatmap of
change from baseline. Same arguments as get_psth. Returns the PNG path and the image.

Input parameters:

- `bin_s` (number)
- `event`
- `group_by`
- `session_id` (string, required)
- `t_after` (number)
- `t_before` (number)
- `units`

### `plot_raster` (~62 tokens)

Figure: spike raster for a time window (max 60 s), with unrecorded spans shaded.

Input parameters:

- `duration_s` (number)
- `max_units` (integer)
- `session_id` (string, required)
- `t0` (number)

### `plot_decoding` (~70 tokens)

Figure: decoded against actual behaviour for a window (max 60 s), one panel per dimension.
Requires fit_decoder first. Pick a window in the test split for an honest picture.

Input parameters:

- `duration_s` (number)
- `session_id` (string, required)
- `t0` (number, required)

### `list_lsl_streams` (~35 tokens)

Lab Streaming Layer streams visible on the local network (needs the lsl extra).

Input parameters:

- `wait_s` (number)

### `get_stream_status` (~37 tokens)

For a live session: how much signal is buffered, whether data is still arriving, and drops.

Input parameters:

- `session_id` (string, required)

### `search_datasets` (~54 tokens)

Search the DANDI Archive for datasets. An empty query returns a short curated list of
intracortical datasets that work well with this server.

Input parameters:

- `limit` (integer)
- `query` (string)

### `list_dataset_files` (~59 tokens)

Licence, citation and NWB files of a DANDI dataset. Pass a file's path to open_session(source='dandi').

Input parameters:

- `dandiset_id` (string, required)
- `limit` (integer)
- `version` (string)

## Diagnostics

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

## Score history

- 2026-10-24: 80
- 2026-10-23: 80
- 2026-10-22: 80
- 2026-10-21: 80
- 2026-10-20: 79
- 2026-10-19: 79
- 2026-10-18: 78
- 2026-10-17: 78
- 2026-10-16: 77
- 2026-10-15: 77
- 2026-10-14: 76
- 2026-10-13: 76
- 2026-10-12: 64
- 2026-10-08: 74
- 2026-10-06: 73
- 2026-10-04: 72
- 2026-10-03: 71
- 2026-10-02: 71
- 2026-10-01: 70
- 2026-09-30: 70
- 2026-09-29: 66
- 2026-09-28: 66
- 2026-09-27: 64
- 2026-09-26: 64
- 2026-09-25: 64
- 2026-09-24: 64
- 2026-09-23: 64
- 2026-09-22: 64

## Common questions

### What is the Ephys MCP server?

Ephys is an MCP server listed in the public MCP registry as io.github.happyc0der/ephys-mcp. Read-only analysis of intracortical BCI recordings (NWB, DANDI, WAV): spikes, PSTHs, decoding, plots. This page covers its PyPI package (ephys-mcp).

### Is the Ephys MCP server safe to use?

Ephys scores 80 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 Ephys MCP server expose?

Ephys exposes 24 tools: list_sources, open_session, close_session, get_session_info, get_signal_quality, and 19 more. Their descriptions and schemas cost roughly 1,920 tokens of context every time the server is loaded.

### Is the Ephys MCP server still maintained?

Ephys 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.

### What licence is the Ephys MCP server under?

Ephys declares the CC0-1.0 licence, which is not on the OSI-approved list. Read the terms before using it at work, and note this covers the source only, not the cost of any service it calls.

## Links

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