create_quote_snapshot
Notifications · Write · account-scoped · server-priced
Turn a completed labor selection into the shop's quote snapshot and get the quoteId the send tool takes.
Usage
The bridge between the read surface and send_quote_notification: a completed service_advisor_lookup ends at selectedTotal, and this tool turns that selection into MotorAdvisor's own commercial record — returning the q_… id, the public approval quoteUrl, and the priced lines.
The caller supplies identifiers, never money. Hours, names, and prices are re-read from MOTOR live at snapshot time, so a figure that was never computable cannot be snapshotted and no price in the record came from model output. The persisted record carries exact-cent money and MOTOR content identifiers only — descriptive MOTOR text is re-materialized on every render, never stored (invariant 4). MOTOR itself is only ever read; the write lands in MotorAdvisor's store and appears in the call trace as its own step.
customerContact is a lookup key, not an address: it links the shop's existing consent record to the quote so send_quote_notification can deliver it. A contact with no consent on file leaves the quote created but unlinked — sending refuses with no-consent, and the advisor shares quoteUrl by hand, exactly like the web flow.
The estimate says whom it is for (MOT-339). Pass customerName and the approval page and the quote email carry a "Prepared for" line — the name, plus customerContact printed as the email or phone it is by shape. Without a name the estimate carries no customer line; the contact alone stays a lookup key.
Manual lines (manualLines) carry the shop's own figures for work with no MOTOR book time — recharge, sublet, shop supplies — at the shop's stored rate unless a rate is given.
Parts ride on the quote (MOT-325). parts takes the parts the customer approves together with the labor, in the same shape issue_invoice bills: name, optional oemPartNumber and quantity, and the advisor's unitPriceCents — the SELL price, never MOTOR's list price, which a lookup returns as an anchor only. A part without a sell price refuses the call, exactly as the web quote refuses to send with one unpriced. The response reports the parts, laborTotal, partsTotal and the full total the customer will approve; the customer's approval page lists the parts under the labor.
The snapshot is created open — it reads as sent only after send_quote_notification actually delivers it. When this quote replaces an earlier one (findings grew the work), pass supersedes with the old q_… id: approval of this quote retires the old one to a terminal superseded status and its approval link goes dead, so two live approvals can never both be billed.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
baseVehicleId | positive integer | Yes | The baseVehicleId field from a resolve_vehicle result. NOT the vehicleId. Example: 22124 |
vehicleDescription | string | Yes | The resolved vehicle identity, from resolve_vehicle's description. Example: "2010 Honda Civic LX" |
applicationIds | array of positive integer | Yes | The labor operations the advisor selected — the same applicationIds a completed service_advisor_lookup costed into selectedTotal (for a variant group, the specific variant's id). Hours and prices are re-read from MOTOR server-side; ids that match no work time refuse the whole call. Example: [23615369] |
laborRateCents | positive integer | No | Labor rate in integer cents/hour. Omit to use the shop's stored rate. Example: 15000 |
manualLines | array of object | No | Shop-authored lines with no MOTOR book time (recharge, sublet, shop supplies — parts go in parts). Priced at the given hours — these are the shop's own figures. Example: [{"name": "Refrigerant recharge", "bookHours": 0.5}] |
manualLines[].name | string | No | Shop-authored line text. Example: "Refrigerant recharge" |
manualLines[].bookHours | number | No | Hours for this line. Example: 0.5 |
manualLines[].rateCentsPerHour | positive integer | No | Line-specific rate in cents/hour; omit for the shop rate. Example: 15000 |
parts | array of object | No | MOT-325: the priced parts the customer approves together with the labor — the same shape issue_invoice bills. Every part needs the advisor's unitPriceCents. Omit when the quote is labor only. Example: [{"name": "A/C Compressor", "oemPartNumber": "38810-RNA-A02", "unitPriceCents": 103035}] |
parts[].name | string | No | Part name as quoted. Example: "A/C Compressor" |
parts[].oemPartNumber | string | No | OEM part number, when known. Example: "38810-RNA-A02" |
parts[].quantity | positive integer | No | How many. Defaults to 1. Example: 1 |
parts[].unitPriceCents | positive integer | No | SELL price in integer cents, set by the advisor — a part without one is refused, as the web quote and issue_invoice refuse it. MOTOR provides the OEM LIST price (listPriceCents on a lookup's parts rows) as an anchor; it is not the sell price and is never quoted automatically. Example: 103035 |
customerContact | string | No | The customer's email or phone, used ONLY to link the shop's existing consent record to this quote so send_quote_notification can reach them. It is never a send destination: a contact with no consent on file leaves the quote unlinked and sending will refuse with no-consent. With customerName it is also printed on the estimate page as the contact the estimate was prepared for. Example: "casey@example.com" |
customerName | string | No | MOT-339: the customer's name, printed on the estimate page and in the quote email as whom the estimate was prepared for — the same name the shop wrote on the work order. Omit it and the estimate carries no customer line. One line. Example: "Casey Rivera" |
supersedes | string | No | The q_… id of the shop's earlier quote this one REPLACES — use it when findings grow the work (diagnose → full quote). When the customer approves THIS quote, the superseded one retires: terminal status, dead approval link, and check_quote_status points from it to this one. Example: "q_1facac2305cc6b3901077d3b" |
Description advertised to clients
The exact description a fully entitled MCP client discovers — the workflow contracts travel with the tool, so a client with no system prompt still uses it correctly. An account missing a licence for content this tool reaches sees the same text behind a [Licensed content — not enabled on this account] or [Partially available] notice:
Create the shop's quote snapshot from a COMPLETED labor selection and return its quoteId (q_…) — the bridge between service_advisor_lookup and send_quote_notification. THIS TOOL WRITES a commercial record owned by the authenticated account's shop (MOTOR itself stays read-only; the call trace shows the write as its own step). Pass the applicationIds a lookup costed into selectedTotal — hours, names, and prices are re-read from MOTOR server-side, so a total that was never computable cannot be snapshotted and no labor figure is taken from input. Parts the customer approves go in parts, each with the advisor's SELL price (the customer approves labor and parts together, as on the web); an unpriced part refuses the call. The persisted record holds exact-cent money and MOTOR content identifiers only, never MOTOR text. Returns quoteId, the public approval quoteUrl (shareable by hand), the priced lines, and the total. The quote is created OPEN — it reads as sent only after send_quote_notification actually delivers it. When this quote REPLACES an earlier one (findings grew the work), pass supersedes with the old q_… id: approval of this quote retires the old one so two live approvals can never both be billed. Use create_quote_snapshot AFTER the advisor confirms the selection; then send_quote_notification with the quoteId. The evaluation sandbox covers model years 2010, 2015, and 2016 ONLY — an UnknownApplicationIds refusal for another vehicle is correct behaviour, not an error.
Example — Snapshot a compressor R&R selection
Illustrative payload shapes (ids and token elided); the money is the server's own computation at the shop's $150/hr rate.
Request
{
"baseVehicleId": 22124,
"vehicleDescription": "2010 Honda Civic LX",
"applicationIds": [
23615369
],
"parts": [
{
"name": "Compressor Assembly",
"oemPartNumber": "38810-RNA-A02",
"unitPriceCents": 103035
}
],
"customerContact": "customer@example.com",
"customerName": "Casey Rivera"
}
Response
{
"quoteId": "q_9c41f0b2ae77d31c55e00e12",
"quoteUrl": "https://motoradvisor.app/q/…",
"vehicle": "2010 Honda Civic LX",
"lines": [
{
"name": "Air Conditioning Compressor R&R",
"hours": 1.9,
"amount": "$285.00"
}
],
"parts": [
{
"name": "Compressor Assembly",
"oemPartNumber": "38810-RNA-A02",
"quantity": 1,
"unitPrice": "$1,030.35",
"amount": "$1,030.35"
}
],
"laborTotal": "$285.00",
"partsTotal": "$1,030.35",
"total": "$1,315.35",
"expiresAt": "2026-08-28T13:30:00.000Z",
"consentNote": "Consent on file for this contact — send_quote_notification can deliver this quote."
}
applicationIds that match no work time on the vehicle refuse the whole call (UnknownApplicationIds) — nothing is silently dropped, and nothing is snapshotted. A part without unitPriceCents refuses the same way (InvalidInput).