# MCP server

> Connect an AI agent to ContractorsNearMe's contractor directory over the Model Context Protocol.

Source: https://docs.buildx.pro/docs/agents/mcp-server/

ContractorsNearMe runs a remote MCP server. Through it, an agent can search the contractor directory, read profiles, reviews and availability, and find public invitations to bid. With an API key, it can also send leads and reviews, or manage a contractor's own leads and profile.

| | |
|---|---|
| Endpoint | `https://contractorsnearme.ai/api/mcp` |
| Transport | Streamable HTTP, stateless, JSON responses (`POST` only; `GET` returns 405) |
| Server | `contractors-near-me` 1.3.0, protocol `2025-06-18` |
| Auth | None for read tools. Write tools need an `x-api-key` header. |

## Connect

### Claude Code

```bash
claude mcp add contractors-near-me --transport http https://contractorsnearme.ai/api/mcp
```

### OpenCode

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "contractors-near-me": {
      "type": "remote",
      "url": "https://contractorsnearme.ai/api/mcp",
      "enabled": true
    }
  }
}
```

### Other MCP clients (Cursor, Claude Desktop and others)

```json
{
  "mcpServers": {
    "contractors-near-me": {
      "url": "https://contractorsnearme.ai/api/mcp"
    }
  }
}
```

To use write tools, add `"headers": { "x-api-key": "<your key>" }` to the server entry. Use your client's environment-variable syntax instead of pasting the key into a shared file.

There is no npm package for this server. Older pages that mention `npx @contractorsnearme/mcp-server` are wrong: use the remote URL above.

## Test it with curl

The server requires an `Accept` header that allows both JSON and event streams. Without it, every call fails with JSON-RPC error `-32000` ("Not Acceptable").

```bash
curl -sS -X POST https://contractorsnearme.ai/api/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{
        "name":"search_contractors",
        "arguments":{"trade":"roofing","city":"Austin","state":"TX","limit":2}}}'
```

The tool result is JSON inside `result.content[0].text`:

```json
{
  "contractors": [
    {
      "contractor_id": "…",
      "company_name": "…",
      "slug": "…",
      "city": "Austin",
      "state_code": "TX",
      "avg_rating": 5,
      "is_verified": false,
      "primary_trade": { "name": "Roofers", "slug": "roofing" },
      "profile_url": "https://contractorsnearme.ai/tx/austin/…"
    }
  ],
  "count": 2
}
```

## Tools

### Read tools (no key)

| Tool | Arguments | Returns |
|---|---|---|
| `search_contractors` | `trade`, `state` (2 letters), `city`, `q` (name or keywords), `lat` + `lng`, `radius_miles` (1-200, default 25), `limit` (1-50, default 20). Give at least one of trade, state, city, q, or lat+lng. | `contractors[]`, `count` |
| `get_contractor` | `slug` (required) | Full profile: about text, phone, website, rating, `is_verified`, primary trade, `profile_url` |
| `get_reviews` | `contractor_slug` (required), `limit` (1-50, default 10) | Newest reviews: rating, text, reviewer name, title, dates |
| `list_trades` | none | All trades with `slug`. Use these slugs for `trade`. |
| `get_availability` | `slug` (required) | Published availability (days, start, end, timezone, note), or `null` with "No availability published" |
| `search_itbs` | `query`, `city`, `state_code`, `limit` (1-50) | Open public invitations to bid: title, description, budget range, city, status |
| `get_itb` | `itb_id` (UUID, required) | One invitation to bid in full |
| `list_marketplace_requests` | `q`, `limit` | Public marketplace requests (currently returns none) |
| `get_marketplace_request` | `id_or_slug` (required), `state_code`, `city_slug` | One marketplace request |

### Write tools (API key)

| Tool | Arguments | Effect |
|---|---|---|
| `submit_lead` | `name`, `email`, `message` (10+ characters) required; `phone`, `contractor_slug`, `trade_slug` | Sends a lead to a contractor (or a trade). Limit 10 per hour per key. |
| `submit_review` | `contractor_slug`, `rating` (1-5), `description` (20+ characters), `reviewer_name` required; `title` and sub-ratings for cleanliness, communication, quality, timeliness, value | Submits a review. It's held for moderation before it appears. Limit 5 per hour per key. |
| `get_my_leads` | `status`, `limit` (1-100) | The key owner's leads. Needs a contractor key on a paid plan. |
| `respond_to_lead` | `lead_id` (required), `status` (`new`, `contacted`, `qualified`, `converted`, `lost`), `note` (up to 2,000 characters) | Updates one of the key owner's leads. Paid plan. |
| `update_my_profile` | Any of `company_name`, `dba`, `phone`, `email`, `website`, `about_text`, `quote`, `license_number`, `year_established`, `company_size` | Edits the key owner's directory profile. Paid plan. |

Leads sent through the MCP server go to the contractor's ContractorsNearMe profile. They don't appear in the contractor's MyBuildX Leads list.

## API keys

Keys look like `cnm_…` and are tied to one contractor profile. They're issued by BuildX support to contractors with an active Contractor ID subscription; there's no self-serve key page yet. To ask for one, [file a ticket](https://docs.buildx.pro/docs/help/file-a-ticket.md). A key is shown once; store it as a secret.

## Errors and limits

- Tool errors don't use HTTP status codes. The call returns `200` with `"isError": true` and a message in `content[0].text`, for example `Invalid trade slug` or `API key required to view leads`.
- Rate limits: 100 requests a minute per IP without a key, 1,000 a minute with a key, plus the per-tool write limits above. Over the limit you get HTTP `429` with `Retry-After`. Limits are approximate and can reset when the service redeploys, so don't design around exact numbers.
- Browser calls are allowed only from contractorsnearme.ai, claude.ai and chatgpt.com; other web origins get `403`. Server-side calls (no `Origin` header) always work.
- `get_contractor` can include a contractor's email address. Don't repeat it to users unless they ask for contact details; link the `profile_url` instead.

## Related

- [BuildX for AI agents](https://docs.buildx.pro/docs/agents/overview.md)
- [Recipes](https://docs.buildx.pro/docs/agents/recipes.md)
- [Find a contractor](https://docs.buildx.pro/docs/owners/find-a-contractor.md)
