Virtual Engineer MCP Server
by benkipnis
README.md
# Virtual Engineer
AI-assisted troubleshooting for field engineers — a runnable demo that shows how an LLM agent grounded in MongoDB Atlas can surface expert-level diagnostic guidance without Level 2 escalation.
When an engineer describes a symptom, the system automatically surfaces the unit's current alarm state, recent operational telemetry, and prior repair history to identify the most likely root cause and recommended next steps. Two audience-specific interfaces are included: a structured evidence view for technical staff and a conversational interface for field teams.
---
## Context
Commercial HVAC equipment generates a constant stream of alarms, telemetry, and work-order data that is rarely connected at the point of diagnosis. A field engineer on-site typically has access to an alarm code and a gut feeling — not the 8-month prior service record, the rising motor temperature trend that preceded the fault, or the knowledge article that recommends PTC sensor inspection before compressor replacement.
Virtual Engineer closes that gap. It is designed to demonstrate MongoDB Atlas as the operational backbone for an agentic field-service AI: deterministic lookups for factual grounding, hybrid semantic search for knowledge and case retrieval, time-series telemetry for trend analysis, and a session/feedback layer for continuous improvement.
---
## Architecture
```
Browser (React + Vite)
│ SSE /api/chat
▼
Express Server ──► LLM Agent (OpenAI / Anthropic / Grove)
│ │ tool calls
│ MCP HTTP client
│ │
▼ ▼
MCP Server (20 tools)
│
▼
MongoDB Atlas
├── Atlas Database chillers, sites, alarms, telemetry, service tickets
├── Atlas Vector Search semantic search on knowledge_documents + service_tickets
├── Atlas Text Search lexical search for $rankFusion hybrid queries
├── Native Reranking Voyage $rerank after hybrid candidate generation
└── Time Series 7-day hourly telemetry per chiller
```
### Two-layer retrieval strategy
| Layer | Pattern | Tools |
|-------|---------|-------|
| **Deterministic** | Exact finds, `$lookup` joins, range scans | `getChillerById`, `getSiteContext`, `getActiveAlarms`, `getAlarmHistory`, `getAlarmDetails`, `getCurrentDeviceState`, `getTelemetry`, `getServiceHistory`, `getPartsHistory` |
| **Probabilistic** | Hybrid `$rankFusion` + Voyage `$rerank` | `searchManuals`, `searchTroubleshootingGuides`, `searchTechnicalBulletins`, `searchCaseNotes`, `filterCases` |
The agent always resolves asset and operational facts first (deterministic), then expands into knowledge and case retrieval (probabilistic). Tool sequences are chosen dynamically by the LLM — nothing is scripted.
### Atlas services used
- **Atlas Database** — operational collections
- **Atlas Vector Search** — autoEmbed on `knowledge_documents.content` and `service_tickets.case_notes`
- **Atlas Text Search** — lexical index for `$rankFusion` hybrid search
- **Atlas Hybrid Search (`$rankFusion`)** — Reciprocal Rank Fusion combining vector + lexical results natively (requires MongoDB 8.1+ or 8.0 with a support-case feature flag)
- **Native Reranking (`$rerank`)** — Voyage cross-encoder rerank in the same aggregation after `$rankFusion` (MongoDB 8.3+, project toggle; Preview). Default model: `rerank-2.5-lite`. See [Enable Native Reranking](#5-enable-native-reranking-rerank) below.
- **Time Series collection** — `telemetry`, with `timeField: timestamp`, `metaField: chiller_id`, `granularity: minutes`
### MCP server
Built on `@modelcontextprotocol/sdk` with Streamable HTTP transport. The server exposes 20 tools across five categories:
| Category | Tools |
|----------|-------|
| Asset & site | `getChillerById`, `getChillerConfiguration`, `getSiteContext` |
| Alarms | `getActiveAlarms`, `getAlarmHistory`, `getAlarmDetails` |
| Telemetry & state | `getCurrentDeviceState`, `getTelemetry`, `getFaultEvents` |
| Service history | `getServiceHistory`, `getPartsHistory` |
| Knowledge & cases | `searchManuals`, `searchTroubleshootingGuides`, `searchTechnicalBulletins`, `filterCases`, `searchCaseNotes` |
| Session & feedback | `startTroubleshootingSession`, `storeRecommendationTrace`, `captureEngineerReaction`, `captureResolutionOutcome` |
Every tool response includes a `query_insight` object (access pattern, collection, pipeline summary) that the UI surfaces in real time.
### LLM agent
Stateless tool-use loop configured via `AGENT_MAX_STEPS` (default: 12). Supports four providers:
| `LLM_PROVIDER` | Gateway | Key |
|----------------|---------|-----|
| `openai` | Direct | `OPENAI_API_KEY` |
| `anthropic` | Direct | `ANTHROPIC_API_KEY` |
| `grove-openai` | MongoDB Grove | `MDB_GROVE_API_KEY` |
| `grove-anthropic` | MongoDB Grove | `MDB_GROVE_API_KEY` |
### Demo UI
Single-page React app (Vite) with three tabs, all sharing a single `ChatContext` SSE stream:
- **Overview** — architecture diagram, Atlas service inventory, scenario picker with starter prompts
- **Evidence Board** — technical view: structured evidence cards, agent flow timeline, query inspector (shows MongoDB access patterns as each tool fires)
- **Field Chat** — conversational interface with X-ray mode to expose tool calls inline
---
## Demo scenarios
| Chiller | Site | Active alarm | What it demonstrates |
|---------|------|--------------|----------------------|
| **CH-ATL-003** | Piedmont Regional Medical Center | `A1.01` — Motor Temp Too High | Hero scenario: rising temperature trend over 24 h, prior PTC sensor replacement 8 months ago, open emergency dispatch ticket. Illustrates repeat-fault pattern recognition. |
| **CH-DAL-002** | Dallas Convention Center | `207` — High Condenser Pressure | Cooling tower fan VFD fault contribution; two prior related work orders. Tests cross-system causal reasoning. |
| **CH-PHX-005** | Phoenix Sky Harbor Logistics | `Co.A1` — Compressor Board Comm Loss | LEN bus fault; unit offline at alarm time. Tests degraded-connectivity diagnostic path. |
| **CH-ATL-001** | Hartsfield-Jackson Tech Hub | _(none)_ | Stable unit — PM history only. Negative control: confirms agent does not hallucinate faults. |
| **CH-CHI-004** | Chicago Medical District | _(none)_ | Cross-case: prior `A1.01` resolved via coil cleaning (not PTC replacement). Tests disambiguation from the hero scenario. |
### Hero demo sequence (CH-ATL-003)
1. Open the **Overview** tab, select **CH-ATL-003**, and click a starter prompt.
2. Switch to **Evidence Board** — watch the agent flow timeline populate as tools fire in sequence: asset lookup → active alarms → telemetry trend → service history → knowledge search → case search.
3. Switch to **Field Chat** and run the same prompt for the conversational framing.
4. Use the thumbs-up/thumbs-down reaction to capture engineer feedback.
---
## Getting started
### Prerequisites
- Node.js 18+
- MongoDB Atlas cluster on **8.3+** for Voyage `$rerank` (choose **Latest version with auto-upgrades** in Cluster Builder). `$rankFusion` hybrid search works on 8.1+ (or 8.0 with a support-case flag); deterministic tools work on earlier versions.
- M10+ recommended for CLI index management (`npm run indexes:create`); M0 can run the demo if you create the four search indexes in the Atlas UI
- LLM API key — one of `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, or `MDB_GROVE_API_KEY`
### 1. Clone and install
```bash
git clone <repo-url>
cd virtual-engineer
npm install
cd frontend && npm install && cd ..
```
### 2. Configure environment
```bash
cp .env.example .env
```
Edit `.env`:
```bash
# Atlas
MONGODB_URI=mongodb+srv://<user>:<pass>@<cluster>.mongodb.net/
MONGODB_DB=virtual_engineer
# LLM — pick one provider
LLM_PROVIDER=openai
OPENAI_API_KEY=sk-...
# Local dev
MCP_AUTH_DISABLED=true
MCP_PORT=3100
CORS_ORIGIN=http://localhost:5173
```
See the full environment variable reference in [`docs/runbook.md`](docs/runbook.md).
### 3. Seed sample data
```bash
npm run seed:drop
```
This regenerates 7-day hourly telemetry, creates the time-series collection, and inserts all sample documents.
### 4. Create Atlas search indexes
```bash
npm run indexes:check # reports READY / BUILDING / MISSING
npm run indexes:create # creates missing indexes and polls until READY
```
Four indexes are required for hybrid search to work without `degraded: true` responses: vector + lexical on both `knowledge_documents` and `service_tickets`. See [`docs/indexes.md`](docs/indexes.md) for manual index definitions.
> **Note:** `$rankFusion` hybrid search requires MongoDB 8.1+ (or 8.0 with a support-case feature flag). On earlier versions, knowledge and case search tools return empty results with `degraded: true` — all deterministic tools continue to work.
### 5. Enable Native Reranking (`$rerank`)
Knowledge and case search append Voyage [`$rerank`](https://www.mongodb.com/docs/vector-search/query/aggregation-stages/rerank/) after `$rankFusion` in the same aggregation. No extra search indexes and no Voyage API key — usage is billed through Atlas (Preview).
Do this once per Atlas project before expecting non-degraded reranked results:
1. Confirm the cluster is **MongoDB 8.3+** (Atlas UI → cluster → version, or create/upgrade with **Latest version with auto-upgrades**). `$rerank` is not available on 8.1/8.2 even if `$rankFusion` works.
2. As **Project Owner**, open **Project Settings** and set **Native Reranking: `$rerank` in the Aggregation Pipeline** to **On**. Confirm the Voyage model usage / billing dialog.
3. Leave the defaults in `.env` (or omit them): `RERANK_ENABLED=true`, `RERANK_MODEL=rerank-2.5-lite`, `RERANK_CANDIDATES=20`.
`$rerank` is not supported on Atlas Local or self-managed `mongod`. It cannot sit inside `$rankFusion` input pipelines — this demo runs it after fusion.
If the toggle is off or the cluster is below 8.3, search retries `$rankFusion` only and returns `degraded: true` with fusion order. Set `RERANK_ENABLED=false` to skip `$rerank` on purpose. Full pipeline notes: [`docs/indexes.md`](docs/indexes.md#native-reranking-via-rerank).
### 6. Start the backend
```bash
npm run mcp:dev
```
Server starts on `http://localhost:3100`. Verify with:
```bash
curl http://localhost:3100/health
```
### 7. Start the demo UI
```bash
cd frontend && npm run dev
```
Open `http://localhost:5173`. The Vite dev server proxies `/api` to the backend automatically.
### 8. Run connectivity tests
```bash
npm run test:connectivity # requires backend running on :3100
```
---
## API endpoints
| Method | Path | Purpose |
|--------|------|---------|
| `GET` | `/health` | DB ping + service status |
| `POST` | `/mcp` | MCP JSON-RPC (initialize, tools/call) |
| `GET` | `/mcp` | SSE stream for server-initiated messages (`mcp-session-id` header required) |
| `DELETE` | `/mcp` | Session termination |
| `POST` | `/api/chat` | LLM agent chat (SSE stream) |
| `POST` | `/api/feedback` | Engineer reaction capture |
| `GET` | `/api/health` | Chat API health (returns provider + model, no secrets) |
---
## Data freshness
Sample data uses `Date.now()`-relative windows for alarms and telemetry. Re-run `npm run seed:drop` any time to reset to a fresh baseline anchored to the current hour.
For continuous automated freshness without manual steps, deploy the Atlas Scheduled Trigger at `scripts/data/atlas-trigger/refresh-demo-data.js` — it fires daily at 02:00 UTC and rolls alarm timestamps and telemetry forward. See [`docs/data-freshness.md`](docs/data-freshness.md) for setup instructions.
---
## Repository layout
| Path | Purpose |
|------|---------|
| `backend/src/` | Express server — MCP tools, LLM agent, SSE chat API |
| `backend/src/mcp/createServer.js` | 20 MCP tool definitions |
| `backend/src/agent/` | LLM orchestrator + MCP HTTP client |
| `backend/src/repositories/` | MongoDB data access layer |
| `frontend/src/` | Demo UI — Overview, Evidence Board, Field Chat |
| `scripts/data/` | Sample data, schemas, seed scripts, Atlas trigger |
| `tests/connectivity/` | MCP smoke tests |
| `docs/` | Architecture, runbook, indexes, schema review, gates |
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues