ReelQuoteAI Survey Tool
Filmed moving surveys, itemized inventories, firm quotes and bookings for moving companies.
使うべきか
品質と安全性
検出事項(6)
- LOWlist_home_profiles 内
- LOWreport_outcome 内
- LOWquote_inventory 内
- LOWcreate_upload_url 内
- LOWlist_webhook_deliveries 内
- LOWreplay_webhook_delivery 内
ツール定義とプロトコルへの準拠に関する自動分析に基づいています。
コンテキストコスト
これは、サーバーのツールがモデルのコンテキストに読み込まれるたびに消費されるおおよそのトークン数です。数が多いほど、ほかのタスクに使える注意が減ります。
インストール
ワンクリックインストール
これを `claude_desktop_config.json` ファイルに追加してください:
{
"mcpServers": {
"survey-tool": {
"url": "https://widget.reelquoteai.com/mcp"
}
}
}リモートエンドポイント
https://widget.reelquoteai.com/mcpstreamable-httpできること
ツール一覧
ツール(32)
🟢get_session
Resolve the key to its mover (name, branding, config). Scope: quote.
入力スキーマ
{
"type": "object",
"properties": {},
"additionalProperties": false
}🟢list_home_profiles
Home sizes for the ballpark step (2br_apt, 3br_house, …). Scope: quote.
入力スキーマ
{
"type": "object",
"properties": {},
"additionalProperties": false
}⚪ballpark_estimate(dwelling, move_date, origin, destination, distance_miles)
Instant estimate range from home size + addresses. Free; never bookable. Scope: quote.
入力スキーマ
{
"type": "object",
"properties": {
"dwelling": {
"type": "string",
"description": "A `dwelling` from GET /home-profiles",
"example": "2br_apt"
},
"move_date": {
"type": "string",
"format": "date",
"description": "Lets date-based pricing apply. Optional but strongly recommended."
},
"origin": {
"type": "object",
"properties": {
"address": {
"type": "string",
"description": "Free text as typed, or as resolved by /places/resolve"
},
"unit": {
"type": "string",
"description": "Apartment or suite. A number like 2104 lets the floor be inferred."
},
"place_id": {
"type": "string",
"description": "From /places/resolve, when address autocomplete was used"
},
"dwelling": {
"type": "string",
"description": "apartment, condo, townhouse, house or storage — free text is normalised"
}
},
"description": "One end of the move, as the customer gives it."
},
"destination": {
"type": "object",
"properties": {
"address": {
"type": "string",
"description": "Free text as typed, or as resolved by /places/resolve"
},
"unit": {
"type": "string",
"description": "Apartment or suite. A number like 2104 lets the floor be inferred."
},
"place_id": {
"type": "string",
"description": "From /places/resolve, when address autocomplete was used"
},
"dwelling": {
"type": "string",
"description": "apartment, condo, townhouse, house or storage — free text is normalised"
}
},
"description": "One end of the move, as the customer gives it."
},
"distance_miles": {
"type": "number",
"description": "Only used when no route can be found from the addresses"
}
},
"required": [
"dwelling"
],
"additionalProperties": false
}🟡create_job(idempotency_key, quote_kind, dwelling, move_date, origin, ...)
Start a job (the survey container). Contact details are NOT accepted here — funnel rule. Send `Idempotency-Key` so a retry cannot create a second job. Scope: jobs:write.
入力スキーマ
{
"type": "object",
"properties": {
"idempotency_key": {
"type": "string",
"minLength": 1,
"maxLength": 255,
"pattern": "^[\\x21-\\x7E]+$",
"description": "Any unique string per intended job (a UUID, your order id). For 24 hours, the same key with the same body returns the ORIGINAL 201 body with `Idempotent-Replayed: true` and creates nothing; the same key with a different body is a 422 `idempotency_key_reused`; the same key while the first request is still running is a 409 `idempotency_in_progress` with `Retry-After: 1`. Scoped to your account and partitioned by the calling key's kind and mode. Omit it and the request is never deduplicated."
},
"quote_kind": {
"type": "string",
"enum": [
"ballpark",
"firm"
],
"default": "firm",
"description": "What the job is for. A firm job becomes bookable once filmed."
},
"dwelling": {
"type": "string",
"description": "What the customer says their home is (any wording; stored beside what the video measures)",
"example": "2br_apt"
},
"move_date": {
"type": "string",
"format": "date",
"description": "YYYY-MM-DD"
},
"origin": {
"type": "object",
"properties": {
"address": {
"type": "string",
"description": "Free text as typed, or as resolved by /places/resolve"
},
"unit": {
"type": "string",
"description": "Apartment or suite. A number like 2104 lets the floor be inferred."
},
"place_id": {
"type": "string",
"description": "From /places/resolve, when address autocomplete was used"
},
"dwelling": {
"type": "string",
"description": "apartment, condo, townhouse, house or storage — free text is normalised"
}
},
"description": "One end of the move, as the customer gives it."
},
"destination": {
"type": "object",
"properties": {
"address": {
"type": "string",
"description": "Free text as typed, or as resolved by /places/resolve"
},
"unit": {
"type": "string",
"description": "Apartment or suite. A number like 2104 lets the floor be inferred."
},
"place_id": {
"type": "string",
"description": "From /places/resolve, when address autocomplete was used"
},
"dwelling": {
"type": "string",
"description": "apartment, condo, townhouse, house or storage — free text is normalised"
}
},
"description": "One end of the move, as the customer gives it."
}
},
"additionalProperties": false
}🟢list_jobs(limit, status, cursor)
List jobs for the tenant, newest first. Secret key only. Paged: pass `limit` (1–100, default 25), optional `status`, and `cursor` from the previous page's `next_cursor` (null on the last page; a bad cursor is a 400). Scope: jobs:read.
入力スキーマ
{
"type": "object",
"properties": {
"limit": {
"type": "integer",
"minimum": 1,
"maximum": 100,
"default": 25
},
"status": {
"type": "string",
"enum": [
"created",
"uploading",
"uploaded",
"queued",
"processing",
"complete",
"quoted",
"booked_requested",
"booked",
"declined",
"error"
],
"description": "Funnel order. **created** — The job exists. Nothing uploaded yet. **uploading** — An upload URL was issued; the video is on its way. **uploaded** — A chunked upload was assembled (a single PUT skips this state). **queued** — POST /submit accepted; waiting its turn to be analysed. **processing** — The walkthrough is being analysed. Poll GET /jobs/{id}; typically 1–4 minutes. **complete** — Inventory ready. The firm quote is computed on the first read. **quoted** — A firm, bookable quote exists (set by the first GET after complete). **booked_requested** — The customer asked to book; the mover has not answered yet. **booked** — The mover confirmed the booking. **declined** — The mover turned the request down. `decline_reason` may say why. **error** — Analysis failed. `error` carries a message; upload again or contact the mover."
},
"cursor": {
"type": "string",
"description": "Opaque; from the previous response's next_cursor."
}
},
"additionalProperties": false
}🟢get_job(job_id)
Full job state: status, inventory, quote, access, terms, move_date. Scope: jobs:read.
入力スキーマ
{
"type": "object",
"properties": {
"job_id": {
"type": "string",
"description": "The job's ULID"
}
},
"required": [
"job_id"
],
"additionalProperties": false
}🟡set_contact(job_id, email, name, consent, move_date)
Store customer email + optional name + move date. Sends NOTHING — required before upload-url. Scope: jobs:write.
入力スキーマ
{
"type": "object",
"properties": {
"job_id": {
"type": "string",
"description": "The job id (ULID)"
},
"email": {
"type": "string"
},
"name": {
"type": "string"
},
"consent": {
"type": "boolean",
"description": "Must be true. Express consent to email about this quote."
},
"move_date": {
"type": [
"string",
"null"
],
"format": "date",
"description": "Null clears; absent leaves unchanged."
}
},
"required": [
"job_id",
"email",
"consent"
],
"additionalProperties": false
}🟡create_upload_url(job_id, content_type, size, consent)
Presigned upload for the walkthrough video. Requires stored contact email. Body: { content_type, consent, size }. Send the byte size: at or above 16 MB the response carries `multipart` (an upload_id, a part_size and a presigned PUT per part) instead of `upload`, and each part is retried on its own rather than restarting the whole video. Report each part's ETag to /upload-complete. Below the threshold, or if opening a multipart upload fails, the response is a single `upload`. Scope: jobs:write.
入力スキーマ
{
"type": "object",
"properties": {
"job_id": {
"type": "string",
"description": "The job id (ULID)"
},
"content_type": {
"type": "string",
"description": "A video/* MIME type",
"example": "video/mp4"
},
"size": {
"type": "integer",
"description": "Byte size of the file. Decides single vs multipart."
},
"consent": {
"type": "boolean",
"description": "Must be true: the customer agrees the video will be analysed and shared with the mover."
}
},
"required": [
"job_id",
"content_type",
"consent"
],
"additionalProperties": false
}🟡complete_upload(job_id, upload_id, parts)
Assemble a chunked upload. Body: { upload_id, parts: [{ part_number, etag }] }. Only after every part has been PUT; then call /submit. Scope: jobs:write.
入力スキーマ
{
"type": "object",
"properties": {
"job_id": {
"type": "string",
"description": "The job id (ULID)"
},
"upload_id": {
"type": "string",
"description": "From the multipart ticket"
},
"parts": {
"type": "array",
"items": {
"type": "object",
"properties": {
"part_number": {
"type": "integer",
"description": "1-based"
},
"etag": {
"type": "string",
"description": "The ETag response header of that part's PUT"
}
}
},
"description": "Every part, in any order"
}
},
"required": [
"job_id",
"upload_id",
"parts"
],
"additionalProperties": false
}🟡submit_job(job_id)
Queue the uploaded video for analysis. Poll GET /jobs/{id} for status. Scope: jobs:write.
入力スキーマ
{
"type": "object",
"properties": {
"job_id": {
"type": "string",
"description": "The job id (ULID)"
}
},
"required": [
"job_id"
],
"additionalProperties": false
}🟡hand_off_video(job_id, url, content_type, consent)
Server-side handoff: give us a public https URL of the walkthrough instead of uploading it from a browser. Secret key only. We fetch it (up to 500 MB, streamed) and the job runs exactly as an upload would — poll GET /jobs/{id}. Needs the stored contact email and consent, like /upload-url. Replaces /upload-url, /upload-complete and /submit for a caller that already holds the file. A job whose fetch failed (`error`) may be handed a new URL. Counts toward the mover's daily analysis cap (429). Scope: jobs:write.
入力スキーマ
{
"type": "object",
"properties": {
"job_id": {
"type": "string",
"description": "The job id (ULID)"
},
"url": {
"type": "string",
"description": "Public https URL that serves the video (a signed cloud-storage link is ideal). Private hosts and http are refused."
},
"content_type": {
"type": "string",
"description": "A video/* MIME type when you know it; otherwise the host's Content-Type must be video/*",
"example": "video/quicktime"
},
"consent": {
"type": "boolean",
"description": "Must be true: the customer agrees the video will be analysed and shared with the mover."
}
},
"required": [
"job_id",
"url",
"consent"
],
"additionalProperties": false
}⚪confirm_access(job_id, origin, destination, move_date)
Confirm floors / elevator / stairs / carry distance for both ends. Scope: jobs:write.
入力スキーマ
{
"type": "object",
"properties": {
"job_id": {
"type": "string",
"description": "The job id (ULID)"
},
"origin": {
"type": "object",
"properties": {
"floor": {
"type": "integer",
"description": "0–199"
},
"elevator": {
"type": "boolean"
},
"stairs_flights": {
"type": "integer",
"description": "0–30"
},
"parking_to_door_ft": {
"type": "integer",
"description": "0–2000"
}
},
"description": "What a customer can actually answer about one end. Only these fields are writable; inferred values are kept."
},
"destination": {
"type": "object",
"properties": {
"floor": {
"type": "integer",
"description": "0–199"
},
"elevator": {
"type": "boolean"
},
"stairs_flights": {
"type": "integer",
"description": "0–30"
},
"parking_to_door_ft": {
"type": "integer",
"description": "0–2000"
}
},
"description": "What a customer can actually answer about one end. Only these fields are writable; inferred values are kept."
},
"move_date": {
"type": [
"string",
"null"
],
"format": "date",
"description": "YYYY-MM-DD"
}
},
"required": [
"job_id"
],
"additionalProperties": false
}🟡edit_items(job_id, items)
Edit inventory (quantity, exclude, add by item_key). Response carries the re-priced quote. Scope: jobs:write.
入力スキーマ
{
"type": "object",
"properties": {
"job_id": {
"type": "string",
"description": "The job id (ULID)"
},
"items": {
"type": "array",
"items": {
"oneOf": [
{
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "An existing row's id"
},
"quantity": {
"type": "integer",
"description": "0 allowed"
},
"excluded": {
"type": "boolean",
"description": "true = staying. Rows are never deleted."
},
"notes": {
"type": "string",
"description": "Up to 500 chars"
}
},
"required": [
"id"
],
"description": "Edit a row"
},
{
"type": "object",
"properties": {
"item_key": {
"type": "string",
"description": "From GET /catalog"
},
"quantity": {
"type": "integer",
"description": "Default 1"
},
"room": {
"type": "string",
"description": "Default 'Added by customer'"
},
"notes": {
"type": "string"
}
},
"required": [
"item_key"
],
"description": "Add a row (source becomes customer_added)"
}
]
},
"description": "Non-empty. Any edit clears the customer's approval and the e-signature."
}
},
"required": [
"job_id",
"items"
],
"additionalProperties": false
}⚪approve_inventory(job_id, approved, notes)
Customer sign-off on the inventory, plus free-text notes. Scope: jobs:write.
入力スキーマ
{
"type": "object",
"properties": {
"job_id": {
"type": "string",
"description": "The job id (ULID)"
},
"approved": {
"type": "boolean",
"description": "Default true. false withdraws the sign-off."
},
"notes": {
"type": "string",
"description": "Up to 2000 chars; absent leaves unchanged"
}
},
"required": [
"job_id"
],
"additionalProperties": false
}🟢acknowledge_gaps(job_id, acknowledged)
Record the customer's answer to the capture-gap prompts (inventory.gaps on GET /jobs/{id}). Body: { acknowledged: true } stamps "nothing is missing"; false clears it. The prompts themselves are recomputed from the current inventory on every read, so fixing a gap removes its prompt without this call. Scope: jobs:write.
入力スキーマ
{
"type": "object",
"properties": {
"job_id": {
"type": "string",
"description": "The job id (ULID)"
},
"acknowledged": {
"type": "boolean",
"description": "Default true"
}
},
"required": [
"job_id"
],
"additionalProperties": false
}🟡send_resume_link(job_id, email, name, consent)
Email the customer their resume link. Only on explicit request. Scope: jobs:write.
入力スキーマ
{
"type": "object",
"properties": {
"job_id": {
"type": "string",
"description": "The job id (ULID)"
},
"email": {
"type": "string"
},
"name": {
"type": "string"
},
"consent": {
"type": "boolean",
"description": "Must be true"
}
},
"required": [
"job_id",
"email",
"consent"
],
"additionalProperties": false
}⚪request_booking(job_id, name, email, phone, approve, ...)
Request the booking. Idempotent per job; requires approve:true (types-name-as-signature approval of the inventory + quote, stored with a quote snapshot) and accept_terms when the mover has terms. Scope: jobs:write.
入力スキーマ
{
"type": "object",
"properties": {
"job_id": {
"type": "string",
"description": "The job id (ULID)"
},
"name": {
"type": "string",
"description": "The submitted name is the signature on the approval."
},
"email": {
"type": "string"
},
"phone": {
"type": "string"
},
"approve": {
"type": "boolean",
"description": "Must be true: approves the inventory as complete and this quote. Cleared if the inventory is edited afterwards."
},
"accept_terms": {
"type": "boolean",
"description": "Required (true) when the job's `terms.terms` is non-null"
}
},
"required": [
"job_id",
"name",
"email",
"approve"
],
"additionalProperties": false
}⚪report_outcome(job_id, actual_hours, actual_total_cents, actual_crew, notes)
Report actuals (hours, final cents, crew). Secret key only. Scope: outcomes.
入力スキーマ
{
"type": "object",
"properties": {
"job_id": {
"type": "string",
"description": "The job id (ULID)"
},
"actual_hours": {
"type": "number"
},
"actual_total_cents": {
"type": "integer",
"description": "What was actually billed, in integer cents. Currency travels beside it — never assume USD."
},
"actual_crew": {
"type": "integer"
},
"notes": {
"type": "string",
"description": "Up to 1000 chars"
}
},
"required": [
"job_id"
],
"additionalProperties": false
}🟢get_cube_sheet_csv(job_id)
Itemized cube sheet as CSV (Excel-ready): rooms, volumes, weights, exclusions. Secret key only. Scope: jobs:read.
入力スキーマ
{
"type": "object",
"properties": {
"job_id": {
"type": "string",
"description": "The job id (ULID)"
}
},
"required": [
"job_id"
],
"additionalProperties": false
}🟢get_date_prices(job_id)
This job's quote priced across a ~2-week window from the mover's date rules. flat=true means every day prices the same. Scope: jobs:read.
入力スキーマ
{
"type": "object",
"properties": {
"job_id": {
"type": "string",
"description": "The job id (ULID)"
}
},
"required": [
"job_id"
],
"additionalProperties": false
}🔴set_move_date(job_id, date)
Set or clear the move date; invalidates and recomputes the quote. Refused after a booking is requested. Scope: jobs:write.
入力スキーマ
{
"type": "object",
"properties": {
"job_id": {
"type": "string",
"description": "The job id (ULID)"
},
"date": {
"type": [
"string",
"null"
],
"format": "date",
"description": "YYYY-MM-DD, or null to clear."
}
},
"required": [
"job_id"
],
"additionalProperties": false
}⚪quote_inventory(total_cuft, items, total_weight_lbs, distance_miles, move_date, ...)
Direct pricing for agents: items or total_cuft in, firm quote out. Secret key only. Scope: quote.
入力スキーマ
{
"type": "object",
"properties": {
"total_cuft": {
"type": "number",
"description": "Either this…"
},
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"item_key": {
"type": "string"
},
"quantity": {
"type": "integer"
}
}
},
"description": "…or this, priced from the catalog"
},
"total_weight_lbs": {
"type": "number",
"description": "Optional with total_cuft; derived from items otherwise"
},
"distance_miles": {
"type": "number"
},
"move_date": {
"type": "string",
"format": "date",
"description": "YYYY-MM-DD"
},
"origin": {
"type": "object",
"properties": {
"address": {
"type": "string",
"description": "Free text as typed, or as resolved by /places/resolve"
},
"unit": {
"type": "string",
"description": "Apartment or suite. A number like 2104 lets the floor be inferred."
},
"place_id": {
"type": "string",
"description": "From /places/resolve, when address autocomplete was used"
},
"dwelling": {
"type": "string",
"description": "apartment, condo, townhouse, house or storage — free text is normalised"
}
},
"description": "One end of the move, as the customer gives it."
},
"destination": {
"type": "object",
"properties": {
"address": {
"type": "string",
"description": "Free text as typed, or as resolved by /places/resolve"
},
"unit": {
"type": "string",
"description": "Apartment or suite. A number like 2104 lets the floor be inferred."
},
"place_id": {
"type": "string",
"description": "From /places/resolve, when address autocomplete was used"
},
"dwelling": {
"type": "string",
"description": "apartment, condo, townhouse, house or storage — free text is normalised"
}
},
"description": "One end of the move, as the customer gives it."
},
"rate_card_id": {
"type": "string",
"description": "A specific card of this mover's; default the active one"
}
},
"additionalProperties": false
}🟢get_availability(from, days)
Which days the mover has marked off, before a job exists. Soft: a marked day can still be requested — the mover confirms and calls first — so steer to an open day when the customer has a choice. Scope: quote.
入力スキーマ
{
"type": "object",
"properties": {
"from": {
"type": "string",
"format": "date",
"description": "First day, YYYY-MM-DD. Default tomorrow; earlier dates clamp to tomorrow."
},
"days": {
"type": "integer",
"minimum": 1,
"maximum": 90,
"default": 30,
"description": "Window length; values above 90 are capped."
}
},
"additionalProperties": false
}🟢list_keys
List the account's API keys — live and test, active and revoked. Never a raw secret. Secret key with the `keys` scope. Scope: keys.
入力スキーマ
{
"type": "object",
"properties": {},
"additionalProperties": false
}🟡create_key(kind, mode, scopes, label)
Mint a key. The raw secret is in THIS response only. A key can never mint one broader than itself; a test key mints only test keys; a live secret key may mint the sandbox pair. Scope: keys.
入力スキーマ
{
"type": "object",
"properties": {
"kind": {
"type": "string",
"enum": [
"publishable",
"secret"
],
"default": "secret"
},
"mode": {
"type": "string",
"enum": [
"live",
"test"
],
"description": "Default: the calling key's mode."
},
"scopes": {
"type": [
"array",
"null"
],
"items": {
"type": "string",
"enum": [
"quote",
"jobs:read",
"jobs:write",
"outcomes",
"keys"
]
},
"description": "null (default) = everything the kind allows"
},
"label": {
"type": "string",
"description": "Up to 80 chars, e.g. 'CRM sync'"
}
},
"additionalProperties": false
}🟡rotate_key(id, grace_hours)
Replace a key with its twin (same kind, mode, scopes, label). The old key keeps working until `replaced.expires_at` so a deploy can swap values without a gap. Needs the `keys` scope even to rotate itself — rotate a scoped key with a full key or from the dashboard. A key is rotated once: rotating it again is a 409 `key_already_rotated`, and during its grace the old key cannot mint, rotate, revoke or read deliveries (403 `key_rotated`). A test key cannot rotate a live key (403 `mode_mismatch`). Scope: keys.
入力スキーマ
{
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Which key. Default: the key making this call."
},
"grace_hours": {
"type": "integer",
"minimum": 0,
"maximum": 72,
"default": 24,
"description": "0 revokes the old key immediately."
}
},
"additionalProperties": false
}⚪revoke_key(id)
Revoke a key immediately. The only live publishable key is refused (409) — rotate it instead. A test key cannot revoke a live key (403 `mode_mismatch`). Scope: keys.
入力スキーマ
{
"type": "object",
"properties": {
"id": {
"type": "string"
}
},
"required": [
"id"
],
"additionalProperties": false
}🟢list_webhook_deliveries(webhook_id, job_id, limit, before)
The delivery log: every webhook POST for 30 days, newest first, with its outcome and the exact body sent. Secret key with the `keys` scope. A key reads the deliveries of its own mode: live keys the live log, test keys the sandbox log. Scope: keys.
入力スキーマ
{
"type": "object",
"properties": {
"webhook_id": {
"type": "string",
"description": "One endpoint only"
},
"job_id": {
"type": "string",
"description": "Everything sent about one job"
},
"limit": {
"type": "integer",
"minimum": 1,
"maximum": 100,
"default": 25
},
"before": {
"type": "string",
"format": "date-time",
"description": "The previous page's next_before"
}
},
"additionalProperties": false
}🟡replay_webhook_delivery(id)
Re-send one delivery to its endpoint: the same body byte for byte (same envelope id, so your dedupe still works) under a fresh signature, with the usual retries. Returns the replay's own delivery record. Deliveries of the other mode are not found (404). Scope: keys.
入力スキーマ
{
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "A delivery id from the log"
}
},
"required": [
"id"
],
"additionalProperties": false
}🟢search_catalog(q)
Search the cube-sheet catalog (?q=sofa). Scope: quote.
入力スキーマ
{
"type": "object",
"properties": {
"q": {
"type": "string",
"minLength": 2,
"description": "Name, synonym or key fragment"
}
},
"required": [
"q"
],
"additionalProperties": false
}🟡suggest_addresses(input, session_token)
Address autocomplete (proxied; send session_token). Scope: quote.
入力スキーマ
{
"type": "object",
"properties": {
"input": {
"type": "string",
"description": "What the customer has typed so far"
},
"session_token": {
"type": "string",
"description": "Any opaque string, kept for the whole typing session and the resolve call"
}
},
"additionalProperties": false
}⚪resolve_address(place_id, session_token)
Resolve a suggestion to a full address (same session_token). Scope: quote.
入力スキーマ
{
"type": "object",
"properties": {
"place_id": {
"type": "string"
},
"session_token": {
"type": "string"
}
},
"additionalProperties": false
}コミュニティ
エビデンス