关于 tfl-ts
tfl-ts 是用于只读查询 Transport for London 数据的类型化 TypeScript 客户端,提供 REST 封装和离线车站拓扑数据。它还包含供 Agent 查询交通信息的本地 MCP 服务。
主要功能
- 01类型化 TfL API 客户端
- 02封装 84 个 REST 端点
- 03离线车站拓扑数据
- 04线路状态与到站查询
- 05行程规划
适用场景
- 01查看当前地铁服务状态
- 02查询车站到站信息
- 03规划 TfL 行程
MCP 相关
运行 `npx tfl-ts mcp` 可启动只读本地 MCP 服务,提供线路状态、到站、行程规划、ID 解析和内置文档等工具。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
npx -y tfl-tsTFL_APP_KEY密钥TfL API primary key. Live tools need it. Static tools (docs, resolve_line_id, get_supported_modes) do not.TFL_MCP_MIN_INTERVAL_MSMinimum milliseconds between outbound TfL requests. Default 250. Cache hits do not count.本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
工具
7收录的工具列表 · 更新于 2 周前
此工具无需参数。
详细介绍
TfL API TypeScript 客户端
类型化的 TfL 客户端:易用的封装、84 个原始 REST 端点,以及涵盖 20 条铁路线路的离线车站拓扑数据。
基于 tfl-ts 构建 → Board
对比
| tfl-ts | tfl-unified-api | tfl-journey-planner | tfl-mcp | mcp-tfl | |
|---|---|---|---|---|---|
| TypeScript 类型 | 有 | 无 | 无 | 仅源码 | 有(src/index.ts) |
| 零依赖 | 是 | 否 | 否 | 否 | 否 |
| 原始端点覆盖数 | 84 | 84 个中的 10 个 | 84 个中的 1 个 | 84 个中的 59 个 | 84 个中的 8 个 |
| 离线车站数据 | 有 | 无 | 无 | 无 | 无 |
| MCP 支持 | 有 | 无 | 无 | 有 | 有 |
计数依据已提交的 OpenAPI 快照中的 84 个操作(src/generated/endpoints.ts 中的 ENDPOINT_COUNT,src/generated/openapi/spec.meta.json 中的 pathCount)。数据已与各仓库的源码,以及 2026 年 10 月 16 日发布到 npm 的 package.json 核对。
tfl-ts 会发布 .d.ts 文件。运行时依赖:无。typescript 是 peer 依赖,供基于这些类型进行编译的用户使用。车站顺序和换乘枢纽随 tfl-ts 一同发布在 tfl-ts/meta 中,无需网络请求。tfl mcp 是本地 MCP 服务。
tfl-unified-api 2.0.2 使用 JavaScript 编写,且没有 types 字段。它依赖 moment、request 和 request-promise-native。Line 模块会调用 84 个路径中的 10 个:meta modes、severity、disruption categories、service types、route sequence、route by id、lines by mode、status by id、status over a date range,以及 status by mode。getRoutesByServiceType 会构造 /Line/Route/{types},该路径不属于这 84 个路径。db/lineColors.json 存储的是线路颜色,而不是车站。
tfl-journey-planner 1.1.2 使用 LiveScript 编写并编译为 JavaScript,且没有 types 字段。它依赖 request 和 querystring。该模块仅调用 GET /Journey/JourneyResults/{from}/to/{to}。
@daanrongen/tfl-mcp 1.4.3 使用 TypeScript 编写。发布的 tarball 包含 dist/main.js、package.json 和 README.md,且 package.json 没有 types 字段。它依赖 @modelcontextprotocol/sdk、effect 和 zod。其中的工具会调用 84 个路径中的 59 个。此外还会调用 /Road/all/Disruption(快照中的路径为 /Road/all/Disruption/{disruptionIds})、/Vehicle/EmissionSurcharge 和 /Vehicle/UlezCompliance。
@pipeworx/mcp-tfl 0.1.2 设置了 "types": "src/index.ts",且该文件包含在 tarball 中。它依赖 @modelcontextprotocol/sdk 和 tsx。其中 6 个工具会调用 84 个路径中的 8 个。
车站顺序随 tfl-ts 一同发布在 tfl-ts/meta 中。
安装并进行首次调用
从 TfL API Portal 获取一个 Primary key。订阅“500 Requests per min”,然后选择 Profile → Show。
pnpm add tfl-ts
export TFL_APP_KEY=your-primary-key
pnpm exec tfl raw line.statusByIds --ids victoria
[
{
"id": "victoria",
"name": "Victoria",
"lineStatuses": [{ "statusSeverityDescription": "Good Service" }]
}
]
线路名称、车站顺序和颜色随软件包一起提供。状态、到站信息和行程会在运行时向 TfL 发起请求。
在门户中你会看到两个密钥(Primary 和 Secondary);任意一个都可以使用。自 2021 年 1 月起,app_id 就已不再使用。
改为在构造函数中传递凭据
const client = new TflClient({
appKey: 'your-primary-key',
});
快速开始
Node
import TflClient from 'tfl-ts';
const client = new TflClient(); // reads TFL_APP_KEY from process.env
const status = await client.line.getStatus({ modes: ['tube'] });
const arrivals = await client.stopPoint.getArrivals({
stopPointIds: ['940GZZLUOXC'], // Oxford Circus
});
const journey = await client.journey.plan({
from: '940GZZLUOXC',
to: '940GZZLUBND',
});
const { matches } = await client.stopPoint.search({
query: 'Oxford Circus',
modes: ['tube'],
});
const busStops = await client.stopPoint.searchBusStops('Trafalgar Sq');
Next.js
使用 ISR(约 60 秒)的服务端组件。看板和浏览器:tfl-components · tfl.manglekuo.com/docs/explorer。地铁看板使用官方线路颜色;公交看板使用线路编号标签。不要混用。请参阅 examples/README.md。
import TflClient, { sortLinesBySeverityAndOrder } from 'tfl-ts';
export const revalidate = 60;
const client = new TflClient();
const tube = await client.line.getStatus({ modes: ['tube'] });
const sorted = sortLinesBySeverityAndOrder(tube);
pnpm dlx shadcn@latest add https://tfl.manglekuo.com/r/tube-status-board.json
pnpm dlx shadcn@latest add https://tfl.manglekuo.com/r/tfl-roundel.json
除非设置 NEXT_PUBLIC_ALLOW_TFL_ROUNDEL=true,否则圆标会显示占位图(启用即表示你承担商标责任)。
Agent / MCP
只读 MCP(npx tfl-ts mcp),带缓存并限制请求速率。静态工具不会调用 TfL。npm 可执行文件是 tfl(dist/cjs/bin/tfl.js);mcp 是 stdio 子命令。设置方法:docs/mcp.md。
{
"mcpServers": {
"tfl-ts": {
"command": "npx",
"args": ["-y", "tfl-ts@latest", "mcp"],
"env": {
"TFL_APP_KEY": "your-primary-key"
}
}
}
}
工具:get_supported_modes、resolve_line_id、docs、resolve_stop_id、get_line_status、get_arrivals、plan_journey。
无法访问仓库,或丢失了上下文?npx tfl-ts docs ls 会列出所有随包提供的 Agent 文档(此 README、CLAUDE.md、docs/agent.md、docs/mcp.md,等等)。docs cat <id> 会打印其中一份文档,docs find <query> / docs grep <pattern> 会搜索全部文档——无需联网,也不需要 API 密钥。本地 MCP 的 docs 工具读取的是同一目录。
注意事项
- 线路 ID 是小写 slug:
'central'、'victoria'、'elizabeth'。不能使用'Central'这类显示名称。 - 站点 ID 格式类似
'940GZZLUOXC'。使用stopPoint.search()或place.search()解析。 - 搜索公交站时可使用 5 位 NaPTAN 格式代码(例如
'51800')。 - 进行实时调用前,优先使用静态常量(
LINE_NAMES、STATION_SEQUENCES、模式列表)。 - 状态缓存约 30 至 60 秒。每个站点查询到站信息的间隔不要短于约 10 至 15 秒。
accidentStats和airQuality已弃用。- TfL 不提供 National Rail 的实时到站信息:
STATION_HUBS会追踪 Southeastern、South Western Railway 等运营商的拓扑信息,但对这些运营商调用getArrivals()会返回空数组,而不是报错。 - 地铁看板使用官方线路颜色;公交看板使用线路编号标签。不要混用这两种样式。
- 圆标商标:除非使用方选择启用,否则显示占位图。
- 一条线路可能包含多个
lineStatuses。不要读取[0]。应使用getWorstCurrentStatus(line.lineStatuses)获取当前生效的状态行。 validityPeriods[].isNow根据disruption.category === 'RealTime'设置,不会检查当前时间。计划内的工程施工即使今天正在进行,isNow也可能为false。validityPeriods[].toDate表示时间窗口结束时间,而不是列车恢复运行的时间。周末工程施工通常在00:29Z(伦敦时间 01:29,即运营日结束时)结束。同一行中跨越午夜拆分的时段属于同一次施工封锁;不要把第一个重叠的toDate当作下一班列车的时间。- 严重性 20 表示计划内停运(Waterloo & City 周末停运,于运营日结束时结束),而非非计划停运 Closed (1)。
sortLinesBySeverityAndOrder会将其排在事故之后。 - Circle / H&C / Met 线路的
lineId会在共用轨道区段发生变化。使用withSharedTrackIdentity。不要改写原始lineId。
前后对比
getDetailedStatus() 返回较短的类型和经过重命名的字段(severity、from、closureType、originName),而不是 TfL 生成的类型结构:
const lines = await client.line.getDetailedStatus({
lineIds: ['bakerloo'],
dateRange: { startDate: '2026-08-08', endDate: '2026-08-10' },
});
const s = lines[0]?.statuses?.[0];
s?.severity;
s?.severityDescription;
s?.validityPeriods?.[0]?.from;
s?.disruption?.closureType;
如需使用 TfL 的精确字段名称,请使用 getStatus({ detail: true }) 或 client.raw.line.*。
车站序列
Tube、Elizabeth line、DLR、Overground、Tram 和河上巴士码头的拓扑结构。无需凭据,也无需网络。仅包含标识、顺序和分支(不包含状态或到站信息)。河上码头不在 STATION_HUBS 中——请轮询 NaptanFerryPort id。实时拓扑:client.line.getRouteSequence()。客户端上还提供:client.line.STATION_SEQUENCES。
import { LINE_STATION_SEQUENCES } from 'tfl-ts/meta';
const bakerloo = LINE_STATION_SEQUENCES.bakerloo;
const outbound = bakerloo.orderedRoutes.find(
(route) => route.direction === 'outbound' && route.serviceType === 'Regular',
);
console.log(outbound?.stationIds, bakerloo.branches);
车站枢纽与标准化到站信息
STATION_HUBS 将每个实体车站映射到其同级 StopPoint id,以及各线路对应的到站信息 id——Liverpool Street 的 Tube id(940GZZLULVT)和铁路 id(910GLIVST)都会映射到同一个 HUBLST 条目,Central 使用 Tube id,Elizabeth line 使用铁路 id。无需凭据,也无需网络。
import { STATION_HUBS, resolveArrivalsStopId } from 'tfl-ts/meta';
const hub = STATION_HUBS['940GZZLULVT']; // any sibling id works
const elizabethStopId = hub && resolveArrivalsStopId(hub, 'elizabeth'); // '910GLIVST'
如果枢纽不承载该线路,resolveArrivalsStopId 会返回 undefined,而不是换乘站 id——轮询换乘站 id 本身不会从 TfL 返回任何到站信息。第三方 National Rail 运营商(Southeastern、South Western Railway、c2c 等)会出现在枢纽拓扑中,但不会返回实时预测:TfL 的 Arrivals API 仅涵盖 Tube、DLR、Tram、Overground 和 Elizabeth line。
client.stopPoint.getNormalizedArrivals() 在 getArrivals() 的基础上,还会提供经过清理的 destination(当 towards 为空或字面值为 "null" 时,回退为 destinationName;这在 Elizabeth line、Overground 和部分公交终点站很常见)和 platform(指南针方位、经过清理的标签,以及 TfL 返回字面值 "Platform Unknown" 时的 isUnknown):
const arrivals = await client.stopPoint.getNormalizedArrivals({
stopPointIds: ['940GZZLUOXC'],
});
arrivals[0]?.destination.name;
arrivals[0]?.platform.label;
getArrivals() 和 client.raw.* 保持不变。
client.stopPoint.get 和 getByGeoPoint 会将 Direction 附加属性提升到站点上:towards、compassPoint 和 compassBearingDegrees。smsCode 从一等字段或属性包 SmsCode 中填充。设施键仍以 TfL 字符串的形式保留在 additionalProperties 中。parseAdditionalPropertyValue 会将属性包的 value 转换为 null / 布尔值 / 数字 / 日期 / 文本("yes"、"null"、Unix 毫秒值 InstallDate)。client.raw.stopPoint.* 不会提升属性。不要将 Prediction 的 bearing(车辆朝向)用作站点标记;标有 W 的站点标记表示 Stop W,而非西方。
const [stop] = await client.stopPoint.get({ stopPointIds: ['490013766E'] });
stop.towards; // 'Aldwych'
stop.compassPoint; // 'E'
stop.compassBearingDegrees; // 90
在 Circle / Hammersmith & City / Metropolitan 共轨路段上,TfL 按车站而非列车分配 lineId。同一个 vehicleId 在 Victoria 可能是 circle,在 Liverpool Street 则可能是 hammersmith-city。withSharedTrackIdentity(stopRows, lineIds, networkArrivals) 会添加 sharedTrackIdentity(根据独占区段命中确定的 canonicalLineId,或 ambiguous + rawLineIds)。它不会改写原始 lineId。调用 line.getArrivals({ lineIds }) 且不传 stopPointId,即可进行证据所需的全网轮询。
颜色、严重程度和详细类型
import type {
DetailedLine,
DetailedLineStatus,
DetailedDisruption,
AffectedRoute,
AffectedStop,
} from 'tfl-ts';
import {
getLineColor,
getLineInlineStyles,
getLineCssProps,
getLineDarkReadableStyles,
sortLinesBySeverityAndOrder,
getLineStatusSummary,
getWorstCurrentStatus,
getStatusKind,
} from 'tfl-ts/ui';
const { hex } = getLineColor('central'); // #E32017
const styles = getLineInlineStyles('central');
const cssProps = getLineCssProps('northern');
const tube = await client.line.getStatus({ modes: ['tube'] });
const sorted = sortLinesBySeverityAndOrder(tube);
const worst = getWorstCurrentStatus(sorted[0]?.lineStatuses);
getStatusKind(worst ?? 10);
Northern 默认采用深色对比的 outline 样式。传入 { darkContrastMode: 'white' } 可使用白色填充和文字。参见 CHANGELOG.md(2.4.0)。原始接口备用方案:client.raw.line.statusByIds({ ids: ['central'] })。
实时数据
使用与 REST 相同的 app_key 轮询到站信息。不使用 SignalR。详情参见:docs/REALTIME.md。
const stop = client.realtime.pollArrivals(
{
stopPointIds: ['940GZZLUOXC'],
sortBy: 'timeToStation',
intervalMs: 30_000,
},
(arrivals, meta) => console.log(`[tick ${meta.tick}]`, arrivals.length),
(error, meta) => console.error(meta.tick, error),
);
stop();
模块索引
| 模块 | 常见用途 |
|---|---|
client.line | 状态、详细状态、服务中断、路线、按线路查询到站信息、静态 LINE_NAMES / STATION_SEQUENCES |
client.stopPoint | 搜索、到站信息、标准化到站信息、站点元数据、静态 STATION_HUBS |
client.journey | 行程规划 |
client.mode | 交通方式列表及按交通方式查询到站信息 |
client.search / place / road / vehicle / occupancy / bikePoint / cabwise / travelTimes | 辅助 API |
client.raw | 全部 84 个 REST 端点(pnpm exec tfl list 会输出 JSON;--text 是旧版的逐行格式) |
client.realtime | 通过 REST 到站信息进行即时拉取轮询 |
零运行时依赖(适用于 Node、浏览器和边缘环境,支持 fetch)。
深入文档
| 资源 | 用途 |
|---|---|
| CLAUDE.md / AGENTS.md | Agent 快速入门:静态数据与实时数据 |
| docs/agent.md | 模块参考、缓存、Next.js 模式 |
| .claude/skills/tfl-ts/SKILL.md | 用法模式与注意事项 |
| docs/mcp.md | 本地 MCP 服务(包含离线 docs 工具) |
| CHANGELOG.md | 发行说明 |
| docs/MIGRATION-v2.md | v1 → v2 迁移 |
| examples/ | 库 → UI 映射(地铁 + 巴士) |
| docs/design/agent-friendly-cli.md | 为什么存在 tfl docs 和 MCP docs |
| docs/design/evals.md | 为什么 pnpm run eval 是速率限制,而不是 Jest 门禁 |
以上内容都会随 npm 包一同发布,并可通过 npx tfl-ts docs ls|cat|find|grep 或 MCP docs 工具离线阅读——无需克隆仓库。
playground/demo/ 下的 Playground 演示面向克隆仓库的用户,而非软件包使用者。
架构
OpenAPI snapshot (committed)
→ types.ts (swagger-typescript-api, types only)
→ rawClient.ts (owned generator, uniform object-param API)
→ client.raw.* (public escape hatch)
→ wrappers (line, stopPoint, …)
pnpm run build 会将 TypeScript 编译为 CJS 和 ESM。无需联网,也无需重新生成 OpenAPI。
参与贡献
git clone https://github.com/ghcpuman902/tfl-ts.git
cd tfl-ts
pnpm install
pnpm run build
pnpm run test
实用脚本:pnpm run generate、pnpm run check、pnpm run check -- --only=drift、pnpm exec tfl list。请参阅 LLM_context.md 和 .cursor/skills/tfl-ts-maintainer/SKILL.md。
许可证
MIT。与 Transport for London 无关联。
| npm | tfl-ts |
| GitHub | ghcpuman902/tfl-ts |
| Issues | 报告错误 |
| 在线演示 | tfl.manglekuo.com |
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。