---
name: hemlo-mcp
description: Connect to and use the Hemlo MCP server (Streamable HTTP, bearer API key) so an agent such as Claude, ChatGPT, Cursor or Codex can do a broker's work in Hemlo — deals, meetings, capture, listing drafts, Stilprofil and approvals. Use when setting up the Hemlo MCP connection or choosing which Hemlo tool to call.
license: Proprietary
compatibility: Needs an MCP client that supports Streamable HTTP with a custom Authorization header.
metadata:
  author: hemlo
  version: "1.0"
---

# Hemlo MCP server

- URL (staging): `https://api.staging.hemlo.ai/api/mcp`
- Transport: Streamable HTTP (stateless, JSON responses)
- Auth: `Authorization: Bearer hsk_u_…` with `me:read deals:read deals:write` (see **hemlo-keys**).
  OAuth sign-in is not supported yet, so the client must be able to send a header.

Tools run as the key's broker, with the same roles, scopes, tenant isolation and audit log as the
Hemlo app (channel `mcp`). Each tool is one `/api/v1` operation, generated from the OpenAPI contract:
path and query parameters are top-level arguments, the JSON request body goes in `body`.

## Connect

Claude Code:
```bash
claude mcp add --transport http hemlo https://api.staging.hemlo.ai/api/mcp \
  --header "Authorization: Bearer $HEMLO_API_KEY"
```

Cursor (`~/.cursor/mcp.json`):
```json
{
  "mcpServers": {
    "hemlo": {
      "url": "https://api.staging.hemlo.ai/api/mcp",
      "headers": { "Authorization": "Bearer ${HEMLO_API_KEY}" }
    }
  }
}
```

Codex (`~/.codex/config.toml`):
```toml
[mcp_servers.hemlo]
url = "https://api.staging.hemlo.ai/api/mcp"
bearer_token_env_var = "HEMLO_API_KEY"
```

OpenAI Responses API / Agents SDK: add a remote MCP tool with `server_url` set to the URL above and
`headers: { "Authorization": "Bearer …" }`. ChatGPT custom connectors that require OAuth can't
connect until Hemlo supports OAuth.

## Workflow

1. `get_me` — who you act as, organizations, roles, `auth.scopes`.
2. `get_broker_today` — what needs attention.
3. Work the deal: `get_deal` → `create_deal_meeting` → `capture_meeting` → `get_listing_draft` →
   `edit_listing_draft` / `style_listing_draft` → `resolve_listing_discrepancy` →
   `approve_listing_draft` (only when the broker asks).
4. Stilprofil: `get_style_profiles`, `put_style_profile` with `body: { "links": [...] }` (max 20
   sold properties) and/or `body: { "samples": [...] }`.

Tools marked destructive (delete_*) and approvals change the broker's real data — confirm first.
A tool error returns the API error JSON; `forbidden` usually means the key lacks `deals:write`.

## Tools

<!-- tools:start -->
| Tool | Kind | API | What it does |
| --- | --- | --- | --- |
| `get_me` | read | `GET /api/v1/me` | The caller, their organizations, roles and API key scopes |
| `update_me` | write | `PATCH /api/v1/me` | Update the caller's profile settings (display name and whether meeting recordings are stored in the backend) |
| `get_broker_today` | read | `GET /api/v1/broker/today` | The agent's day — next meeting, items waiting for approval and active deals |
| `list_deals` | read | `GET /api/v1/deals` | Deals, most recently updated first |
| `create_deal` | write | `POST /api/v1/deals` | Create a deal |
| `get_deal` | read | `GET /api/v1/deals/{id}` | A deal with prep (price picture, comparables, facts), meetings, listing draft and compliance check |
| `update_deal` | write | `PATCH /api/v1/deals/{id}` | Update a deal's property details, stage, price picture, facts or responsible agent |
| `delete_deal` | destructive | `DELETE /api/v1/deals/{id}` | Delete a deal and its meetings, drafts, tasks, documents and checks |
| `create_deal_comparable` | write | `POST /api/v1/deals/{id}/comparables` | Add a comparable sale to a deal |
| `update_deal_comparable` | write | `PATCH /api/v1/deals/{id}/comparables/{comparableId}` | Update a comparable sale |
| `delete_deal_comparable` | destructive | `DELETE /api/v1/deals/{id}/comparables/{comparableId}` | Delete a comparable sale |
| `create_deal_task` | write | `POST /api/v1/deals/{id}/tasks` | Add a follow-up task to a deal |
| `create_deal_meeting` | write | `POST /api/v1/deals/{id}/meetings` | Create a meeting on a deal |
| `list_deal_documents` | read | `GET /api/v1/deals/{id}/documents` | The deal's documents, newest first |
| `download_deal_document` | read | `GET /api/v1/deals/{id}/documents/{documentId}/download` | A short-lived signed link to the file (5 minutes). Downloads are audited. |
| `delete_deal_document` | destructive | `DELETE /api/v1/deals/{id}/documents/{documentId}` | Delete a document from a deal and re-evaluate the Trygghetskoll |
| `update_deal_check` | write | `PATCH /api/v1/deals/{id}/checks` | Update a Trygghetskoll phase summary and issues (asking_price, showing, bidding) |
| `act_on_check_item` | write | `POST /api/v1/deals/{id}/checks/{key}/actions` | Act on a Trygghetskoll item |
| `list_meetings` | read | `GET /api/v1/meetings` | Meetings visible to the caller, newest first |
| `get_meeting` | read | `GET /api/v1/meetings/{id}` | A meeting with its deal, summary, follow-ups and styling tips |
| `update_meeting` | write | `PATCH /api/v1/meetings/{id}` | Update a meeting's schedule, location, summary, transcript or styling tips |
| `delete_meeting` | destructive | `DELETE /api/v1/meetings/{id}` | Delete a meeting |
| `capture_meeting` | write | `POST /api/v1/meetings/{id}/capture` | Submit what was said in the meeting and generate summary, follow-ups and a listing draft |
| `list_meeting_recordings` | read | `GET /api/v1/meetings/{id}/recordings` | Audio recording segments stored for this meeting, in order, with short-lived playback URLs |
| `delete_meeting_recording` | destructive | `DELETE /api/v1/meetings/{id}/recordings/{recordingId}` | Delete a stored recording segment |
| `get_meeting_coaching` | read | `GET /api/v1/meetings/{id}/coaching` | Coach feedback for a captured meeting |
| `regenerate_meeting_coaching` | write | `POST /api/v1/meetings/{id}/coaching` | Regenerate the coach feedback for a captured meeting |
| `list_coaching` | read | `GET /api/v1/coaching` | The caller's recent meeting feedback, newest first |
| `list_recordings` | read | `GET /api/v1/recordings` | Meetings with stored audio recordings, most recently recorded first |
| `update_task` | write | `PATCH /api/v1/tasks/{id}` | Mark a follow-up done or not done, or update its title |
| `delete_task` | destructive | `DELETE /api/v1/tasks/{id}` | Delete a follow-up task |
| `list_listing_drafts` | read | `GET /api/v1/listing-drafts` | Listing drafts and approved listings, drafts first, then most recently updated |
| `create_listing_draft` | write | `POST /api/v1/listing-drafts` | Create or replace the listing draft for a deal |
| `get_listing_draft` | read | `GET /api/v1/listing-drafts/{id}` | A listing draft with source-tagged text and discrepancies |
| `edit_listing_draft` | write | `PATCH /api/v1/listing-drafts/{id}` | Edit the headline and body text |
| `delete_listing_draft` | destructive | `DELETE /api/v1/listing-drafts/{id}` | Delete a listing draft |
| `create_listing_discrepancy` | write | `POST /api/v1/listing-drafts/{id}/discrepancies` | Add a discrepancy to a listing draft |
| `delete_listing_discrepancy` | destructive | `DELETE /api/v1/listing-drafts/{id}/discrepancies/{discrepancyId}` | Delete a discrepancy from a listing draft |
| `get_style_profiles` | read | `GET /api/v1/style-profile` | The caller's Stilprofil, their firms' office profiles and which one applies to new drafts |
| `put_style_profile` | write | `PUT /api/v1/style-profile` | Analyze earlier listing texts and sold-listing links and save them as the caller's own Stilprofil |
| `delete_style_profile` | destructive | `DELETE /api/v1/style-profile` | Remove the caller's own Stilprofil |
| `put_office_style_profile` | write | `PUT /api/v1/organizations/{id}/style-profile` | Set the office Stilprofil (agent_admin, org_admin) |
| `delete_office_style_profile` | destructive | `DELETE /api/v1/organizations/{id}/style-profile` | Remove the office Stilprofil (agent_admin, org_admin) |
| `style_listing_draft` | write | `POST /api/v1/listing-drafts/{id}/style` | Write the draft in the agent's Stilprofil, next to the generic version |
| `approve_listing_draft` | write | `POST /api/v1/listing-drafts/{id}/approve` | Approve the draft. Fails with `open_discrepancies` until every discrepancy is settled. |
| `resolve_listing_discrepancy` | write | `POST /api/v1/listing-drafts/{id}/discrepancies/{discrepancyId}/resolve` | Settle a mismatch between the seller's statement and the documents |
<!-- tools:end -->
