A MCP server, which serve as a BLE Heart Rate Monitoring to connect with a HRM device.
We recommend using a virtual environment and uv to manage dependencies.
This project uses pyproject.toml for dependency management.
# Create and activate a virtual environment
python3 -m venv .venv
source .venv/bin/activate # On Windows use `.venv\Scripts\activate`
# Install uv if not already installed
pip install uv
# Install dependencies
uv syncThis project supports configuration via a .env file. You can use the provided .env.sample as a template.
cp .env.sample .envThe following environment variables are used:
QINIU_ACCESS_KEY: Your Qiniu access key.QINIU_SECRET_KEY: Your Qiniu secret key.QINIU_BUCKET_NAME: The name of the Qiniu bucket.QINIU_BUCKET_DOMAIN: The domain associated with the Qiniu bucket.
uv run src/hrm/server.pyAfter installing from PyPI, you can run the server directly using:
uvx ble-hrm-serveruv run pytestuv run coverage run -m pytest
uv run coverage htmlBluetooth HRM is based on Bluetooth protocol, we should use bleak to discover the device and connect to it.
- Discover Bluetooth Device & Filter by HRM profile (Heart Rate Service 0x180D)
- resource:
discover://hrm
-
Tool: Monitoring Heart Rate
monitoring_heart_rate- Summary: Start monitoring the heart rate of the device for the given duration, default duration is 30 minutes (1800 sec). The monitoring will be done in the background.
- Inputs:
- device_id: str, the device UUID to monitor
- duration: int, the duration to monitor, default is 1800 seconds (30 minutes)
- Outputs: None
-
Tool: Get Heart Rate
get_heart_rate- Summary: Get the current HR, use last 10 sec and return the average of HR
- Inputs: None (assumes baseline reading)
- Output: Current HR (int), e.g.
{"avg_hr": 60}
-
Tool: Evaluate Active Heart Rate
evaluate_active_heart_rate- Summary: Evaluate the maximum heart of the last 60 seconds, the observed maximum during this exercise is recorded as the active heart rate.
- Inputs: None (assumes baseline reading)
- Outputs: Max HR: int, e.g.
{"max_hr": 100}
-
Tool: Get Heart Rate Bucket
get_heart_rate_bucket- Summary: Get the heart rate bucket of the last 10 seconds, the bucket size is 1 second.
- Inputs:
- since_from: float, the start time of the monitoring, default is 10 seconds ago
- bucket_size: float, the size of the bucket, default is 1 second
- Outputs: Heart Rate Bucket: list[dict], e.g.
[{"time": 1715904000, "value": 60}, {"time": 1715904001, "value": 61}]
-
Tool: Build Heart Rate Chart
build_heart_rate_chart- Summary: Build the heart rate chart of the last 600 seconds, the bucket is a dynamic size, the default size is 1 second. The max bucket count is 60, if the bucket count is more than 60, the bucket size will be increased to
duration / 60seconds. - Inputs:
- since_from: float, the start time of the monitoring, default is 600 seconds ago
- Outputs: Heart Rate Chart PNG URL: str, e.g.
https://example.com/chart.png
- Summary: Build the heart rate chart of the last 600 seconds, the bucket is a dynamic size, the default size is 1 second. The max bucket count is 60, if the bucket count is more than 60, the bucket size will be increased to
Here is the MCP settings for the server:
{
"mcpServers": {
"blue-hrm": {
"command": "uvx",
"args": [
"ble-hrm-server"
]
}
}
}Here is a sample prompt for the LLM tested by DeepSeek V3.1:
你是一位健康专家,通过心率来检测测试者的心脏健康程度,现在测试者携带了蓝牙的心率带,
以下为检测步骤
- 你通过发现来获取的设备ID,通过rssi获取最接近的设备,再以此设备开始监控。
- 获取当前测试者的静息心率。
- 当测试结束后,提示测试者开始中高强度运动,比如波比跳,当测试者完成运动后,回复后,立刻测试其最高运动心率。
- 之后提示测试者计时休息2分钟,得到确认后,再次获取当前心率,并与最高心率的差额作为恢复心率。
最后通过恢复心率的值和以下列表评估测试者的心脏健康程度,并用健康专家的口吻给测试者一些关于心脏健康方面的建议。
------
- <22: 身体年龄略大于 真实年龄
- 22~52: 身体年龄= 真实年龄
- 53~58: 身体年龄略小于真实年龄
- 59~65: 身体年龄比真实年龄小
- >=66: 身体年龄比真实年龄小很多
| Name | Stmts | Miss | Cover |
|---|---|---|---|
| src/hrm/__init__.py | 0 | 0 | 100% |
| src/hrm/bt_client.py | 139 | 5 | 96% |
| src/hrm/ts_db.py | 41 | 1 | 98% |
| TOTAL | 180 | 6 | 97% |
Special thanks for Qiniu Cloud for providing the storage service. Qiniu also provides LLM inference services.