# MCP Server

Connect an AI assistant to your Rentalot account with the official [MCP server](https://github.com/Rentalot-ai/rentalot-mcp). The current package registers tools for properties, contacts, showings, conversations, workflows, webhooks, and other API resources. Tool registration is not a promise of complete cross-client parity. The [tool coverage table](#tool-coverage) below and the [API reference](/docs/api-reference) describe current boundaries.

## Before you connect

1. Create an account, verify your email, and sign in at the same Rentalot server origin you will configure for the client.
2. Open **Settings > API Keys** at `<RENTALOT_BASE_URL>/dash/settings?tab=api-keys`, create a key, and save the raw value immediately. It is shown only once. Production Free Trial and paid accounts use `https://rentalot.ai/dash/settings?tab=api-keys`; an optional local Free Trial can use `http://localhost:3000/dash/settings?tab=api-keys` while `RENTALOT_BASE_URL=http://localhost:3000`.
3. Store the key in an environment variable or a file with mode `600`. Never commit it or paste it into a prompt.
4. Set `RENTALOT_BASE_URL` to the **server origin**, not the API path. Use `https://rentalot.ai` for paid or Free Trial accounts. For optional local testing, use a development origin such as `http://localhost:3000`. Do not set the client base URL to `https://rentalot.ai/api/v1`, because the MCP client adds `/api/v1` itself.

## Setup

### Claude Code

```bash
claude mcp add rentalot \
  -e RENTALOT_API_KEY=ra_your_key \
  -e RENTALOT_BASE_URL=https://rentalot.ai \
  -- npx -y @rentalot/mcp-server
```

For a local development server, replace the base URL with `http://localhost:3000`. Keep the API key and local server in the same account and environment.

### Codex

```bash
codex mcp add rentalot \
  --env RENTALOT_API_KEY=ra_your_key \
  --env RENTALOT_BASE_URL=https://rentalot.ai \
  -- npx -y @rentalot/mcp-server
```

### Claude Desktop, Cursor, Windsurf, or another JSON-configured client

The server uses this shape. The exact config file location depends on the client.

```json
{
  "mcpServers": {
    "rentalot": {
      "command": "npx",
      "args": ["-y", "@rentalot/mcp-server"],
      "env": {
        "RENTALOT_API_KEY": "ra_your_key",
        "RENTALOT_BASE_URL": "https://rentalot.ai"
      }
    }
  }
}
```

Common locations:

| Client | Config file |
|--------|-------------|
| Claude Desktop | `~/Library/Application Support/Claude/claude_desktop_config.json` on macOS |
| Cursor | `.cursor/mcp.json` in a project or `~/.cursor/mcp.json` globally |
| Windsurf | `~/.codeium/windsurf/mcp_config.json` |

### OpenCode

Add the server to `~/.config/opencode/opencode.json`:

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "rentalot": {
      "type": "local",
      "command": ["npx", "-y", "@rentalot/mcp-server"],
      "environment": {
        "RENTALOT_API_KEY": "ra_your_key",
        "RENTALOT_BASE_URL": "https://rentalot.ai"
      },
      "enabled": true
    }
  }
}
```

## Authentication and config resolution

The MCP server accepts either environment variables or `~/.config/rentalot/config.yaml`:

```yaml
api_key: ra_your_key
base_url: https://rentalot.ai
```

Environment variables take priority over the file:

1. `RENTALOT_API_KEY`, then `api_key`
2. `RENTALOT_BASE_URL`, then `base_url`, then `https://rentalot.ai`

The client sends `Authorization: Bearer <key>` to the v1 API. A missing, malformed, expired, or revoked key returns `401 Unauthorized`. Revoke keys from **Settings > API Keys** at `<RENTALOT_BASE_URL>/dash/settings?tab=api-keys`, using the same configured origin as the MCP client.

## Current tier and surface boundaries

| Account tier | MCP access |
|---|---|
| **Free Trial** | Private, image-free property and contact CRUD while the account trial is active. Account-wide request and write limits apply in production or on an optional local server. |
| **Starter** | Read-only API authority. List/get tools can read owned records, but writes are not included. |
| **Pro** | Paid full API authority, subject to endpoint limits and ownership checks. |
| **Scale** | Paid full API authority with higher and priority API limits. |

The MCP process is a stdio client of the REST API. It does not create Rentalot accounts or API keys, and its tools do not inherit the management chat confirmation UI. API-key write calls are authorized directly by the v1 API. Destructive operations such as property or contact deletion are soft-delete operations in the API, even where a client description is stale.

## Verified first CRM job

This is the smallest useful cross-surface check. It creates private records, reads them back, updates safe fields, verifies the same IDs in the web CRM, and then revokes the key. It does not publish a listing, upload images, send messages, or run a workflow.

Use the MCP tools below with a Pro/Scale key, or with a Free Trial key against production while the trial deadline is valid. An optional local server is also supported when the same origin is used for sign-in, key issuance, and API requests. Starter can perform the reads but not the writes.

### 1. Create a private property

Use the canonical required fields. The example intentionally omits `title`, `status`, and the development-only `propertyType` field so it stays on the safe first-job contract:

```text
create_property({
  address: "123 Example Street",
  city: "Austin",
  state: "TX",
  zip: "78701",
  monthlyRent: 1800,
  bedrooms: 0,
  bathrooms: 1,
  features: ["hardwood floors"],
  isPublic: false
})
```

Save the returned `data.id` as `PROPERTY_ID`.

### 2. Create a contact

A single API contact create requires a name and at least one of `email` or `phone`. Include one even though the MCP input schema currently treats both fields as optional:

```text
create_contact({
  name: "Ada Lovelace",
  email: "ada@example.com",
  role: "prospect",
  source: "mcp"
})
```

Save the returned `data.id` as `CONTACT_ID`.

### 3. Read and update without drifted fields

```text
get_property({ propertyId: PROPERTY_ID })
get_contact({ contactId: CONTACT_ID })

update_property({ propertyId: PROPERTY_ID, monthlyRent: 1900 })
update_contact({ contactId: CONTACT_ID, channelPreference: "email" })
```

The update examples use canonical safe fields. Do not substitute the current drifted MCP title or status variants in this walkthrough. Retain both IDs and confirm the updated values in the responses.

### 4. Verify the same records in the web CRM

While signed in to the same Rentalot account at the same configured origin, open:

- `<RENTALOT_BASE_URL>/dash/properties/PROPERTY_ID`
- `<RENTALOT_BASE_URL>/dash/contacts/CONTACT_ID`

For a local Free Trial, these resolve to `http://localhost:3000/dash/properties/PROPERTY_ID` and `http://localhost:3000/dash/contacts/CONTACT_ID` when `RENTALOT_BASE_URL=http://localhost:3000`. For paid accounts, use the configured paid origin, normally `https://rentalot.ai`. The private property remains account-owned and does not need a public listing URL. Confirm that the dashboard shows the same IDs, address, rent, contact name, and contact method.

### 5. Revoke the key

Return to **Settings > API Keys** at `<RENTALOT_BASE_URL>/dash/settings?tab=api-keys`, using the same configured origin, and revoke the key. A subsequent request through the MCP client should fail with `401 Unauthorized`. If the key is expired or revoked, do not keep retrying it. Issue a replacement key only while the existing account trial and its deadline remain eligible. A replacement key does not reset or extend the account's request/write budgets or trial deadline. If trial eligibility has ended, upgrade to a paid tier before issuing a new paid key.

## Tool coverage

The package currently registers 65 tools across the following groups. Read access and write access still follow the tier table and the API's account-ownership checks.

| Resource | Registered operations | Current note |
|----------|-----------------------|--------------|
| Properties | list, get, create, update, delete | Canonical create requires address, monthlyRent, bedrooms, and bathrooms. Delete is soft-delete in the API. |
| Contacts | list, get, create, update, delete | Include email or phone for create. API uses `role`, not the CLI's legacy `type`. |
| Showings | list, get, create, update, delete, check availability | Paid surface; current delete wording needs to be read as cancel/soft behavior where the API says so. |
| Events | list | Read-only v1 event access. |
| Conversations | list, get, search, list messages | Conversation reads are separate from paid outbound messaging. |
| Messages | send | Paid outbound write with provider and integration prerequisites. |
| Drafts and follow-ups | list, get, create, update, send/delete as registered | Tier, integration, and confirmation boundaries apply. |
| Workflows | list, get, create, update, delete, trigger run, list/get run | Run pause/resume/cancel parity is not claimed. |
| Webhooks and settings | registered CRUD/read operations | Pro/Scale or paid settings boundaries apply. |
| Property images, sessions, and bulk import | registered operations | Not part of the Free Trial first job; image and bulk operations require their paid surface. |

Before automating writes, test each operation against a private record and check the response. For endpoint schemas, required fields, and response details, see the [API reference](/docs/api-reference).

## Agent Skill

Install the bundled [Agent Skill](https://agentskills.io) so your coding agent knows the current tool names and common workflows:

```bash
npx skills add ariel-frischer/rentalot-mcp
```

See the [GitHub README](https://github.com/Rentalot-ai/rentalot-mcp) for manual installation instructions and the package's detailed API skill.

The application acceptance harness validates route and service behavior with a free mocked database and provider transports. It does not claim installed MCP stdio transport execution or live provider validation.
