---
title: "MCP server"
description: "Connect an AI assistant to the Matchai MCP server to search published listings. Endpoints, transport, limits, and setup for Claude, ChatGPT and other clients."
last_updated: "2026-10-21"
lang: "en"
canonical: "https://matchai.co/docs/developers/mcp-server"
---

# MCP server

> Connect an AI assistant to the Matchai MCP server to search published listings. Endpoints, transport, limits, and setup for Claude, ChatGPT and other clients.

Source: https://matchai.co/docs/developers/mcp-server

The Matchai MCP server lets an AI assistant search the listings that real estate professionals have published on Matchai, open one listing, and look up a broker. MCP, the Model Context Protocol, is the open standard assistants use to call outside tools.

The server is public and read-only. It needs no account, no API key and no sign-in, and it returns published data only.

## Endpoints [#endpoints]

| URL | Use it for |
|---|---|
| `https://matchai.co/mcp` | The default endpoint. Use it for ChatGPT, Claude Code, the MCP Inspector, scripts and every other client. |
| `https://matchai.co/mcp/claude` | The same server, for Claude apps, so that listing cards render inside the conversation. |
| `https://matchai.co/mcp/server-card` | A JSON description of the server for directories and scanners. |

<Callout title="Why there are two connector URLs">
Both URLs run the same server and return the same tools and data. They differ only in one widget setting, and the two assistants disagree about it: ChatGPT wants a dedicated widget domain to be declared, and Claude will not render a widget whose declared domain does not match its own connector URL. Two URLs let each assistant get what it needs.
</Callout>

## Transport [#transport]

The server speaks MCP over Streamable HTTP, in its stateless form, with JSON-RPC 2.0 messages.

- **POST** one JSON-RPC message to the endpoint. A request gets `200` with an `application/json` body. A notification gets `202` with no body.
- **No sessions.** The server issues no `Mcp-Session-Id` and remembers nothing between requests, so any request can be answered on its own. Follow the standard handshake anyway, so your client also works with other servers.
- **No event stream.** `GET` and `DELETE` answer `405`, which is the correct answer for a server with nothing to push. `OPTIONS` answers `204` for CORS.
- **No batches.** Send one message per request. An array is refused with `-32600`.
- **Protocol versions:** `2025-11-25`, `2025-06-18` and `2025-03-26`. `initialize` returns your version when the server speaks it, and the latest otherwise. If you send an `MCP-Protocol-Version` header after `initialize`, it must be one of these, or the request fails with `400`.
- **Size.** A request body can be up to 256 KB.

The server answers these methods:

| Method | Answer |
|---|---|
| `initialize` | Protocol version, capabilities (`tools` and `resources`), `serverInfo` and usage instructions for the model. Requires `params.protocolVersion`. |
| `ping` | An empty result. |
| `tools/list` | The three [public tools](https://matchai.co/docs/developers/mcp-server/public-tools), with input and output schemas. |
| `tools/call` | Runs one tool. |
| `resources/list` | One resource: the listing-card widget. |
| `resources/templates/list` | An empty list. |
| `resources/read` | The listing-card widget document. |
| `notifications/initialized`, `notifications/cancelled` | Accepted with `202`. |

Any other method answers `-32601` (method not found). The server has no prompts, logging or completion features.

`serverInfo.name` is `matchai-listings` and `serverInfo.version` is a semantic version. It changes when a tool or field is added or changes shape.

## Access, data and limits [#access-data-and-limits]

- **Authentication.** None. `/.well-known/mcp.json` declares `authentication.required: false`, and the server asks for no key, token or sign-in.
- **Data.** Published listings, and the public profile of brokers who have published a site, a Matchai Page or a business card. A broker's phone number and email address appear only when they are published on that business card.
- **Order of results.** By relevance to the request and by how recently the listing was updated. No broker can pay to change the order or to be included.
- **Rate limit.** Each calling address can make 600 `tools/call` requests per minute. Above that, the call returns a tool result with `isError: true` and a message asking you to try again in a minute. The limit applies to `tools/call` only. It is an abuse guard, set high enough that a busy assistant host does not reach it.
- **CORS.** Open to every origin, so browser-based tools such as the MCP Inspector work. The server reads no cookie and no credential. Preflight answers are cacheable for 24 hours.
- **Caching.** Responses are sent with `Cache-Control: no-store`.

## Discovery documents [#discovery-documents]

Matchai publishes machine-readable descriptions of the server. All are JSON, send an `ETag`, honour `If-None-Match`, and allow cross-origin reads.

| URL | What it is |
|---|---|
| `https://matchai.co/mcp/server-card` | The MCP server card: name `co.matchai/listings`, version, description, endpoint and supported protocol versions. Also served at `/.well-known/mcp/server-card.json`. |
| `https://matchai.co/.well-known/mcp.json` | A fuller description: both endpoints, `authentication.required: false`, the tools with their annotations, and the widget resource. |
| `https://matchai.co/.well-known/ai-catalog.json` | A domain-level catalogue that points to the server card, the host's OpenAPI document and its `llms.txt`. |
| `https://matchai.co/.well-known/api-catalog` | An RFC 9727 API catalogue listing the public endpoints. |
| `https://matchai.co/openapi.json` | An OpenAPI 3.1 document for the public endpoints. |

Broker sites and Matchai Pages serve their own copies of these documents. They point to the same MCP server on `matchai.co`.

## Connect an assistant [#connect-an-assistant]

<Callout title="Plans and settings">
Whether you can add a custom connector depends on your plan and, in a team workspace, on your administrator. Menu names can change between app versions.
</Callout>

### Claude [#claude]

<Steps>
<Step>

Open Claude and go to **Settings → Connectors**, then choose **Add custom connector**.

</Step>
<Step>

Enter the name `Matchai` and the server URL `https://matchai.co/mcp/claude`.

</Step>
<Step>

Leave any authentication options empty. The server needs no sign-in.

</Step>
<Step>

Save the connector, start a new conversation, and ask for something concrete, such as "Find a 3-bedroom condo for sale in Laval under $600,000". Claude calls `search_listings` and shows the results as listing cards.

</Step>
</Steps>

To add the server to Claude Code from a terminal:

```bash
claude mcp add --transport http matchai https://matchai.co/mcp
```

### ChatGPT [#chatgpt]

<Steps>
<Step>

Open ChatGPT and go to **Settings → Apps & Connectors → Advanced settings**, then turn on developer mode.

</Step>
<Step>

Choose **Create** to add a connector.

</Step>
<Step>

Enter the name `Matchai` and the server URL `https://matchai.co/mcp`, and set authentication to none.

</Step>
<Step>

Save it, enable the connector in a new chat, and ask for listings in your own words.

</Step>
</Steps>

### Other clients [#other-clients]

Most clients that support remote MCP servers accept a URL in a configuration file:

```json title="mcp.json"
{
  "mcpServers": {
    "matchai": {
      "url": "https://matchai.co/mcp"
    }
  }
}
```

Clients that ask for a transport type should use Streamable HTTP, with no authentication.

## Call the server from your own code [#call-the-server-from-your-own-code]

The examples use `curl`. Each request is a single JSON-RPC message.

<Steps>
<Step>

**Initialize.** Send `initialize` with the protocol version you want.

```bash
curl -s https://matchai.co/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "initialize",
    "params": {
      "protocolVersion": "2025-11-25",
      "capabilities": {},
      "clientInfo": { "name": "my-client", "version": "0.1.0" }
    }
  }'
```

An abridged answer:

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "protocolVersion": "2025-11-25",
    "capabilities": { "tools": {}, "resources": {} },
    "serverInfo": {
      "name": "matchai-listings",
      "title": "Matchai",
      "version": "1.0.0",
      "websiteUrl": "https://matchai.co"
    },
    "instructions": "Matchai is a real estate platform where licensed brokers in Québec and across Canada publish their listings. …"
  }
}
```

</Step>
<Step>

**Confirm.** Send the `initialized` notification. The server answers `202` with no body.

```bash
curl -s -i https://matchai.co/mcp \
  -H "Content-Type: application/json" \
  -d '{ "jsonrpc": "2.0", "method": "notifications/initialized" }'
```

</Step>
<Step>

**List the tools.**

```bash
curl -s https://matchai.co/mcp \
  -H "Content-Type: application/json" \
  -d '{ "jsonrpc": "2.0", "id": 2, "method": "tools/list" }'
```

The result has a `tools` array with `search_listings`, `get_listing` and `get_professional`. Each entry carries a `name`, a `title`, a `description`, an `inputSchema`, an `outputSchema` and `annotations` that mark the tool as read-only.

</Step>
<Step>

**Call a tool.**

<Tabs items={['curl', 'JavaScript']}>
<Tab value="curl">

```bash
curl -s https://matchai.co/mcp \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 3,
    "method": "tools/call",
    "params": {
      "name": "search_listings",
      "arguments": { "query": "3 bedroom condo in Laval under 600k", "limit": 3 }
    }
  }'
```

</Tab>
<Tab value="JavaScript">

```js
async function rpc(method, params, id = 1) {
  const response = await fetch('https://matchai.co/mcp', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      Accept: 'application/json, text/event-stream',
    },
    body: JSON.stringify({ jsonrpc: '2.0', id, method, params }),
  });
  return response.json();
}

const { result } = await rpc('tools/call', {
  name: 'search_listings',
  arguments: { query: '3 bedroom condo in Laval under 600k', limit: 3 },
});

if (result.isError) throw new Error(result.content[0].text);
for (const listing of result.structuredContent.listings) {
  console.log(`${listing.title} - ${listing.priceText} - ${listing.url}`);
}
```

</Tab>
</Tabs>

The result holds readable text in `content`, typed data in `structuredContent`, and a `_meta` object for the listing-card widget. See [public tools](https://matchai.co/docs/developers/mcp-server/public-tools) for the shape of each.

</Step>
</Steps>

## Errors [#errors]

Problems with the message itself are JSON-RPC errors. Problems a model can fix, such as a bad argument or a URL that matches no listing, come back as a tool result with `isError: true` and a message the model can read.

| Situation | Response |
|---|---|
| The body is not valid JSON | HTTP `400`, error `-32700` |
| Not a JSON-RPC 2.0 message, a batch, or no method | HTTP `400`, error `-32600` |
| The body is over 256 KB | HTTP `413`, error `-32600` |
| Unsupported `MCP-Protocol-Version` header | HTTP `400`, error `-32600` |
| Unknown method | HTTP `200`, error `-32601` |
| Missing or invalid parameters, or an unknown tool name | HTTP `200`, error `-32602` |
| `resources/read` for a URI the server does not have | HTTP `200`, error `-32002` |
| `GET` or `DELETE` | HTTP `405`, with `Allow: POST, OPTIONS` |
| A tool argument that fails validation | HTTP `200`, result with `isError: true` |
| Nothing matches a `get_listing` or `get_professional` lookup | HTTP `200`, result with `isError: true` |
| Over the rate limit, or a temporary failure | HTTP `200`, result with `isError: true`. Try again shortly. |

<Cards>
  <Card title="Public tools" href="https://matchai.co/docs/developers/mcp-server/public-tools">
    Inputs, outputs and examples for each tool, and the listing-card widget.
  </Card>
  <Card title="Listing search" href="https://matchai.co/docs/developers/listing-search">
    The same search as a plain HTTP endpoint, without MCP.
  </Card>
  <Card title="AI-readable pages" href="https://matchai.co/docs/developers/agent-readable-pages">
    How every Matchai site describes itself to AI assistants.
  </Card>
</Cards>

## Sitemap

- [Every page of Matchai Docs](https://matchai.co/docs/sitemap.md)
- [llms.txt](https://matchai.co/docs/llms.txt)
