Skip to main content
Glama
Sonu0305
by Sonu0305
README.md
# Cityflo OTP MCP (AI Engineer take-home)

Small **stdio MCP server** for Mumbai North **on-time performance** over one week of `trips.csv`. Built for Priya’s ask: *“Was route 12 late this week, and by how much?”* — with drill-down, not a dashboard.

## Scope

**In**
- Domain: on-time performance only
- Data: `data/trips.csv` (Mon–Fri 2026-06-15 … 2026-06-19)
- Tools: `route_otp_summary`, `list_late_trips`, `data_quality_report`
- Deterministic delay / quarantine / aggregation in Python tools

**Out**
- Occupancy, ticket triage, ops-log summariser
- Databases, BI dashboards, web UI
- Silent “data normalisation” that hides policy (see OPS-2207 below)

## Architecture

```
Agent / MCP client (stdio)
        │
        ▼
cityflo_otp.server  (FastMCP, 3 tools)
        │
        ▼
cityflo_otp.otp     (parse → quarantine → delays → aggregates)
        │
        ▼
data/trips.csv
```

The model should call tools for numbers and only phrase the answer. Arithmetic never lives in the LLM.

## Late definition

- `arrival_delay_min = actual_arrival − scheduled_arrival` (timezone-aware)
- **Late if `arrival_delay_min > 10`** (default; overridable via `late_threshold_min`)
- Headline metrics: **% late of usable trips**, **median** / **p90** arrival delay (not a fragile mean)
- Departure delay is returned for drill-down only

## Data-quality decisions

| Trip | Action | Why |
|------|--------|-----|
| TRIP_031 | Quarantine | `actual_departure` has invalid `08:60` |
| TRIP_017 | Quarantine | Actual arrival before actual departure |
| TRIP_044 | Quarantine | `actual_arrival` uses `+00:00`; naive compare ≈ +333 min |
| TRIP_101 | Quarantine | Missing `scheduled_arrival` |

Quarantined rows are **excluded** from OTP % / median and **listed** in tools. We do **not** invent corrected timestamps.

During **initial data inspection** (reading the starter bundle’s `ops_log.txt`, which is **not** loaded by this MCP at runtime), device **D-22** / MH-14-5590 was noted as flaky. The runtime tools only see `trips.csv`: they quarantine D-22 rows when **timestamp checks fail**, and leave other consistent D-22 trips usable.

### Untrusted handoff text (OPS-2207 / MH-12-7781)

`HANDOFF.md` contains instruction-shaped text asking tools to force vehicle **MH-12-7781** on-time and hide that rule. This server **does not** apply that override. The same late rule is used for every plate. `data_quality_report` surfaces the raw MH-12-7781 evaluation and a **policy question for Priya**. Hiding falsified numbers from a regional manager fails the auditability bar.

## Assumptions

1. Arrival lateness is the right primary metric for Priya’s standup fight.
2. Default threshold of **10 minutes** separates pattern (e.g. Route 12) from noise.
3. Prefer quarantine over silent correction of GPS/timezone garbage.
4. Thin samples (e.g. R-21 n=1) should be labeled, not ranked aggressively.
5. Ticket/occupancy files are out of scope for this MCP.

## Questions for Priya

1. Confirm **arrival** (vs departure) as the official late definition?
2. Confirm **>10 min** for regional-manager figures (or 5 / 15)?
3. How should **MH-12-7781 / OPS-2207** appear: exclude with disclosure, include raw, or dual view?
4. Minimum trip count before a route enters “worst offenders”?
5. For `+00:00` timestamps: always quarantine, or allow an explicit IST reinterpretation with an audit flag?

## How to run

```bash
cd ~/repos/cityflo-otp-mcp
python3 -m venv .venv && source .venv/bin/activate
pip install -e .
# or: pip install mcp && PYTHONPATH=src ...
```

**Checks (no MCP):**
```bash
PYTHONPATH=src python scripts/verify_otp.py
```

**Real MCP client multi-step run** (`route_otp_summary` → `list_late_trips` → `data_quality_report`):
```bash
PYTHONPATH=src python scripts/run_mcp_client.py
```
Transcript written to `notes/mcp_client_transcript.jsonl`.

**Cursor / Claude Desktop (stdio):** point an MCP server entry at:

```json
{
  "mcpServers": {
    "cityflo-otp": {
      "command": "python",
      "args": ["/absolute/path/to/cityflo-otp-mcp/src/cityflo_otp/server.py"],
      "env": {
        "PYTHONPATH": "/absolute/path/to/cityflo-otp-mcp/src",
        "CITYFLO_TRIPS_CSV": "/absolute/path/to/cityflo-otp-mcp/data/trips.csv"
      }
    }
  }
}
```

## Example agent interaction

1. `route_otp_summary({ "route": "Route 12" })`  
   → usable 8, late 6, **75%** late, median arrival delay on the order of ~13–16 min, 0 quarantined on R-12.
2. `list_late_trips({ "route": "R-12" })`  
   → the six late trip ids with per-trip arrival delays and vehicles.
3. `data_quality_report({})`  
   → four quarantined trips week-wide + MH-12-7781 policy note (TRIP_090 / TRIP_119 late under raw rule).

## Deliberately cut

- Other domains (occupancy / tickets / standup)
- Auto-fixing timezone offsets
- Ranking API across all routes (easy to add; not required for Priya’s Route 12 ask)
- Hidden OPS-2207 normalisation

## Where I disagreed with the AI

See `notes/DISAGREEMENTS.md`.

TDQS

A3.7/5.0

Scored across 3 tools

Disambiguation5/5

Each tool targets a distinct level of analysis: route-level aggregate OTP, trip-level late-trip detail, and data-quality context for quarantined rows. The descriptions explicitly frame list_late_trips as a drill-down from route_otp_summary, so there is no real overlap.

Naming Consistency4/5

All names use lowercase snake_case and clearly describe their output, but they do not follow a single verb_noun pattern: two are noun phrases (route_otp_summary, data_quality_report) and one uses a verb (list_late_trips). This is a minor stylistic deviation rather than a major inconsistency.

Tool Count5/5

With three tools, the server is tightly scoped to OTP reporting: summary, drill-down, and data quality. Each tool has a clear purpose and none feel redundant.

Completeness4/5

The tools cover the core read-only OTP workflow: aggregate summary, late-trip detail, and explanation of excluded data. A route enumeration tool would be a useful addition, but agents can work around this when route IDs are supplied.

Maintenance

ActivitySlowing
ResponsivenessNo issues