QMSCloud Reporting MCP Server
by koosoftware
README.md
# QMSCloud Reporting MCP Server — Node.js (Metabase)
Queries pre-built **Metabase cards** for QMSCloud and exposes them to AnythingLLM:
```
AnythingLLM (host) -> Ollama / Qwen -> this MCP server -> Metabase card JSON
```
Sibling of `../mcpreport` (which talks to the QMS700i servlet). The difference:
this server has **no login flow** — Metabase authenticates with a static
`x-api-key` header, and each report is a saved Metabase card queried via
`POST /api/card/<id>/query/json`.
## Tools
- `get_report(report, start_date?, end_date?)` — fetch and return card rows.
`workspace_slug` is injected by AnythingLLM (do not pass it manually).
- `list_reports()` — list available report keys.
Currently available reports: `ticket_summary` (card 40). Appointment, rating,
etc. will be added to the `CARDS` catalog in `metabase-core.js`.
## How it works
1. AnythingLLM injects `workspace_slug` on every call. The slug encodes the
tenant as `<companyCode>` or `<companyCode>___<branchCode>` — split on the
**first triple underscore** `___` (codes themselves are hyphen-slugs, e.g.
`branch-001`).
2. The server resolves the tenant **dynamically from Metabase** (no hardcoded
map), caching the lookups for 5 minutes:
- Company: POST card **41**, slugify each `CompanyCode`, match the company
part → `CompanyId`. No match → `invalid company code`.
- Branch (only if present): POST card **42**, slugify each `BranchCode`,
match the branch part **and** `CompanyId` (branch codes repeat across
companies) → `BranchId`. No match → `invalid branch code`.
- So `demo` → DEMO company, whole-company view; `demo___kl001` → DEMO + KL001
branch; `qc___branch-001` → QC + Branch 1. New companies/branches work with
no code change.
3. The model supplies `start_date` / `end_date` from the user's intent.
4. The server POSTs the Metabase `parameters` array (start_date, end_date,
company_id, and branch_id when present) to the ticket card and returns rows.
## 1. Install
Requires Node.js 18+ (built-in `fetch`).
```bash
cd /Users/koo/Desktop/Claude/Projects/QMS/metabase-mcpreport
npm install
```
## 2. Quick local test (no AnythingLLM)
The API key is not hardcoded — export it first:
```bash
export METABASE_API_KEY='mb_...your key...'
node test-ticket-summary.js # demo, month-to-date
node test-ticket-summary.js demo 2026-06-11 2026-06-30
```
## 3. Register in AnythingLLM
Add to `anythingllm_mcp_servers.json` (absolute paths):
```json
{
"mcpServers": {
"qmscloud-report": {
"command": "node",
"args": ["/Users/koo/Desktop/Claude/Projects/QMS/metabase-mcpreport/server.js"],
"env": {
"METABASE_BASE_URL": "http://54.251.164.99:7777",
"METABASE_API_KEY": "mb_...PASTE_KEY..."
}
}
}
}
```
Windows note (same gotcha as `../mcpreport`): if `C:\Program Files\nodejs\node.exe`
fails with `ENOENT`, use the 8.3 short path `C:\\PROGRA~1\\nodejs\\node.exe`.
## 4. Set the workspace system prompt
Paste the contents of `system_prompt.txt` into the AnythingLLM workspace **system
prompt**. It tells the model when/how to call `get_report`. Tools only fire in
`@agent` mode.
`METABASE_API_KEY` is **required** and is not hardcoded (kept out of source
control). Set it in the `env` block above. `METABASE_BASE_URL` defaults to
`http://54.251.164.99:7777` if omitted.
## Configuration reference
- Tenant resolution is dynamic (cards 41/42) — no slug map to maintain. Env
overrides: `METABASE_COMPANY_CARD_ID` (41), `METABASE_BRANCH_CARD_ID` (42),
`METABASE_LOOKUP_TTL_MS` (300000). Test with `node test-lookup-cards.js`.
- `CARDS` (metabase-core.js) — report key → Metabase card id + params.
## Notes
- Tools fire only in AnythingLLM `@agent` mode.
- Treat the Metabase API key as a secret; keep this config out of git.
TDQS
A4/5.0
Scored across 2 tools
Disambiguation5/5
The two tools are clearly distinct: one lists available reports, the other fetches a specific report. No ambiguity.
Naming Consistency5/5
Both tools follow a consistent verb_noun pattern with snake_case: list_reports and get_report.
Tool Count4/5
For a focused reporting server with only one report type currently available, two tools (list and get) are appropriate and efficient.
Completeness3/5
Covers the essential operations for its narrow domain (listing and retrieving a report). However, it lacks support for other expected reports or more advanced filtering, limiting completeness.
Maintenance
ActivityStale
ResponsivenessNo issues