@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 +25 -0
- package/CHANGELOG.md +220 -0
- package/LICENSE +21 -0
- package/README.md +347 -0
- package/build/chunks/chunk-HBUFFGNS.js +54 -0
- package/build/index.js +3 -0
- package/build/sse.js +8 -0
- package/build/streamable-http.js +6 -0
- package/package.json +75 -0
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
|
+
[](https://github.com/montrellcruse/servicetitan-mcp/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/@rowvyn/servicetitan-mcp)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://modelcontextprotocol.io)
|
|
7
|
+
[](#tool-catalog)
|
|
8
|
+
[](#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
|