233 lines
16 KiB
Markdown
233 lines
16 KiB
Markdown
|
|
# 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.
|