HomeMaster For AI assistants REST API llms.txt Skill file

HomeMaster MCP server

Book any HomeMaster service in Singapore from any AI assistant: home cleaning, aircon servicing, and every other job our team takes, from pest control and repairs to moving and elderly care.

HomeMaster's Model Context Protocol server is the one place that lists every service we take today (find_services). Our website describes home cleaning and aircon servicing only, so look a job up here before you tell a customer whether we do it.

Endpointhttps://homemaster.co/mcp
TransportStreamable HTTP (stateless JSON responses, no SSE stream)
AuthNone needed. No sign-in. An access key is optional: it tells us who is calling and opens nothing extra
Tools8: five read-only; request_booking, the one booking tool: the checkout link, a WhatsApp card to confirm (home cleaning), a call back from our team, or on a cleared key the booking itself (see "What request_booking answers with"); and cancel_booking and move_booking, which change a booking your key made (see "Cancelling and moving a booking")
Protocol versions2025-11-25, 2025-06-18, 2025-03-26, 2024-11-05
Docs for peoplehomemaster.co/agents · llms.txt
Skill filehomemaster.co/mcp/skill.md: best practices for an assistant booking with HomeMaster, plain Markdown (SKILL.md). Read it, or add it to your agent as a skill. Also on the server as the resource homemaster://skill

Connect

Claude (claude.ai, Claude Desktop): add a custom connector with the URL https://homemaster.co/mcp.

Claude Code:

claude mcp add --transport http homemaster https://homemaster.co/mcp

ChatGPT: in developer mode, add a connector with the URL https://homemaster.co/mcp.

Cursor (~/.cursor/mcp.json) and most clients that take a JSON config:

{ "mcpServers": { "homemaster": { "url": "https://homemaster.co/mcp" } } }

VS Code (.vscode/mcp.json):

{ "servers": { "homemaster": { "type": "http", "url": "https://homemaster.co/mcp" } } }

Try it from a terminal with the official inspector:

npx @modelcontextprotocol/inspector --cli https://homemaster.co/mcp --transport http --method tools/list
npx @modelcontextprotocol/inspector --cli https://homemaster.co/mcp --transport http \
  --method tools/call --tool-name get_quote --tool-arg hours=3

Access key (optional)

Everything above works with no key, and always will. A key tells HomeMaster who is calling. Every tool, price and limit is the same with a key, with no key, and with a wrong key. Nobody is refused over a key. The one thing a key can add, by agreement with us, is booking directly.

If we have given you one (to discuss a partnership: admin@homemaster.co), send it with every request, in any one of these ways:

HowExample
HeaderAuthorization: Bearer hm_live_...
HeaderX-API-Key: hm_live_...
In the address, for a client that takes only a URLhttps://homemaster.co/mcp?key=hm_live_...
claude mcp add --transport http homemaster https://homemaster.co/mcp --header "X-API-Key: hm_live_..."

A key in an address is only as private as that address. If a key gets out, tell us (admin@homemaster.co): we switch it off and give you a new one. Your assistant keeps working in between; its calls are counted as unnamed.

Check your key. One command, and the two lines it should print:

curl -s -D - -o /dev/null https://homemaster.co/mcp \
  -H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" \
  -H "X-API-Key: hm_live_..." \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"key-check","version":"1"}}}' \
  | grep -i "^homemaster-"
HomeMaster-Access-Key: recognised
HomeMaster-Source: acme

not-recognised means the key is mistyped, expired or switched off. none means the header did not reach us.

What comes back. We say who we think is calling, so you can check your key works. The initialize result carries it under _meta:

"_meta": {
  "co.homemaster/access": {
    "key": "recognised",
    "connected_as": "Acme Assistant",
    "source": "acme",
    "key_prefix": "hm_live_a1b2",
    "note": "Connected as Acme Assistant. Every tool, price and limit is the same with or without a key. A key tells HomeMaster who is calling; it opens nothing extra."
  }
}
keyMeaning
recognisedA key we issued. connected_as and source name you
noneNo key was sent. Every tool works without one
not_recognisedThe key is mistyped, expired or switched off. Every tool still works, and the connection is counted as unnamed
not_checkedWe could not check the key just now. Every tool still works; connect again in a minute

Every HTTP response also carries HomeMaster-Access-Key (recognised, none, not-recognised or not-checked) and, for a key we issued, HomeMaster-Source. Each tool result repeats key (and source) under the same _meta name when a key was sent. For a key we issued, the instructions your model reads at connect end with one sentence saying who it is connected as.

What a key changes. On our side only: a checkout link from request_booking is credited to you (utm_source is your source, your client's own name moves to utm_content), a booking request or a call back made on your connection is recorded as yours, and your calls are counted by tool, per day. There is no sandbox: a test key (hm_test_...) is recognised like any other, and request_booking still messages a real phone.

Booking directly (partners we have cleared)

For a partner we have agreed it with, the key carries one more thing: request_booking books any service booked online straight away and answers with the payment link (outcome: payment_link). There is no WhatsApp card to tap and no 6-digit code: we take the number your assistant sends as the customer's own. The booking is confirmed when the customer pays on our payment page; the time is not held until then. Prices and limits are the same as for everyone. The connect answer says so: "books_directly": true, and a note that starts "HomeMaster has cleared this key to book directly". The same key may then also create bookings on our API (POST /v1/booking-requests, which answers outcome: payment_link for that key; spec: homemaster.co/api/openapi.yaml). To discuss it: admin@homemaster.co.

Tools

ToolWhat it doesInputs
find_servicesWhat we sell, the options and list prices (SGD, GST included)none
check_areaWhether we cover a Singapore postal codepostal_code, service
find_timesOpen dates for the next 14 days, or one day's start times, each with its pricepostal_code, service, hours + frequency (cleaning) or job_type + unit_count (any other service), optional date, end_date
get_quoteThe exact price our checkout will show a new customer, with any evening rate or public voucherservice, hours, frequency (or job_type, unit_count), optional date + start_time
request_bookingThe one booking tool, for every service. The answer's outcome says what it did: a checkout link with the address and visit filled in (any other service, or checkout_link: true), a WhatsApp card to confirm (home cleaning), a call back in a 2-hour window the customer picks (free, except that a quoted job has a quotation fee), or on a cleared key the booking with its payment link. See "What request_booking answers with"service. The link: postal_code, job_type + unit_count (or hours + frequency with checkout_link: true). Cleaning: phone, postal_code, unit, date, weekday, start_time, hours, optional customer_name (recommended), frequency, agent_name. A call back: phone, customer_name, details, call_back_date, call_back_weekday, call_back_from, optional postal_code
cancel_bookingCancels a booking your key made, or one visit of it, under our usual rules. First call: the fee, the refund and why, with a confirm_code; nothing changes. Second call, with the code and the customer's yes: cancelledreference, optional visit_date, then confirm_code, customer_confirmed (phone optional)
move_bookingMoves one visit of a booking your key made to a time its own expert is free. Without a time: the open times. With one: the fee and why, with a confirm_code. With the code and the customer's yes: movedreference, visit_date (several visits), new_date, new_weekday, new_start_time, then confirm_code, customer_confirmed (phone optional)
booking_statusWhere a request_booking request stands: waiting for the customer, declined, expired, awaiting payment (with the payment link), finding a cleaning expert, booked (with the next visit), done or cancelled. For a direct booking (hmb_...): its status, whether it is paid, and its visitsreference

service is cleaning (by the hour: 2, 3 or 4 hours; one-time, weekly or bi-weekly) or the code of any other service under services (by job_type and unit_count from its card; a service with one job, such as a quotation visit, needs neither). Asked about a service under more_services, if any, these tools answer with how that service is booked.

A typical booking

  1. find_services {} first, whatever the job: it is the catalogue, and every tool below needs the service it gives you. A tool called without service is refused.
  2. check_area {"postal_code": "238823", "service": "cleaning"}
  3. find_times {"postal_code": "238823", "service": "cleaning", "hours": 3}, then with a date for that day's times. Every date comes with its weekday ({"date": "2026-10-03", "day": "Saturday", "label": "Sat 3 Oct"}); use it as given.
  4. get_quote {"service": "cleaning", "hours": 3, "date": "2026-10-03", "start_time": "09:00"}
  5. Book it for the customer: ask for their WhatsApp number and their OK, then request_booking (see "Booking for the customer" below). They confirm with one tap on WhatsApp.
  6. If they would rather book themselves: request_booking {"service": "cleaning", "checkout_link": true, "postal_code": "238823", "hours": 3, "frequency": "one-time"} and give them the link.

Example get_quote result (one-time, 3 hours, no time chosen):

{
  "service": "cleaning", "hours": 3, "frequency": "one-time", "currency": "SGD", "gst_included": true,
  "pay_now": 57, "list_price": 75, "saving": 18,
  "offer": "voucher \"You're invited to HomeMaster\" applied (saves S$18.00), for customers booking with us for the first time",
  "evening_rate": "One-time visits starting from 6:00 PM in the next 3 days cost S$18/hour (S$54.00 for 3 hours) when that is cheaper. ...",
  "caveat": "This is what the checkout shows a new customer before their phone is checked. ..."
}

Example request_booking result with checkout_link: true (trimmed):

{
  "outcome": "checkout_link",
  "bookable": true,
  "booking_url": "https://homemaster.co/book/clean?postal=238823&hours=3&frequency=one-time&utm_source=your-client&utm_medium=ai_agent&utm_campaign=mcp",
  "link_note": "Give the customer this link exactly as it is. ...",
  "customer_steps": ["Open the link. ...", "Pick a start time. It is held for 7 minutes ...", "..."],
  "why_we_need_your_phone": "HomeMaster needs the customer's number to confirm the booking ...",
  "terms": { "cancel_or_move": "To cancel or move a visit, the customer messages Helen on WhatsApp ... at least 48 hours before it starts.", "...": "..." }
}

Booking for the customer

With the customer's agreement, request_booking does the booking for them. Use a start time from find_times:

  1. request_booking {"service": "cleaning", "postal_code": "238823", "unit": "#05-12", "date": "2026-10-03", "weekday": "Saturday", "start_time": "09:00", "hours": 3, "phone": "9123 4567", "customer_name": "Alex Tan", "customer_confirmed": true, "agent_name": "Claude"} - weekday is the day the customer agreed to: a date that is not that weekday is refused. - unit is the floor AND unit: a placeholder or the floor alone is refused. - phone is the 8 digits as the customer gave them; we add +65. When you ask for it, say why (the reason is on the field's description). - customer_confirmed is true only when the customer gave you every detail and said yes. Ask them for what you do not have; never fill a field in yourself. A refusal sends nothing and ends "Nothing is booked: do not tell the customer it is". The answer's check reads back the number and unit the card went to: say both to the customer.
  2. We send the customer Helen's booking card on WhatsApp: "Claude asked us to book this for you", the date, their address, the clean and their price, with Yes, book it and No, cancel.
  3. Tell the customer to tap Yes, book it there within 60 minutes. Helen then sends them a payment link. The time is not held while they decide; if it has gone by then, Helen offers other times.
  4. booking_status {"reference": "hmr_..."} at any time.

Example request_booking result (trimmed):

{
  "reference": "hmr_2yq...",
  "status": "waiting_for_customer",
  "expires_at": "2026-10-01T07:05:00.000Z",
  "when": "Saturday, 3 October at 9:00 AM",
  "price": "$75 - $18 = $57",
  "total": 57
}

Figures above are illustrative; the tools always return today's prices.

What request_booking answers with

Every service ends in a booking. Every answer carries outcome: read it before you tell the customer anything.

outcomeWhenWhat was bookedWhat you do next
checkout_linkAny service but home cleaning (on a key not cleared to book directly), or any service with checkout_link: trueNothing: a link to our checkout with the address and the visit filled in. The customer picks the time, confirms their phone with a WhatsApp code and pays there; that is the bookingGive them booking_url exactly as it is
whatsapp_cardHome cleaningNothing yet: we sent the customer Helen's booking card on WhatsApp, and their tap on Yes, book it books itTell them to tap it within 60 minutes. Follow with booking_status and the reference
call_back_bookedAny service but home cleaning, with a window (call_back_date)A call back from our team, in the 2-hour window the customer picked. Free (price: 0) for a job on a price list; for a job our team quotes, price is the quotation fee, which our team collects, never through this booking. No message was sentSay tell_the_customer
pick_a_call_back_timeA service under more_services, called without a windowNothing. The answer holds the question to ask and the windows we takeAsk the customer when they are free, then call again with call_back_date and call_back_from
payment_linkOnly on a key cleared to book directly: any service booked onlineThe booking, waiting for payment. No message was sent and no code was asked forGive the customer payment_url; it is confirmed when they pay. Read back on_the_number, and tell them price_note if there is one. Follow with booking_status and the reference (hmb_...)

On a key cleared to book directly, request_booking takes the same fields as a home clean above, plus customer_name (required), and for any other service job_type and unit_count from its card in place of hours. The price comes from our checkout's own rules for this customer: if it differs from get_quote (an offer for new customers only does not reach someone who has booked with us before), the answer says so in price_note.

Why a call back: a service our team arranges is priced and scheduled by a person, and a job quoted on more information cannot be priced in a tool call. So the customer books the call, and the price and the time for the job are given on it.

The windows we take: any day from today to 14 days ahead, 2 hours long, starting on the hour or half hour from 9:00 AM to 7:00 PM, Singapore time. The service card and the pick_a_call_back_time answer both carry call_back_days (every bookable day with its weekday), earliest_window and singapore_time_now: take the date from the list, never work a weekday out.

There is no emergency or same-hour service. If the customer needs someone now (locked out, water flooding, no power, a person at risk), say plainly that HomeMaster cannot send anyone now and that they need an emergency service; offer the call back only as a follow-up.

The steps for an assistant:

  1. find_services {}, then find_services {"service": "pipe_leak_repair"} for the service card. In its price list, total_price_by_quantity holds whole prices for that many, never a rate.
  2. Tell the customer what the card says, and that HomeMaster needs to find out more and will call them. Pipe leak repair is quoted for each job, so tell them the quotation fee from its card (pricing.quotation_fee): our team collects it; it is taken off the job's total price if they go ahead, and it stays as the charge for the quotation if they do not. Ask when they are free for a call, and for anything the card's how_to_request.ask_the_customer_for lists that you do not have.
  3. request_booking {"service": "pipe_leak_repair", "customer_name": "Alex Tan", "phone": "9123 4567", "postal_code": "238823", "details": "Water dripping from the pipe under the kitchen sink, HDB flat", "preferred_job_time": "a Saturday morning, within two weeks", "call_back_date": "2026-10-10", "call_back_weekday": "Saturday", "call_back_from": "10:00", "customer_confirmed": true}
{
  "outcome": "call_back_booked",
  "reference": "hmc_3f2b8c1e-0a4d-4c7e-9b1a-5d6e7f8a9b0c",
  "status": "booked",
  "service": "Pipe Leak Repair",
  "call_back": { "date": "2026-10-10", "day": "Saturday", "from": "10:00", "to": "12:00", "when": "Saturday, 10 Oct, 10:00 AM to 12:00 PM", "on": "+6591234567", "number_as_said": "9123 4567" },
  "check": "The call is booked for a Saturday, on the number 9123 4567. Say both to the customer exactly as written here. If either is not what they told you, call request_booking again with the right one.",
  "to_change": "To move the call, call request_booking again for this service and phone with a new window: it replaces this one. There is no cancel: a customer who no longer wants it says so on the call. Helen on WhatsApp does not handle call backs.",
  "more_than_one_job": "If the customer has another job under more_services, book it too, in the same window: our team makes one call for all of them.",
  "price": 25,
  "currency": "SGD",
  "price_is_for": "The quotation. The quotation costs S$25.00, collected by our team, not through this booking. If the customer goes ahead with the job, the S$25.00 is taken off the job's total price. If not, it stays as the charge for the quotation on its own. The price of the job itself is confirmed on that call.",
  "tell_the_customer": "Your call back for Pipe Leak Repair is booked: HomeMaster will call you on 9123 4567 on Saturday, 10 Oct, 10:00 AM to 12:00 PM. The quotation costs S$25: our team collects it, it comes off the job's total if you go ahead, and it is the charge for the quotation if you do not. They will give you the price and a time for the job on that call."
}
  1. booking_status {"reference": "hmc_..."} at any time: call_back_booked with its window, called, job_booked or closed. It never returns the customer's details.

Rules worth knowing:

A request that cannot be acted on is a tool error that says what to ask the customer for: a phone that is not a Singapore number, no name, no details, a postal code that is not 6 digits, a window outside the ones we take, or a service code we do not list. A service other than home cleaning is booked online through request_booking's checkout link; it books a call back only for a window (call_back_date).

start_booking, the link tool before 26 Oct 2026, is no longer listed. An assistant that still calls it gets the same link as request_booking's checkout_link.

Cancelling and moving a booking

Since 26 Oct 2026 an assistant can cancel or move a booking its own access key made, under the same rules a customer has: 48 hours' notice, one free change a month for home cleaning, 50% for a late or extra change, free within 4 hours of booking, and an unpaid visit always free. No phone number is needed (since 28 Oct 2026); if you send phone, it must be the customer's number on the booking.

  1. booking_status {"reference": "hmb_..."}: the visits, to name the one to change.
  2. move_booking {"reference": "hmb_..."}: the times that visit's own expert is free.
  3. move_booking {..., "new_date": "2026-10-30", "new_weekday": "Friday", "new_start_time": "14:00"}: outcome: confirm_needed, with the fee and why and a confirm_code good for 15 minutes. Nothing has changed. Tell the customer and get their yes.
  4. The same call with "confirm_code": "...", "customer_confirmed": true: outcome: moved.

A cancel is the same without step 2. A used code returns its first answer, never a second change; a code whose booking changed since the quote returns a new quote. Anything that cannot be changed here answers outcome: refused with what to do instead: a booking made without your key (the customer's My Bookings page, https://homemaster.co/my-booking), a paid booking still waiting for an expert, or a visit under 2 hours away (HomeMaster on WhatsApp). A booking not paid yet with no visit arranged is moved by booking the new time and cancelling it (outcome: rebook_needed). The open API has the same two calls: POST /v1/booking-requests/{reference}/cancel and /move.

What it will and will not do

Without MCP

Limits

Per caller (by IP address): 60 tool calls a minute and 300 an hour; 3,000 an hour across all callers; request_booking 30 an hour per caller for a card, a call back or a direct booking (a checkout link counts only toward the general limits). One phone number can have one request waiting at a time, and at most 3 a day. A limited call returns a tool error saying so. initialize, tools/list and ping are not counted.

Privacy

The tools ask for a postal code and booking choices, never a payment detail. request_booking also asks for the customer's WhatsApp number and unit number, and optionally their name: we message that number once, and keep the request so the customer's tap can book it. The name is saved to their profile only once they tap "Yes, book it", and never over a name we already have. booking_status never returns the customer's name, phone, address or cleaning expert. No other tool reads customer records. For rate limiting we keep a salted hash of the caller's IP address (never the address itself), deleted within 7 days. The name your client sends at initialize is used only to credit bookings made through the link (utm_source) and to count calls. We keep a daily count of tool calls: the tool, the name your client gave, and, if you sent an access key, whose key it was. The count holds no customer details and nothing a tool was asked. A booking request or a call back made with an access key also records which key made it.

Errors

Invalid input and limits come back as tool results with isError: true and a plain message the model can act on. Unknown tools and malformed requests get standard JSON-RPC errors.

Contact

To discuss a partnership: admin@homemaster.co. Customers: Helen on WhatsApp, https://wa.me/6589154889.