# 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.