LIMU Portal MCP
# LIMU Portal MCP
Model Context Protocol server for LIMU Portal operational and finance data. It supports the original local stdio/MySQL mode and a Vercel HTTP mode that authenticates with LIMU OAuth and calls LIMU Portal APIs.
## Exposed Data
- Clients, including editable profile records plus cargo, package, shipment, invoice, payment, order-form, lead, KYC, query, and mobile-account summaries.
- Cargo records, tracking numbers, clients, shipment links, package totals, and cargo logs, with confirmation-gated cargo creation in Vercel HTTP mode.
- Cargo package groups, package units, and stage-check summaries, with confirmation-gated package creation in Vercel HTTP mode.
- Shipments, calendars, status updates, cargo totals, and budget summaries.
- Monthly budgets, detailed budget entries, and controlled purchase scheduling.
- Shipment customs budgets and shipping budgets, including income rows, expense rows, and budget logs.
- Requisitions, requisition items, payment vouchers, voucher items, payment proofs, and controlled approval/payment actions.
- Client profile reports, lead reports, order form reports, import product reports, leave applications, and timesheet reports.
## Setup
```bash
cd /Applications/XAMPP/xamppfiles/htdocs/limu/mcp
cp .env.example .env
npm install
npm run smoke
```
## Vercel HTTP MCP
The Vercel entrypoint is:
```text
/api/mcp
```
It requires an OAuth bearer token issued by the LIMU Portal OAuth provider. The remote server validates tokens through:
```text
GET /Api/v1/oauth/userinfo/
```
Then each tool forwards that same bearer token to the relevant LIMU Portal API endpoint, keeping portal permissions as the source of truth.
The server publishes OAuth Protected Resource Metadata at:
```text
/.well-known/oauth-protected-resource
```
The portal publishes authorization server metadata and supports dynamic client registration, so Codex can register its temporary local callback automatically. Manual callback registration is not required.
Set this Vercel environment variable:
```env
LIMU_PORTAL_BASE_URL=https://portal.limu.co.mw
```
Then deploy:
```bash
npm run build
```
Vercel will serve the MCP route over Streamable HTTP through `mcp-handler`.
Add the deployed server to Codex and authenticate it:
```bash
codex mcp add limu_mcp --url https://limu-mcp.vercel.app/api/mcp
codex mcp login limu_mcp
```
Run `npm run smoke:oauth` to verify protected-resource metadata and audience checks locally.
### Cargo writes through the portal
- Cargo and package creation require the `cargo:write` OAuth scope plus the employee's Cargo create permission.
- Cargo merge and shipment assignment require `cargo:write` plus Cargo edit permission.
- Merge and assignment default to dry-run previews. Execution requires an unchanged SHA-256 preview token, explicit confirmation, and a stable idempotency key.
- Merges accept only `Created`, unassigned cargo owned by one client. Source cargo records are deleted after their related records are moved into the approved primary cargo.
- Shipment assignment accepts only `Created`, unassigned cargo and changes its status to `Booked`.
The remote migration currently covers OAuth-protected health/userinfo, clients, cargo, cargo packages, shipments, monthly budgets, budget reports, purchase schedules, imports-and-orders, import-product, leads, client-profile reports, and a controlled QuickBooks accounting slice. The leads report returns conversion, source, segment, tag, and pipeline analytics while enforcing the viewer's portal report permissions. The client-profile report summarizes clients active in a selected cargo-activity period, including demographics, tiers, and relations-officer coverage. Requisition, payment voucher, and leave tools remain registered with explicit "portal endpoint pending" responses until their matching portal endpoints are added.
### QuickBooks through the portal
The portal is the sole owner of the QuickBooks OAuth connection and keeps its Intuit tokens in the portal database. The MCP never receives or returns those credentials.
- Read tools require the `quickbooks:read` OAuth scope and an Accounts, Customs, or Payment Vouchers view permission.
- Invoice, bill, customer-payment, and bill-payment tools require `quickbooks:write` plus Accounts, Customs, or Payment Vouchers management permission.
- Financial write tools default to `dryRun: true`; a real post requires both `dryRun: false` and `confirm: true`.
- Every post requires a stable `sourceReference` and `idempotencyKey`. The portal records a local audit row and sends a matching QuickBooks `requestid` to prevent accidental duplicate postings.
- Direct journal entries, updates, and deletes are deliberately not exposed.
## Local Stdio MCP
Use a least-privilege MySQL user. A read-only user is enough for reporting tools, but write tools need narrowly scoped `UPDATE` access for `Clients`, `monthly_budget_entry`, and `leave_applications`, `INSERT`/`UPDATE`/`DELETE` access for monthly budget schedule splits, `UPDATE`/`INSERT` access for leave logs, requisitions, requisition logs, payment vouchers, and budget spend tables, plus `DELETE` access for eligible requisitions, requisition items/logs, payment vouchers, voucher items, and voucher proof rows.
Write tools require `confirm: true`; use `dryRun: true` first to preview the exact proposed change.
Client auth secrets, token hashes, KYC link tokens, and government ID numbers are intentionally omitted from client tool responses.
## Run
```bash
cd /Applications/XAMPP/xamppfiles/htdocs/limu/mcp
npm start
```
Most MCP clients should launch it over stdio. Example client config:
```json
{
"mcpServers": {
"limu-portal": {
"command": "node",
"args": [
"/Applications/XAMPP/xamppfiles/htdocs/limu/mcp/src/server.js"
],
"env": {
"LIMU_DB_HOST": "127.0.0.1",
"LIMU_DB_PORT": "3306",
"LIMU_DB_NAME": "limutradee",
"LIMU_DB_USER": "root",
"LIMU_DB_PASSWORD": ""
}
}
}
}
```
## Tools
- `limu_health`
- `limu_get_clients`
- `limu_get_client`
- `limu_update_client`
- `limu_list_cargo`
- `limu_get_cargo`
- `limu_create_cargo`
- `limu_merge_cargo`
- `limu_assign_cargo_shipment`
- `limu_sync_cargo_package_count`
- `limu_get_cargo_action_audit`
- `limu_list_packages`
- `limu_get_package`
- `limu_create_package`
- `limu_list_shipments`
- `limu_get_shipment`
- `limu_get_imports_and_orders_report`
- `limu_get_import_product_report`
- `limu_list_monthly_budgets`
- `limu_get_monthly_budget`
- `limu_get_budget_report`
- `limu_list_purchase_schedule`
- `limu_schedule_budget_purchase`
- `limu_list_shipment_budgets`
- `limu_get_shipment_budget`
- `limu_list_customs_budgets`
- `limu_get_customs_budget`
- `limu_list_shipping_budgets`
- `limu_get_shipping_budget`
- `limu_list_requisitions`
- `limu_get_requisition`
- `limu_review_requisition`
- `limu_delete_requisition`
- `limu_list_payment_vouchers`
- `limu_get_payment_voucher`
- `limu_review_payment_voucher`
- `limu_delete_payment_voucher`
- `limu_mark_payment_voucher_paid`
- `limu_list_client_profile_reports`
- `limu_get_client_profile_report`
- `limu_list_lead_reports`
- `limu_get_lead_report`
- `limu_list_order_form_reports`
- `limu_get_order_form_report`
- `limu_list_leave_applications`
- `limu_review_leave_application`
- `limu_list_timesheet_reports`
- `limu_list_warehouse_timesheet_reports`
All tools return JSON text payloads and list tools enforce a configurable `limit` cap.
TDQS
Scored across 39 tools
Most tools have clearly distinct purposes, but the three budget-related get/list pairs (customs, shipping, shipment) are very similar and could cause confusion, though descriptions help differentiate.
All tools follow a consistent 'limu_verb_noun' pattern in snake_case, with the minor exception of 'limu_health' which is still clear and fits the convention.
With 39 tools, the set is well beyond the recommended 3-15 range. While the domain is broad, the server would benefit from consolidating similar tools (e.g., unifying budget endpoints).
The server covers a wide range of query and management operations, but lacks create and update for many core entities (e.g., cargo, shipments), suggesting incomplete lifecycle coverage for autonomous agents.