MEGAINFRA-WEBAPP/docs/setup/api-and-mcp-connections.md

232 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# MEGA INFRASTRUCTURE — API and MCP connection guide
Historical connection audit, **5 October 2026**, Africa/Johannesburg. This guide describes this checkout and the live checks saved alongside it.
**Remote MCP update:** See [the remote purchasing MCP guide](./remote-purchasing-mcp.md) for the implemented same-app endpoint, dedicated credentials, client settings and deployment status. The audit below describes the earlier local-only transport and preserves its verified API findings.
**Yes: the app has an HTTP API and a working read-only MCP server.** MCP runs as a local process and reads your hosted PocketBase database using existing credentials. Its current transport is **stdio**: there is no MEGA MCP URL, port, API key or OAuth login.
## Addresses and access
| Connection | Address / setting | Authentication |
| --- | --- | --- |
| Production app API | `https://mega.dash.apphub.co.za/api` | Login session or route-specific secret |
| Local app API | `http://localhost:6004/api` | Currently PIN fallback; ordinary routes do not require a session |
| PocketBase REST API | `https://pb.mega.infra.apphub.co.za/api` | PocketBase token in `Authorization` |
| MEGA MCP | `mega-infrastructure`, local stdio | PocketBase credentials from env file |
| MEGA MCP permissions | 27 read-only tools | Superuser database reads across all sites |
| n8n MCP, separate service | `https://wfdash.n8n.apphub.co.za/mcp-server/http` | Separate n8n MCP credential; not tested here |
`npm run dev` uses **6004**; `npm run start` uses **3000**. Production exposes HTTPS through the reverse proxy.
There is **no universal MEGA API key** or general API-key issuance route in this checkout. `MINIMAX_API_KEY` and `ANTHROPIC_API_KEY` are outbound AI-provider credentials. They are not needed to use the read-only MCP tools.
## Copy-ready MCP settings
These paths are correct for this computer. The repository and KIOXIA volume must be mounted when the client starts the server. Verification used Node **22.22.3** and the installed dependencies. Change the paths when using another computer.
| Field | Value |
| --- | --- |
| Name | `mega-infrastructure` |
| Transport | `stdio` |
| Command | `/Volumes/KIOXIA/.hermes/node/bin/node` |
| Arguments, in order | `--import`, `tsx`, `--env-file=/Volumes/EXT/DEVELOPMENT/MEGAIFRASTRUCTURE/.env.local`, `/Volumes/EXT/DEVELOPMENT/MEGAIFRASTRUCTURE/src/mcp/server.ts` |
| Working directory | `/Volumes/EXT/DEVELOPMENT/MEGAIFRASTRUCTURE` |
| Startup timeout | 20 seconds |
| Tool timeout | 60 seconds |
| Extra env variables | None; `.env.local` supplies credentials |
| Server URL / MCP API key | Leave blank |
### Codex TOML
Copy [mega-mcp.codex.toml](./mega-mcp.codex.toml) into your existing MCP configuration, preserving other servers:
```toml
[mcp_servers.mega-infrastructure]
command = "/Volumes/KIOXIA/.hermes/node/bin/node"
args = ["--import", "tsx", "--env-file=/Volumes/EXT/DEVELOPMENT/MEGAIFRASTRUCTURE/.env.local", "/Volumes/EXT/DEVELOPMENT/MEGAIFRASTRUCTURE/src/mcp/server.ts"]
cwd = "/Volumes/EXT/DEVELOPMENT/MEGAIFRASTRUCTURE"
startup_timeout_sec = 20
tool_timeout_sec = 60
```
Codex supports these fields in `~/.codex/config.toml` or a project `.codex/config.toml`. Its MCP settings UI supports choosing STDIO and entering the command. Save and restart the MCP connection after adding it. See [official MCP configuration documentation](https://learn.chatgpt.com/docs/extend/mcp?surface=cli).
### MCP JSON clients
[mega-mcp.json](./mega-mcp.json) contains the same command, arguments and working directory in `mcpServers` format, with no secret values. Client configuration filenames and support for `cwd` vary; the table above supplies each field individually.
The tested launch uses Node directly, keeping npm's script banner out of the protocol stream. The repository also provides `npm run mcp` for manual startup. `--import tsx` loads TypeScript without the tsx CLI's extra IPC listener.
After connecting, ask your client to **list sites**, **show purchase orders**, or **show pending verification**. It should discover 27 tools. The Next.js app does not need to be running for MCP; PocketBase must be reachable. Client configuration was prepared, but your personal MCP settings were not edited.
## Credentials: where each actual value lives
The values already exist in the linked, ignored local files. Commands below load them directly, so you do not need to copy secrets into a shareable document.
| Value | Location | Purpose and verification |
| --- | --- | --- |
| `NEXT_PUBLIC_POCKETBASE_URL` | [.env.local](../../.env.local), [cooler.env](../../cooler.env) | Hosted database URL; reachable |
| `POCKETBASE_ADMIN_EMAIL`, `POCKETBASE_ADMIN_PASSWORD` | Both files above | Server/MCP database login; deployment-file credentials worked in live MCP calls |
| `POCKETBASE_ADMIN_TOKEN` | `.env.local` | Preset superuser token; authenticated PocketBase GET passed |
| Production `N8N_INGEST_SECRET` | **`cooler.env`** | Header `x-ingest-secret`; production PO lookup passed |
| Local `N8N_INGEST_SECRET` | `.env.local` | **Still the literal template placeholder.** Local accepts it; production rejected it with 401. Use `cooler.env` for production integrations |
| App user email/password | Your existing named MEGA account | Login returns a `mega_session` cookie; successful named-user login was not live-tested |
| `RECEIPT_CORRECTION_SECRET` | `.env.local`; production value from deployment settings | Extra correction code for posted receipts, not a general API key; production value not verified |
| n8n MCP bearer token | Your existing n8n MCP credential/client settings | Separate from PocketBase and ingest credentials; not tested or copied here |
| AI provider keys | Server/provider credential settings | Outbound AI calls only |
The current PocketBase adapter requires **both email and password even when a preset token exists**. It uses `POCKETBASE_ADMIN_TOKEN` preferentially, otherwise logs in with email/password. A stale preset token is not automatically replaced by password login. See [src/lib/pb.ts](../../src/lib/pb.ts).
`cooler.env` is a deployment reference; `npm run dev` does not automatically load it. `.env.coolify` is a placeholder template. `.env.coolify.local` also retains the placeholder ingest secret. No credentials or deployment settings were changed during this audit.
## API examples
### Public health
```bash
curl --fail-with-body https://mega.dash.apphub.co.za/api/health
```
Expected: HTTP 200 with `status: "ok"`, `uptime`, `timestamp`. Health alone does not prove database access.
### Named-user app session
With `AUTH_MODE=users`, protected app routes read the **`mega_session` cookie**, not an `Authorization: Bearer ...` API key. Use a MEGA user login, not a PocketBase superuser login:
```http
POST https://mega.dash.apphub.co.za/api/auth/login
Content-Type: application/json
{"email":"YOUR_MEGA_USER_EMAIL","password":"YOUR_MEGA_USER_PASSWORD"}
```
A successful response includes `user`, `landing`, and `Set-Cookie: mega_session=...`. Keep the cookie in your API client's cookie jar, then call:
```http
GET https://mega.dash.apphub.co.za/api/dashboard-attention
Cookie: mega_session=YOUR_SESSION_TOKEN
```
The cookie is HttpOnly, SameSite=Lax, Secure in production, with a five-day browser lifetime. PocketBase checks token validity. Password-change requirements, roles and site assignments can deny requests; non-admin users are scoped to assigned sites, with additional route-specific checks.
Local PIN mode does **not** authenticate ordinary API requests: `getCurrentUser()` supplies the PIN admin identity without a cookie. The local dashboard GET returned 200 without credentials. Production returned 401 without a session. The browser PIN is not an API key.
### Production machine API
Run this from the repository directory. It loads the **tested production secret** from `cooler.env`; replace `YOUR_PO_NUMBER`:
```bash
node --env-file=cooler.env --input-type=module - <<'NODE'
const poNumber = "YOUR_PO_NUMBER";
const response = await fetch(
`${process.env.APP_URL}/api/purchase-orders/by-number/${encodeURIComponent(poNumber)}/items`,
{ headers: { "x-ingest-secret": process.env.N8N_INGEST_SECRET } },
);
console.log("HTTP", response.status);
console.log(await response.json());
NODE
```
Expected: 200 and `{ po, items }`; unmatched POs may return `po: null`. Missing/wrong secrets return 401. The live check looked up a real PO without retaining its contents in the report.
| Method | Route using `x-ingest-secret` | Purpose |
| --- | --- | --- |
| GET | `/api/purchase-orders/by-number/[poNumber]/items` | Read PO lines |
| GET | `/api/purchase-orders/[id]/delivery-reconciliation` | Read reconciliation snapshot |
| GET | `/api/delivery-notes/[id]/slip` | Read source slip |
| POST | `/api/purchase-orders/ingest` | Ingest PO data; changes records |
| POST | `/api/purchase-orders/process-batch` | Finalise PO extraction; changes records |
| POST | `/api/dn-batches/tick` | Process queued delivery documents; changes records and may call AI |
The ingest secret only authorizes handlers that check it. It does not replace the login cookie on ordinary routes. Receipt corrections additionally require operational authorization, the correction code and same-origin checks. Some Ask/action routes check Origin; `x-ask-user` is an Ask identity selector, not a general API key. General cross-origin browser API access is not configured in this checkout; use a server-side client or the app's own origin.
### Direct PocketBase REST API
PocketBase is a separate service. Send its **raw token** in `Authorization`, rather than the app cookie. See [PocketBase authentication documentation](https://pocketbase.io/docs/authentication/).
```bash
node --env-file=.env.local --input-type=module - <<'NODE'
const response = await fetch(
`${process.env.NEXT_PUBLIC_POCKETBASE_URL}/api/collections/sites/records?page=1&perPage=1&fields=id,name,project_code`,
{ headers: { Authorization: process.env.POCKETBASE_ADMIN_TOKEN } },
);
console.log("HTTP", response.status);
console.log(await response.json());
NODE
```
Superuser database access bypasses app business checks and site restrictions. The supplied MCP process is for the owner/admin; it does not authenticate individual app users. Ordinary PocketBase user-token access is subject to collection rules and was not verified here.
The [current route inventory](./api-route-inventory.md) lists **78 route files** with methods and links to source. The older [API reference](../api-reference.md) contains payload details, but some auth descriptions predate named-user login; current source and this guide take precedence.
## All 27 MCP tools
These were discovered through a live `tools/list`. Full descriptions and JSON input schemas are preserved in [connection-check-results.json](./connection-check-results.json).
| Tools | What they read |
| --- | --- |
| `list_sites`, `get_site_financials`, `get_site_trend` | Site totals and trends |
| `site_report`, `portfolio_report`, `problem_report`, `improvement_report` | Reports and findings |
| `vehicles_needing_attention`, `get_fuel_summary`, `get_vehicle_fuel_log` | Fleet and fuel |
| `get_pending_verification` | Unverified POs and delivery notes |
| `get_purchase_order`, `list_purchase_orders` | Purchase orders and lines |
| `get_delivery_note`, `list_delivery_notes`, `delivery_note_queue` | Delivery notes and queues |
| `top_workers`, `list_employees`, `attendance_today` | Workforce and attendance |
| `list_plant_hours`, `plant_equipment_summary` | Plant hours and summaries |
| `list_fixed_costs`, `list_income_claims`, `supplier_balances` | Costs, income and suppliers |
| `list_imports`, `rain_outlook`, `list_change_requests` | Imports, weather and logged requests |
Argument examples: `list_sites` takes `{}`; `list_employees` and `list_purchase_orders` accept `{ "limit": 1 }`; `get_site_financials` requires `{ "site": "SITE_NAME_OR_CODE_OR_ID" }`; `get_site_trend` additionally accepts `"granularity": "month"` or `"week"`.
Ask chat has separate action/write tools. [src/mcp/server.ts](../../src/mcp/server.ts) registers only the read-only tool set; Ask's actions are not exposed through this MCP server.
## Test results
| Check | Result |
| --- | --- |
| MCP initialize/discovery | Passed with both env files; v1.0.0, 27 tools |
| Live MCP reads | `list_sites`, `list_employees`, `list_purchase_orders`, `get_pending_verification` passed with both env files |
| PocketBase health and preset-token records GET | Both 200 |
| Local app health | 200 |
| Local dashboard GET without session | 200; PIN fallback |
| Production app health | 200 |
| Production dashboard GET without session | 401, `Sign in required` |
| PO lookup without secret, both hosts | Both 401 |
| Local PO lookup with `.env.local` secret | 200; placeholder accepted locally |
| Production PO lookup with `.env.local` secret | **401: credential mismatch** |
| Production PO lookup with `cooler.env` secret | **200: working production credentials** |
| Local `/api/mcp` GET | 404; no HTTP MCP route in this checkout |
| Production `/api/mcp` GET | 401 from login guard; does not establish an HTTP MCP endpoint |
| Focused automated tests | **58 passed in 7 files**, covering auth/login, route classification, health, batch tick, agent loads and read-only enforcement |
The [final local report](./local-connection-check-results.json) has **13 passed / 0 failed**. The [combined default-env report](./connection-check-results.json) has **17 passed / 1 failed**: the placeholder secret fails against production. The [deployment-env production report](./production-connection-check-results.json) has **12 passed / 0 failed**. These are connection checks, not exhaustive tests of every tool and operation.
Successful named-user login, data-changing API operations, n8n MCP authentication and AI-provider calls were not live-tested. Automatic approval review rejected the initial batch-processing endpoint probe because it could mutate queued documents. The final checker uses GET-only app requests and read-only MCP tools; the batch handler was covered by automated tests.
Reports contain schemas, status codes and response shapes, without secrets or business records. UTC timestamps on 4 October at about 22:25–22:27 correspond to **5 October** after midnight in Johannesburg.
## Repeat verification
The [saved checker](../../scripts/check-connections.mjs) loads an env file and prints secret-redacted JSON. App requests are GET-only and MCP tools read-only. With password-only PocketBase credentials, database authentication itself uses the normal login request.
```bash
# Local only, with the app running on 6004.
node scripts/check-connections.mjs
# Production with verified credentials — passed 12/12.
MEGA_CONNECTION_ENV_FILE=cooler.env node scripts/check-connections.mjs https://mega.dash.apphub.co.za
# Compare local credentials against both hosts — exposes the mismatch.
node scripts/check-connections.mjs http://127.0.0.1:6004 https://mega.dash.apphub.co.za
```
Repeat the focused automated tests with:
```bash
npm run test:run -- src/lib/agent/read-only.test.ts src/lib/agent/load.test.ts src/lib/auth/guard.test.ts src/lib/auth/proxy-path.test.ts src/app/api/health/route.test.ts src/app/api/auth/login/route.test.ts src/app/api/dn-batches/tick/route.test.ts
```
Failures produce a nonzero exit code. Run from a terminal with local socket/external network access. The checker changes neither env files nor deployed credentials.
For a URL-based MCP client on another device, the current server would need an authenticated HTTP transport or a supported remote-execution arrangement. This audit did not add one. Local MCP already reads the hosted production database.