MCP server for a plumbing business that exposes booking, availability, and service information tools to AI assistants. Handles appointment scheduling, service area validation, pricing estimates, and booking management.
Strong naming conventions (verb_noun pattern: check_*, book_*, cancel_*, describe_*, estimate_*). All 7 tools have clear, substantive descriptions (100-200 chars, well above 20-char minimum). Input schemas present with typed parameters and descriptions. Risk annotations (READ_ONLY, WRITE, REVERSIBLE) demonstrate security awareness. However, output schemas are not documented in the provided source, critical for LLM planning. Parameter descriptions are adequate but lack format constraints (e.g., phone format '10-15 digits' stated in description but no regex/pattern in schema). Error handling guidance is absent from tool definitions.
Backend for the book_appointment WebMCP tool. Validates booking details, takes an atomic hold on the slot, texts the owner, and reports the appointment as held rather than booked. A person still owns confirmation: owner replies YES and /api/confirm turns the hold into an appointment.
Backend for cancel_hold, a conversation-scoped tool. Registered only while a hold is pending. Cancels a pending booking hold by reference. The only scoped tool that changes state.
Backend for the check_availability WebMCP tool. Returns available appointment slots for a given date and optional service. Never returns a slot that cannot actually be booked. Reads live holds from Blobs with strong consistency.
Backend for check_hold_status, one of the two conversation-scoped tools. Registered only once a booking has produced a hold, and unregistered the moment that hold stops being pending. Returns the current status of a booking hold.
Backend for the check_service_area WebMCP tool. Pure math over a lookup table: validates whether a given location is in the service area. No state, no database, no third-party API.
Backend for the describe_services WebMCP tool. Returns comprehensive information about all services, pricing, hours, service area, and business details. Everything here is derived from src/lib/business.ts rather than restated.
Output schemas not documented. LLMs cannot plan downstream tool calls or extract required fields (e.g., what fields does check_availability return? Is there a booking_reference for chaining to check_hold_status?). This forces agents to guess structure.
Parameter format constraints stated in descriptions but not formalized in schema. E.g., phone '10-15 digits' and date 'ISO YYYY-MM-DD' are prose, not regex patterns or format declarations. LLMs cannot validate before calling.
No error handling guidance in tool definitions. If book_appointment fails (e.g., slot already taken, invalid phone), the tool description does not tell the LLM what to do next (retry? ask user? call check_availability again?).
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | D | 59 | 2026-07-28+ | v2 |
Backend for the estimate_job WebMCP tool. Returns a price range for a job and says plainly that the real number comes after someone looks at it. Fake precision from a tool an agent will quote verbatim is the fastest way to make a business look dishonest.
Conversation-scoped tools (cancel_hold, check_hold_status) are registered/unregistered dynamically based on state. This is not documented in tool descriptions, forcing LLMs to infer availability. Agents may attempt to call tools that are no longer registered.
estimate_job has three overlapping parameters (job, service, details) with unclear mutual exclusivity. Description does not state which are required, which are optional, or how they interact.