Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

37 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Incremental MCP Server

基于 FastAPI + FastMCP 的 MCP 服务,为 i.incremental.icu 提供可通过 ChatGPT Desktop 调用的数据库查询能力。

架构

ChatGPT Desktop ──(stdio bridge)──▶ https://i.incremental.icu/mcp
                                        │
                          FastAPI (JWT 中间件)
                                        │
                          FastMCP (streamable-http)
                                        │
                          SQLAlchemy 2.0 + asyncpg
                                        │
                                 PostgreSQL
  • 鉴权:复用 i.incremental.icu 的 JWT(SECRET_KEY + HS256),在 FastAPI ASGI 中间件层统一校验
  • 传输协议streamable-http(MCP 2025-11-25 规范推荐)
  • ORM:SQLAlchemy 2.0 异步 + asyncpg

项目结构

app/
├── main.py              # FastAPI 入口,JWT 中间件,/mcp 挂载
├── mcp_server.py        # FastMCP 定义,tools 注册
├── config.py            # pydantic-settings,从 .env 读取配置
├── auth.py              # JWT 解码与用户 ID 提取
├── db.py                # async SQLAlchemy engine + session
├── models/
│   ├── user.py              # t_users / t_user_refresh_tokens ORM 模型
│   ├── main_activity.py     # t_main_activity ORM 模型(跑步数据)
│   ├── heart_rate_daily.py  # t_heart_rate_daily ORM 模型(每日心率汇总)
│   └── heart_rate_detail.py # t_heart_rate_detail ORM 模型(心率采样明细)
└── tools/
    ├── data_tools.py        # MCP tool 实现(用户信息)
    ├── activity_tools.py    # MCP tool 实现(跑步数据)
    ├── heart_rate_tools.py  # MCP tool 实现(心率数据)
    └── hello_tools.py       # 示例 tool

环境变量

复制 .env.example.env 并填写实际值:

变量 说明
DATABASE_URL PostgreSQL 连接串(asyncpg 驱动)
SECRET_KEY JWT 签名密钥,需与主站一致
JWT_ALGORITHM JWT 算法,默认 HS256

GitHub Secrets 配置

自动化部署通过 .github/workflows/deploy.yml 执行,需要在仓库的 Settings → Secrets and variables → Actions 中配置以下 secrets:

Secret 用途 是否必填
REMOTE_HOST 部署目标服务器的 IP 或域名
REMOTE_USER SSH 登录用户名
SECRET_KEY SSH 私钥;部署时也会写入 .env 作为 JWT 签名密钥
DATABASE_URL PostgreSQL 连接串,部署时写入 .env
SUPABASE_STORAGE_BUCKET Supabase 存储桶名称 否(仅当使用对象存储)
SUPABASE_STORAGE_ENDPOINT Supabase S3 兼容端点 否(仅当使用对象存储)
SUPABASE_STORAGE_REGION Supabase 存储区域 否(仅当使用对象存储)
SUPABASE_ACCESS_KEY_ID Supabase 访问密钥 ID 否(仅当使用对象存储)
SUPABASE_SECRET_ACCESS_KEY Supabase 私有访问密钥 否(仅当使用对象存储)

注意:当前 deploy.yml 使用同一个 SECRET_KEY 既作为 SSH 私钥,又作为应用 JWT 密钥。建议将两者分离,例如 SSH 私钥使用 SSH_PRIVATE_KEY,应用密钥保留 SECRET_KEY

MCP Tools

Tool 描述
get_latest_run 获取当前用户最近一次跑步记录
get_run_history 分页查询当前用户的历史跑步记录
get_heart_rate_history 获取当前用户最近 N 天的每日心率汇总
get_daily_heart_rate 获取当前用户指定日期的当天心率汇总与明细
query_user_profile 获取当前登录用户的基本信息(用户名、邮箱、会员状态等)

跑步数据工具

跑步数据直接查询 t_main_activity 表(与主站 blunt-serv 共享同一 PostgreSQL),不经过 FastAPI 转发。

get_latest_run

无参数,返回最近一条跑步记录的完整数据(距离、时长、心率、配速、爬升等):

{
  "status": "success",
  "data": {
    "id": 123,
    "activity_name": "晨跑",
    "sport_type_raw": "running",
    "start_time_local": "2026-08-05T07:30:00",
    "distance_meters": 5210.0,
    "duration_seconds": 2100.0,
    "average_hr": 152,
    "max_hr": 178,
    "...": "..."
  }
}

get_run_history

分页查询历史跑步记录,按本地开始时间倒序:

参数 类型 默认值 说明
limit int 10 每页条数
offset int 0 跳过的条数
start_date str 起始日期过滤(本地时间),格式 YYYY-MM-DD
end_date str 结束日期过滤(本地时间),格式 YYYY-MM-DD

返回 {"status": "success", "data": [...], "total": N}

跑步类型过滤与后端一致:running / treadmill_running / trail_running / track_running / indoor_running 及 key 100-103

心率数据工具

心率数据直接查询 t_heart_rate_dailyt_heart_rate_detail 表(与主站 blunt-serv 共享同一 PostgreSQL)。

get_heart_rate_history

获取当前用户最近 N 天或指定日期范围的每日心率汇总,按日期倒序:

参数 类型 默认值 说明
days int 7 仅当未指定 start_date/end_date 时生效,返回最近多少天的记录,范围 1-31
start_date str 起始日期(含),格式 YYYY-MM-DD
end_date str 结束日期(含),格式 YYYY-MM-DD,缺省为当前用户时区的今天

指定日期范围时最多返回一个月(31 天)的数据,超出会截断到最近的 31 天。

{
  "status": "success",
  "data": [
    {
      "date": "2026-08-05",
      "max_heart_rate": 80,
      "min_heart_rate": 43,
      "resting_heart_rate": 47,
      "last_seven_days_avg_resting_heart_rate": 45
    },
    {
      "date": "2026-08-04",
      "max_heart_rate": 132,
      "min_heart_rate": 42,
      "resting_heart_rate": 46,
      "last_seven_days_avg_resting_heart_rate": 45
    }
  ]
}

get_daily_heart_rate

获取当前用户指定日期的当天心率数据,包含每日汇总(daily)和心率明细(details)。明细按用户时区(t_users.timezone)计算当天的 UTC 起止范围,采样时间升序返回:

参数 类型 默认值 说明
date str 无(默认用户时区的今天) 日期,格式 YYYY-MM-DD
{
  "status": "success",
  "data": {
    "daily": {
      "id": 28,
      "user_id": 1,
      "date": "2026-08-05",
      "max_heart_rate": 80,
      "min_heart_rate": 43,
      "resting_heart_rate": 47,
      "last_seven_days_avg_resting_heart_rate": 45,
      "created_at": "2026-08-05T00:10:52.344570+00:00",
      "updated_at": "2026-08-05T04:16:44.992412+00:00"
    },
    "details": [
      { "sample_time": "2026-08-04T16:00:00+00:00", "heart_rate": 52 },
      { "sample_time": "2026-08-04T16:02:00+00:00", "heart_rate": 53 }
    ]
  }
}

本地开发

# 创建虚拟环境并安装依赖
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"

# 复制环境变量
cp .env.example .env
# 编辑 .env 填写真实数据库连接和 SECRET_KEY

# 启动服务
uvicorn app.main:app --host 0.0.0.0 --port 8001 --reload

# 查看运行日志
journalctl -u incremental-mcp -n 50

# 运行冒烟测试
python test_smoke.py

MCP 端点:http://localhost:8001/mcp

鉴权方式

所有 /mcp 下的请求需携带:

Authorization: Bearer <JWT>

JWT 需与主站 i.incremental.icu 使用相同的 SECRET_KEYHS256 算法签发,payload 中需包含用户标识字段(sub / user_id / id)。

ChatGPT Desktop 集成

ChatGPT 桌面端通过 mcp-streamable-http-shim 桥接到远程 HTTP MCP 服务。

claude_desktop_config.json / ChatGPT MCP 配置示例:

{
  "mcpServers": {
    "incremental-mcp": {
      "command": "npx",
      "args": [
        "-y", "@tollbit/mcp-streamable-http-shim",
        "--url", "https://i.incremental.icu/mcp",
        "-H", "Authorization: Bearer <YOUR_JWT>"
      ]
    }
  }
}

部署

Docker

docker build -t incremental-mcp .
docker run -d --env-file .env -p 8001:8001 incremental-mcp

反向代理

在 nginx / Caddy 中配置反向代理,将 /mcp 路径指向服务端口 8001:

location /mcp {
    proxy_pass http://127.0.0.1:8001/mcp;
    proxy_http_version 1.1;
    proxy_read_timeout 300s;       # MCP 长连接需要
    proxy_buffering off;           # SSE 流式传输
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

添加新 Tool

app/tools/data_tools.py 中编写异步函数,然后在 app/mcp_server.py 中注册:

# data_tools.py
async def my_new_tool(param: str) -> dict:
    """Tool 的描述(AI 会根据描述决定是否调用)。"""
    ...

# mcp_server.py
mcp.tool()(data_tools.my_new_tool)

About

让 chatgpt 分析你的身体数据

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages