mcpField
T
tfl-ts

tfl-ts

本地服务
github.com/ghcpuman902地图与出行22 天前更新

关于 tfl-ts

tfl-ts 是用于只读查询 Transport for London 数据的类型化 TypeScript 客户端,提供 REST 封装和离线车站拓扑数据。它还包含供 Agent 查询交通信息的本地 MCP 服务。

主要功能

  1. 01类型化 TfL API 客户端
  2. 02封装 84 个 REST 端点
  3. 03离线车站拓扑数据
  4. 04线路状态与到站查询
  5. 05行程规划

适用场景

  1. 01查看当前地铁服务状态
  2. 02查询车站到站信息
  3. 03规划 TfL 行程

MCP 相关

运行 `npx tfl-ts mcp` 可启动只读本地 MCP 服务,提供线路状态、到站、行程规划、ID 解析和内置文档等工具。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"ghcpuman902-tfl-ts": {
"command": "npx",
"args": [
"-y",
"tfl-ts"
],
"env": {
"TFL_APP_KEY": "<TFL_APP_KEY>",
"TFL_MCP_MIN_INTERVAL_MS": "<TFL_MCP_MIN_INTERVAL_MS>"
}
}
}
}
启动命令npx -y tfl-ts
需要配置的环境变量
TFL_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+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
npmtfl-ts2.15.2

工具

7

收录的工具列表 · 更新于 2 周前

此工具无需参数。

详细介绍

TfL API TypeScript 客户端

npm version License: MIT TypeScript Node.js CI

类型化的 TfL 客户端:易用的封装、84 个原始 REST 端点,以及涵盖 20 条铁路线路的离线车站拓扑数据。

tfl CLI 查询 central 和 victoria,然后列出行程端点 tfl.manglekuo.com 上的看板,显示线路状态和实时到站信息

基于 tfl-ts 构建 → Board

对比

tfl-tstfl-unified-apitfl-journey-plannertfl-mcpmcp-tfl
TypeScript 类型有无无仅源码有(src/index.ts)
零依赖是否否否否
原始端点覆盖数8484 个中的 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.mdAgent 快速入门:静态数据与实时数据
docs/agent.md模块参考、缓存、Next.js 模式
.claude/skills/tfl-ts/SKILL.md用法模式与注意事项
docs/mcp.md本地 MCP 服务(包含离线 docs 工具)
CHANGELOG.md发行说明
docs/MIGRATION-v2.mdv1 → 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 无关联。

npmtfl-ts
GitHubghcpuman902/tfl-ts
Issues报告错误
在线演示tfl.manglekuo.com

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。