drive-relay-mcp
by klapom
README.md
# drive-relay-mcp
Uploads a file that already exists on a persona container's local disk to
Microsoft OneDrive/Graph — without round-tripping the bytes through an MCP
tool-call argument.
## Why this exists
Every other Graph upload path in this fleet (`ms-mcp`'s `upload_file` /
`upload_large_file`) requires the full file content as base64 **inside** the
JSON tool-call argument. That has a hard ceiling (~60KB) on the size an LLM
can practically emit as output tokens — well below what a scanned receipt or
generated PDF needs. `ms-mcp`'s `save_attachment_to_drive` solves this for
Microsoft Graph **mail attachments** (fetched server-side, never touching an
LLM argument) — but that only works when the source is a Graph attachment
(`message_id` + `attachment_id`). It cannot help a file that only exists on a
persona container's local disk (e.g. a receipt photo converted to PDF).
This service closes that gap: given a small filename reference (not file
content), it reads the file directly off a shared host directory and uploads
it via a Graph chunked-upload session — the same mechanism `save_attachment_to_drive`
uses, just sourced from local disk instead of a Graph attachment fetch.
## Architecture
- **Shared host directory**: `/home/admin/hermes-shared-uploads/<persona_key>/`
is bind-mounted into each persona's Docker container at `/data/shared-uploads`.
This service (running natively on the same host as `ms-mcp`, via systemd)
reads the same host path directly — no extra Docker networking needed.
- **Tool surface**: one tool, `upload_local_file(file_name, folder_path, target_file_name?, confirm, idempotency_key?, keep_source?)`.
`file_name` is a **filename only** — no path separators, no subdirectories.
The server resolves it strictly to `<shared-root>/<persona_key-from-JWT>/<file_name>`,
where `persona_key` comes from the authenticated gateway JWT, never a
caller-supplied field. See `src/utils/safe-path.ts` for the symlink-escape
hardening (a persona container runs as root and could otherwise plant a
symlink pointing at an arbitrary host file).
- **Auth**: ported from `ms-mcp`'s proven gateway-JWT + persona-scoping stack
(`src/auth/`), trimmed to a single `uploads: boolean` capability. Runs in
`enforce` mode from day one (no off/shadow rollout ramp — this is a new
service with no legacy unauthenticated traffic to protect).
- **Cleanup**: deletes the source file on successful upload by default
(`keep_source: true` to opt out), plus an hourly TTL sweep of anything left
behind by a failed/abandoned upload.
## Setup
```bash
pnpm install
cp .env.example .env # fill in AZURE_TENANT_ID, AZURE_CLIENT_ID, GATEWAY_ISSUER, AUTH_TOKEN, BOOT_AUTH_TOKEN
pnpm build
pnpm auth # one-time interactive device-code sign-in (per deployment identity)
pnpm start # or: systemd unit, see systemd/drive-relay-mcp.service
```
`config/persona-scopes.json` is the fail-closed allowlist — a persona absent
from this file gets `403 Forbidden` regardless of JWT validity.
## Development
```bash
pnpm test:coverage
pnpm lint
pnpm typecheck
pnpm build
```
Maintenance
ActivityStale
ResponsivenessNo issues