
Traintrackr
live.traintrackr由 traintrackr.live 發布官方發布者v0.1.0更新於 Oct 24, 2026
GB (National Rail) train times: live departures, platforms, timetables and journey planning.
概覽
面向英國國家鐵路的遠端 MCP 伺服器:即時出發與抵達、月台、時刻表,以及 A 到 B 的行程規劃。
- 可以做什麼
- 依站名或 CRS 代碼查詢車站,名稱重複時回傳候選站
- 查看即時出發與抵達、預計時間、月台、誤點與取消,或繪製出發資訊板
- 查詢某一班次的月台,或列出某個月台接下來要開的列車
- 讀取任一日的表定出發與抵達,並查看某班車的所有停靠站
- 規劃兩站之間的轉乘行程,包含跨倫敦的連結
- 回報資料來源狀態並回傳隱私政策文字
- 適用情境
- 適合讓助理回答日常英國鐵路問題:某站的下一班車、某班車是否準點、從哪個月台發車,或某天如何從一地前往另一地。
- 執行需求
- 遠端端點 traintrackr.live/mcp,在 Claude 或 ChatGPT 中作為連接器加入。登入採 OAuth,以電子郵件地址加一次性連結或六位數驗證碼,沒有密碼。ChatGPT 需開啟開發人員模式。自行架設則需 uv、Python 3.14、Postgres,以及免費的 Rail Data Marketplace 與 Network Rail Open Data 帳號。
安裝
在 SourceWeft 中
- 開啟 儀表板中的 Traintrackr,將其新增到工作區。
- 連接 所需的 憑證。它們會被私密儲存,絕不會在公開頁面上展示。
- 為需要使用其工具的對話啟用該服務。
Web executable,透過 Streamable HTTP。 遠端服務在工作區中設定後即可從網頁執行環境執行。
交給代理程式安裝
把這段提示詞貼上到 Claude Code、Codex、Cursor 等代理程式,或 SourceWeft 對話中。代理程式會讀取該伺服器的安裝指南,先向你展示發布者、端點和登入方式,經你同意後再把它新增到你的客戶端。金鑰不會寫進任何命令。
閱讀 https://sourceweft.com/mcp/live.traintrackr%2Ftraintracker/install.md,並按其中說明把這個 MCP 伺服器接入我的客戶端。修改任何設定之前,先告訴我它的發布者、端點和登入方式,等我確認。不要把 API 金鑰寫進命令或我會提交的檔案;問我金鑰應從哪裡取得。其他 MCP 客戶端
把它新增到你客戶端的 mcpServers 設定中。 客戶端會在首次連接時完成 OAuth 登入。
{
"mcpServers": {
"traintrackr": {
"type": "http",
"url": "https://traintrackr.live/mcp"
}
}
}README
Traintrackr
An MCP server that lets Claude and ChatGPT answer questions about GB (National Rail) trains: "when's the next train to Sudbury?", "is the 13:00 from Liverpool Street on time?", "how do I get from Cambridge to Sudbury next Saturday morning?"
The public service runs at traintrackr.live. It is free, and you sign in with an email address; there is no password. The connector address is:
Use it
Claude. Traintrackr is listed in Claude's connector directory: open claude.ai/directory/traintrackr, or in claude.ai open Customize → Connectors, choose the + next to Connectors and find Traintrackr in the directory. Choose Connect: Claude opens the Traintrackr sign-in page, where you enter your email address and then open the link, or type the six-digit code, in the email it sends. A connector added in claude.ai also works in Claude Desktop and the mobile apps. To add it by address instead, choose Add custom connector, name it Traintrackr and paste the connector address.
ChatGPT. ChatGPT adds it through developer mode, on the web, in Plus, Pro, Business, Enterprise and Edu plans; in a workspace, an admin may have to allow developer mode first. Turn on Settings → Security and login → Developer mode, then at chatgpt.com/plugins choose +, name it Traintrackr, paste the connector address and choose OAuth. Sign in with your email address as above. In a new chat, choose + → Developer mode and select Traintrackr.
Then ask about trains. traintrackr.live/docs shows what each answer contains, and the limits. The privacy policy and terms of use are on the site too. The server is listed in the MCP Registry as live.traintrackr/traintracker.
The rest of this README describes how the server works and how to run your own copy: locally over stdio for the Claude desktop app, or hosted over HTTP (see Deploy to Render). The timetable is stored in Postgres.
Tools
Every tool accepts station names or CRS codes. Ambiguous names ("Sudbury", "Harrow") return the candidates so Claude can ask which one you meant.
Departure board
show_board comes with an MCP App: a client that supports MCP Apps draws its result as a split-flap departure board next to the answer. A client draws the board for every call of a tool that has one, so it is on a tool of its own, which the assistant is told to use only when someone asks to see a board. live_departures and live_arrivals return the same trains and draw nothing. A client without MCP Apps gets show_board's result as it would theirs.
The board is one page, apps/rail/ui/board.html, served as the resource ui://traintracker/board.html. Its style and script are inline and it loads nothing from anywhere. The client hands it the tool's result; it shows up to 12 trains, the station's notices and the data credit. Refresh asks the client to call show_board again with the same arguments, which counts against the rate limit like any other call. The flaps don't turn for someone who has asked their system for reduced motion, and a screen reader gets the trains as a table.
Run your own copy
Everything from here on is for running the server yourself. The public service at traintrackr.live is this code, deployed as described in Deploy to Render.
Try it without accounts (demo mode)
Set TRAINTRACKER_DEMO=1 to test every tool before your accounts are approved. The server then uses generated example data and makes no network requests:
- Timetable: a generated SCHEDULE feed for 25 real stations in East Anglia and London (Liverpool Street, Stratford, Chelmsford, Colchester, Ipswich, Norwich, Marks Tey, Sudbury, Cambridge, Kings Cross and others), running from today for 90 days. Sundays start later. It is stored in its own schema (
timetable_demo), apart from the real timetable, and rebuilt each day. Demo mode still needsDATABASE_URL. - Live boards: Darwin departure, arrival and service-details responses are generated from that timetable in-process. About one train in five runs late, one in 25 is cancelled and one in 12 has a platform change; the same train on the same day always gets the same result. Large stations (Liverpool Street, Kings Cross, Cambridge, …) announce platforms 15 minutes before departure, so the scheduled-platform fallback gets exercised.
Every board and plan carries a note that the data is generated, and data_status reports demo_mode. Ask for stations outside the demo network and you get an empty board.
Add that to the traintracker entry in the Claude desktop config (see Add to the Claude desktop app), or put TRAINTRACKER_DEMO=1 in .env. Remove it once your keys are in place. Things to try: "next trains from Liverpool Street", "which platform is the next train from Cambridge to Kings Cross?", "how do I get from Cambridge to Sudbury tomorrow at 9?"
Accounts you need
1. Rail Data Marketplace (Darwin)
- Create an account at raildata.org.uk.
- In the product catalogue, search "Live Departure Board" (the LDBWS product, not "Live Fastest Departure Boards") and subscribe. Approval for the free tier ranges from instant to a few days.
- Open the subscribed product → Specification tab. Copy the Consumer key (not the secret) into
DARWIN_API_KEY. - Check the base URL on that tab matches
DARWIN_DEPARTURES_URLin.env.example. If it differs, setDARWIN_DEPARTURES_URLto your value (everything before/GetDepBoardWithDetails). - (Optional) Subscribe to Service Details and Live Arrival Board the same way. Each product has its own key and URL: set
DARWIN_SERVICE_API_KEY/DARWIN_SERVICE_URLandDARWIN_ARRIVALS_API_KEY/DARWIN_ARRIVALS_URL.
2. Network Rail Open Data (SCHEDULE)
- Register at the Network Rail data feeds portal. Activation can take a while.
- Sign in and check that SCHEDULE appears under Static feeds.
- Put your portal username and password in
NR_USERNAMEandNR_PASSWORD. - If the portal gives a different download link for the full daily extract, all operators, JSON than
NR_SCHEDULE_URLin.env.example, setNR_SCHEDULE_URLto it.
The server downloads the feed in the background when the timetable is missing or older than 26 hours. It checks this on start-up and on every tool call, so a server left running for days stays current. If a download fails it keeps serving the old timetable, and data_status shows the error. You can also run manage.py refresh_timetable --commit by hand (see Commands). Set TIMETABLE_AUTO_REFRESH=0 where a separate job runs refresh_timetable, as on Render.
3. Postgres
The timetable lives in one schema (timetable by default) of the database in DATABASE_URL. The role needs to create schemas in that database; the owner of the database can. On a shared server, give traintracker its own database and role:
Put that role in DATABASE_URL, not the server's own user. The server's user owns every database on it, so a fault in traintracker would reach the other applications' data; the traintracker role reaches its own database only.
The full GB timetable takes about 475 MB (4.3 million stops), and about twice that while a refresh builds the new copy. A refresh takes about 40 seconds plus the download. It builds the new timetable in a staging schema and swaps it in with a rename, so readers never see a half-built timetable, and an advisory lock stops two refreshes running at once.
The tables are UNLOGGED: they are rebuilt from the feed every day, so they skip the write-ahead log and don't add to the server's WAL or point-in-time-recovery storage. The cost is that Postgres empties them after a crash; the tools then report "No timetable yet" until the next refresh.
How each source is used
Trainline links
A train from the timetable (timetable, plan_journey, service_details with a tt: ID, and the live tools when they fall back to scheduled times) carries trainline_url: that train's page on Trainline, with its stops and how it is running. The address is <TRAINLINE_LIVE_URL>/departures/<station>/<token>, where the token is the base64 of /callingPattern/<train UID>/<run date>. The station only titles the page: the board's station, the station a leg is boarded at, or the train's origin.
- Trains from Darwin have no link: a Darwin service ID carries no train UID.
- Trainline names stations by a slug of its own name for them. Most follow from the name in the station list;
trainline_slugs.jsonholds the ones that differ, andnullfor stations Trainline has no live board for (mostly ones opened since 2022). A train that calls only at those has no link. - Trainline publishes no specification for these addresses. They were read off its live boards, and the slugs checked against its sitemap of live pages, in September 2026. For a train it doesn't know, Trainline shows the station's board.
- Demo mode gives no links: its trains are made up.
Darwin allowance
Free Darwin access covers 5 million requests per four-week railway period, and one server key serves every user. The server counts the requests it sends to Darwin and reports the total in manage.py rail_status, and in data_status when DJANGO_DEBUG=1 (a hosted server doesn't tell everyone who signs in how much is left):
- What is counted: each request sent to Darwin, failed ones included. A board answered from the 20-second cache sends nothing and counts nothing. Demo mode counts nothing.
- The period: a rolling 28 days. Railway periods are not simple to derive (the first and last of the year vary in length), and a rolling window is never less strict than the period it overlaps.
- Per product: the count is kept for each Rail Data Marketplace product (
departures,arrivals,service) and the percentage uses the total. If your allowance is per product, the percentage overstates your usage. - Daily limit: the server sends Darwin at most
DARWIN_DAILY_LIMITrequests in one UK day (170,000 by default; the allowance works out at about 178,000 a day). Past that the live tools answer with scheduled times, and say why, until midnight. The count for the day is resumed after a restart.0turns the limit off. - Warnings: the log gets a warning when usage passes 70% of the allowance and another at 90%. Each is given once, and again only if usage falls below the mark and returns, or the server restarts above it.
- Storage: one small table,
darwin_requests, in its own schema (USAGE_SCHEMA), so the count survives restarts and timetable refreshes. Counts gather in memory and are written when 50 are waiting or a minute has passed, and at shutdown; a server that is killed loses at most that many. A failed write is logged and retried, and never fails a tool call. Rows older than 60 days are deleted.
What the timetable keeps
The SCHEDULE feed is large (all trains, freight included). On import, traintracker keeps only what a passenger needs:
- Passenger categories only (trains, replacement/timetabled buses, ships). Freight and empty stock are dropped, except overlay and cancellation records, which can stop a passenger train running on a given day.
- Stops with a public time only; junctions and passing points are dropped.
- Schedules that ended more than two days ago are dropped.
The timetable therefore covers two days before it was built up to the last date any schedule runs, and a tool asked about a date outside that says so.
For each date it applies Network Rail's precedence rules per train: cancellation (C) beats new (N), which beats overlay (O), which beats permanent (P), and it honours each schedule's running days and date range. Bank-holiday running flags are not applied yet, so on bank holidays check the live board.
Journey planning
plan_journey uses a round-based Connection Scan over every train hop of the day: round n finds the earliest arrival using at most n trains. That gives the fastest journey and the slower options with fewer changes in one pass, up to max_changes. It repeats from just after each departure to find the next few options, then drops any option that another beats on departure, arrival and changes at once, so you're never told to leave earlier than necessary. Results are ordered by arrival, and the fewest-changes option is always included.
- Minimum change time: 5 minutes between trains by default (
MIN_INTERCHANGE_MINUTES). Walk/Tube links already include time to get in and out of stations, so no extra change time is added after them. - Cross-London: the Tube isn't in the rail timetable, so transfers between London terminals (Kings Cross, Liverpool Street, Waterloo, etc.) are approximated: walks under 0.8 km at walking pace plus 5 minutes, otherwise Tube at ~12 minutes plus 3.5 minutes per km. They're labelled
tube (approx.). - Live overlay: for today's legs departing within two hours, Darwin's expected times and platforms are added, and
connection_at_riskis set if a delay or cancellation eats into a change, including one made via a walk/Tube link. If Darwin is down, the plan is still returned without live times. - The search covers the service day (trains running into the early hours are included); it doesn't carry over to the next morning. Station boards do include trains just after midnight.
- Load: a plan scans the day's trains several times, and the network for a date takes seconds to build and over a hundred megabytes to hold. Two dates are kept in memory. Timetable reads and plans run in threads, two at a time; a call that waits more than 10 seconds for its turn is told the server is busy.
Install
Requires uv, Python 3.14 (uv installs it from .python-version) and a Postgres database (any version from 13). Locally, Docker (or OrbStack) is the quickest way to get one:
Add to the Claude desktop app
Edit ~/Library/Application Support/Claude/claude_desktop_config.json. Use the full path to uv (run which uv), because the app doesn't use your shell's PATH:
--directory makes the project folder the working directory, so the server reads your keys from .env there. serve_stdio answers only the app that started it, so it has no sign-in and no rate limit. Keys can go in an "env" block in this file instead; real environment variables take precedence over .env.
Restart the Claude app, then ask "what's the status of traintracker's data sources?" to check everything is connected.
Configuration
All configuration is by environment variable, read from .env in the project folder if present. See .env.example. Local development needs none of the Django settings; on Render, titan's env groups set them (see Deploy to Render).
The Django settings, in config/settings.py:
The train tools' settings, read by apps/rail/config.py:
Commands
Every command is a Django management command, run as uv run python manage.py <command>. The ones that change data only say what they would do unless given --commit.
The account commands are described in docs/accounts.md. Under serve_stdio, logs go to stderr and stdout carries the MCP protocol.
Deploy to Render
render.yaml defines three services in Frankfurt:
The two env groups are made by titan, the command-line tool that comes with django-titan-template. titan is shared by every Titan app (debug off, the Python version, hosts and origins, the SMTP relay); traintracker is this app's own (DATABASE_URL, DJANGO_SECRET_KEY, ACCOUNTS_EMAIL_KEY, the mail key and sender, SITE_URL, FIVEBAR_DOMAIN and, added by hand, FIVEBAR_TOKEN). ACCOUNTS_EMAIL_KEY makes the one-way hash each account is found by: keep a copy, and never change it (docs/accounts.md). The Blueprint refers to them without declaring them, so a sync never overwrites them; a value set on a service wins over its groups'. The web service runs one worker because the train tools share one event loop, one Darwin budget and one journey network per process (the decision). It holds one day's journey network in memory for plan_journey (about 200 MB), so it needs at least 512 MB.
- Run
titan deploy traintracker. It makes what the Blueprint needs before the first deploy: the sharedtitangroup, the app's role and database on the shared Postgres instance, a sending-only Resend key, and thetraintrackergroup. Then it deploys the Blueprint, adds the domaintraintracker.<parent domain>to the service, and checks/healthzon itsonrender.comname. SetDARWIN_API_KEY,OPENAI_APPS_CHALLENGE,NR_USERNAMEandNR_PASSWORDon their services when Render asks for them. - Run the
traintracker-refreshcron job once by hand (Trigger Run) to load the first timetable. - Put the service on its domain. titan's own
golivepoints an app at<slug>.<parent domain>; this deployment keepstraintrackr.live, which the directory listings, the MCP Registry and people's connectors use, so it is done by hand: add the domain to the service, add it to the service'sDJANGO_ALLOWED_HOSTS(after thetitangroup's value), and setSITE_URL=https://traintrackr.liveandFIVEBAR_DOMAIN=traintrackr.livein thetraintrackergroup. titan never overwrites a value that is already set, soDEFAULT_FROM_EMAILcan be changed there too. - Add the server as a claude.ai connector (below).
Add as a claude.ai connector
/mcp is Titan's MCP view behind mcp-auth, the template's OAuth server on django-oauth-toolkit, so it can be added once in claude.ai and used from claude.ai, Claude Desktop and the mobile apps. ChatGPT connects the same way.
- In claude.ai: Settings → Connectors → Add custom connector, name
traintracker, URLhttps://<your-service-host>/mcp. Leave the OAuth client fields empty: Claude registers itself. - Claude opens the server's sign-in page. Sign in (see Sign-in).
Signing in hands Claude a one-hour access token and a 30-day refresh token, rotated on each refresh. The offline_access scope is listed, which ChatGPT needs to stay connected when its access token expires. Only callbacks to Claude, ChatGPT and loopback addresses (desktop clients such as Claude Code) are accepted (MCP_AUTH in config/settings.py). Anyone with an active account may connect. /oauth/connected/ lists the apps connected to the signed-in account. The traintracker-purge cron job deletes expired tokens each night.
Sign-in
Sign-in is Titan's accounts: an email address, then the link or the six-digit code in the email it sends, or a passkey added from the account page. There are no passwords. docs/accounts.md has the details: how long a link and a code last, the limits on emails and code tries, what an account keeps, and how to close or forget one. With DJANGO_DEBUG=1 the sign-in email is written to the console log in place of being sent; don't use that on a host whose logs other people can read.
Rate limit. Tool calls on /mcp are limited per account: RATE_LIMIT_BURST calls at once, refilled at RATE_LIMIT_PER_MINUTE. One Darwin key serves every user, and the limit stops one account spending the whole allowance. A call pays for one request to Darwin; a call that makes more (a long board read page by page, a journey with several legs) is charged one for each further request, so the account's next calls wait longer. A journey plan counts as three calls. A call over the limit comes back as a tool error, Too many requests. Try again in N seconds., which the model can read and relay. The counts are held in memory, so a restart clears them. serve_stdio is not limited.
Site
The Django app also serves a small public site, so there is one deploy and one domain:
The site is apps/public/. The pages are Django templates: each page's content is a fragment in templates/public/pages/ (index.html, docs.html, privacy.html, terms.html) placed inside templates/public/layout.html, rendered by views.py; pages.py lists them, builds the footer's links, and turns the privacy page into the text the privacy_policy tool returns. The shareable boards (the board pages, the picker, the embed, the TV view, the JSON feed and the station suggestions) are rendered per request by boards.py; frame.html is the board alone, for an embed or a TV. The static files are in static/: one stylesheet, css/site.css, and two scripts: js/site.js, for the copy buttons, the departure boards and the station picker, and js/saved_boards.js, for your last board and favourites. The board is the app's look, drawn by site.js from a table of example trains in index.html: apps/rail/ui/board.html keeps its style and script inline, which the site's content security policy refuses, so the flaps are written out again in site.css and site.js. A change to how the app draws its flaps goes in both. The icon is static/icons/icon.svg, with PNGs rendered from it at 32, 180 and 512 pixels; icon-512.png is the one uploaded to the directory listings, and /favicon.ico redirects to the 32-pixel PNG. To change the icon, edit the SVG and render the PNGs again, for example magick -background none -density 1200 icon.svg -resize 512x512 PNG32:icon-512.png. The link-preview card is static/img/share.png, 1200 by 630, drawn from static/img/share.svg: the front page's promise beside the departure board app. It needs a browser to render: the card uses the system's sans-serif and bold monospace fonts, which ImageMagick's own SVG renderer can't select. Draw share.svg onto a 1200 by 630 canvas in a browser and save that as a PNG, or take a screenshot of it at that size with a device pixel ratio of 1; then magick saved.png -strip PNG24:share.png drops the alpha channel, which the tests reject. In share.svg each flap is a <rect> and each letter its own <text>, so changing a train means changing its letters one by one. There is no build step. The pages name the server's own values (the connector address from SITE_URL, the fair-use line from RATE_LIMIT_PER_MINUTE and RATE_LIMIT_BURST), so a copy deployed elsewhere describes itself. The privacy policy and terms name this repository's deployment and its operator: change privacy.html, terms.html and the support address when you deploy your own.
The stylesheet and script are served by WhiteNoise under names that carry a hash of the file (collectstatic), so a browser fetches a changed file at once and can cache an unchanged one. The pages set no cookies and load nothing from another origin, except fivebar's counter while FIVEBAR_DOMAIN is set; the Content-Security-Policy header allows only the site's own stylesheet, script and fetches. They follow the reader's light or dark setting and work at phone width.
Shareable boards
/board/LST is a station's live departures as a page of its own, /board/LST/9 one platform's, and /board/LST/COL the trains calling at Colchester (or, to a station with no direct train in the next two hours such as /board/LST/SUY, each train with its connection at the station it changes at): the homepage's split-flap board with live trains and a station clock, to keep open on a screen, share as a link or embed in another site. Nobody has to sign in. /board is a form to pick the station, and a destination or a platform; the station fields suggest names as you type.
Darwin answers for two hours, so a quiet line, or any station overnight, would leave a board nearly empty. A board with fewer than its 10 trains goes on with scheduled trains from the timetable, from where Darwin's two hours end and up to a day ahead, so a train is never on it twice. Each says Scheduled in Expected, in pale letters (amber stays live and on time), and turns to its live time once it is within two hours. A train on a later day comes under that day's name (MONDAY 19 OCTOBER), and the credit names both sources. The tools mark these trains source: "timetable", with the date they run on.
After the station, a station's three-letter code is a destination and anything else a platform. A board is for a destination or a platform, never both. Each board has one address: a lower-case code, a name (/board/cambridge, /board/LST/colchester) or a platform written p9 or platform-9 redirects to it, so every share of a board is the same link. A name that fits several stations goes to the picker, which asks which. The board page has the link and the <iframe> code, each with a copy button, and a link to each platform's board.
The page is rendered with the trains in it, as a table, so it works without JavaScript; site.js draws the flaps, which turn from the old letters to the new at each update. Because anyone can open a board, and a board embedded in a busy page is opened by all its visitors, what reaches Darwin is one fetch per board a minute, not one per viewer: an answer (or a failure) is kept for 60 seconds and shared by every request for that board (SharedBoards in board.py), and requests that arrive while it is being fetched wait for the same answer. Each client address may open 30 boards at once and 60 a minute (BOARD_LIMIT in apps/public/board.py); over that the answer is 429 with Retry-After. The boards count toward DARWIN_DAILY_LIMIT like any other request.
Your boards. The front page shows the last board you opened, live, in place of its example board, and your favourites under it as links; a board page has Add to favourites (up to 12, the oldest going when you add a 13th). Signed out, the browser keeps them (localStorage) and nothing is sent to the server. Signed in, the account keeps them (SavedBoard, /api/boards in apps/public/saved.py) and the browser keeps nothing, so they are the same on every device; signing in moves what that browser kept into the account. Embeds and TV views are never remembered. The front page's HTML is the same for everyone, so it stays cached: static/js/saved_boards.js and site.js fill in your boards. Why: docs/decisions/saved-boards-live-in-the-browser-or-the-account.md.
On a TV. ?tv=1 shows the board alone and sizes its flaps so that every train fits the screen, in one line a train on a wide screen and two on a tall one; the columns the screen has left over at that size widen the destination, so the board fills the width and long names fit. On the board page, Show on TV switches to the same view in place and goes full screen, for a window dragged onto a TV used as an extended display or a cast tab; leaving full screen goes back. On a TV the screen is kept awake (Screen Wake Lock, where the browser has it), the cursor hides after 3 seconds without moving, and the page reloads itself once a night, between 03:10 and 03:50 UK time, so a board left running for weeks picks up changes to the site. It shares its fetch with every other view of that board, so it costs Darwin nothing extra. Every live board counts down to its next update beside "Updated", and one that hasn't updated for 5 minutes dims and says when it last did.
MCP Registry
server.json describes the server for the official MCP Registry: the name live.traintrackr/traintracker, the version, and one remote, streamable HTTP at https://traintrackr.live/mcp. It follows the registry's 2025-12-11 schema. A test keeps its version in step with pyproject.toml.
Version 0.1.0 is published. CI doesn't publish: a new version is published by hand, with mcp-publisher login dns and then mcp-publisher publish. A live.traintrackr/ name needs proof that you hold the domain, which is a TXT record on the apex of traintrackr.live carrying the public half of the signing key. For your own deployment, change the name and the URL to your domain.
Limitations
- Past running times ("was the 08:00 late yesterday?") aren't available. Darwin only covers now → +2 hours, and the timetable is scheduled times.
- Darwin outages: when Darwin is offline, the live tools show scheduled times and say that Darwin is offline, so delays, cancellations and live platforms are missing until it returns.
- Fares aren't included.
- Tube/bus/tram aren't in the timetable beyond the approximate London terminal links.
- Engineering works appear in the timetable once Network Rail publishes them (usually well ahead), and in Darwin on the day.
Development
Traintrackr is a Django project on Titan, the project template its sibling apps are built from. It moved onto Titan in four steps, from a Starlette app of its own (the decision); keep new work close to the template so copier update --trust stays a clean merge. HTTP is served by Django under gunicorn, with the train tools on one event loop per process (the decision).
tox runs every check on Python 3.14 from uv.lock (via the tox-uv plugin), as Titan does; docs/testing.md says which test goes where. The rail tests (tests/rail/) need a Postgres they can create schemas in, at TEST_DATABASE_URL (default postgresql://postgres:[email protected]:55432/postgres, which the docker run under Install provides). Each test works in its own schema and drops it afterwards (tests/rail/conftest.py). CI runs Postgres 17 as a service.
CI (.github/workflows/ci.yml) runs each environment as its own job, with sast and a gitleaks scan as well, using the versions of uv, tox and tox-uv written into the workflow, and its Actions pinned to a commit. Dependabot (.github/dependabot.yml) proposes updates to uv.lock, package-lock.json and the Actions each week; the uv and tox versions are raised by hand, uv in render.yaml as well. uv run pre-commit install runs ruff, gitleaks and mypy before each commit.
Tests use a synthetic SCHEDULE feed in Network Rail's JSON format (tests/rail/feedgen.py) and API fixtures shaped on the published Darwin schema. They aren't live recordings, so the first run against real services is the final check.
Preview servers
.claude/launch.json has two servers that Claude Code's preview pane can start. Both run in demo mode against the development Postgres above, ignore .env, and listen on 127.0.0.1 only. Set PREVIEW_DATABASE_URL to use another database.
The Inspector needs Node 22.19 or later. It starts a local server that can run commands, guarded by a token it makes at each launch and puts into the page it serves; the config leaves that on.
Data and licences
Darwin data via the Rail Data Marketplace, and Network Rail data feeds, are used under the terms you accept when you subscribe. Check those terms before redistributing any output. Both ask for the source to be credited:
The credits are on /docs, under "Where the data comes from". The footer of every page of the site carries the National Rail Enquiries credit and a "Data sources" link to that section. The server's instructions carry one line naming the sources, so an assistant can credit them when it says where an answer comes from; tool responses are not padded with it. The footer has the words and the link but not the NRE logo: the brand pack on the Darwin data feeds page makes the logo's use subject to National Rail's permission, which was asked for on 2026-09-30 (TODO(hugo) in layout.html).
Code: MIT.
來源:README.md,提交 4b0f652
工具
0版本歷史
1- v0.1.0最新Sep 30, 2026
評分
已滿足適用檢查項的 85%
該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。
- 近 30 天內可連通必需25 分
- 已宣告認證方式必需10 分
- 已列出工具15 分
請讓伺服器在無憑證時回應 tools/list,或發布可在沙箱中無憑證啟動的軟體包。
- 描述必需15 分
- 圖示10 分
- 文件15 分
- 授權條款10 分