Skip to main content
Glama
ronaldoribeirosm

cloudbeds-mcp-server

cloudbeds-mcp-server

CI Linguagem Protocolo License

Servidor MCP que expõe ocupação, check-ins/check-outs e reservas do Cloudbeds como tools de linguagem natural, sem nenhum caminho de escrita na v1 — por decisão de design, não por limitação técnica.

A coisa em ação

Sem UI — é um servidor MCP (stdio). A imagem abaixo é a suíte de testes rodando de verdade; o restante das capturas reais (setup rejeitando uma key inválida contra a API ao vivo do Cloudbeds, tools nos dois estados do servidor) está na seção Prints.

31 testes passando e ruff limpo

Related MCP server: beds24-mcp

Repositório:  https://github.com/ronaldoribeirosm/cloudbeds-mcp-server
Demo:         sem demo pública — cada instalação precisa da API key e do
              Property ID do Cloudbeds de um hotel real; não dá pra expor
              isso num ambiente compartilhado sem vazar dado de hóspede
Docs/Swagger: não se aplica — não é uma API HTTP. A superfície de tools
              (nomes, parâmetros, descrições) está documentada na tabela
              abaixo e é a mesma coisa que o cliente MCP lista via
              `list_tools()`
Vídeo:        [ opcional — não gravado ]

O problema

Consultar ocupação, check-ins do dia ou o status de uma reserva no Cloudbeds hoje significa abrir o painel, navegar até a tela certa e ler manualmente — ou interromper quem está na recepção pra perguntar. Isso já tinha sido resolvido uma vez no automacao-recepcao, só que atrás de uma UI Streamlit: resolve pra quem abre aquela tela, não pra quem só quer perguntar "quantos check-ins tenho hoje?" de dentro do assistente de IA que já está usando.

A solução ingênua seria integrar a API do Cloudbeds direto em cada assistente de IA que alguém usa — um conector pro Cursor, outro pra qualquer coisa que apareça depois. Isso não escala: N ferramentas de IA × M integrações de dado vira N×M código de cola. MCP inverte essa conta — escreve-se um servidor, e qualquer cliente compatível com o protocolo consegue chamá-lo.

sequenceDiagram
    participant Agente as Agente de IA
    participant MCP as cloudbeds-mcp-server
    participant CB as API Cloudbeds

    Agente->>MCP: "quantos check-ins tenho hoje?"
    MCP->>CB: GET getReservations (checkInFrom=checkInTo=hoje)
    CB-->>MCP: lista de reservas
    MCP-->>Agente: texto formatado, pronto pra responder

A solução (visão geral)

  • Camada de tools (src/cloudbeds_mcp/tools/) separa lógica pura (compute_*, format_*, sem I/O) da tool MCP em si (register_*_tools) — a mesma separação que o automacao-recepcao já fazia entre regra de negócio e interface Streamlit, só que aqui o "front-end" é o protocolo MCP.

  • Camada de auth (src/cloudbeds_mcp/auth.py) valida a API key com uma chamada real ao Cloudbeds antes de gravar qualquer coisa, criptografa em disco, e nunca devolve a key pro modelo de IA depois de configurada.

  • Cliente HTTP (src/cloudbeds_mcp/cloudbeds_client.py) reaproveita a lógica de retry/paginação e as descobertas de comportamento real da API já validadas no projeto irmão — sem herdar nenhum dos métodos de escrita.

  • Detalhes completos na seção Decisões técnicas abaixo.

Prints — o projeto rodando

Sem uma conta Cloudbeds real disponível, três coisas foram validadas de verdade, capturadas em screenshot (não é texto inventado — é a saída real dos comandos):

1. Suíte de testes e lint:

pytest e ruff rodando

2. --setup com uma API key inválida — bateu na API real do Cloudbeds e voltou um 401 de verdade:

setup rejeitando key inválida contra a API real

Isso confirma que o endpoint de validação (getHotels) existe e se comporta como assumido — o que faltava validar era só o caminho de sucesso (key válida), que segue sem teste ao vivo.

3. As tools nos dois estados do servidor (antes e depois do setup), capturado chamando list_tools() de verdade, mais a saída real de format_occupancy_snapshot()/format_movements() contra os dados de tests/test_occupancy.py:

tools expostas antes e depois do setup

Stack

Camada

Tecnologia

Protocolo

MCP (mcp SDK oficial, MCPServer, transporte stdio)

HTTP

requests, com retry/backoff e paginação própria

Criptografia da credencial

cryptography (Fernet), chave e payload fora do repositório

Testes

pytest + responses (mock de HTTP)

Lint

ruff

CI

GitHub Actions

Como rodar localmente

git clone https://github.com/ronaldoribeirosm/cloudbeds-mcp-server
cd cloudbeds-mcp-server
pip install -e ".[dev]"
cloudbeds-mcp-server --setup

O --setup pede a API key e o Property ID do Cloudbeds (Configurações > API no painel), valida com uma chamada real e salva criptografado em ~/.cloudbeds-mcp/credentials.enc. Depois disso, aponte um cliente MCP pro binário cloudbeds-mcp-server (exemplo de config em mcp_client_config.example.json).

Rodar os testes:

pytest -v
ruff check .

Decisões técnicas

Por que nenhuma tool de escrita existe na v1

O dilema: dava pra implementar putGuest/postReservationNote reaproveitando o cliente do automacao-recepcao — a API já suporta. A escolha foi não implementar nenhum método de escrita no cliente desta v1 — não é uma flag de segurança que dá pra contornar, é a própria classe CloudbedsClient deste projeto que não tem o método. Um agente de IA com acesso só-leitura pode, na pior das hipóteses, vazar um dado que já tinha permissão de ver; nunca alterar uma reserva ou um preço sozinho sem confirmação humana. Escrita fica pra v2, atrás de um design de confirmação explícita que ainda não existe.

Por que a API key nunca "aparece" de volta

O dilema: o jeito mais simples de guardar uma credencial de API é variável de ambiente ou arquivo de texto plano — mas isso é publicável, e qualquer pessoa que rode este servidor está potencialmente compartilhando a key com o histórico de conversa de um agente de IA. A escolha: configure_cloudbeds_api_key recebe a key só pra validar (uma chamada real de getHotels) e gravar; a função devolve só o nome do hotel conectado, nunca a key — nem em sucesso, nem no texto de erro de falha. O arquivo fica criptografado com Fernet fora da pasta do projeto (~/.cloudbeds-mcp/), com permissão restrita ao dono. Isso está coberto por teste: test_credentials_file_is_encrypted_on_disk falha se o valor da key aparecer em texto puro no arquivo salvo em disco.

Por que a busca por nome varre reservas em vez de filtrar no servidor

Descoberta herdada do automacao-recepcao (confirmada contra a API real do Cloudbeds em 2026-07-22, não retestada aqui por falta de credencial): getReservations ignora filtro por nome, e getGuestList só filtra de verdade no servidor por e-mail ou telefone exato — nunca por nome parcial. A alternativa seria buscar todas as páginas de reservas da propriedade inteira, o que não escala pra contas com anos de histórico. A solução adotada limita a varredura a uma janela de 12 meses passados + 3 meses futuros e filtra por substring em memória — resolve o caso de uso real ("achar o hóspede que acabou de ligar") sem paginar a conta inteira, ao custo de não achar hóspedes fora dessa janela (documentado como limitação conhecida, não escondido).

Por que get_occupancy_today detalha reserva por reserva

getReservations (lista) não traz quarto nem datas por quarto — só getReservation (individual) traz o array assigned, que é o que permite saber se um quarto específico está ocupado hoje. Isso também foi uma descoberta real do projeto irmão, não uma suposição de documentação. Por isso a tool primeiro filtra candidatos "baratos" pela lista (reservas não canceladas cujo check-out é depois de hoje) e só depois detalha cada uma — evita bater getReservation pra cada reserva histórica da propriedade.

Testes — o que é coberto e por quê

Arquivo

Cobre

Por quê

test_auth.py

Setup, validação, criptografia, roundtrip e limpeza de credenciais

É a parte mais sensível do projeto — precisa provar que a key nunca fica em texto puro em disco e que falha de validação não grava nada

test_cloudbeds_client.py

Paginação, erro HTTP, erro de nível de API, achatamento de getRooms, extração de hóspede principal

Cobre os comportamentos reais da API (paginação por total, não por count) com HTTP mockado via responses, sem depender de rede

test_occupancy.py

Cálculo de ocupação (incluindo o limite check-in ≤ dia < check-out), check-ins/check-outs do dia, formatação de texto

Lógica pura de negócio — testável sem tocar o Cloudbeds

test_guests.py

Busca por substring case-insensitive, ordenação por mais recente, truncamento de lista longa

Garante que a limitação conhecida (busca por nome em memória) pelo menos se comporta de forma previsível

test_reservations.py

Formatação de status de reserva com e sem quarto atribuído

Cobre o caminho feliz e o caso "reserva sem quarto ainda"

Resultado real: 31/31 testes passando. ruff check . limpo.

O que eu faria diferente

  • Só o caminho de erro foi testado contra a API real — não tenho uma key válida disponível agora. Rodar --setup com uma key inválida confirmou que getHotels é um endpoint real e rejeita corretamente (401), mas o formato exato da resposta de sucesso (o propertyName que validate_and_save espera encontrar em hotels[0]) segue assumido a partir do guia original, não confirmado ao vivo. Primeira coisa a fazer antes de publicar: rodar --setup contra um hotel de teste de verdade e corrigir o que a API real devolver diferente do assumido.

  • get_guest_by_name escala mal em contas com muito histórico — varrer 15 meses de reservas por substring funciona pra um hotel pequeno/médio, mas numa conta com dezenas de milhares de reservas isso fica lento. Combinar com getGuestList quando o usuário já tiver e-mail ou telefone (mais rápido, filtro real no servidor) resolveria a maioria dos casos.

  • Sem cache — cada chamada de tool bate a API do zero, mesmo pra dados que não mudam a cada segundo (lista de quartos, por exemplo). Um cache curto (TTL de alguns minutos) em get_rooms reduziria bastante chamada repetida.

  • v2 do guia original não entrou nesta v1get_occupancy_forecast, get_guest_stay_history, search_available_rooms e o resource daily_report ficaram de fora por escopo, não por dificuldade técnica; a base de cliente/auth já suporta.

Licença

MIT.

Available Tools

1 tool
configure_cloudbeds_api_keyA

Configura a conexão com o Cloudbeds na primeira execução.

Recebe a API key e o Property ID (Configurações > API no painel do Cloudbeds), valida os dois com uma chamada real à API e, se forem válidos, salva de forma criptografada em ~/.cloudbeds-mcp/credentials.enc. A key NUNCA é devolvida nesta resposta nem em nenhuma resposta futura — só a confirmação de que deu certo (nome do hotel conectado). Depois de configurar, reinicie o servidor MCP para ativar as tools de ocupação, hóspedes e reservas.

ParametersJSON Schema
NameRequiredDescriptionDefault
api_keyYes
property_idYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description fully carries the transparency burden. It discloses that a real API call is made for validation, credentials are saved encrypted to a specific file path, the API key is never returned in this or future responses, and a restart is required. This is exemplary disclosure of side effects and security behavior.

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?

The description is well-structured in two short paragraphs, and every sentence adds value: purpose, input location, validation process, security guarantee, and next steps. It is slightly longer than strictly needed but remains efficient and readable.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description covers the full context: when to use, what inputs are needed and where to find them, what happens during validation, where credentials are stored, the security guarantee about the key, and the necessary post-step (restart). The output schema exists and the description mentions the confirmation response, so nothing is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema has zero descriptions for its two parameters, but the description compensates completely by explaining both: the API key and Property ID, including where to find the Property ID in the Cloudbeds panel. It also implies both are required and that they are validated together.

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 tool configures the Cloudbeds connection on first run, with a specific verb and resource. It distinguishes this from a generic API call by detailing the validation, encryption, and restart steps, making the purpose unmistakable.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly says 'na primeira execução' (on first run) and instructs to restart the MCP server afterward to activate dependent tools. This provides clear when-to-use context, though it doesn't mention explicit alternatives because no siblings exist.

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. 1 tool updatev0.1.0
    • First observedconfigure_cloudbeds_api_key

TDQS

A4.3/5.0

Scored across 1 tool

Disambiguation5/5

Only one tool exists, so there is no possibility of ambiguity between tools. The tool's purpose is clearly distinct from any potential future tools.

Naming Consistency5/5

The single tool name follows a clear verb_noun pattern (configure_cloudbeds_api_key) and is descriptive. With only one tool, consistency is trivially maintained.

Tool Count2/5

The server has 1 tool, which is too few for its apparent scope of managing Cloudbeds data. The description explicitly mentions future tools for occupancy, guests, and reservations, but those are not present.

Completeness1/5

The tool surface is severely incomplete. Only API key configuration is provided, with no operational tools for reservations, guests, or occupancy, making the server non-functional for its intended domain.

Maintenance

ActivitySlowing
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    B
    quality
    B
    maintenance
    A read-only hospitality-focused MCP server that enables users to retrieve reservation details, listing briefs, and guest conversation contexts from Hostaway. It simplifies hospitality workflows by providing specialized tools for searching threads and viewing reservation data through natural language interfaces.
    6
    26 npm
    MIT
  • F
    license
    A
    quality
    B
    maintenance
    A read-only MCP server that lets AI assistants query the WebHotelier REST API for live hotel data, including property info, room availability, rates, calendars, and offers via natural language tools.
    8
    -