Fresha MCP Server
The server exposes 17 Fresha business tools for reading and managing appointments, clients, sales, reviews, services, team members, and blocked time.
Appointments: list/get appointments by date, status, client, or team member; find free slots for up to 5 services over 14 days; create bookings, reschedule, and cancel appointments or whole series.
Blocked time: add a one-day blocked slot for a team member with title/note and optional online booking.
Clients: list/search clients; get profiles, stats, upcoming appointments, and notes; add clients; add internal team-only notes.
Catalogue and team: list services with category, duration, price, and online booking status; list team members with roles, contact, services, and booking status.
Sales: get sales summaries by period and list sales/invoices over date ranges.
Reviews: list and filter reviews; reply publicly as the business or team member.
Interfaces: use through MCP over stdio or HTTP, a local REST API with OpenAPI docs, or Python calls/scripts; read-only mode limits access to read tools.
Fresha API and MCP server, self-hosted
A Fresha API and a Fresha MCP server you run on your own computer, in Python, on the session of your own Google Chrome. All 17 Fresha tools we run in production at Cotlet: appointments, free slots, booking, rescheduling and cancelling, blocked time, clients and client notes, services and team, the Sales summary and Sales list reports, and reviews with replies.
Fresha has no public API and no official MCP server. Its only data connection is the paid Data Connector add-on, which copies your data out to a Snowflake database and cannot book anything. This repo gives you:
MCP server for Claude Code, Claude Desktop, Cursor, Codex and any MCP client, over stdio or HTTP. Setup
REST API on
http://127.0.0.1:8787/api/v1/fresha, with OpenAPI docs at/docs. SetupPython:
call("list_appointments", {"from": "2026-10-27", "to": "2026-10-27"}), plus 9 ready scripts inexamples/.
It is for technical people who want to run it themselves, for noncommercial use only (license). If you want it running 24/7 on a server, kept working when Fresha changes its panel, for a team or a business, use the hosted version at cotlet.dev.
git clone https://github.com/cotletdev/fresha-api-mcp-server.git && cd fresha-api-mcp-server
python3 -m venv .venv && . .venv/bin/activate && pip install -e .
fresha-mcp connect # reads your Fresha session from Chrome
fresha-mcp call list_services
claude mcp add fresha -- "$PWD/.venv/bin/fresha-mcp" mcpContents
Related MCP server: salonrunner-mcp
How it works
AI agent (MCP) / script (REST, Python) ──▶ fresha-mcp on your computer ──your cookies──▶ *.fresha.com
▲
fresha-mcp connect ──────┘ reads them once from your Chrome (DevTools Protocol)You sign in to Fresha for business in your own Chrome at partners.fresha.com, the way you always do: an email code, then a text message code. Tick Trust this device.
fresha-mcp connectattaches to that Chrome through the Chrome DevTools Protocol, reads the fresha.com cookies and saves them to~/.config/fresha-api-mcp/session.json, readable by your user only. It never sees or stores your password or the codes.Every tool call is a request to the APIs the Fresha partner panel itself calls (
partners-api, thepartners-api-gatewayGraphQL,customers-api,reports.fresha.comand a few more*.fresha.comhosts) with those cookies, sent with curl_cffi, which copies Chrome's TLS fingerprint. No headless browser runs.
The session lives in one cookie, fresha_partners_session. Fresha keeps it for 25 days and renews it while you use the panel or the tools. When Fresha signs you out, the server reads the cookies from Chrome again, if Chrome is open with remote debugging on.
Requirements
Python 3.11 or newer.
Google Chrome 144 or newer, signed in to partners.fresha.com. Any Chrome or Chromium works with a debugging port.
macOS, Linux or Windows.
A Fresha for business account. We build and test with the workspace owner. A team member login sees what Fresha shows that team member, and the two sales tools need its reporting permissions.
Install
git clone https://github.com/cotletdev/fresha-api-mcp-server.git
cd fresha-api-mcp-server
python3 -m venv .venv
. .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e .
fresha-mcp --versionWith uv: uv venv && uv pip install -e ., then uv run fresha-mcp ....
Setting it up with an AI agent (Claude Code, Codex, Cursor)? Open it in this folder and ask it to set up the repo: AGENTS.md tells it every step, and it asks you only for the steps in Chrome.
To update: git pull && pip install -e .. We copy every change of the hosted Fresha tools into this repo.
Connect your Chrome
Option A: your everyday Chrome (recommended)
Open
chrome://inspect/#remote-debuggingin Chrome and switch remote debugging on. Chrome then accepts DevTools connections from programs on this computer, and asks you before each one.Make sure you are signed in at partners.fresha.com.
Run:
fresha-mcp connectChrome asks whether to allow the connection. Click Allow.
Saved 24 Fresha cookies to /Users/you/.config/fresha-api-mcp/session.json
Signed in to Fresha as Daniel Hughes (daniel@example.com)You can switch remote debugging off again afterwards. Leave it on if you want the server to pick up a new session by itself after Fresha signs you out.
connect looks for Chrome's DevToolsActivePort file in the default profile folder. For Chrome Beta, Canary or a custom profile folder, pass --profile-dir "<folder>".
Option B: a Chrome with a debugging port
Chrome does not open a debugging port on your default profile, so start a separate one, sign in to Fresha there once, and connect to it:
# macOS
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" --remote-debugging-port=9222 --user-data-dir="$HOME/.chrome-fresha"
# Linux
google-chrome --remote-debugging-port=9222 --user-data-dir="$HOME/.chrome-fresha"
# Windows
"C:\Program Files\Google\Chrome\Application\chrome.exe" --remote-debugging-port=9222 --user-data-dir="%USERPROFILE%\.chrome-fresha"
fresha-mcp connect --cdp http://127.0.0.1:9222--cdp also takes a ws:// DevTools URL, for example of a Chrome on another machine reached over an SSH tunnel.
Option C: paste the cookie by hand
For a server with no Chrome: in Chrome open DevTools on partners.fresha.com, Application > Cookies > https://partners.fresha.com, copy the value of fresha_partners_session, then:
fresha-mcp connect --token # paste it, input is hidden
# or, without a session file:
export FRESHA_AUTH_TOKEN=...A pasted cookie is not read again from Chrome. Paste a new one when Fresha signs you out.
Check the session at any time:
fresha-mcp statusMCP server
The MCP server lists every tool with its input schema, a description written for agents, and hints. Read tools are marked read-only, and the tools that can reach clients (booking, rescheduling, cancelling, review replies) are marked so your client asks before running them.
Claude Code
claude mcp add fresha -- /path/to/fresha-api-mcp-server/.venv/bin/fresha-mcp mcp
# read only:
claude mcp add fresha -- /path/to/fresha-api-mcp-server/.venv/bin/fresha-mcp mcp --read-onlyClaude Desktop
~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"fresha": {
"command": "/path/to/fresha-api-mcp-server/.venv/bin/fresha-mcp",
"args": ["mcp"]
}
}
}Cursor
~/.cursor/mcp.json, the same block as Claude Desktop. A ready copy is in mcp.json.
Codex
~/.codex/config.toml:
[mcp_servers.fresha]
command = "/path/to/fresha-api-mcp-server/.venv/bin/fresha-mcp"
args = ["mcp"]Over HTTP
fresha-mcp serve also serves MCP over streamable HTTP at http://127.0.0.1:8787/mcp, for clients that take a URL:
fresha-mcp serve
claude mcp add --transport http fresha http://127.0.0.1:8787/mcpThen ask your agent things like:
Anna Smith emailed asking for a cut and colour with Sam on Friday afternoon. Find her, check the free times for both services and book the first one after my OK.
Sam is sick tomorrow. Block his day and show me which of his clients a colleague can take at the same time.
Yesterday's sales by sale type against the same day last week. Flag refunds over £50.
List the reviews without a reply and draft a short answer to each.
REST API
fresha-mcp serve # http://127.0.0.1:8787, MCP at /mcp, docs at /docsEach tool has its own route under /api/v1/fresha, the same paths as the hosted API at cotlet.dev, so code written against one works with the other. Path parts and the query string are arguments, and a JSON body adds the rest.
curl 'http://127.0.0.1:8787/api/v1/fresha/services'
curl 'http://127.0.0.1:8787/api/v1/fresha/appointments?from=2026-10-27&to=2026-10-31'
curl 'http://127.0.0.1:8787/api/v1/fresha/sales-summary?from=2026-10-01&to=2026-10-31'
curl -X POST http://127.0.0.1:8787/api/v1/fresha/blocked-times \
-H 'Content-Type: application/json' \
-d '{"team_member_id": "5652945", "start_at": "2026-11-03T13:00", "end_at": "2026-11-03T14:00", "title": "Lunch"}'Any tool by name, body as its arguments:
curl -X POST http://127.0.0.1:8787/api/v1/fresha/tools/list_clients -d '{"search": "smith"}'GET /api/v1/fresha/tools: every tool with its route, input schema and an example answer.GET /docs: interactive OpenAPI docs,GET /openapi.jsonthe schema, for n8n, Postman or a generated client.Answers are JSON. List tools return
next_cursor; pass it back ascursorfor the next page.
The server listens on 127.0.0.1 only. To reach it from another device of yours, set a token, and every request then needs Authorization: Bearer <token>:
FRESHA_MCP_TOKEN=$(openssl rand -hex 24) fresha-mcp serve --host 0.0.0.0Python and the example scripts
Straight from Python, no server needed:
from fresha_api_mcp import call
from fresha_api_mcp.scripting import paginate, write_rows
services = call("list_services")["services"]
day = call("list_appointments", {"from": "2026-10-27", "to": "2026-10-27"})["appointments"]
clients = list(paginate("list_clients", {"sort": "name"}, "clients"))
write_rows(clients, "clients.csv")
call("add_blocked_time", team_member_id="5652945", start_at="2026-11-03T13:00", end_at="2026-11-03T14:00")Or one tool from the shell: fresha-mcp call find_available_slots service_ids='["25979401"]' date=2026-10-27 days=3, or with a JSON object. An ID stays text where the tool takes text.
Times are wall clock in the business's time zone. 2026-11-03T14:30 means 14:30 at the salon. A time with an offset is converted to it, and answers carry time_zone.
Ready scripts in examples/. Each prints JSON or a short list, takes --out file.csv or --out file.json where it makes sense, and --help:
Script | What it does |
Tomorrow's appointments grouped by team member. | |
Matches a client by name, email or phone, finds free starts for several services in a time window, and with | |
A team member is out: blocks their hours and moves each client to a colleague free at the same time. Prints the plan; | |
Cancels a team member's appointments on a day with your cancellation reason. Lists them; | |
One day's Sales summary by sale type against the same weekday a week earlier, refunds over a limit flagged. | |
Clients with no appointment for N days and nothing booked, with their marketing consent. | |
Every client to CSV or JSON. | |
The Sales list for a date range to CSV or JSON. | |
Reviews without a reply, lowest rating first; |
python examples/tomorrows_appointments.py
python examples/team_member_out.py --team-member-id 5652945 --date 2026-10-30 --block 09:00-18:00
python examples/lapsed_clients.py --days 90 --out lapsed.csvOn a schedule, cron on your own computer works while it is on: 0 8 * * * cd ~/fresha-api-mcp-server && .venv/bin/python examples/daily_sales.py.
All 17 tools
IDs come from the list tools: service_id from list_services, team_member_id from list_team_members, client_id from list_clients, appointment_id from list_appointments, review_id from list_reviews. fresha-mcp tools prints this list, fresha-mcp tools --json adds the input schemas.
Appointments
Tool | Access | REST route | What it does |
| read |
| Appointments in a date range, with client, service, team member, price and status. |
| read |
| One appointment: client, services, team member, times, status, price and notes. |
| read |
| Free start times for one or more services with a team member, day by day. |
| write |
| Book an appointment for a client: services, team member and start time. |
| write |
| Move an appointment to a new start time, optionally to another team member. |
| write |
| Cancel an appointment without a charge, optionally telling the client. |
| write |
| Block a time slot in a team member's calendar so nobody books it. |
Clients
Tool | Access | REST route | What it does |
| read |
| Clients of the business, searchable by name, email or phone. |
| read |
| One client: contact, notification settings, booking stats, upcoming appointments, notes. |
| write |
| Add a client to the business's client list. |
| write |
| Add a note to a client's profile, seen by the team only. |
Catalogue and team
Tool | Access | REST route | What it does |
| read |
| Services of the business with category, duration and price. |
| read |
| Team members with role, contact, services they do and whether they take bookings. |
Sales
Tool | Access | REST route | What it does |
| read |
| Sales summary for a period: sales, refunds, discounts, taxes and payments by type. |
| read |
| Sales (invoices) in a date range: number, date, client, status and totals. |
Reviews
Tool | Access | REST route | What it does |
| read |
| Client reviews of the business with rating, text and replies. |
| write |
| Post a public reply to a client's review. |
create_appointment lets Fresha send its booking confirmation by the client's notification settings; the tool has no switch for it. reschedule_appointment and cancel_appointment tell the client unless you pass notify_client: false. A review reply is public and the reviewer is told. Adding a client, a note or blocked time sends nothing. In list_appointments the status, team member and client filters apply to the first 100 rows of the date range, so narrow the dates for a busy salon.
Not in this version: payouts and the wallet (Fresha Payments), checkout and refunds, products, gift cards, memberships, the waitlist, marketing campaigns and shifts. create_appointment takes no appointment note, add_blocked_time blocks one day with no repeats, and cancel_appointment refuses an appointment with a cancellation fee or a paid deposit.
Never built, on purpose: your Fresha sign-in, email, password and user account, and anything that moves money (subscription, billing, payouts, payment methods). Every request passes a guard that refuses any change to those Fresha paths.
Configuration
Variable | Default | What it does |
|
| Folder of |
| The | |
| Proxy for every request to Fresha, e.g. | |
|
| |
| Bearer token required by | |
|
| Address of |
Security and your Fresha account
Your password never reaches this code. Only the cookies Chrome already holds are read, and only those of fresha.com.
The session file is yours.
session.jsonis created with mode 0600. Anyone who has it can act as you in Fresha for business, so keep it out of git and shared folders.Local by default.
servebinds to127.0.0.1and refuses another address withoutFRESHA_MCP_TOKEN.fresha.com only. Requests and redirects outside fresha.com are refused.
Read-only mode.
--read-onlyorFRESHA_MCP_READ_ONLY=1exposes the 10 read tools only.Actions reach real people. A booking, a move or a cancellation can message your client, and a review reply is public. MCP clients ask before running those tools; a script does not, so try it on a test client of your own first.
Errors
The REST API answers {"error": "<code>", "message": "..."}. The MCP server returns the same code and message as a tool error.
Status | Code | What to do |
401 |
| No session, or Fresha signed you out. Sign in at partners.fresha.com in Chrome and run |
403 |
| Your Fresha role cannot do this, e.g. reports without the reporting permissions. |
403 |
| The server runs read-only, or the request would change your Fresha account itself. |
403 |
| Fresha refused the request or showed a bot check. Open the panel in Chrome once, then retry. |
409 |
| Fresha asks for a CAPTCHA. Solve it in Chrome. |
422 |
| An argument is missing or wrong; the message names it. |
429 |
| Fresha is limiting the account. Wait a few minutes. |
502 |
| Fresha answered with an error (its own message is in |
Self-hosted or hosted
This repo | ||
Tools | the same 17 | the same 17 |
Runs | on your computer, while it is on | in the cloud, 24/7 |
Fresha session | your Chrome, saved in a local file | your own Cloud Browser in the EU, profile encrypted with AES-256-GCM |
When Fresha changes its panel |
| fixed for you, every tool tested daily |
Approvals | your MCP client asks per call | bookings, moves, cancellations and review replies wait for your OK, switchable per tool |
Works with | MCP clients on your machine, local scripts | Claude.ai and ChatGPT connectors, n8n, Make, Zapier, any HTTP client |
Who may use it | you, for noncommercial use | teams, businesses, agencies |
Price | free | free 7-day trial with 2,000 credits, no card; then one-time packs from $29 for 5,000 credits |
On the hosted version a successful call uses 10 credits, failed calls are free, and the Cloud Browser uses 50 credits a day while it has an app enabled for agents. Full pricing.
FAQ
Does Fresha have an official API? No public one. Fresha publishes no API and no developer docs (checked 11 Oct 2026). Its Data Connector add-on, paid per location, syncs your data out to reporting tools through a Snowflake connection, and it cannot book, move or cancel appointments. Details: cotlet.dev/integrations/fresha-api-mcp-server.
Is there a Fresha MCP server for Claude, ChatGPT or Cursor?
Yes, this one runs on your machine: fresha-mcp mcp for Claude Code, Claude Desktop, Cursor and Codex. ChatGPT and Claude.ai connectors need a public HTTPS URL, which the hosted Fresha MCP server gives you. A community server on GitHub reads Data Connector tables from Snowflake; it needs the add-on and does not write anything.
Do I need the Data Connector add-on? No. The tools work on your own partners.fresha.com session, the same requests the panel sends.
Can it find free slots and book for me?
find_available_slots returns the free start times for one or more services done back to back, with one team member, for up to 14 days. create_appointment books one of them. Fresha books over other appointments if you ask it to, so check the slot first.
Can I export my Fresha clients or sales to CSV?
Yes: python examples/export_clients.py --out clients.csv and python examples/export_sales.py --from 2026-10-01 --to 2026-10-31 --out sales.csv.
Does it work outside the UK? Yes. The tools read the time zone and currency of your workspace from Fresha. With several locations, the calendar tools use the first one.
Why does it need my Chrome? To read your Fresha session without your password and the two sign-in codes. Chrome is used once per sign-in, not for each call.
What happens when Fresha signs me out?
Calls fail with auth_expired. With Chrome remote debugging on, the server reads the new session on the next call (click Allow when Chrome asks). Otherwise run fresha-mcp connect again.
Can it run on a VPS or in Docker?
Technically yes, with a pasted cookie or a session.json copied from your computer. Fresha then sees your session from a datacenter IP, which looks less like you. For an always-on setup the hosted version keeps your session in your own Cloud Browser on a fixed IP.
Will my Fresha account get banned? Requests carry Chrome's fingerprint and go out one at a time, as you would click. Nobody can promise a platform never acts. Keep to your normal use.
Can I use it for my salon or for my clients? No. The license allows noncommercial use only, and running your own business counts as business use. For a salon, a team or client work, use Cotlet or write to contact@cotlet.dev.
Can I use the code for anything I like? No. The source is public, but the license allows noncommercial use only: no business use.
A tool broke or I need another one. Open an issue with the tool name and the error message.
License
PolyForm Noncommercial License 1.0.0. In short:
Allowed: noncommercial use: you, for your own personal purposes with your own Fresha account, or a charity, school, public research or government body. Changing the code, and sharing it under the same license.
Not allowed: any commercial use, your own business included; client work; selling or offering it, or a service built on it.
Maintained by Cotlet (@cotletdev). Fresha is a trademark of its owner. This project is unofficial and not affiliated with, endorsed or sponsored by Fresha.
Available Tools
17 toolsadd_blocked_timeAdd blocked timeAInspect
Block a time slot in a team member's calendar so nobody books it.
Adds blocked time as Add > Blocked time in the calendar does: one block on one day for one team member, with a title and a note for the team. It does not repeat. Clients cannot book it online unless allow_online_booking is true. Nobody is told.
| Name | Required | Description | Default |
|---|---|---|---|
| title | No | Reason shown on the calendar, e.g. Lunch. | |
| end_at | Yes | End on the same day. ISO 8601 like start_at. | |
| start_at | Yes | Start, ISO 8601: 2026-11-03T14:30 is wall clock in the business's time zone; with an offset (2026-11-03T14:30:00+01:00) it is converted. | |
| description | No | Note your team sees on the block. | |
| team_member_id | Yes | Team member id from list_team_members. | |
| allow_online_booking | No | Clients may still book this time online. Default: false. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations (readOnly=false, idempotent=false, destructive=false), the description discloses concrete side effects: single non-recurring block, one day only, no notification sent to anyone, and client bookability gated by allow_online_booking. It does not cover edge behavior such as overlapping an existing appointment or validation failures, which keeps it off a 5.
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 short sentences in the first paragraph carry the core purpose and scope, followed by a compact paragraph of behavioral caveats. No filler, no restatement of the title, and the most important constraint (blocks booking) 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?
For a six-parameter mutation with no output schema, the description covers purpose, scope limits, notification behavior, and the booking flag — enough to invoke it correctly. The remaining gap is what a caller gets back and how conflicts with existing appointments are handled.
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 100%, so baseline is 3; the description still adds intent-level meaning by framing the record as 'one block on one day for one team member' (constraining end_at to the same day) and explaining the practical consequence of allow_online_booking rather than just its default. The 'title and a note for the team' phrasing also clarifies the split between title and description.
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?
Opens with a specific verb and resource plus the user-visible effect: 'Block a time slot in a team member's calendar so nobody books it.' This is clearly distinguishable from booking siblings like create_appointment and from find_available_slots, which read availability rather than create blocks.
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?
Anchors the tool to the product's 'Add > Blocked time' action and states key exclusions ('It does not repeat', 'Nobody is told'), which tell an agent this is not a recurring or notifying operation. It stops short of naming sibling tools or an explicit when-not-to-use condition (e.g. vs. reschedule_appointment).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
add_clientAdd clientAInspect
Add a client to the business's client list.
Adds a client as Add client in the Client list does: name, email, mobile and gender. With appointment_messages (default true) Fresha sends the client booking confirmations and reminders to the email and mobile given; marketing (default false) records consent to marketing messages. Nothing is sent when the client is added.
| Name | Required | Description | Default |
|---|---|---|---|
| No | Email. | ||
| phone | No | Mobile number with country code, e.g. +44 7700 900123. | |
| gender | No | Gender. | |
| last_name | No | Last name. | |
| marketing | No | The client agreed to marketing messages. Default: false. | |
| first_name | Yes | First name. | |
| appointment_messages | No | Let Fresha send this client booking confirmations and reminders by email, SMS and WhatsApp. Default: true. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare non-readOnly, non-destructive, non-idempotent and open-world. The description adds genuinely useful behavior beyond that: nothing is sent at creation time, while appointment_messages=true triggers booking confirmations and reminders across email/SMS/WhatsApp, and marketing records consent. It does not cover duplicate-client handling or failure modes.
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 second sentence opens with a circular restatement ('Adds a client as Add client in the Client list does'), which wastes space before the genuinely useful message-consent explanation. The final sentence is well placed and front-loads the important 'nothing is sent' fact late.
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 7-parameter mutation with no output schema, the description covers the essential creation semantics and the non-obvious messaging side effects. Gaps are modest: no mention of duplicate-email behavior or what identifying data comes back.
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 100%, so the schema already documents all seven parameters including defaults and the phone pattern. The description adds only light reinforcement of the appointment_messages/marketing effect, and even refers to 'mobile' where the schema calls the field 'phone'.
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?
States a specific verb and resource: 'Add a client to the business's client list.' An agent can distinguish it from list_clients/get_client/add_client_note by the verb, though no sibling is named explicitly.
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?
Usage is implied by the create verb, but there is no explicit guidance on when to use this versus alternatives (e.g., checking for an existing client first, or using add_client_note for notes). No exclusions or prerequisites are stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
add_client_noteAdd client noteAInspect
Add a note to a client's profile, seen by the team only.
Adds a note to the client's Notes tab for all locations, as the profile's Add note does. Only the team sees it; the client is not told. Line breaks become paragraphs.
| Name | Required | Description | Default |
|---|---|---|---|
| text | Yes | Note text. | |
| client_id | Yes | Client id from list_clients. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already establish that this is a non-read-only, non-idempotent, non-destructive mutation with open-world behavior. The description adds meaningful behavioral context beyond annotations: the note is visible to the team only, the client is not notified, it applies across all locations, and line breaks become paragraphs. It does not cover permissions or response behavior, but the added context is substantial.
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 three short sentences, front-loads the core action, and each sentence contributes useful information: purpose, destination/scope, and visibility/formatting behavior. There is no redundant restatement of the name or schema.
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 simple two-parameter mutation with full schema coverage and annotations covering safety hints, the description is complete enough. It explains visibility, notification behavior, location scope, and text formatting, leaving little an agent needs to infer before calling it.
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 100%, so the baseline is 3. The description adds semantics beyond the schema by explaining that line breaks in the text become paragraphs and that the note applies to all locations, which clarifies formatting behavior and implies there is no location-scoping parameter.
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 states a specific verb and resource: adding a note to a client's profile. It also specifies the destination (Notes tab), scope (all locations), and visibility (team only), so an agent can understand exactly what the tool does without opening the schema.
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 implies this tool is for internal team notes by saying the client is not told and only the team sees it. However, it does not give explicit when-to-use guidance, exclusions, or alternatives for communicating with clients, so usage is only implied.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
cancel_appointmentCancel appointmentADestructiveInspect
Cancel an appointment without a charge, optionally telling the client.
Cancels the appointment as Cancel in its drawer does, with one of the business's cancellation reasons. With notify_client (default true) Fresha sends the client a cancellation message, so this can reach the client; false cancels quietly. whole_series also cancels the later appointments of a repeating series. An appointment with a cancellation fee or a paid deposit is refused: cancel it in Fresha. It cannot be undone here.
| Name | Required | Description | Default |
|---|---|---|---|
| scope | No | this_appointment (default) or whole_series, for repeating appointments: this and every later one. | |
| reason | No | Name of one of the business's cancellation reasons, e.g. Client not available. Default: none. | |
| notify_client | No | Send the client a cancellation message. Default: true. | |
| appointment_id | Yes | Appointment id from list_appointments. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description goes well beyond the annotations: it discloses irreversibility ("cannot be undone here"), side effects (the client may be messaged), refusal conditions, and series-wide blast radius. However, some of this overlaps the destructiveHint=true / openWorldHint=true annotations, and it never describes what the call returns, which matters given there is no output schema.
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?
Front-loaded with the action and its key caveats, and most sentences carry distinct information. There is mild redundancy between the paragraph text and the schema on whole_series/notify_client defaults.
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 destructive mutation with no output schema, it covers safety, side effects, refusal conditions, and scope. The remaining gap is the return value (e.g., which appointments were cancelled) and how to recover after an unexpected series cancellation.
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 100%, so all four parameters are already documented in the input schema. The description restates the notify_client default and the whole_series meaning without adding format, syntax, or edge-case detail beyond the schema, so the baseline applies.
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?
Specific verb + resource ("Cancel an appointment") plus scope qualifiers (single vs whole series) and an explicit analogy to the in-product Cancel action. It is clearly distinguishable from siblings like reschedule_appointment or get_appointment.
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 the precondition that selects the alternative: an appointment with a cancellation fee or paid deposit is refused and must be cancelled in Fresha instead. It also names the quiet-vs-notify choice so the agent knows which mode to pick per situation.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_appointmentCreate appointmentADestructiveInspect
Book an appointment for a client: services, team member and start time.
Books the services one after another from start_at with one team member, as New appointment in the calendar does, at each service's catalogue price. Fresha may send the client a booking confirmation by their notification settings (get_client), so this can reach the client. Find free times with find_available_slots first: Fresha books over other appointments and returns its warnings.
| Name | Required | Description | Default |
|---|---|---|---|
| start_at | Yes | Start, ISO 8601: 2026-11-03T14:30 is wall clock in the business's time zone; with an offset (2026-11-03T14:30:00+01:00) it is converted. | |
| client_id | No | Client from list_clients. Without it: a walk-in. | |
| service_ids | Yes | Service ids from list_services, done one after another in this order. | |
| team_member_id | No | Team member. Default: the first who takes bookings. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructive=true, openWorld=true and idempotent=false, so the safety profile is covered; the description adds real context beyond that: services are booked sequentially from start_at at catalogue price, and the booking can send a real confirmation to the client. The only thing left vague is what the call returns on success or how the over-booking warning surfaces in this tool's own response.
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 compact sentences, front-loaded with what the tool does before the prerequisites and side effects. Every clause earns its place – the calendar analogue, the sequential booking, the client notification and the double-booking warning all change agent behavior.
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 4-parameter destructive mutation with no output schema, the description covers ordering, pricing, external side effects and the slot-checking prerequisite. The remaining gap is the response shape, which is neither documented here nor in an output schema.
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 100%, so every parameter is already documented, including the walk-in default and the team-member default. The description reinforces semantics (services run one after another from start_at with a single team member) but adds no format, ordering or constraint detail the schema does not already carry. Baseline 3 is appropriate.
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?
States a specific verb and resource ('Book an appointment') plus the key inputs (services, team member, start time), and anchors it to a concrete analogue: 'as New appointment in the calendar does'. An agent can distinguish it from list_appointments, reschedule_appointment and find_available_slots without opening any schema.
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?
Gives an explicit prerequisite ('Find free times with find_available_slots first') and a concrete consequence of skipping it ('Fresha books over other appointments and returns its warnings'). It also routes to get_client for the notification-settings question, so both when-to-use and which sibling to consult are spelled out.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
find_available_slotsFind available slotsARead-onlyIdempotentInspect
Free start times for one or more services with a team member, day by day.
Asks the calendar for free start times as New appointment does, for the services done one after another, with one team member, over up to 14 days. Times are wall clock in the business's time zone; closed days have no times. Book a time with create_appointment.
| Name | Required | Description | Default |
|---|---|---|---|
| date | Yes | First day to search. | |
| days | No | Days to search. Default: 1. | |
| service_ids | Yes | Service ids from list_services, done one after another in this order. | |
| team_member_id | No | Team member to book. Default: the first who takes bookings. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, openWorld, and non-destructive behavior. The description adds useful behavioral context: 'Times are wall clock in the business's time zone; closed days have no times' and sequential service scheduling. No contradictions with annotations.
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, front-loaded with purpose. The second sentence is slightly redundant with 'Asks the calendar for free start times as New appointment does,' but overall the description is efficient and every sentence contributes.
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?
No output schema exists, but the description conveys return structure ('day by day') and key behavioral traits (timezone, closed days). For a read-only tool with full annotations and complete parameter schema, this 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?
Schema coverage is 100%, so the baseline is 3. The description reiterates 'one or more services' and 'up to 14 days' but adds little meaning beyond what the schema already provides (e.g., service_ids order, days min/max/default are all documented in the schema).
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?
States a specific verb and resource: 'Free start times for one or more services with a team member, day by day.' It also distinguishes from the booking tool by directing the agent to create_appointment. An agent can readily tell what this tool does versus siblings.
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 a clear next step: 'Book a time with create_appointment.' However, it does not explicitly state when NOT to use this tool or contrast it with other listing tools like list_appointments. Implied usage is clear enough for an agent to select it before booking.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_appointmentGet appointmentARead-onlyIdempotentInspect
One appointment: client, services, team member, times, status, price and notes.
Reads the appointment as its drawer in the calendar shows it: status, start and end (wall clock in the business's time zone), the client, each booked service with its team member and length, the total and what is left to pay, and the appointment notes.
| Name | Required | Description | Default |
|---|---|---|---|
| appointment_id | Yes | Appointment id from list_appointments. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, non-destructive and open-world, so the safety profile is covered. The description adds genuinely useful context beyond that: timestamps are wall clock in the business's time zone, the payload includes balance remaining after the total, and appointment notes are returned.
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 summary sentence is front-loaded and the second sentence elaborates on the returned fields. There is mild repetition between the two ('client, services, team member' appear in both), but every sentence carries information and nothing is padded.
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?
With no output schema, the description is the only place return values are described, and it does that adequately: status, times with time zone semantics, client, per-service team member and length, totals and balance, and notes. For a single-parameter read tool with full annotation coverage, this is sufficient to call correctly.
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?
There is one parameter and schema description coverage is 100%, with the schema already explaining that the id comes from list_appointments and constraining it to digits. The description adds no further meaning about the identifier, so the baseline of 3 applies.
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 states a specific verb and resource ('One appointment') and enumerates the fields returned (client, services, team member, times, status, price, notes), which distinguishes it clearly from list-oriented siblings. It stops short of naming list_appointments as the companion tool inside the description itself, though the schema covers that linkage.
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?
Usage is implied rather than stated: 'One appointment' suggests this is the single-record read rather than a list, and the schema notes the id comes from list_appointments. There is no explicit when-to-use/when-not or named alternative in the description text.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_clientGet clientARead-onlyIdempotentInspect
One client: contact, notification settings, booking stats, upcoming appointments, notes.
Reads the client profile as the client's page shows it: name, email, mobile, gender, birthday, whether Fresha sends them appointment messages and marketing by email, SMS and WhatsApp, booking and sales totals, the next 10 appointments and the latest 10 notes.
| Name | Required | Description | Default |
|---|---|---|---|
| client_id | Yes | Client id from list_clients. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly=true, idempotent=true, destructive=false and openWorld=true, so safety is covered; the description adds genuinely new behavioral detail by stating the result caps ("next 10 appointments", "latest 10 notes") and that output mirrors the client's page. It stops short of noting what happens for a nonexistent client_id or whether data is truncated/paginated.
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, front-loaded with the one-line summary before the detailed field breakdown. Slight redundancy between the list in sentence one and the restatement in sentence two, but the detail second sentence earns its place by adding caps and field types.
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?
No output schema exists, so the description must carry the return contract, and it does: exact field families, item limits on appointments and notes, and the "as the client's page shows it" framing. Combined with a fully documented single parameter and read-only annotations, an agent has everything needed to call it correctly.
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 100% and the client_id property already documents the pattern and origin ("Client id from list_clients."), so the description adds nothing about parameters. Baseline 3 is appropriate when the schema carries the field-level meaning.
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 ("One client: contact, notification settings, booking stats, upcoming appointments, notes") and enumerates the returned fields, so the agent knows exactly what this fetches. It implies the single-record counterpart to list_clients via "One client" but never names that sibling explicitly.
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?
Usage is only implied: the pattern that client_id comes "from list_clients" hints at the list-then-fetch flow, but there is no explicit when-to-use, when-not, or named alternative (e.g., list_clients for browsing, get_sales_summary for aggregate numbers).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_sales_summaryGet sales summaryARead-onlyIdempotentInspect
Sales summary for a period: sales, refunds, discounts, taxes and payments by type.
Reads Reports > Sales summary for the days given: one row per sale type (services, products, memberships, gift cards and so on) with Fresha's own columns (gross and net sales, discounts, refunds, taxes) and a totals row. columns names each key. A period without sales has no rows. Amounts are in the business's currency.
| Name | Required | Description | Default |
|---|---|---|---|
| to | No | Last day. Default: same as from. | |
| from | Yes | First day. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already carry readOnly/idempotent/destructive=false, so the safety profile is covered. The description adds genuine context beyond them: rows are grouped per sale type with a totals row, an empty period returns no rows, and amounts are in the business's currency — all useful for interpreting results.
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 short paragraphs, front-loaded with the purpose then the return shape; every sentence contributes (metrics list, row structure, empty-period behavior, currency). Minor redundancy between the opening list and the parenthetical column list.
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?
With no output schema, the description must describe the return shape, and it does: one row per sale type, Fresha's columns, a totals row, and 'columns names each key.' Only gap is any hint of aggregation granularity limits or currency conversion behavior.
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 100% and both parameters (from required, to defaulting to from) are fully documented there. The description only restates 'for the days given' without adding format, timezone, or range-limit semantics, so it meets the baseline but no more.
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?
Opens with a specific verb+resource ('Sales summary for a period') and immediately enumerates the metrics covered (sales, refunds, discounts, taxes, payments by type). The second paragraph's 'one row per sale type' shape implicitly separates it from the transaction-level list_sales sibling, though it never names an alternative.
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?
It grounds the tool in a known UI location ('Reads Reports > Sales summary for the days given'), which tells the agent this is an aggregated report pull. But there is no explicit when-to-use/when-not guidance and no mention of list_sales as the per-transaction alternative, leaving the choice to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_appointmentsList appointmentsARead-onlyIdempotentInspect
Appointments in a date range, with client, service, team member, price and status.
Lists appointments as Sales > Appointments shows them, latest first: one row per booked service with its appointment id, reference, start (wall clock in the business's time zone), length, client, team member, price, status and channel (online or offline). Cancelled appointments are included with their status. Page with next_cursor. The report itself has no status, team member or client filter, so those three apply to the first 100 rows of the date range: for a busy range, narrow the dates.
| Name | Required | Description | Default |
|---|---|---|---|
| to | No | Last day. Default: same as from. | |
| from | Yes | First day. | |
| limit | No | Page size. Default: 50. | |
| cursor | No | next_cursor from the previous call. | |
| status | No | Only this status, as Fresha names it: booked, confirmed, arrived, started, completed, canceled, no-show. Default: all. | |
| client_id | No | Only this client's appointments. | |
| team_member_id | No | Only this team member's appointments. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the read-only/idempotent annotations, it discloses ordering (latest first), row granularity (one row per booked service), inclusion of cancelled appointments, wall-clock timezone handling, and pagination via next_cursor. The filter limitation on the first 100 rows is an important non-obvious behavior.
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 front-loaded with purpose and ordering, then describes return fields, pagination, and the filter caveat. It is slightly dense and repeats some field-level detail already available in the schema, but every sentence contributes useful behavioral context.
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 read-only list tool with no output schema, it covers return fields, ordering, status inclusion, timezone semantics, pagination, and the important filter-scope limitation. An agent has enough information to call and interpret results correctly.
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 100%, so the schema already defines each parameter. The description still adds meaningful context by explaining that status/client/team_member filters only cover the first 100 rows and that pagination uses next_cursor, which is not apparent from the schema alone.
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 states a specific verb and resource ('Appointments in a date range') plus the fields returned, and anchors behavior to 'Sales > Appointments' ordering. It is clearly distinguishable from siblings like get_appointment or list_sales.
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?
It gives a concrete usage caveat: status, team member and client filters only apply to the first 100 rows of the date range, so busy ranges should be narrowed. It does not explicitly name sibling alternatives for single-appointment lookup, but the operational guidance is clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_clientsList clientsARead-onlyIdempotentInspect
Clients of the business, searchable by name, email or phone.
Lists clients as the Client list shows them, newest first by default, with email, mobile, gender, total sales, review count and whether they are blocked. search matches name, email or phone. total is the number of matching clients. Page with next_cursor.
| Name | Required | Description | Default |
|---|---|---|---|
| sort | No | Order: newest (default), oldest or name. | |
| limit | No | Page size, at most 50 (Fresha refuses more). Default: 50. | |
| cursor | No | next_cursor from the previous call. | |
| search | No | Name, email or phone. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, non-destructive and openWorld, so safety is covered. The description adds genuinely new behavioral context beyond that: default ordering (newest first), the field set returned, a total match count, and cursor-based paging via next_cursor.
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?
Dense and front-loaded, with the resource named first and paging last. Minor waste in repeating the searchable fields twice ('searchable by name, email or phone' then 'search matches name, email or phone'), but overall tight.
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?
With no output schema, the description carries the return-shape burden and does so: it lists the fields returned, the total count, the default sort, and how to page. Four optional parameters are all accounted for, leaving nothing an agent needs missing.
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 100%, so every parameter is already documented with defaults and the 50-item cap. The description mostly restates the schema ('search matches name, email or phone', 'newest first by default', 'Page with next_cursor') rather than adding syntax or edge-case meaning, so baseline 3 applies.
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?
Opens with a clear verb+resource statement ('Clients of the business, searchable by name, email or phone') and enumerates the returned fields. It is distinguishable from get_client via the word 'Lists' and the paging/search framing, but never explicitly contrasts itself with the single-client sibling.
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?
Usage is implied by the tool's shape: an agent can infer this enumerates clients and supports a search filter. However, there is no explicit guidance on when to prefer list_clients over get_client (e.g., 'use get_client when you already know the id') and no stated exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_reviewsList reviewsARead-onlyIdempotentInspect
Client reviews of the business with rating, text and replies.
Lists reviews as Online reputation shows them, newest first: star rating, text, author, the client and appointment it is about, source (Fresha or Google) and the replies. Filter by rating, source, words or reviews still without a reply. Page with next_cursor.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Page size. Default: 50. | |
| cursor | No | next_cursor from the previous call. | |
| search | No | Words in the review. | |
| source | No | Only Fresha or Google reviews. | |
| ratings | No | Only these star ratings. | |
| unreplied_only | No | Only reviews without a reply. Default: false. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent and non-destructive, so the description spends its words well: it adds the ordering guarantee ('newest first'), the source domain (Fresha or Google), and the pagination mechanics via next_cursor. It doesn't discuss page-size defaults or rate limits, but those are minor given the annotation coverage.
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, front-loaded with what is returned, then the ordering, then filters, then pagination. No filler and nothing buried at the end.
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?
No output schema exists, and the description compensates by enumerating the fields each review contains plus sort order and cursor paging. Combined with the fully documented schema, an agent has everything needed to call this correctly.
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 100%, so every parameter is already documented. The description restates the filter dimensions in prose (rating, source, words, unreplied) and names next_cursor for paging, but adds no syntax, defaults, or edge-case semantics beyond what the schema provides — notably omitting `limit`.
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?
States a specific verb and resource ('Client reviews of the business') plus the exact payload each item carries (rating, text, author, client, appointment, source, replies). An agent can immediately distinguish this read-only listing from the mutation sibling reply_to_review without opening the schema.
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?
Usage context is concrete: filter by rating, source, words, or reviews still without a reply, and page with next_cursor. The 'still without a reply' filter implicitly routes the agent toward reply_to_review, but no sibling is named and no exclusion (e.g. when to use a different listing tool) is stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_salesList salesARead-onlyIdempotentInspect
Sales (invoices) in a date range: number, date, client, status and totals.
Lists sales as Sales > Sales list shows them for the days given, one row per sale with Fresha's columns (sale number, date, client, status, gross total and so on; linked records come with an _id). Amounts are in the business's currency. Page with next_cursor.
| Name | Required | Description | Default |
|---|---|---|---|
| to | No | Last day. Default: same as from. | |
| from | Yes | First day. | |
| limit | No | Page size. Default: 50. | |
| cursor | No | next_cursor from the previous call. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint and destructiveHint=false, so the safety profile is covered. The description adds genuinely useful context beyond that: amounts are in the business's currency, linked records carry an `_id`, and results are paginated. It stops short of saying how the date range is interpreted (inclusive boundaries, timezone) or what happens with very large ranges.
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?
Front-loaded and reasonably tight: the shape of the rows comes first, then the pagination instruction. The two paragraphs partially restate each other (date range and column list appear twice), which costs a little economy but no clarity.
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?
With no output schema, the description does the work of describing the return shape (columns, currency, `_id` on linked records) and pagination. That is enough to call the tool correctly, though the treatment of inclusive date boundaries and default range behavior is left to the schema alone.
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 100%, with defaults for `to` and `limit` documented inline, so the baseline is 3. The description only reinforces 'date range' and 'next_cursor', adding no format details or constraint semantics beyond what the schema already provides.
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 states a specific verb and resource ('Sales (invoices) in a date range') and enumerates the columns returned (sale number, date, client, status, gross total). It does not explicitly distinguish itself from the sibling get_sales_summary, so a reader must infer list-vs-aggregate on their own.
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?
Usage is only implied by the date-range framing; there is no statement of when to call this versus get_sales_summary or how to handle open-ended ranges. The pagination hint ('Page with next_cursor') is the only actionable guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_servicesList servicesARead-onlyIdempotentInspect
Services of the business with category, duration and price.
Lists the active services of the business's location as the Catalogue shows them, with their category, duration in minutes, price and whether clients can book them online. A service with several prices (by length or level) lists each as a pricing level; bookings use the first one. Use service_id in find_available_slots and create_appointment.
| Name | Required | Description | Default |
|---|---|---|---|
| search | No | Only services whose name contains this. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, idempotentHint=true, and destructiveHint=false, so the safety profile is covered. The description adds meaningful behavior beyond annotations: only active services are listed, results mirror the Catalogue, multi-price services appear as pricing levels, and bookings use the first level.
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 front-loaded and tightly structured: the opening sentence names the resource and fields, and the second explains catalogue semantics and downstream tool usage. Every sentence carries operational value without padding.
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?
With no output schema, the description compensates by explaining the returned service attributes, active-only scope, multi-price representation, and how service_id is used downstream. No essential calling context is missing for a read-only catalogue list tool.
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 100%, so the single 'search' parameter is already fully documented in the schema. The description adds no parameter-specific guidance, making the baseline 3 appropriate.
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 states a specific verb and resource ('Lists the active services of the business's location') and enumerates the returned fields: category, duration, price, and online booking availability. It also differentiates itself from adjacent workflows by pointing to find_available_slots and create_appointment for service_id consumption.
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?
It clearly establishes the context: retrieving the catalogue of active services, including multi-price handling and the rule that bookings use the first pricing level. It does not explicitly name competing sibling tools or exclusion conditions, but the downstream usage guidance for service_id is concrete.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_team_membersList team membersARead-onlyIdempotentInspect
Team members with role, contact, services they do and whether they take bookings.
Lists the team as Team members shows it: name, email, mobile, role (owner, manager, basic), job title, whether they take bookings and online bookings, the services assigned to them and their location. Also lists the business's locations. Use team_member_id in the calendar tools.
| Name | Required | Description | Default |
|---|---|---|---|
| include_former | No | Also team members who left. Default: false. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint and destructiveHint=false, so the safety profile is covered. The description adds useful context about what is returned (including business locations), but says nothing about ordering, pagination, or authorization needs.
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?
Content is front-loaded with the resource and its fields. The phrasing 'Lists the team as Team members shows it' is slightly awkward and redundant, but overall there is little waste.
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?
With no output schema, the description carries the burden of describing returns, and it does so by enumerating the member fields and noting locations are included. An agent can call this confidently; only ordering/pagination details are absent.
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 100% and the single boolean include_former is fully documented in the schema ('Also team members who left. Default: false'). The description does not touch the parameter at all, so baseline 3 applies.
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?
States a specific verb and resource (list team members) and enumerates the fields returned – name, email, role, bookings, services, location. It is clearly distinguishable from siblings like list_clients or list_services, though no sibling is named explicitly.
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 closing line 'Use team_member_id in the calendar tools' implies the primary workflow (discover members, then use their IDs for appointments). However, there is no explicit when/when-not guidance or mention of alternatives such as list_services for the service side.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
reply_to_reviewReply to reviewADestructiveInspect
Post a public reply to a client's review.
Posts a reply under the review as Reply in Online reputation does, signed as the business or the team member. The reply is public on the business's Fresha page and the client is told, so this reaches people. It needs a review that exists: take review_id from list_reviews.
| Name | Required | Description | Default |
|---|---|---|---|
| text | Yes | Reply text. | |
| reply_as | No | Sign the reply as the business (default) or as the team member signed in. | |
| review_id | Yes | Review id from list_reviews. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare write, destructive, open-world and non-idempotent, and the description adds real consequence: the reply is public on the business's Fresha page and the client is notified, plus who it is signed as. It falls short of explaining the non-idempotent/destructive angle — e.g. whether a second reply replaces the first or stacks — which matters given idempotentHint=false.
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?
Short and front-loaded with the action and its audience. The middle sentence ('as Reply in Online reputation does') is slightly redundant framing that could be cut, but nothing else is wasted.
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 3-parameter write tool with no output schema, the description covers the action, the audience, the prerequisite and the identity option. The only real omission is what the call returns and what happens on a repeat reply.
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 100%, so all three parameters (text, reply_id, reply_as enum) are already documented in the schema, including the enum meanings and the list_reviews provenance. The description restates signing-as-both rather than adding syntax, limits, or edge cases beyond the schema.
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?
Specific verb+resource ('Post a public reply to a client's review') with the write target and visibility stated up front. It is clearly distinct from list_reviews and the rest of the sibling set, which are read/list or scheduling tools.
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?
Gives an explicit prerequisite chain: 'It needs a review that exists: take review_id from list_reviews,' which routes the agent to the sibling that produces the required input. It does not state any when-not-to-use conditions, but no competing reply tool exists.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
reschedule_appointmentReschedule appointmentADestructiveInspect
Move an appointment to a new start time, optionally to another team member.
Moves every service of the appointment by the same amount so the first starts at start_at, as Reschedule in the appointment drawer does; lengths and prices stay. team_member_id moves all services to that team member. With notify_client (default true) Fresha sends the client a reschedule message by their notification settings; false moves it quietly. Cancelled, completed and no-show appointments are refused.
| Name | Required | Description | Default |
|---|---|---|---|
| start_at | Yes | New start. ISO 8601: 2026-11-03T14:30 is wall clock in the business's time zone; with an offset (2026-11-03T14:30:00+01:00) it is converted. | |
| notify_client | No | Let Fresha send the client a reschedule message, by their notification settings. Default: true. | |
| appointment_id | Yes | Appointment id from list_appointments. | |
| team_member_id | No | New team member for every service. Default: unchanged. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond destructiveHint/openWorldHint, the description discloses the crucial non-obvious behaviors: all services shift by the same offset with lengths and prices preserved, team_member_id reassigns every service, and notify_client=true (default) sends a client message while false moves it quietly. Refusal on cancelled/completed/no-show is exactly the kind of behavioral constraint annotations cannot express.
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?
Four sentences, front-loaded with purpose, then mechanics, then the notification default, then the refusal conditions. No filler and no repetition of schema text.
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?
With no output schema, the description never indicates what comes back after a reschedule (updated appointment, confirmation, error shape), which is the one remaining gap. Everything an agent needs to decide, prepare inputs, and anticipate side effects is otherwise present.
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 100%, so the baseline is 3; the description still adds meaning the schema lacks by explaining that start_at is the first service's start and that all remaining services are shifted relative to it, and that team_member_id applies to every service rather than one. It does not clarify timezone conversion, which is left entirely to the schema.
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?
States a specific verb and resource ('Move an appointment to a new start time') plus the optional scope ('optionally to another team member'), which cleanly separates it from create_appointment and cancel_appointment in the sibling list. The added sentence about shifting all services makes the operation concrete.
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?
Gives clear when-not guidance: 'Cancelled, completed and no-show appointments are refused,' and equivalences the agent can reason from ('as Reschedule in the appointment drawer does'). It never names a sibling alternative for the refusal cases (e.g. create_appointment for a new booking), so it stops short of full routing guidance.
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.
17 tool updates
v0.1.0- First observed
add_blocked_time - First observed
add_client - First observed
add_client_note - First observed
cancel_appointment - First observed
create_appointment - First observed
find_available_slots - First observed
get_appointment - First observed
get_client - First observed
get_sales_summary - First observed
list_appointments - First observed
list_clients - First observed
list_reviews - First observed
list_sales - First observed
list_services - First observed
list_team_members - First observed
reply_to_review - First observed
reschedule_appointment
TDQS
Scored across 17 tools
Most tools target a distinct resource+action (add_client, get_appointment, cancel_appointment, reply_to_review), and the descriptions draw careful boundaries between calendar writes. The main soft overlap is in reporting: list_appointments, list_sales and get_sales_summary all return revenue-flavoured rows, so an agent wanting 'today's bookings' vs 'today's invoices' must read descriptions carefully.
Nearly everything follows a clean verb_noun pattern: add_/list_/get_/create_/cancel_/reschedule_/find_ plus a resource noun. The only slight deviation is reply_to_review (verb_to_noun), but it reads naturally and matches the domain language.
17 tools is slightly on the heavy side but each maps to a real capability across clients, calendar, services, team, sales and reviews. Nothing looks like filler; the count is justified for a multi-surface business-management domain.
Reads and the core booking lifecycle (find slots, create, reschedule, cancel) are well covered, but write coverage is uneven: clients can be created, listed and noted but never edited or deleted; blocked time can be added but never listed for removal. Several lifecycle steps (mark complete/no-show, edit appointment services or notes, get a single sale/service) are missing, leaving dead ends.
Maintenance
Related MCP Connectors
Hosted MCP server for Cliniko — patients, appointments, availability, and invoices for AI agents.
Hosted Google Business Profile MCP server: reviews, posts, profile edits, Maps rank. OAuth sign-in.
Agentic CRM for service businesses — bookings, customers, WhatsApp, loyalty, invoicing.
Marketo MCP server for AI. 130 tools to operate Marketo from Claude, Cursor, or ChatGPT.
Related MCP Servers
- AlicenseAqualityFmaintenanceSelf-hosted MCP server for AI browser automation. Connects to your own Chromium instance via CDP, providing tools for browser control, navigation, interaction, and content extraction.191MIT
- AlicenseAqualityDmaintenanceEnables AI assistants to find, book, and cancel salon appointments via SalonRunner accounts. Self-hosted for local or remote use.616 npmMIT
- FlicenseNot gradedqualityCmaintenanceSelf-hosted MCP server for browser automation with stealth patches, offering 50 tools for navigation, scraping, and session configuration. Runs via Docker and supports remote control from any MCP client.-
- AlicenseAqualityBmaintenanceEnables AI agents and scripts to manage bookings, provider assignment, rescheduling, cancellations, customers, quotes, invoices, and dashboard data through a self-hosted API and MCP interface using your own Chrome session.2171PolyForm Noncommercial 1.0.0