docs: use existing app URL for remote MCP design

This commit is contained in:
DEls 2026-10-05 01:16:34 +02:00
parent 4970e61e70
commit 7ffe4eb94d

View file

@ -6,22 +6,22 @@ Status: ready for user review; product implementation has not started.
Cloud agents, local Codex scheduled tasks, Hermes and cloud Cowork should be able to connect to MEGA without relying on the owner's Mac being awake. Agents should read purchasing data, retrieve source documents, upload documents, create and edit purchase orders, match delivery notes to orders and lines, post receipts and update received/outstanding quantities. Agents must not delete business records or documents.
The user approved starting the authenticated Coolify service and expanded purchasing tools described in the preceding discussion. This specification makes that approach reviewable. Stock is scoped to receipt and outstanding PO quantities in this release: no site/warehouse stock-on-hand, consumption, transfers or stock adjustments are inferred. Those need a separate inventory specification if requested.
The user requested authenticated remote MCP access and expanded purchasing tools, then selected an endpoint inside the existing Next.js app using its existing public base URL. This specification records that architecture. Stock is scoped to receipt and outstanding PO quantities in this release: no site/warehouse stock-on-hand, consumption, transfers or stock adjustments are inferred. Those need a separate inventory specification if requested.
## First page: decisions to review
- Deploy a separate, continuously running MCP service in Coolify using HTTPS and Streamable HTTP. Preserve the existing local stdio connection.
- Add Streamable HTTP at `https://mega.dash.apphub.co.za/api/mcp` inside the existing Next.js app and Coolify container. Reuse its domain and HTTPS connection; no separate container, public port or MCP hostname is required. Preserve the existing local stdio connection.
- Use a dedicated credential for each agent, with read-only or read/write permissions. PocketBase credentials remain on the server. The first release is for owner-controlled agents with access to all sites; it does not claim per-site authorization.
- Allow direct authenticated business writes through named tools. Do not add a mandatory human confirmation screen to scheduled workflows. Receipt posting requires an explicit tool argument and the current matching preview, rather than silently posting during document reading or matching.
- Preserve documents, posted receipts and audit history. Reject business deletion, including indirect deletion inside called routines. Temporary lock cleanup is allowed and is not exposed as a tool.
- Treat matching, processing and posting as distinct operations. A match or an AI processing result does not book a receipt by itself.
- Build and test locally first. A hostname, certificate or production-ready claim is only made after the Coolify deployment and external connection tests succeed.
- Build and test locally first. The existing hostname is known; endpoint availability is only claimed after the updated app is deployed and external connection tests succeed.
## Present implementation and constraints
1. [src/mcp/server.ts](../../../src/mcp/server.ts) uses `StdioServerTransport` and registers only the 27 tools from [src/lib/agent/tools.ts](../../../src/lib/agent/tools.ts). It listens on no network port.
2. The app's Dockerfile packages a Next.js standalone server. It is not an MCP image and does not establish an HTTP MCP endpoint.
3. App routes authenticate through Next.js cookies and route-specific guards. Importing route handlers into an independent MCP process would couple it to Next.js request context and would not establish machine authorization. Shared operations must sit below both transports.
2. The app's Dockerfile packages a Next.js standalone server. It does not currently establish an HTTP MCP endpoint. The new route will be packaged in this same image.
3. App routes authenticate through Next.js cookies and route-specific guards. Calling cookie-authenticated route handlers from MCP would not establish machine authorization. Shared operations must sit below both transports.
4. PO add/edit handlers validate line ownership and reject verified orders, then use `computeItemAmount` and `recomputePoTotals`. Those rules remain authoritative.
5. DN entity/item resolution checks batch membership, site, supplier and PO/line ownership and records decisions/aliases. Those validations must be preserved.
6. [src/lib/dn-pipeline/post-line.ts](../../../src/lib/dn-pipeline/post-line.ts) supports snapshot-bound, deterministic line receipt posting with lease fencing and source-proof PDFs. Repeated calls return the existing booking rather than another receipt.
@ -36,9 +36,9 @@ The user approved starting the authenticated Coolify service and expanded purcha
```text
Cloud agent / Hermes / Cowork / scheduled agent
|
HTTPS /mcp + MCP credential
HTTPS /api/mcp + MCP credential
|
Independent MEGA MCP service
Existing MEGA Next.js app
authentication -> tool registry
|
Shared purchasing operations
@ -50,23 +50,23 @@ Local stdio client -> same server factory and purchasing operations
App API handlers -> shared purchasing operations after their existing guards
```
Create a reusable server factory in `src/mcp/create-server.ts`. Keep transport lifecycle out of business handlers. The stdio entry point remains available; the HTTP entry point is `src/mcp/http-server.ts`. Use the installed MCP SDK's Streamable HTTP transport, not a hand-written JSON-RPC implementation. Stateless request transport is preferred: business concurrency and idempotency live in PocketBase, not in a process-wide MCP session map.
Create a reusable server factory in `src/mcp/create-server.ts`. Keep transport lifecycle out of business handlers. The stdio entry point remains available; the HTTP entry point is `src/app/api/mcp/route.ts`, using the Node.js runtime and the installed SDK's `WebStandardStreamableHTTPServerTransport` for Next.js Request/Response handling. Use the installed MCP SDK's Streamable HTTP transport, not a hand-written JSON-RPC implementation. Stateless request transport is preferred: business concurrency and idempotency live in PocketBase, not in a process-wide MCP session map.
The service uses its own runtime image, `Dockerfile.mcp`, and `docker-compose.mcp.yml`. Package only the runtime source/assets needed by its imports, dependencies and the PDF rasterisation binaries used by the staged pipeline. Run as a non-root user on Node 22. Bind to `0.0.0.0` inside the container; Coolify exposes HTTPS. Container health is separate from authenticated MCP access.
Reuse the existing Next.js standalone Dockerfile and Coolify deployment. Verify that standalone tracing includes the SDK and purchasing dependencies; preserve the existing PDF rasterisation binaries. The route shares the app's internal listener and Coolify HTTPS proxy. Reuse `/api/health` for container liveness; authenticated MCP initialization is a separate readiness check.
Proposed public hostname: `mcp.mega.infra.apphub.co.za`; proposed path: `/mcp`. This address is not currently deployed. The hostname may be changed in configuration without changing tools.
Target URL: `https://mega.dash.apphub.co.za/api/mcp`. The dashboard domain already exists; this MCP route is not currently deployed. Add the exact `/api/mcp` route and bounded document/job subpaths to the proxy's machine-auth classification so the cookie login guard does not intercept them. Each machine handler independently validates its MCP bearer credential.
## HTTP access and credentials
- `POST /mcp`: authenticated MCP initialize, discovery and calls.
- `GET /mcp`: follows the selected SDK transport's supported behavior; unsupported stream operations return the protocol-appropriate status.
- `DELETE /mcp`: do not support business deletion. If a later stateful transport uses this method, it may terminate a protocol session only; that is not a record deletion capability.
- `GET /health`: public minimal liveness response containing no credentials, records or detailed dependency errors.
- `POST /api/mcp`: authenticated MCP initialize, discovery and calls.
- `GET /api/mcp`: follows the selected SDK transport's supported behavior; unsupported stream operations return the protocol-appropriate status.
- `DELETE /api/mcp`: do not support business deletion. If a later stateful transport uses this method, it may terminate a protocol session only; that is not a record deletion capability.
- `GET /api/health`: public minimal liveness response containing no credentials, records or detailed dependency errors.
- Authentication is checked before constructing tool handlers or contacting PocketBase. Missing/invalid credentials return 401.
- Use SHA-256 token hashes and constant-time comparison. `MCP_CLIENTS_JSON` supplies objects with `id`, `tokenHash` and `permissions`, where permissions are `read` or `read,write`. No actual credential appears in committed configuration. Invalid or placeholder configuration prevents HTTP startup.
- Use SHA-256 token hashes and constant-time comparison. `MCP_CLIENTS_JSON` supplies objects with `id`, `tokenHash` and `permissions`, where permissions are `read` or `read,write`. No actual credential appears in committed configuration. Invalid or placeholder MCP configuration makes MCP routes return a setup error (503) before database access; it does not stop the dashboard from starting.
- A read credential sees read tools only and cannot invoke writes by submitting an undiscovered tool name. A write credential includes reads and named business writes. Every tool invocation rechecks its required permission.
- Initial credentials grant all-site owner access. Do not offer a `siteIds` config option until all affected loaders and document paths enforce it.
- Configure `MCP_PUBLIC_URL`, `MCP_PORT`, `MCP_ALLOWED_ORIGINS`, PocketBase variables and optional AI variables. Honor deployment-provided env; `.env.local` is a development convenience only.
- Configure `MCP_PUBLIC_URL=https://mega.dash.apphub.co.za/api/mcp`, `MCP_ALLOWED_ORIGINS`, PocketBase variables and optional AI variables. Honor deployment-provided env; `.env.local` is a development convenience only.
- Validate a supplied Origin against the configured allowlist, validate the expected host, bound request sizes and configure proxy timeouts for processing calls. Server-to-server clients may omit Origin.
- Issue separate credentials for Codex, Hermes and Cowork so one may be revoked independently. OAuth is outside the initial fixed-credential release; test each chosen client's request-header support before marking it connected.
@ -90,7 +90,7 @@ New purchasing tools use Zod schemas with unknown keys rejected. Record IDs are
Typed document references cover a PO original, receipt slip/proof PDF, DN batch source, source page image and a candidate's assembled proof PDF. Resolve filenames from database records and encode path components. Do not accept arbitrary upstream URLs or caller-selected filesystem paths. Candidate proof PDFs use the selected source pages in saved order.
HTTP document downloads require the same MCP credential and stay on the MCP service origin. A URL alone does not grant access. Downloads are streamed with byte limits and correct Content-Type/Content-Disposition. Stdio clients receive bounded resource content because they have no HTTP service URL by default. Source documents stay immutable; an upload creates a new source rather than overwriting/deleting an old one.
HTTP document downloads use `/api/mcp/documents/...`, require the same MCP credential and stay on the existing app origin. A URL alone does not grant access. Downloads are streamed with byte limits and correct Content-Type/Content-Disposition. Stdio clients receive bounded resource content because they have no HTTP service URL by default. Source documents stay immutable; an upload creates a new source rather than overwriting/deleting an old one.
### Create, edit, upload and matching tools
@ -137,32 +137,32 @@ Shared helper use must not silently weaken the no-delete boundary. Select the ne
- Serialize PO header/item/verification edits through a shared resource lease used by both relevant app handlers and MCP operations. A lease used only by MCP would not prevent concurrent web edits.
- Require the PocketBase batch/locking schema when writes need it. If unavailable, fail closed with a setup error instead of falling back to unfenced writes.
- Do not describe multi-chunk PocketBase batches as atomic. Keep one business commit within the transaction's supported mutation count; reject oversized manual PO creations with a clear limit, or split them into explicitly observable draft operations before final verification.
- Long batch processing uses durable `mcp_jobs` records, with a create-based claim, ownership fence and bounded retries. A small worker in the MCP deployment runs `runStagedBatch` through the no-delete client; provider keys are optional for read tools but required for AI processing. Start the worker only when writes and required schema are configured.
- A disconnect does not enqueue the same job twice or start a second receipt posting. Workers resume status from PocketBase after a container restart. No job invokes `/api/dn-batches/tick` with the shared ingest secret or changes global production pipeline flags.
- Long batch processing uses durable `mcp_jobs` records, with a create-based claim, ownership fence and bounded retries. An authenticated `POST /api/mcp/jobs/tick` handler claims a bounded amount of work and runs the staged pipeline through the no-delete client. An external scheduler, such as the existing n8n instance, calls this handler periodically using a dedicated processing credential; no additional app container is required. Jobs remain queued until this schedule is configured. Provider keys are optional for read tools but required for AI processing. Do not rely on an untracked background promise after a Next.js response.
- A disconnect does not enqueue the same job twice or start a second receipt posting. Subsequent scheduled ticks resume status from PocketBase after a container restart. No job invokes `/api/dn-batches/tick` with the shared ingest secret or changes global production pipeline flags.
## Schema and runtime setup
Provide an idempotent migration for `mcp_operations`, `mcp_jobs` and MCP resource locks, including unique operation/resource keys and server-only access rules. Use a development/staging database to verify it before production application. Retain existing DN transaction/locking schema requirements. Audit records preserve before/after business snapshots or references without source file bytes or credentials.
Use local-only `.env.mcp.local` for developer credentials and `.env.mcp.example` for committed placeholders. Coolify supplies actual runtime variables. Provide token generation/hash instructions and separate read-only and write-enabled client examples for Codex, Hermes and Cowork. Avoid reusing the n8n ingest secret or exposing PocketBase administrator credentials to agents.
Use the app's ignored `.env.local` for developer credentials and a committed MCP environment example containing placeholders only. Coolify supplies actual runtime variables. Provide token generation/hash instructions and separate read-only and write-enabled client examples for Codex, Hermes and Cowork. Avoid reusing the n8n ingest secret or exposing PocketBase administrator credentials to agents.
The MCP image must explicitly exclude `cooler.env`, `kilo.json`, `.env*` secrets, local agent settings, Git metadata and nested checkouts. Verify the build context and image contents; do not rely on the current dashboard `.dockerignore`, which does not explicitly exclude every known local secret file.
The existing app image must explicitly exclude `cooler.env`, `kilo.json`, `.env*` secrets, local agent settings, Git metadata and nested checkouts. Update the existing `.dockerignore` and verify the build context and image contents; it does not currently exclude every known local secret file.
## Delivery sequence
1. Implement server factory, authenticated HTTP transport, health endpoint, isolated client policy, read-only registry and HTTP/stdio integration tests. This produces a runnable remote service locally without enabling writes.
1. Implement server factory, authenticated Next.js HTTP route, existing health integration, isolated client policy, read-only registry and HTTP/stdio integration tests. This produces a runnable HTTP endpoint in the local app without enabling writes.
2. Implement document references, downloads/resources and paginated purchasing detail reads. Prove protected document access and correct file bytes.
3. Add shared validated PO/supplier operations, journal/locks and app-route compatibility tests. Enable creation, updating and verification with no delete.
4. Add DN immutable upload/candidate/grouping/matching operations. Preserve revision and provenance rules.
5. Add preview processing, durable jobs, line receipt posting/corrections and retry/concurrency tests. Verify received/outstanding quantities after posting.
6. Package Coolify runtime and worker, finalize client settings/connection documentation, and run the full checks. Stage and review an end-to-end workflow before exposing production write credentials.
7. Deploy only after deployment access, host routing and actual runtime credentials are available. Confirm external HTTPS initialize/discovery, authenticated file access and allowed read/write scope. A prepared image/configuration is not reported as a deployed URL.
6. Verify the existing Coolify app image and configure the authenticated job schedule, finalize client settings/connection documentation, and run the full checks. Stage and review an end-to-end workflow before exposing production write credentials.
7. Deploy the updated app after deployment access and actual MCP runtime credentials are available; reuse the existing host routing. Confirm external HTTPS initialize/discovery, authenticated file access and allowed read/write scope. A prepared image/configuration is not reported as a deployed URL.
## Acceptance tests
- Existing stdio read tools remain callable; application read-only enforcement tests continue to pass without broad write-module exemptions.
- HTTP initialize/list/read succeed with a valid read key; absent/incorrect/revoked keys fail before database access.
- A read key cannot discover or invoke writes; malformed credential config prevents startup.
- A read key cannot discover or invoke writes; malformed MCP credential config returns 503 without disabling the dashboard.
- Supplied untrusted Origin/Host is rejected; valid server clients can connect without Origin.
- Any attempted business record delete, batch delete, file removal or legacy promotion call fails without deleting anything. Infrastructure lock cleanup remains functional.
- Document download returns the expected filename/MIME/bytes for POs, source slips, batch sources/pages and candidate proof PDFs; missing documents and wrong references return meaningful errors. No response includes admin tokens or upstream signed URLs.
@ -178,6 +178,6 @@ The MCP image must explicitly exclude `cooler.env`, `kilo.json`, `.env*` secrets
## Deployment readiness and remaining inputs
There is no Coolify management connector available in this session's tool inventory. Implementation, tests and deployment files can be completed locally; actual deployment may require access through the existing deployment workflow or the user's Coolify session. The proposed domain also needs DNS/routing access. Do not request that access before a runnable, tested service is ready to deploy.
There is no Coolify management connector available in this session's tool inventory. Implementation, tests and deployment files can be completed locally; actual deployment may require access through the existing deployment workflow or the user's Coolify session. No new DNS entry or public port is needed for the selected same-app route. Do not request deployment access before a runnable, tested endpoint is ready to deploy.
The remaining product question is whether a separate stock-on-hand ledger is desired. It does not block remote access, document handling or PO receipt functionality and is not silently added to this implementation.