Skip to main content
Glama
sciflow-mcp
by Claybarn

sciflow-mcp

An MCP server that lets scientists and their agents build reproducible, interoperable analysis workflows over stamped data: data positioned along time, cortical depth, frequency, or any other parametric axis.

It builds on three earlier pieces of work:

  • Collate: every array is paired with a stamp per sample, and multiple acquisition systems are aligned to one clock. Its GeneralizedData successor generalizes time to any parametric axis.

  • EventLogic: logical operations (AND, OR, NOT, merge, containment) on [on, off] intervals.

  • The generalized analysis functions from Barnes et al. 2026: peri-event collation, normalization and correlation that work whatever the data's dimensionality.

Install

pip install sciflow-mcp

To run the MCP server without installing anything, use uvx sciflow-mcp, for example in a client config: {"command": "uvx", "args": ["sciflow-mcp"], "env": {"SCIFLOW_STORE": "/path/to/store"}}.

For development, clone the repo:

git clone https://github.com/Claybarn/sciflow-mcp && cd sciflow-mcp && uv sync

Opening the clone in Claude Code picks up the server from .mcp.json.

Related MCP server: workflow-mcp

Why it interoperates

There are exactly two artifact kinds. Both lie on a named parametric axis with a unit (time in s by default):

kind

contents

invariant

series

N-D data, 1-D stamps, axis labels, coordinates, stamp_unit

len(stamps) == data.shape[0], stamps finite and non-decreasing, the parametric axis is always axis 0

intervals

sorted ons, offs, axis, unit

on <= off; point events have on == off

  • Workflows are type-checked before they run. Every operation declares which kinds it takes and which it returns.

  • No parametricAxis argument. The parametric axis is always axis 0, so peri_event works the same on a 1-D wheel trace, a channels × time LFP and a 32 × 32 × time imaging stack.

  • Axes can't be mixed silently. Operations that combine artifacts require the same parametric axis and unit. Time intervals can't be applied to a depth profile, and spike sample numbers can't be mixed with seconds. The error names the fix: reparametrize, rescale_stamps or align_clock.

  • Any axis can become the parametric one. reparametrize turns a time × depth CSD into a depth × time series. Every operation then works along depth, for example restrict to a cortical layer defined with define_intervals.

  • Derived results stay stamped. Peri-event epochs are stamped by event position and get a lag axis. Their average is a series whose parametric axis is lag, which can be windowed with lag intervals. Per-interval correlations are stamped by interval onset. Any output can feed any later step.

Why it's reproducible

  • Content-addressed artifacts. An artifact's id is the SHA-256 of its arrays and header, so identical bytes always get identical ids.

    • Same platform and image: results are bit-identical, so cache hits and ids carry over. This holds for a local Docker run and the same image on Slurm or Kubernetes.

    • Different hardware or OS: floating-point operations can differ in the last bits. A Butterworth filter computed on macOS and in the Linux image differs by up to ~7e-15 relative, which changes its id. compare_artifacts checks that two results agree within a tolerance, and every run records Python, platform and package versions.

  • Deterministic step keys. Each step's cache key is hash(op, op version, validated params, input ids). Re-running reuses results, and changing one parameter re-runs only that step and the steps downstream of it.

  • Provenance records. Every run writes runs/<run_id>.json. It holds the resolved workflow, Python/package/operation versions, and each step's params, inputs, output, cache key and timing. Each output artifact also records the step that made it.

Operations

sciflow ops (or the list_operations tool) prints the full list. The current 34:

  • intervals: threshold_intervals (fixed or Collate's midpoint threshold), define_intervals, event_windows, interval_union / intersection / difference / xor / complement, merge_intervals, filter_intervals, select_contained

  • series: restrict, select, reduce_axis, reparametrize, events_where (e.g. spikes of chosen clusters), arithmetic, standardize

  • filtering: butter_filter, median_filter, bin_average (downsample along any axis, with a circular mean for phase)

  • peri-event: peri_event, peri_event_normalize (dff / zscore / subtract), epoch_average, event_histogram (PSTH)

  • correlation: interval_correlation, pairwise_interval_correlation

  • dataset: stack_sessions, group_reduce (hierarchical: sessions → subjects → conditions)

  • stats: permutation_test (stratified, from Claybarn/permute), mixed_model (statsmodels MixedLM)

  • alignment: align_clock (Collate's pulse cross-correlation plus linear fit; converts ticks or frame numbers to the reference unit), rescale_stamps, interpolate_to

The filtering, threshold, rescaling and events_where operations cover the preprocessing that each AcquisitionSystem subclass in Collate's examples re-implemented by hand. Examples are LFP/EMG high-pass filtering, median-filtered sync lines, OpenEphys sample numbers, Kilosort cluster labels and the miniscope's low-pass dF/F baseline. Each becomes a workflow step with recorded parameters.

Workflows

name: running-onset-widefield
inputs: {wheel: sha256:…, widefield: sha256:…, sync_ref: sha256:…, sync_img: sha256:…}
steps:
  - {id: aligned, op: align_clock, inputs: {target: inputs.widefield, local_clock: inputs.sync_img, reference_clock: inputs.sync_ref}}
  - {id: running, op: threshold_intervals, inputs: {series: inputs.wheel}, params: {threshold: 2.0}}
  - {id: epochs, op: peri_event_normalize, inputs: {series: steps.aligned, events: steps.running},
     params: {pre: 2, post: 4, baseline_start: -2, baseline_stop: 0, method: dff}}
  - {id: response, op: epoch_average, inputs: {epochs: steps.epochs}}

See examples/running_response.yaml. To run it end to end on synthetic two-clock data:

uv run python examples/make_demo_data.py
uv run python examples/run_demo.py

Datasets: experiment → subject → session

A dataset is a manifest of sessions. Each session has a subject, attributes (condition, day, genotype, …) and named inputs, given as artifact ids or as files to import. register_dataset imports the files and stores the resolved manifest under a content-derived dataset: id, so the id pins the exact data.

A pipeline runs the same session_steps on every session. collect stacks a chosen per-session result along a new session axis, carrying each session's subject and attributes. aggregate_steps then combine the stacks:

name: running-onset-lfp-by-condition
dataset: dataset:b30a456710f97312
where: {day: [1, 2]}                       # optional session filter
session_steps:
  - {id: running, op: threshold_intervals, inputs: {series: inputs.wheel}, params: {threshold: 2}}
  - {id: epochs, op: peri_event_normalize, inputs: {series: inputs.lfp, events: steps.running},
     params: {pre: 1, post: 2, baseline_start: -1, baseline_stop: 0, method: zscore}}
  - {id: response, op: epoch_average, inputs: {epochs: steps.epochs}}
collect: {responses: response}
aggregate_steps:
  - {id: condition_mean, op: group_reduce, inputs: {series: inputs.responses},
     params: {group_by: [condition], within: [subject], statistic: mean}}
  • Averaging is hierarchical. group_reduce with within: [subject] averages each subject's sessions before combining subjects. A subject with more sessions doesn't dominate, and statistic: sem gives the standard error across subjects, not sessions.

  • Pipelines compile to ordinary workflows. Session steps become <session>__<step>, so validation, caching, provenance and the Slurm/Argo exporters all apply unchanged. On a cluster, sessions run in parallel and the aggregate steps wait for all of them.

  • Adding a session only runs that session. Everything else is a cache hit, and because results are keyed by content, sessions with identical inputs even share results. A pipeline's run record includes the pipeline and dataset id.

  • Results export as a long-format table. export_artifact(format="table") writes one row per element, with columns for every axis plus subject, condition and other attributes. That's ready for mixed-effects models in R or statsmodels.

Statistics that use every session

Both tests run on a stacked series and test every element (each lag × channel, each pixel) separately, without first averaging sessions within subject:

  • permutation_test calls your multitest_stratified_permutationtest. It shuffles condition labels only within each strata attribute (default subject), so repeated sessions are never treated as independent.

    • correction: max (the default) is the permutation max-statistic (Westfall–Young) FWER correction. Each test's statistic is first standardized by its own null, so channels on different scales compete fairly. Holm, Bonferroni and FDR (BH) are also available.

    • Strata missing a condition are dropped and listed in the metadata, and elements with missing data get NaN. Without this, the raw function marks every test as significant at the minimum p-value.

    • The seed is a parameter, so the result is cacheable and reproducible. With strata: null it falls back to multitest_two_sample.

  • mixed_model fits formula (e.g. value ~ condition + C(day)) with a random intercept per groups (or re_formula slopes) at every element. Output adds term and statistic axes (estimate, se, z, p, Holm-corrected p). Failed fits and convergence warnings are counted in the metadata.

Results stay on the parametric axis (lag), so p-values can be restricted to a window, plotted or exported to a table.

Seeing a pipeline

visualize_workflow returns Mermaid text, or the node/edge graph as JSON for a visual editor such as the Genepi rete-js frontend. Pipelines are drawn with session steps collapsed into one "each of N sessions" block, and every edge is labeled with its artifact kind. examples/onset_pipeline.mmd is the rendered demo pipeline.

examples/dataset_demo.py builds a synthetic 4-subject × 2-condition × 2-day experiment and runs examples/onset_pipeline.yaml on it. The result recovers the planted 1.6× condition effect (5.05 vs 3.19). The permutation test (max-corrected) and the mixed model both find the condition effect in the first 100–150 ms after onset.

MCP tools

tool

purpose

list_operations, describe_operation

discover operations and the JSON schema of their params

register_dataset, get_dataset

register an experiment's sessions (importing files) and inspect it

import_data

.npy / .npz / .mat (incl. Collate structs) / .csv into the store; ticks_per_unit converts sample numbers

list_artifacts, inspect_artifact

parametric axis and unit, shape, axes, extent, sample rate, NaN fraction, provenance

validate_workflow

static check of a workflow or pipeline: references, cycles, kinds, params

compare_artifacts

check two results agree within a tolerance, e.g. a laptop run vs a cluster run

visualize_workflow

Mermaid flowchart or node/edge graph of a workflow or pipeline

run_workflow, get_run

execute a workflow or pipeline locally and read the provenance record

export_workflow

compile to Slurm (sbatch chain) or Argo Workflows (Kubernetes)

export_artifact

.npz, .mat (opens in MATLAB), .csv, or a long-format table for statistics

This repo's .mcp.json registers the server for Claude Code, with the store in ./.sciflow. For other clients, use absolute paths:

{"command": "uv", "args": ["run", "--directory", "/path/to/sciflow-mcp", "sciflow-mcp"], "env": {"SCIFLOW_STORE": "/path/to/store"}}

Running on a cluster

Both exporters run each step as sciflow run-step <workflow.json> <step> --store <store> against a shared store. Results pass between jobs through the content-addressed cache, so a cluster run and a local run produce identical artifact ids.

  • Slurm: export_workflow(target="slurm") writes one .sbatch per step and a submit.sh that chains them with --dependency=afterok. The store must be on a shared filesystem.

  • Kubernetes: export_workflow(target="argo") writes a ConfigMap (the workflow) plus an Argo Workflow DAG. The store is a ReadWriteMany PVC mounted at /store. Build the step image with the included Dockerfile. It installs exactly the versions in uv.lock.

Development

uv sync
uv run pytest

To develop against a local EventLogic checkout, uncomment [tool.uv.sources] in pyproject.toml.

Fixes relative to the MATLAB generalized functions

  • binEventDataToEvents / collate_event_data_to_events passed every event's window on every loop iteration. event_histogram uses each event's own window.

  • collatePairwiseCorrContinuousDataToEvents correlated data1 with itself. The port correlates data1 with data2.

  • The correlation helpers' exponent 0.args.post_event_seconds (a find-and-replace accident) is back to a square root.

  • The Python translations matched each event to the first sample at or after it (searchsorted). sciflow defaults to the nearest sample; peri_event and peri_event_normalize take match: "next" to reproduce the original. The windows' 0-based bounds check (ind - pre >= 0) also fixes the off-by-one carried over from MATLAB's 1-based > 0.

  • binEventDataToEvents summed over events. event_histogram keeps one row per event; epoch_average with statistic: sum gives the original total.

datetime64 timestamps and ISO-date CSV columns are converted to seconds since the Unix epoch, recorded as time_origin in the artifact's meta, so daily or market data works too (e.g. pre: 2592000 for 30 days).

Workarounds for EventLogic 0.2.0

  • Events.__xor__ is broken. XOR is composed as (a | b) & ~(a & b).

  • The sweeps assume each operand is internally non-overlapping, so inputs are normalized with self_or() first.

Available Tools

14 tools
compare_artifactsA

Check whether two artifacts agree numerically, e.g. the same step run on a laptop and on a cluster. Ids are hashes of exact bytes, so different hardware can give different ids for results that agree to ~1e-15; this compares kind, axes, stamps, coordinates and data within |a - b| <= atol + rtol * |b|.

Inputs: a (required), b (required), atol, rtol. Returns: object

describe_operationC

Full documentation and JSON schema of an operation's parameters.

Inputs: name (required). Returns: object

export_artifactC

Write an artifact to disk as .npz, .mat (Collate struct layout, readable in MATLAB), .csv, or 'table': a long-format CSV with one row per element and columns for every axis plus subject / condition / other attributes of stacked sessions (ready for mixed-effects models in R or statsmodels).

Inputs: path (required), artifact_id (required), format. Returns: object

export_workflowB

Compile a workflow or pipeline into cluster jobs that each run one step against a shared store (a pipeline's sessions run in parallel; its aggregate steps wait for all of them).

slurm options: store_path, partition, time, mem, cpus, account, setup (shell lines, e.g. module load), sciflow_cmd, extra_sbatch. argo options: image, namespace, store_pvc, cpu, memory.

Inputs: target (required), out_dir (required), workflow (required), options. Returns: object

get_datasetC

A registered dataset's sessions, subjects, attributes and input artifact ids. With no id, lists datasets.

Inputs: dataset_id. Returns: result

get_runA

Full provenance record of a run (resolved workflow, environment and package versions, every step's params, inputs, outputs and cache key). With no run_id, lists recent runs.

Inputs: run_id. Returns: result

import_dataB

Import a .npy/.npz/.mat/.csv file into the store and return its artifact id.

Series: the array under data_key with its parametric axis at index parametric_axis, paired with stamps_key (default 'stamps' or Collate's 'timestamps'), or stamps generated from sample_rate and start. ticks_per_unit divides raw stamps (e.g. 30000 for sample numbers at 30 kHz). axes labels every axis, the first naming the parametric axis; or pass just axis (e.g. "depth") and its unit. Intervals: ons_key/offs_key, or times_key for point events, on parametric axis. stream selects one struct in a Collate-style .mat file. CSV series use stamp_column (default: first).

Inputs: path (required), axes, axis, kind, +14 more. Returns: object

inspect_artifactA

Summarise an artifact (parametric axis and unit, shape, axes, extent, sample rate, NaN fraction) and how it was produced. Accepts a full id or a unique prefix.

Inputs: artifact_id (required). Returns: object

list_artifactsB

List every artifact in the store.

Inputs: no named parameters. Returns: result

list_operationsA

List available operations with their input kinds and output kind. Optionally filter by category (intervals, series, filtering, peri-event, correlation, alignment).

Inputs: category. Returns: result

register_datasetB

Register an experiment: {name, sessions: [{id, subject, attributes: {condition: ..., day: ...}, inputs: {: | {path, kind, options}}}]}. File inputs are imported (options are import_data's keyword arguments). Returns a content-derived dataset id that pins the exact data.

Inputs: dataset (required). Returns: object

run_workflowA

Run a workflow or pipeline locally. Steps whose (operation, version, params, inputs) already ran are reused from cache unless force, so adding sessions to a dataset only runs the new ones. Returns run_id, per-step outputs (summarised for pipelines) and the workflow outputs.

Inputs: workflow (required), force. Returns: object

validate_workflowA

Statically check a workflow or pipeline (JSON object, YAML/JSON text, or file path): references, cycles, input/output kinds and parameters. Returns the execution order and each step's output kind; pipelines are compiled first (session steps appear as '__').

Inputs: workflow (required). Returns: object

visualize_workflowA

Draw a workflow or pipeline: Mermaid flowchart text (render it for the user), or the node / edge graph as JSON for a visual editor. Pipelines show their session steps once, inside an 'each of N sessions' block, with the artifact kind on every edge.

Inputs: workflow (required), format. Returns: object

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.5/5.0

    Scored across 14 tools

    Disambiguation4/5

    Most tools map to a distinct resource+action: operations (list/describe), artifacts (import/list/inspect/compare/export), datasets (register/get), workflows (validate/run/visualize/export) and runs (get_run). The only soft overlaps are the four *_workflow tools and three *_artifact tools, but their descriptions (static check vs. execute vs. draw vs. compile to cluster jobs; inspect vs. compare vs. write to disk) clearly separate them. get_dataset doubling as 'list datasets' when no id is given is a minor collision with list_artifacts.

    Naming Consistency5/5

    Every tool follows a single snake_case verb_noun convention (list_operations, register_dataset, run_workflow, export_artifact). Verbs are reused consistently for the same intent (list_, get_, export_, *_workflow), making the surface predictable.

    Tool Count5/5

    14 tools sits squarely in the well-scoped range and each one corresponds to a real lifecycle stage (discovery, import/store, dataset registration, workflow validate/run/visualize/export, provenance, artifact export). No obviously redundant or filler tools.

    Completeness4/5

    The core pipeline is covered end to end: discover operations, import data, register datasets, validate/run/visualize/export workflows, inspect provenance, and export results for R/statsmodels. Gaps are minor: no delete/prune for artifacts or datasets, no explicit in-memory artifact data read (only summary via inspect or disk write via export), and no workflow listing tool.

    Maintenance

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      B
      maintenance
      Enables creation and invocation of reusable research workflow programs derived from demonstrations, with live scholarly data search and workspace automation via multiple protocols.
      MIT
    • A
      license
      Not graded
      quality
      C
      maintenance
      Enables deterministic, resumable workflow automation through DAG step graphs, boolean gate branching, automatic parallel execution, and in-step agent loops with sandboxed file and command tools.
      7 npm
      1
      MIT
    • A
      license
      Not graded
      quality
      B
      maintenance
      Enables agents to track ML experiments with a reproducible write contract, internal git snapshots, and a queryable knowledge graph, plus a live web UI for humans.
      Apache 2.0
    • F
      license
      Not graded
      quality
      B
      maintenance
      Enables deterministic, harness-neutral research workflows by unifying research object identity, evidence receipts, claim-evidence graphs, and append-only decision logs, along with specialized scholarly skills for literature analysis, reproducibility audits, and multi-agent review.
      206 PyPI
      1
      -