Tools reference
The Formule MCP server exposes 33 tools. Every one of them runs as
you: the server resolves your identity first and then queries the database under
row-level security, so a tool can only ever return what you could see by signing
into the app yourself. A real estate agent asking search_leads gets their own
leads; a manager asking the same question gets the agency’s.
Read-only tools
Section titled “Read-only tools”These answer questions. They never modify anything.
whoami
Section titled “whoami”Who this call acts for: the person, their agency (or agencies), their agent profile if any, whether you can write (can_write) and whether this chat is linked. Also work: the agency they work for and their role there (the Formule workspace is not always that agency), with how_to_talk to them, or ask when it is not known yet. Call it first. If user is null, explain the linking steps in scoping and do nothing else (on WhatsApp, scoping also says how someone new signs up with start_signup).
Takes no parameters.
recent_actions
Section titled “recent_actions”What you (Really) did for this person lately, in every channel (the app, WhatsApp, calls): posts, Facebook Page posts, adverts, listings created or changed, WhatsApp messages sent, designs, reels, leads added. Each line has when, the channel, what and where, and the link when there is one. Read it before saying something was not done (“did you post the garage on Facebook?”), and when they ask what you did.
| Parameter | Type | What it does |
|---|---|---|
days |
integer | How far back (default 30, max 90). |
limit |
integer | Most lines (default 20, max 50). |
list_companies
Section titled “list_companies”The agencies this person belongs to (all agencies for a super admin), with how they belong (via) and which one is active. Use the slugs with select_company or company_slug.
Takes no parameters.
search_leads
Section titled “search_leads”Find leads (the people this person already has in Formule) by free text (name, surname, property, email, phone), by what an agent wrote in the lead’s notes, by status or by assignment. Returns the most recent first. Only leads this person may see in Formule. Not for finding homes: ‘what do we have for a client with 4 million in Marsascala’ or ‘my buyer wants a 3-bed in Sliema’ asks for properties for that client, which is search_listings.
| Parameter | Type | What it does |
|---|---|---|
query |
string | Text to match against name, surname, property, email and the lead’s notes; a phone number is matched exactly. A lead matched on its notes carries matched_note, the part of the note that matched. |
status |
string | Filter by crm_status. |
unassigned_only |
boolean | — |
limit |
integer | Max results (default 20, max 100). |
get_lead
Section titled “get_lead”Full details for one lead, including comments, its open property matches (from the agency’s external listing catalogue, when connected) and open reminders. A lead with awaiting_your_acceptance: true was offered to this agent by a manager and is not accepted yet: its contact details come back masked and its notes are left out until they accept it in Formule.
| Parameter | Type | What it does |
|---|---|---|
lead_key (required) |
string | — |
list_stale_leads
Section titled “list_stale_leads”Leads that still need attention (UNCLEAR / ATTEMPTED_NO_RESPONSE / NURTURE) with no comment in the last N days. The raw material for a follow-up digest.
| Parameter | Type | What it does |
|---|---|---|
days |
integer | Default 7. |
limit |
integer | — |
list_matches
Section titled “list_matches”Saved property matches between this person’s leads and the agency’s external listing catalogue, best score first. Default: ‘new’ matches only; matches found on a person’s request (find_matches_for_lead) are saved as ‘shown’. A catalogue: “none” answer means no catalogue is connected: relay that, never ‘no matches’. A matching: “not_available” answer means this person is not on the matching list: say so, then search the catalogue with search_listings instead.
| Parameter | Type | What it does |
|---|---|---|
status |
string | — |
since |
string | ISO timestamp; only matches created after this. |
limit |
integer | — |
search_listings
Section titled “search_listings”Search property listings (active, newest first). The tool for ‘what do we have for a client with 4 million in Marsascala’, ‘anything for a buyer with 2M in Attard’: a client’s budget and place are max_price (with min_price near it) and locality. Searches the agency’s own catalogue (its portal feed, or its own website once connected with connect_agency_website) by default, or the whole market with scope “market”: every agency catalogue Formule holds (portal feeds and agencies’ own websites), each listing with listed_by naming the agency. An agency without a catalogue of its own always searches the market. Pass agency to search one agency’s listings by its name. Covers homes and commercial property (shops, Class 4B retail, offices, Class 4A, restaurants and food & drink, showrooms, warehouses, hotels, land, garages), for sale and for rent. Always pass property_type when the person names a kind: plain words work (‘retail’, ‘shop’, ‘office’, ‘commercial’, ‘restaurant’, ‘land’). Pass sale_or_rent when they say buy or rent. With a locality, listings in that place itself come first and the rest of its wider region after them; several places can be given at once (‘Sliema, Gzira’); in_locality says how many are in the places themselves, so say when the others are nearby rather than in them. A rental’s price is per month unless price_basis says otherwise (e.g. ‘Yearly Price Per Square Meter’). When nothing matches, kinds_available lists what the same search does have: offer those instead of saying there is nothing. Pass ref to look up one listing by its reference (the code at the end of its link). The catalogue knows no distances (schools, the sea): say so, and give the listings with their links so the agent can check. When the agency’s own search finds nothing, try the market (try_market says so). For agencies and portals outside Formule’s catalogue, use web_search and preview_listing_from_url (beyond says how). A still_reading answer means a website is still being read: say the results cover only the listings read so far. A catalogue: “none” answer means no agency has any catalogue yet.
| Parameter | Type | What it does |
|---|---|---|
ref |
string | Listing reference, e.g. SAPT670357. The other filters are ignored when it is given. |
scope |
string | agency (default): the agency’s own catalogue. market: every agency catalogue Formule holds. |
agency |
string | Only this agency’s listings, by its name or website. Implies the market. |
locality |
string | Locality or region name, any spelling (St Julians, San Giljan, Gżira); several separated by commas. |
min_price |
number | Lowest asking price (or monthly rent). |
max_price |
number | Highest asking price (or monthly rent). |
min_bedrooms |
integer | Fewest bedrooms. |
property_type |
string | The kind of property in plain words, e.g. Apartment, Penthouse, Villa, retail, shop, office, commercial, restaurant, warehouse, land, garage. |
sale_or_rent |
string | For sale or for rent. |
limit |
integer | Max results (default 20, max 100). |
list_agents
Section titled “list_agents”Active agents of the agency (names and contact details, as shown in the app).
Takes no parameters.
my_stats
Section titled “my_stats”This person’s numbers over the leads they may see: leads by status, new in the last 7 and 30 days, leads with no note yet, leads still open but quiet for 7+ days, last activity, open reminders. Admins see their agency-wide view.
Takes no parameters.
find_leads_for_listing
Section titled “find_leads_for_listing”Which of this person’s leads would want a given property, e.g. “which clients fit a 2-bed in the centre?”. Pass listing_key for one of the agency’s own listings, a listing reference from the agency’s external listing catalogue, or describe the property (locality, price, bedrooms, property_type, sale_or_rent) with whatever the person said. Reads every lead’s stated preferences, the wishes typed on the lead, and what agents wrote in its notes; place names match whatever the case, accents, a known short form or a typo. Returns the best few with the reason each fits; a fit read from the notes carries matched_note, the note it came from.
| Parameter | Type | What it does |
|---|---|---|
listing_key |
string | One of the agency’s own listings, as list_my_listings or get_listing returned it. |
ref |
string | Listing reference in the agency’s external catalogue. |
locality |
string | Town or area, as the person wrote it (misspellings are fine). |
price |
number | — |
bedrooms |
integer | — |
property_type |
string | e.g. Apartment, Penthouse, Villa |
sale_or_rent |
string | — |
limit |
integer | Default 10. |
list_due_followups
Section titled “list_due_followups”Open reminders due up to a point in time (default: end of today on the agency’s clock) plus any overdue ones, soonest first. Your own reminders; admins also see their agency’s.
| Parameter | Type | What it does |
|---|---|---|
until |
string | ISO 8601 timestamp; default the end of the current day in the agency’s time zone. |
include_future |
boolean | Also list reminders due after until. |
limit |
integer | — |
get_lead_timeline
Section titled “get_lead_timeline”What the assistant remembers about one lead’s WhatsApp conversation: last contact, counts, listings already shared, warmth, and the latest messages (text kept 30 days). Only for leads whose agent enabled WhatsApp reading in Formule.
| Parameter | Type | What it does |
|---|---|---|
lead_key (required) |
string | — |
limit |
integer | Messages to return, default 20. |
list_nudges
Section titled “list_nudges”What the assistant should tell this person now: follow-ups due, leads gone quiet, new property matches, next steps after a conversation. Each has a ready-to-send draft. Pure read: call acknowledge_nudges after showing them.
| Parameter | Type | What it does |
|---|---|---|
include_delivered |
boolean | Also return nudges already shown (default true). |
limit |
integer | — |
search
Section titled “search”Free-text search across this agency’s leads and its external listing catalogue. Returns lightweight {id, title, url} hits; pass an id to fetch for the whole record. Scoped by Formule’s own permissions, so it returns only what this person can already see. (ChatGPT deep research and company knowledge require a tool named exactly search.)
| Parameter | Type | What it does |
|---|---|---|
query (required) |
string | Free text, matched against lead name, surname, property and email, and against listing title, locality and region. |
limit |
integer | Max results (default 10, max 50). |
Return one whole record as readable text, given an id from a search result. Ids look like lead:<lead_key> or listing:<ref>. (ChatGPT deep research and company knowledge require a tool named exactly fetch.)
| Parameter | Type | What it does |
|---|---|---|
id (required) |
string | An id exactly as returned by search. |
Tools that change data
Section titled “Tools that change data”These write. Expect your client to confirm before running one.
create_lead
Section titled “create_lead”Add a new lead (a client) to the agency, as the app’s New lead form does: assigned to this person when they are an agent. Needs the first name and a phone number; pass the surname, email, what they came for (property) and a note only when the person said them, never invented. A national phone number gets the agency’s calling code. If one of this person’s leads already has that phone or email, nothing is added and that lead comes back with duplicate: true.
| Parameter | Type | What it does |
|---|---|---|
name (required) |
string | First name. |
surname |
string | — |
telephone (required) |
string | As the person gave it. |
email |
string | — |
property |
string | The listing or source they came from, e.g. “seafront penthouse” or “walk-in”. |
note |
string | Anything else the person said about them; saved as the lead’s notes. |
from_whatsapp_chat |
boolean | true when the person forwarded you this contact card or chat from their own WhatsApp (they already talk to this client): the lead is filed under WhatsApp and gets no automatic welcome message. |
create_leads
Section titled “create_leads”Add up to ${MAX_BULK_LEADS} leads at once from a list the person pasted, each exactly as create_lead adds one, after one approval. Every person needs a first name and a phone number from the list itself, never invented; leave out anyone without a phone and say so. The result gives each row: created, already_a_lead (same phone or email as one of this person’s leads), or skipped with the reason.
| Parameter | Type | What it does |
|---|---|---|
leads |
array | First name. |
add_lead_note
Section titled “add_lead_note”Append a dated note to a lead’s comments (the same log agents write in the CRM).
| Parameter | Type | What it does |
|---|---|---|
lead_key (required) |
string | — |
note (required) |
string | — |
schedule_followup
Section titled “schedule_followup”Remind this person about a lead at a given time (‘remind me to call Maria on Friday’). Reminders belong to the person asking; they can only be set in an agency they are a member of.
| Parameter | Type | What it does |
|---|---|---|
lead_key (required) |
string | — |
due_at (required) |
string | ISO 8601 timestamp with the agency’s UTC offset (whoami gives its time_zone and local_time), e.g. 2026-09-11T09:00:00+02:00. |
note |
string | What to do, in the person’s words. |
link_channel
Section titled “link_channel”Finish the one-time account link: the person got a code from Formule and sent it in this WhatsApp chat (8 characters, in the message LINK CODE that Profile → Really on WhatsApp and by phone, or the page that finishes signing up on WhatsApp, writes for them). Pass the code; the chat identity comes from the assistant runtime. From then on this chat acts as that person.
| Parameter | Type | What it does |
|---|---|---|
code (required) |
string | — |
chat_id |
string | The chat the code was sent from. Set by the assistant runtime; do not fill in by hand. |
platform |
string | Default whatsapp. |
start_signup
Section titled “start_signup”For an estate agent who is new to Formule, messages you on WhatsApp and wants to start: emails them a link that creates their Formule account. Only in a WhatsApp chat that is not linked yet (whoami: user null); never for a linked chat.
| Parameter | Type | What it does |
|---|---|---|
full_name (required) |
string | Their first and last name, as they gave it. |
agency_name |
string | The agency they work for; leave it out when they work independently. |
email (required) |
string | Their email address, exactly as they confirmed it. |
language |
string | The language you are chatting in, as a code such as it or de. The email is written in it when Formule speaks it (en, de, fr, es, it, pl), else in English. |
confirm_signup_code
Section titled “confirm_signup_code”For the WhatsApp sender who asked for a sign-up (start_signup) and sends back the 6-digit code from that email. Only in a WhatsApp chat that is not linked yet, only from the number that asked.
| Parameter | Type | What it does |
|---|---|---|
code (required) |
string | The 6 digits from the email, as sent (spaces are fine). |
set_my_work_profile
Section titled “set_my_work_profile”Save which agency the person works for and their role there (whoami shows it as work). Call it as soon as they say it, in any words (“I’m the CEO of …”, “agent at …”), and when they correct it; only the fields given change.
| Parameter | Type | What it does |
|---|---|---|
agency_name |
string | The agency they work for, as they name it. |
agency_website |
string | The agency’s website, e.g. agency.com. |
job_role |
string | — |
job_title |
string | Their title in their own words, e.g. CEO, Branch manager. |
select_company
Section titled “select_company”Switch the active agency for a person who belongs to several (see whoami). Pass the agency’s slug: map the agency name the person used to a slug yourself, never ask them for a slug. In a chat the choice sticks for later calls.
| Parameter | Type | What it does |
|---|---|---|
company_slug (required) |
string | — |
update_lead_status
Section titled “update_lead_status”Set a lead’s crm_status.
| Parameter | Type | What it does |
|---|---|---|
lead_key (required) |
string | — |
status (required) |
string | — |
set_match_status
Section titled “set_match_status”Mark a property match as shown, sent (the listing was sent to the client) or dismissed.
| Parameter | Type | What it does |
|---|---|---|
match_id (required) |
string | — |
status (required) |
string | — |
find_matches_for_lead
Section titled “find_matches_for_lead”When the person asks for it: read this lead’s notes, work out what the client wants (area, budget, bedrooms, type, buy or rent) and match that against the agency’s external listing catalogue. Saves those wishes and the matches on the lead (as ‘shown’) and returns the best matches with the reasons. Every call is one AI read of the notes, so only call it when the person asks. A matching: “not_available” answer means this person is not on the property matching list: say so. A reason: “no_wishes” answer means the notes do not say what the client wants yet: say what is missing.
| Parameter | Type | What it does |
|---|---|---|
lead_key (required) |
string | — |
complete_followup
Section titled “complete_followup”Mark a reminder as done (optionally logging what happened as a note on the lead).
| Parameter | Type | What it does |
|---|---|---|
followup_id (required) |
string | — |
outcome_note |
string | If given, appended to the lead’s comments. |
acknowledge_nudges
Section titled “acknowledge_nudges”Mark nudges as delivered after showing them to the person (list_nudges is a pure read). Only pending nudges change.
| Parameter | Type | What it does |
|---|---|---|
nudge_ids (required) |
array | nudge_id values from list_nudges (max 100). |
dismiss_nudge
Section titled “dismiss_nudge”The person does not want this nudge (e.g. already handled it elsewhere).
| Parameter | Type | What it does |
|---|---|---|
nudge_id (required) |
string | — |
snooze_nudge
Section titled “snooze_nudge”Bring this nudge back later (default 24 hours).
| Parameter | Type | What it does |
|---|---|---|
nudge_id (required) |
string | — |
hours |
number | — |
send_whatsapp_message
Section titled “send_whatsapp_message”Send ONE message to a lead from the agent’s own WhatsApp number, only after the agent explicitly approved this exact text in this conversation. Logs it on the lead in the CRM, closes the nudge it answers, and sets the next reminder (default 3 days; 0 for none). Repeating a call with the same idempotency_key within 24 hours returns the first result and sends nothing. Never claim a message was sent unless sent is true.
| Parameter | Type | What it does |
|---|---|---|
lead_key (required) |
string | — |
text (required) |
string | The exact text the agent approved. |
idempotency_key (required) |
string | A value unique to this approval (e.g. the chat message id of the approval). Reuse it if you retry. |
nudge_id |
string | The nudge this send answers, if any. |
next_follow_up_days |
integer | Default 3. 0 = no reminder. |
This page is generated from the server source. If a tool here looks wrong, the
source is the truth — see supabase/functions/mcp/server.ts.

