MCP Integration
Overview
Bronjoy speaks MCP (Model Context Protocol), so an AI assistant — Claude, ChatGPT, or anything else that talks MCP — can search for a business, answer questions about it, and book an appointment.
There are two servers, for two different audiences:
| Marketplace server | Organization server | |
|---|---|---|
| For | Anyone. End users, developers, AI assistants | One organization's own assistant |
| Covers | The whole public catalogue | A single organization |
| Auth | None | Bearer token |
| Endpoint | https://api.bronjoy.com/api/mcp | https://api.bronjoy.com/api/mcp/organization/{id} |
| Cost | Free | Paid plan feature |
If you are a person who wants to book a haircut through your assistant, you want the marketplace server. If you are a business that wants your own assistant to manage your calendar, you want the organization server.
Marketplace server
The public catalogue as an MCP server. No account, no token, no signup — it exposes exactly what the Bronjoy website already serves to any browser, plus the same verified booking path.
Connect
claude mcp add --transport http bronjoy https://api.bronjoy.com/api/mcp
{
"mcpServers": {
"bronjoy": {
"url": "https://api.bronjoy.com/api/mcp"
}
}
}
Transport: Streamable HTTP
Endpoint: https://api.bronjoy.com/api/mcp
Auth: none
That is the whole setup. Ask your assistant "find me a barbershop in Tashkent" and it will use the tools below.
Tools
Six tools, in the order a booking conversation uses them.
search-venues
Search the catalogue by keyword, city and category. Every other tool takes the slug this returns.
| Parameter | Type | Required | Description |
|---|---|---|---|
query | string | No | Free text — a business name, a service (стрижка, haircut), or a landmark. Matches across Latin and Cyrillic |
city | string | No | City slug or name — tashkent, samarkand, Бухара |
category | string | No | Category slug — barber, dental, car-wash |
bookable_only | boolean | No | Return only venues that take online bookings. Set this when the user wants to book rather than to be told about places |
limit | integer | No | 1–20, default 5 |
{
"venues": [
{
"name": "Britva Barbershop",
"slug": "demo-barber",
"url": "https://bronjoy.com/tashkent/barber/demo-barber",
"city": "Tashkent",
"district": "Юнусабадский район",
"address": "проспект Амира Темура, 15",
"specialty": "Men's haircuts and beard care",
"rating": 4.9,
"reviews": 7,
"is_bookable": true,
"next_available": "Tomorrow 09:00",
"price_from": 50000,
"has_phone": true
}
],
"returned": 1,
"total_matches": 1
}
get-venue
The full public profile of one venue: address, opening hours, services, amenities, payment methods, website, rating. The phone number comes back masked here.
| Parameter | Type | Required | Description |
|---|---|---|---|
slug | string | Yes | From search-venues |
{
"name": "Britva Barbershop",
"city": "Ташкент",
"address": "проспект Амира Темура, 15",
"is_bookable": true,
"website": "https://britva.uz",
"masked_phone": "+998 71 ***-**-70",
"opening_hours": {
"monday": { "open": "09:00", "close": "19:00" },
"saturday": { "open": "08:00", "close": "18:00" },
"sunday": { "open": null, "close": null }
},
"service_tags": ["Haircuts", "Beard shaping", "Royal shave", "Styling"],
"price_from": 50000,
"price_to": 110000,
"rating": "4.90",
"reviews": 7,
"booking": "Online booking is available — continue with check-availability."
}
reveal-phone
The venue's real phone number. Deliberately a separate call from get-venue, and rate limited to 10 per minute — request it for a venue the user has actually chosen, never in bulk across search results.
| Parameter | Type | Required | Description |
|---|---|---|---|
slug | string | Yes | From search-venues |
{
"name": "Britva Barbershop",
"phone": "+998 71 200-70-70",
"url": "https://bronjoy.com/tashkent/barber/demo-barber"
}
check-availability
Bookable services and open slots. Call it without service_id first to list the services and their prices, then again with one to get its slots.
| Parameter | Type | Required | Description |
|---|---|---|---|
slug | string | Yes | From search-venues |
date | string | Yes | YYYY-MM-DD, today or up to 30 days ahead |
service_id | integer | No | Omit on the first call to list services |
{
"date": "2026-09-10",
"service": {
"service_id": 1,
"name": "Мужская стрижка",
"duration_minutes": 30,
"price": "80000.00",
"currency": "UZS"
},
"slots": [
{
"start_time": "2026-09-10T09:00:00+05:00",
"end_time": "2026-09-10T09:30:00+05:00",
"staff_id": 1,
"staff_name": "Азиз Рахимов"
}
]
}
start_time is exactly the value book-appointment expects. Do not round it or convert it.
request-booking-code
Sends a 6-digit code to the user's own email or phone.
| Parameter | Type | Required | Description |
|---|---|---|---|
slug | string | Yes | The venue being booked |
email | string | Either | The user's email |
phone | string | Either | The user's phone, +998901234567 |
The code lasts 5 minutes and covers one booking. Sends are capped per sender and per recipient — do not loop.
book-appointment
Creates the appointment.
| Parameter | Type | Required | Description |
|---|---|---|---|
slug | string | Yes | The venue |
service_id | integer | Yes | From check-availability |
start_time | string | Yes | The exact start_time from a slot |
name | string | Yes | The user's name, as the business should see it |
verification_code | string | Yes | The 6 digits the user read back |
email / phone | string | Either | The same contact the code was sent to |
notes | string | No | Max 500 characters |
The booking is created as pending — the business still confirms it. Tell the user it is requested, not guaranteed. If the venue takes prepayment, the response carries a payment_url the user must open.
The booking flow
1. search-venues → bookable_only: true
2. check-availability → pick a service, then a slot
3. request-booking-code → code goes to the user, not to you
4. ask the user to read you the code
5. book-appointment → with that code
Two kinds of venue
Every result carries is_bookable, and it decides what you can offer next:
is_bookable: true— set up for online booking. The flow above works.is_bookable: false— a directory listing. Real address, real hours, often a service list and a phone number, but no online booking. Answer from that data and offer the phone number.
bookable_only: true when the user's intent is to book, and say plainly when a place has to be phoned.Reading the data
- Names, services and addresses are in Russian or Uzbek. Do not translate an address the user has to show a taxi driver.
- Prices are in Uzbek som (UZS). Many listings have no price.
от 300000 сумmeans "from 300,000 UZS". - Times are in the venue's local timezone, and carry their offset.
- Ratings on search results come from the source directory, not from Bronjoy reviews.
Limits
| Scope | Limit |
|---|---|
| All requests | 30/minute per caller |
reveal-phone | 10/minute per caller |
request-booking-code | 10/hour per caller, plus a per-recipient cap |
Exceeding a limit returns an error naming the seconds to wait.
Organization server
A private, per-organization server an owner connects their own assistant to. It manages that organization's calendar: services, slots, bookings and cancellations. It is a paid plan feature and requires a token.
1. Generate a token
From the provider dashboard, or via the API:
POST /api/org/ai/tokens
Authorization: Bearer your-staff-token
Content-Type: application/json
{
"name": "Claude Desktop",
"expires_in_days": 90
}
Response:
{
"token": "3|AbCdEf...",
"mcp_endpoint": "https://api.bronjoy.com/api/mcp/organization/1",
"expires_at": "2026-06-23T12:00:00Z",
"instructions": "Use this token in the Authorization header: Bearer <token>"
}
Save the token immediately — it is shown only once.
2. Connect your AI client
claude mcp add --transport http bronjoy-org \
https://api.bronjoy.com/api/mcp/organization/1 \
--header "Authorization: Bearer 3|AbCdEf..."
{
"mcpServers": {
"bronjoy": {
"url": "https://api.bronjoy.com/api/mcp/organization/1",
"headers": {
"Authorization": "Bearer 3|AbCdEf..."
}
}
}
}
Transport: Streamable HTTP
Endpoint: https://api.bronjoy.com/api/mcp/organization/{id}
Auth: Authorization: Bearer {token}
Tools
list-services
List all bookable services with prices and durations. Use this first to discover service IDs.
Parameters: None
{
"services": [
{
"id": 1,
"name": "Haircut",
"description": "Classic haircut with styling",
"duration_minutes": 30,
"price": "25.00",
"currency": "USD",
"category": "Hair Services"
}
],
"total": 1
}
search-availability
Find available time slots for a specific service and date.
| Parameter | Type | Required | Description |
|---|---|---|---|
service_id | integer | Yes | Service ID (from list-services) |
date | string | Yes | YYYY-MM-DD, today or up to 30 days ahead |
staff_id | integer | No | Specific staff member |
{
"date": "2026-03-26",
"service": "Haircut",
"timezone": "Asia/Tashkent",
"available_times": ["09:00", "10:00", "14:30", "15:00"],
"total_found": 10,
"showing": 4,
"note": "Times are in H:i format (24-hour) in the organization timezone."
}
create-appointment
Book an appointment for a client.
| Parameter | Type | Required | Description |
|---|---|---|---|
client_name | string | Yes | Client's full name (2–100 chars) |
client_phone | string | Yes | Phone in international format (+998901234567) |
service_id | integer | Yes | Service ID |
date | string | Yes | YYYY-MM-DD |
time | string | Yes | HH:MM, 24-hour |
staff_id | integer | No | Specific staff member (auto-assigned if omitted) |
branch_id | integer | No | Branch location |
notes | string | No | Max 500 chars |
{
"success": true,
"appointment_id": 123,
"message": "Appointment booked for John Doe on 2026-03-26 at 14:30."
}
check-appointment
Look up existing appointments by phone number.
| Parameter | Type | Required | Description |
|---|---|---|---|
client_phone | string | Yes | Phone in international format |
appointment_id | integer | No | Specific appointment (shows all upcoming if omitted) |
Returns a single appointment or up to 5 upcoming appointments for the phone number.
cancel-appointment
Cancel an existing appointment. The client's phone must match the booking.
| Parameter | Type | Required | Description |
|---|---|---|---|
appointment_id | integer | Yes | Appointment ID |
client_phone | string | Yes | Must match the booking |
reason | string | No | Max 500 chars |
Resources
organization://profile
The organization profile — services, staff, branches, timezone and currency. Lets an AI client understand the context without calling individual tools.
Prompts
booking-assistant
A system prompt that sets up a booking conversation, with instructions for the recommended workflow.
Recommended workflow
1. list-services → Show what's available
2. search-availability → Find open slots for the chosen service
3. Confirm with user → Let the human pick a time
4. create-appointment → Book it
For cancellations:
1. check-appointment → Find the appointment by phone
2. Confirm with user → Which one to cancel?
3. cancel-appointment → Cancel it
Limits
| Scope | Limit |
|---|---|
| Per token | 60 requests/minute |
| Per organization | 120 requests/minute |
Exceeding limits returns 429 Too Many Requests.
Security
- Each token is scoped to a single organization — it cannot reach another organization's data
- Tokens expire after the configured number of days (default 90, max 365)
- The server is booking-only — no internal management endpoints, financial data or staff settings
- Phone numbers verify the client on check and cancel operations
- All operations are logged for audit
Troubleshooting
| Issue | Solution |
|---|---|
401 Unauthorized | Token expired or invalid — generate a new one |
403 Forbidden | Token doesn't have access to this organization |
429 Too Many Requests | Rate limit exceeded — wait and retry |
Feature not available | MCP access is not enabled for this organization |
| No slots returned | Staff schedules may not be set, or the service has no assigned staff |