Claude Guide · Safely

Connect Safely to Claude

An MCP server so Claude can query your Safely properties, reservations, guest verifications, and damage claims.

10 minute setup Bearer auth 300 req/min
How Safely data flows
1
Property
/properties
2
Reservation
/reservations
3
Verification
/verifications
4
Claim (if needed)
/claims

What you'll be able to ask Claude

"Show me every guest verification that's still pending for arrivals in the next 7 days."
"How many claims have I filed in the last 90 days, grouped by loss type?"
"Which properties don't have coverage set up yet?"
"List all my open claims with status and filing date."
"What reservations came in this week and which ones have verified guests?"

Prerequisites

1

Generate your Safely API key

Safely uses a single Bearer API key issued from your account portal. No OAuth flow, no multi-step setup.

  1. Log in to the Safely portal.
  2. Navigate to the Account page from the main menu.
  3. Find the API key section and copy the key.

Rate limits: Safely enforces 300 API calls per minute per key. Exceeding that will return 429 errors or temporarily disable your key, so don't hammer it in loops. For MCP use with Claude Desktop this is effectively unlimited (each user prompt results in a small number of calls).

2

Set up the Node.js project

Two dependencies: the MCP SDK and zod. Safely has no official Node SDK, so we'll make HTTP calls directly.

Terminal
mkdir safely-mcp
cd safely-mcp
npm init -y
npm install @modelcontextprotocol/sdk zod

Add "type": "module" to package.json:

package.json
{
  "name": "safely-mcp",
  "version": "1.0.0",
  "type": "module",
  "main": "index.js",
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.0.0",
    "zod": "^3.22.0"
  }
}
3

Create the MCP server file

Save as index.js. The server exposes four tools mapped to the four key Safely resources: list-properties, list-reservations, list-verifications, and list-claims.

index.js
#!/usr/bin/env node
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

const API_KEY = process.env.SAFELY_API_KEY;
const BASE_URL = "https://api.safely.com/api/v1";

if (!API_KEY) {
  console.error("Missing SAFELY_API_KEY environment variable");
  process.exit(1);
}

async function safelyGet(path, params = {}) {
  const url = new URL(`${BASE_URL}${path}`);
  for (const [key, value] of Object.entries(params)) {
    if (value !== undefined && value !== null && value !== "") {
      url.searchParams.append(key, String(value));
    }
  }

  const response = await fetch(url.toString(), {
    method: "GET",
    headers: {
      Authorization: `Bearer ${API_KEY}`,
      Accept: "application/json",
      "User-Agent": "rapideye-safely-mcp/1.0",
    },
  });

  if (!response.ok) {
    const body = await response.text();
    throw new Error(`Safely API ${response.status}: ${body}`);
  }

  return response.json();
}

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

// Tool 1: List properties
server.tool(
  "list-properties",
  "List all properties on the Safely account. Returns property ID, name, address, type, and coverage status. Use this as the entry point for any property-level question.",
  {
    page: z.number().optional().describe("Page number (default: 1)"),
    limit: z.number().optional().describe("Results per page (default: 20)"),
  },
  async ({ page, limit }) => {
    const data = await safelyGet("/properties/", { page, limit });
    return {
      content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
    };
  }
);

// Tool 2: List reservations
server.tool(
  "list-reservations",
  "List reservations tracked in Safely. Returns reservation details including guest, dates, property, and associated verification status.",
  {
    page: z.number().optional(),
    limit: z.number().optional(),
    property_id: z
      .string()
      .optional()
      .describe("Optional property ID filter"),
  },
  async ({ page, limit, property_id }) => {
    const data = await safelyGet("/reservations/", {
      page,
      limit,
      property_id,
    });
    return {
      content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
    };
  }
);

// Tool 3: List verifications
server.tool(
  "list-verifications",
  "List guest verifications. Each verification ties a guest to a reservation and has a status (pending, passed, failed, etc). Use this to surface guests who haven't been verified yet or whose verification flagged an issue.",
  {
    page: z.number().optional(),
    limit: z.number().optional(),
  },
  async ({ page, limit }) => {
    const data = await safelyGet("/verifications/", { page, limit });
    return {
      content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
    };
  }
);

// Tool 4: List claims
server.tool(
  "list-claims",
  "List damage claims filed through Safely. Returns claim ID, status, loss type, filing date, and associated reservation.",
  {
    page: z.number().optional(),
    limit: z.number().optional(),
  },
  async ({ page, limit }) => {
    const data = await safelyGet("/claims/", { page, limit });
    return {
      content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
    };
  }
);

const transport = new StdioServerTransport();
await server.connect(transport);

Safely's API also exposes lookup endpoints (/claims/losstypes/, /reservations/types/, /properties/types/, /countries/, /currencies/) and per-resource detail endpoints (/properties/{id}/, /claims/{id}/, etc.). Add more tools by copying the existing pattern and pointing at those paths.

4

Register the server with Claude Desktop

Open your Claude Desktop config file:

macOS path
~/Library/Application Support/Claude/claude_desktop_config.json
Windows path
%APPDATA%\Claude\claude_desktop_config.json

Add the Safely entry (merge into your existing mcpServers block if present):

claude_desktop_config.json
{
  "mcpServers": {
    "safely": {
      "command": "node",
      "args": ["/absolute/path/to/safely-mcp/index.js"],
      "env": {
        "SAFELY_API_KEY": "your_safely_api_key_here"
      }
    }
  }
}

Replace the path and API key with your real values. Then fully quit and reopen Claude Desktop.

Try it

"List my Safely properties, then show me how many open claims I have, grouped by loss type."

"Which verifications are still pending right now?"

Troubleshooting

401 Unauthorized

Your Bearer token is wrong or expired. Generate a fresh one from the Safely portal Account page.

Also verify the header format in the code is exactly Authorization: Bearer <key> with a space between "Bearer" and the key. A missing space or wrong casing will 401.

429 Too Many Requests

Safely enforces 300 API calls per minute per key. If you're hitting this from normal Claude usage, something is wrong (probably Claude is in a loop). Check the Claude Desktop conversation for repeated identical tool calls.

If you're extending the server with batch operations that legitimately need more throughput, batch your requests and stay under the limit.

Claude says it doesn't see any Safely tools

Claude Desktop only reads the config file at startup. Fully quit Claude (Cmd+Q on macOS, right-click tray icon → Quit on Windows) and reopen it. Closing the window doesn't reload the config.

Still broken? Check Help → Open Logs Folder. Look for JSON parse errors in the config, a wrong absolute path to index.js, or node not in PATH.

Empty results on a resource I know has data

Safely paginates. The default page size is small. Try passing limit: 100 in your prompt ("list my properties with a limit of 100") or check the next pagination link in the response.

I want to add write operations (create claims, file verifications)

Safely's API supports write operations. Add tools using method: "POST" and a JSON body. Consult developer.safely.com for the exact request schemas.

We'd recommend being deliberate about write access. Filing a claim through Claude is possible but probably not something you want to enable casually.

Other guides