Core & DSP API¶
bci_mcp.core.device ¶
Device abstraction shared by every EEG backend.
Chunk
dataclass
¶
Source code in src/bci_mcp/core/device.py
21 22 23 24 | |
Device ¶
Bases: ABC
A streaming EEG source. Subclasses run their own acquisition internally.
Source code in src/bci_mcp/core/device.py
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | |
read
abstractmethod
¶
read()
Non-blocking pull of newly available samples, or None if none/not streaming.
Source code in src/bci_mcp/core/device.py
38 39 40 | |
DeviceInfo
dataclass
¶
Source code in src/bci_mcp/core/device.py
10 11 12 13 14 15 16 17 18 | |
bci_mcp.core.registry ¶
URI-based device registry: create_device('synthetic://?focus=0.8').
Device ¶
Bases: ABC
A streaming EEG source. Subclasses run their own acquisition internally.
Source code in src/bci_mcp/core/device.py
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | |
read
abstractmethod
¶
read()
Non-blocking pull of newly available samples, or None if none/not streaming.
Source code in src/bci_mcp/core/device.py
38 39 40 | |
create_device ¶
create_device(uri)
Source code in src/bci_mcp/core/registry.py
17 18 19 20 21 22 23 24 25 | |
discover ¶
discover()
Best-effort device discovery: always-available schemes + scanned serial ports.
Source code in src/bci_mcp/core/registry.py
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | |
list_schemes ¶
list_schemes()
Source code in src/bci_mcp/core/registry.py
28 29 | |
register ¶
register(scheme, factory)
Source code in src/bci_mcp/core/registry.py
13 14 | |
bci_mcp.pipeline ¶
Pipeline: ties a Device/Stream to the DSP chain and emits BrainState.
BrainState
dataclass
¶
Source code in src/bci_mcp/dsp/state.py
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | |
Calibration
dataclass
¶
Source code in src/bci_mcp/dsp/calibration.py
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | |
Device ¶
Bases: ABC
A streaming EEG source. Subclasses run their own acquisition internally.
Source code in src/bci_mcp/core/device.py
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | |
read
abstractmethod
¶
read()
Non-blocking pull of newly available samples, or None if none/not streaming.
Source code in src/bci_mcp/core/device.py
38 39 40 | |
Pipeline ¶
Source code in src/bci_mcp/pipeline.py
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | |
Stream ¶
Source code in src/bci_mcp/core/stream.py
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | |
create_device ¶
create_device(uri)
Source code in src/bci_mcp/core/registry.py
17 18 19 20 21 22 23 24 25 | |
bci_mcp.dsp.bands ¶
Band power via Welch PSD.
Band power is the integral of the Welch power spectral density over each band
(scipy welch + trapezoid). This is the standard EEG band-power estimator; by
Parseval it equals the variance (RMS²) of the band-filtered signal up to scaling,
so the choice of "PSD vs RMS" is a unit convention, not a correctness question.
Two practical details handled here:
- Welch averaging.
npersegtargets ~1 s segments with 50% overlap, so a longer analysis window is averaged over several segments (lower-variance PSD) instead of returning a single noisy periodogram. - Narrow-band guard.
trapezoidover a single frequency bin returns 0. At coarse resolution a narrow band (e.g. gamma at a low sample rate) can capture just one bin, which would silently zero that band's power; we fall back to rectangular integration in that case.
BANDS
module-attribute
¶
BANDS = {'delta': (1.0, 4.0), 'theta': (4.0, 8.0), 'alpha': (8.0, 13.0), 'beta': (13.0, 30.0), 'gamma': (30.0, 45.0)}
band_powers ¶
band_powers(data, fs, nperseg=None)
Mean absolute power per band, averaged over channels (µV²).
Source code in src/bci_mcp/dsp/bands.py
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | |
relative_band_powers ¶
relative_band_powers(bp)
Source code in src/bci_mcp/dsp/bands.py
63 64 65 | |
bci_mcp.dsp.metrics ¶
Cognitive metrics derived from band powers.
These are heuristic band-power ratios, not validated clinical measurements.
Each one is a recognizable simplification of an index from the literature; the
METRIC_INFO table below records the exact formula, the work it draws on, and
an honest caveat for every metric. Downstream consumers (CLI, dashboard,
MCP/LLM) should treat the values as proxies, weight them by the per-reading
confidence the pipeline attaches, and never present them as ground-truth
cognitive states.
Design notes¶
focususes the genuine Pope, Bogart & Bartolome (1995) engagement indexβ / (α + θ)— the best-validated of these ratios.engagementis a distinct arousal ratioβ / α(not the Pope index), so the two are not duplicates.- Delta and gamma are intentionally excluded from every metric: on consumer hardware over short windows, 30-45 Hz "gamma" is dominated by EMG and <1-4 Hz delta by drift/ocular artifact, so building user-facing metrics on them adds noise, not signal. They are still reported as raw band powers.
- The metrics are chosen so no two are collinear on the same band in the same
direction — in particular
meditationfalls with theta whilefatiguerises with it, so a drowsy reading cannot score high on both (a relative- theta "meditation" metric would have confounded the two).
References¶
- Pope, Bogart & Bartolome (1995), Biological Psychology — engagement index
β / (α + θ). - Lubar (1991); Monastra et al. (1999), Neuropsychology — theta/beta ratio (TBR) for attention/ADHD; diagnostic validity later contested (Arns et al. 2013).
- Eoh, Chung & Kim (2005), Int. J. Industrial Ergonomics —
(θ + α) / βas a driver mental-fatigue index. - Berger (1929); NeuroSky eSense — alpha as the canonical relaxed-wakefulness rhythm underlying consumer calm/meditation meters.
DEFAULT_SCALING
module-attribute
¶
DEFAULT_SCALING = {'focus': (1.0, 1.0), 'calm': (0.5, 0.2), 'attention': (1.0, 1.0), 'engagement': (1.0, 1.0), 'fatigue': (1.0, 1.0), 'meditation': (0.5, 0.2)}
METRIC_INFO
module-attribute
¶
METRIC_INFO = {'focus': {'formula': 'beta / (alpha + theta)', 'basis': 'Pope et al. (1995) EEG engagement/concentration index — the best-validated of these ratios.', 'caveat': "A proxy for task engagement/concentration, not a direct measurement of 'focus'."}, 'calm': {'formula': 'alpha / (alpha + beta)', 'basis': 'Alpha-up / beta-down is a long-standing relaxation correlate.', 'caveat': "Resting alpha also tracks drowsiness and eyes-closed state, so high 'calm' is not necessarily relaxed attention."}, 'attention': {'formula': 'beta / theta (inverse of the theta/beta ratio, TBR)', 'basis': 'TBR (Lubar 1991; Monastra 1999) is the classic inattention marker.', 'caveat': "TBR's diagnostic validity is contested (Arns et al. 2013) and it is a between-subjects trait marker, not a moment-to-moment readout."}, 'engagement': {'formula': 'beta / alpha', 'basis': 'Beta/alpha arousal–activation ratio.', 'caveat': "An arousal proxy, distinct from the Pope engagement index (that formula is used by 'focus')."}, 'fatigue': {'formula': '(theta + alpha) / beta', 'basis': 'Eoh et al. (2005) driver mental-fatigue / drowsiness index.', 'caveat': 'Slow-wave dominance is ambiguous; corroborate with low engagement before concluding fatigue.'}, 'meditation': {'formula': 'alpha / (alpha + beta + theta)', 'basis': 'Relative alpha as a relaxed-wakefulness proxy (Berger rhythm; NeuroSky eSense convention).', 'caveat': 'NOT a validated meditation classifier. Deliberately falls with theta so it does not collapse into the drowsiness signal the way a relative-theta metric would.'}}
METRIC_NAMES
module-attribute
¶
METRIC_NAMES = ('focus', 'calm', 'attention', 'engagement', 'fatigue', 'meditation')
raw_metrics ¶
raw_metrics(bp)
Unscaled metric ratios. Calibration maps these into 0..1 later.
See the module docstring and METRIC_INFO for the basis and caveats of
each metric. Only theta/alpha/beta are used; delta and gamma are excluded
on purpose (drift- and EMG-dominated on consumer hardware).
Source code in src/bci_mcp/dsp/metrics.py
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | |
bci_mcp.dsp.citations ¶
Machine-readable citations for what this pipeline computes.
METRIC_INFO explains each metric in prose; this module gives an agent that
cites its sources something it can resolve and check: DOI-keyed references for
the analysis method and for each metric's literature basis, the software's own
citation and license, and a plain statement of data provenance.
Scope is deliberately narrow. bci-mcp only vouches for sources it actually
implements, so a metric with no single primary source gets an empty list rather
than a plausible-looking citation, and a source that contests a metric is
tagged caveat so it is never cited as support. Dataset licensing is out of
scope: the server bundles no datasets and cannot see where an upstream stream's
samples came from (see DATA_PROVENANCE).
Entries mirror paper/paper.bib; a test keeps the DOIs in sync.
DATA_PROVENANCE
module-attribute
¶
DATA_PROVENANCE = {'bundled_datasets': [], 'statement': "bci-mcp bundles no datasets. Readings are computed live from the connected source: a headset, an LSL stream, or generated synthetic:// signal. The server cannot see where an upstream stream's samples came from, so it attaches no dataset, license or access terms to readings. If a third-party dataset is replayed into it, that dataset's own license and citation still apply; cite it directly."}
METHOD_CITATIONS
module-attribute
¶
METHOD_CITATIONS = [('welch1967', 'basis'), ('virtanen2020', 'implementation')]
METRIC_CITATIONS
module-attribute
¶
METRIC_CITATIONS = {'focus': [('pope1995', 'basis')], 'calm': [('berger1929', 'basis')], 'attention': [('lubar1991', 'basis'), ('monastra1999', 'basis'), ('arns2013', 'caveat')], 'engagement': [], 'fatigue': [('eoh2005', 'basis')], 'meditation': [('berger1929', 'basis')]}
NOTE
module-attribute
¶
NOTE = 'DOIs are the stable identifiers; resolve one via its `url` before citing it. Cite `caveat` sources alongside the metric, never as support for it. An empty list means the metric has no single primary source (see get_metric_definitions `basis`).'
REFERENCES
module-attribute
¶
REFERENCES = {'welch1967': {'authors': ['Welch, Peter D.'], 'year': 1967, 'title': 'The use of fast Fourier transform for the estimation of power spectra: A method based on time averaging over short, modified periodograms', 'journal': 'IEEE Transactions on Audio and Electroacoustics', 'doi': '10.1109/TAU.1967.1161901'}, 'virtanen2020': {'authors': ['Virtanen, Pauli', 'Gommers, Ralf', 'Oliphant, Travis E.'], 'et_al': True, 'year': 2020, 'title': 'SciPy 1.0: Fundamental algorithms for scientific computing in Python', 'journal': 'Nature Methods', 'doi': '10.1038/s41592-019-0686-2'}, 'pope1995': {'authors': ['Pope, Alan T.', 'Bogart, Edward H.', 'Bartolome, Debbie S.'], 'year': 1995, 'title': 'Biocybernetic system evaluates indices of operator engagement in automated task', 'journal': 'Biological Psychology', 'doi': '10.1016/0301-0511(95)05116-3'}, 'lubar1991': {'authors': ['Lubar, Joel F.'], 'year': 1991, 'title': 'Discourse on the development of EEG diagnostics and biofeedback for attention-deficit/hyperactivity disorders', 'journal': 'Biofeedback and Self-Regulation', 'doi': '10.1007/BF01000016'}, 'monastra1999': {'authors': ['Monastra, Vincent J.', 'Lubar, Joel F.', 'Linden, Michael', 'VanDeusen, Peter', 'Green, George', 'Wing, William', 'Phillips, Arthur', 'Fenger, T. Nick'], 'year': 1999, 'title': 'Assessing attention deficit hyperactivity disorder via quantitative electroencephalography: An initial validation study', 'journal': 'Neuropsychology', 'doi': '10.1037/0894-4105.13.3.424'}, 'arns2013': {'authors': ['Arns, Martijn', 'Conners, C. Keith', 'Kraemer, Helena C.'], 'year': 2013, 'title': 'A decade of EEG theta/beta ratio research in ADHD: A meta-analysis', 'journal': 'Journal of Attention Disorders', 'doi': '10.1177/1087054712460087'}, 'eoh2005': {'authors': ['Eoh, Hong J.', 'Chung, Min K.', 'Kim, Seong-Han'], 'year': 2005, 'title': 'Electroencephalographic study of drowsiness in simulated driving with sleep deprivation', 'journal': 'International Journal of Industrial Ergonomics', 'doi': '10.1016/j.ergon.2004.09.006'}, 'berger1929': {'authors': ['Berger, Hans'], 'year': 1929, 'title': 'Über das Elektrenkephalogramm des Menschen', 'journal': 'Archiv für Psychiatrie und Nervenkrankheiten', 'doi': '10.1007/BF01797193'}}
SOFTWARE_CITATION
module-attribute
¶
SOFTWARE_CITATION = {'title': 'BCI-MCP: Plug your brain into any AI', 'authors': [{'name': 'Ganbold, Enkhbold', 'orcid': 'https://orcid.org/0009-0007-1785-7034'}], 'license': 'MIT', 'repository': 'https://github.com/enkhbold470/bci-mcp', 'url': 'https://enkhbold470.github.io/bci-mcp/'}
_resolve ¶
_resolve(entries)
Source code in src/bci_mcp/dsp/citations.py
142 143 144 145 146 147 | |
citations ¶
citations()
Everything an agent needs to cite this server's output.
Source code in src/bci_mcp/dsp/citations.py
160 161 162 163 164 165 166 167 168 169 170 | |
method_citations ¶
method_citations()
Resolved references for the analysis method (Welch PSD).
Source code in src/bci_mcp/dsp/citations.py
150 151 152 | |
metric_citations ¶
metric_citations(metric)
Resolved references for one metric (empty if it has no primary source).
Source code in src/bci_mcp/dsp/citations.py
155 156 157 | |
bci_mcp.dsp.state ¶
The unified brain-state snapshot shared by CLI, dashboard, and MCP.
BrainState
dataclass
¶
Source code in src/bci_mcp/dsp/state.py
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | |