Bookeo MCP Server
This MCP server lets you look up Bookeo customer bookings and payment information.
Search bookings by customer name or email (case-insensitive, with configurable days back)
Retrieve a specific booking by its booking number
Search bookings within a date range, optionally including canceled bookings
Get payment details for a booking, including methods, amounts, and manual vs Stripe detection
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Bookeo MCP ServerFind my bookings for last week"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Bookeo MCP Server
An MCP (Model Context Protocol) server for looking up customer bookings and payment information from Bookeo.
Features
Search bookings by customer name or email
Look up specific bookings by booking number
Search bookings within a date range
Get payment details with manual vs Stripe payment detection
Related MCP server: AutotaskMCP
Prerequisites
Python 3.10+
Bookeo API credentials (API Key and Secret Key)
Installation
Clone the repository and create a virtual environment:
cd bookeo
python3 -m venv .venv
source .venv/bin/activateInstall the package:
pip install -e .Configuring with Claude Code
Using claude mcp add (Recommended)
claude mcp add --transport stdio bookeo \
-e API_KEY=your_bookeo_api_key \
-e API_SECRET=your_bookeo_secret_key \
-- /path/to/bookeo/.venv/bin/python -m bookeo_mcp.serverReplace /path/to/bookeo with the actual path to the project directory.
Using .mcp.json
Alternatively, add to your .mcp.json file (project directory for project-specific, or ~/.claude/.mcp.json for global):
{
"mcpServers": {
"bookeo": {
"type": "stdio",
"command": "/path/to/bookeo/.venv/bin/python",
"args": ["-m", "bookeo_mcp.server"],
"cwd": "/path/to/bookeo",
"env": {
"API_KEY": "your_bookeo_api_key",
"API_SECRET": "your_bookeo_secret_key"
}
}
}
}Note: If using .mcp.json, you can alternatively store credentials in a .env file in the project directory instead of in the config.
Configuring with the Claude desktop app
Add the server to ~/Library/Application Support/Claude/claude_desktop_config.json
(Settings → Developer → Edit Config), then quit and reopen the app:
{
"mcpServers": {
"bookeo": {
"command": "/path/to/bookeo/.venv/bin/python",
"args": ["-m", "bookeo_mcp.server"]
}
}
}The desktop app ignores cwd and starts the server from an unrelated
directory, so credentials are read from the .env next to the bookeo_mcp
package (the project directory for an editable install). Alternatively put
API_KEY and API_SECRET in an env block as above.
Dates passed to the search tools are interpreted in the business's timezone,
America/Toronto by default. Set BOOKEO_TIMEZONE to an IANA zone name to
change it.
Available Tools
search_bookings_by_customer
Search for bookings by customer name or email.
customer_name: Full or partial customer name (case-insensitive)customer_email: Full or partial email address (case-insensitive)days_back: How many days back to search (default 90, max 365)
get_booking
Look up a specific booking by its booking number.
booking_number: The Bookeo booking number
search_bookings_by_date
Find all bookings within a date range.
start_date: Start date in YYYY-MM-DD formatend_date: End date in YYYY-MM-DD formatinclude_canceled: Whether to include canceled bookings (default false)
get_booking_payments
Get payment details for a specific booking.
booking_number: The Bookeo booking number
Returns payment breakdown including methods, amounts, and whether payments were manual or via Stripe.
Running Standalone
stdio Transport (default)
bookeo-mcpStreamable HTTP Transport
For network-accessible deployments, use the Streamable HTTP transport:
bookeo-mcp --transport streamable-http --host 0.0.0.0 --port 8000Options:
--transport:stdio(default) orstreamable-http--host: Host to bind to (default:127.0.0.1)--port: Port to listen on (default:8000)
Environment variables for the HTTP transport:
AUTH_TOKEN: if set,/mcprequires eitherAuthorization: Bearer <token>or an OAuth access token obtained by signing in with it (see Claude connectors)PUBLIC_URL: the URL clients reach the server at, advertised in the OAuth metadata (default:https://plus the firstALLOWED_HOSTSentry, elsehttp://localhost:8000)ALLOWED_HOSTS: comma-separated hosts for DNS rebinding protection, e.g.ekbookeo.fallday.ca:*; leave unset to disable it for local development
Docker
Using Pre-built Image
docker pull ghcr.io/fdstevex/bookeo-mcp:latest
docker run -p 8000:8000 \
-e API_KEY=your_bookeo_api_key \
-e API_SECRET=your_bookeo_secret_key \
ghcr.io/fdstevex/bookeo-mcp:latestBuilding Locally
docker build -t bookeo-mcp .
docker run -p 8000:8000 \
-e API_KEY=your_bookeo_api_key \
-e API_SECRET=your_bookeo_secret_key \
bookeo-mcpThe Docker image runs with Streamable HTTP transport on port 8000 by default.
The MCP endpoint is /mcp.
Deployment
The server runs as a single container on the Oracle VM (ovm.fallday.ca,
arm64) at https://ekbookeo.fallday.ca/mcp, behind the shared Traefik on
infra-network.
Push to main and GitHub Actions builds a multi-arch (amd64 + arm64) image,
pushes it to ghcr.io/fdstevex/bookeo-mcp:<sha> (and :latest), then SSHes
to the VM with the sha. The deploy key is a forced command in the VM's
~/.ssh/authorized_keys that can only run ~/apps/bookeo/deploy.sh, which
writes the tag to .env, pulls the image, takes docker-compose.yml out of it
and runs docker compose up -d. The compose file ships in the image so that it
deploys with the code while the key still carries nothing but a tag. CI runs
tests/oauth_e2e.py before building and, after deploying, checks that the
live server serves its OAuth metadata.
VM prerequisites
~/apps/bookeo/on the VM holdingdocker-compose.yml,deploy.shand a.envwithAPI_KEY,API_SECRET,AUTH_TOKEN,ALLOWED_HOSTSandIMAGE_TAG. Both files come fromovm/in this repo. CI keepsdocker-compose.ymlcurrent;deploy.shis the one file toscpover by hand when it changes, since it is what the deploy key is locked to.The public half of the CI deploy key in
~/.ssh/authorized_keys, withcommand="/home/ubuntu/apps/bookeo/deploy.sh"and the usual restrictions. The private half is theOVM_DEPLOY_KEYrepo secret.
Manual deploy
ssh ubuntu@ovm.fallday.ca ~/apps/bookeo/deploy.sh <sha-or-latest>Adding it to Claude as a connector
Claude's custom connectors (claude.ai, the desktop app and the mobile apps) only authenticate with OAuth; they cannot send a fixed header. The server is its own single-user OAuth authorization server for this:
In Claude, Settings → Connectors → Add custom connector, URL
https://ekbookeo.fallday.ca/mcp. Leave the OAuth client fields empty; Claude registers itself.Connect. The browser opens the server's sign-in page; paste
AUTH_TOKEN.
The connector belongs to the Claude account, so it then works on every
device. Nothing is stored server-side: client ids, codes and tokens are signed
with a key derived from AUTH_TOKEN, so they survive redeploys, and rotating
AUTH_TOKEN signs every connector out. tests/oauth_e2e.py exercises the
whole flow against a local server.
Configuring Claude Code with the HTTP transport
To connect Claude Code to the deployed server:
claude mcp add --transport http bookeo https://ekbookeo.fallday.ca/mcp \
--header "Authorization: Bearer <AUTH_TOKEN>"Or in .mcp.json:
{
"mcpServers": {
"bookeo": {
"type": "http",
"url": "https://ekbookeo.fallday.ca/mcp",
"headers": {
"Authorization": "Bearer <AUTH_TOKEN>"
}
}
}
}Available Tools
4 toolsget_bookingA
Look up a specific booking by its booking number.
Args: booking_number: The Bookeo booking number (e.g., "123456789")
Returns: Complete booking details including customer, pricing, and product info
| Name | Required | Description | Default |
|---|---|---|---|
| booking_number | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description must carry the full burden of behavioral disclosure. It only mentions that the tool 'looks up' a booking, implying a read operation, but does not explicitly state idempotency, side effects, or any constraints. This is minimal for a tool with no 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?
The description is concise with three lines covering purpose, parameter details, and return summary. It is well-structured and front-loaded, with no unnecessary information.
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 1-parameter lookup tool with no output schema or annotations, the description provides sufficient context: return includes 'customer, pricing, and product info.' It could mention potential errors or references to siblings, but overall it is adequate.
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?
The description explains the only parameter 'booking_number' with an example ('e.g., 123456789'), adding meaningful context beyond the input schema which only provides a title. Given 0% schema description coverage, this adequately compensates.
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 clearly states 'Look up a specific booking by its booking number,' specifying the verb (look up), resource (booking), and identifier. This effectively distinguishes it from sibling tools like get_booking_payments and search_bookings_by_customer/date.
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 use when you have a booking number, but does not explicitly state when to use this tool versus siblings (e.g., searching by customer or date). The context of sibling names provides some guidance, but the description lacks explicit usage context.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_booking_paymentsA
Get payment details for a specific booking.
Args: booking_number: The Bookeo booking number
Returns: Payment breakdown including methods, amounts, and manual vs Stripe detection
| Name | Required | Description | Default |
|---|---|---|---|
| booking_number | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries full burden. It indicates the tool returns payment breakdown with methods, amounts, and detection source, but does not disclose permissions needed, error behavior (e.g., booking not found), or side effects. For a simple read operation, this is adequate but not thorough.
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 very concise: two sentences specifying purpose and parameter, with a structured Args and Returns section. No irrelevant information, and the key points are 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?
Given the low complexity (one param, no output schema), the description covers the essential aspects: what it does, what parameter is required, and what is returned (payment breakdown including methods, amounts, and detection). It is complete enough for a simple read tool, though the return structure could be slightly more detailed.
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 0%, so the description compensates by explaining 'booking_number' as 'The Bookeo booking number'. This adds clear meaning beyond the schema's generic title. For a single required parameter, it is sufficient.
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 clearly states the verb 'Get' and the resource 'payment details for a specific booking'. It differentiates from sibling tools like get_booking (returns booking info) and search_bookings (returns list of bookings) by focusing exclusively on payments.
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 provides the required parameter (booking_number) but lacks explicit guidance on when to use this tool versus alternatives. No exclusions or when-not-to-use information is given, leaving the agent to infer based on the specific payment focus.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_bookings_by_customerA
Search for bookings by customer name or email.
Args: customer_name: Full or partial customer name to search for (case-insensitive) customer_email: Full or partial email address to search for (case-insensitive) days_back: How many days back to search (default 90, max 365)
Returns: List of matching bookings with customer info, dates, and product details
| Name | Required | Description | Default |
|---|---|---|---|
| days_back | No | ||
| customer_name | No | ||
| customer_email | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries full burden for behavioral disclosure. It does not mention if the tool is read-only, whether permissions are needed, pagination, or error handling. It only states the return format, missing important traits for a search tool.
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 concise and well-structured with an Args section and Returns section. Every sentence adds value, and it is front-loaded with the core purpose. No wasted words.
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 search tool with 3 optional parameters and an output schema, the description explains parameters well. However, it does not clarify behavior when both name and email are provided (AND vs OR), or what happens when no search criteria are given. The return mention is sufficient, but some gaps remain.
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 0%, but the description adds useful meaning: 'Full or partial... (case-insensitive)' for customer_name and customer_email, and the range for days_back. This goes beyond the bare schema and helps the agent understand parameter usage.
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 clearly states the verb 'Search' and the resource 'bookings', and specifies criteria 'by customer name or email'. This distinguishes it from siblings like 'search_bookings_by_date' which searches by date, and 'get_booking' which retrieves a specific booking.
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 does not provide guidance on when to use this tool versus alternatives. It does not mention when not to use it or point to siblings like 'search_bookings_by_date' for date-based queries. The context signals and sibling names imply differentiation but the description itself lacks explicit usage guidelines.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_bookings_by_dateB
Find all bookings within a date range.
Args: start_date: Start date in YYYY-MM-DD format end_date: End date in YYYY-MM-DD format include_canceled: Whether to include canceled bookings
Returns: List of bookings with summary info
| Name | Required | Description | Default |
|---|---|---|---|
| end_date | Yes | ||
| start_date | Yes | ||
| include_canceled | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations provided, so description carries full burden. It does not mention read-only nature, potential rate limits, or pagination. Only describes basic functionality.
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?
Clear structure with Args and Returns sections. Slightly verbose due to docstring format, but each line adds value. No redundant information.
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 output schema present, returns explanation is acceptable. However, lacks details on pagination, limits, or edge cases. Adequate for basic use but leaves gaps for complex queries.
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 0%, but description adds format (YYYY-MM-DD) and purpose for each parameter, including default for include_canceled. Adequately compensates for missing schema descriptions.
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 clearly states the action (Find all bookings) and resource (bookings) with a specific scope (date range). It distinguishes from siblings like search_bookings_by_customer and get_booking.
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?
No guidance on when to use this tool versus alternatives (e.g., search_bookings_by_customer, get_booking). No mention of when not to use or prerequisites.
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.
4 tool updates
v1.0.0- First observed
get_booking - First observed
get_booking_payments - First observed
search_bookings_by_customer - First observed
search_bookings_by_date
TDQS
Scored across 4 tools
Each tool has a clearly distinct purpose: retrieving a single booking by number, getting payment details for a booking, searching by customer, and searching by date. No overlap.
All tool names follow a consistent verb_noun pattern: get_booking, get_booking_payments, search_bookings_by_customer, search_bookings_by_date.
4 tools is on the low side but acceptable for a read-only booking query server. However, the scope feels slightly thin.
The tool set is limited to read operations only, missing critical create, update, delete, or cancel booking tools, which are essential for booking management.
Maintenance
Related MCP Connectors
Manage an EasyWeek business from AI: bookings, availability, customers, services, orders, messaging.
Booking gateway for AI agents — discover events, movies & hotels, hand off to partner checkout.
Discover local services and availability, then create, track, reschedule, or cancel bookings.
Ask questions in plain language, get answers from your business database. No SQL required.
Related MCP Servers
- AlicenseAqualityDmaintenanceEnables fetching and searching Freshdesk tickets, including details like conversations, attachments, and custom fields, via natural language queries.3243 npm1MIT
- FlicenseNot gradedqualityDmaintenanceEnables interaction with Autotask REST API for ticket management and querying through natural language.-
- FlicenseNot gradedqualityDmaintenanceEnables natural language interaction with SimplyBook.me API to manage bookings, services, and performers through MCP.2-
- AlicenseNot gradedqualityCmaintenanceEnables querying Chargebee subscriptions and customer data through natural language, supporting subscription details, customer lists, and customer profiles.4 npmMIT