Skip to main content
Glama
fdstevex

Bookeo MCP Server

by fdstevex

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

  1. Clone the repository and create a virtual environment:

cd bookeo
python3 -m venv .venv
source .venv/bin/activate
  1. Install 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.server

Replace /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 format

  • end_date: End date in YYYY-MM-DD format

  • include_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-mcp

Streamable HTTP Transport

For network-accessible deployments, use the Streamable HTTP transport:

bookeo-mcp --transport streamable-http --host 0.0.0.0 --port 8000

Options:

  • --transport: stdio (default) or streamable-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, /mcp requires either Authorization: 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 first ALLOWED_HOSTS entry, else http://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:latest

Building 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-mcp

The 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 holding docker-compose.yml, deploy.sh and a .env with API_KEY, API_SECRET, AUTH_TOKEN, ALLOWED_HOSTS and IMAGE_TAG. Both files come from ovm/ in this repo. CI keeps docker-compose.yml current; deploy.sh is the one file to scp over 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, with command="/home/ubuntu/apps/bookeo/deploy.sh" and the usual restrictions. The private half is the OVM_DEPLOY_KEY repo 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:

  1. In Claude, Settings → Connectors → Add custom connector, URL https://ekbookeo.fallday.ca/mcp. Leave the OAuth client fields empty; Claude registers itself.

  2. 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 tools
get_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

ParametersJSON Schema
NameRequiredDescriptionDefault
booking_numberYes

TDQS

A3.8/5.0
Behavior2/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines3/5

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

ParametersJSON Schema
NameRequiredDescriptionDefault
booking_numberYes

TDQS

A4/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines3/5

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

ParametersJSON Schema
NameRequiredDescriptionDefault
days_backNo
customer_nameNo
customer_emailNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A3.5/5.0
Behavior2/5

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.

Conciseness5/5

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.

Completeness3/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines2/5

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

ParametersJSON Schema
NameRequiredDescriptionDefault
end_dateYes
start_dateYes
include_canceledNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

B3.4/5.0
Behavior2/5

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.

Conciseness4/5

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.

Completeness3/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines2/5

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.

  1. 4 tool updatesv1.0.0
    • First observedget_booking
    • First observedget_booking_payments
    • First observedsearch_bookings_by_customer
    • First observedsearch_bookings_by_date

TDQS

A3.7/5.0

Scored across 4 tools

Disambiguation5/5

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.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern: get_booking, get_booking_payments, search_bookings_by_customer, search_bookings_by_date.

Tool Count4/5

4 tools is on the low side but acceptable for a read-only booking query server. However, the scope feels slightly thin.

Completeness2/5

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

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers