Skip to main content
Glama
README.md
# 车来了 MCP

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.7-blue.svg)](https://www.typescriptlang.org/)
[![MCP](https://img.shields.io/badge/MCP-1.6+-green.svg)](https://modelcontextprotocol.io)
[![Upstream](https://img.shields.io/badge/fork_of-PeanutSplash%2Fchelaile--mcp-blue.svg)](https://github.com/PeanutSplash/chelaile-mcp)

一个基于 MCP 的车来了实时公交服务器,让大语言模型可以查询国内的公交、地铁实时数据 —— 包括线路时刻表、车辆实时位置、附近站点、关键词搜索、以及公交+地铁的换乘路线规划。

回答 "我的公交还有多久到站" 这类问题。**无需登录、无需任何账号配置**,开箱即用。

> **Fork 说明**
> 本仓库 fork 自 [PeanutSplash/chelaile-mcp](https://github.com/PeanutSplash/chelaile-mcp)(MIT),
> 修复了 `bus_get_timetable` 在服务日尾部查不到班次的问题,并新增 `at_time` 参数。
> 完整改动见 [CHANGELOG_FORK.md](CHANGELOG_FORK.md),差异概览见[下文](#发车预测说明本-fork-的改动)。
> 上游版本仍发布在 npm 上(`chelaile-mcp-server`),本 fork 未发布到 npm。

## 目录

- [特性](#特性)
- [安装](#安装)
- [工具一览](#工具一览)
- [发车预测说明(本 fork 的改动)](#发车预测说明本-fork-的改动)
- [配置](#配置)
- [开发](#开发)
- [贡献](#贡献)
- [致谢](#致谢)
- [免责声明](#免责声明)
- [License](#license)

## 特性

- **15 个工具**覆盖城市、线路、站点、实时车辆、时刻表、换乘规划全链路
- **两种输出格式**:每个工具都支持 `response_format: "markdown" | "json"`,JSON 模式同时附带 `structuredContent` 供程序化消费
- **坐标系统明确**:WGS-84 与 GCJ-02 在工具描述中逐个标注,避免坐标混用
- **发车预测含末班车**:回落到前两个服务日的实际班次,回答不再随查询时刻漂移
- **无需账号**:直接复用车来了的公开接口,无 API Key、无登录态
- **纯 TypeScript**:零运行时依赖(除 MCP SDK 与 zod),`tsc` 编译即可

## 安装

本 fork 未发布到 npm,直接从 GitHub 构建:

```bash
git clone https://github.com/misakacloud/chelaile-mcp.git
cd chelaile-mcp
bun install
npm run build
```

然后在 MCP 客户端里指向 `dist/index.js` 的绝对路径:

```json
{
  "mcpServers": {
    "chelaile": {
      "command": "node",
      "args": ["/absolute/path/to/chelaile-mcp/dist/index.js"]
    }
  }
}
```

Claude Code 等价写法:

```bash
claude mcp add chelaile -- node /absolute/path/to/chelaile-mcp/dist/index.js
```

也可以先 `npm link`,之后直接 `chelaile-mcp` 启动。

重启客户端后,工具会以 `chelaile` 为前缀出现。

### Docker

```bash
docker build -t chelaile-mcp .
docker run -i --rm chelaile-mcp
```

镜像以 stdio 方式运行,直接挂到 MCP 客户端上即可。

## 工具一览

| 工具                    | 用途                                    |
| ----------------------- | --------------------------------------- |
| `bus_list_cities`       | 列出支持的城市及其 ID                   |
| `bus_get_city_config`   | 某城市的刷新间隔限制与展示策略          |
| `bus_get_my_location`   | 基于调用方 IP 估算位置(精度到城市级)  |
| `bus_reverse_geocode`   | WGS-84 经纬度 → 中文地址                |
| `bus_search`            | 按关键词混合搜索:线路 + 站点 + POI     |
| `bus_search_more`       | 按某一分类分页"查看更多"                |
| `bus_get_nearby_stops`  | 某 GPS 坐标附近的站点及到站预计时间     |
| `bus_get_stop_detail`   | 某站点经过的所有线路 + 附近的地铁       |
| `bus_get_line_detail`   | 某线路的完整站点列表 + 当前车辆         |
| `bus_get_line_route`    | 某线路的地图轨迹坐标                    |
| `bus_get_line_realtime` | 某线路即将到达某站点的车辆实时信息      |
| `bus_list_line_buses`   | 某线路上所有车辆的位置与载客率          |
| `bus_get_timetable`     | 首站发车预测(含末班车,支持 `at_time` 查指定时刻后的下一班) |
| `bus_refresh_lines`     | 一次性批量刷新多个 (线路, 站点) 对      |
| `bus_plan_transit`      | 两个 GCJ-02 坐标之间的公交+地铁换乘规划 |

每个工具都支持 `response_format: "markdown" | "json"`(默认 `markdown`),返回 JSON 时同时附带 `structuredContent`。

### 典型用法

```
用户:71 路还有多久到站?
  → bus_search { city_id, keyword: "71路" }        # 拿到 lineId 与方向
  → bus_get_nearby_stops { lat, lng }               # 找到附近该线路的站点
  → bus_get_line_realtime { line_id, station_id }   # 该站点的实时车辆 ETA
```

```
用户:明天早上 7 点后下一班从始发站几点发?
  → bus_get_timetable { city_id, line_id, line_no, direction, at_time: "07:00" }
```

## 发车预测说明(本 fork 的改动)

`bus_get_timetable` 回答「始发站下一班几点发车」。

上游 `bus/line!preStartTimetableNew.action` 有两个坑,原版都没处理:

1. 传入 `tabType=0` 会让上游**只返回当天预测**,屏蔽另外两份完整数据。
2. 当天预测只覆盖**未来约 1.5–2 小时的滚动窗口**,服务日尾部(如 21:00 末班)永远不在其中。

实测同一线路不同时刻的窗口末端:

| 查询时刻 | 当天预测班数 | 当天预测末班 | 官方末班 |
| --- | --- | --- | --- |
| 16:02 | 31 | 17:35 | 21:00 |
| 18:10 | 35 | 20:00 | 21:00 |

窗口随当前时间前移,所以「19:10 之后下一班」在下午查不到、在傍晚查得到 —— 但**回答必须一致**,不能取决于查询时刻。

本 fork 的处理:

- **不再传 `tabType`** —— 上游因此额外返回 `yesterdayTimetable` /
  `beforeYesterdayTimetable`,即前两个服务日的**完整实际班次**(到 21:00)。
- 解析上游嵌套结构 `timetable.timetable[]`(原版用 `Array.isArray` 判断,恒为 `null`)。
- 新增 `at_time` 参数,按「指定时刻之后的第一班」计算 `nextDeparture`;
  当天预测够不到时自动回落到昨日实际班次,`nextDepartureSource` 标明数据来源。

### 示例

```jsonc
// bus_get_timetable { city_id: "<cityId>", line_id: "<lineId>", line_no: "<lineNo>",
//                     direction: "1", at_time: "19:10" }
{
  "nextDeparture": "20:00",
  "nextDepartureSource": "yesterday-actual",   // 或 today-prediction,见下表
  "lastPredictedDeparture": "20:00",
  "yesterdayDepartures": ["06:30", "...", "19:10", "20:00", "21:00"],
  "segments": [{ "fromTime": "06:30", "toTime": "10:00", "intervalMin": 18 }]
}
```

「19:10 之后第一班 = 20:00」这个结论稳定,只有来源标签随查询时刻变化
(当天预测是滚动窗口,覆盖到哪算到哪):

| 查询时刻 | 当天预测末班 | `nextDeparture` | `nextDepartureSource` |
| --- | --- | --- | --- |
| 16:02 | 17:35 | 20:00 | `yesterday-actual` |
| 18:10 | 20:00 | 20:00 | `today-prediction` |

`at_time` 超出所有数据覆盖范围时返回 `null` 而非猜一个值:

| `at_time` | `nextDeparture` |
| --- | --- |
| `05:00` | `06:30`(今日首班) |
| `20:30` | `21:00`(昨日末班) |
| `21:30` | `null`(已过末班) |

不传 `at_time` 时按当前北京时间取下一班。

## 配置

无需任何环境变量或配置文件。所有工具的参数都通过 MCP 调用传入。

唯一的外部依赖是上游接口的车来了签名参数,它们固化在 `src/constants.ts` 中。
上游接口并非公开 API,**可能随时变更或失效**;出现大面积 `status:400` 通常意味着上游调整了签名算法,需要同步上游仓库的最新实现。

## 开发

```bash
bun install
npm run build        # tsc 编译到 dist/
bun test tests/unit  # 单元测试(171 个,不联网)
npm run dev          # tsx watch 调试
```

端到端测试会真实请求上游接口,默认跳过:

```bash
CHELAILE_E2E=1 bun test tests/e2e   # 15 个用例,检测上游 schema 漂移
```

用 MCP Inspector 交互式调试:

```bash
npm run inspect
```

### 项目结构

```
src/
  index.ts          # MCP server 入口,注册全部工具
  constants.ts      # 上游签名、请求头、超时
  http-client.ts    # 签名、加密信封解密、gzip/br 解压、请求封装
  format.ts         # 输出格式化、截断、坐标取整、距离清洗
  types.ts          # 上游响应与精简响应的类型定义
  tools/
    city.ts         # 城市列表与城市配置
    geo.ts          # IP 定位、逆地理编码
    search.ts       # 关键词搜索、分类分页
    stops.ts        # 附近站点、站点详情
    lines.ts        # 线路详情、实时车辆、轨迹、时刻表、批量刷新
    transit.ts      # 公交+地铁换乘规划
tests/
  unit/             # 171 个单元测试,纯离线
  e2e/              # 15 个真实上游契约测试,需 CHELAILE_E2E=1
```

### 提交规范

项目使用 [Conventional Commits](https://www.conventionalcommits.org/),由 `release-it` + `conventional-changelog` 生成 changelog:

```
feat(timetable): add at_time argument
fix(timetable): fall back to yesterday's schedule
docs(readme): clarify coordinate systems
```

## 贡献

欢迎参与改进。

- **提交 PR** —— 请先阅读 [CONTRIBUTING.md](CONTRIBUTING.md),其中说明了开发环境、
  提交规范,以及「上游是唯一事实来源」「WGS-84 / GCJ-02 坐标约定」等必须遵守的约定。
- **报告 Bug** —— 请使用 [Bug 报告模板](.github/ISSUE_TEMPLATE/bug_report.yml)。
  提交前请把真实坐标、IP、住址替换成大致位置。
- **上游接口变更** —— 车来了接口不是公开 API,随时可能变动。
  若所有工具同时大面积返回 `status:400`,请用 [上游接口变更模板](.github/ISSUE_TEMPLATE/upstream_drift.yml) 反馈。
- **报告安全漏洞** —— 不要开公开 Issue,请按 [SECURITY.md](SECURITY.md) 的流程私下提交。

## 致谢

- 上游作者 [PeanutSplash](https://github.com/PeanutSplash/chelaile-mcp) —— 原始实现与全部工具,本 fork 基于其 `2ba26c3`
- [Model Context Protocol](https://modelcontextprotocol.io) —— MCP 协议与 TypeScript SDK
- [车来了](https://www.chelaile.net.cn) —— 数据来源

## 免责声明

- 本项目为**非官方**的第三方工具,与车来了官方无任何隶属关系。
- 数据全部来自车来了的公开接口,其准确性、时效性、可用性由上游负责,本项目不作任何保证。
- 本项目仅作技术研究与个人学习用途。**请遵守上游服务的使用条款,不要用于高频调用或商业用途。**
- 换乘规划结果仅供参考,出行请以现场实际运营为准。
- 使用者需自行确保其使用行为符合当地法律法规。

## License

[MIT](LICENSE) —— 版权归原作者所有,详见 [LICENSE](LICENSE)。