# user_mcp

MCP server for interacting with the human user — the agent's "speak and show" layer.
It presents HTML GUIs on the user's screen, waits for the user's next instruction
(speech-to-text), and provides date/time and a shared drive. It runs a FastMCP server
plus a FastAPI + WebSocket bridge to the browser.

# General Overview
![General Overview](docs/diagram.png)

## Run

```bash
./run.sh
```

The script loads secrets from `/root/.secrets/user_mcp.env` (it aborts if the file
is missing), creates a local `.venv`, installs `requirements.txt`, and starts
`user_mcp.py`.

## Tests

```bash
.venv/bin/python -m pytest tests
```

## Tools (key ones)

- `what_next(last_operation)` — block until the user speaks their next instruction
  (returned as text, together with every screen the user had in front of them while
  speaking, each with its id). The main "wait for the user" hook.
- `present_data(html_data, js_data?, replaces?)` — render HTML (plus optional JS) on
  the screen. Without `replaces` it adds a new screen; with `replaces` set to a screen's
  id it swaps that screen's content in place, so a "change this" command corrects what
  the user is looking at instead of stacking another card. Returns the new screen id.
- `get_current_date()`
- `shared_drive_get_last_uploads(count)` — to me moved somewhere else
- `wiki_dodaj_strone(title, content)` — to me moved somewhere else
- `reconnect_mcp(server_name)`, `autoenter_off()`  — to me moved somewhere else

`present_data` and `what_next` require a live browser WebSocket connection.

## Screen identity

Every screen has an id: a SHA-1 of its content, computed on the server and sent to the
browser with the content. The browser never computes it — it stores it and sends the
same string back with a recording, so a recording made on an older screen is matched to
the right one. New content always gets a new id, and a replaced screen is kept rather
than overwritten: a recording already in the transcription queue still refers to the
tree it was made on.

One recording can span several screens (the record button docks and the carousel keeps
swiping), so the prompt lists each screen the user passed through, in order, with the
seconds it was visible — including screens they only looked at while talking. A single
command can therefore change more than one screen.

## Access PIN

The page asks for a four-digit PIN on an on-screen keypad before it connects
(`www/pin-gate.js`). The PIN is the last path segment of both browser-facing
endpoints — `POST /backendSrv02/<pin>` and `ws /gui-socket/<pin>` — so a wrong PIN
is simply an address that does not exist and an outsider can neither see the
presented views nor send a recording.

The PIN is a secret like the OpenAI key: `access-pin=${USER_MCP_PIN}` in
`lib/speech_to_text/config.properties`, value in `/root/.secrets/user_mcp.env`.
To change it, edit the secrets file and restart — both servers read it from
there. Without `USER_MCP_PIN` the component refuses to start.
