Confused? Book a call with Sam
MCP Guide

Connect Guesty to Claude

Run a small local server so Claude can read your Guesty listings, reservations, calendar, and guests, and answer questions about them in plain English. Free, working code, about twenty minutes.

~20 minutes Read-only by default Updated June 2026
Hand it to Claude Code

Can you connect Guesty to Claude? Yes. You run a small MCP server: a lightweight program on your own machine that wraps the Guesty Open API and exposes it to Claude as a set of tools. Once it's connected, Claude can read your listings, reservations, calendar, and guests and answer questions about them, with no dashboards or exports.

MCP (the Model Context Protocol) is an open standard from Anthropic that lets AI assistants talk to outside systems. Guesty has no first-party MCP server as of June 2026, so this guide builds a focused, read-only one. Your Open API credentials are self-serve: you create them yourself under Integrations.

What you can ask once it's connected

Before you start

Three things: a Guesty account with Open API access (you create OAuth credentials under Integrations), Node.js 18 or newer (20+ recommended; check with node --version), and either Claude Desktop or Claude Code.

1

Create Open API credentials

In Guesty, open Integrations → OAuth applications and click New application. Give it a name and description and save. Guesty shows you a client ID and client secret. You can create up to five applications.

Copy the client secret right away. According to Guesty's Open API documentation, these credentials use the OAuth 2.0 client-credentials flow. If you don't see OAuth applications under Integrations, Open API access is a plan-level feature, so check with Guesty to enable it.
2

Scaffold the project

Make a folder and install the two dependencies, the official MCP SDK and zod (used to describe each tool's inputs). No build step, no TypeScript compiler.

terminal
mkdir guesty-mcp && cd guesty-mcp
npm init -y
npm pkg set type="module"
npm install @modelcontextprotocol/sdk zod
3

Write the server

Save this as index.js. Guesty's token lives for 24 hours and you are allowed only five tokens per day, so the server caches the token to a small file and reuses it across restarts instead of minting a new one each time. It exposes six read-only tools.

index.js
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { readFileSync, writeFileSync } from "fs";
import { join } from "path";
import { tmpdir } from "os";

const API_BASE = "https://open-api.guesty.com/v1";
const TOKEN_URL = "https://open-api.guesty.com/oauth2/token";
const CLIENT_ID = process.env.GUESTY_CLIENT_ID;
const CLIENT_SECRET = process.env.GUESTY_CLIENT_SECRET;
const CACHE = join(tmpdir(), "guesty-mcp-token.json");

if (!CLIENT_ID || !CLIENT_SECRET) {
  console.error("Set GUESTY_CLIENT_ID and GUESTY_CLIENT_SECRET first.");
  process.exit(1);
}

// Guesty allows only 5 tokens per client per 24h, and each lasts 24h.
// Cache the token to disk so restarts reuse it instead of minting a new one.
let token = null;
let tokenExp = 0;

async function getToken() {
  const now = Date.now();
  if (token && now < tokenExp) return token;
  try {
    const c = JSON.parse(readFileSync(CACHE, "utf8"));
    if (c.access_token && now < c.expires_at) {
      token = c.access_token; tokenExp = c.expires_at; return token;
    }
  } catch {}
  const res = await fetch(TOKEN_URL, {
    method: "POST",
    headers: { "Content-Type": "application/json", Accept: "application/json" },
    body: JSON.stringify({ clientId: CLIENT_ID, clientSecret: CLIENT_SECRET }),
  });
  if (!res.ok) throw new Error(`Token request failed (${res.status})`);
  const data = await res.json();
  token = data.access_token;
  // Refresh ~1 hour before the 24h expiry, per Guesty's guidance.
  tokenExp = now + (data.expires_in - 3600) * 1000;
  try { writeFileSync(CACHE, JSON.stringify({ access_token: token, expires_at: tokenExp })); } catch {}
  return token;
}

async function get(path) {
  const res = await fetch(`${API_BASE}${path}`, {
    headers: { Authorization: `Bearer ${await getToken()}`, Accept: "application/json" },
  });
  if (!res.ok) throw new Error(`Guesty ${res.status}: ${await res.text()}`);
  return res.json();
}

function json(data) {
  return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
}

const server = new McpServer({ name: "guesty", version: "1.0.0" });

server.registerTool(
  "list_listings",
  {
    description: "List the listings (properties) in the Guesty account.",
    inputSchema: {
      limit: z.number().optional().describe("Max rows, 1-100, default 100"),
      skip: z.number().optional().describe("Rows to skip, for paging"),
    },
  },
  async ({ limit, skip }) => {
    const q = new URLSearchParams({ limit: String(limit ?? 100) });
    if (skip) q.set("skip", String(skip));
    return json(await get(`/listings?${q}`));
  }
);

server.registerTool(
  "get_listing",
  {
    description: "Get full details for one listing by its id.",
    inputSchema: { id: z.string().describe("Guesty listing id") },
  },
  async ({ id }) => json(await get(`/listings/${id}`))
);

server.registerTool(
  "list_reservations",
  {
    description: "List reservations. Use skip to page through results.",
    inputSchema: {
      limit: z.number().optional().describe("Max rows, 1-100, default 100"),
      skip: z.number().optional().describe("Rows to skip, for paging"),
    },
  },
  async ({ limit, skip }) => {
    const q = new URLSearchParams({ limit: String(limit ?? 100) });
    if (skip) q.set("skip", String(skip));
    return json(await get(`/reservations?${q}`));
  }
);

server.registerTool(
  "get_reservation",
  {
    description: "Get one reservation by its id, including guest, dates, and status.",
    inputSchema: { id: z.string().describe("Guesty reservation id") },
  },
  async ({ id }) => json(await get(`/reservations/${id}`))
);

server.registerTool(
  "get_calendar",
  {
    description: "Get availability and nightly pricing for one or more listings over a date range.",
    inputSchema: {
      listingIds: z.string().describe("One listing id, or several comma-separated"),
      startDate: z.string().describe("First day, YYYY-MM-DD"),
      endDate: z.string().describe("Last day, YYYY-MM-DD"),
    },
  },
  async ({ listingIds, startDate, endDate }) => {
    const q = new URLSearchParams({ listingIds, startDate, endDate });
    return json(await get(`/availability-pricing/api/calendar/listings?${q}`));
  }
);

server.registerTool(
  "get_guest",
  {
    description: "Get one guest by id (find the guestId on a reservation).",
    inputSchema: { guestId: z.string().describe("Guesty guest id") },
  },
  async ({ guestId }) => json(await get(`/guests-crud/${guestId}`))
);

const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Guesty MCP server running on stdio");
Large portfolio? The list tools cap at limit 100 (the Guesty maximum) and page with skip. If you run 100+ units and ask Claude to total a full month, tell it to page through with skip or narrow the question, otherwise it only sees the first 100 rows.
4

Connect it to Claude

Point Claude at the server and pass your two credentials.

Claude Desktop

Add a guesty entry to your config file (~/Library/Application Support/Claude/claude_desktop_config.json on macOS, %AppData%\Claude\claude_desktop_config.json on Windows), then fully quit and reopen the app.

claude_desktop_config.json
{
  "mcpServers": {
    "guesty": {
      "command": "node",
      "args": ["/ABSOLUTE/PATH/TO/guesty-mcp/index.js"],
      "env": {
        "GUESTY_CLIENT_ID": "your-client-id",
        "GUESTY_CLIENT_SECRET": "your-client-secret"
      }
    }
  }
}
Claude Code

One command from your terminal. Everything after -- is what launches the server.

terminal
claude mcp add \
  --env GUESTY_CLIENT_ID=your-client-id \
  --env GUESTY_CLIENT_SECRET=your-client-secret \
  --transport stdio guesty \
  -- node /ABSOLUTE/PATH/TO/guesty-mcp/index.js

Confirm with claude mcp list. Add --scope user to make it available in every project.

5

Ask away

Start a new conversation. Claude asks permission the first time it calls a tool, approve it, and you're set.

try these
Which reservations check in over the next 7 days? Group them by listing.

How many active listings do we have, and which are inactive?

Get the calendar for this listing id for the next 14 days and flag open nights.

Prefer to skip the typing?

Paste this into Claude Code (or any coding agent) and it will build, install, and wire up the server for you. Then come back to step 1 for your credentials.

prompt for Claude Code
Build a local MCP server (Node.js, ESM, @modelcontextprotocol/sdk and zod)
that wraps the Guesty Open API (base https://open-api.guesty.com/v1). Read
GUESTY_CLIENT_ID and GUESTY_CLIENT_SECRET from the environment. Get an access
token by POSTing JSON {clientId, clientSecret} to
https://open-api.guesty.com/oauth2/token; the token lasts 24h and Guesty only
allows 5 tokens per day, so CACHE the token to a file in tmpdir and reuse it
until ~1h before expiry. Add an authenticated GET helper that returns the
parsed JSON body. Register these read-only tools, default limit 100, built
with URLSearchParams: list_listings(limit?, skip?), get_listing(id),
list_reservations(limit?, skip?), get_reservation(id),
get_calendar(listingIds, startDate, endDate) ->
/availability-pricing/api/calendar/listings, get_guest(guestId) ->
/guests-crud/{guestId}. Use StdioServerTransport; log startup to stderr. Then
make a package.json (type: module), install the deps, and show me the
claude mcp add command, the claude_desktop_config.json block, and three
prompts to try.

Good to know

One Guesty connection worth its own server: your turnover photos. This MCP server gives you conversational access to reservations and listings. RapidEye covers the other half of the operation, the turnover itself, reading the inspection photos your cleaners capture after each clean and flagging damage and missed cleaning before the next guest checks in.

FAQ

Can you connect Guesty to Claude?

Yes. Guesty has no first-party MCP server as of June 2026, but its Open API is self-serve, so you can build a small read-only server yourself (this guide) in about 20 minutes. A community server on npm and Zapier's no-code bridge are alternatives.

Does Guesty have an official MCP server?

Not a first-party one. There is a community MCP server on npm and Zapier offers a no-code bridge. Building your own, as here, keeps it read-only and under your control.

Why does the server cache its token?

Guesty allows only five access tokens per client ID per 24 hours, and each lasts 24 hours. Caching the token to a file lets the server reuse it across restarts instead of burning through the daily limit.

Can I do this with Hostaway, Hostfully, or OwnerRez too?

Yes. The structure is identical for any PMS with a public REST API; only the auth step changes. See our Hostaway, Hostfully, and OwnerRez guides for worked examples.

Sources

Independent integration guide. RapidEye is not affiliated with or endorsed by Guesty or Anthropic. API behavior and dashboard menus can change; confirm specifics against the official docs above. Last reviewed June 14, 2026.