lifesaver-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@lifesaver-mcpPull the work order list report for last month."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
lifesaver-mcp
Pull SSRS "Work Order List" reports from Lifesaver Software (lsscloud.com) as
structured data, and track customer-retention KPIs on top of the accumulated
history. Three phases (see spec.md and dashboard/DESIGN.md):
Phase 1 — client + API (
lifesaver/): authenticates, runs the 3-step ReportViewer scrape, parses the CSV. Usable as a library (LifesaverClient) or a local FastAPI service.Phase 2 — MCP server (
mcp_server/): exposes the report as an MCP tool, over stdio locally or streamable-http when deployed (e.g. a claude.ai custom connector). Calls Phase 1's client in-process. Deployed to Cloud Run — see DEPLOY.md.Phase 3 — retention warehouse + dashboard (
warehouse/,dashboard/): accumulates every pull into a local SQLite warehouse (the source only keeps a rolling ~36 months, and the KPIs need full per-customer history), computes five retention KPIs, and renders a self-contained HTML dashboard published to a GCS static site. See warehouse/README.md.
Layout
Path | What |
| the 3-step scrape as a class; one session, re-login + retry on expiry |
| CSV bytes → typed rows (BOM, |
| per-report config (page path + date-field control IDs); WorkOrderList only |
| pydantic row/response schemas |
| FastAPI app (local dev; not deployed) |
| MCP server: one tool, |
| Phase 3 backend: raw landing → |
| reads |
| publish the rendered site to GCS / full daily cycle (pull → sync → publish) |
| original standalone reference script (still runnable); also has |
| bulk-pull invoices as PDFs ( |
| pull the full customer list as CSV (every customer, not scoped to any date range) |
| one-off recon of the other SSRS report pages (parked — see its README); also has |
| one-image build + Cloud Run deploy for the remote (claude.ai) MCP setup |
| on push to |
| offline suite: fake session + saved HTML/CSV fixtures, plus the warehouse pipeline tests |
Related MCP server: connectwise-command-mcp
Setup
python3 -m venv .venv
.venv/bin/pip install -r requirements-dev.txt # runtime + pytest
cp .env.example .env # fill in LIFESAVER_USERNAME / LIFESAVER_PASSWORD(requirements.txt alone is the container runtime set.)
Run the API
.venv/bin/uvicorn lifesaver.api:app --port 8000GET /reports/work-order-list?start=2025-08-01&end=2025-08-31→ JSONadd
&format=csvfor the raw exportinteractive docs at
http://localhost:8000/docs
Run the MCP server (Phase 2)
Does not need the Phase 1 API — it runs the scrape in-process. It just needs
LIFESAVER_USERNAME / LIFESAVER_PASSWORD (from .env or the environment).
Locally (stdio). .mcp.json is already wired for Claude Code — approve the
lifesaver server. Or by hand:
.venv/bin/python -m mcp_server.serverRemote (streamable-http) — for a claude.ai custom connector:
MCP_TRANSPORT=streamable-http MCP_AUTH_TOKEN=$(openssl rand -hex 32) \
.venv/bin/python -m mcp_server.server
# MCP at http://localhost:8080/mcp (needs Authorization: Bearer <token>)
# health at http://localhost:8080/healthDeploying this to Cloud Run + GHCR: DEPLOY.md.
One tool: get_work_order_list_report(start_date, end_date) (ISO dates) → parsed
work-order rows.
Retention warehouse + dashboard (Phase 3)
export LIFESAVER_USERNAME=... LIFESAVER_PASSWORD=...
.venv/bin/python -m warehouse.job backfill # full history, month by month, resumable
.venv/bin/python -m warehouse.job kpis # eyeball against the baselines
.venv/bin/python -m dashboard.build # -> dashboard/index.htmlFull CLI and layer model: warehouse/README.md. Design,
KPI definitions, and open questions: dashboard/DESIGN.md.
warehouse.db, warehouse_raw/, and the rendered dashboard/index.html are all
gitignored — the system of record is a GCS bucket (see DESIGN.md §15).
Standalone script (no server)
export LIFESAVER_USERNAME=... LIFESAVER_PASSWORD=...
.venv/bin/python lifesaver_report_pull.py --start 8/1/2025 --end 8/31/2025 --out aug.csvPulling invoices as PDFs
A single invoice (lifesaver_report_pull.pull_invoice(session, invoice_number))
hits a different endpoint than WorkOrderList — /Reports/Invoice/ needs no
postback, but the human-readable "Invoice #" must first be resolved to an
internal id via a separate POST (resolve_invoice_id()) before it works;
using the human number directly renders a silently-empty PDF ($0.00
everywhere, #Error in place of the invoice number). See
spec.md → "Invoices — a separate, simpler flow" for how both of
these were confirmed.
Bulk-pulling, from a WorkOrderList CSV export's invoiceNumber column or a
plain list:
export LIFESAVER_USERNAME=... LIFESAVER_PASSWORD=...
.venv/bin/python scripts/pull_invoices.py --csv aug.csv --out-dir invoices
.venv/bin/python scripts/pull_invoices.py --ids 584,591,602 --out-dir invoicesWrites invoices/invoice_<id>.pdf per invoice, one login/session for the
whole batch, a delay between requests (--delay, default 1.5s), and one
invoice failing doesn't stop the rest.
Pulling the customer list
Don't scrape it from the invoice PDFs (name + cell phone only, no
email/address, one request per invoice) — the report catalog has a report
built for exactly this. Filter: "No filter, show all customers." returns
every customer in the database with real columns (name, email, phone,
address, first/last purchase, total spend, ...), not scoped to any date
range. See spec.md → "Customer list — a report built for exactly
this, once found" for how that was confirmed.
export LIFESAVER_USERNAME=... LIFESAVER_PASSWORD=...
.venv/bin/python scripts/pull_customers.py --out customers.csvOne session per user
LifeSaver only allows one active session per user (license-limited). If a run
doesn't log out, the next login is blocked with UserAlreadyLoggedIn. The client
handles this automatically: it terminates its own stale session (never another
user's) and retries. To clear one manually:
LIFESAVER_USERNAME=... LIFESAVER_PASSWORD=... .venv/bin/python scripts/terminate_my_sessions.pySet LIFESAVER_TERMINATE_OWN_SESSION=false to disable the auto-clear.
Tests
.venv/bin/python -m pytest -qOffline only — no network, no credentials, no GCP. The fixtures in
tests/fixtures/ approximate the live responses; export_sample.csv is a real
capture. After any live run, refresh the HTML fixtures from real responses to
make the suite true regression coverage.
This server cannot be deployed
Maintenance
Related MCP Connectors
Provide seamless access to Appfolio Property Manager Reporting API through a standardized MCP serv…
LegiScan MCP — wraps the LegiScan API (api.legiscan.com)
CareerOneStop MCP — wraps the CareerOneStop Web API (U.S. Dept. of Labor)
BatchData MCP — wraps the BatchData property-data + skip-trace API (batchdata.com)
Related MCP Servers
- FlicenseNot gradedqualityDmaintenancePulls golf data from Arccos, GHIN, and 18Birdies, and generates interactive HTML and PDF round reports with shot maps and stats via local MCP tools.-
- AlicenseNot gradedqualityBmaintenanceExposes the ConnectWise Command (Continuum) ITSupport247 Reporting API as MCP tools, enabling querying of sites, devices, patches, antivirus, performance, and more.Apache 2.0
- FlicenseNot gradedqualityCmaintenanceMCP server for Bvoip / 1Stream that exposes call-reporting, phone-status, and CRM-extension-mapping endpoints as MCP tools.-
- AlicenseNot gradedqualityDmaintenanceMCP server for read-only queries and reports from the Astrea (Aurum) legal software. Enables retrieving case information by client, new cases per month, tags, financials (fees/expenses), schedule (hearings, meetings, deadlines, tasks), and case updates for client reports.12MIT