@rowvyn/servicetitan-mcp 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.env.example ADDED
@@ -0,0 +1,25 @@
1
+ # Required
2
+ ST_CLIENT_ID=your-client-id
3
+ ST_CLIENT_SECRET=your-client-secret
4
+ ST_APP_KEY=your-app-key
5
+ ST_TENANT_ID=your-tenant-id
6
+
7
+ # Optional
8
+ ST_ENVIRONMENT=integration # integration | production
9
+ ST_READONLY=true # true | false (default: true)
10
+ ST_CONFIRM_WRITES=false # Require _confirmed: true on write operations (default: false)
11
+ ST_DOMAINS= # comma-separated: crm,pricebook,reporting (empty = all)
12
+ ST_LOG_LEVEL=info # debug | info | warn | error
13
+ ST_TIMEZONE= # IANA timezone for date alignment (e.g. America/New_York)
14
+ ST_MAX_RESPONSE_CHARS=100000 # Max characters in tool response before truncation
15
+ ST_ALLOWED_CALLERS= # Optional comma-separated caller allowlist (matches authInfo or proxy-injected caller headers when available)
16
+ # ST_RESPONSE_SHAPING=true # Set to false to disable response transformation (returns raw API data)
17
+
18
+ # Remote deployment (Streamable HTTP or SSE)
19
+ # ST_MCP_API_KEY= # Required for remote transports (Streamable HTTP and SSE)
20
+ # ST_MCP_PORT=3100 # HTTP server port (also accepts PORT env var)
21
+ # ST_CORS_ORIGIN= # CORS origin (default: empty = no CORS headers). Set to a specific origin for remote access.
22
+
23
+ # Intelligence layer
24
+ # ST_INTEL_CACHE_TTL_MS=300000 # Intelligence cache TTL in milliseconds (default: 5 minutes)
25
+ # ST_INTEL_MAX_PAGES=20 # Max pages for intelligence pagination (default: 20 pages × 500 rows = 10,000 rows)
package/CHANGELOG.md ADDED
@@ -0,0 +1,220 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/), and this project adheres to [Semantic Versioning](https://semver.org/).
6
+
7
+ ## [2.4.0] - 2026-03-28
8
+
9
+ ### Added
10
+ - **npm distribution metadata** — package description, author, repository, homepage, bugs, keywords, and Node.js engine requirements are now declared for npm publishing.
11
+ - **CLI bin entries** — `servicetitan-mcp-server`, `servicetitan-mcp-sse`, and `servicetitan-mcp-http` now map to the stdio, SSE, and Streamable HTTP entrypoints.
12
+
13
+ ### Changed
14
+ - **Files whitelist** — npm packages now ship only `build/`, `LICENSE`, `README.md`, `CHANGELOG.md`, `.env.example`, and `package.json`.
15
+ - **`prepublishOnly` validation** — `npm publish` now runs typecheck, lint, tests, and build before packaging.
16
+ - **README quick start restructure** — npm `npx` usage is now documented first, followed by global install and source-build instructions.
17
+ - **Entry-point shebangs** — all three transport entrypoints now compile to directly executable Node.js bin scripts.
18
+
19
+ ## [2.3.1] - 2026-03-28
20
+
21
+ ### Fixed
22
+ - **Pagination truncation surfaced** — `fetchAllPages()` and `fetchAllPagesParallel()` now push truncation and page-failure warnings to callers via optional `warnings[]` parameter. Intelligence tools surface these as `_warnings` in responses instead of silently undercounting.
23
+ - **GPS provider path injection** — `people_gps_create` now validates `gpsProvider` with `^[A-Za-z0-9_-]+$` regex, preventing path traversal in outbound ST API calls.
24
+ - **Truncation guard edge case** — `Math.min` → `Math.max(0, ...)` prevents negative slice index when `ST_MAX_RESPONSE_CHARS` < 256.
25
+ - **Response shaping scoped to intelligence only** — removed from `toolResult()` baseline; added `shape: true` parameter to 9 intelligence domain handlers. Non-intelligence tools now return unmodified ServiceTitan schemas.
26
+ - **UTC day math** — `countWeekdaysInclusive()` and `dayDiff()` accept timezone parameter, normalize to tenant-local calendar dates. Fixes jobsPerDay inflation for non-UTC tenants.
27
+ - **Pagination off-by-one** — page increment no longer skips `maxPages` detection; `_truncated` marker properly surfaced.
28
+ - **JSON truncation format** — truncated responses now produce valid parseable JSON.
29
+ - **Phone number PII redaction** — expanded `sanitizeParams()` to catch `contactNumbers`, `campaignPhoneNumbers`, `phoneNumberCalled`, `callerPhoneNumber` variants.
30
+ - **Cross-domain import** — CRM customers no longer imports `getErrorMessage` from intelligence helpers.
31
+ - **SSE CORS consistency** — `sendCorsHeaders()` skips header emission when `corsOrigin` is empty, matching Streamable HTTP behavior.
32
+ - **Domain loader error masking** — distinguishes transitive import failures (throw) from missing `index.js` (skip gracefully).
33
+ - **Non-UTC end-of-day 999ms off-by-one** — `parseDateInput()` computes local date via `Intl.DateTimeFormat` without offset reconstruction.
34
+ - **`ST_INTEL_MAX_PAGES` configurable** — pagination cap now reads from env var (default 20 pages × 500 rows).
35
+ - **`sortParam()` validation** — accepts bare field names alongside signed `+Field`/`-Field` format.
36
+
37
+ ### Changed
38
+ - **Version read from package.json** — removed hardcoded version strings from all 3 entry points (stdio, SSE, Streamable HTTP). Single source of truth.
39
+ - **TOOLS.md regenerated** — 467 tools (was 460), catalog script corrected.
40
+ - **Documentation alignment** — ARCHITECTURE.md domain tables, health check description, readonly semantics, name matching examples, route table entries all corrected to match implementation. README env vars complete. CONTRIBUTING.md getErrorMessage guidance updated.
41
+
42
+ ### Added
43
+ - 249 tests across 18 test files (up from 245 / 17).
44
+ - 7-round audit trail in `audit/` directory with full findings and remediation evidence.
45
+
46
+ ## [2.3.0] - 2026-03-28
47
+
48
+ ### Security
49
+ - **Write middleware** — readonly mode, confirmation enforcement (`_confirmed: true`), and audit logging now enforced at the registry level. No write tool can bypass these guards regardless of handler implementation.
50
+ - **CORS** — default changed from wildcard `*` to empty (no CORS headers sent). Set `ST_CORS_ORIGIN` explicitly for remote deployments.
51
+ - **Health check** — stripped config details (environment, readonly_mode, tools_registered). Returns only auth and tenant connectivity status.
52
+ - **Error sanitization** — Zod validation errors now return human-readable `Invalid input: field: message` instead of raw schema internals.
53
+ - **Health check errors** — FAILED messages no longer leak internal error text.
54
+
55
+ ### Fixed
56
+ - **Response shaping** — semantic fields (id, type, description, active, count, date, notes, tags, source, category) no longer stripped from tool output. Only true metadata (pagination, HTTP status, internal timestamps) excluded.
57
+ - **Route table** — added 48 missing API route prefixes (18 regular + 17 export + 13 domain routes). Added drift test to prevent regressions.
58
+ - **Session lifecycle** — proper `server.close()` on client disconnect for both SSE and Streamable HTTP transports. Prevents file descriptor leaks.
59
+ - **HTTP timeouts** — 60s request timeout, 15s token fetch timeout, single retry with `Retry-After` backoff. `parseRetryAfter()` now handles HTTP-date format.
60
+ - **Report 175 validation** — Zod runtime schema validates response structure before data extraction. Fails fast with clear error on schema drift.
61
+ - **safeDivide()** — all division operations in intelligence layer protected against NaN/Infinity. Zero-denominator edge-case tests for all 10 intel tools.
62
+ - **Empty write schemas** — 6 write tools (capacity calculate, payment create, payment status update, AP credits/payments mark-as-exported, invoice adjustment/mark-as-exported) now require valid input instead of accepting empty `{}`.
63
+ - **Server-managed fields** — removed `createdOn`, `modifiedOn`, `createdBy`, `modifiedBy`, `mergedToId`, `id` from write schemas across CRM, inventory, pricebook, and settings domains.
64
+
65
+ ### Changed
66
+ - **12 duplicate tool registrations removed** — CRM (customer note/tag delete aliases), marketing (campaign category list alias), scheduling (technician shifts moved to canonical domain), pricebook (images alias). Net **-1,300 lines**.
67
+ - **getErrorMessage** consolidated from 59 per-file copies to 2 shared utilities: `src/utils.ts` (main) and `src/domains/intelligence/helpers.ts` (intelligence layer).
68
+ - **Domain loader** extracted to `src/domains/loader.ts`, replacing 3 identical `loadDomainModules()` copies across stdio, SSE, and Streamable HTTP transports.
69
+ - **Duplicate tool name detection** — `ToolRegistry.register()` now throws on duplicate tool names at startup.
70
+ - Dead gzip infrastructure removed from Streamable HTTP transport (`createGzip` import, `acceptsGzip`, `supportsGzip()`).
71
+ - Version bumped to 2.3.0 across package.json and all transports.
72
+
73
+ ### Added
74
+ - **Per-tool cache TTL** — `withIntelCache()` and `ReferenceDataCache.getOrLoad()` support per-call TTL overrides (default 5min unchanged).
75
+ - `tests/safety/write-middleware.test.ts` — dedicated test suite for readonly blocking, confirmation enforcement, audit logging, and schema hardening.
76
+ - `tests/domains/intelligence-revenue-validation.test.ts` — Report 175 structure validation and empty dataset handling.
77
+ - `cacheTtlMs` field on `ToolDefinition` for declarative per-tool cache configuration.
78
+ - 245 tests across 17 test files (up from 222 / 14).
79
+
80
+ ## [2.2.0] - 2026-03-27
81
+
82
+ ### Security
83
+ - **Constant-time API key comparison** via `timingSafeEqual` (replaces `===`)
84
+ - Request ID tracking on all error responses (UUID per request)
85
+ - Configurable CORS origin (`ST_CORS_ORIGIN`)
86
+ - SSE keepalive (30s interval) with dead connection detection
87
+ - Graceful shutdown on SIGTERM/SIGINT
88
+
89
+ ### Added
90
+ - **Streamable HTTP transport** (`src/streamable-http.ts`) — replaces SSE as primary remote transport. Per-session server isolation, session TTL (30min), 1MB body limit.
91
+ - Full README rewrite with hero section, Claude Desktop quickstart, tool catalog, intelligence spotlight, comparison table
92
+ - CHANGELOG, ARCHITECTURE.md, CONTRIBUTING.md, SECURITY.md
93
+ - GitHub issue/PR templates, CI + MIT badges
94
+ - Startup banner with version, port, tool count, config summary
95
+
96
+ ### Changed
97
+ - **94 → 222 tests** (+136%) — SSE transport, cache, domain registration, accounting, CRM domain tests
98
+ - SSE transport deprecated in favor of Streamable HTTP (still available at `build/sse.js`)
99
+
100
+ ## [2.1.1] - 2026-03-26
101
+
102
+ ### Security
103
+ - Fixed unguarded `JSON.parse` in SSE `/messages` handler — returns HTTP 400 on malformed JSON instead of crashing
104
+ - Added 1MB request body size limit to SSE `/messages` endpoint (HTTP 413 on oversize requests)
105
+ - Added `SIGTERM`/`SIGINT` graceful shutdown handler with 10-second force-exit fallback
106
+ - Added `uncaughtException` and `unhandledRejection` handlers in SSE server to prevent silent crashes
107
+
108
+ ### Changed
109
+ - Extracted 19 duplicated `errorMessage()` functions across domain files to a shared `getErrorMessage` import from `src/domains/intelligence/helpers.ts`
110
+ - Documented `ST_RESPONSE_SHAPING` and `ST_MCP_API_KEY` environment variables in README
111
+
112
+ ### Added
113
+ - MIT `LICENSE` file
114
+ - `.editorconfig` for consistent editor settings across contributors
115
+
116
+ ## [2.1.0] - 2026-03-10
117
+
118
+ ### Added
119
+ - Reference data cache (`src/cache.ts`) with 30-minute TTL, in-flight request deduplication, and name-based filtering for technicians, business units, payment types, and membership types
120
+ - `intel_lookup` tool for resolving names to IDs via the reference cache
121
+ - Response shaping middleware (`src/response-shaping.ts`) — strips low-signal fields, abbreviates keys, rounds currency/ratio values, compacts ISO timestamps to date-only strings, and applies per-field array limits to reduce LLM token consumption
122
+ - 10th intelligence tool (`intel_lookup`) completing the intelligence layer
123
+
124
+ ### Changed
125
+ - Expanded intelligence layer from 9 to 10 tools
126
+ - Autoresearch iter2 round 2 shaping improvements ported into response shaping middleware
127
+ - Updated README and TOOLS.md to document all 10 intelligence tools and name-filtering capability
128
+ - Rewrote PROJECT_MAP for current architecture
129
+
130
+ ### Removed
131
+ - Dead cache module replaced by the new unified `ReferenceDataCache`
132
+
133
+ ## [2.0.0] - 2026-03-09 through 2026-03-05
134
+
135
+ ### Added
136
+ - **Enterprise intelligence layer** — 9 composite business intelligence tools built on 18 ServiceTitan reports:
137
+ - `intel_revenue_summary` — revenue by business unit using Report 175
138
+ - `intel_technician_scorecard` — per-technician performance via ST Reporting API
139
+ - `intel_membership_health` — membership metrics via Report 182
140
+ - `intel_campaign_performance` — marketing ROI using Reports 172 and 176
141
+ - `intel_operational_snapshot` — daily operational health snapshot
142
+ - `intel_csr_performance` — CSR call performance metrics
143
+ - `intel_labor_cost` — payroll and labor efficiency analysis
144
+ - `intel_pipeline` — estimate and job pipeline status
145
+ - `intel_invoice_tracking` — invoice aging and collection metrics
146
+ - SSE server (`src/sse.ts`) for remote MCP access over HTTP with bearer token authentication
147
+ - Fly.io deployment configuration (Dockerfile, `fly.toml`)
148
+ - `ST_TIMEZONE` environment variable for tenant-local date alignment (IANA format)
149
+ - `ST_MCP_API_KEY` requirement for remote SSE deployments
150
+ - CI pipeline (GitHub Actions), Docker support, and generated tool catalog (TOOLS.md)
151
+ - Versioned API prefix routing — resolves `/tenant/{id}/customers` to `/crm/v2/tenant/{id}/customers` automatically via route table
152
+ - Safety layer: readonly mode, write confirmation, audit logging with sensitive field sanitization
153
+ - Health check endpoint (`GET /health`) on SSE server
154
+
155
+ ### Fixed
156
+ - Revenue accuracy: switched to ST Reporting API (Report 175) to match dashboard totals exactly
157
+ - Technician scorecard uses job totals for revenue calculation
158
+ - Route table corrections for production ServiceTitan API
159
+ - Filtered zero-activity rows from intelligence reports
160
+ - Corrected v3 call API mocks in tests
161
+ - Closed previous MCP connection before accepting new SSE client
162
+ - Single Fly machine to prevent SSE session routing mismatch
163
+ - Bumped Fly memory to 512MB to prevent tool registration crashes
164
+ - Bound SSE server to `0.0.0.0` for Fly proxy compatibility
165
+ - Kept 1 machine always running to avoid cold-start 502s
166
+ - Removed `src/` from `.dockerignore` (required for multi-stage build)
167
+ - 5 accuracy bugs from live MCP audit
168
+
169
+ ### Changed
170
+ - Rewired `intel_membership_health` to use ST Report 182
171
+ - Rewired `intel_technician_scorecard` to use ST Reporting API
172
+ - Optimized campaign performance tool; added Reports 172/176 integration
173
+ - Removed spec files (build artifacts, not source)
174
+
175
+ ## [1.0.0] - 2026-03-04
176
+
177
+ ### Added
178
+ - **Core infrastructure** (Spec 01):
179
+ - `ServiceTitanClient` with OAuth 2.0 client credentials flow, token caching, 401 auto-retry, and 429 rate-limit backoff
180
+ - `ToolRegistry` with domain filtering, readonly mode, write confirmation, and audit logging
181
+ - `loadConfig()` with validation for all environment variables
182
+ - `AuditLogger` with sensitive field sanitization
183
+ - `Logger` (structured JSON output)
184
+ - `st_health_check` system tool
185
+ - stdio transport via `@modelcontextprotocol/sdk`
186
+ - **Domain tools** (Spec 02) — 440+ tools across 14 domains:
187
+ - `accounting` — invoices, payments, GL accounts, journal entries, tax zones
188
+ - `crm` — customers, contacts, leads, locations, bookings, tags
189
+ - `dispatch` — appointment assignments, arrival windows, capacity, teams, zones
190
+ - `estimates` — estimate CRUD and line items
191
+ - `export` — bulk export endpoints for all major resources
192
+ - `inventory` — purchase orders, vendors, warehouses, adjustments, transfers, receipts
193
+ - `marketing` — campaigns, costs, attributed leads, submissions
194
+ - `memberships` — memberships, membership types, recurring services
195
+ - `payroll` — payrolls, adjustments, gross pay items, timesheets
196
+ - `people` — employees, user roles
197
+ - `pricebook` — services, materials, equipment, categories, discounts
198
+ - `reporting` — report categories, dynamic value sets, report data
199
+ - `scheduling` — appointments, job types, job cancel/hold reasons
200
+ - `settings` — business units, technicians, tag types, activity categories
201
+ - **Safety layer** (Spec 03):
202
+ - Required `confirm=true` for all delete operations
203
+ - Optional `confirm=true` for write operations when `ST_CONFIRM_WRITES=true`
204
+ - Audit log entries for all write/delete operations
205
+ - Expanded test suite covering safety layer behavior
206
+ - **Initial intelligence layer** (Spec 04) — 6 composite business tools
207
+ - **CI, docs, and Docker** (Spec 05/06):
208
+ - GitHub Actions CI workflow
209
+ - Dockerfile for containerized deployment
210
+ - Generated TOOLS.md tool catalog
211
+ - `.env.example` with all environment variables documented
212
+
213
+ [2.3.1]: https://github.com/montrellcruse/servicetitan-mcp/compare/v2.3.0...v2.3.1
214
+ [2.4.0]: https://github.com/montrellcruse/servicetitan-mcp/compare/v2.3.1...v2.4.0
215
+ [2.3.0]: https://github.com/montrellcruse/servicetitan-mcp/compare/v2.2.0...v2.3.0
216
+ [2.2.0]: https://github.com/montrellcruse/servicetitan-mcp/compare/v2.1.1...v2.2.0
217
+ [2.1.1]: https://github.com/montrellcruse/servicetitan-mcp/compare/v2.1.0...v2.1.1
218
+ [2.1.0]: https://github.com/montrellcruse/servicetitan-mcp/compare/v2.0.0...v2.1.0
219
+ [2.0.0]: https://github.com/montrellcruse/servicetitan-mcp/compare/v1.0.0...v2.0.0
220
+ [1.0.0]: https://github.com/montrellcruse/servicetitan-mcp/releases/tag/v1.0.0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Montrell Cruse
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,347 @@
1
+ # ServiceTitan MCP Server
2
+
3
+ [![CI](https://github.com/montrellcruse/servicetitan-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/montrellcruse/servicetitan-mcp/actions/workflows/ci.yml)
4
+ [![npm version](https://img.shields.io/npm/v/@rowvyn/servicetitan-mcp.svg)](https://www.npmjs.com/package/@rowvyn/servicetitan-mcp)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
6
+ [![MCP](https://img.shields.io/badge/MCP-Compatible-green.svg)](https://modelcontextprotocol.io)
7
+ [![Tools](https://img.shields.io/badge/tools-467-blue.svg)](#tool-catalog)
8
+ [![Domains](https://img.shields.io/badge/domains-15-purple.svg)](#tool-catalog)
9
+
10
+ The only MCP server for the ServiceTitan API — 467 tools across 15 domains, plus 10 intelligence tools that turn raw API data into business decisions.
11
+
12
+ <p align="center">
13
+ <em>Connect any AI assistant to ServiceTitan's full API — CRM, dispatch, invoicing, payroll, and operational intelligence — through the Model Context Protocol.</em>
14
+ </p>
15
+
16
+ ---
17
+
18
+ ## Why
19
+
20
+ - **ServiceTitan has no official MCP server** or developer tooling beyond REST docs
21
+ - **Raw API access is friction-heavy** — OAuth token management, module-prefix routing, pagination, and response parsing all fall on you
22
+ - **This server handles all of that** and adds 10 intelligence tools that aggregate multiple endpoints into operational insights
23
+ - **Revenue accuracy within 0.001% of ServiceTitan's own dashboard** (verified on production data using Report 175 — the same source ST's internal engine uses)
24
+
25
+ ---
26
+
27
+ ## Features
28
+
29
+ - **467 tools across 15 domains** — CRM, dispatch, accounting, payroll, inventory, marketing, and more
30
+ - **10 intelligence tools** — composite analytics that aggregate multiple API calls into revenue summaries, ops snapshots, technician scorecards, and more
31
+ - **Dashboard-accurate revenue** — `intel_revenue_summary` uses Report 175 to match ST's dashboard within 0.001%
32
+ - **Read-only by default** — `ST_READONLY=true` out of the box; write tools only activate when you're ready
33
+ - **Safety layer** — confirmation workflow for writes/deletes, audit logging with sensitive field redaction
34
+ - **Domain filtering** — expose only the tool groups you need via `ST_DOMAINS`
35
+ - **Name-based filtering** — pass `businessUnitName` or `technicianName` instead of numeric IDs; resolved via 30-minute cache
36
+ - **LLM-optimized responses** — response shaping trims API noise and structures data for AI consumption
37
+ - **Streamable HTTP remote deployment** — deploy to Fly.io (or anywhere) and connect via `mcp-remote`
38
+ - **Built-in health check** — `st_health_check` validates auth and tenant access (no config details exposed)
39
+
40
+ ---
41
+
42
+ ## Quick Start
43
+
44
+ ### Prerequisites
45
+
46
+ - Node.js 22 or newer
47
+ - ServiceTitan API credentials: Client ID, Client Secret, App Key, Tenant ID
48
+
49
+ ### npx (recommended)
50
+
51
+ No install needed — runs directly:
52
+
53
+ ```json
54
+ {
55
+ "mcpServers": {
56
+ "servicetitan": {
57
+ "command": "npx",
58
+ "args": ["-y", "@rowvyn/servicetitan-mcp"],
59
+ "env": {
60
+ "ST_CLIENT_ID": "your-client-id",
61
+ "ST_CLIENT_SECRET": "your-client-secret",
62
+ "ST_APP_KEY": "your-app-key",
63
+ "ST_TENANT_ID": "your-tenant-id"
64
+ }
65
+ }
66
+ }
67
+ }
68
+ ```
69
+
70
+ ### Install globally
71
+
72
+ ```bash
73
+ npm install -g @rowvyn/servicetitan-mcp
74
+ servicetitan-mcp # stdio transport (for Claude Desktop)
75
+ servicetitan-mcp-sse # SSE transport (legacy remote)
76
+ servicetitan-mcp-http # Streamable HTTP transport (recommended remote)
77
+ ```
78
+
79
+ ### From source
80
+
81
+ ```bash
82
+ npm install
83
+ npm run build
84
+ ```
85
+
86
+ ### Claude Desktop
87
+
88
+ For a local checkout, point Claude Desktop at the built stdio entrypoint:
89
+
90
+ Add to your `claude_desktop_config.json`:
91
+
92
+ ```json
93
+ {
94
+ "mcpServers": {
95
+ "servicetitan": {
96
+ "command": "node",
97
+ "args": ["/absolute/path/to/servicetitan-mcp/build/index.js"],
98
+ "env": {
99
+ "ST_CLIENT_ID": "your-client-id",
100
+ "ST_CLIENT_SECRET": "your-client-secret",
101
+ "ST_APP_KEY": "your-app-key",
102
+ "ST_TENANT_ID": "your-tenant-id",
103
+ "ST_TIMEZONE": "America/New_York"
104
+ }
105
+ }
106
+ }
107
+ }
108
+ ```
109
+
110
+ Works the same way in **Cursor**, **Windsurf**, and any other MCP-compatible host.
111
+
112
+ ### Generic stdio
113
+
114
+ ```bash
115
+ ST_CLIENT_ID=your-client-id \
116
+ ST_CLIENT_SECRET=your-client-secret \
117
+ ST_APP_KEY=your-app-key \
118
+ ST_TENANT_ID=your-tenant-id \
119
+ ST_TIMEZONE=America/New_York \
120
+ node /absolute/path/to/servicetitan-mcp/build/index.js
121
+ ```
122
+
123
+ ### Remote Deployment (Streamable HTTP)
124
+
125
+ Deploy to Fly.io or any server, then connect via `mcp-remote`:
126
+
127
+ ```bash
128
+ # On the server
129
+ ST_CLIENT_ID=... ST_CLIENT_SECRET=... ST_APP_KEY=... ST_TENANT_ID=... \
130
+ ST_MCP_API_KEY=your-secret node build/streamable-http.js
131
+ ```
132
+
133
+ ```json
134
+ {
135
+ "mcpServers": {
136
+ "servicetitan": {
137
+ "command": "npx",
138
+ "args": [
139
+ "mcp-remote",
140
+ "https://your-instance.fly.dev/mcp",
141
+ "--header",
142
+ "x-api-key: YOUR_MCP_API_KEY"
143
+ ]
144
+ }
145
+ }
146
+ }
147
+ ```
148
+
149
+ > SSE remains available at `build/sse.js` for backward compatibility, but it is deprecated. Prefer Streamable HTTP at `/mcp` for new deployments.
150
+
151
+ ---
152
+
153
+ ## Tool Catalog
154
+
155
+ 467 tools registered across 15 domains:
156
+
157
+ | Domain | Tools | Example Tools |
158
+ |--------|------:|---------------|
159
+ | `dispatch` | 74 | `dispatch_jobs_list`, `dispatch_appointments_get`, `dispatch_job_types_list` |
160
+ | `crm` | 71 | `crm_customers_list`, `crm_contacts_get`, `crm_bookings_create` |
161
+ | `export` | 49 | `export_invoices`, `export_customers`, `export_jobs` |
162
+ | `inventory` | 37 | `inventory_purchase_orders_list`, `inventory_vendors_list`, `inventory_warehouses_list` |
163
+ | `marketing` | 35 | `marketing_campaigns_list`, `marketing_calls_v3_list`, `marketing_reviews` |
164
+ | `accounting` | 33 | `accounting_invoices_list`, `accounting_payments_list`, `accounting_gl_accounts_list` |
165
+ | `pricebook` | 31 | `pricebook_services_list`, `pricebook_materials_list`, `pricebook_equipment_list` |
166
+ | `payroll` | 27 | `payroll_payrolls_list`, `payroll_timesheets_job_list`, `payroll_gross_pay_items_list` |
167
+ | `settings` | 23 | `settings_business_units_list`, `settings_tag_types_list`, `settings_user_roles_list` |
168
+ | `people` | 22 | `people_technicians_list`, `people_employees_list`, `people_trucks_list` |
169
+ | `memberships` | 21 | `memberships_list`, `memberships_types_list`, `memberships_recurring_services_list` |
170
+ | `scheduling` | 17 | `scheduling_teams_list`, `scheduling_zones_list`, `scheduling_capacity_calculate` |
171
+ | `estimates` | 11 | `estimates_list`, `estimates_get`, `estimates_items_list` |
172
+ | **`intelligence`** | **10** | `intel_revenue_summary`, `intel_daily_snapshot`, `intel_technician_scorecard` |
173
+ | `reporting` | 5 | `reporting_reports_list`, `reporting_report_categories_list` |
174
+ | **Total** | **466** | *(+ 1 system tool: `st_health_check` = **467** total)* |
175
+
176
+ > 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.
177
+
178
+ ---
179
+
180
+ ## Intelligence Tools
181
+
182
+ 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.
183
+
184
+ | Tool | What It Returns |
185
+ |------|----------------|
186
+ | `intel_revenue_summary` | Dashboard-accurate revenue by business unit using Report 175 — matches ServiceTitan within 0.001%. Includes completed revenue, non-job revenue, adjustments, collections, and BU productivity rollups. |
187
+ | `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. |
188
+ | `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. |
189
+ | `intel_membership_health` | Active memberships, signups, cancellations, renewals, retention rate, invoiced revenue, and conversion by business unit. |
190
+ | `intel_estimate_pipeline` | Open/sold/dismissed funnel, conversion rate, days-to-close, age buckets, stale opportunities, and tech sales funnel metrics. |
191
+ | `intel_campaign_performance` | Campaign calls, bookings, conversion rate, total revenue context, and BU lead-gen metrics from Report 176. |
192
+ | `intel_csr_performance` | CSR booking performance: jobs booked, revenue, avg ticket, top campaigns, job type mix, conversion metrics, and team averages. |
193
+ | `intel_labor_cost` | Labor cost summary from the Master Pay File: employee hours, gross pay, hourly rates, activity mix, and BU breakdowns. |
194
+ | `intel_invoice_tracking` | Invoice email tracking: sent vs not-sent counts, send rate, dollar impact, and unsent breakdowns by business unit and technician. |
195
+ | `intel_lookup` | Cached reference data — technicians, business units, payment types, membership types with IDs and names. 30-minute TTL. |
196
+
197
+ These tools are why this server exists. Raw CRUD tools are table stakes. Intelligence tools turn API data into business decisions.
198
+
199
+ ### Revenue Accuracy
200
+
201
+ `intel_revenue_summary` uses ServiceTitan's **Reporting API** (Report 175: "Revenue") to calculate totals. This is the same source ST's own dashboard uses — which means it includes:
202
+
203
+ - **Completed Revenue** — revenue from completed jobs
204
+ - **Non-Job Revenue** — membership fees, add-ons, and other income not tied to a specific job
205
+ - **Adjustment Revenue** — credits, adjustments, and corrections
206
+
207
+ 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`.
208
+
209
+ ### Name-Based Filtering
210
+
211
+ Most intelligence tools accept `businessUnitName` and `technicianName` as alternatives to numeric IDs:
212
+
213
+ ```
214
+ # Instead of:
215
+ intel_revenue_summary(startDate="2026-01-01", endDate="2026-04-01", businessUnitId=12345)
216
+
217
+ # Use:
218
+ intel_revenue_summary(startDate="2026-01-01", endDate="2026-04-01", businessUnitName="HVAC")
219
+ ```
220
+
221
+ 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.
222
+
223
+ ---
224
+
225
+ ## Configuration
226
+
227
+ Copy `.env.example` to `.env` and fill in your credentials.
228
+
229
+ **Required**
230
+
231
+ | Variable | Description |
232
+ |----------|-------------|
233
+ | `ST_CLIENT_ID` | ServiceTitan OAuth client ID |
234
+ | `ST_CLIENT_SECRET` | ServiceTitan OAuth client secret |
235
+ | `ST_APP_KEY` | ServiceTitan app key (`ST-App-Key` header) |
236
+ | `ST_TENANT_ID` | ServiceTitan tenant identifier |
237
+ | `ST_MCP_API_KEY` | API key for remote MCP clients *(required for remote HTTP deployment)* |
238
+
239
+ **Optional**
240
+
241
+ | Variable | Default | Description |
242
+ |----------|---------|-------------|
243
+ | `ST_ENVIRONMENT` | `integration` | ServiceTitan environment: `integration` or `production` |
244
+ | `ST_READONLY` | `true` | Block write and delete operations at execution time |
245
+ | `ST_CONFIRM_WRITES` | `false` | Require `_confirmed: true` to execute write tools |
246
+ | `ST_MAX_RESPONSE_CHARS` | `100000` | Cap tool response size |
247
+ | `ST_DOMAINS` | *(all)* | Comma-separated domain allowlist (e.g. `crm,dispatch,reporting`) |
248
+ | `ST_LOG_LEVEL` | `info` | Log level: `debug`, `info`, `warn`, `error` |
249
+ | `ST_TIMEZONE` | `UTC` | IANA timezone for the tenant (e.g. `America/New_York`) — critical for date-bound intelligence tools |
250
+ | `ST_RESPONSE_SHAPING` | `true` | Set to `false` to disable intelligence response transformation |
251
+ | `ST_CORS_ORIGIN` | _(none)_ | Allowed CORS origin for Streamable HTTP / SSE. Required for browser access. |
252
+ | `ST_ALLOWED_CALLERS` | _(none)_ | Comma-separated caller identity allowlist (e.g. `alice@example.com,svc-user`) |
253
+ | `ST_INTEL_CACHE_TTL_MS` | `300000` | Intelligence result cache TTL in milliseconds (default: 5 minutes) |
254
+ | `ST_MCP_PORT` / `PORT` | `3100` | HTTP server port for Streamable HTTP and SSE transports |
255
+ | `ST_MCP_API_KEY` | _(none)_ | API key for authenticating remote MCP clients (required for HTTP transports) |
256
+
257
+ > **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.
258
+
259
+ Boolean env vars accept: `true`, `false`, `1`, `0` (case-insensitive).
260
+
261
+ ---
262
+
263
+ ## Architecture
264
+
265
+ The server is built as a layered system: **Config → OAuth Client → Domain Registry → MCP Server**.
266
+
267
+ - **Domain module pattern** — each domain lives in `src/domains/<domain>/` and exports a loader that registers its tools
268
+ - **Dynamic discovery** — `src/domains/loader.ts` scans `src/domains/*/index.ts` and imports each domain at runtime
269
+ - **Central registry** — `ToolRegistry` handles domain filtering, read-only enforcement, confirmation wrapping, and audit logging
270
+ - **OAuth client** — `ServiceTitanClient` manages client-credentials auth, token refresh, retry-on-401/429, and `{tenant}` path injection
271
+ - **Response shaping** — transformer layer strips API noise and structures responses for LLM consumption
272
+ - **Intelligence layer** — composite tools fan out to multiple endpoints and report IDs, then aggregate results
273
+
274
+ See [ARCHITECTURE.md](ARCHITECTURE.md) for the full deep-dive: route table, response shaping pipeline, intelligence layer design, and safety system.
275
+
276
+ ---
277
+
278
+ ## CLI Companion
279
+
280
+ 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:
281
+
282
+ ```bash
283
+ # Install
284
+ npm install -g @rowvyn/servicetitan-cli
285
+
286
+ # Same intelligence — no MCP required
287
+ st revenue --period ytd --compact
288
+ st snapshot --compact
289
+
290
+ # Pipe into your agent
291
+ st customers list --all --json | your-agent process
292
+ ```
293
+
294
+ 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.
295
+
296
+ ---
297
+
298
+ ## Comparison
299
+
300
+ | Feature | This Server | Community MCP Servers |
301
+ |---------|------------|----------------------|
302
+ | Revenue accuracy | ✅ 0.001% of dashboard | ❌ $17–21K off per period |
303
+ | Intelligence tools | ✅ 10 tools | ❌ None |
304
+ | Domain coverage | ✅ 467 tools, 15 domains | ⚠️ 10–50 tools |
305
+ | Safety layer | ✅ Read-only default, audit log | ❌ None |
306
+ | Response shaping | ✅ LLM-optimized | ❌ Raw API responses |
307
+ | Name-based filtering | ✅ Resolve names to IDs automatically | ❌ Numeric IDs required |
308
+ | Remote deployment | ✅ Streamable HTTP + `mcp-remote` | ⚠️ Varies |
309
+
310
+ ---
311
+
312
+ ## Development
313
+
314
+ ```bash
315
+ npm run dev # tsc --watch
316
+ npm run test # vitest run
317
+ npm run lint # eslint src/
318
+ npm run typecheck # tsc --noEmit
319
+ npm run build # compile to build/
320
+ npm run start # run compiled server
321
+ ```
322
+
323
+ ---
324
+
325
+ ## Safety
326
+
327
+ - **Read-only mode** (`ST_READONLY=true` by default) — all tools are registered but write and delete operations are blocked at execution time
328
+ - **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)
329
+ - **Audit logging** — all write/delete executions emit `[AUDIT]` log records with timestamp, tool, operation, resource, and sanitized params (secrets/tokens redacted)
330
+
331
+ ---
332
+
333
+ ## Contributing
334
+
335
+ See [CONTRIBUTING.md](CONTRIBUTING.md).
336
+
337
+ ---
338
+
339
+ ## Security
340
+
341
+ See [SECURITY.md](SECURITY.md) for the vulnerability disclosure policy.
342
+
343
+ ---
344
+
345
+ ## License
346
+
347
+ MIT