Content
# ServiceTitan MCP Server
[](https://github.com/montrellcruse/servicetitan-mcp/actions/workflows/ci.yml)
[](https://www.npmjs.com/package/@rowvyn/servicetitan-mcp)
[](LICENSE)
[](https://modelcontextprotocol.io)
[](#tool-catalog)
[](#tool-catalog)
The only MCP server for the ServiceTitan API — 483 tools across 15 domains, plus 10 intelligence tools that turn raw API data into business decisions.
Built by [**Rowvyn**](https://rowvyn.com) — [See the case study →](https://rowvyn.com/case-studies/servicetitan-mcp)
<p align="center">
<em>Connect any AI assistant to ServiceTitan's full API — CRM, dispatch, invoicing, payroll, and operational intelligence — through the Model Context Protocol.</em>
</p>
---
## Why
- **ServiceTitan has no official MCP server** or developer tooling beyond REST docs
- **Raw API access is friction-heavy** — OAuth token management, module-prefix routing, pagination, and response parsing all fall on you
- **This server handles all of that** and adds 10 intelligence tools that aggregate multiple endpoints into operational insights
- **Dashboard-matched revenue** — verified on production data using the same Reporting API source that powers ST's own dashboard
---
## Features
- **483 tools across 15 domains** — CRM, dispatch, accounting, payroll, inventory, marketing, and more
- **10 intelligence tools** — composite analytics that aggregate multiple API calls into revenue summaries, ops snapshots, technician scorecards, and more
- **Dashboard-matched revenue** — `intel_revenue_summary` pulls from the same source as ST's own dashboard
- **Read-only by default** — `ST_READONLY=true` out of the box; write tools only activate when you're ready
- **Safety layer** — confirmation workflow for writes/deletes, audit logging with sensitive field redaction
- **Domain filtering** — expose only the tool groups you need via `ST_DOMAINS`
- **Name-based filtering** — pass `businessUnitName` or `technicianName` instead of numeric IDs; resolved via 30-minute cache
- **LLM-optimized responses** — response shaping trims API noise and structures data for AI consumption
- **Streamable HTTP remote deployment** — deploy to Fly.io (or anywhere) and connect via `mcp-remote`
- **Built-in health check** — `st_health_check` validates auth and tenant access (no config details exposed)
---
## Quick Start
### Prerequisites
- Node.js 22 or newer
- ServiceTitan API credentials: Client ID, Client Secret, App Key, Tenant ID
### npx (recommended)
No install needed — runs directly:
```json
{
"mcpServers": {
"servicetitan": {
"command": "npx",
"args": ["-y", "@rowvyn/servicetitan-mcp"],
"env": {
"ST_CLIENT_ID": "your-client-id",
"ST_CLIENT_SECRET": "your-client-secret",
"ST_APP_KEY": "your-app-key",
"ST_TENANT_ID": "your-tenant-id",
"ST_ENVIRONMENT": "production"
}
}
}
}
```
> ⚠️ **`ST_ENVIRONMENT` defaults to `integration`.** If you're connecting to a live ServiceTitan account, you **must** set `ST_ENVIRONMENT` to `production` or you'll get silent auth failures and empty results.
### Install globally
```bash
npm install -g @rowvyn/servicetitan-mcp
servicetitan-mcp # stdio transport (for Claude Desktop)
servicetitan-mcp-sse # SSE transport (legacy remote)
servicetitan-mcp-http # Streamable HTTP transport (recommended remote)
```
### From source
```bash
npm install
npm run build
```
### Claude Desktop
For a local checkout, point Claude Desktop at the built stdio entrypoint:
Add to your `claude_desktop_config.json`:
```json
{
"mcpServers": {
"servicetitan": {
"command": "node",
"args": ["/absolute/path/to/servicetitan-mcp/build/index.js"],
"env": {
"ST_CLIENT_ID": "your-client-id",
"ST_CLIENT_SECRET": "your-client-secret",
"ST_APP_KEY": "your-app-key",
"ST_TENANT_ID": "your-tenant-id",
"ST_TIMEZONE": "America/New_York"
}
}
}
}
```
Works the same way in **Cursor**, **Windsurf**, and any other MCP-compatible host.
### Generic stdio
```bash
ST_CLIENT_ID=your-client-id \
ST_CLIENT_SECRET=your-client-secret \
ST_APP_KEY=your-app-key \
ST_TENANT_ID=your-tenant-id \
ST_TIMEZONE=America/New_York \
node /absolute/path/to/servicetitan-mcp/build/index.js
```
### Remote Deployment (Streamable HTTP)
Deploy to Fly.io or any server, then connect via `mcp-remote`:
```bash
# On the server
ST_CLIENT_ID=... ST_CLIENT_SECRET=... ST_APP_KEY=... ST_TENANT_ID=... \
ST_MCP_API_KEY=your-secret node build/streamable-http.js
```
```json
{
"mcpServers": {
"servicetitan": {
"command": "npx",
"args": [
"mcp-remote",
"https://your-instance.fly.dev/mcp",
"--header",
"x-api-key: YOUR_MCP_API_KEY"
]
}
}
}
```
> SSE remains available at `build/sse.js` for backward compatibility, but it is deprecated. Prefer Streamable HTTP at `/mcp` for new deployments.
---
## Tool Catalog
483 tools registered across 15 domains:
| Domain | Tools | Example Tools |
|--------|------:|---------------|
| `dispatch` | 79 | `dispatch_jobs_list`, `dispatch_appointments_get`, `dispatch_job_types_list` |
| `crm` | 71 | `crm_customers_list`, `crm_contacts_get`, `crm_bookings_create` |
| `export` | 49 | `export_invoices`, `export_customers`, `export_jobs` |
| `inventory` | 37 | `inventory_purchase_orders_list`, `inventory_vendors_list`, `inventory_warehouses_list` |
| `marketing` | 35 | `marketing_campaigns_list`, `marketing_calls_v3_list`, `marketing_reviews` |
| `accounting` | 33 | `accounting_invoices_list`, `accounting_payments_list`, `accounting_gl_accounts_list` |
| `pricebook` | 31 | `pricebook_services_list`, `pricebook_materials_list`, `pricebook_equipment_list` |
| `payroll` | 27 | `payroll_payrolls_list`, `payroll_timesheets_job_list`, `payroll_gross_pay_items_list` |
| `settings` | 23 | `settings_business_units_list`, `settings_tag_types_list`, `settings_user_roles_list` |
| `people` | 22 | `people_technicians_list`, `people_employees_list`, `people_trucks_list` |
| `memberships` | 21 | `memberships_list`, `memberships_types_list`, `memberships_recurring_services_list` |
| `scheduling` | 17 | `scheduling_teams_list`, `scheduling_zones_list`, `scheduling_capacity_calculate` |
| `estimates` | 22 | `estimates_list`, `estimates_get`, `estimates_items_list` |
| **`intelligence`** | **10** | `intel_revenue_summary`, `intel_daily_snapshot`, `intel_technician_scorecard` |
| `reporting` | 5 | `reporting_reports_list`, `reporting_report_categories_list` |
| **Total** | **482** | *(+ 1 system tool: `st_health_check` = **483** total)* |
> With `ST_READONLY=true` (default), all tools are registered but write and delete operations are blocked at execution time with a clear error message (`Readonly mode: operation not permitted`). Use `ST_CONFIRM_WRITES=true` to require `_confirmed: true` on write operations, or `confirm: true` on delete operations.
---
## Intelligence Tools
The real differentiator. These 10 tools aggregate multiple API calls and report endpoints into operational insights — the kind of answers that would otherwise require custom BI tooling.
| Tool | What It Returns |
|------|----------------|
| `intel_revenue_summary` | Dashboard-matched revenue by business unit. Includes completed revenue, non-job revenue, adjustments, collections, and BU productivity rollups. |
| `intel_daily_snapshot` | 6-metric same-day ops briefing: revenue-to-date, jobs in progress, call outcomes, bookings, open estimates, and upcoming next-day jobs. |
| `intel_technician_scorecard` | Per-tech jobs, revenue, avg ticket, productivity, lead gen, membership close rate, and sales from both tech and marketing leads — with team averages. |
| `intel_membership_health` | Active memberships, signups, cancellations, renewals, retention rate, invoiced revenue, and conversion by business unit. |
| `intel_estimate_pipeline` | Open/sold/dismissed funnel, conversion rate, days-to-close, age buckets, stale opportunities, and tech sales funnel metrics. |
| `intel_campaign_performance` | Campaign calls, bookings, conversion rate, total revenue context, and BU lead-gen metrics from Report 176. |
| `intel_csr_performance` | CSR booking performance: jobs booked, revenue, avg ticket, top campaigns, job type mix, conversion metrics, and team averages. |
| `intel_labor_cost` | Labor cost summary from the Master Pay File: employee hours, gross pay, hourly rates, activity mix, and BU breakdowns. |
| `intel_invoice_tracking` | Invoice email tracking: sent vs not-sent counts, send rate, dollar impact, and unsent breakdowns by business unit and technician. |
| `intel_lookup` | Cached reference data — technicians, business units, payment types, membership types with IDs and names. 30-minute TTL. |
These tools are why this server exists. Raw CRUD tools are table stakes. Intelligence tools turn API data into business decisions.
### Revenue Accuracy
`intel_revenue_summary` uses ServiceTitan's **Reporting API** to calculate totals. This is the same source ST's own dashboard uses — which means it includes:
- **Completed Revenue** — revenue from completed jobs
- **Non-Job Revenue** — membership fees, add-ons, and other income not tied to a specific job
- **Adjustment Revenue** — credits, adjustments, and corrections
Raw invoice or job endpoint sums will not match the dashboard. Both miss non-job revenue. For dashboard-matching figures, use `intel_revenue_summary`. For invoice-level detail, use `accounting_invoices_list`.
### Name-Based Filtering
Most intelligence tools accept `businessUnitName` and `technicianName` as alternatives to numeric IDs:
```
# Instead of:
intel_revenue_summary(startDate="2026-01-01", endDate="2026-04-01", businessUnitId=12345)
# Use:
intel_revenue_summary(startDate="2026-01-01", endDate="2026-04-01", businessUnitName="HVAC")
```
Business unit name matching uses exact → prefix → contains fallback. Technician name matching uses case-insensitive substring search. If no match is found, the tool returns all data with a warning.
---
## Configuration
Copy `.env.example` to `.env` and fill in your credentials.
**Required**
| Variable | Description |
|----------|-------------|
| `ST_CLIENT_ID` | ServiceTitan OAuth client ID |
| `ST_CLIENT_SECRET` | ServiceTitan OAuth client secret |
| `ST_APP_KEY` | ServiceTitan app key (`ST-App-Key` header) |
| `ST_TENANT_ID` | ServiceTitan tenant identifier |
| `ST_MCP_API_KEY` | API key for remote MCP clients *(required for remote HTTP deployment)* |
**Optional**
| Variable | Default | Description |
|----------|---------|-------------|
| `ST_ENVIRONMENT` | `integration` | ServiceTitan environment: `integration` or `production` |
| `ST_READONLY` | `true` | Block write and delete operations at execution time |
| `ST_CONFIRM_WRITES` | `false` | Require `_confirmed: true` to execute write tools |
| `ST_MAX_RESPONSE_CHARS` | `100000` | Cap tool response size |
| `ST_DOMAINS` | *(all)* | Comma-separated domain allowlist (e.g. `crm,dispatch,reporting`) |
| `ST_LOG_LEVEL` | `info` | Log level: `debug`, `info`, `warn`, `error` |
| `ST_TIMEZONE` | `UTC` | IANA timezone for the tenant (e.g. `America/New_York`) — used for date-bound intelligence queries and local-time display conversion in all tool responses |
| `ST_RESPONSE_SHAPING` | `true` | Set to `false` to disable intelligence response transformation |
| `ST_CORS_ORIGIN` | _(none)_ | Allowed CORS origin for Streamable HTTP / SSE. Required for browser access. |
| `ST_ALLOWED_CALLERS` | _(none)_ | Comma-separated caller identity allowlist (e.g. `alice@example.com,svc-user`) |
| `ST_INTEL_CACHE_TTL_MS` | `300000` | Intelligence result cache TTL in milliseconds (default: 5 minutes) |
| `ST_MCP_PORT` / `PORT` | `3100` | HTTP server port for Streamable HTTP and SSE transports |
| `ST_MCP_API_KEY` | _(none)_ | API key for authenticating remote MCP clients (required for HTTP transports) |
> **Set `ST_TIMEZONE`** to your tenant's local timezone. Without it, date-only queries (e.g. `startDate: "2026-02-01"`) are interpreted as UTC midnight — which can miss or include invoices and jobs near day boundaries for non-UTC tenants, and tool responses will keep timestamps in UTC instead of your local display timezone.
Boolean env vars accept: `true`, `false`, `1`, `0` (case-insensitive).
---
## Architecture
The server is built as a layered system: **Config → OAuth Client → Domain Registry → MCP Server**.
- **Domain module pattern** — each domain lives in `src/domains/<domain>/` and exports a loader that registers its tools
- **Dynamic discovery** — `src/domains/loader.ts` scans `src/domains/*/index.ts` and imports each domain at runtime
- **Central registry** — `ToolRegistry` handles domain filtering, read-only enforcement, confirmation wrapping, and audit logging
- **OAuth client** — `ServiceTitanClient` manages client-credentials auth, token refresh, retry-on-401/429, and `{tenant}` path injection
- **Response shaping** — transformer layer strips API noise and structures responses for LLM consumption
- **Intelligence layer** — composite tools fan out to multiple endpoints and report IDs, then aggregate results
See [ARCHITECTURE.md](ARCHITECTURE.md) for the full deep-dive: route table, response shaping pipeline, intelligence layer design, and safety system.
---
## CLI Companion
If you prefer working from the terminal directly, [servicetitan-cli](https://github.com/montrellcruse/servicetitan-cli) is a companion `st` binary built from the same auth and intelligence layer:
```bash
# Install
npm install -g @rowvyn/servicetitan-cli
# Same intelligence — no MCP required
st revenue --period ytd --compact
st snapshot --compact
# Pipe into your agent
st customers list --all --json | your-agent process
```
The CLI and this MCP server share the same design philosophy: push the business question into the tool name, minimize schema overhead, and shape responses for AI consumption.
---
## Comparison
| Feature | This Server | Community MCP Servers |
|---------|------------|----------------------|
| Revenue accuracy | ✅ Dashboard-matched | ❌ $17–21K off per period |
| Intelligence tools | ✅ 10 tools | ❌ None |
| Domain coverage | ✅ 483 tools, 15 domains | ⚠️ 10–50 tools |
| Safety layer | ✅ Read-only default, audit log | ❌ None |
| Response shaping | ✅ LLM-optimized | ❌ Raw API responses |
| Name-based filtering | ✅ Resolve names to IDs automatically | ❌ Numeric IDs required |
| Remote deployment | ✅ Streamable HTTP + `mcp-remote` | ⚠️ Varies |
---
## Development
```bash
npm run dev # tsc --watch
npm run test # vitest run
npm run lint # eslint src/
npm run typecheck # tsc --noEmit
npm run build # compile to build/
npm run start # run compiled server
```
---
## Safety
- **Read-only mode** (`ST_READONLY=true` by default) — all tools are registered but write and delete operations are blocked at execution time
- **Confirmation workflow** — delete tools require `confirm: true` (returns preview payload when missing); write tools optionally require `_confirmed: true` via `ST_CONFIRM_WRITES=true` (returns error when missing)
- **Audit logging** — all write/delete executions emit `[AUDIT]` log records with timestamp, tool, operation, resource, and sanitized params (secrets/tokens redacted)
---
## Troubleshooting
### npm permission errors (EACCES)
If you see `EACCES: permission denied` errors when Claude Desktop tries to start the server, your npm cache directory is likely owned by root (common when Node was installed with `sudo`).
**Fix:**
```bash
sudo chown -R $(whoami) ~/.npm
```
Then restart Claude Desktop.
### Server won't start / "Could not connect to MCP server"
1. **Test outside Claude Desktop first:**
```bash
npx -y @rowvyn/servicetitan-mcp
```
If this fails, the issue is with Node, npm, or your environment — not Claude Desktop.
2. **Check Node version:** `node -v` — requires Node 22 or newer.
3. **Check for conflicting MCP installs:** If you previously installed a different ServiceTitan MCP server, remove it:
```bash
npm ls -g --depth=0 | grep -i servicetitan
npm uninstall -g <old-package-name>
npx clear-npx-cache
```
4. **Verify your `claude_desktop_config.json`** has no leftover entries from a previous MCP server. Only one `servicetitan` entry should exist under `mcpServers`.
### Auth failures / $0 revenue / empty results
- **Set `ST_ENVIRONMENT` to `production`.** This is the #1 cause. The default is `integration`, which authenticates against ServiceTitan's sandbox — not your live account. You'll get valid auth tokens that return zero data.
- **Verify credentials match:** `ST_CLIENT_ID` and `ST_CLIENT_SECRET` must be from the same ServiceTitan app. You can't mix credentials from different apps.
- **Check `ST_TENANT_ID`:** Must match the tenant associated with your app in the ServiceTitan developer portal.
### Claude Desktop logs
Claude Desktop writes MCP server logs to:
- **macOS:** `~/Library/Logs/Claude/mcp*.log`
- **Windows:** `%APPDATA%\Claude\logs\mcp*.log`
---
## Contributing
See [CONTRIBUTING.md](CONTRIBUTING.md).
---
## Security
See [SECURITY.md](SECURITY.md) for the vulnerability disclosure policy.
---
## License
MIT
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
No recommendations available