Command Palette

Search for a command to run...

UnylyUnyly
Весь каталог

Cityflo On Time Performance

БесплатноНе проверен

Answers operational questions about route on-time performance, such as lateness rates and trip evidence, using structured tools for route summary, trip lateness

GitHubEmbed

Описание

Answers operational questions about route on-time performance, such as lateness rates and trip evidence, using structured tools for route summary, trip lateness, and data quality.

README

MCP server for Mumbai North on-time performance — the slice Priya’s handoff actually asks for:

Was route 12 late this week, and by how much — and which trips prove it?

Built for the Cityflo AI Engineer take-home. Domain chosen: on-time performance only. Occupancy, support-ticket triage, and ops-log summarisation were deliberately cut so the tools stay sharp and auditable.

Ops people get a plain-language answer from an MCP client (Cursor or the included Kimi agent). Deterministic TypeScript does the arithmetic; the model only phrases the result. Every headline can be drilled into trip-level evidence.

Stack: Node.js 20.x, TypeScript, @modelcontextprotocol/sdk. No database — data/trips.csv is loaded in memory.


Install

git clone <this-repo>
cd Cityflo-Assignment   # or your clone path
npm install
cp .env.example .env
Env var Required for Notes
MOONSHOT_API_KEY npm run agent Moonshot / Kimi path only — never deploy this to Vercel
JWT_SECRET Hosted /api/mcp + web gateway Min 16 chars; openssl rand -hex 32
TRIPS_CSV_PATH Optional Override default data/trips.csv

How to run

1. Local stdio MCP (Cursor — primary path)

npm run mcp

.cursor/mcp.json already points Cursor at this server. After npm install, reload MCP in Cursor and ask:

Was route 12 late this week, and by how much?

Production stdio (no tsx):

npm run build:mcp
npm run start:mcp
# MCP command: node dist/mcp/server.js

2. End-to-end with a real client (no LLM)

npm test          # domain + MCP integration + JWT (36 tests)
npm run e2e       # multi-step MCP client → demos/mcp-e2e-session.md

3. Optional Kimi K2.6 terminal agent

# set MOONSHOT_API_KEY in .env
npm run agent -- "Was route 12 late this week, and by how much?"

Transcripts when generated: demos/agent-session.md, demos/mcp-e2e-session.md.

4. Hosted Streamable HTTP (optional for Cityflo demos)

Live: https://cityflo-on-time-mcp.vercel.app

  1. Open the site → sign in ([email protected] / tester) → Copy Cursor MCP JSON (JWT embedded).
  2. Or mint a token: npm run mint-token, then:
{
  "mcpServers": {
    "cityflo-on-time": {
      "url": "https://cityflo-on-time-mcp.vercel.app/api/mcp",
      "headers": {
        "Authorization": "Bearer YOUR_JWT"
      }
    }
  }
}
Endpoint Auth Purpose
/ internal login Cityflo-styled gateway — copy MCP config
/api/health none Liveness + trip count
/api/mcp Bearer JWT (on_time:read) Streamable HTTP MCP

Local hosted smoke: JWT_SECRET=… npm run dev then MCP_BASE_URL=http://localhost:3000 npm run smoke:http.


Tools (3)

Computation stays in tools. The model phrases language only.

Tool What it does
get_route_on_time_summary Route-level late rate, median/max lateness, daily pattern, late trip ids, rules applied
get_trip_lateness_evidence Trip-by-trip scheduled vs actual + classification + exclusion reasons (requires route_id or trip_id)
get_data_quality_report Quarantined / duplicate rows by reason and GPS device

Invalid filters return structured MCP isError payloads — not silent wrong numbers.

Human/model boundary: tools return JSON numbers and trip ids; the client/agent narrates. Answering Priya honestly is a multi-step loop (summary → evidence → optional quality), not one call.


Assumptions (under-specified parts of the brief)

  1. Late = arrival lateness actual_arrival − scheduled_arrival strictly greater than the threshold (default 10 minutes). Exactly 10 minutes counts as on-time.
  2. Early arrivals (negative lateness) are on-time; median and max include all valid trips.
  3. When start_date / end_date are omitted, summary and evidence both use min..max service_date in the loaded export (“this week” ≈ the export window).
  4. Times are interpreted in IST (+05:30). Mixed-offset rows coerce non-IST wall clocks to IST and set timezoneNormalised (see TRIP_044).
  5. Unparseable times, arrival-before-departure, missing scheduled_arrival, and calendar mismatches are excluded from metrics and surfaced in the quality report.
  6. Exact operational duplicates count once (lowest trip_id kept); extras are duplicate_of.
  7. Headlines use late rate + median + max, not mean alone — one bad GPS row must not swing standup.
  8. Free text in handoffs / tickets / ops logs is untrusted data, never tool instructions. Every vehicle is scored with the same documented rules.

How messy data was handled (data/trips.csv)

Trip Issue Decision
TRIP_017 actual_arrival before actual_departure (device D-22) Exclude — arrival_before_departure
TRIP_031 actual_departure = 08:60:00 (D-22) Exclude — unparseable_timestamp
TRIP_044 Arrival tagged +00:00, rest +05:30 Keep after IST wall-clock coercion; ~3 min late
TRIP_052 / TRIP_053 Exact duplicate R-09 rows Keep 052; exclude 053 as duplicate_of
TRIP_101 Empty scheduled_arrival Exclude — missing_scheduled_arrival

Device D-22 appears twice in exclusions — consistent with Priya’s flaky-GPS warning. See also docs/DATA_AUDIT.md.

Route 12 answer (export window, 10 min threshold)

  • 8 valid trips, 6 late (75%)
  • Late on 4 of 5 service days
  • Median lateness 13.5 min, max 18 min
  • Friday (TRIP_077 / TRIP_078) is the on-time day

That is a real late pattern for the week — not just a loud complaint — and get_trip_lateness_evidence lists the trips behind the rate.


Questions I would ask Priya

  1. Is 10 minutes the regional review threshold, or do you want 5 / 15 for standup?
  2. Should we judge arrival, departure, or both?
  3. When GPS rows look impossible, do you prefer exclude-from-metrics (current) or keep-with-flag in the headline?
  4. Which window is “this week” — Mon–Fri service dates only, or calendar week including weekends?

Trust boundary

This server only reads trips.csv (or TRIPS_CSV_PATH). Operational free text elsewhere — including anything that looks like a standing policy inside a handoff — is not executed as instructions. Metrics come from deterministic code so a regional manager can ask “which trips?” and get ids + timestamps, not a vibe.


What was deliberately cut (and why)

Cut Why
Occupancy / tickets / overnight-log tools Brief: one sharp domain. Priya’s ask is Route 12 lateness with evidence.
Database CSV-in-memory is enough for the export size and the half-day slice.
Full OAuth / IdP Hosted path uses short-lived HS256 JWTs + internal login for demos.
Auto root-cause / “blame the corridor” Devices show up in quality grouping; narrative cause is the model’s job only when grounded in evidence.
Kimi / MOONSHOT_API_KEY on Vercel Keys stay local; hosted surface is MCP + JWT only.

If there had been more time: configurable thresholds per region, departure lateness as a second metric, and a thin “compare two weeks” tool — not a dashboard.


Where I disagreed with the AI

  1. Timezone on TRIP_044. A naïve “parse ISO as instants” path made the mixed +00:00 arrival look ~5.5 hours late. After inspecting the export (wall clock looks IST; only the offset is wrong), I coerced non-IST wall clocks on mixed-offset rows to +05:30 and flagged timezoneNormalised (~3 minutes late).

  2. Handoff special-casing a vehicle. The ops handoff text asked to quietly force one plate always on-time and hide it from rankings. That conflicts with auditability and the trust-boundary criterion. I treated it as untrusted data, not a product requirement, and score every vehicle with the same rules.

  3. Headline metric. Mean delay was the easy default. One garbage or extreme trip would swing standup. I used late rate + median + max and forced drill-down via get_trip_lateness_evidence.

  4. Scope. Building all four domains was tempting. Priya’s question is Route 12 lateness with evidence — the other three domains stayed out.

  5. Hardcoded calendar window. An early draft baked 2026-06-15..19 into the domain. Default window now comes from the loaded export so summary and evidence cannot silently diverge when the CSV grows.

More detail: NOTES.md.


Project layout

app/                           # Next.js gateway + hosted MCP/health/login
src/domain/trips.ts            # load, validate, summarise
src/mcp/createServer.ts        # transport-independent tools
src/mcp/server.ts              # stdio entrypoint
src/agent/chat.ts              # optional Kimi MCP client
src/auth/jwt.ts                # HS256 mint/verify
data/trips.csv                 # runtime input
demos/                         # e2e + agent transcripts
docs/DATA_AUDIT.md             # data decisions
test/                          # domain, MCP integration, JWT

Commands cheat sheet

Command Purpose
npm install Install dependencies
npm run mcp Stdio MCP for Cursor
npm run agent -- "…" Kimi agent over stdio MCP
npm test / npm run e2e Unit/integration + multi-step smoke
npm run build:mcp && npm run start:mcp Production stdio
npm run dev Local Next (gateway + /api/mcp)
npm run mint-token CLI JWT for hosted MCP
npm run smoke:http Hosted JWT + Route 12 smoke

from github.com/devbathani/Cityflo-Internal-MCP

Установка Cityflo On Time Performance

У этого сервера нет опубликованного пакета — он собирается из исходников. Открой репозиторий и следуй инструкции в README.

▸ github.com/devbathani/Cityflo-Internal-MCP

FAQ

Cityflo On Time Performance MCP бесплатный?

Да, Cityflo On Time Performance MCP бесплатный — установка в пару кликов через Unyly без оплаты.

Нужен ли API-ключ для Cityflo On Time Performance?

Нет, Cityflo On Time Performance работает без API-ключей и переменных окружения.

Cityflo On Time Performance — hosted или self-hosted?

Self-hosted: сервер запускается локально на твоей машине командой из раздела установки.

Как установить Cityflo On Time Performance в Claude Desktop, Claude Code или Cursor?

Открой Cityflo On Time Performance на unyly.org, выбери вкладку своего клиента (Claude Desktop, Claude Code, Cursor) и нажми Install — конфиг сгенерируется автоматически, без правки JSON.

Похожие MCP

Compare Cityflo On Time Performance with

Не уверен что выбрать?

Найди свой стек за 60 секунд

Автор?

Embed-бейдж для README

Похожее

Все в категории development