servicetitan-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@servicetitan-mcpFind customer by phone 555-1234 and show their recent jobs"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
servicetitan-mcp
A read-only MCP server over the ServiceTitan API: six task-shaped tools (find a customer, look at their jobs, their money, resolve an external id) instead of a thin wrapper over ServiceTitan's own endpoints. ReadOnlyClient defines only get and get_page — no write path exists.
See docs/DESIGN.md for the reasoning behind these choices.
The credentials rule
The MCP client config carries exactly one setting: ST_ENV_FILE, a path — never a secret directly. Everything else (client id, secret, app key, tenant id) lives in the file it points at, chmod 600, outside the repo and outside any MCP config: MCP configs get pasted into chats, and some (.mcp.json) are designed to be committed.
permission_warning checks the file's mode at startup and warns — doesn't refuse to start — if it's looser than 600. Full reasoning in docs/DESIGN.md.
{
"mcpServers": {
"servicetitan": {
"command": "/absolute/path/to/servicetitan-mcp/.venv/bin/servicetitan-mcp",
"env": { "ST_ENV_FILE": "/home/you/.config/servicetitan-mcp/env" }
}
}
}command must be an absolute path into this project's .venv — an MCP client launches from an arbitrary directory, so a bare python may resolve to an interpreter without mcp/httpx/st_mcp installed.
Related MCP server: ServiceTitan MCP
The six tools
Tool | Answers | Reach for it when |
| Who is this, by name/phone/external id? | You have a human identifier, not a ServiceTitan id. One match returns the full card; several return a short list to disambiguate. |
| Full card for one customer: contacts, locations, 5 recent jobs, external ids. | You already have the numeric customer id — from |
| Which jobs match these filters, and when's the next visit? | You want a list of jobs by customer, date range, or status. Not for finding a customer — that's |
| Everything about one job: appointments, technicians. | You have a job id and need its full appointment history. |
| Balance, estimates, invoices, payments — for one customer, one call. | Any "what do they owe" or billing-history question. |
| Which ServiceTitan customer has this id in another system? | You're crossing over from a CRM synced into ServiceTitan via |
Job types, business units, and payment types are also exposed as MCP resources (cached client-side) rather than tools, and back the id→name substitution above.
Token savings, measured against a live tenant
chars / 4, on the same four ServiceTitan reads:
tokens | |
| 1516 |
| 1001 |
| 69 |
| 43 |
| 28 |
| 318 |
22x against pretty-printed JSON, 15x against minified, per call — and customer_overview is one model round trip for four ServiceTitan reads, where an endpoint-shaped tool surface would spend four.
ServiceTitan findings
totalCountisnullunless the request sendsincludeTotal=true.jpm/appointmentshas no batch filter —jobIds,jobId=<csv>, repeatedjobIdare all ignored or wrong; onlyjobId=<single>works.sales/estimatesignorescustomerId; reachable only viajobIdorlocationId.A 404 on an appointment's assigned-technicians means "nobody assigned yet", not "not found" — the API's own "could not" that actually means "none".
Setup
Requires Python 3.11+.
python -m venv .venv
.venv/bin/pip install -e ".[dev]"Create the env file ST_ENV_FILE will point at (e.g. ~/.config/servicetitan-mcp/env), chmod 600 it, and fill in:
ST_CLIENT_ID=...
ST_CLIENT_SECRET=...
ST_APP_KEY=...
ST_TENANT_ID=...
# Optional:
ST_AUTH_HOST=auth.servicetitan.io
ST_API_HOST=api.servicetitan.io
ST_APP_GUID=... # required only for find_by_external_id
ST_EXTERNAL_ID_KEY=... # the externalData key find_customer treats as an idST_AUTH_HOST/ST_API_HOST default to production; ServiceTitan's sandbox uses different hosts (*-integration.*) and both must match the credentials' environment, or the token request 400s with invalid_client.
Registering it with a client
Both routes need an absolute path to the servicetitan-mcp console script in this project's .venv, and ST_ENV_FILE. Neither carries a secret.
Claude Code:
claude mcp add servicetitan -s user \
-e ST_ENV_FILE=/home/you/.config/servicetitan-mcp/env \
-- /home/you/servicetitan-mcp/.venv/bin/servicetitan-mcp-s user registers it for every project on the machine — prefer it over -s project, which writes a committable .mcp.json. Verify with claude mcp list (✔ Connected).
Claude Desktop: copy claude_desktop_config_example.json, fix the two paths, and merge it into the client's own config.
Run the tests with .venv/bin/pytest. Nothing in the suite makes a live ServiceTitan call — every test uses the fake clients in tests/conftest.py.
Available Tools
6 toolscustomer_moneyA
Balance, estimates, invoices, and payments for one customer, in one response.
Use for any "what do they owe" or billing-history question. Needs a customer id; find_customer supplies one.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| customer_id | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the burden. It discloses the content of the response (balance, invoices, etc.) and that it comes 'in one response', but does not explicitly state read-only behavior, error handling, or any side effects. It adds some context beyond a bare description, but not rich behavioral detail.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences and every word earns its place. The first sentence states the tool's output and scope; the second gives usage guidance and prerequisite. It is concise, front-loaded, and free of fluff.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool has an output schema (per context signals), the description does not need to explain return values, and it does cover the main use case and key parameter. However, the lack of any explanation for 'limit' leaves a small but real completeness gap, so not a perfect 5.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate. It only implicitly explains customer_id ('Needs a customer id'), but the 'limit' parameter is left completely unexplained. Without any schema descriptions or additional context, an agent cannot know what 'limit' controls (e.g., number of invoices or transactions). This is a notable gap.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool returns a consolidated set of financial data (balance, estimates, invoices, payments) for a single customer, using specific nouns and a clear scope ('for one customer'). This distinguishes it from siblings like customer_overview or find_customer, which likely cover broader or different data.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states when to use ('what do they owe' or billing-history questions) and mentions the prerequisite of a customer ID, pointing to find_customer as the source. However, it does not explicitly mention when not to use it or how it compares to customer_overview, so it lacks full when-not coverage.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
customer_overviewA
Full card for one customer: contacts, locations, 5 recent jobs, external ids.
Needs a ServiceTitan customer id; use find_customer if you only have a name or phone.
| Name | Required | Description | Default |
|---|---|---|---|
| customer_id | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden. It discloses the required input type and lists the returned data categories. It does not explicitly state whether the tool is read-only or what happens if the ID does not exist, but the described behavior ('full card') implies a read operation. Slightly more detail on error handling would push it higher.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, perfectly front-loaded: the first sentence states the tool's purpose and contents; the second gives prerequisite and alternative. Every word earns its place with no redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter tool with an output schema, the description covers all necessary context: what data is returned, what input is needed, and when to use a different tool. The presence of an output schema means return values need not be spelled out. This description is complete and self-sufficient.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0% (no parameter description), so the description must compensate. It adds key meaning by specifying the customer_id must be a 'ServiceTitan customer id', which distinguishes it from other possible identifiers. It stops short of providing examples or format hints, but the single parameter is adequately clarified.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states a specific function: returns a 'full card' for one customer, enumerating contents (contacts, locations, 5 recent jobs, external ids). This is distinct from sibling tools like find_customer (search by name/phone) and job_detail (single job focus).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit when-to-use guidance: 'Needs a ServiceTitan customer id' and gives a direct alternative: 'use find_customer if you only have a name or phone.' This tells the agent exactly when to choose this tool over a sibling.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
find_by_external_idA
Find a customer by an externalData key/value pair — another system's id.
Requires ST_APP_GUID. Use when you hold an id from a CRM synced into ServiceTitan.
| Name | Required | Description | Default |
|---|---|---|---|
| key | Yes | ||
| value | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden of behavioral disclosure. It discloses a key prerequisite (ST_APP_GUID) and clarifies the matching mechanism ('externalData key/value pair'), which is useful. However, it does not describe return behavior, edge cases (e.g., not found, multiple matches), or potential side effects, leaving notable gaps for a no-annotation tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact: two sentences, no fluff, and it front-loads the core purpose. The prerequisite and use-case are included without redundancy, making every sentence earn its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a low-complexity tool with only two parameters and an output schema, the description provides adequate context: the lookup mechanism, the source of the external id, and a required app GUID. It does not explain the response when no customer is found, but the output schema likely covers return structure. Slightly more context about limits or filtering could improve completeness, so it stops short of a perfect score.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0%, so the description must compensate for the minimal schema. It does add meaning by explaining that 'key' and 'value' refer to an externalData pair and that the value is another system's id. Yet it lacks specifics such as accepted key formats, value types, or examples, so it does not fully bridge the coverage gap.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states a specific action: 'Find a customer by an externalData key/value pair.' It also explains that the value is 'another system's id', which distinguishes it from the sibling 'find_customer' tool by indicating it searches through external data rather than standard customer fields.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides clear usage context: 'Use when you hold an id from a CRM synced into ServiceTitan.' This tells the agent when the tool is appropriate, though it does not explicitly mention when not to use it or name alternative tools. This is clear guidance but lacks explicit exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
find_customerA
Find a customer by name, phone, or external id — no ServiceTitan id needed.
One match returns the full card (contacts, locations, recent jobs); several return a short list to choose from. Email lookup is not supported by ServiceTitan.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| query | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the burden of disclosing behavior. It explains the one-match versus several-match result behavior and explicitly notes the email lookup limitation. However, it does not mention what happens when there are zero matches or whether any permissions are needed, leaving minor gaps.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is exceptionally concise, using only three short lines to cover purpose, search keys, result behavior, and a limitation. Every sentence earns its place, and the most important information is front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the relatively simple tool, an output schema, and clear sibling context, the description is largely complete. It covers the main behavioral nuance (single vs. multiple matches) and a critical limitation, though it omits the no-match case and the exact effect of the limit parameter.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema has no descriptions, but the description gives useful meaning to the 'query' parameter by listing valid search fields (name, phone, external id). The 'limit' parameter is not explained beyond its schema default, so the description only partially compensates for the 0% schema coverage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool finds a customer by name, phone, or external id, making the purpose and resource explicit. It also distinguishes itself from siblings like find_by_external_id by noting that no ServiceTitan id is needed, and clarifies it is customer-focused rather than job-focused.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description conveys when to use this tool: when you have a name, phone, or external id rather than a ServiceTitan id. It also provides an explicit exclusion ('Email lookup is not supported'), which helps steer users away from unsupported inputs, though it does not name specific alternative tools for those cases.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
find_jobsA
List jobs with the next appointment inlined. Dates are ISO (2026-08-01).
An invalid status returns the valid list, not an empty result. Not for finding a customer — use find_customer.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| since | No | ||
| until | No | ||
| status | No | ||
| customer_id | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden for behavioral disclosure. It goes beyond a simple statement by revealing that invalid status values return the valid list rather than an empty result—a non-obvious failure mode—and that dates use ISO format. The inlined next appointment also hints at the return structure. This is substantial added context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three concise sentences each add unique value: purpose and a key feature, date format, and an exclusion with an alternative. The information is front-loaded and there is no fluff or redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the presence of an output schema, the description is not required to explain return values. It covers purpose, date format, an important edge case, and an explicit alternative. For a 5-parameter tool with no annotations, it could further clarify filters like customer_id or pagination, but the existing guidance is sufficiently robust for an agent to avoid major pitfalls.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate, but it only partially does. It clarifies date format for since/until and mentions status behavior, but leaves customer_id and limit semantically implicit. The parameter names are self-explanatory, yet the description could do more to define filtering semantics.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb+resource ('List jobs') and a distinguishing feature ('with the next appointment inlined'), making the tool's purpose immediately clear. It also explicitly differentiates from the sibling tool find_customer, reducing ambiguity about scope.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides a clear when-not case: 'Not for finding a customer — use find_customer.' This directs the agent away from a likely misuse. It also notes a behavioral edge case (invalid status returns a valid list) that affects usage, though it does not enumerate all sibling distinctions (e.g., job_detail).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
job_detailA
One job with its appointments and assigned technicians.
For several jobs at once — a customer's list, a date range, a status — use find_jobs instead of calling this once per job.
| Name | Required | Description | Default |
|---|---|---|---|
| job_id | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the burden of disclosing behavior. It adds useful context by noting that appointments and technicians are included, but it does not explicitly state that the operation is read-only nor mention any potential side effects or prerequisites. For a simple retrieval, this is adequate but not comprehensive.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise and well-structured: the first sentence states the core purpose, and the second sentence gives usage guidance without redundancy. Every word adds value, and it is front-loaded with the primary information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's simplicity (single parameter, no enums) and the existence of an output schema that describes return values, the description covers the essential context. It explains what the tool does and when to use an alternative. It lacks information on error handling or edge cases, but for a straightforward retrieval tool, it is sufficiently complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With 0% schema description coverage, the description must compensate for parameter documentation. The only parameter, job_id, is self-explanatory by name and type, and the description implies its role by stating it returns 'one job'. However, it does not provide additional detail such as acceptable formats or source of the ID. The meaning is clear enough, but more explicit description would be better.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool returns a single job with its appointments and assigned technicians. It distinguishes this from the sibling tool find_jobs, which handles multiple jobs, making the purpose unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The second sentence explicitly provides guidance on when to use find_jobs instead, listing examples like a customer's list, date range, or status. This effectively differentiates the tool from its sibling and prevents misuse.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
6 tool updates
v0.1.0- First observed
customer_money - First observed
customer_overview - First observed
find_by_external_id - First observed
find_customer - First observed
find_jobs - First observed
job_detail
TDQS
Scored across 6 tools
find_customer, customer_overview, and find_by_external_id all locate customers, with find_customer already supporting external IDs, causing potential confusion. However, descriptions clarify that customer_overview requires a ServiceTitan ID and find_by_external_id needs a specific externalData key, so agents can usually pick correctly.
Tool names mix patterns: find_customer and find_jobs use verb_noun, customer_overview and job_detail use noun_noun, and find_by_external_id uses verb_preposition_noun. All are snake_case and readable, but the inconsistent structure makes it harder to predict tool names.
Six tools is a reasonable count for a customer/job lookup server, not overwhelming. However, the overlap between find_customer and find_by_external_id means the count is slightly padded, preventing a perfect score.
The server covers customer retrieval, job lookups, and billing summaries well for read-only scenarios. Missing write operations like creating/updating customers or jobs, and there is no direct way to fetch all appointments independently, which are notable gaps for a full lifecycle.
Maintenance
Related MCP Connectors
Read-only MCP server for ClassQuill, a tutoring-business-management platform.
Read-only MCP server for Muovi, Argentina's trust-first local services marketplace (6 tools).
Read-only MCP server for turva.dev's published service catalog, pricing and contact details. Five tools return JSON, including dated agent-readiness and security evidence with verification links. Connect over Streamable HTTP without an API key. The server answers questions about turva.dev and does not scan other websites or run audits.
Authenticated, read-only field-service workspace tools for FieldRobin businesses.
Related MCP Servers
- FlicenseDqualityDmaintenanceAn MCP server that allows AI assistants to interact with the ServiceTitan API, requiring client credentials for authentication.4-
- AlicenseBqualityAmaintenanceConnects AI assistants to ServiceTitan with a default catalog of 264 read-only tools for CRM, dispatch, accounting, reporting, and analytics. Supports focused discovery profiles; write operations are experimental and require explicit opt-in.264434 npm2MIT
- AlicenseAqualityDmaintenanceCustomer-hosted, read-only MCP server for Jobber operations workflows. It helps owners query Jobber for action lists, overdue invoices, stale requests, estimate/job follow-up, and safe read-only GraphQL validation.631 npm1MIT
- AlicenseNot gradedqualityDmaintenanceA remote MCP server that exposes the ServiceTitan API as tools, enabling Claude and other clients to manage customers, jobs, pricebook, invoicing, and more with write safety and built-in observability.MIT