Qobrix CRM MCP Server
Qobrix CRM MCP Server is a read-only MCP layer with 64 tools that lets AI clients query live Qobrix real-estate CRM data across listings, leads, viewings, offers, contracts, activity, analytics, audit, caching, and identity.
Browse, get, and search CRM entities: properties, projects, contacts, agents, opportunities/leads, viewings, tasks, media, offers, contracts, calls, meetings, and email messages.
Perform relevance-ranked search with hard
searchfilters plus weightedboost[]ranking across most resources.Run analytics:
qobrix_count,qobrix_top_values,qobrix_top_records, andqobrix_aggregatewith grouping.Generate reports: time series with YoY, sales funnel, rep scorecard, stale leads, win/loss, days-on-market, and customer cohorts.
Use
qobrix_dealsas a flexible contracts shortcut for sales, rentals, listings, and pipeline reservations.Resolve relationships via
include[], FK search, or related-record tools; maps to RESO Data Dictionary 2.0 workflows.Discover schemas, field options/enums, and full search DSL help before composing queries.
Audit changes: record change log, cross-resource change search, field-level history, and top field changers.
Inspect and clear response cache with
qobrix_cache_statsandqobrix_cache_clear.Check identity/session with
qobrix_whoami; sign in/out in OAuth modes.Connect through stdio, HTTP
api_key, signed-header OAuth elicitation, or remote MCP OAuth for Claude.ai/Dust.tt/Cursor.All tools are read-only GETs; optional Redis-backed caching reduces API load.
Provides optional Redis-backed response caching for improved performance, along with cache management tools (stats and clear).
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Qobrix CRM MCP Server search for active listings under $500k in zip 10001"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Table of contents
Installation Guide — Sharp Matrix intranet, pm2, Apache, Claude.ai + Dust.tt connectors
User Guide — none → api_key → the signed-header path → oauth_user (Claude.ai + Dust.tt) step-by-step
What It Does
An AI assistant connected to this server can browse properties, qualify leads, track showings, review offers and contracts, audit follow-up activity, and discover CRM field schemas — all through natural language. Every tool description teaches the LLM which canonical real-estate workflow it belongs to, which RESO resource it maps to, and which tools to chain next.
Who it is for
Brokerages & developers using Qobrix who want Claude.ai, Dust.tt, ChatGPT, or Cursor to answer questions grounded in live CRM data (not copy-pasted exports).
Engineers wiring MCP into internal tools: stdio transport, typed Zod inputs, and no write surface — safe to experiment with prompts and agents.
Data & operations teams running dashboards: use
qobrix_count/qobrix_top_valuesfor YoY-style metrics without custom scripts, and response caching to cut API load on repeat queries.Enterprise IT ready for per-agent identity: run
noneorapi_keyfrom this package, then pair the signed-header path with SharpSir’s Enterprise OAuth (SSO) product when every user must authenticate as themselves — see Enterprise OAuth.
Canonical RE Workflows
The server is organized around six RESO-aligned business processes. The LLM receives these as built-in instructions so it can navigate the CRM without prior training.
# | Workflow | RESO Mapping | Key Tools |
1 | Listing Lifecycle |
|
|
2 | Lead-Contact Lifecycle |
|
|
3 | Sales Pipeline | 8-stage buyer journey |
|
4 | Showing / Viewing |
|
|
5 | Transaction / Offer |
|
|
6 | Activity / Follow-up | Engagement tracking |
|
Status Mappings
Qobrix Property Status | RESO StandardStatus |
| Active |
| Pending / Under Contract |
| Closed |
| Withdrawn / Canceled |
Qobrix Opportunity Status | RESO Lead Funnel |
| MQL / Raw Lead |
| SQL / Active |
| Closed Won |
| Lost |
Tools at a Glance
64 tools — CRM entities, schema discovery, analytics (qobrix_count, qobrix_top_values, qobrix_top_records, qobrix_aggregate), a flexible deals shortcut (qobrix_deals), reporting (qobrix_timeseries, qobrix_funnel, qobrix_rep_scorecard, qobrix_stale_leads, qobrix_win_loss, qobrix_days_on_market), customer intelligence (qobrix_cohort), audit / change history (qobrix_get_changes, qobrix_search_changes, qobrix_field_change_history, qobrix_top_field_changers), cache helpers (qobrix_cache_stats, qobrix_cache_clear), and session & identity (qobrix_whoami):
Entity Group | Tools | Capabilities |
Properties | 5 | List, Get, Search, Coordinates (map), Properties-by-Lead |
Contacts | 3 | List, Get, Search |
Agents | 3 | List, Get, Search |
Opportunities / Leads | 5 | List, Get, Search, Leads-by-Property, Lead-Properties |
Property Viewings | 3 | List, Get, Search |
Tasks | 3 | List, Get, Search |
Media | 2 | List (with entity filter), Get (with size variants) |
Projects | 4 | List, Get, Search, Coordinates |
Offers | 3 | List, Get, Search |
Contracts | 3 | List, Get, Search |
Calls | 2 | List, Get |
Meetings | 2 | List, Get |
Email Messages | 2 | List, Get |
Schema / Meta | 3 | Get Schema (field discovery), Get Field Options (enum values), Search DSL Help (full grammar + cheatsheets) |
Analytics | 4 | Counts, top-N field values, full-scan top-N records by numeric/date, and sum/avg/min/max/count aggregates (with single- or multi-dim grouping). Prefer list/search |
Deals | 1 | Flexible domain shortcut over the Contracts table (sales, rentals, listings, pipeline) with kind / contract_types[] / contract_statuses[] / date_field / min_price / party filters / summary block |
Reporting | 6 | Time-series with YoY ( |
Customers | 1 | Repeat-buyer / seller / lead cohorts ( |
Audit | 4 | Per-record change log ( |
Cache | 2 | Stats and prefix or full invalidation for fresher reads |
Session & identity | 1 | Current user profile ( |
Every tool description includes its canonical workflow role, RESO equivalent, verified include[] options, FK resolution guidance, and search expression examples.
Analytics & Deals usage examples
Server-side sort (OpenAPI sort[]) works for most fields — e.g.
sort: "-list_selling_price_amount" on properties. Use qobrix_top_records /
qobrix_aggregate when you need a full-dataset scan, or when a nullable
field (e.g. opportunities.budget) returns no rows under server sort.
"Closed deals" don't live as a property flag — they're rows in the Contracts
table. The analytics/deals tools remove the need for client-side scripting:
// 1) Top 5 closed 2026 sales, sorted by final_selling_price_amount,
// with property + agent + lawyers resolved to readable names.
{
"tool": "qobrix_top_records",
"args": {
"resource": "contracts",
"sort_by": "final_selling_price_amount",
"search": "contract_type == \"cos\" and contract_status == \"agreed\" and date_of_contract >= \"2026-01-01\" and date_of_contract < \"2027-01-01\"",
"top": 5
}
}
// 2) 2026 sales volume, plus an agent leaderboard in one extra call.
{
"tool": "qobrix_aggregate",
"args": {
"resource": "contracts",
"field": "final_selling_price_amount",
"op": "sum",
"search": "contract_type == \"cos\" and contract_status == \"agreed\" and date_of_contract >= \"2026-01-01\" and date_of_contract < \"2027-01-01\"",
"group_by": "commission_to_2",
"top": 10
}
}
// 3) Flexible "deals" shortcut — same answer as (1) with one default-laden call,
// plus a full-set summary block (by_status, by_type, totals, median).
{ "tool": "qobrix_deals", "args": { "year": 2026, "top": 5 } }
// 4) Best 2026 rental contracts by final rental price.
{ "tool": "qobrix_deals", "args": { "kind": "rental", "year": 2026, "top": 5 } }
// 5) Under-contract reservations + closed sales together (pipeline + actuals).
{
"tool": "qobrix_deals",
"args": { "contract_statuses": ["reserved", "agreed"], "year": 2026 }
}
// 6) "My deals this year": uses the CURRENT_USER special var.
{
"tool": "qobrix_deals",
"args": { "assigned_to": "CURRENT_USER", "year": 2026 }
}
// 7) Monthly 2026 closed-sale volume with prior-year YoY %.
{
"tool": "qobrix_timeseries",
"args": {
"resource": "contracts",
"bucket": "month",
"metric": "sum",
"field": "final_selling_price_amount",
"year": 2026,
"search": "contract_type == \"cos\" and contract_status == \"agreed\"",
"compare_to_prior": true
}
}
// 8) Full 2026 sales funnel (Leads → Qualified → Viewing → Offer → Reserved → Closed).
{ "tool": "qobrix_funnel", "args": { "year": 2026 } }
// 9) 2026 agent leaderboard by volume (omit `user` for leaderboard mode).
{ "tool": "qobrix_rep_scorecard", "args": { "year": 2026, "sort_by": "volume", "top": 10 } }
// 10) Silent leads — open opportunities with no activity in 30 days.
{ "tool": "qobrix_stale_leads", "args": { "since_days": 30 } }
// 11) Multi-dim pivot: 2026 closed-sale volume by city × property_type.
{
"tool": "qobrix_aggregate",
"args": {
"resource": "contracts",
"field": "final_selling_price_amount",
"op": "sum",
"search": "contract_type == \"cos\" and contract_status == \"agreed\" and date_of_contract >= \"2026-01-01\" and date_of_contract < \"2027-01-01\"",
"group_by": ["property_id", "contract_type"],
"top": 10
}
}
// 12) Repeat buyers — contacts behind 2+ closed sales in 2026.
{ "tool": "qobrix_cohort", "args": { "kind": "buyers", "year": 2026, "min_count": 2 } }
// 13) Win-rate by lead source in 2026, with top loss reasons resolved.
{
"tool": "qobrix_win_loss",
"args": { "year": 2026, "group_by": "source", "include_top_losses": true }
}
// 14) 2026 days-on-market by property type, with longest/shortest outliers.
{
"tool": "qobrix_days_on_market",
"args": { "kind": "sold", "year": 2026, "group_by": "property_type", "include_outliers": true }
}Quick Start
git clone https://github.com/gca-ltd/qobrix-crm-mcp.git
cd qobrix-crm-mcp
npm install
npm run buildConfiguration
Create a .env file in the project root:
QOBRIX_API_URL=https://yourcrm.qobrix.com
QOBRIX_API_USER=your-api-user-uuid
QOBRIX_API_KEY=your-api-key
QOBRIX_LOCALE=en-US # optionalVariable | Required | Description |
| Yes (none) | Qobrix instance base URL |
| Yes (none) |
|
| Yes (none) |
|
| No |
|
Auth modes
Clone this package, run none or api_key, and put live Qobrix data in front of Claude, Cursor, or any MCP client — Apache 2.0.
Auth type | In this package? | When | How credentials arrive |
none (default) | Yes |
| Shared |
api_key | Yes |
|
|
signed-header (legacy) | Needs companion AS |
| Self-service OAuth: MCP returns a |
oauth_user | Needs companion AS |
| Remote MCP OAuth (RFC 9728 PRM + Bearer on |
none and api_key are fully supported out of this package. The signed-header path and oauth_user require SharpSir’s separate Enterprise OAuth / SSO product — not distributed as part of this repo. oauth_user does not change none, api_key, or the signed-header path — select it when you want remote hosts such as Claude.ai or Dust.tt to drive OAuth themselves.
Enterprise OAuth
Need the agent to work as a signed-in Qobrix user — not a shared API key? the signed-header path is designed for that. It requires SharpSir’s Enterprise OAuth solution: a hosted Authorization Server bundle (login + 2FA + consent, per-user API-key minting, encrypted credential vault, audience-bound tokens) that pairs exclusively with this MCP server.
How the signed-header path works (MCP self-auth — northbound clients unchanged):
A tool runs with no session → the MCP returns an authorization URL:
URL-mode elicitation (
JSON-RPC -32042) when the client supportselicitation.url(Claude, Cursor, etc.)A Markdown
[Sign In to Qobrix](/connect?e=…)link in the tool result for clients without elicitation (e.g. ragchat / LangChain) — the LLM must relay it verbatim (unique / single-use; never reuse an older link)
The user opens
/connecton this server (anti-phishing indirection) → signed cookie + redirect to the Enterprise OAuth login pageAfter login + 2FA + consent, the AS redirects to
/oauth/callback; this MCP exchanges the code (PKCE), introspects for Qobrix credentials, and stores them in an encrypted session vaultThe next tool call runs authenticated. On Qobrix
401/403, the vault is cleared and a fresh/connectURL is returnedAgents call
qobrix_whoamito confirm the signed-in account. Sign-in is the client's OAuth flow, not a tool.
Not available as a public download and not something you can clone from GitHub.
Delivered and configured by our team upon request as an enterprise solution bundle.
No third-party OAuth servers — the signed-header path is hard-wired to this Enterprise OAuth solution only.
Security: the signed-header path uses per-user encrypted session vaults (keyed by chat identity headers) and leaves
/mcpwithout a client bearer. BindQOBRIX_MCP_HOST=127.0.0.1and setQOBRIX_MCP_IDENTITY_SECRET(shared only with the trusted MCP host like ragchat) so identity headers cannot be forged. Keep vault encryption onQOBRIX_MCP_STATE_SECRET(MCP-only). If you reverse-proxy for browsers, publish only/connectand/oauth/callback— deny public/mcpand/health. Local agents (ragchat) callhttp://127.0.0.1:<port>/mcp. WhenALLOWED_HOSTSlists only the public hostname, loopback Host values (127.0.0.1/localhost/::1) are auto-added if the server binds to loopback. Connect cookiePathfollowsPUBLIC_URLpathname; Expresstrust proxyis2behind Cloudflare→Apache. Deliver/connectlinks only to the individual user — never into a shared/group thread.
Ready to upgrade? Contact SharpSir Group · dev@sharpsir.group and ask for the Qobrix CRM MCP Enterprise OAuth bundle.
Once delivered, you point this server at the issuer you receive:
export QOBRIX_MCP_TRANSPORT=http
export QOBRIX_MCP_AUTH=oauth
export QOBRIX_MCP_HOST=127.0.0.1
export QOBRIX_MCP_PORT=3502
export QOBRIX_MCP_PUBLIC_URL=http://127.0.0.1:3502
export QOBRIX_MCP_RESOURCE_URL=http://127.0.0.1:3502/mcp
export QOBRIX_OAUTH_ISSUER=<issuer-from-enterprise-bundle>
export QOBRIX_OAUTH_INTROSPECTION_SECRET=<shared-secret-from-bundle>
export QOBRIX_MCP_STATE_SECRET=<16+-char-secret>
export QOBRIX_MCP_IDENTITY_SECRET=<16+-char-secret-shared-with-ragchat>
export QOBRIX_MCP_DATA_DIR=./data/mcp-oauth
export QOBRIX_MCP_ALLOWED_HOSTS=qobrix-mcp.example.com # loopback Hosts auto-added when HOST is 127.0.0.1
npm startthe signed-header path endpoints (after the Enterprise OAuth solution is paired):
GET /connect?e=…— start authorization (sets cookie, 302 to AS)GET /oauth/callback— PKCE code exchange + per-user session vault writeGET /health— includesconnectedandsession_vaultscountUnauthenticated
/mcpis intentional for northbound clients: tools surface the connect URL when needed — keep/mcpon localhost in production
See docs/USER_GUIDE.md for none → B → C step-by-step, reverse-proxy lockdown, and Host allowlist details.
For ragchat / the signed-header path, register the remote MCP URL (…/mcp) as a normal Streamable HTTP server (no client-side OAuth provider required); the MCP handles auth via /connect. Keep /mcp on localhost in that topology.
oauth_user — Claude.ai and Dust.tt remote MCP (shared resource)
Use a separate MCP process (or host) with QOBRIX_MCP_AUTH=oauth_user. Remote hosts drive OAuth themselves against the same HTTPS /mcp URL:
Host | How to connect | Auth |
Claude.ai / Claude Desktop | Settings → Connectors → Add custom connector | Automatic DCR + PKCE (redirect |
Spaces → Tools → Add MCP Server | Prefer Automatic; Static OAuth fallback — see INSTALL — Connect Dust |
User pastes
https://intranet.sharpsir.group/qobrix-crm/mcpinto Claude or DustHost hits
/mcp→ receives401+WWW-Authenticate: Bearer resource_metadata=…Host fetches
/.well-known/oauth-protected-resource→ discoversQOBRIX_OAUTH_ISSUERHost completes OAuth (DCR or Static) + PKCE against the Enterprise OAuth AS
Subsequent
/mcpcalls sendAuthorization: Bearer <access_token>; this server introspects and runs tools as that Qobrix user
Claude and Dust share one oauth_user stack (same MCP resource + same Authorization Server). Each host registers its own OAuth client; each member logs into Qobrix as themselves.
export QOBRIX_MCP_TRANSPORT=http
export QOBRIX_MCP_AUTH=oauth_user
export QOBRIX_MCP_HOST=127.0.0.1
export QOBRIX_MCP_PORT=3502
export QOBRIX_MCP_ALLOWED_HOSTS=intranet.sharpsir.group
export QOBRIX_MCP_PUBLIC_URL=https://intranet.sharpsir.group/qobrix-crm
export QOBRIX_MCP_RESOURCE_URL=https://intranet.sharpsir.group/qobrix-crm/mcp
export QOBRIX_OAUTH_ISSUER=https://intranet.sharpsir.group/qobrix-crm/mcp-oauth
export QOBRIX_OAUTH_INTROSPECTION_SECRET=<shared-secret-from-bundle>
npm startOn the AS, when using a redirect allowlist, keep Claude’s callback and append exact Dust finalize URLs (never replace Claude’s entry). Cursor also needs https://www.cursor.com/agents/mcp/oauth/callback in addition to cursor:// (DCR sends both plus http://localhost:8787/callback):
export QOBRIX_OAUTH_REDIRECT_ALLOWLIST=https://claude.ai/api/mcp/auth_callback,http://127.0.0.1,http://localhost,cursor://,https://www.cursor.com/agents/mcp/oauth/callback,https://eu.dust.tt/oauth/mcp/finalize,https://eu.dust.tt/oauth/mcp_static/finalize,https://dust.tt/oauth/mcp/finalize,https://dust.tt/oauth/mcp_static/finalize,https://app.dust.tt/oauth/mcp/finalize,https://app.dust.tt/oauth/mcp_static/finalizePublish HTTPS /mcp + PRM (and the AS) to the public internet; allowlist Anthropic egress 160.79.104.0/21 if WAF’d, and allow Dust egress in addition — do not remove Claude’s allowlist. the signed-header path’s loopback/deny public /mcp guidance stays valid for ragchat deployments — do not flip that topology for the signed-header path processes.
Full steps: INSTALL — Connect Claude · INSTALL — Connect Dust · Dust: Adding an MCP Server.
Caching
All MCP tools are read-only GETs, so a response cache cannot corrupt CRM state. The server wraps one chokepoint (QobrixClient.request()) with a read-through cache, so every list/get/search/schema call — including each page of a relevance max_scan — is cached. Boost scoring is post-fetch and does not change the cache key, so re-ranking with different boost[] reuses the same candidate pages.
Design — cache-aside with single-flight coalescing:
Tier 1 — in-memory LRU (always on, zero deps): per-process, TTL'd, size-capped.
Tier 2 — Redis (optional, lazy-loaded via dynamic
import()): setQOBRIX_REDIS_URLto enable; the server falls back to memory-only on any Redis error.Single-flight: when the LLM fires parallel tool calls that hit the same cold cache key (common with
qobrix_top_values), all in-process callers share one upstream fetch.Errors are never cached — a transient 5xx will not get stuck.
TTL only, no stale-while-revalidate in v1.
Environment variables:
Variable | Default | Description |
|
| Set to |
|
| TTL in seconds; CRM edits visible within this window |
|
| LRU cap for the in-memory tier |
|
|
|
|
| Namespace when sharing a Redis instance |
Cache tools (exposed to the LLM):
Tool | Use |
| Hits/misses/size/in-flight/Redis status — verify the cache is paying off |
| Invalidate all keys or by |
Recommended Redis server config (for a dedicated cache-only Redis, per Redis docs):
maxmemory 256mb
maxmemory-policy allkeys-lru
maxmemory-samples 10TTL guidance — Redis docs recommend short TTLs for frequently-changing data (60–120s) and longer for stable data (hours). 300s is a conservative default for a CRM that mixes lead pipeline (changes minutely) with property listings (changes hourly). Use qobrix_cache_clear when you need an instant refresh.
Trade-off / known limit: Single-flight coalescing is in-process only. Multi-instance deployments behind one shared Redis can still see modest stampede on cold keys; a distributed SETNX lock is future work and not needed for single-user MCP clients.
Best-practices alignment:
Best practice | Where honored |
Cache-aside / read-through (Redis docs, MCP caching guides) |
|
Canonical, versioned cache key |
|
Conservative TTL |
|
Errors not cached | Wrap stores only on resolved upstream success |
Single-flight stampede prevention | In-process |
| Documented above for self-hosters |
Observability + manual invalidation |
|
Official Node.js Redis client |
|
Cursor IDE setup
none is a local stdio process. oauth_user is the hosted URL
https://intranet.sharpsir.group/qobrix-crm/mcp (same resource Claude and Dust
use). For oauth_user, see INSTALL — Connect Cursor.
This stdio path uses a local node process. Cursor discovers servers from
project or user mcp.json: .cursor/mcp.json
inside the folder you opened, or ~/.cursor/mcp.json for all workspaces.
1. Prerequisites
Node.js 20+ on the machine where Cursor runs the MCP (local laptop or remote SSH host).
Clone this repo, install, and build (see Quick Start).
dist/index.jsmust exist (npm run build) before adding the MCP entry.
2. Credentials
Copy the template:
cp .env.example .envEdit
.envand set at leastQOBRIX_API_URL,QOBRIX_API_USER, andQOBRIX_API_KEY(see Configuration).Keep
.envout of git; it is listed in.gitignore.
3. Where to put the JSON
Location | When to use |
| You opened that project folder in Cursor; teammates can commit a template (without secrets) or you keep it local-only. |
| Same MCP on every workspace on that machine. |
Merge your entry into the existing "mcpServers" object; do not replace the whole file if you already have other servers.
4. Recommended: node --env-file (Node 20+)
Pass absolute paths so it works the same whether the workspace root is this repo or a parent folder (and so SSH remote paths resolve correctly).
{
"mcpServers": {
"qobrix-crm-mcp": {
"command": "node",
"args": [
"--env-file=/absolute/path/to/qobrix-crm-mcp/.env",
"/absolute/path/to/qobrix-crm-mcp/dist/index.js"
],
"description": "Read-only Qobrix CRM MCP"
}
}
}Why this pattern:
Credentials stay in
.env, not in JSON.Node loads the file before your server starts, so
process.envis populated even when the host’senvFilefield is ignored or behaves inconsistently for stdio servers.
5. Alternative: inline env
Useful if you cannot use --env-file (older Node). Secrets live in mcp.json — restrict file permissions and do not commit them.
{
"mcpServers": {
"qobrix-crm-mcp": {
"command": "node",
"args": ["/absolute/path/to/qobrix-crm-mcp/dist/index.js"],
"env": {
"QOBRIX_API_URL": "https://yourcrm.qobrix.com",
"QOBRIX_API_USER": "your-api-user-uuid",
"QOBRIX_API_KEY": "your-api-key",
"QOBRIX_LOCALE": "en-US"
}
}
}
}You can also use Cursor’s config interpolation (for example ${env:QOBRIX_API_KEY}) so values are injected from your OS environment instead of literals.
6. Optional: envFile in MCP JSON
Cursor supports an envFile property for stdio servers. Some setups do not pass those variables into the child process reliably; if tools fail with “Missing required environment variables”, switch to --env-file as in step 4.
7. After editing mcp.json or .env
Reload MCP — Command Palette → MCP restart, or reload the Cursor window.
Check logs — View → Output → pick “MCP” / “MCP Logs” in the dropdown; fix path or Node errors there.
Tool approval — By default Cursor asks before each tool call; you can allow auto-run for trusted tools in Cursor settings if you prefer.
Other MCP hosts
Claude.ai / Claude Desktop (oauth_user) — remote custom connector at https://intranet.sharpsir.group/qobrix-crm/mcp. See oauth_user and INSTALL — Connect Claude.
Dust.tt (oauth_user) — Spaces → Tools → Add MCP Server with the same URL. Prefer Automatic auth and Personal accounts. See INSTALL — Connect Dust.
Claude Desktop / Cursor (none stdio) — same stdio shape: command + args to node and either --env-file or env in the host’s MCP config file.
CI / headless — run node --env-file=.env dist/index.js with a stdio MCP client library; ensure .env is supplied via secrets, not committed.
Search Expression Syntax
Tools that accept a search parameter use Qobrix's Symfony Expression Language (OpenAPI SearchExpression). Call qobrix_search_dsl_help for the full grammar + property/project field cheatsheets (optionally with live schema field names).
Feature | Syntax | Example |
Equality |
|
|
Comparison |
|
|
Contains |
|
|
Set membership |
|
|
Range |
|
|
Logical |
|
|
Date helpers |
|
|
Time shortcuts |
|
|
Current user |
|
|
Geo / misc |
|
|
Association path |
|
|
Tip: Call
qobrix_search_dsl_help({ resource: "Properties" })before composing free-language demand into a query. Useqobrix_get_field_optionsfor enum values andqobrix_get_schemafor the full field list.
Relevant search on all resources (F1)
Every qobrix_search_* tool (properties, projects, contacts, agents, opportunities, viewings, tasks, offers, contracts) uses a two-tier design so free-language demand maps to high precision and high recall:
search— hard must-haves (server-side DSL filter → precision floor).boost[]— soft weighted nice-to-haves scored in-process over a candidate pool (recall + ranking).limit— how many ranked rows to return (default 10, max 100). Raise for more options; keep modest to avoid context overload.max_scan— candidate pool when boosting (default 100, hard cap 500). Higher improves recall; each scanned page is response-cached.
With boost, each row includes _relevance (score) and _matched (which clauses hit); pagination.mode is "ranked". Without boost, a single cached list page is returned (mode: "fast").
qobrix_search_properties({
search: 'status == "available" and sale_rent == "for_sale"',
boost: [
{ field: "sea_view", op: "==", value: true, weight: 3 },
{ field: "bedrooms", op: ">=", value: 3, weight: 2 },
{ field: "list_selling_price_amount", op: "in", value: "200000..600000", weight: 2 },
],
limit: 15,
max_scan: 200,
});Lead ↔ listing matching via search (2-way)
Demand → supply: take a lead's criteria →
qobrix_search_properties/qobrix_search_projectswithsearch+boost. Native:qobrix_get_properties_by_lead/qobrix_get_lead_properties.Supply → demand:
qobrix_search_opportunitieswith open-leadsearch+boostagainst the listing (works for projects too). Native for properties only:qobrix_get_leads_by_property.
// Who wants a Limassol 3-bed ~€400k listing?
qobrix_search_opportunities({
search: 'status in ["new","open"] and buy_rent == "buy"',
boost: [
{ field: "area_of_interest", op: "contains", value: "Limassol", weight: 3 },
{ field: "bedrooms_from", op: "<=", value: 3, weight: 2 },
{ field: "list_selling_price_to", op: ">=", value: 400000, weight: 2 },
],
limit: 15,
max_scan: 200,
});Boost operators: == != < > <= >= in contains starts_with ends_with. For ranges use op: "in" with value: "min..max".
Search (and every other list/get) shares the global cache TTL (QOBRIX_CACHE_TTL, default 300s). After CRM edits, refresh with qobrix_cache_clear({ prefix: "v1:request:properties" }) (or opportunities, projects, …).
Fetching Related Data
Three strategies to resolve foreign keys:
include[]parameter — expand associations inline in one call
qobrix_get_property({ id: "...", include: ["Agents", "PropertyViewings"] })Separate get call — take the UUID from an FK field and call the appropriate tool
// property.agent → UUID
qobrix_get_agent({ id: "<agent-uuid>" })Search by FK — find related records via search expression
qobrix_search_properties({ search: 'agent == "<agent-uuid>"' })Only include[] values marked Verified in tool descriptions are guaranteed to work. When include[] is unavailable for an association, use search-by-FK.
Payload defaults
To keep tool outputs short enough for the calling LLM's context window, list / search / get tools default to compact payloads:
Param | Default | Effect when default |
|
| Foreign keys come back as UUID strings instead of being expanded into nested objects. Resolve them on demand with the matching get tool or with a targeted |
|
| Inline media (photos, floor plans, thumbnail URLs) is not attached to list rows. Use |
Override per call only when the caller actually needs the heavier payload:
// Cheap browse — recommended for most reporting / pipeline calls
qobrix_list_properties({ limit: 10 });
// Heavy detail — only when the LLM truly needs nested FKs + media URLs
qobrix_list_properties({ limit: 5, expand: true, media: true });
// Prefer surgical include[] over full expand=true:
qobrix_get_property({ id: "...", include: ["AgentAgents", "ProjectProjects"] });This change typically shrinks qobrix_list_properties({ limit: 10 }) from ~300 KB to ~5–10 KB.
Output cap
Every tool result is capped at QOBRIX_MCP_MAX_RESULT_CHARS characters of rendered JSON (default 30 000, roughly 7.5 K tokens). Behaviour:
Paginated payloads (
{ data: [...], pagination: {...} }): truncated to the largest prefix ofdata[]that fits, and a_truncatedblock is attached withkept_rows,omitted_rows,original_chars,max_chars, and ahinttelling the LLM how to scope the next call. If nested expand/media objects alone blow the cap, rows are compacted to scalars (_truncated.compacted: true) so at least one usable row is returned.Grossly oversized (default: original size
> 8 ×the cap, overrideQOBRIX_MCP_REFINE_MULTIPLIER): returnsstatus: "result_too_large"with_refine_required(assistant instruction + suggested narrowing + smallreturned_sample) so the LLM asks the user to reformulate — not dump.Non-paginated payloads (single
get, custom analytic shapes): the JSON is clipped at the cap and aQOBRIX_MCP TRUNCATEDtrailer is appended (or the same refine directive when grossly oversized).
When boost is used with expand=true or media=true, max_scan is auto-capped at 100 and pagination.scan_capped_reason may be "expand/media".
Override the cap / refine threshold:
QOBRIX_MCP_MAX_RESULT_CHARS=60000
QOBRIX_MCP_REFINE_MULTIPLIER=8If you regularly hit the cap or refine guard, use fields[] (whitelist columns), a tighter search expression, a smaller limit, or keep expand=false / media=false.
Testing
The project includes 226 automated tests across 63 describe suites (integration, multi-step scenarios, RESO workflows, cache, relevance, output-cap, client-sort, and OAuth mode smoke):
# Integration tests — individual tool mechanics
npm test
# Scenario tests — multi-step tool chains (19 real-world scenarios)
npm run test:scenarios
# Workflow tests — canonical RE business processes (8 RESO-aligned suites)
npm run test:workflows
# Cache tests — read-through, single-flight, LRU eviction, search-page keys (no API needed)
npm run test:cache
# Relevance tests — boost scoring, DSL help, search cache keys (no API needed)
npm run test:relevance
# Format tests — output cap + truncation behaviour (no API needed)
npm run test:format
# OAuth modes smoke — api_key header rejection + the signed-header path /connect elicitation path
npm run test:oauth-modes
# Run everything
npm run test:allSuite | Tests | Coverage |
Integration | 70 | Every tool, pagination edge cases, include/fields mechanics, analytics + reporting tools |
Scenarios | 55 | Agent morning brief, buyer search, lead triage, FK chains, pipeline reports |
Workflows | 39 | Listing lifecycle, lead funnel, sales pipeline, showing, transaction, media, activity, schema |
Cache | 22 | Read-through cache, single-flight coalescing, LRU eviction, key canonicalization, search-page keys (no live API) |
Relevance | 23 | Boost eval/score/rank (incl. opportunity/contact shapes), fields[]+boost union, DSL help text, search cache-key stability (no live API) |
Format | 7 |
|
Client sort | 7 |
|
OAuth modes | 4 | api_key headers, the signed-header path |
Architecture
src/
├── index.ts # MCP server entry point + RESO workflow instructions
├── http.ts # Streamable HTTP transport (api_key, oauth_user, signed-header)
├── auth-types.ts # Public auth types: none, api_key, oauth_user
├── client.ts # QobrixClient — HTTP + read-through response cache
├── auth-context.ts # AsyncLocalStorage per-request credentials
├── oauth-client.ts # the signed-header path self-service OAuth client + session vault
├── oauth-rs.ts # Companion AS metadata + introspection helpers
├── request-context.ts# ALS for McpServer (elicitation capability detection)
├── cache.ts # LRU memory tier, optional Redis, single-flight coalescing
├── relevance.ts # Boost scoring + cached candidate pager for search
├── search-dsl.ts # Full SearchExpression DSL reference + field cheatsheets
├── types.ts # TypeScript interfaces
├── schemas.ts # Zod schemas with rich LLM-facing descriptions
└── tools/
├── index.ts # Tool registration hub + formatResult / errorResult
├── properties.ts # Listing Lifecycle + relevance search
├── contacts.ts # Lead-Contact Lifecycle tools
├── agents.ts # RESO Member tools
├── opportunities.ts # Sales Pipeline tools
├── viewings.ts # Showing Lifecycle tools
├── tasks.ts # Follow-up & Pipeline Management tools
├── media.ts # Media Lifecycle tools
├── projects.ts # Project/Development + relevance search
├── offers.ts # Transaction Lifecycle tools
├── contracts.ts # Transaction close tools
├── activities.ts # Activity Tracking (calls, meetings, emails)
├── analytics.ts # qobrix_count, qobrix_top_values, qobrix_top_records, qobrix_aggregate
├── deals.ts # qobrix_deals (flexible Contracts shortcut)
├── reports.ts # qobrix_timeseries (bucketed metric + YoY), qobrix_days_on_market
├── pipeline.ts # qobrix_funnel, qobrix_stale_leads, qobrix_win_loss
├── productivity.ts # qobrix_rep_scorecard
├── customers.ts # qobrix_cohort (repeat buyers/sellers/leads)
├── cache.ts # qobrix_cache_stats, qobrix_cache_clear
├── audit.ts # change log / field history / top changers
└── meta.ts # Schema discovery + qobrix_search_dsl_help
test-suite/
├── integration.test.mjs # Live API smoke tests
├── scenarios.test.mjs # Multi-step CRM scenarios
├── workflows.test.mjs # RESO workflow coverage
├── cache.test.mjs # Cache unit tests (incl. search-page keys)
├── relevance.test.mjs # Boost scoring + DSL help unit tests
├── format.test.mjs # Output-cap / truncation tests
└── oauth-modes.test.mjs # api_key/C auth smoke testsHow the LLM Learns
The server teaches the LLM at three levels:
Server instructions — top-level
instructionsfield in the MCPinitializeresponse provides the full data model, six canonical workflows with tool recipes, search syntax, FK resolution strategies, and known quirks.Tool descriptions — each tool description includes its canonical workflow role, RESO equivalent, verified
include[]options, FK field mappings, response shape, and search examples. Relevance search tools document the two-tiersearch+boostrecipe;qobrix_search_dsl_helpexposes the full DSL on demand.Parameter descriptions — Zod schemas provide per-parameter help with concrete examples, valid enum values, and cross-tool references.
Technology
Component | Technology |
Runtime | Node.js ≥ 20 |
Language | TypeScript 5.7 |
MCP SDK |
|
Validation | Zod 3.24 |
Optional cache |
|
Transport | stdio (default) · Streamable HTTP ( |
API Auth | none and api_key: |
Testing | Node.js built-in test runner ( |
License
Apache License 2.0 — Copyright 2025–2026 SharpSir Group
none and api_key are included in this open-source package. The signed-header path pairs with SharpSir’s Enterprise OAuth Authorization Server (SSO / per-user identity) — a separate commercial product delivered upon request — sharpsir.group · dev@sharpsir.group.
Available Tools
64 toolsqobrix_aggregateA
Sum / avg / min / max / count of a numeric field across all matching records — optionally grouped by another field (top-N buckets). Paginates server-side (cap 20,000 rows) and computes in-process, which is how to get totals on Qobrix fields the API won't sort. Example uses: Total 2026 closed-sale volume: resource='contracts', field='final_selling_price_amount', op='sum', search='contract_type == "cos" and contract_status == "agreed" and date_of_contract >= "2026-01-01" and date_of_contract < "2027-01-01"'. Agent leaderboard by volume: same search, group_by='commission_to_2', top=5. Average list price by property type: resource='properties', field='list_selling_price_amount', op='avg', group_by='property_type', search='status == "available"'.
Inputs: op (required), field (required), resource (required), top, +3 more.
qobrix_cache_clearA
Invalidate cached Qobrix responses. Use this when CRM data was edited in Qobrix and you need fresh reads before the TTL (default 300s) expires. Without 'prefix': clears the whole cache (memory + Redis). With 'prefix': clears only matching keys, e.g. 'v1:request:opportunities' to refresh lead lists, or 'v1:request:properties' to refresh listings. Returns the number of cleared entries in each tier.
Inputs: prefix.
qobrix_cache_statsA
Inspect the response cache: hits/misses, in-memory size, in-flight coalesced requests, TTL (seconds), and Redis tier status. Use to verify the cache is paying off (high hits, low misses on repeated workflows) or to diagnose why a query feels slow (cold cache, Redis degraded). Stats are process-local; restart resets them.
Inputs: no named parameters.
qobrix_cohortA
Find contacts that appear on multiple deals or opportunities — the 'repeat customer' report every CRM analyst gets asked for. Three modes: 'buyers' (default) walks closed contracts → opportunity_id → opportunities.contact_name to identify the buyer behind each closed sale. 'sellers' walks contracts → property_id → properties.seller. 'leads' groups all opportunities by contact_name regardless of close. Returns each repeat contact with deal_count, total_volume, total_commission, first_deal, last_deal, and a deals[] breakdown. Example uses: 2026 repeat buyers: { kind: 'buyers', year: 2026, min_count: 2 }. All-time loyal customers: { kind: 'buyers', min_count: 3 }. Recurring sellers this year: { kind: 'sellers', year: 2026 }. Frequent enquirers (any close): { kind: 'leads', since_days: 365 }.
Inputs: to, top, from, kind, +5 more.
qobrix_countA
Count records matching a search expression — returns just the total count, no data payload. Faster and lighter than fetching records when you only need a number. Example uses: Total active sale listings: resource='properties', search='status == "available" and sale_rent == "for_sale"'. Q1 closed-won opportunities: resource='opportunities', search='status == "closed_won" and last_status_change >= "2026-01-01" and last_status_change < "2026-04-01"'. Contacts created this month: resource='contacts', search='created >= THIS_MONTH'.
Inputs: resource (required), search.
qobrix_days_on_marketA
Days-on-market analytics. Joins Contracts to Properties on property_id and computes the (close_date - listing_date) duration in days, then aggregates count / mean / median / p75 / p90 / min / max. Defaults: kind='sold' (cos + agreed), listing_date_field='listing_date' with per-row fallback to website_listing_date then created. Use group_by to break down by property_type, city, agent, or commission_to_2 (broker). Set include_outliers=true for the 5 longest and 5 shortest deals. Example uses: Overall DOM for 2026 sales: { kind: 'sold', year: 2026 }. DOM by property type: { kind: 'sold', year: 2026, group_by: 'property_type' }. DOM by city (with outliers): { year: 2026, group_by: 'city', include_outliers: true }. Reservation cycle: { kind: 'reserved', year: 2026 }.
Inputs: to, top, from, kind, +6 more.
qobrix_dealsA
Flexible 'deals' tool over the Contracts table — covers sales, rentals, listing agreements, and pipeline reservations in a single call. Defaults: kind='sale', contract_statuses=['agreed'] (so a no-arg call still means 'closed sale contracts'). Every default is overridable via kind / contract_types / contract_statuses / include_reserved / date_field / min_price / max_price / party filters / raw search. Sorts in-process across the matching set (for a single page of highest prices you can also use list/search with sort='-final_selling_price_amount') and resolves property_id, agent, commission_to, lawyers, bank, etc. to readable names. Example uses: Best 2026 closed sales: { year: 2026, top: 5 }. Best 2026 rentals: { kind: 'rental', year: 2026, top: 5 }. Under-contract pipeline value: { contract_statuses: ['reserved'], year: 2026 }. My deals: { assigned_to: 'CURRENT_USER', year: 2026 }. All revenue deals >€1M last 90 days: { kind: 'any_revenue', since_days: 90, min_price: 1000000 }. Agent leaderboard (by commission): { year: 2026, by: 'commission', top: 10 }.
Inputs: by, to, top, desc, +15 more.
qobrix_field_change_historyA
Build a chronological timeline of edits to one field on one CRM record. Paginates GET /{resource}/{id}/changes and filters rows where the field appears in original or changed. Returns [{timestamp, user_id, user_name?, type, before, after}] sorted oldest-first. Use for: 'who changed source on this opportunity and when?'
Inputs: id (required), field (required), resource (required), max_pages, +1 more.
qobrix_funnelA
Canonical sales funnel in one call. Six stages with conversion %: Leads (opportunities created in window) → Qualified (status in [open,won]) → Viewing (property-viewings created in window) → Offer (offers created in window) → Reserved (contracts with contract_status=reserved) → Closed (contracts cos + agreed). Each stage is scoped by the same date window and (optional) assigned_to / agent. Use stage_overrides to substitute a tenant-specific definition for any stage. Example uses: 2026 funnel: { year: 2026 }. My funnel last 90 days: { assigned_to: 'CURRENT_USER', since_days: 90 }. Agent Vera's funnel: { agent: '', year: 2026 }.
Inputs: to, from, year, agent, +3 more.
qobrix_get_agentA
Get a single agent by UUID — resolve a RESO Member/ListAgent from a listing or lead. Returns { data: { id, ref, ... } }. Verified include: PrimaryContactContacts, User, Brands, AgencyAgents (parent brokerage). Agent 360° pattern: 1. Active listings: qobrix_search_properties with agent == '' and status == "available" 2. Active leads: qobrix_search_opportunities with agent == '' and status == "open" 3. Pipeline deals: qobrix_search_offers / qobrix_search_contracts linked to agent's opportunities 4. Activity: search Tasks, Calls, Meetings by the agent FK
Inputs: id (required), include.
qobrix_get_callA
Get a single call record by UUID — call detail in the Activity Tracking workflow. Returns { data: { id, ... } }. Include: AssignedToUsers, ContactContacts, RelatedOpportunityOpportunities, RelatedAgentAgents, CreatedByUsers.
Inputs: id (required), include.
qobrix_get_changesA
Retrieve audit / change-log entries for a single CRM record (Qobrix LogAudit). Calls GET /api/v2/{resource}/{id}/changes. Each row has original (before), changed (after), user_id (who), timestamp, type (create/update/delete). IMPORTANT: LogAudit.source is the resource name (e.g. 'Opportunities'), NOT the lead marketing source field. Field-level before/after for e.g. lead source are keys inside original and changed objects. For a filtered timeline of one field use qobrix_field_change_history.
Inputs: id (required), resource (required), page, sort, +3 more.
qobrix_get_contactA
Get a single contact by UUID — the person/company detail view in the Lead-Contact Lifecycle. Returns { data: { id, first_name, last_name, email, phone, ... } }. Verified include: AssignedToUsers (RESO OwnerMember), User, Language, Organizations. To build the full contact journey (canonical 360° view): 1. Leads: qobrix_search_opportunities with contact_name == '' 2. Tasks: qobrix_search_tasks with contact == '' 3. Calls: qobrix_search_calls (search by contact FK) 4. Meetings: qobrix_search_meetings (search by contact FK) 5. Seller listings: qobrix_search_properties with seller == '' This contact 360° pattern is the core of Lead-Contact Lifecycle and Follow-up workflows.
Inputs: id (required), include.
qobrix_get_contractA
Get a single contract by UUID — the deal close detail view in the Transaction Lifecycle. Returns { data: { id, ... } }. Verified include: Contacts (buyer/seller), PropertyIdProperties (the listing), OpportunityIdOpportunities (the lead), PaymentInstallments (payment schedule), ContractParties (all signatories), CreatedByUsers. Use include=['PaymentInstallments'] to audit the payment timeline.
Inputs: id (required), include.
qobrix_get_email_messageA
Get a single email message by UUID — email detail in the Activity Tracking workflow. Returns { data: { id, ... } }. Include: Properties, ContactContacts, RelatedOpportunityOpportunities, Agent, Campaign.
Inputs: id (required), include.
qobrix_get_field_optionsA
Get dropdown/enum field options — discover valid values for status fields, types, and categories. Essential for building correct search expressions in any workflow. Returns { data: [...], pagination: { ... } }. Each item has: id, resource, field, label, value. Key examples for canonical workflows: Listing statuses: search='resource == "Properties" and field == "status"' → available/reserved/sold/withdrawn. Lead statuses: search='resource == "Opportunities" and field == "status"' → new/open/won/closed_lost. Property types: search='resource == "Properties" and field == "property_type"'. Task types: search='resource == "Tasks" and field == "task_type"'. Pair with qobrix_get_schema for complete field discovery.
Inputs: page, limit, search.
qobrix_get_lead_propertiesA
Get properties linked to a lead — the 'demand-side matching' tool in the Sales Pipeline. Answers: 'What properties does this buyer want?' by returning properties attached to an opportunity. This is the canonical property suggestion pattern (RESO Demand/Solution matching). Reverse of qobrix_get_leads_by_property (which finds leads for a property). For free-language / ranked demand→supply (incl. projects), use qobrix_search_properties / qobrix_search_projects with search+boost derived from the lead's criteria. In the 8-stage buyer journey, use at the Solution/Viewing stage to prepare showing candidates. The id is the Opportunity UUID.
Inputs: id (required).
qobrix_get_leads_by_propertyA
Get matching leads for a property — the 'supply-side matching' tool in the Sales Pipeline. Answers: 'Who is interested in this listing?' by returning leads whose criteria match the property. This is the canonical buyer-property matching pattern (RESO Prospecting). Use after listing a new property to find potential buyers in the existing lead database. Complement with qobrix_get_lead_properties for the reverse (demand-side) match, or qobrix_search_opportunities with boost for ranked matching (incl. projects). The propertyId is the Property UUID. Returns paginated results.
Inputs: propertyId (required), page, limit.
qobrix_get_mediaA
Get a single media item by UUID — media detail view in the Media Lifecycle. Returns media metadata including URL/path. Optionally specify size variant for images: 'thumbnail', 'medium', 'large'. Use 'thumbnail' for preview grids, 'large' for full-resolution display.
Inputs: id (required), size.
qobrix_get_meetingA
Get a single meeting by UUID — meeting detail in the Activity/Showing Lifecycle. Returns { data: { id, ... } }. Include: AssignedToUsers, ContactContacts, ViewingPropertyViewings (if it wraps a showing), CreatedByUsers.
Inputs: id (required), include.
qobrix_get_offerA
Get a single offer by UUID — offer detail view in the Transaction Lifecycle. Returns { data: { id, ... } }. Include: OpportunityOpportunities (the lead/buyer intent), PropertyProperties (the listing), CreatedByUsers, ModifiedByUsers. Use to trace the deal chain: Offer → Opportunity → Contact + Property → Contract.
Inputs: id (required), include.
qobrix_get_opportunityA
Get a single lead/opportunity by UUID — drill into a pipeline deal in the Sales Pipeline workflow. Returns { data: { id, ref, status, enquiry_type, contact_name, agent, ... } }. RESO mapping: status 'new' = Lead, 'open' = Active/SQL, 'won' = Closed Won, 'closed_lost' = Lost. Verified include: ContactNameContacts (the person), Properties (linked listings), AgentAgents (assigned broker, RESO ListAgent), PropertyTypes, ClosedLostReason, User, OwnerUsers. Canonical deal drill-down pattern: 1. Get the lead with include=['ContactNameContacts','Properties','AgentAgents'] 2. Check showing history: qobrix_list_viewings (search by related property) 3. Check offers: qobrix_search_offers for this lead 4. Check activity: search Tasks, Calls, Meetings linked to this opportunity FK: contact_name → Contacts, agent → Agents, owner → Users.
Inputs: id (required), include.
qobrix_get_projectA
Get a single project by UUID — development detail view for off-plan/new-build listings. Returns { data: { id, ... } }. Verified include: Agents, Developer, Translations, LocationLocations, Assignee, Recommended, Favorites. To see project units: qobrix_search_properties with project == ''. To see project media: qobrix_list_media with related_model='Projects' and related_id=.
Inputs: id (required), include.
qobrix_get_project_coordinatesA
Get lat/lng coordinates for projects, useful for map display. Optionally filter with a search expression.
Inputs: search.
qobrix_get_properties_by_leadA
Get properties explicitly linked to a lead — the 'demand-side view' in the Sales Pipeline. Shows which properties a lead/opportunity has been matched to or expressed interest in. The id parameter is the Opportunity UUID (not the property UUID). In the canonical buyer journey: after Qualification, use this to review Solution/Viewing candidates. Alternative: qobrix_get_opportunity with include=['Properties'] for inline expansion. Complement with qobrix_get_leads_by_property for the reverse (supply-side) match.
Inputs: id (required), page, limit.
qobrix_get_propertyA
Get a single property by UUID — the listing detail view in the Listing Lifecycle. Returns { data: { id, name, status, ... } }. RESO StandardStatus: 'available' = Active, 'reserved' = Pending, 'sold' = Closed, 'withdrawn' = Withdrawn. Use include[] to expand related entities inline: Verified: Agents, PropertyTypes, PropertySubtypes, PropertyViewings, Opportunities, Translations, SalespersonUsers, CreatedByUsers, LocationLocations, AgentAgents, SellerContacts, ProjectProjects. Workflow tips: include=['PropertyViewings'] → see showing history (Showing Lifecycle). include=['Opportunities'] → see interested leads (Sales Pipeline). include=['AgentAgents'] → listing agent (RESO ListAgent). include=['SellerContacts'] → seller/owner (RESO OwnerMember). FK fields: agent → Agents, seller → Contacts, project → Projects, salesperson → Users.
Inputs: id (required), expand, include.
qobrix_get_property_coordinatesA
Get lat/lng coordinates for properties, useful for map display. Optionally filter with a search expression to get coordinates for a subset. Returns array of { id, coordinates } objects.
Inputs: search.
qobrix_get_schemaA
Get the full field schema for any CRM resource — the Schema Discovery tool. CALL THIS FIRST when unsure about field names for search, fields[], or sort. Returns every field with name, type, label, and validation rules (RESO Data Dictionary equivalent). Resources (case-sensitive): Properties, Contacts, Opportunities, Agents, Tasks, PropertyViewings, Projects, Offers, Contracts, Calls, Meetings, EmailMessages, Media, PropertyTypes, PropertySubtypes, PropertyFeatures, Locations. Canonical use: before building search expressions for any workflow (Listing, Sales Pipeline, Follow-up), call this to discover the correct field names. The schema is the source of truth. Pair with qobrix_get_field_options to see allowed enum values for dropdown fields. For search grammar, prefer qobrix_search_dsl_help.
Inputs: resource (required).
qobrix_get_taskA
Get a single task by UUID — task detail for pipeline management and follow-up audit. Returns { data: { id, ... } }. Include: AssignedToUsers (who), ContactContacts (for whom), Properties (which listing), RelatedOpportunityOpportunities (which deal), RelatedAgentAgents, TaskStatus, TaskTypes.
Inputs: id (required), include.
qobrix_get_viewingA
Get a single viewing by UUID — the showing detail view in the Showing Lifecycle. Returns { data: { id, ... } }. Verified include: PropertyViewingViewing. After retrieving, use the property and contact FKs to trace back to the listing and lead.
Inputs: id (required), include.
qobrix_list_agentsB
List agents (RESO Member resource) — real estate brokers managing listings and leads. Agents are the central actor across all workflows: they manage the Listing Lifecycle, drive the Sales Pipeline, and own Follow-up activities. Referenced by property.agent and opportunity.agent FKs. Returns { data: [...], pagination: { count, current_page, has_next_page, ... } }. Verified include: PrimaryContactContacts, User, Brands, AgencyAgents (parent agency). Workflow: to see an agent's full portfolio, search Properties and Opportunities by agent == ''.
Inputs: page, sort, limit, fields, +2 more.
qobrix_list_callsA
List call records — part of the Activity Tracking / Follow-up workflow. Calls are touchpoints in the Lead-Contact Lifecycle; zero tolerance on missed follow-ups. Returns { data: [...], pagination: { count, current_page, has_next_page, ... } }. Include: ContactContacts (who was called), AssignedToUsers (who made the call), RelatedOpportunityOpportunities (which lead), RelatedAgentAgents, RelatedContractContracts. Workflow: use with qobrix_list_meetings and qobrix_list_email_messages for a complete activity timeline. To audit a contact's engagement: search calls, meetings, and emails by their contact UUID.
Inputs: page, sort, limit, search, +1 more.
qobrix_list_contactsA
List contacts (RESO Contacts resource) from Qobrix CRM — the people/companies in the CRM. Core to the Lead-Contact Lifecycle: a Contact becomes a Lead when an Opportunity is created for them. RESO ContactType mapping: use opportunity.enquiry_type and property.seller FK to determine Buyer/Seller/Tenant role. Returns { data: [...], pagination: { count, current_page, has_next_page, ... } }. Key fields (42 total): first_name, last_name, name, email, phone, city, country, assigned_to (UUID → user, RESO OwnerMember), role, is_company, nationality, preferred_language, preferred_contact_method, consent fields, created, modified. Cross-references: property.seller → Contact UUID, opportunity.contact_name → Contact UUID, task.contact → Contact UUID, call/meeting.contact → Contact UUID. Workflow: to see a contact's full journey, search Opportunities, Tasks, Calls, and Meetings by their UUID.
Inputs: page, sort, limit, fields, +3 more.
qobrix_list_contractsA
List contracts (RESO TransactionManagement close) — the 'Deal Signing / Payment / Close' stage of the Sales Pipeline. A Contract finalizes the transaction: it links the Property, buyer/seller Contacts, and the Opportunity. Returns { data: [...], pagination: { count, current_page, has_next_page, ... } }. Verified include: Contacts, PropertyIdProperties, OpportunityIdOpportunities, PaymentInstallments (payment schedule), ContractParties (all signatories), CreatedByUsers. Canonical chain: Offer → Contract → Payment → Close. After contract signing, the property status moves to 'reserved' (Pending), then 'sold' (Closed).
Inputs: page, sort, limit, search, +1 more.
qobrix_list_email_messagesA
List email messages — part of the Activity Tracking / Follow-up workflow and marketing campaigns. Emails are touchpoints in the Lead-Contact Lifecycle for nurturing and communication. Returns { data: [...], pagination: { count, current_page, has_next_page, ... } }. Include: ContactContacts (recipient/sender), Properties (listing references), Campaign (marketing campaign), RelatedOpportunityOpportunities, RelatedAgentAgents. Use with qobrix_list_calls and qobrix_list_meetings for a complete contact engagement timeline.
Inputs: page, sort, limit, search, +1 more.
qobrix_list_mediaA
List media items (RESO Media resource) — photos, documents, floor plans attached to any entity. Core to the Media Lifecycle: every listing needs photos and floor plans; projects need brochures. NOTE: Returns { data: [...] } WITHOUT pagination metadata (unlike other endpoints). Must provide BOTH related_model AND related_id together, or omit both for global media. Listing media: related_model='Properties', related_id=''. Contact documents: related_model='Contacts', related_id=''. Project brochures: related_model='Projects', related_id=''. Alternative: qobrix_list_properties with media=true (default) embeds media in property responses. Each item has: id, media_type, reference_id, display_order, category, created.
Inputs: page, limit, related_id, related_model.
qobrix_list_meetingsA
List meetings — bridges the Activity Tracking and Showing Lifecycle workflows. Meetings can wrap property viewings (RESO Showing ↔ Meeting). Returns { data: [...], pagination: { count, current_page, has_next_page, ... } }. Include: ContactContacts, ViewingPropertyViewings (linked showings), AssignedToUsers, RelatedOpportunityOpportunities, RelatedAgentAgents, CreatedByUsers, ModifiedByUsers. Workflow: include=['ViewingPropertyViewings'] to see which meetings are property showings vs general meetings. The first meeting/showing is the canonical trigger that moves a lead from follow-up to active sales.
Inputs: page, sort, limit, search, +1 more.
qobrix_list_offersA
List offers (RESO TransactionManagement) — the 'Decision Making' stage of the Sales Pipeline. An Offer formalizes a buyer's intent: it links an Opportunity (the lead) to a Property (the listing) with a price. Returns { data: [...], pagination: { count, current_page, has_next_page, ... } }. Include: OpportunityOpportunities (the lead), PropertyProperties (the listing), CreatedByUsers, ModifiedByUsers. Canonical transaction chain: Lead → Viewing → Offer → Contract → Close. After an offer is accepted, the next step is Contract creation (qobrix_list_contracts).
Inputs: page, sort, limit, search, +1 more.
qobrix_list_opportunitiesA
List opportunities/leads from Qobrix CRM — the central entity of the Sales Pipeline and Lead-Contact Lifecycle. In Qobrix, leads ARE opportunities. Each represents a buyer/renter/investor enquiry. RESO funnel mapping: 'new' = MQL/Lead, 'open' = SQL/Active, 'won' = Closed Won, 'closed_lost' = Lost. Returns { data: [...], pagination: { count, current_page, has_next_page, ... } }. IMPORTANT: Avoid include=['Locations']. Safe verified includes: ContactNameContacts, Properties, AgentAgents, PropertyTypes, ClosedLostReason, User, OwnerUsers. Key fields (77 total): status, enquiry_type (buy/rent/invest), buy_rent, contact_name (UUID → Contact), agent (UUID → Agent), owner (UUID → user), source, area_of_interest, bedrooms_from/to, price ranges, next_follow_up_date, enquiry_date. Workflow patterns: Pipeline overview: group by status to see funnel distribution. Overdue follow-ups: status == "open" and next_follow_up_date <= NOW. New leads this week: created >= THIS_WEEK and status == "new". Two-way matching: demand→supply via qobrix_search_properties with lead criteria; supply→demand via this tool with boost against a listing (or native qobrix_get_leads_by_property).
Inputs: page, sort, limit, fields, +2 more.
qobrix_list_projectsA
List projects (property developments/complexes) — RESO Project equivalent for off-plan and new-build listings. Projects group multiple property units under one development, relevant in the Listing Lifecycle for new-builds. Returns { data: [...], pagination: { count, current_page, has_next_page, ... } }. Properties reference their project via the 'project' FK. Verified include: Agents, Developer, Translations, LocationLocations, Assignee, Recommended, Favorites. Workflow: to see units in a project, qobrix_search_properties with project == ''.
Inputs: page, sort, limit, fields, +2 more.
qobrix_list_propertiesA
List properties (RESO Property resource) from Qobrix CRM. Core tool for the Listing Lifecycle — use to browse active inventory, track status changes, or audit listings. Returns { data: [...], pagination: { count, current_page, has_next_page, has_prev_page, page_count, limit } }. RESO StandardStatus mapping: 'available' = Active, 'reserved' = Pending/Under Contract, 'sold' = Closed, 'withdrawn' = Withdrawn. Key fields (184 total): name, ref, status, sale_rent (for_sale/for_rent), property_type, property_subtype, city, country, list_selling_price_amount, list_rental_price_amount, bedrooms, bathrooms, covered_area_amount, plot_area_amount, agent (UUID → qobrix_get_agent), seller (UUID → qobrix_get_contact), project (UUID → qobrix_get_project), salesperson (UUID → user). Workflow recipes: Active inventory → search: status == "available" and sale_rent == "for_sale". Recent listings → sort: '-created'. Buyer-property match → prefer qobrix_search_properties with search + boost for free-language demand. Listing media → follow up with qobrix_list_media(related_model='Properties'). PAYLOAD DEFAULTS: expand=false and media=false — FKs come back as UUIDs and media is not inlined (set expand=true / media=true explicitly when full nested objects or media URLs are needed). Prefer include[] for surgical expansion of specific associations. Default limit 10, max 100.
Inputs: page, sort, limit, media, +4 more.
qobrix_list_tasksA
List tasks — the operational backbone of all canonical workflows: Follow-up, Pipeline Management, and Listing Checklists. Tasks drive daily agent cadence: overdue follow-ups, listing prep, closing steps, payment tracking. Returns { data: [...], pagination: { count, current_page, has_next_page, ... } }. Include: AssignedToUsers (task owner), ContactContacts (related person), Properties (listing), RelatedOpportunityOpportunities (lead), RelatedAgentAgents, RelatedContractContracts, TaskStatus, TaskTypes, CreatedByUsers, ModifiedByUsers. Canonical patterns: My open tasks: search with assigned_to == CURRENT_USER and status != "completed". Overdue: search with due_date <= NOW and status == "pending". Tasks for a lead: search with related opportunity FK. Listing checklist: search tasks linked to a property UUID.
Inputs: page, sort, limit, search, +1 more.
qobrix_list_viewingsA
List property viewings (RESO ShowingAppointment) — the Showing/Viewing Lifecycle tool. Viewings are the pivot between Follow-up and Active Sales: the first viewing marks a lead as actively engaged. Returns { data: [...], pagination: { count, current_page, has_next_page, ... } }. Each viewing links a property, a contact, and an agent/creator. Verified include: PropertyViewingViewing. Workflow patterns: This week's viewings: search with created >= THIS_WEEK. Property's showing history: qobrix_get_property with include=['PropertyViewings']. Meetings linked to viewings: qobrix_list_meetings with include=['ViewingPropertyViewings']. After a showing, the next pipeline step is Offer → Contract → Close.
Inputs: page, sort, limit, search, +1 more.
qobrix_rep_scorecardA
Per-rep productivity roll-up. One call returns a wide row for a single user or a top-N leaderboard (default). Counts calls/meetings/emails/tasks/viewings/opportunities and deals closed; sums total_volume + total_commission from closed contracts. Bucketed by assigned_to across every resource. Example uses: Leaderboard 2026 by volume: { year: 2026 }. My scorecard last 30 days: { user: 'CURRENT_USER', since_days: 30 }. Top 5 by activities this quarter: { from: '2026-01-01', to: '2026-04-01', sort_by: 'activities', top: 5 }.
Inputs: to, top, from, user, +3 more.
qobrix_search_agentsA
Relevance-ranked agent search (F1-optimized). TWO-TIER: search = hard must-haves; boost[] = soft weighted preferences; limit/max_scan control top-N and pool size. With boost: _relevance + _matched; pagination.mode='ranked'. Call qobrix_search_dsl_help({resource:'Agents'}) for DSL + fields. Example: boost=[{field:'ref',op:'contains',value:'LIM',weight:2}], limit=10, max_scan=100.
Inputs: page, sort, boost, limit, +3 more.
qobrix_search_changesA
Search tenant-wide audit log for a CRM resource (Qobrix LogAudit). Calls GET /api/v2/{resource}/changes. Use to find who changed what across all records. Search examples: timestamp >= DAYS_AGO(30), user_id == "", primary_key == "". Pair with qobrix_top_field_changers for leaderboards or qobrix_field_change_history for one record.
Inputs: resource (required), page, sort, limit, +2 more.
qobrix_search_contactsA
Relevance-ranked contact search (F1-optimized). TWO-TIER: search = hard must-haves; boost[] = soft weighted preferences scored over up to max_scan candidates; limit = top-N returned. With boost: rows get _relevance + _matched; pagination.mode='ranked'. Call qobrix_search_dsl_help({resource:'Contacts'}) for DSL + fields. Examples: search='country == "CY"', boost=[{field:'city',op:'==',value:'Limassol',weight:2},{field:'is_company',op:'==',value:false,weight:1}], limit=10, max_scan=100. Hard-only: assigned_to == CURRENT_USER; name contains "Smith"; created >= THIS_MONTH.
Inputs: page, sort, boost, limit, +3 more.
qobrix_search_contractsA
Relevance-ranked contract search (F1-optimized). TWO-TIER: search = hard must-haves; boost[] = soft weighted preferences; limit/max_scan for top-N and pool. With boost: _relevance + _matched; pagination.mode='ranked'. Call qobrix_search_dsl_help({resource:'Contracts'}) for fields. Example: search='contract_type == "cos" and contract_status == "agreed"', boost=[{field:'final_selling_price_amount',op:'>=',value:300000,weight:2}], limit=10, max_scan=150. Prefer qobrix_deals for common closed-deal questions.
Inputs: page, sort, boost, limit, +3 more.
qobrix_search_dsl_helpA
Return the full Qobrix Search Expression DSL reference so you can build correct search strings and boost[] clauses for ANY qobrix_search_* tool (properties, projects, contacts, agents, opportunities, viewings, tasks, offers, contracts). Includes operators, functions, date helpers, association paths, and the two-tier relevance recipe (hard search filter + soft boost ranking + limit/max_scan). Optional resource (e.g. 'Properties', 'Opportunities', 'Contacts') appends a field cheatsheet and live schema field names (schema fetch is response-cached). Call before composing free-language queries.
Inputs: resource.
qobrix_search_offersA
Relevance-ranked offer search (F1-optimized). TWO-TIER: search = hard must-haves; boost[] = soft weighted preferences; limit/max_scan for top-N and pool. With boost: _relevance + _matched; pagination.mode='ranked'. Call qobrix_search_dsl_help({resource:'Offers'}) for fields. Example: search='created >= THIS_MONTH', boost=[{field:'created',op:'>=',value:'2026-01-01',weight:1}], limit=10, max_scan=100.
Inputs: page, sort, boost, limit, +3 more.
qobrix_search_opportunitiesA
Relevance-ranked lead/opportunity search (F1-optimized) — primary pipeline filter AND supply→demand matching. TWO-TIER: search = hard must-haves; boost[] = soft weighted preferences scored over max_scan candidates; limit = top-N. With boost: _relevance + _matched; pagination.mode='ranked'. Call qobrix_search_dsl_help({resource:'Opportunities'}) for DSL + buyer-criteria fields. Pipeline recipes: status == "open"; created >= THIS_WEEK and status == "new"; next_follow_up_date <= NOW and status == "open". Supply→demand (who wants this listing?): search='status in ["new","open"] and buy_rent == "buy"', boost=[{field:'area_of_interest',op:'contains',value:'Limassol',weight:3},{field:'bedrooms_from',op:'<=',value:3,weight:2},{field:'list_selling_price_to',op:'>=',value:400000,weight:2}], limit=15, max_scan=200. Works for projects too (native by-property does not).
Inputs: page, sort, boost, limit, +3 more.
qobrix_search_projectsA
Highly relevant project (development) search for free-language demand (F1-optimized). TWO-TIER RECIPE: (1) search = hard DSL must-haves (server filter → precision). (2) boost[] = soft weighted nice-to-haves scored over up to max_scan candidates (recall + ranking). (3) limit = how many ranked rows to return (default 10, max 100). With boost: top-N with _relevance and _matched; pagination.mode='ranked'. Without boost: fast path single cached page; pagination.mode='fast'. Call qobrix_search_dsl_help({resource:'Projects'}) for DSL + field cheatsheet. Example: search='city contains "Paphos"', boost=[{field:'starting_price_from',op:'<=',value:400000,weight:2},{field:'construction_stage',op:'==',value:'under_construction',weight:1}], limit=10, max_scan=150. When expand=true or media=true, max_scan is auto-capped at 100. If status='result_too_large' with _refine_required, ask the user to narrow then retry. All upstream pages are response-cached.
Inputs: page, sort, boost, limit, +5 more.
qobrix_search_propertiesA
Highly relevant property search for free-language buyer demand (F1-optimized). TWO-TIER RECIPE: (1) search = hard DSL must-haves (server filter → precision). (2) boost[] = soft weighted nice-to-haves scored client-side over up to max_scan candidates (recall + ranking → better precision@top). (3) limit = how many ranked rows to return (default 10, max 100) — raise for more options, keep modest to avoid context overload. With boost: returns top-N with _relevance (score) and _matched (which boosts hit); pagination.mode='ranked' and pagination.scanned shows pool size. Without boost: fast path — single cached list page (pagination.mode='fast'). Call qobrix_search_dsl_help({resource:'Properties'}) before composing queries. Examples: Hard only: search='status == "available" and sale_rent == "for_sale" and city contains "Limassol"'. Demand match: search='status == "available" and sale_rent == "for_sale"', boost=[{field:'sea_view',op:'==',value:true,weight:3},{field:'bedrooms',op:'>=',value:3,weight:2},{field:'list_selling_price_amount',op:'in',value:'200000..600000',weight:2}], limit=15, max_scan=200. PAYLOAD: keep expand=false / media=false for search; when either is true, max_scan is auto-capped at 100. If a result returns status='result_too_large' with _refine_required, ask the user to narrow the query (filters, fields[], smaller limit, drop expand/media) then retry. All upstream pages are response-cached (QOBRIX_CACHE_TTL, default 300s).
Inputs: page, sort, boost, limit, +5 more.
qobrix_search_tasksA
Relevance-ranked task search (F1-optimized) — daily ops for pipeline and follow-up. TWO-TIER: search = hard must-haves; boost[] = soft weighted preferences; limit/max_scan for top-N and pool. With boost: _relevance + _matched; pagination.mode='ranked'. Call qobrix_search_dsl_help({resource:'Tasks'}) for fields. Recipes: search='assigned_to == CURRENT_USER and status != "completed"', boost=[{field:'due_date',op:'<=',value:'2026-12-31',weight:3}], limit=15, max_scan=100. Overdue hard filter: due_date <= NOW and status == "pending".
Inputs: page, sort, boost, limit, +3 more.
qobrix_search_viewingsA
Relevance-ranked viewing search (F1-optimized). TWO-TIER: search = hard must-haves; boost[] = soft weighted preferences; limit/max_scan for top-N and pool. With boost: _relevance + _matched; pagination.mode='ranked'. Call qobrix_search_dsl_help({resource:'PropertyViewings'}) for fields. Example: search='created >= THIS_WEEK', boost=[{field:'created',op:'>=',value:'2026-01-01',weight:1}], limit=10.
Inputs: page, sort, boost, limit, +3 more.
qobrix_sign_inA
Start interactive Qobrix sign-in (Mode C only). When not connected, returns a Sign In to Qobrix link (or native URL elicitation) for the user to complete login + 2FA + consent. When already connected, reports the current identity. In Mode A/B this is a no-op — credentials come from env / request headers. Mode C uses a per-user encrypted session vault keyed by the chat identity (X-Chat-Platform / X-Chat-User-Id). Deliver the Sign In link only to that individual — never post it into a shared/group thread.
Inputs: no named parameters.
qobrix_sign_outA
Sign out of Qobrix (Mode C only). Fully revokes the current user's session: calls the Authorization Server /disconnect (deletes the minted Qobrix API key and clears AS tokens/vault), then clears this user's local encrypted session vault. Other users' vaults on this MCP process are not affected. In Mode A/B there is no interactive session to clear.
Inputs: no named parameters.
qobrix_stale_leadsA
Find live opportunities (default status in [new,open]) that have no recent activity. An opportunity is 'stale' when no call/meeting/email/task touched it within since_days AND the opportunity itself wasn't modified within that window. Sorted oldest-modified first. Example uses: Silent leads (default, 30d): {}. Aggressive cadence: { since_days: 7 }. My silent leads: { assigned_to: 'CURRENT_USER' }.
Inputs: top, statuses, since_days, assigned_to.
qobrix_timeseriesA
Time-bucketed metric (count / sum / avg / min / max) over any resource. Paginates once over the window, buckets in-process by day/week/month/quarter/year (default 'month'). Per-resource default date_field: contracts→date_of_contract, opportunities/properties/calls/etc.→created. Use search to filter (e.g. only closed sales). Set compare_to_prior=true to also fetch the prior identical-length window for YoY %. Example uses: Monthly 2026 closed-sale volume with YoY: resource='contracts', metric='sum', field='final_selling_price_amount', year=2026, search='contract_type == "cos" and contract_status == "agreed"', compare_to_prior=true. Weekly lead intake last 90 days: resource='opportunities', bucket='week', metric='count', since_days=90. Quarterly listing additions 2026: resource='properties', bucket='quarter', year=2026, metric='count'.
Inputs: resource (required), to, from, year, +7 more.
qobrix_top_field_changersA
Leaderboard: which users most often edited a given field (tenant-wide audit scan). Paginates GET /{resource}/changes, counts rows where field appears in changed, groups by user_id. Returns [{user_id, user_name?, change_count, latest_change_at, sample_record_ids}]. Use for: 'who rewrites lead source to direct most often in the last 6 months?'
Inputs: field (required), resource (required), limit, since, +3 more.
qobrix_top_recordsA
Top-N rows of any resource sorted by a numeric or ISO-date field. Paginates server-side (cap 20,000 rows), sorts in-process, and resolves common FK fields (property_id, agent, owner, assigned_to, commission_to, commission_to_2, contact_name, salesperson, seller, project, developer_id, campaign_id) into readable names so the agent doesn't have to chain lookups. Use this for full-dataset top-N, or when list/search sort returns no rows on a nullable/computed field (e.g. opportunities.budget). For a single page of highest list prices, prefer qobrix_list_properties / qobrix_search_properties with sort='-list_selling_price_amount'. Example uses: Top 2026 closed sales: resource='contracts', sort_by='final_selling_price_amount', search='contract_type == "cos" and contract_status == "agreed" and date_of_contract >= "2026-01-01" and date_of_contract < "2027-01-01"'. Largest active listings (full scan): resource='properties', sort_by='list_selling_price_amount', search='status == "available" and sale_rent == "for_sale"'. Most recently modified leads: resource='opportunities', sort_by='modified'.
Inputs: sort_by (required), resource (required), top, desc, +4 more.
qobrix_top_valuesA
Aggregate records by a field and return the top N most-frequent values with counts. This tool paginates through all matching records server-side — no client-side scripting needed. Caps at 20,000 records for safety; use a search filter to narrow large datasets. Example uses: Top listing developers: resource='properties', field='developer_id', search='status == "available" and sale_rent == "for_sale"', resolve=true. Lead sources: resource='opportunities', field='source', search='created >= "2026-01-01"'. Agent workload: resource='opportunities', field='owner', search='status == "open"', resolve=true. Property type mix: resource='properties', field='property_type', search='status == "available"'. Set resolve=true to convert UUIDs (developer_id, agent, owner, etc.) to human-readable names.
Inputs: field (required), resource (required), top, search, +1 more.
qobrix_whoamiA
Return the current Qobrix user profile, capabilities, and portals (GET /api/v2/session/). In Mode C with no session for this chat identity, surfaces a Sign In link so the user can authenticate first. Also includes the OAuth subject when available. Use to confirm which CRM identity the agent is acting as for this user.
Inputs: no named parameters.
qobrix_win_lossA
Win-rate analytics over opportunities. Globally returns counts of new / open / won / closed_lost plus win_rate_pct = won / (won + closed_lost). Optional group_by slices by source, enquiry_type, owner, agent, or closed_lost_reason_id (multi-dim arrays supported). Window applied to last_status_change (fallback modified). Set include_top_losses=true to also receive the 10 most-recent closed_lost opportunities with reason labels and details. Example uses: Overall 2026 win rate: { year: 2026 }. Win rate by lead source: { year: 2026, group_by: 'source' }. Top loss reasons last 90 days: { since_days: 90, group_by: 'closed_lost_reason_id' }. My win rate: { assigned_to: 'CURRENT_USER', year: 2026 }. Forensic loss review: { year: 2026, include_top_losses: true }.
Inputs: to, top, from, year, +5 more.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 64 tools
Most tools are clearly distinct by resource (contacts, properties, opportunities, etc.) and action (list, get, search). However, there are some overlaps: qobrix_get_properties_by_lead and qobrix_get_lead_properties appear to do the same thing (both return properties for a lead), and qobrix_search_offers / qobrix_search_contracts vs qobrix_deals may be confused for deal queries. Also, qobrix_count, qobrix_top_values, qobrix_top_records, qobrix_aggregate, qobrix_timeseries all perform analytics over resources, requiring agents to read carefully to choose the right one.
Tool names follow a consistent verb_noun pattern: list_<resource>, get_<resource>, search_<resource>, plus a few domain-specific verbs like count, aggregate, timeseries, funnel, win_loss, etc. All are snake_case with clear resource identifiers. The few exceptions (qobrix_deals, qobrix_funnel, qobrix_win_loss) are still consistent with the verb-based style (e.g., deals is a verb-like noun). Overall, the naming is predictable and uniform.
64 tools is on the heavier side, but the server covers a full CRM domain with core entities (contacts, properties, opportunities, viewings, tasks, media, projects, offers, contracts, calls, meetings, emails) plus analytics and admin tools. While a 64-tool surface may be overwhelming for agents, the tools are organized and each serves a specific purpose. The count is borderline high but justified by the breadth of functionality. It is not an extreme mismatch (like 100+ or a single trivial tool).
The tool surface covers the primary CRM lifecycle: list/get/search for all main entities, plus creation/update/delete appears missing for most resources—only search/list/get are provided. However, the server is likely read-only (no create/update/delete tools, except for auth). Given that the domain is CRM data retrieval and analytics, the coverage is quite complete for read operations: all key resources are represented, and advanced analytics (funnel, win_loss, stale_leads, cohort) fill gaps. Some missing operations like creating a contact or updating an opportunity are absent, but the server appears designed as a read/analytics layer rather than a full CRUD tool, so this is a minor gap.
Maintenance
Related MCP Connectors
MCP server for real-estate agents: listings, CRM, German market data and AI media tools.
Read-only MCP for Oria CRM: properties, contacts, deals, viewings, agents, auctions.
MCP server for SmartAgent CRM: leads, tasks, sales pipelines, property listings
API-first CRM for LLMs - contacts, companies, deals and activities over a native MCP server.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceA comprehensive Model Context Protocol server for real estate data management that provides tools and resources for property listings, agent management, market analysis, client relationships, and area intelligence.71AGPL 3.0
- AlicenseAqualityDmaintenanceRead-only MCP server for the Daktela contact center REST API, providing 40 tools to access tickets, calls, emails, chats, contacts, CRM records, campaigns, and real-time agent status.451MIT
- AlicenseCqualityDmaintenanceEnterprise-level MCP server integrating with Vista CRM (Loft Edition) for real estate operations, offering 40+ tools for property search, pipeline management, lead capture, and agenda control.42MIT
- FlicenseNot gradedqualityCmaintenanceMCP server for Moody's Commercial Real Estate API, providing 37 tools for property lookups, market analytics, comps, CMBS data, tax records, and more.-