| compass_search_propertiesA | Search Compass listings by location (city, ZIP, neighborhood) and optional filters. Resolves free-text via slugification into Compass's URL routing, then fetches the SSR search-results page and extracts the embedded listings array. Compass server-renders ~41 listings into that page (its num), and total_items reports the full market count. PAGINATION (issue #87): Compass no longer paginates the SSR search via any URL — /page-N/, ?page=N, and ?start=N all canonicalize back to page 1 and return the identical listings, so only the first SSR page (~41 listings) is reachable through this primitive. offset is honored WITHIN that page, and next_offset is emitted only when more listings remain within it — it is never a false cursor that re-fetches page 1. TO REACH BEYOND THE FIRST PAGE, narrow with price_min / price_max / beds_min/beds_max to bucket the result set into <~41-listing bands (price-banding), then search each band. Returns each matching listing's address, price, beds/baths, sqft, lat/lng, the Compass homedetails URL (_lid/ form, content-addressed by listing_id_sha), and the stable _pid/ URL via property_url and the surfaced pid field. The per-listing primary_photo_url / primary_thumbnail_url are omitted in the default compact view (they are Compass CDN URLs a model cannot see); pass view: "full" to get them, or compass_get_property_photos for the whole gallery. USE pid/_pid/ FOR LONG-LIVED REFERENCES (trackers, sheets, bookmarks) — sha-based _lid/ URLs change when a property is delisted and relisted. Use the sha-based URL to fetch the current listing record. Read-only; safe to call repeatedly. |
| compass_get_propertyA | Fetch a property's full Compass record. Pass either url (a full Compass homedetails URL or path from a compass_search_properties result) or listing_id_sha alone — when only the sha is supplied, the tool fetches /listing//view, which redirects to the canonical /homedetails//_lid/ page. Returns address, neighborhood, beds/baths, sqft, lot size (lot_size_sqft plus the derived lot_size_acres = round(sqft / 43560, 2), null — never 0 — for condos / missing lots), price + price-per-sqft, monthly charges, MLS status, amenities, schools, parcel number, and the canonical Compass URL. Also returns extracted_features (lake_front, hot_tub, basement, furnished, dock, community) keyword-parsed from the description. DESCRIPTION HANDLING: The raw description (Compass marketing copy) is omitted by default — pass include_description: true to keep it. extracted_features is always populated and usually sufficient. URL FORMS: Compass exposes two URL shapes for a listing. _lid/ (content-addressed by listing_id_sha) — what this tool fetches and what url returns — is the form to use for reading the current listing record. _pid/ (opaque short ID, in property_url and the surfaced pid field) is stable across re-listings and is the right choice for any long-lived reference (trackers, sheets, bookmarks); sha-based URLs go stale when a property is delisted and relisted. Read-only; safe to call repeatedly. |
| compass_get_saved_homesA | Not yet supported — Compass renders /overview/favorites via an auth-scoped GraphQL we have not yet identified. Throws a clear error explaining the limitation. |
| compass_get_saved_searchesA | Not yet supported — Compass renders saved searches via an auth-scoped GraphQL we have not yet identified. Throws a clear error explaining the limitation. |
| compass_calculate_mortgageA | Local-only mortgage payment calculator. Returns a full PITI breakdown (principal + interest, property tax, insurance, HOA, PMI) and total interest over the life of the loan. No network call. Provide either down_payment OR down_payment_percent; defaults to 20%. Property tax can be given as property_tax_annual or property_tax_rate (% of home price). PMI applies automatically when LTV > 80% and pmi_rate is provided. |
| compass_get_price_historyA | Full listing history for a Compass property — Listed / Sold / Pending / Price Change / Delisted events with date, price, and MLS attribution. Returns three arrays: events covers this listing's events, history aggregates events from prior listings of the same property, and events_normalized merges both into a shared cross-MCP schema ({date, type, price?, price_change_pct?, source_mls?} with a fixed type enum: Listed | PriceChange | Pending | Contingent | Sold | Withdrawn | Relisted | Delisted). Pass either url (the full Compass homedetails URL or path) or listing_id_sha alone — sha-only calls fetch /listing//view, which redirects to the homedetails page. Note: most of this data is already returned inline on compass_get_property (the events[] / history[] arrays live on the same listing record); call this tool only when you want the merged + normalized timeline. Read-only; safe to call repeatedly. |
| compass_compare_propertiesA | Fetch 2 or more Compass properties and align their facts side-by-side. Each target may supply url (a full Compass homedetails URL or path) or listing_id_sha alone — sha-only targets fetch /listing//view, which redirects to the homedetails page. Returns the full per-property record per row (with extracted_features populated). Per-target errors are captured per-row — one bad target will not fail the whole call. Calls are concurrent, and the whole call is bounded by an overall deadline: any row still unsettled when it is reached comes back as { status: "pending", retryable: true, error } with a top-level pending count — re-run just those targets. The raw description is omitted from each row by default — pass include_description: true to keep it. The redundant summary table is also opt-in via include_summary: true — by default only results[] is returned, which already carries every fact. |
| compass_calculate_affordabilityA | Solve for the maximum home price you can afford under the standard 28/36 DTI rule. Inputs: monthly income, recurring monthly debts (car/student loans), down payment, interest rate, optional property-tax rate / insurance / HOA / loan term. Output: max home price, binding constraint (front-end vs back-end), and the PITI breakdown at that price. Identical math to zillow-mcp and redfin-mcp. No network — pure local math. |
| compass_get_property_photosA | The full photo gallery for a Compass listing — every image in listing.media[]. Each entry returns the original CDN URL plus a thumbnail URL and pixel dimensions. Pass either url (the full Compass homedetails URL or path) or listing_id_sha alone — sha-only calls fetch /listing//view, which redirects to the homedetails page. By default only photos (category 0) are returned; set include_all_categories: true to also include floorplans and other media. Returns { listing_id_sha, count, photos }. Read-only; safe to call repeatedly. |
| compass_healthcheckA | Round-trips a small public www.compass.com URL (/robots.txt) through the fetchproxy bridge and returns diagnostics: the bridge's role (host/peer/null), port, version, the extension link (linked / pair pending / not attached / never answered), the elapsed round-trip time, and a plain-English hint distinguishing 'bridge never came up' from 'extension not connected' from 'real www.compass.com-side problem'. Read-only, no auth required. Call this when a real tool fails and you want to know which hop broke. |
| compass_get_by_addressA | Resolve a free-text street address to a Compass listing's canonical URL and identifiers in one call. Walks three rungs: first the structured typeahead POST /api/v3/omnisuggest/autocomplete (the primary rung — Compass's address-suggest API, which routes around the AWS WAF that 403s the SSR free-text path, issues #78/#79), then /homes-for-sale/?q=<address> (the free-text rung) and — when those return no verified match — a slug-based search at /homes-for-sale/<city-state-or-zip>/ (the search-fallback rung, issue #71). Each candidate is verified against the query (case + street-type abbreviation normalization, then whole-token equality, issue #45) before being accepted. Returns { url, listing_id_sha, pid, address, resolved, matched_via } where matched_via is "typeahead", "freetext", or "search_fallback" so callers can see which rung found the match. When no rung matches, returns { resolved: false, error: "no listing matched" } rather than leaking a wrong URL. When the lookup was blocked by a sign-in / AWS WAF challenge instead, it returns { resolved: false, status: "auth_required", error, hint } — NOT a miss: sign in to compass.com in the browser and retry. The url is the stable _pid/ form when Compass provides a navigationPageLink (preferred for trackers/bookmarks — sha URLs go stale on relisting), falling back to the _lid/ form otherwise. Read-only; safe to call repeatedly. |
| compass_bulk_getA | Fetch up to 200 Compass listings in a single call. Returns one structured row per input target (no side-by-side summary table — use compass_compare_properties for that). Each row is either { listing_id_sha, url, property } on success or { listing_id_sha, url, error } on failure — one bad target never fails the whole call. When the failure was a bridge timeout (after one retry) or an unreachable bridge (issue #73), the row also carries { status: "timeout" | "bridge_down", retryable: true } — that is NOT a missing listing, so retry it (a cold bridge usually succeeds on the second call) rather than concluding Compass has no record. Targets accept the same url / listing_id_sha shape as compass_get_property. Calls fan out concurrently. The whole call is bounded by an overall deadline: a slow or hung row never wedges it — any row still unsettled when the deadline is reached comes back as { status: "pending", retryable: true, error } alongside a top-level pending count, so re-run just those targets. extracted_features is populated per row. The raw description is omitted by default — pass include_description: true to keep it. |
| compass_resolve_addressesA | Resolve up to 100 street addresses to Compass listing URLs in a single call. Returns one row per input, either { resolved: true, url, listing_id_sha, pid, address, matched_via }, { resolved: false, error, query } for a genuine no-match, or — when the bridge timed out / was unreachable (issue #85) — { resolved: false, status: "timeout" | "bridge_down", retryable: true, error, query }. A status row is NOT a miss: the lookup never completed, so retry it (a cold bridge usually succeeds on the second call) rather than concluding Compass has no listing. A row with status: "auth_required" (plus error and hint) means the browser session was signed out or stuck on an AWS WAF challenge: sign in to compass.com, then retry. Each row walks the same three rungs as compass_get_by_address — first the structured typeahead POST /api/v3/omnisuggest/autocomplete (the primary rung that routes around the AWS WAF, issues #78/#79), then /homes-for-sale/?q=<address> (freetext), then /homes-for-sale/<locality-slug>/ (search_fallback, issue #71) — and verifies candidates against the same whole-token address-match policy (#45). The matched_via field on each resolved row indicates which rung found it. Compass's search degrades into far-away top hits when the local market has no match, and bulk amplifies the corruption surface, so a miss returns resolved: false with no URL rather than leaking the wrong property. Calls fan out concurrently server-side, and the whole call is bounded by an overall deadline: any row still unsettled when it is reached comes back as { resolved: false, status: "pending", retryable: true, error, query } with a top-level pending count — that is NOT a miss, so re-run just those addresses. Read-only; safe to call repeatedly. |
| compass_register_sessionA | Register (or refresh) an authenticated Compass session keyed by signed-in account identity. Re-registering the same account_identity updates the existing session rather than creating a duplicate. Returns the session_id to use when routing per-tool calls. The first registered session becomes the default active_session_id. Pass mark_active: true to make the newly-registered session active in the same call. |
| compass_set_active_sessionA | Switch which registered session subsequent tool calls route through by default. Pass a session_id previously returned by compass_register_session. Tools that accept an explicit session_id parameter override this default per-call. |
| compass_get_session_contextA | Return the full set of registered sessions plus the current active_session_id. When no sessions are registered, sessions is empty and active_session_id is null. |
| compass_get_comparable_rentalsA | Surface nearby rental listings for a Compass property — useful for evaluating STR (short-term-rental) viability in vacation markets. Lifts the target's city/state/zip from the homedetails page, then searches Compass's type-rental/ filter in the same locality and returns each rental's address, monthly price (in price_formatted), beds/baths, sqft, and the Compass URL. Honest-by-default: when no rentals come back, rentals: [] with the target locality preserved so the caller can decide to widen. Read-only; safe to call repeatedly. |
| compass_get_agent_listingsA | Fetch the listings represented by a Compass agent from their profile page (/agents//). Pass either slug (e.g. "paige-mcguirk") or profile_url (a full https://www.compass.com/agents// URL — both forms are accepted). Returns { agent: {name, slug}, active_listings: [...] }, where each active listing carries the SAME normalized fields as compass_get_property (address, beds/baths, sqft, lot size, price + price-per-sqft, MLS status, the canonical Compass URL + stable pid, extracted_features, etc.). CLOSED DEALS: the agent's sold/closed deals are opt-in — pass include_closed: true to add a closed_deals array (same normalized shape). Omitted by default to keep the payload lean. CHAINING: the agent slug is surfaced on each property's listing_agent.profile_slug in compass_get_property results (compass_search_properties results don't carry the listing agent), so you can go property → agent → their other listings. Read-only; safe to call repeatedly. |