Skip to main content
Glama
crashzero9
by crashzero9
README.md
# OHMS - Order Hub Management System

Flauraly Flowers and Plants - Python FastMCP server hosted on Replit Reserved VM.

## Purpose

OHMS exposes a small, hardened set of order- and inventory-related tools to
Violet (and any other authorized MCP client) over the Model Context Protocol.
It centralizes Shopify Admin REST access behind a Bearer-auth gate so agents
never touch raw Shopify credentials.

## Architecture

```
+-----------------+      Bearer       +------------------------------+
|  MCP Client     |  ---------------> |  OHMS (Replit Reserved VM)   |
|  (Violet, etc.) |   /mcp or /sse    |                              |
+-----------------+                   |  Starlette parent app        |
                                      |  +-- /health (open)          |
                                      |  +-- /mcp   (Streamable HTTP)|
                                      |  +-- /sse   (SSE fallback)   |
                                      |  +-- BearerAuthMiddleware    |
                                      +--------------+---------------+
                                                     |
                                                     v
                                      +------------------------------+
                                      |  Shopify Admin REST API      |
                                      +------------------------------+
```

Both `/mcp` (Streamable HTTP) and `/sse` (Server-Sent Events fallback) are
mounted simultaneously so any MCP client transport profile works.

## Environment Variables

OHMS authenticates to Shopify via the OAuth 2.0 **client_credentials** grant.
The server never holds a long-lived `SHOPIFY_ACCESS_TOKEN`; instead it holds a
`SHOPIFY_CLIENT_ID` + `SHOPIFY_CLIENT_SECRET` pair and mints a short-lived
access token on demand against `https://{shop}.myshopify.com/admin/oauth/access_token`.
The minted token is cached in-memory with a 5-minute clock-skew buffer and
re-minted automatically (or on a 401/403 from any subsequent call). All values
are read via `os.environ.get(...)`. Nothing is hardcoded.

| Var | Purpose |
|---|---|
| `PORT` | TCP port to bind (Replit injects this; defaults to 8080). |
| `OHMS_API_TOKEN` | Static bearer token required on every non-`/health` request (client to OHMS auth - separate from Shopify). |
| `SHOPIFY_STORE_URL` | Shop domain, e.g. `flauraly.myshopify.com`. |
| `SHOPIFY_CLIENT_ID` | Shopify app client ID (used for OAuth client_credentials grant). |
| `SHOPIFY_CLIENT_SECRET` | Shopify app client secret (used for OAuth client_credentials grant). Rotate per `Secrets_Registry.md` schedule. |
| `SHOPIFY_API_VERSION` | Pinned Shopify API version, e.g. `2025-01`. |
| `PRINTER_IP` | Local network IP of the receipt printer (stub uses this). |

See `.env.example` for the placeholder template.

## Local Dev (Windows)

OHMS reads `.env` only when running locally (via `python-dotenv`). Bootstrap
your `.env` from the Windows DPAPI-protected secrets store rather than typing
secrets in plaintext:

```powershell
# 1. Pull each secret from DPAPI into the local .env (PowerShell pseudocode)
$secrets = @("OHMS_API_TOKEN","SHOPIFY_STORE_URL","SHOPIFY_CLIENT_ID","SHOPIFY_CLIENT_SECRET","SHOPIFY_API_VERSION","PRINTER_IP")
foreach ($k in $secrets) {
    $v = Unprotect-DpapiSecret -Name $k    # your local helper
    Add-Content .env "$k=$v"
}

# 2. Run the server
python -m venv .venv
.venv\Scripts\Activate.ps1
pip install -r requirements.txt
python main.py
```

The server listens on `http://0.0.0.0:8080` by default. Probe with:

```bash
curl http://localhost:8080/health
# => OHMS OK
```

## Replit Deploy

1. Create a Replit project and import this folder.
2. In the Secrets pane, set every variable from `.env.example` (using the real
   values - never paste them into any committed file).
3. Confirm `.replit` shows `deploymentTarget = "reserved_vm"` and port
   `8080 -> 80`.
4. Deploy. The public URL is `https://ohms-server.crashzero9.replit.app`.
5. Verify both transports:
   ```bash
   curl https://ohms-server.crashzero9.replit.app/health
   curl -H "Authorization: Bearer $OHMS_API_TOKEN" \
        -H "Content-Type: application/json" \
        -d '{"jsonrpc":"2.0","method":"tools/list","id":1}' \
        https://ohms-server.crashzero9.replit.app/mcp
   ```

## Tool Registry

| Tool | Source | Notes |
|---|---|---|
| `get_order(order_id)` | Shopify Admin REST | `GET /orders/{id}.json` |
| `list_pending_orders()` | Shopify Admin REST | `GET /orders.json?status=open&limit=50` |
| `update_order_status(order_id, status)` | Shopify Admin REST | `PUT /orders/{id}.json` (sets tags) |
| `get_inventory_snapshot()` | Shopify Admin REST | `GET /inventory_levels.json?limit=50` |
| `get_doordash_orders_via_browser()` | Stub | Returns routing dict for browser-automation handoff. |
| `print_order_ticket(order_id)` | Stub | Reads `PRINTER_IP`; returns queued status. Driver pending. |

## Phase Status

- **Phase 1 (current):** MVP scaffold, Bearer auth, six tools (4 live + 2
  stubs), Replit Reserved VM deploy, basic pytest suite.
- **Phase 2 (planned):** OAuth 2.1 replacing static bearer, full DoorDash
  driver, real network printer driver, structured logging with secret
  redaction, rate limiting.

## Security Notes

- No secret values appear in any committed file.
- `Authorization` headers and full Shopify response bodies are never logged.
- `/health` returns only the literal string `OHMS OK` - no version, env, or
  path info.
- All `httpx` calls have an explicit 30-second timeout.