MCP Server

Connect an MCP client with Fondaro OAuth or a scoped fdr_mcp_ API key.

Fondaro runs a hosted Model Context Protocol (MCP) server. It accepts Fondaro OAuth or a scoped fdr_mcp_ API key. Once connected, a client can work with CRM records, search property sources, turn selected listings into shareable interactive brochures, and write, share, and attach documents from your organization's library on your behalf.

The key is bound to the user and organization that created it: members reach only the leads assigned to them, and organization admins reach the whole organization. Nothing about MCP widens what an account can already do in the dashboard.

Server Endpoint

There is one endpoint. Configure it in a supported client exactly as written, path included and with no trailing slash:

https://api.fondaro.com/mcp/v1

The server prefers MCP 2026-07-28 over Streamable HTTP and negotiates earlier revisions from 2025-11-25 through 2024-10-07. Both paths are stateless and return JSON. Only POST carries MCP traffic; GET and DELETE return 405, and there is no SSE stream or session to configure.

Authentication

The public server accepts Fondaro OAuth access tokens issued for the exact MCP resource and scoped Fondaro MCP API keys. A bearer credential of any other shape, including a dashboard session token, is rejected with 401.

ChatGPT and Claude Code discover OAuth from the MCP endpoint and open Fondaro's browser approval flow. Each acts as a public CIMD client, uses authorization code with S256 PKCE, and manages token refresh itself. Users never copy an OAuth token. The authorization server publishes metadata at https://api.fondaro.com/.well-known/oauth-authorization-server; the protected resource metadata is available at https://api.fondaro.com/.well-known/oauth-protected-resource/mcp/v1.

Keys use the prefix fdr_mcp_ followed by 32 lowercase hexadecimal characters:

fdr_mcp_YOUR_KEY

Send it as a Bearer token. This authenticated discovery request shows the complete 2026-07-28 envelope and headers every direct client must send:

curl -X POST https://api.fondaro.com/mcp/v1 \
  -H "Authorization: Bearer fdr_mcp_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -H "Mcp-Protocol-Version: 2026-07-28" \
  -H "Mcp-Method: server/discover" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "server/discover",
    "params": {
      "_meta": {
        "io.modelcontextprotocol/protocolVersion": "2026-07-28",
        "io.modelcontextprotocol/clientInfo": {
          "name": "your-client",
          "version": "1.0.0"
        },
        "io.modelcontextprotocol/clientCapabilities": {}
      }
    }
  }'

Key facts:

  • Keys are minted per user from the dashboard (Settings, then Integrations, then Fondaro MCP). See the Fondaro MCP dashboard guide.
  • The plaintext key is shown exactly once at creation and is stored only as a SHA-256 hash. If you lose it, revoke it and mint a new one.
  • Each key carries a chosen subset of tool scopes, enforced fail-closed: a tool whose scope the key lacks is hidden from tools/list and denied at call time.
  • Keys can have an optional expiry (1 to 3650 days) and can be revoked at any time. Members revoke their own keys; organization admins can revoke any key in the organization.

Tool scopes

Every tool declares the scope it needs. Seven scopes exist:

ScopeGrants
crm:readRead leads, tasks, notes, deals, calls, and email history
crm:writeCreate and modify leads, tasks, notes, and deals
properties:readSearch property sources and read listings
brochures:readList and read interactive brochures
brochures:writeCreate, edit, renew, revoke, and delete interactive brochures
documents:readList and read library documents, including their live share links
documents:writeCreate and edit documents, share or revoke links, attach to leads

API keys carry whatever subset you choose when minting them (omit the subset to grant all seven scopes available at creation time). A key missing a tool's scope never sees that tool. Existing keys keep their stored scope set: mint a replacement key and reconnect the client if an older key needs brochure or document access.

Creating a brochure, adding or refreshing a listing, and renewing with optional refresh require both brochures:write and properties:read, because those operations may snapshot a paid portal listing. Metadata edits, reorder, remove, revoke, and delete need only brochures:write.

Document tools need only their own scope. Attaching a document to a lead is gated on your access to that lead, not on a CRM scope: a document is never a side door onto a lead you cannot already open.

Identity and scoping

Every tool call runs as the caller, with the same access the dashboard enforces:

  • Members see and touch only leads assigned to them. Lists, searches, counts, and timelines all narrow to their own leads. Reaching a lead they are not assigned to returns an access error, never another member's data.
  • Brochure members see and change only brochures they created; organization admins can work across the organization's brochures.
  • Organization admins operate organization-wide.
  • Admin-only tools (lead assignment and organization-wide bulk status changes) are hidden from a member's tools/list entirely.
  • Document tools follow the library's own visibility: you see organization-wide and publicly shared documents plus your own private ones, and an organization admin sees every document. Changing or sharing a document is limited to its creator and organization admins.
  • CRM write tools and every document tool, reads included additionally require the organization's active subscription entitlement. Interactive brochure writes remain available as a free Fondaro feature.

Access is bound to live organization membership, re-checked on every request behind a short role cache (about five minutes). Removing a user from the organization therefore severs their OAuth connections and fdr_mcp_ keys within that window. Individual grants and keys can also be disconnected or revoked at any time from the dashboard.

Rate limits

  • Per user or key: 120 requests per 60 seconds. Exceeding it returns a structured rate-limit error with a Retry-After header.
  • Portal property reads and brochure snapshots: external property portals share a daily allowance of 300 calls per organization per UTC day. A brochure operation is charged once per portal listing it requests. Once reached, portal-backed calls return an informative error until midnight UTC; internal MLS and Resales Online brochure work stays available.

Preferred MCP 2026-07-28 request contract

Every request is independent. Send all of these on every POST:

  • Mcp-Protocol-Version: 2026-07-28, matching the protocol version in params._meta.
  • Mcp-Method, matching the JSON-RPC method.
  • Mcp-Name when calling a named tool, matching params.name.
  • The io.modelcontextprotocol/protocolVersion, io.modelcontextprotocol/clientInfo, and io.modelcontextprotocol/clientCapabilities fields in params._meta.

A missing or mismatched header returns a protocol error. An unsupported protocol version is rejected. Mcp-Session-Id and Last-Event-ID do not create state and should not be sent.

server/discover describes the Fondaro server and advertises a tools-only capability set. Fondaro does not advertise prompts, resources, logging, subscriptions, tasks, MCP Apps, or extensions. Successful list and call responses carry resultType: "complete" plus the Fondaro server identity in result _meta.

Compatibility path for earlier revisions

Clients using 2025-11-25, 2025-06-18, 2025-03-26, 2024-11-05, or 2024-10-07 may use the legacy initialize and headerless JSON-RPC shapes. The SDK negotiates the revision and echoes the selected version. The following compatibility sequence initializes with 2025-11-25 and then lists tools:

# 1. Negotiate the compatibility revision.
curl -X POST https://api.fondaro.com/mcp/v1 \
  -H "Authorization: Bearer fdr_mcp_YOUR_KEY" \
  -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": "your-client", "version": "1.0.0" }
    }
  }'

# 2. List tools with the same bearer credential.
curl -X POST https://api.fondaro.com/mcp/v1 \
  -H "Authorization: Bearer fdr_mcp_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc": "2.0",
    "id": 2,
    "method": "tools/list",
    "params": {}
  }'

No server-side session is created: no Mcp-Session-Id is issued, and each POST is independent. A headerless tools/list or tools/call therefore also works without a preceding initialize. The compatibility path exposes the same role- and scope-filtered tool catalogue and the same JSON Schema 2020-12 inputs as the preferred path.

Calling tools

MCP traffic is JSON-RPC over the endpoint itself. List the available tools:

curl -X POST https://api.fondaro.com/mcp/v1 \
  -H "Authorization: Bearer fdr_mcp_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -H "Mcp-Protocol-Version: 2026-07-28" \
  -H "Mcp-Method: tools/list" \
  -d '{
    "jsonrpc": "2.0",
    "id": 2,
    "method": "tools/list",
    "params": {
      "_meta": {
        "io.modelcontextprotocol/protocolVersion": "2026-07-28",
        "io.modelcontextprotocol/clientInfo": {
          "name": "your-client",
          "version": "1.0.0"
        },
        "io.modelcontextprotocol/clientCapabilities": {}
      }
    }
  }'

Fondaro returns tools in ascending name order. The list result has a private five-minute cache hint because its contents depend on the current user, scopes, role, entitlement, and connected property providers. Do not share or reuse one user's cached list for another user.

Call a tool by name with its arguments:

curl -X POST https://api.fondaro.com/mcp/v1 \
  -H "Authorization: Bearer fdr_mcp_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -H "Mcp-Protocol-Version: 2026-07-28" \
  -H "Mcp-Method: tools/call" \
  -H "Mcp-Name: list_leads" \
  -d '{
    "jsonrpc": "2.0",
    "id": 3,
    "method": "tools/call",
    "params": {
      "name": "list_leads",
      "arguments": { "limit": 10 },
      "_meta": {
        "io.modelcontextprotocol/protocolVersion": "2026-07-28",
        "io.modelcontextprotocol/clientInfo": {
          "name": "your-client",
          "version": "1.0.0"
        },
        "io.modelcontextprotocol/clientCapabilities": {}
      }
    }
  }'

In practice you will rarely craft these by hand. Your MCP client renders the tools and calls them for you once connected.

Connecting clients

The dashboard has setup flows for the clients below. Use current released versions. Clients that do not yet send MCP 2026-07-28 may negotiate one of the supported earlier revisions automatically.

ClientSetupConfiguration path
ChatGPTOAuthAdd the Fondaro MCP endpoint as a custom app, then sign in to Fondaro and approve access in the browser. No token or API key is copied.
Codex CLIAPI keyLoad FONDARO_MCP_API_KEY in a terminal, then use --bearer-token-env-var as shown below. The shared Codex configuration stores the variable name, not the key.
Claude CodeOAuthAdd the user-scoped HTTP server without an Authorization header, choose Authenticate in /mcp, then approve access in the browser. No token or API key is copied.
CursorAPI keyAdd an environment-backed Authorization header to a private or global mcp.json; launch inheritance is not reliable across every installation.
Visual Studio CodeAPI keyAdd the endpoint and an environment-backed Authorization header to your user-level MCP configuration, then start the editor from the prepared terminal.
Scripts and other header-capable clientsAPI keySend Authorization: Bearer fdr_mcp_… to the endpoint.

For Codex CLI, securely load the key in the current terminal session, add the server, list the saved configuration, and start Codex from that same terminal:

codex mcp add fondaro --url https://api.fondaro.com/mcp/v1 \
  --bearer-token-env-var FONDARO_MCP_API_KEY
codex mcp list

Use /mcp to inspect the connection. Do not put the key value in config.toml.

For Claude Code, add the endpoint without --header using a current released version:

claude mcp add --transport http --scope user fondaro \
  https://api.fondaro.com/mcp/v1

Start Claude Code, open /mcp, choose fondaro, then choose Authenticate. Sign in to Fondaro, select the organization, review the five permissions, and choose Allow. Claude Code owns and refreshes the resulting OAuth credentials. Cancelling an explicit re-login can leave the server at Needs authentication; run /mcp and authenticate again.

For Cursor, use this only when Cursor can be launched from the terminal where FONDARO_MCP_API_KEY was loaded. Keep the configuration private/global rather than in a project:

{
  "mcpServers": {
    "fondaro": {
      "url": "https://api.fondaro.com/mcp/v1",
      "headers": {
        "Authorization": "Bearer ${env:FONDARO_MCP_API_KEY}"
      }
    }
  }
}

For ChatGPT, use https://api.fondaro.com/mcp/v1 as the custom app's MCP server URL. ChatGPT discovers the Fondaro authorization server, opens browser sign-in, and stores and refreshes the resulting OAuth credentials. Disconnect the grant under Your connections in Fondaro.

The friendly dashboard guide includes the ChatGPT and Claude Code browser-sign-in walkthroughs plus hidden-input commands for macOS zsh, Linux bash, and Windows PowerShell, current-session lifetime guidance, recovery for a lost key, and dashboard-backed connection verification.

Every credential attached to your account, whatever its type, is listed under Your connections on that page, with its owner, scopes, last use, expiry, and status. Each one is revoked independently.

Managing keys

MCP API keys are created and revoked from the dashboard, which calls the key-management REST endpoints below. These endpoints use your normal dashboard session (a Clerk JWT), not an fdr_mcp_ key.

MethodEndpointDescription
POST/mcp-keysMint a key for yourself. Body: { name, scopes?, expiresInDays? }. Returns { id, key, keyPrefix, scopes, expiresAt }.
GET/mcp-keysList keys. A member sees their own; an admin sees every key in the organization. Never includes the key value.
DELETE/mcp-keys/:idRevoke a key. Members revoke their own; admins revoke any key in the organization.

Request body for POST /mcp-keys:

FieldTypeRequiredDescription
namestringYesA label to identify the key
scopesstring[]NoAny of crm:read, crm:write, properties:read, brochures:read, brochures:write, documents:read, documents:write. Omit for all seven scopes current at creation time.
expiresInDaysnumberNo1 to 3650. Omit for a non-expiring key.

The key field is returned only in the POST response and never again.

Tool catalogue

The server exposes 63 tools from thirteen explicitly allowlisted tool classes. CRM reads and writes carry their corresponding CRM scope, property tools carry properties:read, and brochure and document tools carry the scopes shown below. CRM writes and every document tool (reads included) require the organization's subscription entitlement; brochure writes do not. The three CRM admin-only tools are hidden from a member's tool list.

Leads

ToolDescriptionScopeAdmin only
list_leadsList CRM leads with optional filters, paginatedcrm:readNo
search_leadsFuzzy-search leads by name, email, or phonecrm:readNo
get_leadFull detail for one leadcrm:readNo
get_lead_timelineA lead's chronological activity timelinecrm:readNo
get_lead_countsLead counts for each pipeline stagecrm:readNo
list_tagsThe organization's CRM tag cataloguecrm:readNo
list_org_membersThe organization's members, for assignmentcrm:readNo
create_leadCreate a new CRM leadcrm:writeNo
update_lead_contactUpdate a lead's name, email, or phonecrm:writeNo
update_lead_statusMove a lead to a different pipeline stagecrm:writeNo
set_lead_tagsReplace the full tag list on a leadcrm:writeNo

Lead assignment

ToolDescriptionScopeAdmin only
set_lead_assigneesReplace the owners on a leadcrm:writeYes
add_lead_assigneesAdd owners to a lead without removing existing onescrm:writeYes
bulk_update_lead_statusMove many leads to one stage, organization-widecrm:writeYes

Tasks

ToolDescriptionScopeAdmin only
list_tasksList your tasks, or a single lead's taskscrm:readNo
create_taskCreate a task on a leadcrm:writeNo
update_taskUpdate a task (reassigning is admin-only)crm:writeNo
delete_taskPermanently delete a taskcrm:writeNo

Notes

ToolDescriptionScopeAdmin only
list_notesList the notes on a leadcrm:readNo
create_noteAdd a note to a leadcrm:writeNo
update_noteEdit a note's contentcrm:writeNo
delete_noteDelete a note (author, or any admin)crm:writeNo

Deals

ToolDescriptionScopeAdmin only
list_dealsList deals for a lead or across the organizationcrm:readNo
get_dealFetch one deal by idcrm:readNo
create_dealCreate a deal on a leadcrm:writeNo
update_dealUpdate a deal's fieldscrm:writeNo
change_deal_stageMove a deal to another pipeline stagecrm:writeNo
close_deal_wonMark a deal as woncrm:writeNo
close_deal_lostMark a deal as lostcrm:writeNo
reopen_dealReopen a won or lost dealcrm:writeNo

Calls (read-only)

ToolDescriptionScopeAdmin only
get_call_historyA lead's call history with recording, transcript, and analysis flagscrm:readNo
get_call_transcriptThe stored transcript for a callcrm:readNo
get_call_analysisThe stored AI analysis for a callcrm:readNo

Email (read-only)

ToolDescriptionScopeAdmin only
get_email_historyA lead's email historycrm:readNo

Semantic search (read-only)

ToolDescriptionScopeAdmin only
search_crm_semanticRank the people whose CRM history matches a plain-language description, with evidence snippetscrm:readNo
match_leads_to_listingRank the people whose CRM history suggests interest in one property listingcrm:readNo

Both tools match meaning rather than exact words, across notes, call transcripts and their stored summaries, human-written emails from the last 24 months, and the assistant's distilled memory of each lead. Automated template sends are never part of the searchable corpus. Neither tool counts or aggregates; use the CRM read tools for totals.

An evidence snippet labelled Memory comes from the short document Fondaro AI keeps about that person, not from something they wrote. Memory is per lead only: organization and personal notes-to-self are never searchable this way.

Scoping is identical to every other CRM read. A member's search reaches only the leads assigned to them and an organization admin's reaches the whole organization. That narrowing is applied in the database, not by the tool, so no argument can widen it.

search_crm_semantic arguments:

FieldTypeRequiredDescription
querystringYesWhat to look for, in natural language. Phrase it the way the person would have said it.
occurredFromstringNoISO date or timestamp. Consider only history at or after this moment.
occurredTostringNoISO date or timestamp. Consider only history at or before this moment.
leadIdnumberNoNarrow the search to one person's own history.
maxResultsnumberNo1 to 30. Defaults to 10.

Dates are enforced in SQL and are never inferred from the query text. Resolve a relative phrase such as "this summer" into occurredFrom and occurredTo before calling.

match_leads_to_listing arguments:

FieldTypeRequiredDescription
propertyListingIdstringConditionalThe id of one of your own property listings.
sourcestringConditionalThe property source, as returned by list_property_sources or a property search result.
listingIdstringConditionalThe listing id within that source, as returned by a property search result.
maxResultsnumberNo1 to 30. Defaults to 10.

Send either propertyListingId, or both source and listingId. A raw listing URL is never accepted. The listing's own text becomes the search query at match time and is never stored in Fondaro's search index; the response repeats it as listingQuery so a surprising match list can be judged against what was actually searched.

curl -X POST https://api.fondaro.com/mcp/v1 \
  -H "Authorization: Bearer fdr_mcp_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -H "Mcp-Protocol-Version: 2026-07-28" \
  -H "Mcp-Method: tools/call" \
  -H "Mcp-Name: search_crm_semantic" \
  -d '{
    "jsonrpc": "2.0",
    "id": 4,
    "method": "tools/call",
    "params": {
      "name": "search_crm_semantic",
      "arguments": {
        "query": "wants a sea view penthouse, visiting this summer",
        "occurredFrom": "2026-01-01",
        "maxResults": 5
      },
      "_meta": {
        "io.modelcontextprotocol/protocolVersion": "2026-07-28",
        "io.modelcontextprotocol/clientInfo": {
          "name": "your-client",
          "version": "1.0.0"
        },
        "io.modelcontextprotocol/clientCapabilities": {}
      }
    }
  }'

The tool result is a JSON text block. matches is ordered best first, and rank is the only relevance signal the server publishes: there is no score, deliberately, because a percentage invites more confidence than a semantic match earns. bestMatchAt is the timestamp of the strongest matching record, not the person's last touch, and each evidence entry's sourceId is the note, call, or email row it came from.

{
  "matches": [
    {
      "leadId": 4821,
      "firstName": "Sofia",
      "lastName": "Andersson",
      "rank": 1,
      "matchCount": 4,
      "sourceCount": 3,
      "bestMatchAt": "2026-05-14T09:12:00.000Z",
      "evidence": [
        {
          "sourceType": "call_transcript",
          "sourceId": "8f1c0d0e-0000-4000-8000-000000000000",
          "occurredAt": "2026-05-14T09:12:00.000Z",
          "snippet": "she is set on a penthouse with open sea views, and is flying in during August"
        },
        {
          "sourceType": "note",
          "sourceId": "6c2b1a44-0000-4000-8000-000000000000",
          "occurredAt": "2026-03-02T16:40:00.000Z",
          "snippet": "top floor only, wants the terrace facing the water"
        }
      ]
    }
  ],
  "tookMs": 940,
  "reranked": true
}

sourceType is one of note, call_transcript, call_summary, or email. reranked is false when the reranking model was unavailable and the fused ordering was served instead: retrieval degrades rather than failing, and says so. match_leads_to_listing returns the same shape plus the listingQuery string.

Properties (read-only)

ToolDescriptionScopeAdmin only
list_property_sourcesList every property source the organization can search, and what each supportsproperties:readNo
list_property_search_optionsList exact values for one source and one search facetproperties:readNo
search_propertiesSearch one property source for listingsproperties:readNo
get_propertyFetch the full details of one propertyproperties:readNo
autocomplete_locationResolve a place name to a source-native location tokenproperties:readNo

The property tools span the internal Fondaro MLS, the per-organization connected sources (Resales Online, Zoddak, and Inmobalia, when the organization has each integration set up), and 19 public property portals. Use list_property_sources when you need to choose a source. Use the bounded list_property_search_options tool for one source-specific facet (property_type, feature, stage, operation, status, or sort) instead of loading every source vocabulary at once.

search_properties uses a strict source-aware contract. Location selections are the opaque locationIds returned by autocomplete_location; property types, locations, features, stages, and reference numbers are arrays where the source supports multiple values. Price, bedroom, bathroom, build-size, and plot-size bounds use min…/max… fields. operation, newDevelopments, sort, supported geo, an opaque continuation cursor, and a limit from 1–20 are also available where advertised. Older singular fields such as locationId, propertyType, page, minSize, and top-level latitude/longitude are rejected by schema validation.

A successful search includes a compact receipt with the requested criteria, resolved source-native values, applied facets, redacted wire parameter names, verification status, excluded upstream mismatches, and an opaque next cursor when more results exist. Unsupported facets, invalid or ambiguous option values, invalid/stale locations, missing required locations, disconnected sources, exhausted portal allowance, upstream mismatch, and upstream failure return distinct machine-readable errors. Fondaro never returns a successful result that silently ignored an explicit criterion.

Interactive brochures

ToolDescriptionScopeAdmin only
list_brochuresList visible brochures with explicit pagination, status, share URL, listing count, and view statisticsbrochures:readNo
get_brochureRead one brochure and compact listing summariesbrochures:readNo
create_brochureCreate a shareable brochure from 1–25 property identities, with optional public location visibilitybrochures:write + properties:readNo
update_brochureUpdate title, recipient fields, theme, accent, or public location visibilitybrochures:writeNo
add_brochure_listingAdd one source property, optionally at a positionbrochures:write + properties:readNo
remove_brochure_listingRemove one brochure listingbrochures:writeNo
reorder_brochure_listingsReplace the complete brochure listing orderbrochures:writeNo
refresh_brochure_listingRefresh one source snapshot while preserving overridesbrochures:write + properties:readNo
set_brochure_listing_locationCorrect one snapshot's address or coordinatesbrochures:writeNo
update_brochure_listing_detailsSet or clear per-brochure detail overrides and hide flagsbrochures:writeNo
revoke_brochureDisable the share URL reversiblybrochures:writeNo
renew_brochureRestore the share URL, optionally refreshing listingsbrochures:write + properties:readNo
delete_brochurePermanently delete a brochurebrochures:writeNo

Start with search_properties or get_property, then pass each returned source and exact source-native id unchanged. Brochure creation accepts internal, resales_online, and every public portal source: idealista, rightmove, immobiliare, immoscout, funda, zoopla, seloger, otodom, immowelt, realtor, homes, trulia, propertyfinder, dubizzle, centris, loopnet, zillow, redfin, and bayut. zoddak, inmobalia, resales_viewer, arbitrary URLs, and normalized or guessed IDs are not accepted for MCP creation. An existing dashboard brochure containing a resales_viewer listing remains readable and manageable.

For Idealista, preserve the optional country value (es, it, or pt) alongside the bare property id. The returned publicUrl works immediately; background image ingest and coordinate enrichment may still be running. Listing edits use the brochure-listing UUID returned by create_brochure or get_brochure, not the source property id. Reorder always sends the complete current UUID set.

create_brochure accepts an optional showLocations boolean. An explicit value overrides the organization's starting value; omitted input inherits it. update_brochure can change the value later, independently from a locked branding preset. When false, the public viewer omits structured locations, maps, nearby places, and location-derived copy, and the public payload redacts address and coordinate fields to null; authenticated brochure data remains available for editing. Brochure list, get, create, and update results report the effective showLocations value.

This JSON-RPC example creates and shares an Andrew brochure from five Idealista results:

curl -X POST https://api.fondaro.com/mcp/v1 \
  -H "Authorization: Bearer fdr_mcp_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -H "Mcp-Protocol-Version: 2026-07-28" \
  -H "Mcp-Method: tools/call" \
  -H "Mcp-Name: create_brochure" \
  -d '{
    "jsonrpc": "2.0",
    "id": 5,
    "method": "tools/call",
    "params": {
      "name": "create_brochure",
      "arguments": {
        "title": "Estepona homes for Andrew",
        "recipientName": "Andrew",
        "recipientNote": "Five homes selected for your review.",
        "showLocations": false,
        "listings": [
          {"source":"idealista","id":"ID_FROM_SEARCH_1","country":"es"},
          {"source":"idealista","id":"ID_FROM_SEARCH_2","country":"es"},
          {"source":"idealista","id":"ID_FROM_SEARCH_3","country":"es"},
          {"source":"idealista","id":"ID_FROM_SEARCH_4","country":"es"},
          {"source":"idealista","id":"ID_FROM_SEARCH_5","country":"es"}
        ]
      },
      "_meta": {
        "io.modelcontextprotocol/protocolVersion": "2026-07-28",
        "io.modelcontextprotocol/clientInfo": {
          "name": "your-client",
          "version": "1.0.0"
        },
        "io.modelcontextprotocol/clientCapabilities": {}
      }
    }
  }'

Take publicUrl from the successful tool result and share that HTTPS link. It stops resolving after revocation or expiry and resumes at the same URL after renewal.

Documents

ToolDescriptionScopeAdmin only
list_documentsList library documents, most recently changed first, with the live share URL on eachdocuments:readNo
get_documentRead one document including its markdown bodydocuments:readNo
list_lead_documentsList the documents attached to one lead, with who attached each one and whendocuments:readNo
create_markdown_documentCreate a markdown document in the librarydocuments:writeNo
update_documentReplace a markdown document's title, body, or bothdocuments:writeNo
share_documentPublish the document behind a public link and return that URLdocuments:writeNo
revoke_document_shareTake the public link downdocuments:writeNo
attach_document_to_leadRecord that a document belongs with a leaddocuments:writeNo
detach_document_from_leadRemove that link between a document and a leaddocuments:writeNo

Uploading a PDF and deleting a document are deliberately not exposed. An MCP request has no file transport, and deletion stays a dashboard action.

All nine document tools are hidden from an organization without an active subscription, the three reads included. Documents is gated fully to active plans, unlike the CRM, where reads stay available.

list_documents accepts optional kind (markdown or pdf), visibility (private, organization, or public), a free-text q on the title, and limit (1 to 50, default 20) with offset. A visibility filter only narrows what you can already see; it never widens it. Each result carries shareUrl, non-null only while the link is genuinely live, so a client that just listed the library can put a link in an email without a second call.

get_document returns the same fields plus body, attachedLeadIds, and attachedListingIds. A body longer than 8000 characters comes back trimmed with truncated: true rather than failing. Only a markdown document has a body; an uploaded PDF returns null and is read through its share link.

update_document replaces the whole body, so send everything that should survive. Only the document's creator or an organization admin can change, share, or revoke a document.

share_document takes an optional expiresAt (an ISO-8601 instant in the future). Omit it for a link that never expires, which is the default. Re-sharing a document whose link is still live returns the same URL, so a link already sent in an email keeps working. revoke_document_share kills the link permanently: anyone holding the URL stops being able to open it, and sharing the document again later mints a different URL. The document itself stays in the library at organization visibility.

curl -X POST https://api.fondaro.com/mcp/v1 \
  -H "Authorization: Bearer fdr_mcp_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -H "Mcp-Protocol-Version: 2026-07-28" \
  -H "Mcp-Method: tools/call" \
  -H "Mcp-Name: share_document" \
  -d '{
    "jsonrpc": "2.0",
    "id": 6,
    "method": "tools/call",
    "params": {
      "name": "share_document",
      "arguments": {
        "documentId": "DOCUMENT_ID_FROM_LIST"
      },
      "_meta": {
        "io.modelcontextprotocol/protocolVersion": "2026-07-28",
        "io.modelcontextprotocol/clientInfo": {
          "name": "your-client",
          "version": "1.0.0"
        },
        "io.modelcontextprotocol/clientCapabilities": {}
      }
    }
  }'

Take shareUrl from the successful result and send that HTTPS link. It resolves at /d/… until it is revoked or its optional expiry passes.

attach_document_to_lead and detach_document_from_lead need access to both the document and the lead. Attaching twice is harmless and keeps the first receipt; detaching something already detached is a clean no-op. An attachment is not a grant: a private document someone else attached stays invisible to you.

Security best practices

  • Treat an fdr_mcp_ key like a password. Store it in a secrets manager, never in source control.
  • Never copy an OAuth access or refresh token. ChatGPT or Claude Code owns those credentials, and Fondaro exposes only the revocable connected-app grant.
  • Grant each key only the scopes its client needs. A read-only assistant does not need crm:write.
  • A client that only reads brochures needs brochures:read; a client that builds them normally needs brochures:write plus properties:read, and usually brochures:read for follow-up list/get calls.
  • Use an expiry for time-limited integrations, and revoke keys you no longer use.
  • Rotate a key by minting a new one, updating the client, then revoking the old one. Multiple live keys plus revoke make this zero-downtime.
  • Keep the key value out of config.toml, mcp.json, source control, logs, screenshots, and shell history. Prefer environment-variable references supported by the client.
  • Treat a document shareUrl like a bearer link, exactly as you would a brochure URL. Anyone with it can open the document until it is revoked or expires. Revoking is instant and permanent for that URL; re-sharing mints a new one, so links already sent stay dead.
  • Treat a brochure publicUrl like a bearer link. Anyone with it can view the brochure until it expires or is revoked. Recipient names and notes, plus the share URL returned by a tool, enter the selected MCP client's conversation history; do not send sensitive personal data to a client whose retention policy you have not accepted.