ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

bci-mcp:把EEG脑电波实时接入Claude的MCP实践

bci-mcp:把EEG脑电波实时接入Claude的MCP实践 最近把吃灰快两年的 EEG 头环重新翻了出来折腾出一个叫 bci-mcp 的小项目——把实时脑电信号做成的“脑状态”通过 MCP 协议直接流进 Claude。简单说就是让 Claude 能实时“感知”你当前是专注还是涣散、是放松还是紧张甚至能捕捉到你眨了一下眼。这个项目解决的问题很直接以前想让 AI 读取 EEG 数据通常得先录一段脑电离线分析再写成文字喂给模型。现在不用了Claude 自己就能按需调用一个“脑状态”工具拿到结构化数据然后根据大脑状态动态调整回答或行为。如果你是脑机接口爱好者、MCP 插件开发者或者单纯想体验一把“用意念和 GPT 对话”的科幻感这篇文章都值得看完。我把整个实现过程、架构思路、以及踩过的几个比较隐蔽的坑都整理在下面了。1. 项目初探把脑电波“接”进 Claude 是什么体验1.1 一句话解释 bci-mcp 解决了什么问题先拆两个词BCI 是 Brain-Computer Interface脑机接口MCP 是 Model Context Protocol模型上下文协议Anthropic 推出的那个让 AI 模型连接外部工具和数据源的开放标准。bci-mcp 的核心工作就是把这两个东西接起来。在它出现之前Claude 无论多聪明就是一个没有感官的“大脑”只能处理文本输入。你让 Claude 帮你分析 EEG你得先把 csv 文件上传过去再写一段 prompt 让它算频段功率。整个过程是离线的割裂的。而 bci-mcp 把 EEG 设备变成了 Claude 的一个“感官器官”。它通过 MCP 暴露了一组工具比如get_brain_state、get_eeg_trend。Claude 在对话过程中可以主动调用这些工具拿回当前脑电状态分析结果然后基于这个上下文继续对话。举个例子我戴着 Muse S 头环写稿子Claude 会隔一段时间调用一次get_brain_state看到我的“专注度评分”掉到 60 以下它会在回答里加一句“你现在的专注度在下降建议起身拉伸两分钟”。这种体验跟手动传数据完全不是一回事更像是有一个能读懂你状态的工作搭档在一旁。1.2 一个典型的应用场景演示实时脑状态让 Claude 感知我实际演示这个项目时最喜欢用两个场景。第一个是“脑控记笔记”。Muse 设备里有一个眨眼检测功能连续眨两下眼会在 EEG 信号里产生一个明显的伪迹模式。bci-mcp 检测到这个事件后会通过 MCP 工具调用触发一条消息Claude 收到后执行一个预设指令比如“把刚才对话里的关键结论整理成 3 个要点添加到笔记文件”。整个过程不用碰键盘只要眨眨眼就行。第二个是“状态感知问答”。我设定 Claude 的系统提示词里明确要求“回答前先调用 get_brain_state如果专注度低于 50就用简洁口语化方式回答如果高于 80就给出深度分析。”刚开始我觉得这有点噱头但实际用下来当你真的脑子一片浆糊时Claude 确实会用更轻松的方式回应不会甩一大堆术语。这种闭环一旦跑起来你会很自然地开始设计更多基于状态的条件响应。这两个场景共同说明一件事脑电信号不一定要变成“文字”才能被 AI 理解它变成结构化的状态参数后AI 可以像使用天气接口一样直接使用它。2. 系统架构拆解从脑电帽到 Claude 的消息链路这个项目看起来是“EEG MCP”两个关键词的拼接但真正落地时要拆成四个环节信号采集、信号处理、状态封装、协议传输。每一个环节都有一堆细节下面逐个讲。2.1 硬件端EEG 设备与数据采集支持哪些设备我手上有一台 Muse S三通道参考电极 两通道辅助电极一个比较老的 OpenBCI Cyton 板子八通道。bci-mcp 在设计上抽象了一个统一的EEGSource接口只要能输出 raw EEG 数据流的设备都可以接。不同设备的接入方式差别很大Muse 系列官方提供 iOS/Android 的开发者库也有开源的muselsl库可以扫描、连接、接收实时数据。Muse 采样率是 256Hz四通道TP9、AF7、AF8、TP10用蓝牙低功耗传输。OpenBCI通过 Cyton 的 dongle 串口或 WiFi Shield 传输采样率默认 250Hz可以配置到更高。OpenBCI 的好处是电极位置灵活能测更多脑区适合研究用。NeuroSky 类单导设备消费级玩具滤波和参考做得比较糙但胜在便宜、连接方便用于 demo 也够。连接上之后设备会源源不断吐原始数据包通常是一个时间戳加每个通道的微伏值。这个数据还不能直接用里面混着大量环境噪声、电极接触噪声、肌肉活动干扰得先进下一环。2.2 信号处理端脑状态解析从 raw EEG 到“专注度”“放松度”这种语义化指标中间需要做几步固定操作带通滤波EEG 有效频段主要集中在 0.5~50Hz先用 5 阶巴特沃斯滤波器做带通滤掉工频干扰中国 50Hz有些地方是 60Hz和高频噪声。滑动窗口切分取最近 2 秒的数据作为一个窗口与下一个窗口重叠 1 秒这样每秒能出一个新状态值兼顾实时性和稳定性。FFT 功率谱估计对窗口数据做快速傅里叶变换分别计算 delta1-3Hz、theta4-7Hz、alpha8-12Hz、beta13-30Hz四个频段的平均功率。状态指数算法不同频段功率组合能反映不同状态。比如经典的神驰指数distraction index用 beta 功率除以 alpha 加 theta 的功率专注度也常用 beta 波相对功率做线性映射。实际实现里我直接用一个经验公式focus_score 50 if beta_relative 0.25: focus_score min(40, (beta_relative - 0.25) * 300) if alpha_relative 0.35: focus_score - min(20, (alpha_relative - 0.35) * 200)这类线性映射其实非常粗糙但作为实时交互指标已经够了。重要的是把连续值控制在 0~100Claude 才能直观理解。眨眼检测眨眼在额叶电极AF7/AF8上会引起一个短时大幅值尖峰时域上做幅度阈值检测即可。Muse 的数据里眨眼信号通常超过安静状态的 4~5 倍而且持续时间很短很好识别。再加一个 500ms 的冷却时间防止连续眨眼被计数成多次。2.3 协议端MCP 如何把脑状态“翻译”成 Claude 可读的上下文MCP 服务器的形态很简单一个进程通过标准输入输出stdio或者 HTTP 与宿主程序通信用 JSON-RPC 2.0 处理 request/response。bci-mcp 实现的是 stdio 模式因为 Claude Desktop 和 Claude Code 都能直接用本地命令启动一个子进程。你在 MCP 服务器里定义工具数据结构大概是这样的{ name: get_brain_state, description: 获取当前脑电信号分析结果包括专注度、放松度和眨眼事件, inputSchema: { type: object, properties: { window_seconds: { type: number, description: 分析窗口长度可选 1 或 2 秒 } } } }Claude 收到用户指令后如果判断需要脑状态信息就会向 MCP 服务器发送工具调用请求。服务器立刻计算当前窗口的状态返回一个 JSON{ timestamp: 2025-06-04T10:23:45.22108:00, focus_score: 78.5, relaxation_score: 43.2, blink_count: 2, last_blink_time: 2025-06-04T10:23:41.88008:00, band_power: { delta: 1245.2, theta: 876.1, alpha: 1422.8, beta: 980.3 } }这个 JSON 就是 Claude 的“上下文”。Claude 不需要懂信号处理它只需要把它当成传感器读数来理解即可。2.4 为什么选择 MCP 而不是传统 API/串口转发你可能会问这不就是个数据服务我自己写个 HTTP 接口让 Claude 调不行吗也能行但有几个实际痛点 MCP 帮你解决了。首先是工具发现的自动化。如果自己实现 HTTP API你需要把接口文档写进 system promptClaude 才有可能正确构造请求。MCP 支持工具列表发现宿主程序会自动把工具名、描述、参数 schema 注入给模型不需要人工维护 prompt。其次是统一的标准。不同 AI 客户端Claude Desktop、Claude Code、可能以后还有别的 IDE 插件都遵循同一套 MCP 协议。我今天在 Claude Desktop 里配置好的 bci-mcp过几天迁移到 Claude Code 上只需要在配置文件里加一行同样的命令。不用为每个客户端单独写一套适配层。再就是权限控制更清晰。MCP 天然是用户主动授权的Claude 只能调用已注册的工具并且工具可以限制调用频率、设置请求参数上界。如果只是写一个 localhost HTTP server你得自己处理跨域、鉴权、请求泛滥甚至有可能被浏览器网页里夹带的恶意脚本偷偷调用。MCP 这种双向确认机制用起来放心得多。当然MCP 也有学习成本配置文件格式、JSON-RPC 协议、stdio 的生命周期管理都需要看文档。但比起从零写一套“反序列化 函数路由 错误处理”的框架MCP 的收益是长期的。3. 实操搭建从零跑通 bci-mcp 全流程写得再多的架构分析最后还是要跑起来才算数。这里给出一份我实测可行的搭建流程按步骤抄作业就可以了。3.1 环境准备采样设备、Python、Node、Claude Desktop/Claude Code我先列一下我的环境版本尽量与你环境一致减少不必要的排障时间操作系统Windows 11macOS 也测过但蓝牙权限配置略不同Python 3.10需要安装numpy、scipy、pylsl、mne可选Node.js 16因为官方 MCP SDKmodelcontextprotocol/sdk需要 Node 环境Claude Desktop 最新版或 Claude Code 最新 CLI如果你用 Muse 设备建议先装muselsl库验证数据源pip install muselsl scipy numpy muselsl list看到自己的 Muse 设备出现在列表里说明蓝牙和驱动都没问题。Claude Code 的安装相对独立我上次从零装最靠谱的方式是npm install -g anthropic-ai/claude-code claude --version如果你是在 VS Code 里集成使用也可以通过 Claude Code VS Code 扩展来操作。注意一个常见错误Windows 下如果提示 “Claude native binary not installed”多半是 npm postinstall 脚本没跑成功重新安装 npm 包或者检查 Node.js 权限即可解决。还有不少人是卡在 virtual machine platform 的提示上这个是 Windows 系统功能没启用需要打开“虚拟机平台”可选功能然后在 PowerShell 里执行wsl --update。另外如果直接跑 Claude Code 遇到网络连接问题比如ECONNRESET之类绝大多数时候是代理配置问题检查你的系统环境变量HTTP_PROXY/HTTPS_PROXY是否设置正确。这属于 Claude Code 的基础环境问题和 bci-mcp 本身无关。3.2 安装并配置 MCP 服务器我的 bci-mcp 代码仓库用一个脚本入口同时支持两种宿主方式但原理一致最终是通过一个命令行启动 python 进程。配置 Claude Desktop找到claude_desktop_config.json在 Claude 的菜单中打开“开发者”设置可以直达然后在mcpServers节点加一个字段{ mcpServers: { bci-mcp: { command: python, args: [-m, bci_mcp.server, --device, muse], env: { MUSE_DEVICE_NAME: Muse-S-XXXX } } } }如果你的 Python 开启了虚拟环境这里的command要改成虚拟环境里 python 的绝对路径否则 MCP 进程可能找不到依赖。配置 Claude Code直接在终端执行claude mcp add bci-mcp -- python -m bci_mcp.server --device muse这个命令会自动把服务器注册到 Claude Code 的本地配置里用claude mcp list可以看到。写完后重启 Claude Desktop在对话界面里输入一个触发词比如“读取一下我的专注度”如果配置正确Claude 会先调用工具再回答。提示第一次配置最容易出的问题是 JSON 格式错误。env里的值必须是字符串端口号这种数字要加引号写成字符串。我以前单独给capture_interval_ms配了整数结果 Claude Desktop 直接不加载 MCP server日志里报 “invalid config type”排查了半天才发现是类型问题。3.3 创建自定义“脑状态”工具tools的代码逻辑MCP 服务器代码并没有多复杂核心是继承FastMCP类并注册函数。我用 Python SDK 写的核心逻辑关键代码也就百来行。下面这个片段展示了get_brain_state的实现from fastmcp import FastMCP import numpy as np from eeg_processing import compute_band_power, detect_blink, calculate_state mcp FastMCP(bci-mcp) eeg_buffer BufferManager(window_size512) # 2s at 256Hz mcp.tool() def get_brain_state(window_seconds: int 2) - dict: 获取当前脑电状态包括专注度、放松度和最近眨眼时间 data eeg_buffer.get_window(window_seconds) if data is None or data.shape[0] 128: return {error: insufficient_eeg_data, hint: 请等待2秒后再试} band_power compute_band_power(data, fs256, bands{ delta: (1, 3), theta: (4, 7), alpha: (8, 12), beta: (13, 30) }) state calculate_state(band_power) blink_time detect_blink(data, fs256) return { timestamp: get_current_iso_time(), **state, blink_detected: blink_time is not None, blink_timestamp: blink_time, }一些关键设计点BufferManager维护一个环形缓冲后台线程持续从lsl实验室流网络读取 EEG 数据并覆盖写入。这样无论工具多久被调用一次取到的都是“最近的 N 秒”。compute_band_power内部对原始信号去均值、应用 5~50Hz 带通滤波再做 FFT。这一步相对耗时但窗口 2 秒数据量只有 512 个点现代 CPU 计算时间在 5ms 以内完全不影响交互。calculate_state返回的focus_score和relaxation_score都是从功率比值映射到 0~100 的整数。对 Claude 来说整数比带一堆小数点的浮点数更好理解和比较。服务器注册完工具后最后一件事就是mcp.run()默认走 stdio 模式。3.4 流式传输的效果观察Claude 如何“实时”响应配置好之后我启动 Claude Code在会话中输入你现在是我的专注力助手。每当我问“我现在状态如何”你就调用 get_brain_state然后基于数据给我反馈。让我试试。然后我戴上 Muse盯着屏幕看了一分钟文档。给 Claude 发送“我现在状态如何”。从 Claude Code 的界面能看到它先展示工具调用大约几百毫秒后返回结果然后生成一段自然语言回复你当前专注度是 82放松度是 31处于比较紧张的高投入状态。建议30分钟后安排一个5分钟放松我可以到时候提醒你。如果我在调用前故意闭眼放松十几秒再问一次回复就会变成专注度下降到 45放松度上升到 72看起来你刚切换到了休息模式。需要我把当前任务暂停吗这就是流式传输的意义Claude 每次调用工具拿到的都是新的状态它能察觉数据变化产生更有针对性的对话。我在这步踩过的最大坑是工具调用频率失控。如果没有限制Claude 很可能会连续调用十几次get_brain_state导致数据缓冲读空返回一堆insufficient_eeg_data。解决方法是给工具加一个 2 秒的最小调用间隔调用太快直接抛异常Claude 看到异常后通常会退而使用缓存或等待。这招是从限流器借来的思路实测有效。4. 踩坑实录与关键参数调优理论和 demo 都跑通后你会发现想稳定使用还需要跟数据质量和运行环境较劲。下面是我在实际项目中花时间最多的几类问题。4.1 EEG 数据噪声问题如何滤掉眨眼和肌肉伪迹脑电信号极其微弱微伏级别很容易被其他生物电信号污染。额叶电极最头疼的就是眨眼眼睑运动会带来一个振幅超大的尖峰如果这个尖峰进入功率统计会显著拉高 beta 波功率直接导致专注度指数爆表让人产生错觉“我闭个眼反而特专注”。我的处理策略分两道防线第一道是在进入统计分析前先做一个峰值检测。设定一个动态阈值比如当前通道振幅超过最近 10 秒中位数的 5 倍就认为这是伪迹片段整个窗口丢弃。宁可少一个状态值也不能用脏污数据。第二道是频段选择。开眼专注状态下的 beta 波主要集中在额叶中央区而眨眼伪迹的能量遍布全频段尤其在 0~10Hz。我用 beta 相对功率时再叠加一个 deltatheta 绝对功率的阈值如果低频段功率也同时爆炸那大概率是伪迹把 focus_score 强制拉回 50 附近。经过这两道处理实时数据流基本稳定。但要承认这样只能滤掉明显的粗大伪迹想要把肌电、电极接触不良等长时噪声也滤干净需要 ICA独立成分分析这类离线算法不适合实时场景。对于 bci-mcp 这种交互型项目没必要过度追求信号纯度。4.2 信号延迟与采样率取舍做实时交互延迟是硬指标。我在实际联调中测了一组数据整体链路EEG 采集 → 蓝牙传输 → LSL 缓冲 → 工具调用 → FFT 计算 → JSON 返回 → Claude 生成回复。纯数据采集到返回结果延迟大约在 300-500ms 之间。但注意get_brain_state只会反映“调用发生时”之前 2 秒窗口的状态所以 Claude 拿到的其实是 2 秒前的状态。这个滞后对“专注度下降提醒”完全够用毕竟人的状态变化是以分钟为单位的。如果你把窗口从 2 秒改成 1 秒实时性会更好但 FFT 的分辨率从 0.5Hz 降到 1Hz低频段的区分度变差alpha 波8-12Hz和 theta 波4-7Hz计算更容易受漂移影响。我建议交互场景保持 2 秒窗口1 秒重叠步长。采样率方面256Hz 足够覆盖到 128Hz 的奈奎斯特频率而脑电研究的有效频段一般在 50Hz 以内。没必要追求 OpenBCI 的 2000Hz 高采样率数据量大了反而增加传输和计算成本。4.3 MCP 连接中的常见错误这部分单独说因为我把常见问题整理成了一张速查表解答效率极高。现象可能原因解决方案Claude Desktop 配置后不加载 MCP serverJSON 格式错误或command路径不对用claude mcp list验证确保配置键值都是字符串启动后立刻退出日志显示 module not foundPython 虚拟环境没激活或未安装依赖把command改为虚拟环境绝对路径调用工具后一直转圈不返回mcp.run()后回执被阻塞缓存缓冲区空转给 MCP server 加--log-level输出调试日志确认 EEG 后台线程已启动Connection dropped (ECONNRESET)宿主应用与 MCP 子进程的 stdio 管道断开重启 Claude 应用删除 MCP 配置对应的旧日志文件Claude 以为工具不存在配置写错位置或服务名和代码里不一致检查配置文件中mcpServers下的 key 是否与文件名对应另外如果你使用claude code并通过 npx 启动 JS 版本 MCP 服务器常见问题是 npx 首次下载包时等待时间过长Claude 默认超时可以改成先用npm install -g全局安装再直接用全局命令启动。不要小看这些问题。我最初以为 MCP 已经稳定了结果在 Windows 和 macOS 之间来回切换时光 stdio 管道编码问题就折腾了小半天。后来统一命令行启动方式后迁移问题才减少。4.4 从“玩具”到“生产力”未来扩展方向bci-mcp 跑通后我发现它天然是一个很好的“人体状态输入框架”。顺着这个思路延展有几个方向很值得探索。第一个是状态历史分析。现在get_brain_state只返回当前时刻状态但 MCP 还支持 resource 和 prompt 资源。我计划把每 5 秒的状态写入一个时序队列然后提供一个get_brain_trend(minutes30)工具让 Claude 能读取过去半小时的专注度曲线回答“你上午哪个时间段效率最高”这类问题。第二个是多模态融合。EEG 状态可以叠加到已有的桌面助手场景中Claude 检测到你长时间高专注后主动建议进行深呼吸训练检测到频繁眨眼意识到你可能疲劳了自动把回复速度放慢。这些结合 MCP 已有的文件、终端工具可以形成一个真正的“状态驱动自动化”平台。第三个是更精细的认知任务识别。目前用线性映射只能粗略区分专注/放松。如果结合多通道源定位和最小范数估计比如用mne-minimum-norm做实时源定位理论上可以区分“视觉注意力”和“言语思维”两种不同模式Claude 就能根据你当前脑区活跃模式给出完全不同的交互策略。这已经是比较深的研究方向但对感兴趣的人是个很好的进阶目标。最后说点我自己的体会整个 bci-mcp 从设计到跑通最大的感触是“稳定的数据比复杂的算法重要”。最初我试图用深度学习模型从 raw EEG 里挖更多细粒度状态后来发现实际使用中模型在个体间迁移效果很差每个人头骨厚度、电极位置差异都会毁掉模型有效性。反而是几个简单频段功率指标 适当滤波在几十个小时的使用中都非常稳定。如果你也想玩这个方向我建议按“最小闭环”思路推进先戴上头环用现成的muselsl命令行看看实时波形确认数据是干净的再写一个最小的 MCP 工具返回一个简单的focus_score让 Claude 输出你能否读懂最后再逐步加入眨眼事件、放松度、趋势分析等功能。另外一个非常实用的小技巧在 MCP 服务器里给每次工具调用的返回结果加一个cached_ttl字段告诉 Claude 这条状态在多长时间内有效。这样能减少无意义的重复调用也避免了 Claude 在你不注意时疯狂刷新脑状态把有限注意力消耗在无意义的上下文翻页上。bci-mcp 这个项目目前还在持续迭代但哪怕保持现在这个功能水平它也让我第一次觉得“AI 不再只是聊天窗口而是真的能跟身体状态待在同一条时间线上”。接下来我会重点完善趋势记录和能力插件化争取把更多 EEG 设备支持加进去。如果你也在 MCP 上做过类似人体信号接入欢迎交流踩坑经验。
返回列表