Skip to main content
Glama
pavansunkara958

Helios Field Service

README.md
# Helios Field Service — Production MCP Server & Client

Module 4 Lab — Model Context Protocol

Turns the Helios Robotics parts, inventory and RMA systems into an MCP capability
that any MCP-aware client can plug into. Built with [FastMCP](https://gofastmcp.com)
3.x against the [MCP specification](https://modelcontextprotocol.io).

| Requirement | Implementation |
|---|---|
| ≥3 tools | **4** — `search_parts`, `get_inventory`, `analyse_failure`, `create_rma` |
| ≥2 resources | **3** — `helios://catalog/summary` + two URI templates |
| ≥1 prompt | `diagnose_fault(fault_code, sku, site)` |
| Client discovers + invokes each | `client.py` — lists all three, invokes all three |
| Transport + justification | stdio (default), HTTP supported — [rationale](docs/architecture.md#transport-choice) |
| Client-side safety | **Both** — elicitation on the write, roots on the client |
| Security design summary | [docs/security.md](docs/security.md) |
| Error handling | Invalid input, unknown record, **and** unreachable backing store |

Docs: [architecture + transport](docs/architecture.md) ·
[tools/resources/prompts](docs/capabilities.md) ·
[security](docs/security.md) · logs: [`logs/`](logs/)

## Quick start

```bash
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
python seed_data.py
python client.py            # spawns the server over stdio and runs the full demo
```

No API key, no model, no cost — MCP is a protocol, and the client invokes the
server directly.

### Other runs

```bash
AUTO_APPROVE=1 python client.py           # non-interactive (CI, log capture)
SIMULATE_DB_OUTAGE=1 python client.py     # backing data source unreachable
MCP_TRANSPORT=http python server.py       # serve on 127.0.0.1:8000
MCP_TRANSPORT=http python client.py       # ...and connect to it
```

### Use it from any MCP host

```json
{
  "mcpServers": {
    "helios-field-service": {
      "command": "python",
      "args": ["/absolute/path/to/helios-mcp/server.py"]
    }
  }
}
```

That is the point of the exercise — built once, usable by every MCP-aware client.

## What the demo shows

`logs/demo.log` — full discovery-and-invocation flow:

```
1. DISCOVERY — tools
  • search_parts       [read-only]  Search the Helios spare parts catalogue...
  • get_inventory      [read-only]  Stock level and lead time for a part...
  • analyse_failure    [read-only]  Correlate a fault code with known issues...
  • create_rma         [WRITE]      Raise a Return Material Authorisation.

1. DISCOVERY — resources
  • helios://catalog/summary         Catalogue summary
  • helios://parts/{part_number}     Catalogue entry  (template)
  • helios://kb/{doc_id}             Knowledge base article  (template)

1. DISCOVERY — prompts
  • diagnose_fault(fault_code, sku, site)
```

The write, gated by elicitation:

```
  ┌─ SERVER REQUESTS CONFIRMATION ──────────────────────────────────
  │ Raise an RMA for 1 x HX2-BMS-03 (HX-200 Battery Management Board rev C)?
  │ Serial: HX200-PHX-0442
  │ Total value: $1,240.00
  └─────────────────────────────────────────────────────────────────
{"created": true, "rma_id": "RMA-00001", "value_usd": 1240.0,
 "requested_by": "mahesh.s"}
```

`logs/demo-db-outage.log` — the backing store unreachable. What the client sees:

```
TOOL UNAVAILABLE — Failure analysis is temporarily unavailable.
                   Quote reference dddc45d0fc07 to support if this persists.
```

What the server logged, on stderr (`logs/server-errors.log`):

```
ERROR [helios-mcp] [dddc45d0fc07] Failure analysis failed:
OperationalError: could not connect to helios-db-prod-01.internal:5432: timeout
```

The hostname and port never cross the protocol boundary. The correlation id is the
bridge between the two.

## Design notes

**Resources and tools are not interchangeable.** `search_parts` finds a part when
you don't know its id; `helios://parts/{pn}` fetches one you already have. Same
data, different access pattern.

**The prompt lives on the server on purpose.** The diagnostic procedure is Helios
domain knowledge, not host logic. Every connecting client gets the same rules —
rule out firmware before condemning hardware, never quote a superseded part —
rather than each reimplementing them and drifting.

**All logging goes to stderr.** On stdio, stdout carries the JSON-RPC frames. A
stray `print()` corrupts the protocol stream; there are none in the server.

**The client controls the server's environment.** A spawned stdio server does not
inherit the parent environment automatically, so `PythonStdioTransport(env=...)`
passes an explicit allow-list rather than handing over the caller's whole shell.

## Layout

```
server.py            MCP server: 4 tools, 3 resources, 1 prompt
client.py            MCP client: discovery, invocation, elicitation, roots
seed_data.py         Creates data/helios.db
docs/
  architecture.md    Diagrams + transport justification
  capabilities.md    Every tool, resource and prompt documented
  security.md        Auth, least privilege, error redaction
logs/
  demo.log                 Successful discovery-and-invocation flow
  demo-db-outage.log       Backing store unreachable, client view
  server-errors.log        Server-side detail with correlation ids
```