mcpField
M
mifit

mifit

本地服务
github.com/Jenwein健康医疗02 周前更新

关于 mifit

mifit 是用于从 Xiaomi 云端读取个人 Mi Fitness 数据的 Python 库、命令行工具和 MCP 服务,提供结构化健康与运动记录。MCP 服务默认不提供 GPS 轨迹。

主要功能

  1. 01读取步数、睡眠和心率数据
  2. 02获取运动记录及每日健康摘要
  3. 03按日期范围导出 JSON 数据
  4. 04读取未经解析的服务数据
  5. 05扫描二维码登录

适用场景

  1. 01让助手总结近期睡眠情况
  2. 02查看步数或心率趋势
  3. 03导出指定日期范围的运动记录
  4. 04检查其他健身数据字段

MCP 相关

本地 MCP 服务提供读取 Mi Fitness 记录以及启动或检查二维码登录的工具。只有使用 `--allow-gps` 明确启用后,才会返回运动 GPS 轨迹。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"jenwein-mifit": {
"command": "uvx",
"args": [
"mifit"
]
}
}
}
启动命令uvx mifit

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
pypimifit0.3.2

工具

16

收录的工具列表 · 更新于 2 周前

此工具无需参数。

详细介绍

mifit

CI PyPI

从 Xiaomi 云端读取你自己的 Mi Fitness(小米运动健康)数据:一个轻量级 Python 库、命令行工具和面向 AI 助手的 MCP 服务。

Mi Fitness 应用会将你的步数、心率、睡眠等数据同步到 Xiaomi 云端,但 Xiaomi 不为个人用户提供公开 API。mifit 通过二维码登录你自己的账号,读取已同步的数据,并以 JSON 或 Python 对象的形式返回,方便你基于这些数据进行开发。

非官方。 与 Xiaomi 无关联,未获其认可或支持。 请参阅免责声明。

当前可读取的数据

领域可获取的数据records 类型已与应用核对
steps每日步数和距离StepRecord是
heart_rate原始心率采样HeartRatePoint是
heart_rate_daily每日心率:平均值、最高值、最低值、静息心率HeartRateDailySummary是
consumption每日卡路里:静息、活动、摄入、总计ConsumptionRecord是
sleep每日睡眠:总时长、各阶段分钟数,以及包含睡眠阶段时间线的睡眠时段SleepRecord → SleepSession → SleepStage是;无法核对睡眠评分,仅在 raw 中提供
workouts每次锻炼一条记录:开始和结束时间、运动类型、时长、距离、活动和总卡路里、平均和最高心率、步数Workout是
workout_detail每次锻炼(户外跑步和骑行):每秒采样数据(心率、累计距离和步数、步频、配速或速度)以及 GPS 轨迹WorkoutDetail → WorkoutSample / TrackPoint是(一场户外跑步、一次骑行);海拔仅在 raw 中提供:跑步数据与应用显示值相差几米
spo2_daily每日血氧(SpO2):平均值、最高值、最低值Spo2DailySummary是
spo2原始 SpO2 读数Spo2Point是
stress_daily每日压力水平:平均值、最高值、最低值,以及各压力区间(放松、轻度、中度、重度)的分钟数StressDailySummary是
activity_daily每日活动数据,与应用中的活力指标页面一致:活动次数(活动次数)、活动分钟数(活动时长)、活动卡路里(卡路里)ActivityDailySummary是
weight体重记录:时间、体重、BMI(身体成分仅在 raw 中提供)WeightRecord是;无法核对身体成分,仅在 raw 中提供
profile(独立命令:mifit profile)身高、体重、每日步数、卡路里和活动时长目标UserProfile是
raw(CLI:mifit raw;Python:client.raw、raw_window、daily_report)服务发送的任意健身数据键或每日报告键对应的行无:服务返回的原始行,不作解读不适用:不会对数据作解读

“已与应用核对”表示这些数值已与 Mi Fitness 应用的显示值进行比较,使用的是一个真实账号(中国区,UTC+8)。后续还会核对更多数据。在此期间,mifit raw(Python 中为 raw_window 和 daily_report)会原样返回健身数据或(使用 --daily 时)每日报告端点中任意其他键对应的行,不作解读。

各版本的变更(包括重大变更)请参阅 CHANGELOG.md。

安装

需要 Python 3.12 或更高版本;如有需要,uv 会为你安装。

uv tool install mifit     # recommended: its own environment, `mifit` on your PATH
pipx install mifit        # the same, with pipx
pip install mifit         # into the current environment, to use it as a library

升级:uv tool upgrade mifit(或 pipx upgrade mifit、pip install -U mifit)。

登录

mifit login

终端中会显示一个二维码,二维码下方是登录 URL。用手机上的 Xiaomi / Mi Fitness 应用扫描二维码(或打开 URL),并批准登录。你的密码不会经过 mifit。

会话保存在 ~/.mifit/session.json 中(Windows 上为 %USERPROFILE%\.mifit\session.json)。它可访问你 Mi Fitness 账户的健康数据(不仅限于读取)。其中还包含你的账户 passToken,mifit 会在会话过期时用它续期。passToken 适用于整个账户:它可以获取任何 Xiaomi 服务的令牌,因此该文件可访问你的整个 Xiaomi 账户,而不仅仅是 Mi Fitness 健康数据;即使会话令牌本身过期很久,它仍然有效。请像对待密码一样妥善保管,不要分享或提交该文件。在 Linux 和 macOS 上,只有你本人可以读取该文件;在 Windows 上,则取决于用户配置文件的权限。

如果 Xiaomi 要求进行额外的安全确认,mifit login 会打印一个 URL:打开并确认,然后再次运行 mifit login。

与 AI 助手搭配使用(MCP)

mifit mcp 是一个 Model Context Protocol 服务:Claude Desktop、Claude Code、Cursor 和 VS Code 等 AI 助手会在你的电脑上启动它,并在你询问 Mi Fitness 数据时读取相关数据(“我上周睡得怎么样?”)。

哪些数据会离开你的电脑。 助手调用的工具所返回的结果会发送给助手的模型提供商,就像对话中的其他内容一样。登录信息保存在这台电脑的 ~/.mifit 中:服务在本地通过 stdin/stdout 运行,不打开网络端口,不会在结果中放入凭据,并且只读取你的健康数据(登录工具会将新会话保存到 ~/.mifit)。GPS 轨迹默认关闭:只有在服务启动时使用了 --allow-gps(在 Claude Desktop 扩展中:开启 允许 GPS 轨迹 开关),并且助手提出请求时,服务才会提供锻炼路线;路线会显示你去过哪里,包括你的住址。

只需登录一次,在终端中运行:

uvx mifit login

(或者在运行 uv tool install mifit 后执行 mifit login)。如果没有终端,可以让助手帮你登录:它会调用 login_start,返回一个二维码,你可以用 Xiaomi 或 Mi Fitness 应用扫描。

将服务添加到助手。 uvx([uv](https://docs.astral.sh/uv/) 的一部分)会在需要时下载并运行 mifit:

claude mcp add --scope user mifit -- uvx mifit mcp
// Claude Desktop: %APPDATA%\Claude\claude_desktop_config.json
// (macOS: ~/Library/Application Support/Claude/claude_desktop_config.json)
{"mcpServers": {"mifit": {"command": "uvx", "args": ["mifit", "mcp"]}}}

// Cursor: ~/.cursor/mcp.json
{"mcpServers": {"mifit": {"type": "stdio", "command": "uvx", "args": ["mifit", "mcp"]}}}

// VS Code: .vscode/mcp.json
{"servers": {"mifit": {"command": "uvx", "args": ["mifit", "mcp"]}}}

运行 uv tool install mifit 后,也可以将命令改为 mifit,并将 mcp 作为参数。要允许提供 GPS 轨迹,请在 mcp 后添加 --allow-gps(["mifit", "mcp", "--allow-gps"])。

对于可从 MCP Registry 安装服务的客户端,mifit 也已列入官方 MCP Registry,名称为 io.github.Jenwein/mifit。

一键安装到 Claude Desktop。 从 最新版本下载 mifit-<version>.mcpb 并打开它(或从 Claude Desktop 的“设置” → “扩展”安装):Claude Desktop(Windows、macOS)会提示安装该扩展。扩展设置中有一个默认关闭的 允许 GPS 轨迹 开关。扩展运行的是随附的 mifit 版本:要更新,请安装新版本中的 .mcpb。请使用扩展或上述 JSON 配置之一,不要同时使用两者。

工具。 每个工具读取一种数据;日期使用本地日历日。

工具读取内容每次调用
get_status今天的日期、时区、登录状态、GPS 设置(不读取云端数据)—
get_steps每日步数和距离92 天
get_calories每日卡路里:静息、活动、摄入、总计31 天
get_activity每日活动次数、时长和卡路里92 天
get_heart_rate_daily每日心率:平均值、最大值、最小值、静息心率92 天
get_sleep每日睡眠及其时段;可按需提供阶段时间线92 天(含睡眠阶段时为 7 天)
get_spo2_daily每日血氧92 天
get_stress_daily每日压力水平及各压力区间的时长92 天
get_weight称重记录92 天
get_workouts锻炼记录92 天
get_heart_rate心率采样;超过 500 条时按区间汇总7 天
get_spo2血氧读数;超过 500 条时按区间汇总7 天
get_workout_detail单次锻炼的逐秒采样(户外跑步和骑行),以及使用 --allow-gps 时的 GPS 轨迹(扩展中:开启 允许 GPS 轨迹 后)单次锻炼,≤ 500 行
get_profile身高、体重和每日目标—
login_start通过二维码登录(返回二维码)—
login_status登录状态—

结果是 Python API 的 records,时间使用本地时间 (2026-10-07T23:12:00+08:00),而不是 Unix 秒数;不会返回 raw。密集数据会裁减到最多 500 行,并在说明中注明裁减方式。结果会保留五分钟,因此再次询问不会发送新请求。

用自己的话提问;助手会选择相应工具。例如:“我这个月的静息心率升高了吗?”

导出

mifit export <domain> --from YYYY-MM-DD --to YYYY-MM-DD [--json PATH] [--full] [--no-track] [--chunk day|week|month] [--long] [--resume]
  • <domain>:activity_daily、consumption、heart_rate、heart_rate_daily、sleep、 spo2、spo2_daily、steps、stress_daily、weight、workouts 或 workout_detail (户外跑步和骑行的每秒采样数据及 GPS 轨迹)。
  • --from / --to:起始和结束日期,包含结束日期,按本地日历日计算。
  • --json PATH:写入文件,而不是标准输出。
  • --full:仅适用于 heart_rate;向过去翻页读取历史数据,而不是查询一个时间窗口(参见已知限制)。 这是一次读取:它会忽略 --from,因此无论范围多长,都不需要 --long,且不能与 --chunk、--long 或 --resume 一起使用。
  • --no-track:仅适用于 workout_detail;跳过 GPS 文件,因此不会请求或写入轨迹。
  • --chunk day|week|month:范围超过一个分块时的分块大小(1、7 或 31 天);默认值因域而异。
  • --long:允许导出超过 92 天的范围。
  • --resume:从 PATH.partial 继续中断的导出(需要 --json)。
mifit export sleep --from 2026-10-01 --to 2026-10-07 --json sleep.json

输出是一个包含三个列表的 JSON 对象:

{ "records": [], "raw": [], "notes": [] }
长时间范围

范围超过一个分块时(原始心率、SpO2 和 workout detail 为 7 天,其他数据为 31 天,均从 --from 开始计算),会按分块逐个读取,每次发送一个请求,并在标准错误输出进度。较短的范围仍像之前一样一次读取。各分块会合并为相同结构的单个输出;如果有多个分块,每条备注都会以其来源分块开头。超过 92 天的范围需要 --long:此类导出需要发送许多请求,可能持续数分钟。唯一的例外是 heart_rate --full:它会忽略 --from,一次读取完成,不分块,因此不需要 --long。

mifit export heart_rate --from 2026-01-01 --to 2026-06-30 --long --json heart_rate.json

使用 --json PATH 时,进度会保存在 PATH.partial 中:其中包含已导出的数据和导出参数(域、日期、分块大小),不含凭据。请像保护输出文件一样对待它。如果导出停止(例如网络错误、会话过期或按下 Ctrl+C),已完成的分块仍保留在该文件中;使用 --resume 重新运行相同命令,即可仅读取缺失的分块。输出与未中断运行的结果相同,写入完成后会删除临时文件。导出到标准输出时也会按相同方式分块,但无法恢复。

分块失败会导致导出停止;不会自动重试。如果某个分块持续失败,可以分别导出它之前和之后的范围,跳过该分块。

任意键:mifit raw
mifit raw <key> --from YYYY-MM-DD --to YYYY-MM-DD [--daily [--tag TAG]] [--json PATH] [--long]

按服务发送的原样读取健身数据端点中的任意键,或读取每日报告端点中的任意键(使用 --daily);无论 mifit 是否也为该键定义了域(heart_rate 有对应域):

  • 写入 {records: [], raw, notes}。raw 保存服务返回的行,包含其自身字段;value 会从 JSON 字符串解码。records 始终为空。
  • 对原始键,会提前一天向服务发出请求,并仅保留窗口内的行,以确保窗口最初几小时的数据完整。
  • --daily 会改为读取该键的每日报告,每个有数据的本地日期对应一行;--tag TAG 用于选择其他聚合标签(默认值为 daily_report)。
  • 这是一次分页读取:超过 92 天的范围需要 --long,且不支持 --resume。
  • 目前已知的键列于 notes/domain-catalog.md。
mifit raw stress --from 2026-10-01 --to 2026-10-07 --json stress.json

Python API

from mifit import MiFit

client = MiFit()  # uses the session saved by `mifit login`

steps = client.steps("2026-10-01", "2026-10-07")
for day in steps.records:
    print(day.date, day.steps, day.distance_m)

sleep = client.sleep("2026-10-01", "2026-10-07")
energy = client.consumption("2026-10-01", "2026-10-07")
heart = client.heart_rate("2026-10-07", "2026-10-07")      # one time window
heart_full = client.heart_rate("2026-10-07", "2026-10-07", full=True)

detail = client.workout_detail("2026-10-12", "2026-10-12")  # samples and GPS track
no_gps = client.workout_detail("2026-10-12", "2026-10-12", track=False)

rows = client.raw("sleep", "2026-10-07", "2026-10-07")     # any fitness-data key, as served
window = client.raw_window("stress", "2026-10-07", "2026-10-07")        # a complete window
daily = client.daily_report("valid_stand", "2026-10-01", "2026-10-07")  # daily rows

raw_window(key, ...) 是补全后的 raw:它会提前一天请求,并保留你指定时间窗口内的行,同时解码 value。daily_report(key, ...) 会返回该 key 的每日行,每个有数据的本地日对应一行;可以用 tag= 选择不同于 daily_report 的标签。两者返回的 records=[],行则位于 raw 中。

哪些 key 存在以及已验证的内容:notes/domain-catalog.md。

workout_detail 会在 records 中为每次锻炼提供一个 WorkoutDetail:其中包含 workout 摘要(即 workouts 中的 Workout)、samples(WorkoutSample,每个记录秒对应一个)和 track(TrackPoint);设置 track=False 可不请求 GPS 文件。

日期可以是 "YYYY-MM-DD" 字符串(本地日历日,包含起止日期),也可以是 Unix 秒数整数。每个领域方法都会返回包含 records、raw 和 notes 的结果;raw(key, ...) 会返回健身数据端点(data/get_fitness_data_by_time)中任意 key 的原始服务端行,而不是每日报告(完整时间窗口请参阅 raw_window)。mifit 特有的错误均继承自 mifit.MiFitError:AuthError(没有可用会话,或 HTTP 401)、其子类 RenewalError(自动续期无法解决的 HTTP 401;请重新登录)、ApiError(服务拒绝请求或返回了无法读取的内容)以及 NotificationRequiredError(小米要求进行安全确认时,登录过程会引发此错误)。网络故障会作为 requests 异常抛出;日期参数不正确则会引发 ValueError 或 TypeError。

对于较长的时间范围,read_chunks 会一次读取一个区块(使用 CLI 的区块大小;size="day"、"week" 或 "month" 可覆盖这些大小),merge_chunks 会合并各部分。日期只能使用 "YYYY-MM-DD" 字符串。

from mifit.chunks import merge_chunks

parts = []
for chunk, part in client.read_chunks("heart_rate", "2026-01-01", "2026-06-30"):
    print(chunk.label, len(part.records))  # each chunk is read when the loop asks for it
    parts.append((chunk, part))
heart = merge_chunks(parts)  # its records are those of one read of the whole range

read_chunks(..., skip=labels) 会跳过标签已存在的区块,以便继续读取;options= 会将关键字参数传递给每个区块的调用,例如,为 workout_detail 传入 {"track": False}。options={"full": True} 会被 ValueError 拒绝:完整读取会从每个区块的末尾向前翻页,因此每个区块都会重复读取相同的样本;请改为调用一次 heart_rate(..., full=True)。此库没有 92 天的限制:较长的时间范围会产生大量请求,因此请按需读取。

请求遇到 HTTP 401 被拒绝时,客户端会使用 passToken 续期一次会话,并重新发送请求。使用 mifit login 保存的会话的客户端,会将续期后的会话保存回 ~/.mifit。显式会话(MiFit(session))会在内存中续期;读取 client.session 即可保留续期后的会话。MiFit(auto_renew=False) 会关闭续期,因此遇到 401 时会直接引发 AuthError。

MiFit(http=...) 可注入 HTTP 客户端,以便离线使用客户端。启用续期时,续期操作也会使用该客户端,因此其 get 必须接受 cookies= 和 allow_redirects= 关键字,以及常见的 params=、headers= 和 timeout=(requests.Session 支持这些参数)。如果没有注入客户端,续期会使用其自身新建的 requests.Session。

两层:raw 和 records

  • raw 是服务解码后的载荷(每行内部的 value,或者对于消耗量而言,响应的 result 对象;对于 workout_detail,则是每次锻炼解码后的摘要和作为存储值的文件),使用 Xiaomi 自己的字段名称。它并非服务发送的每一行的完整转储。通用读取是例外:对于 raw_window、daily_report 和 mifit raw,raw 保存服务针对你的时间窗口发送的每一整行,包括其外层字段,并且已解码 value。
  • records 是使用中性名称(asleep_min、avg_hr_bpm 等)的类型化数据类。在进入这一层之前,字段的含义是根据应用显示的内容或应用自身的代码确定的。其中,REM 阶段仅依据应用代码确定,因为作者的设备未报告 REM。睡眠评分不在这一层:应用只显示了评级,因此无法核实该数值;它以 sleep_score 的形式保存在 raw 中。服务未报告的值为 None,绝不会凭空填成 0。
  • notes 是关于特定结果的注意事项:跳过的行、可能不完整的数据。

已知限制

  • 仅支持云端,且云端数据会有延迟。 mifit 读取的是应用已同步到小米云的数据。如果关闭了云同步或同步尚未运行,数据会缺失或延迟;最近一天的数据可能不完整。
  • 卡路里圆环的数据由应用合成。 应用会结合云端活动和手机端活动,因此最近一天的 consumption 可能低于圆环显示的数值。steps 中的 step_calories 是步数报告中的步行卡路里数值,并非圆环数值。
  • SDK 会修正原始数据窗口。 服务会丢弃原始数据窗口开头的样本(第一个数据桶);检查过的每个密集原始数据键都会这样处理:heart_rate、spo2、stress 和 calories(notes/08、notes/11、notes/domain-catalog.md)。heart_rate、spo2 和 weight 读取、raw_window 以及 mifit raw 会向服务请求开始时间前一天的数据,并只保留请求的时间窗口。raw(key, ...) 会原样返回服务对任意键的响应,不做此修正;如需完整窗口,请使用 raw_window。--full(仅限 heart_rate 的 full=True)则从结束日期向前分页;它会忽略开始日期,并在 10 页后停止,如果仍有更早的数据,会在 notes 中说明。
  • NaN 和 Infinity 会转换为 null。 JSON 不支持这类数值,因此服务发送此类数值时,CLI 会写入 null,并在说明中注明替换的数量。
  • 服务中断读取时会说明情况。 如果服务报告还有更多分页,却没有发送可用于获取这些分页的游标,读取会在此处停止,并在 notes 中说明该范围内可能缺少数据行。
  • 训练详情读取仅支持户外跑步和骑行。 目前支持已见过的文件格式(记录文件版本 8 和 5,GPS 文件版本 2)。步行、徒步、游泳、室内跑步和其他运动,以及其他文件版本,都会给出说明但不返回样本:每种格式都需要先有实际文件并手动编写布局表。海拔数据仅包含在 raw 中。单次调用最多读取 10 次训练的详情(较长的日期范围会按每周一块读取)。GPS 轨迹会显示你的位置,包括住址:--no-track(track=False)可将其排除。输出中不会出现下载链接和密钥。
  • 尚未测试中国区以外的训练文件。 文件仅会从 .fds.api.xiaomi.com 下的主机下载,这是中国区账号所见的主机。若链接指向其他主机,则会拒绝下载并给出说明;该训练仍会保留摘要,但不包含样本或轨迹。
  • 只读。 mifit 不会写入、更改或删除你账号中的任何内容。
  • 会话会过期。 收到 HTTP 401 时,mifit 会使用已存储的 passToken 更新会话一次,并重试请求。如果更新失败,或者会话是在更新功能出现前保存的(不含 passToken),请重新运行 mifit login。已于 2026-10-15 在线验证会话更新功能。我们仍在观察未更新会话的有效期;目前已确认至少为 22 小时:登录、401 和更新事件(仅记录事件、时间和持续时间)会写入 ~/.mifit/session-log.jsonl。(HTTP 401 也可能表示请求签名被拒绝;如果更新后立即收到 401,则会视为此情况,不再重试。)
  • 测试范围有限。 仅测试过一个中国区账号。其他地区虽已配置路由,但未经验证;小米也可能随时更改协议。
  • 请适度使用服务。 请求会逐个发送,每次间隔约一秒并加入随机抖动。较长的日期范围会分块读取,超过 92 天的范围需要使用 --long。这能降低被限流的风险,但无法完全避免。

开发

git clone https://github.com/Jenwein/mifit.git
cd mifit
pip install -e ".[test]"
python -m pytest -q

测试在离线环境中运行:使用伪造传输代替小米云,任何尝试建立网络连接的行为都会导致测试失败。test 附加项包含 tzdata,因此夏令时测试也能在没有自带时区数据库的 Windows 上运行。CI 使用 Python 3.12、3.13 和 3.14 在 Linux 上运行测试套件。

免责声明

mifit 是一个独立的非官方项目,与小米没有任何关联,也未获得小米认可或支持。“Xiaomi”和“Mi Fitness”是其所有者的商标,在此仅用于说明该软件适用的对象。

本软件旨在使用你自己的账户读取你自己的数据。自动访问可能违反小米的服务条款,并可能导致你的账户受到速率限制或限制;使用风险由你自行承担。本软件输出的任何内容都不构成医疗建议。本软件按“原样”提供,不作任何形式的保证(请参阅 LICENSE)。

致谢

请求签名和加密、传输、地区表及 STS 令牌交换功能改编自 kevinkwee 开发的 Mi-Fitness-Sync(MIT License)。请参阅 THIRD_PARTY_NOTICES。

许可证

MIT。请参阅 LICENSE。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。