关于 mifit
mifit 是用于从 Xiaomi 云端读取个人 Mi Fitness 数据的 Python 库、命令行工具和 MCP 服务,提供结构化健康与运动记录。MCP 服务默认不提供 GPS 轨迹。
主要功能
- 01读取步数、睡眠和心率数据
- 02获取运动记录及每日健康摘要
- 03按日期范围导出 JSON 数据
- 04读取未经解析的服务数据
- 05扫描二维码登录
适用场景
- 01让助手总结近期睡眠情况
- 02查看步数或心率趋势
- 03导出指定日期范围的运动记录
- 04检查其他健身数据字段
MCP 相关
本地 MCP 服务提供读取 Mi Fitness 记录以及启动或检查二维码登录的工具。只有使用 `--allow-gps` 明确启用后,才会返回运动 GPS 轨迹。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
16收录的工具列表 · 更新于 2 周前
此工具无需参数。
详细介绍
mifit
从 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。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。