{"id":"a12e28a8-f700-4423-98db-6b3b0d4ed0ee","entityType":"agent","slug":"clawhub-chrischall-compass-fpx","name":"compass-fpx","canonicalUrl":"https://www.xpersona.co/agent/clawhub-chrischall-compass-fpx","canonicalPath":"/agent/clawhub-chrischall-compass-fpx","generatedAt":"2026-10-10T15:53:06.506Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"editorial-content","verified":true,"confidence":"high","updatedAt":"2026-10-10T13:39:32.277Z","emptyReason":null},"description":"Query compass.com (US real-estate portal) from a shell with the fpx CLI (@fetchproxy/cli) instead of running the compass-mcp server — search listings, get property/agent detail, price history, and resolve street addresses through one-shot fetches over a signed-in browser tab. Use when you want Compass data without the MCP, in a script, or on a machine where the MCP isn't installed. Skill: compass-fpx Owner: chrischall Summary: Query compass.com (US real-estate portal) from a shell with the fpx CLI (@fetchproxy/cli) instead of running the compass-mcp server — search listings, get property/agent detail, price history, and resolve street addresses through one-shot fetches over a signed-in browser tab. Use when you want Compass data without the MCP, in a script, or on a machine where the MCP isn't","descriptionLabel":"Technical summary","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 1.4K downloads reported by the source. Last updated 10/10/2026.","installCommand":"clawhub skill install s17cjx1a349nz5apaqp02vgz4h85728z:compass-fpx","sourceUrl":"https://clawhub.ai/chrischall/compass-fpx","homepage":"https://clawhub.ai/chrischall/skills/compass-fpx","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/chrischall/compass-fpx","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/chrischall/skills/compass-fpx","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":63,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"Query compass.com (US real-estate portal) from a shell with the fpx CLI (@fetchproxy/cli) instead of running the compass-mcp server — search listings, get prope"},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-10T13:39:32.277Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T13:39:32.277Z","emptyReason":null},"stars":null,"forks":null,"downloads":1407,"packageName":null,"latestVersion":"1.1.9","tractionLabel":"1.4K downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T13:39:32.277Z","emptyReason":null},"lastUpdatedAt":"2026-10-10T13:39:32.277Z","lastCrawledAt":"2026-10-10T13:39:32.277Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-11T13:39:32.277Z","lastVerifiedAt":null,"highlights":[{"version":"1.1.9","createdAt":"2026-10-09T23:26:19.319Z","changelog":"- Removed the file skill-card.md. - No user-facing changes to functionality or documentation.","fileCount":5,"zipByteSize":9607},{"version":"1.1.8","createdAt":"2026-10-07T13:35:25.423Z","changelog":"- Removed the skill-card.md file. - No user-facing or behavioral changes; documentation and core usage unchanged.","fileCount":5,"zipByteSize":9500},{"version":"1.1.7","createdAt":"2026-10-05T02:48:18.386Z","changelog":"- Removed the file skill-card.md. - No changes to documentation or functionality; this is a minor cleanup release.","fileCount":5,"zipByteSize":9555},{"version":"1.1.6","createdAt":"2026-10-03T01:41:18.949Z","changelog":"- Removed the skill-card.md file. - No changes to core functionality or documentation in SKILL.md.","fileCount":5,"zipByteSize":9643},{"version":"1.1.5","createdAt":"2026-09-28T13:58:03.590Z","changelog":"- Updated all documentation to reference the extension's new name, \"ContextMint Bridge\", instead of \"fetchproxy extension\". - Clarified install and pairing instructions to reflect the rebranded browser extension and provided a link to its public releases. - Removed outdated file: skill-card.md. - No changes to code or core functionality; documentation only.","fileCount":5,"zipByteSize":9618},{"version":"1.1.4","createdAt":"2026-09-25T15:53:39.114Z","changelog":"- Removed the documentation file skill-card.md. - No changes to core functionality or user workflow.","fileCount":5,"zipByteSize":9565},{"version":"1.1.3","createdAt":"2026-09-24T15:03:35.442Z","changelog":"- Removed the skill-card.md file. - No changes to functionality or documentation in SKILL.md. - Maintenance update to clean up unused project files.","fileCount":5,"zipByteSize":9708},{"version":"1.1.2","createdAt":"2026-09-23T21:38:19.046Z","changelog":"- Removed the sample file skill-card.md. - No functional or user-facing changes to the main skill or documentation.","fileCount":5,"zipByteSize":9652}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s17cjx1a349nz5apaqp02vgz4h85728z:compass-fpx","setupComplexity":"low","setupSteps":["Setup complexity is classified as HIGH. You must provision dedicated cloud infrastructure or an isolated VM. Do not run this directly on your local workstation.","Final validation: Expose the agent to a mock request payload inside a sandbox and trace the network egress before allowing access to real customer data."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-compass-fpx/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-compass-fpx/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-compass-fpx/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-compass-fpx/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-compass-fpx/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-compass-fpx/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-10T15:53:06.504Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-compass-fpx/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-compass-fpx/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-compass-fpx/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-chrischall-compass-fpx/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"high","updatedAt":"2026-10-10T13:39:32.277Z","emptyReason":null},"readme":"Skill: compass-fpx\n\nOwner: chrischall\n\nSummary: Query compass.com (US real-estate portal) from a shell with the fpx CLI (@fetchproxy/cli) instead of running the compass-mcp server — search listings, get property/agent detail, price history, and resolve street addresses through one-shot fetches over a signed-in browser tab. Use when you want Compass data without the MCP, in a script, or on a machine where the MCP isn't installed.\n\nTags: latest:1.1.9\n\nVersion history:\n\nv1.1.9 | 2026-10-09T23:26:19.319Z | auto\n\n- Removed the file skill-card.md.\n- No user-facing changes to functionality or documentation.\n\nv1.1.8 | 2026-10-07T13:35:25.423Z | auto\n\n- Removed the skill-card.md file.\n- No user-facing or behavioral changes; documentation and core usage unchanged.\n\nv1.1.7 | 2026-10-05T02:48:18.386Z | auto\n\n- Removed the file skill-card.md.\n- No changes to documentation or functionality; this is a minor cleanup release.\n\nv1.1.6 | 2026-10-03T01:41:18.949Z | auto\n\n- Removed the skill-card.md file.\n- No changes to core functionality or documentation in SKILL.md.\n\nv1.1.5 | 2026-09-28T13:58:03.590Z | auto\n\n- Updated all documentation to reference the extension's new name, \"ContextMint Bridge\", instead of \"fetchproxy extension\".\n- Clarified install and pairing instructions to reflect the rebranded browser extension and provided a link to its public releases.\n- Removed outdated file: skill-card.md.\n- No changes to code or core functionality; documentation only.\n\nv1.1.4 | 2026-09-25T15:53:39.114Z | auto\n\n- Removed the documentation file skill-card.md.\n- No changes to core functionality or user workflow.\n\nv1.1.3 | 2026-09-24T15:03:35.442Z | auto\n\n- Removed the skill-card.md file.\n- No changes to functionality or documentation in SKILL.md.\n- Maintenance update to clean up unused project files.\n\nv1.1.2 | 2026-09-23T21:38:19.046Z | auto\n\n- Removed the sample file skill-card.md.\n- No functional or user-facing changes to the main skill or documentation.\n\nv1.1.1 | 2026-09-23T15:44:59.153Z | auto\n\n- Removed the file skill-card.md.\n- No changes made to the SKILL.md documentation content or implementation.\n- This update is solely a cleanup of unused sample documentation.\n\nv1.1.0 | 2026-09-20T02:51:17.401Z | auto\n\n- Removed the file skill-card.md.\n- No functional or documentation changes in the core SKILL.md.\n- Minor internal cleanup; user-facing usage and instructions unchanged.\n\nv1.0.0 | 2026-09-17T23:36:36.974Z | auto\n\n- Removed the sample file skill-card.md.\n- No functional or behavioral changes; documentation and usage remain the same.\n\nv0.14.4 | 2026-09-15T19:27:35.918Z | auto\n\n- Removed the sample file skill-card.md.\n- No functional or user-facing changes; documentation and skill behavior remain unchanged.\n\nv0.14.3 | 2026-09-14T14:10:03.923Z | auto\n\n- Removed the sample file skill-card.md.\n- No changes to functionality or documentation in SKILL.md.\n\nv0.14.2 | 2026-09-10T17:50:33.008Z | auto\n\n- Removed the sample file skill-card.md.\n- No changes to functionality or documentation content.\n\nv0.14.1 | 2026-09-09T21:19:14.440Z | auto\n\n- Removed the file skill-card.md.\n- No functional or documentation changes in the main SKILL.md.\n- Maintenance update: cleanup of unused/legacy documentation file.\n\nv0.14.0 | 2026-09-04T22:21:41.295Z | auto\n\n- Removed the file skill-card.md.\n- No user-facing or functional changes; documentation and usage remain unchanged.\n\nv0.13.0 | 2026-08-29T13:53:57.071Z | auto\n\n- Removed the file: skill-card.md\n- No user-facing or functional changes to commands or documentation\n- Minor housekeeping/cleanup of project files\n\nv0.12.5 | 2026-08-28T21:07:10.156Z | auto\n\n- Removed the file skill-card.md.\n- No other functional or documentation changes in this version.\n\nv0.12.4 | 2026-08-28T11:34:18.295Z | auto\n\n- Removed the file skill-card.md.\n- No other functional or documentation changes introduced in this version.\n\nv0.12.3 | 2026-08-06T00:42:33.188Z | auto\n\n- Removed the sample file skill-card.md.\n- No user-facing or functional changes; only documentation/internal file cleanup.\n\nv0.12.2 | 2026-07-30T12:53:36.526Z | auto\n\n- Added detailed usage instructions and background for querying compass.com using the fpx CLI without the compass-mcp server.\n- Documented one-time setup, common command patterns, endpoint limitations, and tips for reliable data fetching.\n- Explained error conditions, bot-wall detection, and exit codes for robust scripting.\n- Provided guidance for pagination, listing URLs, and known limitations compared to compass-mcp.\n- Outlined requirements for fetchproxy extension, session handling, and best practices for extracting data.\n\nArchive index:\n\nArchive v1.1.9: 5 files, 9607 bytes\n\nFiles: references/extract-global.mjs (2851b), references/requests.md (7967b), skill-card.md (1968b), SKILL.md (5531b), _meta.json (130b)\n\nFile v1.1.9:SKILL.md\n\n---\nname: compass-fpx\ndescription: >-\n  Query compass.com (US real-estate portal) from a shell with the fpx CLI\n  (@fetchproxy/cli) instead of running the compass-mcp server — search\n  listings, get property/agent detail, price history, and resolve street\n  addresses through one-shot fetches over a signed-in browser tab. Use when\n  you want Compass data without the MCP, in a script, or on a machine where\n  the MCP isn't installed.\n---\n\n# Compass via fpx (no MCP)\n\nCompass has no public JSON data-API for search/listing/agent data — it's a\nfully server-rendered React app that embeds each page's data as JSON inside\nan inline `<script>` global (`global.uc`, `window.__INITIAL_DATA__`,\n`window.__AGENT_PROFILE__`). compass.com also runs **AWS WAF**, which 403s\nplain `curl`/Node requests on some paths regardless of headers. `fpx` routes\nevery request through the user's own signed-in browser tab (the ContextMint\nBridge extension), which already carries a cleared WAF session, so the page\nrenders normally — you then pull the JSON out of the HTML yourself.\n\nThis is **Bucket 1 (full-fetchproxy)**: every call, not just an auth\nbootstrap, rides the bridge. Compass validates at the session level per\nrequest, same as the `compass-mcp` server itself (no server-side path\nexists). No Compass login is required beyond having a signed-in tab open —\nsearch/listing/agent data and the address-typeahead endpoint are all public.\n\n## One-time setup\n\n```sh\nnpm install -g @fetchproxy/cli              # provides `fpx`\nfpx profile add compass --domain compass.com\nfpx pair -p compass                          # prints a pair code → approve in the ContextMint Bridge extension\n```\n\nRequirements: the **ContextMint Bridge** browser extension installed\n([releases](https://github.com/nullnet-app/contextmint-bridge/releases); it is the\nfetchproxy extension renamed, from the same maintainer, with public source and a\n`.sha256` beside each release zip), an open\n`www.compass.com` tab, and its Chrome **Site access** allowing `compass.com`.\nPairing persists — after the first approval every later `fpx` call reuses it.\n\n## Core call pattern\n\nTwo request shapes:\n\n1. **SSR pages** (search, homedetails, agent profile) — `fpx get` the path,\n   then pull the inline JSON global out of the HTML.\n2. **The one structured endpoint** (address typeahead) — `fpx post-json`.\n\n```sh\nfpx get 'https://www.compass.com/homes-for-sale/manhattan-ny/' -p compass > /tmp/page.html\nnode references/extract-global.mjs /tmp/page.html uc | jq '.sharedReactAppProps.initialResults.lolResults.data | length'\n```\n\n`references/extract-global.mjs` is a small, dependency-free re-implementation\nof the balanced-brace walk `compass-mcp`'s own `src/page-state.ts` uses\n(`extractGlobalAssign`) — Compass writes each global as a JSON *literal*\n(`global.uc = {...};`), so once the object is sliced out, `JSON.parse`/`jq`\nworks normally. Regexing for it without brace-balancing breaks the moment a\nnested value contains `}`.\n\nFull endpoint list, exact paths, and `jq` projections are in\n`references/requests.md`.\n\n## The one rule: try the typeahead before free-text search\n\nCompass's SSR free-text search (`/homes-for-sale/?q=<query>`) is WAF-walled\nin production and reliably 403s. For **address resolution**, always hit the\nstructured, WAF-immune typeahead first (`POST /api/v3/omnisuggest/autocomplete`)\nand only fall back to the SSR search pages when it returns nothing — mirror\n`compass-mcp`'s own rung order (see `references/requests.md` §4).\n\n## Sign-in / bot-wall detection\n\nA response is NOT a good page when either is true:\n\n- The final URL redirects to `/login` (fpx follows redirects like a browser,\n  so check the body for the login form, or pass `-H` / inspect with\n  `--json` to see the resolved URL).\n- The body contains **both** `awswaf.com` and `challenge.js`, and is under\n  ~80 KB (the AWS WAF challenge interstitial; a real signed-in page is much\n  larger). `compass-mcp`'s `throwIfSignInPage` uses this exact pair of\n  markers — grep for both before trusting a fetch:\n\n```sh\ngrep -q 'awswaf.com' /tmp/page.html && grep -q 'challenge.js' /tmp/page.html && \\\n  [ \"$(wc -c < /tmp/page.html)\" -lt 80000 ] && echo \"WAF-blocked — refresh the compass.com tab\"\n```\n\n## Exit codes (fetch verbs)\n\n- `0` — success.\n- `2` — bridge unavailable: extension not connected or pairing pending → `fpx pair -p compass`, confirm a compass.com tab is open.\n- `3` — bot wall: the tab hasn't cleared the AWS WAF challenge → refresh a `www.compass.com` tab and retry.\n- `4` — upstream non-2xx from Compass.\n\n## Notes\n\n- **Pagination ceiling.** Compass's SSR search only server-renders its first\n  page (~41 listings); `/page-N/` canonicalizes back to page 1 and returns\n  identical data. To reach more of the market, narrow with price/bed bands\n  (`/<lo>-<hi>-bed/`, `/<lo>-<hi>-price/`) and re-search each band — don't\n  paginate.\n- **Two URL forms per listing.** `_pid/` (`navigationPageLink`) is a stable\n  opaque id — use it for bookmarks/trackers. `_lid/` (content-addressed by\n  `listingIdSHA`) is what you fetch to read the *current* record, but it\n  changes when a property is delisted and relisted.\n- No saved-homes/saved-searches or market-trends endpoint exists server-side\n  (client-rendered GraphQL / not exposed) — `compass-mcp` stubs those tools\n  out too; this skill doesn't cover them.\n- `fpx health -p compass` shows bridge connection state when a call fails.\n- This project is developed and maintained by AI (Claude).\n\nFile v1.1.9:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"compass-fpx\",\n  \"version\": \"1.1.9\",\n  \"publishedAt\": 1791588379319\n}\n\nFile v1.1.9:references/requests.md\n\n# Compass requests for fpx\n\nAll paths are relative to `https://www.compass.com`. GET requests return HTML\nwith an embedded JSON global — pull it out with `extract-global.mjs` (in this\nsame directory) before piping to `jq`. The one structured endpoint (§4) is a\nplain JSON POST.\n\n```sh\nfpx get 'https://www.compass.com/<path>' -p compass > /tmp/page.html\nnode extract-global.mjs /tmp/page.html <globalName> | jq '...'\n```\n\nEndpoints and field shapes below are transcribed from `compass-mcp`'s\n`src/tools/*.ts` and `src/page-state.ts` — the same live-verified paths and\nextraction targets the MCP tools use.\n\n---\n\n## 1. Search listings\n\n`GET /homes-for-sale/<location-slug>/[<filters>/]`\n\n- `<location-slug>`: lowercase, hyphenated free text (`\"Brooklyn, NY\"` →\n  `brooklyn-ny`, `\"94110\"` → `94110`).\n- Filters are path segments, in this order: `<bedsMin>-<bedsMax>-bed/`,\n  `<priceMin>-<priceMax>-price/`, a home-type slug (`type-house`,\n  `type-condo`, `type-townhouse`, `type-multi-family`, `type-land`,\n  `type-rental`). Omit any filter you don't need.\n- **Pagination ceiling**: Compass server-renders only ~41 listings into this\n  page (`num`); `/page-N/` canonicalizes back to page 1 with identical data.\n  To see more of the market, narrow the price/bed bands and re-search each\n  band — there is no further page to fetch.\n\n```sh\nfpx get 'https://www.compass.com/homes-for-sale/brooklyn-ny/2-3-bed/500000-1500000-price/type-condo/' -p compass > /tmp/search.html\nnode extract-global.mjs /tmp/search.html uc > /tmp/search.json\n```\n\nExtraction path: `.sharedReactAppProps.initialResults.lolResults`\n(`{ totalItems, data: [ { listing: {...} } ] }`).\n\n```sh\njq -r '.sharedReactAppProps.initialResults.lolResults |\n  \"total=\\(.totalItems)\",\n  (.data[].listing |\n    \"\\(.listingIdSHA)\\t\\(.subtitles[0] // \"\")\\t\\(.title // \"\")\\t\\(.subStats // [] | map(select(.title==\"beds\")) | .[0].subtitle // \"\")bd\")' \\\n  /tmp/search.json\n```\n\nPer-listing fields worth pulling: `listingIdSHA`, `pageLink` (the `_lid/`\ndetail URL, relative), `navigationPageLink` (the stable `_pid/` URL when\npresent), `title` (formatted price string), `subtitles` (`[street,\nneighborhood]`), `status`, `location.{latitude,longitude}`,\n`media[].{originalUrl,thumbnailUrl,category}` (`category:0` = photo),\n`subStats[]` (`{title:\"beds\"|\"baths\"|\"sqft\", subtitle:\"<n>\"}`),\n`clusterSummary.{bedrooms,bathrooms,priceRange:[min,max],formattedLotSize,savedListing}`.\n\n**Comparable rentals** reuse this exact path shape with the rental filter:\n`/homes-for-sale/<locality-slug>/type-rental/` — same extraction.\n\n**Free-text SSR search** (`/homes-for-sale/?q=<query>`) exists but is\nWAF-walled in production (reliably 403s) — don't rely on it; use §4 for\naddress resolution instead.\n\n---\n\n## 2. Property / listing detail\n\n`GET /homedetails/<slug>/<listingIdSHA>_lid/`\n\nIf you only have the `listingIdSHA` (no slug), fetch\n`GET /listing/<listingIdSHA>/view` instead — it 302-redirects to the\ncanonical slugged homedetails URL, and fpx follows the redirect like a\nbrowser.\n\n```sh\nfpx get 'https://www.compass.com/homedetails/162-04-12th-Rd-Queens-NY-11357/2109718971930079225_lid/' -p compass > /tmp/detail.html\n# or, sha-only:\nfpx get 'https://www.compass.com/listing/2109718971930079225/view' -p compass > /tmp/detail.html\n\nnode extract-global.mjs /tmp/detail.html __INITIAL_DATA__ > /tmp/detail.json\n```\n\nExtraction path: `.props.listingRelation.listing`.\n\n```sh\njq '.props.listingRelation.listing |\n  {address: .location.prettyAddress, city: .location.city, state: .location.state, zip: .location.zipCode,\n   price: .price.lastKnown, price_formatted: .price.formatted, per_sqft: .price.perSquareFoot,\n   beds: .size.bedrooms, baths: .size.totalBathrooms, sqft: .size.squareFeet,\n   lot_sqft: .size.lotSizeInSquareFeet, status: .localizedStatus, mls_status: .mlsStatus,\n   photos: [.media[]?.originalUrl], schools: .detailedInfo.schools,\n   agent: .agents[0]}' /tmp/detail.json\n```\n\nOther useful top-level fields: `compassPropertyId`, `pageLink` (`_lid/` URL),\n`navigationPageLink` (stable `_pid/` URL), `description`,\n`detailedInfo.{amenities,taxAnnualAmount,associationFee:{amount,frequency},\narchitecturalStyle,garageSpaces,totalParkingSpaces}`,\n`events[]`/`history[]` (see §3), `mlsAlternateAddresses[]`,\n`agents[].{id,fullName,companyName,profileUrl}`.\n\n**Sentinel values**: `detailedInfo.taxAnnualAmount` under `10` is a\nnot-yet-assessed placeholder on new construction, not a real tax bill.\n\n---\n\n## 3. Price history\n\nSame fetch + extraction as §2 (`__INITIAL_DATA__.props.listingRelation.listing`).\nRead `events[]` (this listing's own MLS events) and `history[]` (events from\nprior listings of the same property) — each entry:\n`{ price, status, localizedStatus, timestamp (unix ms), pageLink, source:{externalSourceName} }`.\n\n```sh\njq '.props.listingRelation.listing | {events, history}' /tmp/detail.json\n# Earliest \"Listed\" event (days-on-market anchor):\njq -r '.props.listingRelation.listing.events // [] |\n  map(select(.status==1 or (.localizedStatus|test(\"^listed$\";\"i\")))) |\n  sort_by(.timestamp) | .[0] // empty' /tmp/detail.json\n```\n\n---\n\n## 4. Address typeahead (resolve an address → listing id) — WAF-immune, try FIRST\n\n`POST /api/v3/omnisuggest/autocomplete`\n\nBody: `{\"q\": \"<street + city + state>\", \"sources\": [0]}` — `sources: [0]`\nrestricts results to the Addresses source.\n\n```sh\ncat > /tmp/omni.json <<'JSON'\n{\"q\": \"158 Raven Blvd Lake Lure NC\", \"sources\": [0]}\nJSON\nfpx post-json 'https://www.compass.com/api/v3/omnisuggest/autocomplete' @/tmp/omni.json -p compass \\\n  | jq '.categories[] | select(.name==1) | .items[] | {id, text, subText, redirectUrl}'\n```\n\nResponse shape: `{ categories: [ { name, label, items: [ { text, subText,\nredirectUrl, source, id, ucGeoId } ] } ], success, rankerVersion }`. Category\n`name === 1` is \"Addresses\". `id` **is** the listing's `listingIdSHA` — it\nmatches the `_lid/` segment on the public homedetails page, so it feeds\nstraight into §2's sha-only fetch (`/listing/<id>/view`).\n\n**Verify the match before trusting it** — the query terms should appear as\nwhole tokens in `text`/`subText` (case + street-suffix folded, e.g.\n\"Ln\"/\"Lane\"); Compass's suggest can return an unrelated top hit when the\nlocal market has no real match. `compass-mcp`'s rung order is: try this\ntypeahead first, and only fall back to the (WAF-walled) free-text/slug SSR\nsearch in §1 if it returns nothing verified.\n\n---\n\n## 5. Agent's listings\n\n`GET /agents/<slug>/` (e.g. `/agents/paige-mcguirk/`)\n\n```sh\nfpx get 'https://www.compass.com/agents/paige-mcguirk/' -p compass > /tmp/agent.html\nnode extract-global.mjs /tmp/agent.html __AGENT_PROFILE__ > /tmp/agent.json\n```\n\nExtraction paths (same `listing` shape as §2's `listingRelation.listing`):\n\n- Active listings: `.data.agentProfileProps.activeListingsProps.initialSales[]`\n- Closed deals: `.data.agentProfileProps.closedDealsProps.initialSales[].listing`\n  (each entry wraps the listing under a `.listing` key, alongside `aiHexID`).\n\n```sh\njq '.data.agentProfileProps.activeListingsProps.initialSales[] |\n  {sha: .listingIdSHA, address: .location.prettyAddress, price: .price.formatted, status: .localizedStatus}' \\\n  /tmp/agent.json\n\njq '.data.agentProfileProps.closedDealsProps.initialSales[].listing |\n  {sha: .listingIdSHA, address: .location.prettyAddress, close_price: .price.lastPropertyClosePrice}' \\\n  /tmp/agent.json\n```\n\nThis page is large (~1.4 MB rendered); the `extract-global.mjs` walk handles\nthat fine, but expect the fetch itself to take a few seconds.\n\n---\n\n## Not covered (no server-reachable endpoint)\n\n- **Saved homes / saved searches** — `/overview/favorites` is fully\n  client-rendered via auth-scoped GraphQL the bridge can't intercept.\n  `compass-mcp` stubs these tools out too; there's no fpx recipe for them.\n- **Market trends / stats** — Compass doesn't expose region-level market\n  data in any SSR blob (unlike some other portals' dedicated endpoint).\n\nFile v1.1.9:skill-card.md\n\n## Description:\n\nHelps agents query Compass real-estate listings, property details, price histories, and agent profiles through the fpx CLI and a paired browser tab.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and agents use this skill to search US real-estate listings, inspect property and agent details, check price history, and resolve addresses from Compass without running an MCP server.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Installing a global CLI and browser extension adds software with access to browser-mediated requests.\n\nMitigation: Install only if comfortable with both components; use a dedicated browser profile or Compass session and limit extension site access to compass.com.\n\nRisk: The browser bridge pairing persists and can expose browsing-session data to later requests.\n\nMitigation: Remove the pairing when no longer needed and keep the browser session limited to the intended Compass use.\n\n## Reference(s):\n\n- [Compass fpx request recipes](references/requests.md)\n- [Compass fpx release](https://clawhub.ai/chrischall/skills/compass-fpx)\n- [ContextMint Bridge releases](https://github.com/nullnet-app/contextmint-bridge/releases)\n\n## Skill Output:\n\n**Output Type(s):** [Text, JSON, Shell commands, Guidance]\n\n**Output Format:** [Text or JSON with shell command examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Listing results depend on the available Compass pages and the paired browser session.]\n\n## Skill Version(s):\n\n1.1.9 (source: ClawHub release metadata)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.1.8: 5 files, 9500 bytes\n\nFiles: references/extract-global.mjs (2851b), references/requests.md (7967b), skill-card.md (1676b), SKILL.md (5531b), _meta.json (130b)\n\nFile v1.1.8:SKILL.md\n\n---\nname: compass-fpx\ndescription: >-\n  Query compass.com (US real-estate portal) from a shell with the fpx CLI\n  (@fetchproxy/cli) instead of running the compass-mcp server — search\n  listings, get property/agent detail, price history, and resolve street\n  addresses through one-shot fetches over a signed-in browser tab. Use when\n  you want Compass data without the MCP, in a script, or on a machine where\n  the MCP isn't installed.\n---\n\n# Compass via fpx (no MCP)\n\nCompass has no public JSON data-API for search/listing/agent data — it's a\nfully server-rendered React app that embeds each page's data as JSON inside\nan inline `<script>` global (`global.uc`, `window.__INITIAL_DATA__`,\n`window.__AGENT_PROFILE__`). compass.com also runs **AWS WAF**, which 403s\nplain `curl`/Node requests on some paths regardless of headers. `fpx` routes\nevery request through the user's own signed-in browser tab (the ContextMint\nBridge extension), which already carries a cleared WAF session, so the page\nrenders normally — you then pull the JSON out of the HTML yourself.\n\nThis is **Bucket 1 (full-fetchproxy)**: every call, not just an auth\nbootstrap, rides the bridge. Compass validates at the session level per\nrequest, same as the `compass-mcp` server itself (no server-side path\nexists). No Compass login is required beyond having a signed-in tab open —\nsearch/listing/agent data and the address-typeahead endpoint are all public.\n\n## One-time setup\n\n```sh\nnpm install -g @fetchproxy/cli              # provides `fpx`\nfpx profile add compass --domain compass.com\nfpx pair -p compass                          # prints a pair code → approve in the ContextMint Bridge extension\n```\n\nRequirements: the **ContextMint Bridge** browser extension installed\n([releases](https://github.com/nullnet-app/contextmint-bridge/releases); it is the\nfetchproxy extension renamed, from the same maintainer, with public source and a\n`.sha256` beside each release zip), an open\n`www.compass.com` tab, and its Chrome **Site access** allowing `compass.com`.\nPairing persists — after the first approval every later `fpx` call reuses it.\n\n## Core call pattern\n\nTwo request shapes:\n\n1. **SSR pages** (search, homedetails, agent profile) — `fpx get` the path,\n   then pull the inline JSON global out of the HTML.\n2. **The one structured endpoint** (address typeahead) — `fpx post-json`.\n\n```sh\nfpx get 'https://www.compass.com/homes-for-sale/manhattan-ny/' -p compass > /tmp/page.html\nnode references/extract-global.mjs /tmp/page.html uc | jq '.sharedReactAppProps.initialResults.lolResults.data | length'\n```\n\n`references/extract-global.mjs` is a small, dependency-free re-implementation\nof the balanced-brace walk `compass-mcp`'s own `src/page-state.ts` uses\n(`extractGlobalAssign`) — Compass writes each global as a JSON *literal*\n(`global.uc = {...};`), so once the object is sliced out, `JSON.parse`/`jq`\nworks normally. Regexing for it without brace-balancing breaks the moment a\nnested value contains `}`.\n\nFull endpoint list, exact paths, and `jq` projections are in\n`references/requests.md`.\n\n## The one rule: try the typeahead before free-text search\n\nCompass's SSR free-text search (`/homes-for-sale/?q=<query>`) is WAF-walled\nin production and reliably 403s. For **address resolution**, always hit the\nstructured, WAF-immune typeahead first (`POST /api/v3/omnisuggest/autocomplete`)\nand only fall back to the SSR search pages when it returns nothing — mirror\n`compass-mcp`'s own rung order (see `references/requests.md` §4).\n\n## Sign-in / bot-wall detection\n\nA response is NOT a good page when either is true:\n\n- The final URL redirects to `/login` (fpx follows redirects like a browser,\n  so check the body for the login form, or pass `-H` / inspect with\n  `--json` to see the resolved URL).\n- The body contains **both** `awswaf.com` and `challenge.js`, and is under\n  ~80 KB (the AWS WAF challenge interstitial; a real signed-in page is much\n  larger). `compass-mcp`'s `throwIfSignInPage` uses this exact pair of\n  markers — grep for both before trusting a fetch:\n\n```sh\ngrep -q 'awswaf.com' /tmp/page.html && grep -q 'challenge.js' /tmp/page.html && \\\n  [ \"$(wc -c < /tmp/page.html)\" -lt 80000 ] && echo \"WAF-blocked — refresh the compass.com tab\"\n```\n\n## Exit codes (fetch verbs)\n\n- `0` — success.\n- `2` — bridge unavailable: extension not connected or pairing pending → `fpx pair -p compass`, confirm a compass.com tab is open.\n- `3` — bot wall: the tab hasn't cleared the AWS WAF challenge → refresh a `www.compass.com` tab and retry.\n- `4` — upstream non-2xx from Compass.\n\n## Notes\n\n- **Pagination ceiling.** Compass's SSR search only server-renders its first\n  page (~41 listings); `/page-N/` canonicalizes back to page 1 and returns\n  identical data. To reach more of the market, narrow with price/bed bands\n  (`/<lo>-<hi>-bed/`, `/<lo>-<hi>-price/`) and re-search each band — don't\n  paginate.\n- **Two URL forms per listing.** `_pid/` (`navigationPageLink`) is a stable\n  opaque id — use it for bookmarks/trackers. `_lid/` (content-addressed by\n  `listingIdSHA`) is what you fetch to read the *current* record, but it\n  changes when a property is delisted and relisted.\n- No saved-homes/saved-searches or market-trends endpoint exists server-side\n  (client-rendered GraphQL / not exposed) — `compass-mcp` stubs those tools\n  out too; this skill doesn't cover them.\n- `fpx health -p compass` shows bridge connection state when a call fails.\n- This project is developed and maintained by AI (Claude).\n\nFile v1.1.8:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"compass-fpx\",\n  \"version\": \"1.1.8\",\n  \"publishedAt\": 1791380125423\n}\n\nFile v1.1.8:references/requests.md\n\n# Compass requests for fpx\n\nAll paths are relative to `https://www.compass.com`. GET requests return HTML\nwith an embedded JSON global — pull it out with `extract-global.mjs` (in this\nsame directory) before piping to `jq`. The one structured endpoint (§4) is a\nplain JSON POST.\n\n```sh\nfpx get 'https://www.compass.com/<path>' -p compass > /tmp/page.html\nnode extract-global.mjs /tmp/page.html <globalName> | jq '...'\n```\n\nEndpoints and field shapes below are transcribed from `compass-mcp`'s\n`src/tools/*.ts` and `src/page-state.ts` — the same live-verified paths and\nextraction targets the MCP tools use.\n\n---\n\n## 1. Search listings\n\n`GET /homes-for-sale/<location-slug>/[<filters>/]`\n\n- `<location-slug>`: lowercase, hyphenated free text (`\"Brooklyn, NY\"` →\n  `brooklyn-ny`, `\"94110\"` → `94110`).\n- Filters are path segments, in this order: `<bedsMin>-<bedsMax>-bed/`,\n  `<priceMin>-<priceMax>-price/`, a home-type slug (`type-house`,\n  `type-condo`, `type-townhouse`, `type-multi-family`, `type-land`,\n  `type-rental`). Omit any filter you don't need.\n- **Pagination ceiling**: Compass server-renders only ~41 listings into this\n  page (`num`); `/page-N/` canonicalizes back to page 1 with identical data.\n  To see more of the market, narrow the price/bed bands and re-search each\n  band — there is no further page to fetch.\n\n```sh\nfpx get 'https://www.compass.com/homes-for-sale/brooklyn-ny/2-3-bed/500000-1500000-price/type-condo/' -p compass > /tmp/search.html\nnode extract-global.mjs /tmp/search.html uc > /tmp/search.json\n```\n\nExtraction path: `.sharedReactAppProps.initialResults.lolResults`\n(`{ totalItems, data: [ { listing: {...} } ] }`).\n\n```sh\njq -r '.sharedReactAppProps.initialResults.lolResults |\n  \"total=\\(.totalItems)\",\n  (.data[].listing |\n    \"\\(.listingIdSHA)\\t\\(.subtitles[0] // \"\")\\t\\(.title // \"\")\\t\\(.subStats // [] | map(select(.title==\"beds\")) | .[0].subtitle // \"\")bd\")' \\\n  /tmp/search.json\n```\n\nPer-listing fields worth pulling: `listingIdSHA`, `pageLink` (the `_lid/`\ndetail URL, relative), `navigationPageLink` (the stable `_pid/` URL when\npresent), `title` (formatted price string), `subtitles` (`[street,\nneighborhood]`), `status`, `location.{latitude,longitude}`,\n`media[].{originalUrl,thumbnailUrl,category}` (`category:0` = photo),\n`subStats[]` (`{title:\"beds\"|\"baths\"|\"sqft\", subtitle:\"<n>\"}`),\n`clusterSummary.{bedrooms,bathrooms,priceRange:[min,max],formattedLotSize,savedListing}`.\n\n**Comparable rentals** reuse this exact path shape with the rental filter:\n`/homes-for-sale/<locality-slug>/type-rental/` — same extraction.\n\n**Free-text SSR search** (`/homes-for-sale/?q=<query>`) exists but is\nWAF-walled in production (reliably 403s) — don't rely on it; use §4 for\naddress resolution instead.\n\n---\n\n## 2. Property / listing detail\n\n`GET /homedetails/<slug>/<listingIdSHA>_lid/`\n\nIf you only have the `listingIdSHA` (no slug), fetch\n`GET /listing/<listingIdSHA>/view` instead — it 302-redirects to the\ncanonical slugged homedetails URL, and fpx follows the redirect like a\nbrowser.\n\n```sh\nfpx get 'https://www.compass.com/homedetails/162-04-12th-Rd-Queens-NY-11357/2109718971930079225_lid/' -p compass > /tmp/detail.html\n# or, sha-only:\nfpx get 'https://www.compass.com/listing/2109718971930079225/view' -p compass > /tmp/detail.html\n\nnode extract-global.mjs /tmp/detail.html __INITIAL_DATA__ > /tmp/detail.json\n```\n\nExtraction path: `.props.listingRelation.listing`.\n\n```sh\njq '.props.listingRelation.listing |\n  {address: .location.prettyAddress, city: .location.city, state: .location.state, zip: .location.zipCode,\n   price: .price.lastKnown, price_formatted: .price.formatted, per_sqft: .price.perSquareFoot,\n   beds: .size.bedrooms, baths: .size.totalBathrooms, sqft: .size.squareFeet,\n   lot_sqft: .size.lotSizeInSquareFeet, status: .localizedStatus, mls_status: .mlsStatus,\n   photos: [.media[]?.originalUrl], schools: .detailedInfo.schools,\n   agent: .agents[0]}' /tmp/detail.json\n```\n\nOther useful top-level fields: `compassPropertyId`, `pageLink` (`_lid/` URL),\n`navigationPageLink` (stable `_pid/` URL), `description`,\n`detailedInfo.{amenities,taxAnnualAmount,associationFee:{amount,frequency},\narchitecturalStyle,garageSpaces,totalParkingSpaces}`,\n`events[]`/`history[]` (see §3), `mlsAlternateAddresses[]`,\n`agents[].{id,fullName,companyName,profileUrl}`.\n\n**Sentinel values**: `detailedInfo.taxAnnualAmount` under `10` is a\nnot-yet-assessed placeholder on new construction, not a real tax bill.\n\n---\n\n## 3. Price history\n\nSame fetch + extraction as §2 (`__INITIAL_DATA__.props.listingRelation.listing`).\nRead `events[]` (this listing's own MLS events) and `history[]` (events from\nprior listings of the same property) — each entry:\n`{ price, status, localizedStatus, timestamp (unix ms), pageLink, source:{externalSourceName} }`.\n\n```sh\njq '.props.listingRelation.listing | {events, history}' /tmp/detail.json\n# Earliest \"Listed\" event (days-on-market anchor):\njq -r '.props.listingRelation.listing.events // [] |\n  map(select(.status==1 or (.localizedStatus|test(\"^listed$\";\"i\")))) |\n  sort_by(.timestamp) | .[0] // empty' /tmp/detail.json\n```\n\n---\n\n## 4. Address typeahead (resolve an address → listing id) — WAF-immune, try FIRST\n\n`POST /api/v3/omnisuggest/autocomplete`\n\nBody: `{\"q\": \"<street + city + state>\", \"sources\": [0]}` — `sources: [0]`\nrestricts results to the Addresses source.\n\n```sh\ncat > /tmp/omni.json <<'JSON'\n{\"q\": \"158 Raven Blvd Lake Lure NC\", \"sources\": [0]}\nJSON\nfpx post-json 'https://www.compass.com/api/v3/omnisuggest/autocomplete' @/tmp/omni.json -p compass \\\n  | jq '.categories[] | select(.name==1) | .items[] | {id, text, subText, redirectUrl}'\n```\n\nResponse shape: `{ categories: [ { name, label, items: [ { text, subText,\nredirectUrl, source, id, ucGeoId } ] } ], success, rankerVersion }`. Category\n`name === 1` is \"Addresses\". `id` **is** the listing's `listingIdSHA` — it\nmatches the `_lid/` segment on the public homedetails page, so it feeds\nstraight into §2's sha-only fetch (`/listing/<id>/view`).\n\n**Verify the match before trusting it** — the query terms should appear as\nwhole tokens in `text`/`subText` (case + street-suffix folded, e.g.\n\"Ln\"/\"Lane\"); Compass's suggest can return an unrelated top hit when the\nlocal market has no real match. `compass-mcp`'s rung order is: try this\ntypeahead first, and only fall back to the (WAF-walled) free-text/slug SSR\nsearch in §1 if it returns nothing verified.\n\n---\n\n## 5. Agent's listings\n\n`GET /agents/<slug>/` (e.g. `/agents/paige-mcguirk/`)\n\n```sh\nfpx get 'https://www.compass.com/agents/paige-mcguirk/' -p compass > /tmp/agent.html\nnode extract-global.mjs /tmp/agent.html __AGENT_PROFILE__ > /tmp/agent.json\n```\n\nExtraction paths (same `listing` shape as §2's `listingRelation.listing`):\n\n- Active listings: `.data.agentProfileProps.activeListingsProps.initialSales[]`\n- Closed deals: `.data.agentProfileProps.closedDealsProps.initialSales[].listing`\n  (each entry wraps the listing under a `.listing` key, alongside `aiHexID`).\n\n```sh\njq '.data.agentProfileProps.activeListingsProps.initialSales[] |\n  {sha: .listingIdSHA, address: .location.prettyAddress, price: .price.formatted, status: .localizedStatus}' \\\n  /tmp/agent.json\n\njq '.data.agentProfileProps.closedDealsProps.initialSales[].listing |\n  {sha: .listingIdSHA, address: .location.prettyAddress, close_price: .price.lastPropertyClosePrice}' \\\n  /tmp/agent.json\n```\n\nThis page is large (~1.4 MB rendered); the `extract-global.mjs` walk handles\nthat fine, but expect the fetch itself to take a few seconds.\n\n---\n\n## Not covered (no server-reachable endpoint)\n\n- **Saved homes / saved searches** — `/overview/favorites` is fully\n  client-rendered via auth-scoped GraphQL the bridge can't intercept.\n  `compass-mcp` stubs these tools out too; there's no fpx recipe for them.\n- **Market trends / stats** — Compass doesn't expose region-level market\n  data in any SSR blob (unlike some other portals' dedicated endpoint).\n\nFile v1.1.8:skill-card.md\n\n## Description:\n\nGuides agents to query Compass real-estate listings, property and agent details, price history, and address suggestions with fpx through an active browser session.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and agents use this skill to find US real-estate listings, inspect property and agent details, and resolve addresses without running the Compass MCP server.\n\n### Deployment Geography for Use:\n\nUnited States\n\n## Known Risks and Mitigations:\n\nRisk: Compass requests use the active browser session and may share its cookies or browsing state.\n\nMitigation: Use a dedicated browser profile to isolate Compass cookies and browsing state, and install only if comfortable with this access.\n\n## Reference(s):\n\n- [Compass request recipes](references/requests.md)\n- [Compass page-data extraction helper](references/extract-global.mjs)\n- [ContextMint Bridge releases](https://github.com/nullnet-app/contextmint-bridge/releases)\n\n## Skill Output:\n\n**Output Type(s):** [Text, Shell commands, Code, Guidance]\n\n**Output Format:** [Markdown with shell and jq examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Recipes can extract JSON from Compass pages and responses.]\n\n## Skill Version(s):\n\n1.1.8 (source: ClawHub release metadata)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.1.7: 5 files, 9555 bytes\n\nFiles: references/extract-global.mjs (2851b), references/requests.md (7967b), skill-card.md (1823b), SKILL.md (5531b), _meta.json (130b)\n\nFile v1.1.7:SKILL.md\n\n---\nname: compass-fpx\ndescription: >-\n  Query compass.com (US real-estate portal) from a shell with the fpx CLI\n  (@fetchproxy/cli) instead of running the compass-mcp server — search\n  listings, get property/agent detail, price history, and resolve street\n  addresses through one-shot fetches over a signed-in browser tab. Use when\n  you want Compass data without the MCP, in a script, or on a machine where\n  the MCP isn't installed.\n---\n\n# Compass via fpx (no MCP)\n\nCompass has no public JSON data-API for search/listing/agent data — it's a\nfully server-rendered React app that embeds each page's data as JSON inside\nan inline `<script>` global (`global.uc`, `window.__INITIAL_DATA__`,\n`window.__AGENT_PROFILE__`). compass.com also runs **AWS WAF**, which 403s\nplain `curl`/Node requests on some paths regardless of headers. `fpx` routes\nevery request through the user's own signed-in browser tab (the ContextMint\nBridge extension), which already carries a cleared WAF session, so the page\nrenders normally — you then pull the JSON out of the HTML yourself.\n\nThis is **Bucket 1 (full-fetchproxy)**: every call, not just an auth\nbootstrap, rides the bridge. Compass validates at the session level per\nrequest, same as the `compass-mcp` server itself (no server-side path\nexists). No Compass login is required beyond having a signed-in tab open —\nsearch/listing/agent data and the address-typeahead endpoint are all public.\n\n## One-time setup\n\n```sh\nnpm install -g @fetchproxy/cli              # provides `fpx`\nfpx profile add compass --domain compass.com\nfpx pair -p compass                          # prints a pair code → approve in the ContextMint Bridge extension\n```\n\nRequirements: the **ContextMint Bridge** browser extension installed\n([releases](https://github.com/nullnet-app/contextmint-bridge/releases); it is the\nfetchproxy extension renamed, from the same maintainer, with public source and a\n`.sha256` beside each release zip), an open\n`www.compass.com` tab, and its Chrome **Site access** allowing `compass.com`.\nPairing persists — after the first approval every later `fpx` call reuses it.\n\n## Core call pattern\n\nTwo request shapes:\n\n1. **SSR pages** (search, homedetails, agent profile) — `fpx get` the path,\n   then pull the inline JSON global out of the HTML.\n2. **The one structured endpoint** (address typeahead) — `fpx post-json`.\n\n```sh\nfpx get 'https://www.compass.com/homes-for-sale/manhattan-ny/' -p compass > /tmp/page.html\nnode references/extract-global.mjs /tmp/page.html uc | jq '.sharedReactAppProps.initialResults.lolResults.data | length'\n```\n\n`references/extract-global.mjs` is a small, dependency-free re-implementation\nof the balanced-brace walk `compass-mcp`'s own `src/page-state.ts` uses\n(`extractGlobalAssign`) — Compass writes each global as a JSON *literal*\n(`global.uc = {...};`), so once the object is sliced out, `JSON.parse`/`jq`\nworks normally. Regexing for it without brace-balancing breaks the moment a\nnested value contains `}`.\n\nFull endpoint list, exact paths, and `jq` projections are in\n`references/requests.md`.\n\n## The one rule: try the typeahead before free-text search\n\nCompass's SSR free-text search (`/homes-for-sale/?q=<query>`) is WAF-walled\nin production and reliably 403s. For **address resolution**, always hit the\nstructured, WAF-immune typeahead first (`POST /api/v3/omnisuggest/autocomplete`)\nand only fall back to the SSR search pages when it returns nothing — mirror\n`compass-mcp`'s own rung order (see `references/requests.md` §4).\n\n## Sign-in / bot-wall detection\n\nA response is NOT a good page when either is true:\n\n- The final URL redirects to `/login` (fpx follows redirects like a browser,\n  so check the body for the login form, or pass `-H` / inspect with\n  `--json` to see the resolved URL).\n- The body contains **both** `awswaf.com` and `challenge.js`, and is under\n  ~80 KB (the AWS WAF challenge interstitial; a real signed-in page is much\n  larger). `compass-mcp`'s `throwIfSignInPage` uses this exact pair of\n  markers — grep for both before trusting a fetch:\n\n```sh\ngrep -q 'awswaf.com' /tmp/page.html && grep -q 'challenge.js' /tmp/page.html && \\\n  [ \"$(wc -c < /tmp/page.html)\" -lt 80000 ] && echo \"WAF-blocked — refresh the compass.com tab\"\n```\n\n## Exit codes (fetch verbs)\n\n- `0` — success.\n- `2` — bridge unavailable: extension not connected or pairing pending → `fpx pair -p compass`, confirm a compass.com tab is open.\n- `3` — bot wall: the tab hasn't cleared the AWS WAF challenge → refresh a `www.compass.com` tab and retry.\n- `4` — upstream non-2xx from Compass.\n\n## Notes\n\n- **Pagination ceiling.** Compass's SSR search only server-renders its first\n  page (~41 listings); `/page-N/` canonicalizes back to page 1 and returns\n  identical data. To reach more of the market, narrow with price/bed bands\n  (`/<lo>-<hi>-bed/`, `/<lo>-<hi>-price/`) and re-search each band — don't\n  paginate.\n- **Two URL forms per listing.** `_pid/` (`navigationPageLink`) is a stable\n  opaque id — use it for bookmarks/trackers. `_lid/` (content-addressed by\n  `listingIdSHA`) is what you fetch to read the *current* record, but it\n  changes when a property is delisted and relisted.\n- No saved-homes/saved-searches or market-trends endpoint exists server-side\n  (client-rendered GraphQL / not exposed) — `compass-mcp` stubs those tools\n  out too; this skill doesn't cover them.\n- `fpx health -p compass` shows bridge connection state when a call fails.\n- This project is developed and maintained by AI (Claude).\n\nFile v1.1.7:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"compass-fpx\",\n  \"version\": \"1.1.7\",\n  \"publishedAt\": 1791168498386\n}\n\nFile v1.1.7:references/requests.md\n\n# Compass requests for fpx\n\nAll paths are relative to `https://www.compass.com`. GET requests return HTML\nwith an embedded JSON global — pull it out with `extract-global.mjs` (in this\nsame directory) before piping to `jq`. The one structured endpoint (§4) is a\nplain JSON POST.\n\n```sh\nfpx get 'https://www.compass.com/<path>' -p compass > /tmp/page.html\nnode extract-global.mjs /tmp/page.html <globalName> | jq '...'\n```\n\nEndpoints and field shapes below are transcribed from `compass-mcp`'s\n`src/tools/*.ts` and `src/page-state.ts` — the same live-verified paths and\nextraction targets the MCP tools use.\n\n---\n\n## 1. Search listings\n\n`GET /homes-for-sale/<location-slug>/[<filters>/]`\n\n- `<location-slug>`: lowercase, hyphenated free text (`\"Brooklyn, NY\"` →\n  `brooklyn-ny`, `\"94110\"` → `94110`).\n- Filters are path segments, in this order: `<bedsMin>-<bedsMax>-bed/`,\n  `<priceMin>-<priceMax>-price/`, a home-type slug (`type-house`,\n  `type-condo`, `type-townhouse`, `type-multi-family`, `type-land`,\n  `type-rental`). Omit any filter you don't need.\n- **Pagination ceiling**: Compass server-renders only ~41 listings into this\n  page (`num`); `/page-N/` canonicalizes back to page 1 with identical data.\n  To see more of the market, narrow the price/bed bands and re-search each\n  band — there is no further page to fetch.\n\n```sh\nfpx get 'https://www.compass.com/homes-for-sale/brooklyn-ny/2-3-bed/500000-1500000-price/type-condo/' -p compass > /tmp/search.html\nnode extract-global.mjs /tmp/search.html uc > /tmp/search.json\n```\n\nExtraction path: `.sharedReactAppProps.initialResults.lolResults`\n(`{ totalItems, data: [ { listing: {...} } ] }`).\n\n```sh\njq -r '.sharedReactAppProps.initialResults.lolResults |\n  \"total=\\(.totalItems)\",\n  (.data[].listing |\n    \"\\(.listingIdSHA)\\t\\(.subtitles[0] // \"\")\\t\\(.title // \"\")\\t\\(.subStats // [] | map(select(.title==\"beds\")) | .[0].subtitle // \"\")bd\")' \\\n  /tmp/search.json\n```\n\nPer-listing fields worth pulling: `listingIdSHA`, `pageLink` (the `_lid/`\ndetail URL, relative), `navigationPageLink` (the stable `_pid/` URL when\npresent), `title` (formatted price string), `subtitles` (`[street,\nneighborhood]`), `status`, `location.{latitude,longitude}`,\n`media[].{originalUrl,thumbnailUrl,category}` (`category:0` = photo),\n`subStats[]` (`{title:\"beds\"|\"baths\"|\"sqft\", subtitle:\"<n>\"}`),\n`clusterSummary.{bedrooms,bathrooms,priceRange:[min,max],formattedLotSize,savedListing}`.\n\n**Comparable rentals** reuse this exact path shape with the rental filter:\n`/homes-for-sale/<locality-slug>/type-rental/` — same extraction.\n\n**Free-text SSR search** (`/homes-for-sale/?q=<query>`) exists but is\nWAF-walled in production (reliably 403s) — don't rely on it; use §4 for\naddress resolution instead.\n\n---\n\n## 2. Property / listing detail\n\n`GET /homedetails/<slug>/<listingIdSHA>_lid/`\n\nIf you only have the `listingIdSHA` (no slug), fetch\n`GET /listing/<listingIdSHA>/view` instead — it 302-redirects to the\ncanonical slugged homedetails URL, and fpx follows the redirect like a\nbrowser.\n\n```sh\nfpx get 'https://www.compass.com/homedetails/162-04-12th-Rd-Queens-NY-11357/2109718971930079225_lid/' -p compass > /tmp/detail.html\n# or, sha-only:\nfpx get 'https://www.compass.com/listing/2109718971930079225/view' -p compass > /tmp/detail.html\n\nnode extract-global.mjs /tmp/detail.html __INITIAL_DATA__ > /tmp/detail.json\n```\n\nExtraction path: `.props.listingRelation.listing`.\n\n```sh\njq '.props.listingRelation.listing |\n  {address: .location.prettyAddress, city: .location.city, state: .location.state, zip: .location.zipCode,\n   price: .price.lastKnown, price_formatted: .price.formatted, per_sqft: .price.perSquareFoot,\n   beds: .size.bedrooms, baths: .size.totalBathrooms, sqft: .size.squareFeet,\n   lot_sqft: .size.lotSizeInSquareFeet, status: .localizedStatus, mls_status: .mlsStatus,\n   photos: [.media[]?.originalUrl], schools: .detailedInfo.schools,\n   agent: .agents[0]}' /tmp/detail.json\n```\n\nOther useful top-level fields: `compassPropertyId`, `pageLink` (`_lid/` URL),\n`navigationPageLink` (stable `_pid/` URL), `description`,\n`detailedInfo.{amenities,taxAnnualAmount,associationFee:{amount,frequency},\narchitecturalStyle,garageSpaces,totalParkingSpaces}`,\n`events[]`/`history[]` (see §3), `mlsAlternateAddresses[]`,\n`agents[].{id,fullName,companyName,profileUrl}`.\n\n**Sentinel values**: `detailedInfo.taxAnnualAmount` under `10` is a\nnot-yet-assessed placeholder on new construction, not a real tax bill.\n\n---\n\n## 3. Price history\n\nSame fetch + extraction as §2 (`__INITIAL_DATA__.props.listingRelation.listing`).\nRead `events[]` (this listing's own MLS events) and `history[]` (events from\nprior listings of the same property) — each entry:\n`{ price, status, localizedStatus, timestamp (unix ms), pageLink, source:{externalSourceName} }`.\n\n```sh\njq '.props.listingRelation.listing | {events, history}' /tmp/detail.json\n# Earliest \"Listed\" event (days-on-market anchor):\njq -r '.props.listingRelation.listing.events // [] |\n  map(select(.status==1 or (.localizedStatus|test(\"^listed$\";\"i\")))) |\n  sort_by(.timestamp) | .[0] // empty' /tmp/detail.json\n```\n\n---\n\n## 4. Address typeahead (resolve an address → listing id) — WAF-immune, try FIRST\n\n`POST /api/v3/omnisuggest/autocomplete`\n\nBody: `{\"q\": \"<street + city + state>\", \"sources\": [0]}` — `sources: [0]`\nrestricts results to the Addresses source.\n\n```sh\ncat > /tmp/omni.json <<'JSON'\n{\"q\": \"158 Raven Blvd Lake Lure NC\", \"sources\": [0]}\nJSON\nfpx post-json 'https://www.compass.com/api/v3/omnisuggest/autocomplete' @/tmp/omni.json -p compass \\\n  | jq '.categories[] | select(.name==1) | .items[] | {id, text, subText, redirectUrl}'\n```\n\nResponse shape: `{ categories: [ { name, label, items: [ { text, subText,\nredirectUrl, source, id, ucGeoId } ] } ], success, rankerVersion }`. Category\n`name === 1` is \"Addresses\". `id` **is** the listing's `listingIdSHA` — it\nmatches the `_lid/` segment on the public homedetails page, so it feeds\nstraight into §2's sha-only fetch (`/listing/<id>/view`).\n\n**Verify the match before trusting it** — the query terms should appear as\nwhole tokens in `text`/`subText` (case + street-suffix folded, e.g.\n\"Ln\"/\"Lane\"); Compass's suggest can return an unrelated top hit when the\nlocal market has no real match. `compass-mcp`'s rung order is: try this\ntypeahead first, and only fall back to the (WAF-walled) free-text/slug SSR\nsearch in §1 if it returns nothing verified.\n\n---\n\n## 5. Agent's listings\n\n`GET /agents/<slug>/` (e.g. `/agents/paige-mcguirk/`)\n\n```sh\nfpx get 'https://www.compass.com/agents/paige-mcguirk/' -p compass > /tmp/agent.html\nnode extract-global.mjs /tmp/agent.html __AGENT_PROFILE__ > /tmp/agent.json\n```\n\nExtraction paths (same `listing` shape as §2's `listingRelation.listing`):\n\n- Active listings: `.data.agentProfileProps.activeListingsProps.initialSales[]`\n- Closed deals: `.data.agentProfileProps.closedDealsProps.initialSales[].listing`\n  (each entry wraps the listing under a `.listing` key, alongside `aiHexID`).\n\n```sh\njq '.data.agentProfileProps.activeListingsProps.initialSales[] |\n  {sha: .listingIdSHA, address: .location.prettyAddress, price: .price.formatted, status: .localizedStatus}' \\\n  /tmp/agent.json\n\njq '.data.agentProfileProps.closedDealsProps.initialSales[].listing |\n  {sha: .listingIdSHA, address: .location.prettyAddress, close_price: .price.lastPropertyClosePrice}' \\\n  /tmp/agent.json\n```\n\nThis page is large (~1.4 MB rendered); the `extract-global.mjs` walk handles\nthat fine, but expect the fetch itself to take a few seconds.\n\n---\n\n## Not covered (no server-reachable endpoint)\n\n- **Saved homes / saved searches** — `/overview/favorites` is fully\n  client-rendered via auth-scoped GraphQL the bridge can't intercept.\n  `compass-mcp` stubs these tools out too; there's no fpx recipe for them.\n- **Market trends / stats** — Compass doesn't expose region-level market\n  data in any SSR blob (unlike some other portals' dedicated endpoint).\n\nFile v1.1.7:skill-card.md\n\n## Description:\n\nHelps agents query Compass US real-estate listings, property and agent details, price history, and addresses through the fpx CLI and a paired browser tab without running an MCP server.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and agents use this skill to search US Compass listings, inspect property and agent information, and resolve addresses from shell-driven workflows without the Compass MCP server.\n\n### Deployment Geography for Use:\n\nUnited States\n\n## Known Risks and Mitigations:\n\nRisk: Browser-bridge pairing persists and relies on an external extension and CLI package.\n\nMitigation: Review the ContextMint Bridge extension and fpx package before installation, and revoke pairing when no longer needed.\n\n## Reference(s):\n\n- [ClawHub compass-fpx release](https://clawhub.ai/chrischall/skills/compass-fpx)\n- [Compass request examples](references/requests.md)\n- [Compass JSON extraction utility](references/extract-global.mjs)\n- [ContextMint Bridge releases](https://github.com/nullnet-app/contextmint-bridge/releases)\n\n## Skill Output:\n\n**Output Type(s):** [Shell commands, Code, Guidance]\n\n**Output Format:** [Markdown with shell commands and JSON extraction examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Listing search pages expose a limited first page; further coverage requires narrower searches.]\n\n## Skill Version(s):\n\n1.1.7 (source: ClawHub release)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.1.6: 5 files, 9643 bytes\n\nFiles: references/extract-global.mjs (2851b), references/requests.md (7967b), skill-card.md (2055b), SKILL.md (5531b), _meta.json (130b)\n\nFile v1.1.6:SKILL.md\n\n---\nname: compass-fpx\ndescription: >-\n  Query compass.com (US real-estate portal) from a shell with the fpx CLI\n  (@fetchproxy/cli) instead of running the compass-mcp server — search\n  listings, get property/agent detail, price history, and resolve street\n  addresses through one-shot fetches over a signed-in browser tab. Use when\n  you want Compass data without the MCP, in a script, or on a machine where\n  the MCP isn't installed.\n---\n\n# Compass via fpx (no MCP)\n\nCompass has no public JSON data-API for search/listing/agent data — it's a\nfully server-rendered React app that embeds each page's data as JSON inside\nan inline `<script>` global (`global.uc`, `window.__INITIAL_DATA__`,\n`window.__AGENT_PROFILE__`). compass.com also runs **AWS WAF**, which 403s\nplain `curl`/Node requests on some paths regardless of headers. `fpx` routes\nevery request through the user's own signed-in browser tab (the ContextMint\nBridge extension), which already carries a cleared WAF session, so the page\nrenders normally — you then pull the JSON out of the HTML yourself.\n\nThis is **Bucket 1 (full-fetchproxy)**: every call, not just an auth\nbootstrap, rides the bridge. Compass validates at the session level per\nrequest, same as the `compass-mcp` server itself (no server-side path\nexists). No Compass login is required beyond having a signed-in tab open —\nsearch/listing/agent data and the address-typeahead endpoint are all public.\n\n## One-time setup\n\n```sh\nnpm install -g @fetchproxy/cli              # provides `fpx`\nfpx profile add compass --domain compass.com\nfpx pair -p compass                          # prints a pair code → approve in the ContextMint Bridge extension\n```\n\nRequirements: the **ContextMint Bridge** browser extension installed\n([releases](https://github.com/nullnet-app/contextmint-bridge/releases); it is the\nfetchproxy extension renamed, from the same maintainer, with public source and a\n`.sha256` beside each release zip), an open\n`www.compass.com` tab, and its Chrome **Site access** allowing `compass.com`.\nPairing persists — after the first approval every later `fpx` call reuses it.\n\n## Core call pattern\n\nTwo request shapes:\n\n1. **SSR pages** (search, homedetails, agent profile) — `fpx get` the path,\n   then pull the inline JSON global out of the HTML.\n2. **The one structured endpoint** (address typeahead) — `fpx post-json`.\n\n```sh\nfpx get 'https://www.compass.com/homes-for-sale/manhattan-ny/' -p compass > /tmp/page.html\nnode references/extract-global.mjs /tmp/page.html uc | jq '.sharedReactAppProps.initialResults.lolResults.data | length'\n```\n\n`references/extract-global.mjs` is a small, dependency-free re-implementation\nof the balanced-brace walk `compass-mcp`'s own `src/page-state.ts` uses\n(`extractGlobalAssign`) — Compass writes each global as a JSON *literal*\n(`global.uc = {...};`), so once the object is sliced out, `JSON.parse`/`jq`\nworks normally. Regexing for it without brace-balancing breaks the moment a\nnested value contains `}`.\n\nFull endpoint list, exact paths, and `jq` projections are in\n`references/requests.md`.\n\n## The one rule: try the typeahead before free-text search\n\nCompass's SSR free-text search (`/homes-for-sale/?q=<query>`) is WAF-walled\nin production and reliably 403s. For **address resolution**, always hit the\nstructured, WAF-immune typeahead first (`POST /api/v3/omnisuggest/autocomplete`)\nand only fall back to the SSR search pages when it returns nothing — mirror\n`compass-mcp`'s own rung order (see `references/requests.md` §4).\n\n## Sign-in / bot-wall detection\n\nA response is NOT a good page when either is true:\n\n- The final URL redirects to `/login` (fpx follows redirects like a browser,\n  so check the body for the login form, or pass `-H` / inspect with\n  `--json` to see the resolved URL).\n- The body contains **both** `awswaf.com` and `challenge.js`, and is under\n  ~80 KB (the AWS WAF challenge interstitial; a real signed-in page is much\n  larger). `compass-mcp`'s `throwIfSignInPage` uses this exact pair of\n  markers — grep for both before trusting a fetch:\n\n```sh\ngrep -q 'awswaf.com' /tmp/page.html && grep -q 'challenge.js' /tmp/page.html && \\\n  [ \"$(wc -c < /tmp/page.html)\" -lt 80000 ] && echo \"WAF-blocked — refresh the compass.com tab\"\n```\n\n## Exit codes (fetch verbs)\n\n- `0` — success.\n- `2` — bridge unavailable: extension not connected or pairing pending → `fpx pair -p compass`, confirm a compass.com tab is open.\n- `3` — bot wall: the tab hasn't cleared the AWS WAF challenge → refresh a `www.compass.com` tab and retry.\n- `4` — upstream non-2xx from Compass.\n\n## Notes\n\n- **Pagination ceiling.** Compass's SSR search only server-renders its first\n  page (~41 listings); `/page-N/` canonicalizes back to page 1 and returns\n  identical data. To reach more of the market, narrow with price/bed bands\n  (`/<lo>-<hi>-bed/`, `/<lo>-<hi>-price/`) and re-search each band — don't\n  paginate.\n- **Two URL forms per listing.** `_pid/` (`navigationPageLink`) is a stable\n  opaque id — use it for bookmarks/trackers. `_lid/` (content-addressed by\n  `listingIdSHA`) is what you fetch to read the *current* record, but it\n  changes when a property is delisted and relisted.\n- No saved-homes/saved-searches or market-trends endpoint exists server-side\n  (client-rendered GraphQL / not exposed) — `compass-mcp` stubs those tools\n  out too; this skill doesn't cover them.\n- `fpx health -p compass` shows bridge connection state when a call fails.\n- This project is developed and maintained by AI (Claude).\n\nFile v1.1.6:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"compass-fpx\",\n  \"version\": \"1.1.6\",\n  \"publishedAt\": 1790991678949\n}\n\nFile v1.1.6:references/requests.md\n\n# Compass requests for fpx\n\nAll paths are relative to `https://www.compass.com`. GET requests return HTML\nwith an embedded JSON global — pull it out with `extract-global.mjs` (in this\nsame directory) before piping to `jq`. The one structured endpoint (§4) is a\nplain JSON POST.\n\n```sh\nfpx get 'https://www.compass.com/<path>' -p compass > /tmp/page.html\nnode extract-global.mjs /tmp/page.html <globalName> | jq '...'\n```\n\nEndpoints and field shapes below are transcribed from `compass-mcp`'s\n`src/tools/*.ts` and `src/page-state.ts` — the same live-verified paths and\nextraction targets the MCP tools use.\n\n---\n\n## 1. Search listings\n\n`GET /homes-for-sale/<location-slug>/[<filters>/]`\n\n- `<location-slug>`: lowercase, hyphenated free text (`\"Brooklyn, NY\"` →\n  `brooklyn-ny`, `\"94110\"` → `94110`).\n- Filters are path segments, in this order: `<bedsMin>-<bedsMax>-bed/`,\n  `<priceMin>-<priceMax>-price/`, a home-type slug (`type-house`,\n  `type-condo`, `type-townhouse`, `type-multi-family`, `type-land`,\n  `type-rental`). Omit any filter you don't need.\n- **Pagination ceiling**: Compass server-renders only ~41 listings into this\n  page (`num`); `/page-N/` canonicalizes back to page 1 with identical data.\n  To see more of the market, narrow the price/bed bands and re-search each\n  band — there is no further page to fetch.\n\n```sh\nfpx get 'https://www.compass.com/homes-for-sale/brooklyn-ny/2-3-bed/500000-1500000-price/type-condo/' -p compass > /tmp/search.html\nnode extract-global.mjs /tmp/search.html uc > /tmp/search.json\n```\n\nExtraction path: `.sharedReactAppProps.initialResults.lolResults`\n(`{ totalItems, data: [ { listing: {...} } ] }`).\n\n```sh\njq -r '.sharedReactAppProps.initialResults.lolResults |\n  \"total=\\(.totalItems)\",\n  (.data[].listing |\n    \"\\(.listingIdSHA)\\t\\(.subtitles[0] // \"\")\\t\\(.title // \"\")\\t\\(.subStats // [] | map(select(.title==\"beds\")) | .[0].subtitle // \"\")bd\")' \\\n  /tmp/search.json\n```\n\nPer-listing fields worth pulling: `listingIdSHA`, `pageLink` (the `_lid/`\ndetail URL, relative), `navigationPageLink` (the stable `_pid/` URL when\npresent), `title` (formatted price string), `subtitles` (`[street,\nneighborhood]`), `status`, `location.{latitude,longitude}`,\n`media[].{originalUrl,thumbnailUrl,category}` (`category:0` = photo),\n`subStats[]` (`{title:\"beds\"|\"baths\"|\"sqft\", subtitle:\"<n>\"}`),\n`clusterSummary.{bedrooms,bathrooms,priceRange:[min,max],formattedLotSize,savedListing}`.\n\n**Comparable rentals** reuse this exact path shape with the rental filter:\n`/homes-for-sale/<locality-slug>/type-rental/` — same extraction.\n\n**Free-text SSR search** (`/homes-for-sale/?q=<query>`) exists but is\nWAF-walled in production (reliably 403s) — don't rely on it; use §4 for\naddress resolution instead.\n\n---\n\n## 2. Property / listing detail\n\n`GET /homedetails/<slug>/<listingIdSHA>_lid/`\n\nIf you only have the `listingIdSHA` (no slug), fetch\n`GET /listing/<listingIdSHA>/view` instead — it 302-redirects to the\ncanonical slugged homedetails URL, and fpx follows the redirect like a\nbrowser.\n\n```sh\nfpx get 'https://www.compass.com/homedetails/162-04-12th-Rd-Queens-NY-11357/2109718971930079225_lid/' -p compass > /tmp/detail.html\n# or, sha-only:\nfpx get 'https://www.compass.com/listing/2109718971930079225/view' -p compass > /tmp/detail.html\n\nnode extract-global.mjs /tmp/detail.html __INITIAL_DATA__ > /tmp/detail.json\n```\n\nExtraction path: `.props.listingRelation.listing`.\n\n```sh\njq '.props.listingRelation.listing |\n  {address: .location.prettyAddress, city: .location.city, state: .location.state, zip: .location.zipCode,\n   price: .price.lastKnown, price_formatted: .price.formatted, per_sqft: .price.perSquareFoot,\n   beds: .size.bedrooms, baths: .size.totalBathrooms, sqft: .size.squareFeet,\n   lot_sqft: .size.lotSizeInSquareFeet, status: .localizedStatus, mls_status: .mlsStatus,\n   photos: [.media[]?.originalUrl], schools: .detailedInfo.schools,\n   agent: .agents[0]}' /tmp/detail.json\n```\n\nOther useful top-level fields: `compassPropertyId`, `pageLink` (`_lid/` URL),\n`navigationPageLink` (stable `_pid/` URL), `description`,\n`detailedInfo.{amenities,taxAnnualAmount,associationFee:{amount,frequency},\narchitecturalStyle,garageSpaces,totalParkingSpaces}`,\n`events[]`/`history[]` (see §3), `mlsAlternateAddresses[]`,\n`agents[].{id,fullName,companyName,profileUrl}`.\n\n**Sentinel values**: `detailedInfo.taxAnnualAmount` under `10` is a\nnot-yet-assessed placeholder on new construction, not a real tax bill.\n\n---\n\n## 3. Price history\n\nSame fetch + extraction as §2 (`__INITIAL_DATA__.props.listingRelation.listing`).\nRead `events[]` (this listing's own MLS events) and `history[]` (events from\nprior listings of the same property) — each entry:\n`{ price, status, localizedStatus, timestamp (unix ms), pageLink, source:{externalSourceName} }`.\n\n```sh\njq '.props.listingRelation.listing | {events, history}' /tmp/detail.json\n# Earliest \"Listed\" event (days-on-market anchor):\njq -r '.props.listingRelation.listing.events // [] |\n  map(select(.status==1 or (.localizedStatus|test(\"^listed$\";\"i\")))) |\n  sort_by(.timestamp) | .[0] // empty' /tmp/detail.json\n```\n\n---\n\n## 4. Address typeahead (resolve an address → listing id) — WAF-immune, try FIRST\n\n`POST /api/v3/omnisuggest/autocomplete`\n\nBody: `{\"q\": \"<street + city + state>\", \"sources\": [0]}` — `sources: [0]`\nrestricts results to the Addresses source.\n\n```sh\ncat > /tmp/omni.json <<'JSON'\n{\"q\": \"158 Raven Blvd Lake Lure NC\", \"sources\": [0]}\nJSON\nfpx post-json 'https://www.compass.com/api/v3/omnisuggest/autocomplete' @/tmp/omni.json -p compass \\\n  | jq '.categories[] | select(.name==1) | .items[] | {id, text, subText, redirectUrl}'\n```\n\nResponse shape: `{ categories: [ { name, label, items: [ { text, subText,\nredirectUrl, source, id, ucGeoId } ] } ], success, rankerVersion }`. Category\n`name === 1` is \"Addresses\". `id` **is** the listing's `listingIdSHA` — it\nmatches the `_lid/` segment on the public homedetails page, so it feeds\nstraight into §2's sha-only fetch (`/listing/<id>/view`).\n\n**Verify the match before trusting it** — the query terms should appear as\nwhole tokens in `text`/`subText` (case + street-suffix folded, e.g.\n\"Ln\"/\"Lane\"); Compass's suggest can return an unrelated top hit when the\nlocal market has no real match. `compass-mcp`'s rung order is: try this\ntypeahead first, and only fall back to the (WAF-walled) free-text/slug SSR\nsearch in §1 if it returns nothing verified.\n\n---\n\n## 5. Agent's listings\n\n`GET /agents/<slug>/` (e.g. `/agents/paige-mcguirk/`)\n\n```sh\nfpx get 'https://www.compass.com/agents/paige-mcguirk/' -p compass > /tmp/agent.html\nnode extract-global.mjs /tmp/agent.html __AGENT_PROFILE__ > /tmp/agent.json\n```\n\nExtraction paths (same `listing` shape as §2's `listingRelation.listing`):\n\n- Active listings: `.data.agentProfileProps.activeListingsProps.initialSales[]`\n- Closed deals: `.data.agentProfileProps.closedDealsProps.initialSales[].listing`\n  (each entry wraps the listing under a `.listing` key, alongside `aiHexID`).\n\n```sh\njq '.data.agentProfileProps.activeListingsProps.initialSales[] |\n  {sha: .listingIdSHA, address: .location.prettyAddress, price: .price.formatted, status: .localizedStatus}' \\\n  /tmp/agent.json\n\njq '.data.agentProfileProps.closedDealsProps.initialSales[].listing |\n  {sha: .listingIdSHA, address: .location.prettyAddress, close_price: .price.lastPropertyClosePrice}' \\\n  /tmp/agent.json\n```\n\nThis page is large (~1.4 MB rendered); the `extract-global.mjs` walk handles\nthat fine, but expect the fetch itself to take a few seconds.\n\n---\n\n## Not covered (no server-reachable endpoint)\n\n- **Saved homes / saved searches** — `/overview/favorites` is fully\n  client-rendered via auth-scoped GraphQL the bridge can't intercept.\n  `compass-mcp` stubs these tools out too; there's no fpx recipe for them.\n- **Market trends / stats** — Compass doesn't expose region-level market\n  data in any SSR blob (unlike some other portals' dedicated endpoint).\n\nFile v1.1.6:skill-card.md\n\n## Description:\n\nHelps agents query Compass real-estate listings, property and agent details, price history, and addresses from a shell through a browser-backed CLI instead of an MCP server.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and agents use this skill to look up US real-estate listings, property and agent details, price history, and street addresses on Compass from shell-based workflows.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The skill relies on a third-party CLI and browser extension with persistent pairing.\n\nMitigation: Review @fetchproxy/cli and ContextMint Bridge before installation and approve pairing only for intended use.\n\nRisk: Broad browser-extension site access could expose browsing activity outside the intended Compass lookups.\n\nMitigation: Limit the extension's site access to compass.com and use the skill only for intended Compass data lookups.\n\n## Reference(s):\n\n- [Compass FPX release on ClawHub](https://clawhub.ai/chrischall/skills/compass-fpx)\n- [Compass request examples](references/requests.md)\n- [Compass page-data extraction helper](references/extract-global.mjs)\n- [ContextMint Bridge releases](https://github.com/nullnet-app/contextmint-bridge/releases)\n\n## Skill Output:\n\n**Output Type(s):** [Shell commands, Configuration instructions, Guidance]\n\n**Output Format:** [Markdown with shell commands and JSON field examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Compass listing and address results depend on the connected browser tab and available page data.]\n\n## Skill Version(s):\n\n1.1.6 (source: server-resolved ClawHub release)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.1.5: 5 files, 9618 bytes\n\nFiles: references/extract-global.mjs (2851b), references/requests.md (7967b), skill-card.md (1993b), SKILL.md (5531b), _meta.json (130b)\n\nFile v1.1.5:SKILL.md\n\n---\nname: compass-fpx\ndescription: >-\n  Query compass.com (US real-estate portal) from a shell with the fpx CLI\n  (@fetchproxy/cli) instead of running the compass-mcp server — search\n  listings, get property/agent detail, price history, and resolve street\n  addresses through one-shot fetches over a signed-in browser tab. Use when\n  you want Compass data without the MCP, in a script, or on a machine where\n  the MCP isn't installed.\n---\n\n# Compass via fpx (no MCP)\n\nCompass has no public JSON data-API for search/listing/agent data — it's a\nfully server-rendered React app that embeds each page's data as JSON inside\nan inline `<script>` global (`global.uc`, `window.__INITIAL_DATA__`,\n`window.__AGENT_PROFILE__`). compass.com also runs **AWS WAF**, which 403s\nplain `curl`/Node requests on some paths regardless of headers. `fpx` routes\nevery request through the user's own signed-in browser tab (the ContextMint\nBridge extension), which already carries a cleared WAF session, so the page\nrenders normally — you then pull the JSON out of the HTML yourself.\n\nThis is **Bucket 1 (full-fetchproxy)**: every call, not just an auth\nbootstrap, rides the bridge. Compass validates at the session level per\nrequest, same as the `compass-mcp` server itself (no server-side path\nexists). No Compass login is required beyond having a signed-in tab open —\nsearch/listing/agent data and the address-typeahead endpoint are all public.\n\n## One-time setup\n\n```sh\nnpm install -g @fetchproxy/cli              # provides `fpx`\nfpx profile add compass --domain compass.com\nfpx pair -p compass                          # prints a pair code → approve in the ContextMint Bridge extension\n```\n\nRequirements: the **ContextMint Bridge** browser extension installed\n([releases](https://github.com/nullnet-app/contextmint-bridge/releases); it is the\nfetchproxy extension renamed, from the same maintainer, with public source and a\n`.sha256` beside each release zip), an open\n`www.compass.com` tab, and its Chrome **Site access** allowing `compass.com`.\nPairing persists — after the first approval every later `fpx` call reuses it.\n\n## Core call pattern\n\nTwo request shapes:\n\n1. **SSR pages** (search, homedetails, agent profile) — `fpx get` the path,\n   then pull the inline JSON global out of the HTML.\n2. **The one structured endpoint** (address typeahead) — `fpx post-json`.\n\n```sh\nfpx get 'https://www.compass.com/homes-for-sale/manhattan-ny/' -p compass > /tmp/page.html\nnode references/extract-global.mjs /tmp/page.html uc | jq '.sharedReactAppProps.initialResults.lolResults.data | length'\n```\n\n`references/extract-global.mjs` is a small, dependency-free re-implementation\nof the balanced-brace walk `compass-mcp`'s own `src/page-state.ts` uses\n(`extractGlobalAssign`) — Compass writes each global as a JSON *literal*\n(`global.uc = {...};`), so once the object is sliced out, `JSON.parse`/`jq`\nworks normally. Regexing for it without brace-balancing breaks the moment a\nnested value contains `}`.\n\nFull endpoint list, exact paths, and `jq` projections are in\n`references/requests.md`.\n\n## The one rule: try the typeahead before free-text search\n\nCompass's SSR free-text search (`/homes-for-sale/?q=<query>`) is WAF-walled\nin production and reliably 403s. For **address resolution**, always hit the\nstructured, WAF-immune typeahead first (`POST /api/v3/omnisuggest/autocomplete`)\nand only fall back to the SSR search pages when it returns nothing — mirror\n`compass-mcp`'s own rung order (see `references/requests.md` §4).\n\n## Sign-in / bot-wall detection\n\nA response is NOT a good page when either is true:\n\n- The final URL redirects to `/login` (fpx follows redirects like a browser,\n  so check the body for the login form, or pass `-H` / inspect with\n  `--json` to see the resolved URL).\n- The body contains **both** `awswaf.com` and `challenge.js`, and is under\n  ~80 KB (the AWS WAF challenge interstitial; a real signed-in page is much\n  larger). `compass-mcp`'s `throwIfSignInPage` uses this exact pair of\n  markers — grep for both before trusting a fetch:\n\n```sh\ngrep -q 'awswaf.com' /tmp/page.html && grep -q 'challenge.js' /tmp/page.html && \\\n  [ \"$(wc -c < /tmp/page.html)\" -lt 80000 ] && echo \"WAF-blocked — refresh the compass.com tab\"\n```\n\n## Exit codes (fetch verbs)\n\n- `0` — success.\n- `2` — bridge unavailable: extension not connected or pairing pending → `fpx pair -p compass`, confirm a compass.com tab is open.\n- `3` — bot wall: the tab hasn't cleared the AWS WAF challenge → refresh a `www.compass.com` tab and retry.\n- `4` — upstream non-2xx from Compass.\n\n## Notes\n\n- **Pagination ceiling.** Compass's SSR search only server-renders its first\n  page (~41 listings); `/page-N/` canonicalizes back to page 1 and returns\n  identical data. To reach more of the market, narrow with price/bed bands\n  (`/<lo>-<hi>-bed/`, `/<lo>-<hi>-price/`) and re-search each band — don't\n  paginate.\n- **Two URL forms per listing.** `_pid/` (`navigationPageLink`) is a stable\n  opaque id — use it for bookmarks/trackers. `_lid/` (content-addressed by\n  `listingIdSHA`) is what you fetch to read the *current* record, but it\n  changes when a property is delisted and relisted.\n- No saved-homes/saved-searches or market-trends endpoint exists server-side\n  (client-rendered GraphQL / not exposed) — `compass-mcp` stubs those tools\n  out too; this skill doesn't cover them.\n- `fpx health -p compass` shows bridge connection state when a call fails.\n- This project is developed and maintained by AI (Claude).\n\nFile v1.1.5:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"compass-fpx\",\n  \"version\": \"1.1.5\",\n  \"publishedAt\": 1790603883590\n}\n\nFile v1.1.5:references/requests.md\n\n# Compass requests for fpx\n\nAll paths are relative to `https://www.compass.com`. GET requests return HTML\nwith an embedded JSON global — pull it out with `extract-global.mjs` (in this\nsame directory) before piping to `jq`. The one structured endpoint (§4) is a\nplain JSON POST.\n\n```sh\nfpx get 'https://www.compass.com/<path>' -p compass > /tmp/page.html\nnode extract-global.mjs /tmp/page.html <globalName> | jq '...'\n```\n\nEndpoints and field shapes below are transcribed from `compass-mcp`'s\n`src/tools/*.ts` and `src/page-state.ts` — the same live-verified paths and\nextraction targets the MCP tools use.\n\n---\n\n## 1. Search listings\n\n`GET /homes-for-sale/<location-slug>/[<filters>/]`\n\n- `<location-slug>`: lowercase, hyphenated free text (`\"Brooklyn, NY\"` →\n  `brooklyn-ny`, `\"94110\"` → `94110`).\n- Filters are path segments, in this order: `<bedsMin>-<bedsMax>-bed/`,\n  `<priceMin>-<priceMax>-price/`, a home-type slug (`type-house`,\n  `type-condo`, `type-townhouse`, `type-multi-family`, `type-land`,\n  `type-rental`). Omit any filter you don't need.\n- **Pagination ceiling**: Compass server-renders only ~41 listings into this\n  page (`num`); `/page-N/` canonicalizes back to page 1 with identical data.\n  To see more of the market, narrow the price/bed bands and re-search each\n  band — there is no further page to fetch.\n\n```sh\nfpx get 'https://www.compass.com/homes-for-sale/brooklyn-ny/2-3-bed/500000-1500000-price/type-condo/' -p compass > /tmp/search.html\nnode extract-global.mjs /tmp/search.html uc > /tmp/search.json\n```\n\nExtraction path: `.sharedReactAppProps.initialResults.lolResults`\n(`{ totalItems, data: [ { listing: {...} } ] }`).\n\n```sh\njq -r '.sharedReactAppProps.initialResults.lolResults |\n  \"total=\\(.totalItems)\",\n  (.data[].listing |\n    \"\\(.listingIdSHA)\\t\\(.subtitles[0] // \"\")\\t\\(.title // \"\")\\t\\(.subStats // [] | map(select(.title==\"beds\")) | .[0].subtitle // \"\")bd\")' \\\n  /tmp/search.json\n```\n\nPer-listing fields worth pulling: `listingIdSHA`, `pageLink` (the `_lid/`\ndetail URL, relative), `navigationPageLink` (the stable `_pid/` URL when\npresent), `title` (formatted price string), `subtitles` (`[street,\nneighborhood]`), `status`, `location.{latitude,longitude}`,\n`media[].{originalUrl,thumbnailUrl,category}` (`category:0` = photo),\n`subStats[]` (`{title:\"beds\"|\"baths\"|\"sqft\", subtitle:\"<n>\"}`),\n`clusterSummary.{bedrooms,bathrooms,priceRange:[min,max],formattedLotSize,savedListing}`.\n\n**Comparable rentals** reuse this exact path shape with the rental filter:\n`/homes-for-sale/<locality-slug>/type-rental/` — same extraction.\n\n**Free-text SSR search** (`/homes-for-sale/?q=<query>`) exists but is\nWAF-walled in production (reliably 403s) — don't rely on it; use §4 for\naddress resolution instead.\n\n---\n\n## 2. Property / listing detail\n\n`GET /homedetails/<slug>/<listingIdSHA>_lid/`\n\nIf you only have the `listingIdSHA` (no slug), fetch\n`GET /listing/<listingIdSHA>/view` instead — it 302-redirects to the\ncanonical slugged homedetails URL, and fpx follows the redirect like a\nbrowser.\n\n```sh\nfpx get 'https://www.compass.com/homedetails/162-04-12th-Rd-Queens-NY-11357/2109718971930079225_lid/' -p compass > /tmp/detail.html\n# or, sha-only:\nfpx get 'https://www.compass.com/listing/2109718971930079225/view' -p compass > /tmp/detail.html\n\nnode extract-global.mjs /tmp/detail.html __INITIAL_DATA__ > /tmp/detail.json\n```\n\nExtraction path: `.props.listingRelation.listing`.\n\n```sh\njq '.props.listingRelation.listing |\n  {address: .location.prettyAddress, city: .location.city, state: .location.state, zip: .location.zipCode,\n   price: .price.lastKnown, price_formatted: .price.formatted, per_sqft: .price.perSquareFoot,\n   beds: .size.bedrooms, baths: .size.totalBathrooms, sqft: .size.squareFeet,\n   lot_sqft: .size.lotSizeInSquareFeet, status: .localizedStatus, mls_status: .mlsStatus,\n   photos: [.media[]?.originalUrl], schools: .detailedInfo.schools,\n   agent: .agents[0]}' /tmp/detail.json\n```\n\nOther useful top-level fields: `compassPropertyId`, `pageLink` (`_lid/` URL),\n`navigationPageLink` (stable `_pid/` URL), `description`,\n`detailedInfo.{amenities,taxAnnualAmount,associationFee:{amount,frequency},\narchitecturalStyle,garageSpaces,totalParkingSpaces}`,\n`events[]`/`history[]` (see §3), `mlsAlternateAddresses[]`,\n`agents[].{id,fullName,companyName,profileUrl}`.\n\n**Sentinel values**: `detailedInfo.taxAnnualAmount` under `10` is a\nnot-yet-assessed placeholder on new construction, not a real tax bill.\n\n---\n\n## 3. Price history\n\nSame fetch + extraction as §2 (`__INITIAL_DATA__.props.listingRelation.listing`).\nRead `events[]` (this listing's own MLS events) and `history[]` (events from\nprior listings of the same property) — each entry:\n`{ price, status, localizedStatus, timestamp (unix ms), pageLink, source:{externalSourceName} }`.\n\n```sh\njq '.props.listingRelation.listing | {events, history}' /tmp/detail.json\n# Earliest \"Listed\" event (days-on-market anchor):\njq -r '.props.listingRelation.listing.events // [] |\n  map(select(.status==1 or (.localizedStatus|test(\"^listed$\";\"i\")))) |\n  sort_by(.timestamp) | .[0] // empty' /tmp/detail.json\n```\n\n---\n\n## 4. Address typeahead (resolve an address → listing id) — WAF-immune, try FIRST\n\n`POST /api/v3/omnisuggest/autocomplete`\n\nBody: `{\"q\": \"<street + city + state>\", \"sources\": [0]}` — `sources: [0]`\nrestricts results to the Addresses source.\n\n```sh\ncat > /tmp/omni.json <<'JSON'\n{\"q\": \"158 Raven Blvd Lake Lure NC\", \"sources\": [0]}\nJSON\nfpx post-json 'https://www.compass.com/api/v3/omnisuggest/autocomplete' @/tmp/omni.json -p compass \\\n  | jq '.categories[] | select(.name==1) | .items[] | {id, text, subText, redirectUrl}'\n```\n\nResponse shape: `{ categories: [ { name, label, items: [ { text, subText,\nredirectUrl, source, id, ucGeoId } ] } ], success, rankerVersion }`. Category\n`name === 1` is \"Addresses\". `id` **is** the listing's `listingIdSHA` — it\nmatches the `_lid/` segment on the public homedetails page, so it feeds\nstraight into §2's sha-only fetch (`/listing/<id>/view`).\n\n**Verify the match before trusting it** — the query terms should appear as\nwhole tokens in `text`/`subText` (case + street-suffix folded, e.g.\n\"Ln\"/\"Lane\"); Compass's suggest can return an unrelated top hit when the\nlocal market has no real match. `compass-mcp`'s rung order is: try this\ntypeahead first, and only fall back to the (WAF-walled) free-text/slug SSR\nsearch in §1 if it returns nothing verified.\n\n---\n\n## 5. Agent's listings\n\n`GET /agents/<slug>/` (e.g. `/agents/paige-mcguirk/`)\n\n```sh\nfpx get 'https://www.compass.com/agents/paige-mcguirk/' -p compass > /tmp/agent.html\nnode extract-global.mjs /tmp/agent.html __AGENT_PROFILE__ > /tmp/agent.json\n```\n\nExtraction paths (same `listing` shape as §2's `listingRelation.listing`):\n\n- Active listings: `.data.agentProfileProps.activeListingsProps.initialSales[]`\n- Closed deals: `.data.agentProfileProps.closedDealsProps.initialSales[].listing`\n  (each entry wraps the listing under a `.listing` key, alongside `aiHexID`).\n\n```sh\njq '.data.agentProfileProps.activeListingsProps.initialSales[] |\n  {sha: .listingIdSHA, address: .location.prettyAddress, price: .price.formatted, status: .localizedStatus}' \\\n  /tmp/agent.json\n\njq '.data.agentProfileProps.closedDealsProps.initialSales[].listing |\n  {sha: .listingIdSHA, address: .location.prettyAddress, close_price: .price.lastPropertyClosePrice}' \\\n  /tmp/agent.json\n```\n\nThis page is large (~1.4 MB rendered); the `extract-global.mjs` walk handles\nthat fine, but expect the fetch itself to take a few seconds.\n\n---\n\n## Not covered (no server-reachable endpoint)\n\n- **Saved homes / saved searches** — `/overview/favorites` is fully\n  client-rendered via auth-scoped GraphQL the bridge can't intercept.\n  `compass-mcp` stubs these tools out too; there's no fpx recipe for them.\n- **Market trends / stats** — Compass doesn't expose region-level market\n  data in any SSR blob (unlike some other portals' dedicated endpoint).\n\nFile v1.1.5:skill-card.md\n\n## Description:\n\nHelps agents query Compass real-estate listings, property and agent details, price history, and street addresses from a shell through a paired browser session.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and agents use this skill to search US real-estate listings, resolve addresses, and retrieve Compass property, agent, and price-history details without an MCP server.\n\n### Deployment Geography for Use:\n\nUnited States\n\n## Known Risks and Mitigations:\n\nRisk: Pairing the browser bridge can expose the active Compass session to skill-driven requests.\n\nMitigation: Use a dedicated browser profile when session separation matters and scope extension site access to compass.com.\n\nRisk: Searched addresses are sent to Compass.\n\nMitigation: Avoid entering sensitive addresses unless sharing them with Compass is appropriate.\n\nRisk: Example commands can overwrite existing files in /tmp.\n\nMitigation: Choose unique output paths before running the examples.\n\n## Reference(s):\n\n- [Compass FPX on ClawHub](https://clawhub.ai/chrischall/skills/compass-fpx)\n- [Compass request recipes](references/requests.md)\n- [ContextMint Bridge releases](https://github.com/nullnet-app/contextmint-bridge/releases)\n\n## Skill Output:\n\n**Output Type(s):** [Shell commands, Code, Guidance]\n\n**Output Format:** [Markdown with shell and JSON examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Commands can extract listing, property, and agent data as JSON; access depends on a paired Compass browser tab.]\n\n## Skill Version(s):\n\n1.1.5 (source: ClawHub release evidence)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.1.4: 5 files, 9565 bytes\n\nFiles: references/extract-global.mjs (2851b), references/requests.md (7967b), skill-card.md (2060b), SKILL.md (5308b), _meta.json (130b)\n\nFile v1.1.4:SKILL.md\n\n---\nname: compass-fpx\ndescription: >-\n  Query compass.com (US real-estate portal) from a shell with the fpx CLI\n  (@fetchproxy/cli) instead of running the compass-mcp server — search\n  listings, get property/agent detail, price history, and resolve street\n  addresses through one-shot fetches over a signed-in browser tab. Use when\n  you want Compass data without the MCP, in a script, or on a machine where\n  the MCP isn't installed.\n---\n\n# Compass via fpx (no MCP)\n\nCompass has no public JSON data-API for search/listing/agent data — it's a\nfully server-rendered React app that embeds each page's data as JSON inside\nan inline `<script>` global (`global.uc`, `window.__INITIAL_DATA__`,\n`window.__AGENT_PROFILE__`). compass.com also runs **AWS WAF**, which 403s\nplain `curl`/Node requests on some paths regardless of headers. `fpx` routes\nevery request through the user's own signed-in browser tab (the fetchproxy\nextension), which already carries a cleared WAF session, so the page renders\nnormally — you then pull the JSON out of the HTML yourself.\n\nThis is **Bucket 1 (full-fetchproxy)**: every call, not just an auth\nbootstrap, rides the bridge. Compass validates at the session level per\nrequest, same as the `compass-mcp` server itself (no server-side path\nexists). No Compass login is required beyond having a signed-in tab open —\nsearch/listing/agent data and the address-typeahead endpoint are all public.\n\n## One-time setup\n\n```sh\nnpm install -g @fetchproxy/cli              # provides `fpx`\nfpx profile add compass --domain compass.com\nfpx pair -p compass                          # prints a pair code → approve in the fetchproxy extension\n```\n\nRequirements: the **fetchproxy** browser extension installed, an open\n`www.compass.com` tab, and its Chrome **Site access** allowing `compass.com`.\nPairing persists — after the first approval every later `fpx` call reuses it.\n\n## Core call pattern\n\nTwo request shapes:\n\n1. **SSR pages** (search, homedetails, agent profile) — `fpx get` the path,\n   then pull the inline JSON global out of the HTML.\n2. **The one structured endpoint** (address typeahead) — `fpx post-json`.\n\n```sh\nfpx get 'https://www.compass.com/homes-for-sale/manhattan-ny/' -p compass > /tmp/page.html\nnode references/extract-global.mjs /tmp/page.html uc | jq '.sharedReactAppProps.initialResults.lolResults.data | length'\n```\n\n`references/extract-global.mjs` is a small, dependency-free re-implementation\nof the balanced-brace walk `compass-mcp`'s own `src/page-state.ts` uses\n(`extractGlobalAssign`) — Compass writes each global as a JSON *literal*\n(`global.uc = {...};`), so once the object is sliced out, `JSON.parse`/`jq`\nworks normally. Regexing for it without brace-balancing breaks the moment a\nnested value contains `}`.\n\nFull endpoint list, exact paths, and `jq` projections are in\n`references/requests.md`.\n\n## The one rule: try the typeahead before free-text search\n\nCompass's SSR free-text search (`/homes-for-sale/?q=<query>`) is WAF-walled\nin production and reliably 403s. For **address resolution**, always hit the\nstructured, WAF-immune typeahead first (`POST /api/v3/omnisuggest/autocomplete`)\nand only fall back to the SSR search pages when it returns nothing — mirror\n`compass-mcp`'s own rung order (see `references/requests.md` §4).\n\n## Sign-in / bot-wall detection\n\nA response is NOT a good page when either is true:\n\n- The final URL redirects to `/login` (fpx follows redirects like a browser,\n  so check the body for the login form, or pass `-H` / inspect with\n  `--json` to see the resolved URL).\n- The body contains **both** `awswaf.com` and `challenge.js`, and is under\n  ~80 KB (the AWS WAF challenge interstitial; a real signed-in page is much\n  larger). `compass-mcp`'s `throwIfSignInPage` uses this exact pair of\n  markers — grep for both before trusting a fetch:\n\n```sh\ngrep -q 'awswaf.com' /tmp/page.html && grep -q 'challenge.js' /tmp/page.html && \\\n  [ \"$(wc -c < /tmp/page.html)\" -lt 80000 ] && echo \"WAF-blocked — refresh the compass.com tab\"\n```\n\n## Exit codes (fetch verbs)\n\n- `0` — success.\n- `2` — bridge unavailable: extension not connected or pairing pending → `fpx pair -p compass`, confirm a compass.com tab is open.\n- `3` — bot wall: the tab hasn't cleared the AWS WAF challenge → refresh a `www.compass.com` tab and retry.\n- `4` — upstream non-2xx from Compass.\n\n## Notes\n\n- **Pagination ceiling.** Compass's SSR search only server-renders its first\n  page (~41 listings); `/page-N/` canonicalizes back to page 1 and returns\n  identical data. To reach more of the market, narrow with price/bed bands\n  (`/<lo>-<hi>-bed/`, `/<lo>-<hi>-price/`) and re-search each band — don't\n  paginate.\n- **Two URL forms per listing.** `_pid/` (`navigationPageLink`) is a stable\n  opaque id — use it for bookmarks/trackers. `_lid/` (content-addressed by\n  `listingIdSHA`) is what you fetch to read the *current* record, but it\n  changes when a property is delisted and relisted.\n- No saved-homes/saved-searches or market-trends endpoint exists server-side\n  (client-rendered GraphQL / not exposed) — `compass-mcp` stubs those tools\n  out too; this skill doesn't cover them.\n- `fpx health -p compass` shows bridge connection state when a call fails.\n- This project is developed and maintained by AI (Claude).\n\nFile v1.1.4:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"compass-fpx\",\n  \"version\": \"1.1.4\",\n  \"publishedAt\": 1790351619114\n}\n\nFile v1.1.4:references/requests.md\n\n# Compass requests for fpx\n\nAll paths are relative to `https://www.compass.com`. GET requests return HTML\nwith an embedded JSON global — pull it out with `extract-global.mjs` (in this\nsame directory) before piping to `jq`. The one structured endpoint (§4) is a\nplain JSON POST.\n\n```sh\nfpx get 'https://www.compass.com/<path>' -p compass > /tmp/page.html\nnode extract-global.mjs /tmp/page.html <globalName> | jq '...'\n```\n\nEndpoints and field shapes below are transcribed from `compass-mcp`'s\n`src/tools/*.ts` and `src/page-state.ts` — the same live-verified paths and\nextraction targets the MCP tools use.\n\n---\n\n## 1. Search listings\n\n`GET /homes-for-sale/<location-slug>/[<filters>/]`\n\n- `<location-slug>`: lowercase, hyphenated free text (`\"Brooklyn, NY\"` →\n  `brooklyn-ny`, `\"94110\"` → `94110`).\n- Filters are path segments, in this order: `<bedsMin>-<bedsMax>-bed/`,\n  `<priceMin>-<priceMax>-price/`, a home-type slug (`type-house`,\n  `type-condo`, `type-townhouse`, `type-multi-family`, `type-land`,\n  `type-rental`). Omit any filter you don't need.\n- **Pagination ceiling**: Compass server-renders only ~41 listings into this\n  page (`num`); `/page-N/` canonicalizes back to page 1 with identical data.\n  To see more of the market, narrow the price/bed bands and re-search each\n  band — there is no further page to fetch.\n\n```sh\nfpx get 'https://www.compass.com/homes-for-sale/brooklyn-ny/2-3-bed/500000-1500000-price/type-condo/' -p compass > /tmp/search.html\nnode extract-global.mjs /tmp/search.html uc > /tmp/search.json\n```\n\nExtraction path: `.sharedReactAppProps.initialResults.lolResults`\n(`{ totalItems, data: [ { listing: {...} } ] }`).\n\n```sh\njq -r '.sharedReactAppProps.initialResults.lolResults |\n  \"total=\\(.totalItems)\",\n  (.data[].listing |\n    \"\\(.listingIdSHA)\\t\\(.subtitles[0] // \"\")\\t\\(.title // \"\")\\t\\(.subStats // [] | map(select(.title==\"beds\")) | .[0].subtitle // \"\")bd\")' \\\n  /tmp/search.json\n```\n\nPer-listing fields worth pulling: `listingIdSHA`, `pageLink` (the `_lid/`\ndetail URL, relative), `navigationPageLink` (the stable `_pid/` URL when\npresent), `title` (formatted price string), `subtitles` (`[street,\nneighborhood]`), `status`, `location.{latitude,longitude}`,\n`media[].{originalUrl,thumbnailUrl,category}` (`category:0` = photo),\n`subStats[]` (`{title:\"beds\"|\"baths\"|\"sqft\", subtitle:\"<n>\"}`),\n`clusterSummary.{bedrooms,bathrooms,priceRange:[min,max],formattedLotSize,savedListing}`.\n\n**Comparable rentals** reuse this exact path shape with the rental filter:\n`/homes-for-sale/<locality-slug>/type-rental/` — same extraction.\n\n**Free-text SSR search** (`/homes-for-sale/?q=<query>`) exists but is\nWAF-walled in production (reliably 403s) — don't rely on it; use §4 for\naddress resolution instead.\n\n---\n\n## 2. Property / listing detail\n\n`GET /homedetails/<slug>/<listingIdSHA>_lid/`\n\nIf you only have the `listingIdSHA` (no slug), fetch\n`GET /listing/<listingIdSHA>/view` instead — it 302-redirects to the\ncanonical slugged homedetails URL, and fpx follows the redirect like a\nbrowser.\n\n```sh\nfpx get 'https://www.compass.com/homedetails/162-04-12th-Rd-Queens-NY-11357/2109718971930079225_lid/' -p compass > /tmp/detail.html\n# or, sha-only:\nfpx get 'https://www.compass.com/listing/2109718971930079225/view' -p compass > /tmp/detail.html\n\nnode extract-global.mjs /tmp/detail.html __INITIAL_DATA__ > /tmp/detail.json\n```\n\nExtraction path: `.props.listingRelation.listing`.\n\n```sh\njq '.props.listingRelation.listing |\n  {address: .location.prettyAddress, city: .location.city, state: .location.state, zip: .location.zipCode,\n   price: .price.lastKnown, price_formatted: .price.formatted, per_sqft: .price.perSquareFoot,\n   beds: .size.bedrooms, baths: .size.totalBathrooms, sqft: .size.squareFeet,\n   lot_sqft: .size.lotSizeInSquareFeet, status: .localizedStatus, mls_status: .mlsStatus,\n   photos: [.media[]?.originalUrl], schools: .detailedInfo.schools,\n   agent: .agents[0]}' /tmp/detail.json\n```\n\nOther useful top-level fields: `compassPropertyId`, `pageLink` (`_lid/` URL),\n`navigationPageLink` (stable `_pid/` URL), `description`,\n`detailedInfo.{amenities,taxAnnualAmount,associationFee:{amount,frequency},\narchitecturalStyle,garageSpaces,totalParkingSpaces}`,\n`events[]`/`history[]` (see §3), `mlsAlternateAddresses[]`,\n`agents[].{id,fullName,companyName,profileUrl}`.\n\n**Sentinel values**: `detailedInfo.taxAnnualAmount` under `10` is a\nnot-yet-assessed placeholder on new construction, not a real tax bill.\n\n---\n\n## 3. Price history\n\nSame fetch + extraction as §2 (`__INITIAL_DATA__.props.listingRelation.listing`).\nRead `events[]` (this listing's own MLS events) and `history[]` (events from\nprior listings of the same property) — each entry:\n`{ price, status, localizedStatus, timestamp (unix ms), pageLink, source:{externalSourceName} }`.\n\n```sh\njq '.props.listingRelation.listing | {events, history}' /tmp/detail.json\n# Earliest \"Listed\" event (days-on-market anchor):\njq -r '.props.listingRelation.listing.events // [] |\n  map(select(.status==1 or (.localizedStatus|test(\"^listed$\";\"i\")))) |\n  sort_by(.timestamp) | .[0] // empty' /tmp/detail.json\n```\n\n---\n\n## 4. Address typeahead (resolve an address → listing id) — WAF-immune, try FIRST\n\n`POST /api/v3/omnisuggest/autocomplete`\n\nBody: `{\"q\": \"<street + city + state>\", \"sources\": [0]}` — `sources: [0]`\nrestricts results to the Addresses source.\n\n```sh\ncat > /tmp/omni.json <<'JSON'\n{\"q\": \"158 Raven Blvd Lake Lure NC\", \"sources\": [0]}\nJSON\nfpx post-json 'https://www.compass.com/api/v3/omnisuggest/autocomplete' @/tmp/omni.json -p compass \\\n  | jq '.categories[] | select(.name==1) | .items[] | {id, text, subText, redirectUrl}'\n```\n\nResponse shape: `{ categories: [ { name, label, items: [ { text, subText,\nredirectUrl, source, id, ucGeoId } ] } ], success, rankerVersion }`. Category\n`name === 1` is \"Addresses\". `id` **is** the listing's `listingIdSHA` — it\nmatches the `_lid/` segment on the public homedetails page, so it feeds\nstraight into §2's sha-only fetch (`/listing/<id>/view`).\n\n**Verify the match before trusting it** — the query terms should appear as\nwhole tokens in `text`/`subText` (case + street-suffix folded, e.g.\n\"Ln\"/\"Lane\"); Compass's suggest can return an unrelated top hit when the\nlocal market has no real match. `compass-mcp`'s rung order is: try this\ntypeahead first, and only fall back to the (WAF-walled) free-text/slug SSR\nsearch in §1 if it returns nothing verified.\n\n---\n\n## 5. Agent's listings\n\n`GET /agents/<slug>/` (e.g. `/agents/paige-mcguirk/`)\n\n```sh\nfpx get 'https://www.compass.com/agents/paige-mcguirk/' -p compass > /tmp/agent.html\nnode extract-global.mjs /tmp/agent.html __AGENT_PROFILE__ > /tmp/agent.json\n```\n\nExtraction paths (same `listing` shape as §2's `listingRelation.listing`):\n\n- Active listings: `.data.agentProfileProps.activeListingsProps.initialSales[]`\n- Closed deals: `.data.agentProfileProps.closedDealsProps.initialSales[].listing`\n  (each entry wraps the listing under a `.listing` key, alongside `aiHexID`).\n\n```sh\njq '.data.agentProfileProps.activeListingsProps.initialSales[] |\n  {sha: .listingIdSHA, address: .location.prettyAddress, price: .price.formatted, status: .localizedStatus}' \\\n  /tmp/agent.json\n\njq '.data.agentProfileProps.closedDealsProps.initialSales[].listing |\n  {sha: .listingIdSHA, address: .location.prettyAddress, close_price: .price.lastPropertyClosePrice}' \\\n  /tmp/agent.json\n```\n\nThis page is large (~1.4 MB rendered); the `extract-global.mjs` walk handles\nthat fine, but expect the fetch itself to take a few seconds.\n\n---\n\n## Not covered (no server-reachable endpoint)\n\n- **Saved homes / saved searches** — `/overview/favorites` is fully\n  client-rendered via auth-scoped GraphQL the bridge can't intercept.\n  `compass-mcp` stubs these tools out too; there's no fpx recipe for them.\n- **Market trends / stats** — Compass doesn't expose region-level market\n  data in any SSR blob (unlike some other portals' dedicated endpoint).\n\nFile v1.1.4:skill-card.md\n\n## Description:\n\nHelps agents query Compass real-estate listings, property details, price history, and agent profiles from a shell using a paired browser session.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and real-estate researchers use this skill to retrieve Compass listing and agent information, resolve addresses, and inspect price histories in shell-based workflows without an MCP server.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Browser-session-backed Compass requests may run afoul of site terms or exceed the user's authorization.\n\nMitigation: Confirm authorization and compliance with Compass terms before enabling the extension or automating requests.\n\nRisk: Persistent browser pairing and WAF-aware access reuse the user's browsing session.\n\nMitigation: Use only a browser session you control and review the pairing and requested access before running the skill.\n\nRisk: Search pages can expose only the first server-rendered results, so broad searches may appear complete when they are not.\n\nMitigation: Narrow searches by price or bedroom range and verify coverage before drawing market conclusions.\n\n## Reference(s):\n\n- [Compass request recipes](references/requests.md)\n- [HTML data extraction helper](references/extract-global.mjs)\n\n## Skill Output:\n\n**Output Type(s):** [Text, Shell commands, Code, Guidance]\n\n**Output Format:** [Markdown with shell commands and JSON examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Results depend on Compass page availability and the user's paired browser session.]\n\n## Skill Version(s):\n\n1.1.4 (source: ClawHub release evidence)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.1.3: 5 files, 9708 bytes\n\nFiles: references/extract-global.mjs (2851b), references/requests.md (7967b), skill-card.md (2387b), SKILL.md (5308b), _meta.json (130b)\n\nFile v1.1.3:SKILL.md\n\n---\nname: compass-fpx\ndescription: >-\n  Query compass.com (US real-estate portal) from a shell with the fpx CLI\n  (@fetchproxy/cli) instead of running the compass-mcp server — search\n  listings, get property/agent detail, price history, and resolve street\n  addresses through one-shot fetches over a signed-in browser tab. Use when\n  you want Compass data without the MCP, in a script, or on a machine where\n  the MCP isn't installed.\n---\n\n# Compass via fpx (no MCP)\n\nCompass has no public JSON data-API for search/listing/agent data — it's a\nfully server-rendered React app that embeds each page's data as JSON inside\nan inline `<script>` global (`global.uc`, `window.__INITIAL_DATA__`,\n`window.__AGENT_PROFILE__`). compass.com also runs **AWS WAF**, which 403s\nplain `curl`/Node requests on some paths regardless of headers. `fpx` routes\nevery request through the user's own signed-in browser tab (the fetchproxy\nextension), which already carries a cleared WAF session, so the page renders\nnormally — you then pull the JSON out of the HTML yourself.\n\nThis is **Bucket 1 (full-fetchproxy)**: every call, not just an auth\nbootstrap, rides the bridge. Compass validates at the session level per\nrequest, same as the `compass-mcp` server itself (no server-side path\nexists). No Compass login is required beyond having a signed-in tab open —\nsearch/listing/agent data and the address-typeahead endpoint are all public.\n\n## One-time setup\n\n```sh\nnpm install -g @fetchproxy/cli              # provides `fpx`\nfpx profile add compass --domain compass.com\nfpx pair -p compass                          # prints a pair code → approve in the fetchproxy extension\n```\n\nRequirements: the **fetchproxy** browser extension installed, an open\n`www.compass.com` tab, and its Chrome **Site access** allowing `compass.com`.\nPairing persists — after the first approval every later `fpx` call reuses it.\n\n## Core call pattern\n\nTwo request shapes:\n\n1. **SSR pages** (search, homedetails, agent profile) — `fpx get` the path,\n   then pull the inline JSON global out of the HTML.\n2. **The one structured endpoint** (address typeahead) — `fpx post-json`.\n\n```sh\nfpx get 'https://www.compass.com/homes-for-sale/manhattan-ny/' -p compass > /tmp/page.html\nnode references/extract-global.mjs /tmp/page.html uc | jq '.sharedReactAppProps.initialResults.lolResults.data | length'\n```\n\n`references/extract-global.mjs` is a small, dependency-free re-implementation\nof the balanced-brace walk `compass-mcp`'s own `src/page-state.ts` uses\n(`extractGlobalAssign`) — Compass writes each global as a JSON *literal*\n(`global.uc = {...};`), so once the object is sliced out, `JSON.parse`/`jq`\nworks normally. Regexing for it without brace-balancing breaks the moment a\nnested value contains `}`.\n\nFull endpoint list, exact paths, and `jq` projections are in\n`references/requests.md`.\n\n## The one rule: try the typeahead before free-text search\n\nCompass's SSR free-text search (`/homes-for-sale/?q=<query>`) is WAF-walled\nin production and reliably 403s. For **address resolution**, always hit the\nstructured, WAF-immune typeahead first (`POST /api/v3/omnisuggest/autocomplete`)\nand only fall back to the SSR search pages when it returns nothing — mirror\n`compass-mcp`'s own rung order (see `references/requests.md` §4).\n\n## Sign-in / bot-wall detection\n\nA response is NOT a good page when either is true:\n\n- The final URL redirects to `/login` (fpx follows redirects like a browser,\n  so check the body for the login form, or pass `-H` / inspect with\n  `--json` to see the resolved URL).\n- The body contains **both** `awswaf.com` and `challenge.js`, and is under\n  ~80 KB (the AWS WAF challenge interstitial; a real signed-in page is much\n  larger). `compass-mcp`'s `throwIfSignInPage` uses this exact pair of\n  markers — grep for both before trusting a fetch:\n\n```sh\ngrep -q 'awswaf.com' /tmp/page.html && grep -q 'challenge.js' /tmp/page.html && \\\n  [ \"$(wc -c < /tmp/page.html)\" -lt 80000 ] && echo \"WAF-blocked — refresh the compass.com tab\"\n```\n\n## Exit codes (fetch verbs)\n\n- `0` — success.\n- `2` — bridge unavailable: extension not connected or pairing pending → `fpx pair -p compass`, confirm a compass.com tab is open.\n- `3` — bot wall: the tab hasn't cleared the AWS WAF challenge → refresh a `www.compass.com` tab and retry.\n- `4` — upstream non-2xx from Compass.\n\n## Notes\n\n- **Pagination ceiling.** Compass's SSR search only server-renders its first\n  page (~41 listings); `/page-N/` canonicalizes back to page 1 and returns\n  identical data. To reach more of the market, narrow with price/bed bands\n  (`/<lo>-<hi>-bed/`, `/<lo>-<hi>-price/`) and re-search each band — don't\n  paginate.\n- **Two URL forms per listing.** `_pid/` (`navigationPageLink`) is a stable\n  opaque id — use it for bookmarks/trackers. `_lid/` (content-addressed by\n  `listingIdSHA`) is what you fetch to read the *current* record, but it\n  changes when a property is delisted and relisted.\n- No saved-homes/saved-searches or market-trends endpoint exists server-side\n  (client-rendered GraphQL / not exposed) — `compass-mcp` stubs those tools\n  out too; this skill doesn't cover them.\n- `fpx health -p compass` shows bridge connection state when a call fails.\n- This project is developed and maintained by AI (Claude).\n\nFile v1.1.3:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"compass-fpx\",\n  \"version\": \"1.1.3\",\n  \"publishedAt\": 1790262215442\n}\n\nFile v1.1.3:references/requests.md\n\n# Compass requests for fpx\n\nAll paths are relative to `https://www.compass.com`. GET requests return HTML\nwith an embedded JSON global — pull it out with `extract-global.mjs` (in this\nsame directory) before piping to `jq`. The one structured endpoint (§4) is a\nplain JSON POST.\n\n```sh\nfpx get 'https://www.compass.com/<path>' -p compass > /tmp/page.html\nnode extract-global.mjs /tmp/page.html <globalName> | jq '...'\n```\n\nEndpoints and field shapes below are transcribed from `compass-mcp`'s\n`src/tools/*.ts` and `src/page-state.ts` — the same live-verified paths and\nextraction targets the MCP tools use.\n\n---\n\n## 1. Search listings\n\n`GET /homes-for-sale/<location-slug>/[<filters>/]`\n\n- `<location-slug>`: lowercase, hyphenated free text (`\"Brooklyn, NY\"` →\n  `brooklyn-ny`, `\"94110\"` → `94110`).\n- Filters are path segments, in this order: `<bedsMin>-<bedsMax>-bed/`,\n  `<priceMin>-<priceMax>-price/`, a home-type slug (`type-house`,\n  `type-condo`, `type-townhouse`, `type-multi-family`, `type-land`,\n  `type-rental`). Omit any filter you don't need.\n- **Pagination ceiling**: Compass server-renders only ~41 listings into this\n  page (`num`); `/page-N/` canonicalizes back to page 1 with identical data.\n  To see more of the market, narrow the price/bed bands and re-search each\n  band — there is no further page to fetch.\n\n```sh\nfpx get 'https://www.compass.com/homes-for-sale/brooklyn-ny/2-3-bed/500000-1500000-price/type-condo/' -p compass > /tmp/search.html\nnode extract-global.mjs /tmp/search.html uc > /tmp/search.json\n```\n\nExtraction path: `.sharedReactAppProps.initialResults.lolResults`\n(`{ totalItems, data: [ { listing: {...} } ] }`).\n\n```sh\njq -r '.sharedReactAppProps.initialResults.lolResults |\n  \"total=\\(.totalItems)\",\n  (.data[].listing |\n    \"\\(.listingIdSHA)\\t\\(.subtitles[0] // \"\")\\t\\(.title // \"\")\\t\\(.subStats // [] | map(select(.title==\"beds\")) | .[0].subtitle // \"\")bd\")' \\\n  /tmp/search.json\n```\n\nPer-listing fields worth pulling: `listingIdSHA`, `pageLink` (the `_lid/`\ndetail URL, relative), `navigationPageLink` (the stable `_pid/` URL when\npresent), `title` (formatted price string), `subtitles` (`[street,\nneighborhood]`), `status`, `location.{latitude,longitude}`,\n`media[].{originalUrl,thumbnailUrl,category}` (`category:0` = photo),\n`subStats[]` (`{title:\"beds\"|\"baths\"|\"sqft\", subtitle:\"<n>\"}`),\n`clusterSummary.{bedrooms,bathrooms,priceRange:[min,max],formattedLotSize,savedListing}`.\n\n**Comparable rentals** reuse this exact path shape with the rental filter:\n`/homes-for-sale/<locality-slug>/type-rental/` — same extraction.\n\n**Free-text SSR search** (`/homes-for-sale/?q=<query>`) exists but is\nWAF-walled in production (reliably 403s) — don't rely on it; use §4 for\naddress resolution instead.\n\n---\n\n## 2. Property / listing detail\n\n`GET /homedetails/<slug>/<listingIdSHA>_lid/`\n\nIf you only have the `listingIdSHA` (no slug), fetch\n`GET /listing/<listingIdSHA>/view` instead — it 302-redirects to the\ncanonical slugged homedetails URL, and fpx follows the redirect like a\nbrowser.\n\n```sh\nfpx get 'https://www.compass.com/homedetails/162-04-12th-Rd-Queens-NY-11357/2109718971930079225_lid/' -p compass > /tmp/detail.html\n# or, sha-only:\nfpx get 'https://www.compass.com/listing/2109718971930079225/view' -p compass > /tmp/detail.html\n\nnode extract-global.mjs /tmp/detail.html __INITIAL_DATA__ > /tmp/detail.json\n```\n\nExtraction path: `.props.listingRelation.listing`.\n\n```sh\njq '.props.listingRelation.listing |\n  {address: .location.prettyAddress, city: .location.city, state: .location.state, zip: .location.zipCode,\n   price: .price.lastKnown, price_formatted: .price.formatted, per_sqft: .price.perSquareFoot,\n   beds: .size.bedrooms, baths: .size.totalBathrooms, sqft: .size.squareFeet,\n   lot_sqft: .size.lotSizeInSquareFeet, status: .localizedStatus, mls_status: .mlsStatus,\n   photos: [.media[]?.originalUrl], schools: .detailedInfo.schools,\n   agent: .agents[0]}' /tmp/detail.json\n```\n\nOther useful top-level fields: `compassPropertyId`, `pageLink` (`_lid/` URL),\n`navigationPageLink` (stable `_pid/` URL), `description`,\n`detailedInfo.{amenities,taxAnnualAmount,associationFee:{amount,frequency},\narchitecturalStyle,garageSpaces,totalParkingSpaces}`,\n`events[]`/`history[]` (see §3), `mlsAlternateAddresses[]`,\n`agents[].{id,fullName,companyName,profileUrl}`.\n\n**Sentinel values**: `detailedInfo.taxAnnualAmount` under `10` is a\nnot-yet-assessed placeholder on new construction, not a real tax bill.\n\n---\n\n## 3. Price history\n\nSame fetch + extraction as §2 (`__INITIAL_DATA__.props.listingRelation.listing`).\nRead `events[]` (this listing's own MLS events) and `history[]` (events from\nprior listings of the same property) — each entry:\n`{ price, status, localizedStatus, timestamp (unix ms), pageLink, source:{externalSourceName} }`.\n\n```sh\njq '.props.listingRelation.listing | {events, history}' /tmp/detail.json\n# Earliest \"Listed\" event (days-on-market anchor):\njq -r '.props.listingRelation.listing.events // [] |\n  map(select(.status==1 or (.localizedStatus|test(\"^listed$\";\"i\")))) |\n  sort_by(.timestamp) | .[0] // empty' /tmp/detail.json\n```\n\n---\n\n## 4. Address typeahead (resolve an address → listing id) — WAF-immune, try FIRST\n\n`POST /api/v3/omnisuggest/autocomplete`\n\nBody: `{\"q\": \"<street + city + state>\", \"sources\": [0]}` — `sources: [0]`\nrestricts results to the Addresses source.\n\n```sh\ncat > /tmp/omni.json <<'JSON'\n{\"q\": \"158 Raven Blvd Lake Lure NC\", \"sources\": [0]}\nJSON\nfpx post-json 'https://www.compass.com/api/v3/omnisuggest/autocomplete' @/tmp/omni.json -p compass \\\n  | jq '.categories[] | select(.name==1) | .items[] | {id, text, subText, redirectUrl}'\n```\n\nResponse shape: `{ categories: [ { name, label, items: [ { text, subText,\nredirectUrl, source, id, ucGeoId } ] } ], success, rankerVersion }`. Category\n`name === 1` is \"Addresses\". `id` **is** the listing's `listingIdSHA` — it\nmatches the `_lid/` segment on the public homedetails page, so it feeds\nstraight into §2's sha-only fetch (`/listing/<id>/view`).\n\n**Verify the match before trusting it** — the query terms should appear as\nwhole tokens in `text`/`subText` (case + street-suffix folded, e.g.\n\"Ln\"/\"Lane\"); Compass's suggest can return an unrelated top hit when the\nlocal market has no real match. `compass-mcp`'s rung order is: try this\ntypeahead first, and only fall back to the (WAF-walled) free-text/slug SSR\nsearch in §1 if it returns nothing verified.\n\n---\n\n## 5. Agent's listings\n\n`GET /agents/<slug>/` (e.g. `/agents/paige-mcguirk/`)\n\n```sh\nfpx get 'https://www.compass.com/agents/paige-mcguirk/' -p compass > /tmp/agent.html\nnode extract-global.mjs /tmp/agent.html __AGENT_PROFILE__ > /tmp/agent.json\n```\n\nExtraction paths (same `listing` shape as §2's `listingRelation.listing`):\n\n- Active listings: `.data.agentProfileProps.activeListingsProps.initialSales[]`\n- Closed deals: `.data.agentProfileProps.closedDealsProps.initialSales[].listing`\n  (each entry wraps the listing under a `.listing` key, alongside `aiHexID`).\n\n```sh\njq '.data.agentProfileProps.activeListingsProps.initialSales[] |\n  {sha: .listingIdSHA, address: .location.prettyAddress, price: .price.formatted, status: .localizedStatus}' \\\n  /tmp/agent.json\n\njq '.data.agentProfileProps.closedDealsProps.initialSales[].listing |\n  {sha: .listingIdSHA, address: .location.prettyAddress, close_price: .price.lastPropertyClosePrice}' \\\n  /tmp/agent.json\n```\n\nThis page is large (~1.4 MB rendered); the `extract-global.mjs` walk handles\nthat fine, but expect the fetch itself to take a few seconds.\n\n---\n\n## Not covered (no server-reachable endpoint)\n\n- **Saved homes / saved searches** — `/overview/favorites` is fully\n  client-rendered via auth-scoped GraphQL the bridge can't intercept.\n  `compass-mcp` stubs these tools out too; there's no fpx recipe for them.\n- **Market trends / stats** — Compass doesn't expose region-level market\n  data in any SSR blob (unlike some other portals' dedicated endpoint).\n\nFile v1.1.3:skill-card.md\n\n## Description:\n\nQuery compass.com from a shell with the fpx CLI to search listings, get property and agent details, read price history, and resolve street addresses through one-shot fetches over a signed-in browser tab.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and agents use this skill to fetch Compass real-estate search, listing, price-history, address-resolution, and agent-listing data from shell workflows without running the Compass MCP server.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: fpx requests use the browser session context associated with a paired Compass tab.\n\nMitigation: Limit the fpx profile to compass.com, query only data you intend to access, and remove the pairing when the skill is no longer needed.\n\nRisk: Compass may return a login page or AWS WAF challenge instead of the expected data page.\n\nMitigation: Check for login redirects and AWS WAF markers before trusting fetched HTML, then refresh the Compass tab or re-pair fetchproxy if needed.\n\nRisk: Compass SSR search results expose only the first rendered result set, so broad searches can be incomplete.\n\nMitigation: Narrow searches with price, bed, and home-type bands and re-run each band instead of relying on pagination.\n\n## Reference(s):\n\n- [Compass request recipes](references/requests.md)\n- [Compass global extraction helper](references/extract-global.mjs)\n- [Compass website](https://www.compass.com)\n- [ClawHub skill page](https://clawhub.ai/chrischall/skills/compass-fpx)\n\n## Skill Output:\n\n**Output Type(s):** [text, markdown, code, shell commands, configuration, guidance]\n\n**Output Format:** [Markdown with shell commands, JSON extraction examples, and jq projections]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Produces command recipes and parsing guidance for Compass HTML and JSON responses; the skill itself does not expose a long-running service.]\n\n## Skill Version(s):\n\n1.1.3 (source: server release evidence)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.1.2: 5 files, 9652 bytes\n\nFiles: references/extract-global.mjs (2851b), references/requests.md (7967b), skill-card.md (2297b), SKILL.md (5308b), _meta.json (130b)\n\nFile v1.1.2:SKILL.md\n\n---\nname: compass-fpx\ndescription: >-\n  Query compass.com (US real-estate portal) from a shell with the fpx CLI\n  (@fetchproxy/cli) instead of running the compass-mcp server — search\n  listings, get property/agent detail, price history, and resolve street\n  addresses through one-shot fetches over a signed-in browser tab. Use when\n  you want Compass data without the MCP, in a script, or on a machine where\n  the MCP isn't installed.\n---\n\n# Compass via fpx (no MCP)\n\nCompass has no public JSON data-API for search/listing/agent data — it's a\nfully server-rendered React app that embeds each page's data as JSON inside\nan inline `<script>` global (`global.uc`, `window.__INITIAL_DATA__`,\n`window.__AGENT_PROFILE__`). compass.com also runs **AWS WAF**, which 403s\nplain `curl`/Node requests on some paths regardless of headers. `fpx` routes\nevery request through the user's own signed-in browser tab (the fetchproxy\nextension), which already carries a cleared WAF session, so the page renders\nnormally — you then pull the JSON out of the HTML yourself.\n\nThis is **Bucket 1 (full-fetchproxy)**: every call, not just an auth\nbootstrap, rides the bridge. Compass validates at the session level per\nrequest, same as the `compass-mcp` server itself (no server-side path\nexists). No Compass login is required beyond having a signed-in tab open —\nsearch/listing/agent data and the address-typeahead endpoint are all public.\n\n## One-time setup\n\n```sh\nnpm install -g @fetchproxy/cli              # provides `fpx`\nfpx profile add compass --domain compass.com\nfpx pair -p compass                          # prints a pair code → approve in the fetchproxy extension\n```\n\nRequirements: the **fetchproxy** browser extension installed, an open\n`www.compass.com` tab, and its Chrome **Site access** allowing `compass.com`.\nPairing persists — after the first approval every later `fpx` call reuses it.\n\n## Core call pattern\n\nTwo request shapes:\n\n1. **SSR pages** (search, homedetails, agent profile) — `fpx get` the path,\n   then pull the inline JSON global out of the HTML.\n2. **The one structured endpoint** (address typeahead) — `fpx post-json`.\n\n```sh\nfpx get 'https://www.compass.com/homes-for-sale/manhattan-ny/' -p compass > /tmp/page.html\nnode references/extract-global.mjs /tmp/page.html uc | jq '.sharedReactAppProps.initialResults.lolResults.data | length'\n```\n\n`references/extract-global.mjs` is a small, dependency-free re-implementation\nof the balanced-brace walk `compass-mcp`'s own `src/page-state.ts` uses\n(`extractGlobalAssign`) — Compass writes each global as a JSON *literal*\n(`global.uc = {...};`), so once the object is sliced out, `JSON.parse`/`jq`\nworks normally. Regexing for it without brace-balancing breaks the moment a\nnested value contains `}`.\n\nFull endpoint list, exact paths, and `jq` projections are in\n`references/requests.md`.\n\n## The one rule: try the typeahead before free-text search\n\nCompass's SSR free-text search (`/homes-for-sale/?q=<query>`) is WAF-walled\nin production and reliably 403s. For **address resolution**, always hit the\nstructured, WAF-immune typeahead first (`POST /api/v3/omnisuggest/autocomplete`)\nand only fall back to the SSR search pages when it returns nothing — mirror\n`compass-mcp`'s own rung order (see `references/requests.md` §4).\n\n## Sign-in / bot-wall detection\n\nA response is NOT a good page when either is true:\n\n- The final URL redirects to `/login` (fpx follows redirects like a browser,\n  so check the body for the login form, or pass `-H` / inspect with\n  `--json` to see the resolved URL).\n- The body contains **both** `awswaf.com` and `challenge.js`, and is under\n  ~80 KB (the AWS WAF challenge interstitial; a real signed-in page is much\n  larger). `compass-mcp`'s `throwIfSignInPage` uses this exact pair of\n  markers — grep for both before trusting a fetch:\n\n```sh\ngrep -q 'awswaf.com' /tmp/page.html && grep -q 'challenge.js' /tmp/page.html && \\\n  [ \"$(wc -c < /tmp/page.html)\" -lt 80000 ] && echo \"WAF-blocked — refresh the compass.com tab\"\n```\n\n## Exit codes (fetch verbs)\n\n- `0` — success.\n- `2` — bridge unavailable: extension not connected or pairing pending → `fpx pair -p compass`, confirm a compass.com tab is open.\n- `3` — bot wall: the tab hasn't cleared the AWS WAF challenge → refresh a `www.compass.com` tab and retry.\n- `4` — upstream non-2xx from Compass.\n\n## Notes\n\n- **Pagination ceiling.** Compass's SSR search only server-renders its first\n  page (~41 listings); `/page-N/` canonicalizes back to page 1 and returns\n  identical data. To reach more of the market, narrow with price/bed bands\n  (`/<lo>-<hi>-bed/`, `/<lo>-<hi>-price/`) and re-search each band — don't\n  paginate.\n- **Two URL forms per listing.** `_pid/` (`navigationPageLink`) is a stable\n  opaque id — use it for bookmarks/trackers. `_lid/` (content-addressed by\n  `listingIdSHA`) is what you fetch to read the *current* record, but it\n  changes when a property is delisted and relisted.\n- No saved-homes/saved-searches or market-trends endpoint exists server-side\n  (client-rendered GraphQL / not exposed) — `compass-mcp` stubs those tools\n  out too; this skill doesn't cover them.\n- `fpx health -p compass` shows bridge connection state when a call fails.\n- This project is developed and maintained by AI (Claude).\n\nFile v1.1.2:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"compass-fpx\",\n  \"version\": \"1.1.2\",\n  \"publishedAt\": 1790199499046\n}\n\nFile v1.1.2:references/requests.md\n\n# Compass requests for fpx\n\nAll paths are relative to `https://www.compass.com`. GET requests return HTML\nwith an embedded JSON global — pull it out with `extract-global.mjs` (in this\nsame directory) before piping to `jq`. The one structured endpoint (§4) is a\nplain JSON POST.\n\n```sh\nfpx get 'https://www.compass.com/<path>' -p compass > /tmp/page.html\nnode extract-global.mjs /tmp/page.html <globalName> | jq '...'\n```\n\nEndpoints and field shapes below are transcribed from `compass-mcp`'s\n`src/tools/*.ts` and `src/page-state.ts` — the same live-verified paths and\nextraction targets the MCP tools use.\n\n---\n\n## 1. Search listings\n\n`GET /homes-for-sale/<location-slug>/[<filters>/]`\n\n- `<location-slug>`: lowercase, hyphenated free text (`\"Brooklyn, NY\"` →\n  `brooklyn-ny`, `\"94110\"` → `94110`).\n- Filters are path segments, in this order: `<bedsMin>-<bedsMax>-bed/`,\n  `<priceMin>-<priceMax>-price/`, a home-type slug (`type-house`,\n  `type-condo`, `type-townhouse`, `type-multi-family`, `type-land`,\n  `type-rental`). Omit any filter you don't need.\n- **Pagination ceiling**: Compass server-renders only ~41 listings into this\n  page (`num`); `/page-N/` canonicalizes back to page 1 with identical data.\n  To see more of the market, narrow the price/bed bands and re-search each\n  band — there is no further page to fetch.\n\n```sh\nfpx get 'https://www.compass.com/homes-for-sale/brooklyn-ny/2-3-bed/500000-1500000-price/type-condo/' -p compass > /tmp/search.html\nnode extract-global.mjs /tmp/search.html uc > /tmp/search.json\n```\n\nExtraction path: `.sharedReactAppProps.initialResults.lolResults`\n(`{ totalItems, data: [ { listing: {...} } ] }`).\n\n```sh\njq -r '.sharedReactAppProps.initialResults.lolResults |\n  \"total=\\(.totalItems)\",\n  (.data[].listing |\n    \"\\(.listingIdSHA)\\t\\(.subtitles[0] // \"\")\\t\\(.title // \"\")\\t\\(.subStats // [] | map(select(.title==\"beds\")) | .[0].subtitle // \"\")bd\")' \\\n  /tmp/search.json\n```\n\nPer-listing fields worth pulling: `listingIdSHA`, `pageLink` (the `_lid/`\ndetail URL, relative), `navigationPageLink` (the stable `_pid/` URL when\npresent), `title` (formatted price string), `subtitles` (`[street,\nneighborhood]`), `status`, `location.{latitude,longitude}`,\n`media[].{originalUrl,thumbnailUrl,category}` (`category:0` = photo),\n`subStats[]` (`{title:\"beds\"|\"baths\"|\"sqft\", subtitle:\"<n>\"}`),\n`clusterSummary.{bedrooms,bathrooms,priceRange:[min,max],formattedLotSize,savedListing}`.\n\n**Comparable rentals** reuse this exact path shape with the rental filter:\n`/homes-for-sale/<locality-slug>/type-rental/` — same extraction.\n\n**Free-text SSR search** (`/homes-for-sale/?q=<query>`) exists but is\nWAF-walled in production (reliably 403s) — don't rely on it; use §4 for\naddress resolution instead.\n\n---\n\n## 2. Property / listing detail\n\n`GET /homedetails/<slug>/<listingIdSHA>_lid/`\n\nIf you only have the `listingIdSHA` (no slug), fetch\n`GET /listing/<listingIdSHA>/view` instead — it 302-redirects to the\ncanonical slugged homedetails URL, and fpx follows the redirect like a\nbrowser.\n\n```sh\nfpx get 'https://www.compass.com/homedetails/162-04-12th-Rd-Queens-NY-11357/2109718971930079225_lid/' -p compass > /tmp/detail.html\n# or, sha-only:\nfpx get 'https://www.compass.com/listing/2109718971930079225/view' -p compass > /tmp/detail.html\n\nnode extract-global.mjs /tmp/detail.html __INITIAL_DATA__ > /tmp/detail.json\n```\n\nExtraction path: `.props.listingRelation.listing`.\n\n```sh\njq '.props.listingRelation.listing |\n  {address: .location.prettyAddress, city: .location.city, state: .location.state, zip: .location.zipCode,\n   price: .price.lastKnown, price_formatted: .price.formatted, per_sqft: .price.perSquareFoot,\n   beds: .size.bedrooms, baths: .size.totalBathrooms, sqft: .size.squareFeet,\n   lot_sqft: .size.lotSizeInSquareFeet, status: .localizedStatus, mls_status: .mlsStatus,\n   photos: [.media[]?.originalUrl], schools: .detailedInfo.schools,\n   agent: .agents[0]}' /tmp/detail.json\n```\n\nOther useful top-level fields: `compassPropertyId`, `pageLink` (`_lid/` URL),\n`navigationPageLink` (stable `_pid/` URL), `description`,\n`detailedInfo.{amenities,taxAnnualAmount,associationFee:{amount,frequency},\narchitecturalStyle,garageSpaces,totalParkingSpaces}`,\n`events[]`/`history[]` (see §3), `mlsAlternateAddresses[]`,\n`agents[].{id,fullName,companyName,profileUrl}`.\n\n**Sentinel values**: `detailedInfo.taxAnnualAmount` under `10` is a\nnot-yet-assessed placeholder on new construction, not a real tax bill.\n\n---\n\n## 3. Price history\n\nSame fetch + extraction as §2 (`__INITIAL_DATA__.props.listingRelation.listing`).\nRead `events[]` (this listing's own MLS events) and `history[]` (events from\nprior listings of the same property) — each entry:\n`{ price, status, localizedStatus, timestamp (unix ms), pageLink, source:{externalSourceName} }`.\n\n```sh\njq '.props.listingRelation.listing | {events, history}' /tmp/detail.json\n# Earliest \"Listed\" event (days-on-market anchor):\njq -r '.props.listingRelation.listing.events // [] |\n  map(select(.status==1 or (.localizedStatus|test(\"^listed$\";\"i\")))) |\n  sort_by(.timestamp) | .[0] // empty' /tmp/detail.json\n```\n\n---\n\n## 4. Address typeahead (resolve an address → listing id) — WAF-immune, try FIRST\n\n`POST /api/v3/omnisuggest/autocomplete`\n\nBody: `{\"q\": \"<street + city + state>\", \"sources\": [0]}` — `sources: [0]`\nrestricts results to the Addresses source.\n\n```sh\ncat > /tmp/omni.json <<'JSON'\n{\"q\": \"158 Raven Blvd Lake Lure NC\", \"sources\": [0]}\nJSON\nfpx post-json 'https://www.compass.com/api/v3/omnisuggest/autocomplete' @/tmp/omni.json -p compass \\\n  | jq '.categories[] | select(.name==1) | .items[] | {id, text, subText, redirectUrl}'\n```\n\nResponse shape: `{ categories: [ { name, label, items: [ { text, subText,\nredirectUrl, source, id, ucGeoId } ] } ], success, rankerVersion }`. Category\n`name === 1` is \"Addresses\". `id` **is** the listing's `listingIdSHA` — it\nmatches the `_lid/` segment on the public homedetails page, so it feeds\nstraight into §2's sha-only fetch (`/listing/<id>/view`).\n\n**Verify the match before trusting it** — the query terms should appear as\nwhole tokens in `text`/`subText` (case + street-suffix folded, e.g.\n\"Ln\"/\"Lane\"); Compass's suggest can return an unrelated top hit when the\nlocal market has no real match. `compass-mcp`'s rung order is: try this\ntypeahead first, and only fall back to the (WAF-walled) free-text/slug SSR\nsearch in §1 if it returns nothing verified.\n\n---\n\n## 5. Agent's listings\n\n`GET /agents/<slug>/` (e.g. `/agents/paige-mcguirk/`)\n\n```sh\nfpx get 'https://www.compass.com/agents/paige-mcguirk/' -p compass > /tmp/agent.html\nnode extract-global.mjs /tmp/agent.html __AGENT_PROFILE__ > /tmp/agent.json\n```\n\nExtraction paths (same `listing` shape as §2's `listingRelation.listing`):\n\n- Active listings: `.data.agentProfileProps.activeListingsProps.initialSales[]`\n- Closed deals: `.data.agentProfileProps.closedDealsProps.initialSales[].listing`\n  (each entry wraps the listing under a `.listing` key, alongside `aiHexID`).\n\n```sh\njq '.data.agentProfileProps.activeListingsProps.initialSales[] |\n  {sha: .listingIdSHA, address: .location.prettyAddress, price: .price.formatted, status: .localizedStatus}' \\\n  /tmp/agent.json\n\njq '.data.agentProfileProps.closedDealsProps.initialSales[].listing |\n  {sha: .listingIdSHA, address: .location.prettyAddress, close_price: .price.lastPropertyClosePrice}' \\\n  /tmp/agent.json\n```\n\nThis page is large (~1.4 MB rendered); the `extract-global.mjs` walk handles\nthat fine, but expect the fetch itself to take a few seconds.\n\n---\n\n## Not covered (no server-reachable endpoint)\n\n- **Saved homes / saved searches** — `/overview/favorites` is fully\n  client-rendered via auth-scoped GraphQL the bridge can't intercept.\n  `compass-mcp` stubs these tools out too; there's no fpx recipe for them.\n- **Market trends / stats** — Compass doesn't expose region-level market\n  data in any SSR blob (unlike some other portals' dedicated endpoint).\n\nFile v1.1.2:skill-card.md\n\n## Description:\n\nQuery Compass real-estate pages from a shell with the fpx CLI to search listings, retrieve property and agent details, inspect price history, and resolve street addresses through one-shot fetches over a signed-in browser tab.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and automation operators use this skill to fetch public Compass listing, property, price-history, and agent data from scripts when the Compass MCP server is unavailable or unnecessary.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Fetches run through the user's browser session and depend on the fetchproxy bridge and a Compass tab.\n\nMitigation: Use a dedicated browser profile when isolation matters, verify the fpx profile before use, and review commands before running them.\n\nRisk: Temporary HTML or JSON files can contain Compass listing or agent data.\n\nMitigation: Store outputs only as needed and remove temporary files such as /tmp HTML or JSON captures after processing.\n\nRisk: Compass WAF or sign-in interstitials can produce unusable page captures.\n\nMitigation: Check for documented bot-wall markers and refresh the Compass tab or repair pairing before trusting extracted data.\n\n## Reference(s):\n\n- [ClawHub skill page](https://clawhub.ai/chrischall/skills/compass-fpx)\n- [Compass request recipes](artifact/references/requests.md)\n- [Compass global extraction helper](artifact/references/extract-global.mjs)\n\n## Skill Output:\n\n**Output Type(s):** [text, markdown, code, shell commands, configuration, guidance]\n\n**Output Format:** [Markdown guidance with shell commands, jq filters, and JavaScript helper code]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Commands use fpx with a Compass browser profile and may write temporary HTML or JSON files for local processing.]\n\n## Skill Version(s):\n\n1.1.2 (source: server release evidence)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.1.1: 5 files, 9523 bytes\n\nFiles: references/extract-global.mjs (2851b), references/requests.md (7967b), skill-card.md (2001b), SKILL.md (5308b), _meta.json (130b)\n\nFile v1.1.1:SKILL.md\n\n---\nname: compass-fpx\ndescription: >-\n  Query compass.com (US real-estate portal) from a shell with the fpx CLI\n  (@fetchproxy/cli) instead of running the compass-mcp server — search\n  listings, get property/agent detail, price history, and resolve street\n  addresses through one-shot fetches over a signed-in browser tab. Use when\n  you want Compass data without the MCP, in a script, or on a machine where\n  the MCP isn't installed.\n---\n\n# Compass via fpx (no MCP)\n\nCompass has no public JSON data-API for search/listing/agent data — it's a\nfully server-rendered React app that embeds each page's data as JSON inside\nan inline `<script>` global (`global.uc`, `window.__INITIAL_DATA__`,\n`window.__AGENT_PROFILE__`). compass.com also runs **AWS WAF**, which 403s\nplain `curl`/Node requests on some paths regardless of headers. `fpx` routes\nevery request through the user's own signed-in browser tab (the fetchproxy\nextension), which already carries a cleared WAF session, so the page renders\nnormally — you then pull the JSON out of the HTML yourself.\n\nThis is **Bucket 1 (full-fetchproxy)**: every call, not just an auth\nbootstrap, rides the bridge. Compass validates at the session level per\nrequest, same as the `compass-mcp` server itself (no server-side path\nexists). No Compass login is required beyond having a signed-in tab open —\nsearch/listing/agent data and the address-typeahead endpoint are all public.\n\n## One-time setup\n\n```sh\nnpm install -g @fetchproxy/cli              # provides `fpx`\nfpx profile add compass --domain compass.com\nfpx pair -p compass                          # prints a pair code → approve in the fetchproxy extension\n```\n\nRequirements: the **fetchproxy** browser extension installed, an open\n`www.compass.com` tab, and its Chrome **Site access** allowing `compass.com`.\nPairing persists — after the first approval every later `fpx` call reuses it.\n\n## Core call pattern\n\nTwo request shapes:\n\n1. **SSR pages** (search, homedetails, agent profile) — `fpx get` the path,\n   then pull the inline JSON global out of the HTML.\n2. **The one structured endpoint** (address typeahead) — `fpx post-json`.\n\n```sh\nfpx get 'https://www.compass.com/homes-for-sale/manhattan-ny/' -p compass > /tmp/page.html\nnode references/extract-global.mjs /tmp/page.html uc | jq '.sharedReactAppProps.initialResults.lolResults.data | length'\n```\n\n`references/extract-global.mjs` is a small, dependency-free re-implementation\nof the balanced-brace walk `compass-mcp`'s own `src/page-state.ts` uses\n(`extractGlobalAssign`) — Compass writes each global as a JSON *literal*\n(`global.uc = {...};`), so once the object is sliced out, `JSON.parse`/`jq`\nworks normally. Regexing for it without brace-balancing breaks the moment a\nnested value contains `}`.\n\nFull endpoint list, exact paths, and `jq` projections are in\n`references/requests.md`.\n\n## The one rule: try the typeahead before free-text search\n\nCompass's SSR free-text search (`/homes-for-sale/?q=<query>`) is WAF-walled\nin production and reliably 403s. For **address resolution**, always hit the\nstructured, WAF-immune typeahead first (`POST /api/v3/omnisuggest/autocomplete`)\nand only fall back to the SSR search pages when it returns nothing — mirror\n`compass-mcp`'s own rung order (see `references/requests.md` §4).\n\n## Sign-in / bot-wall detection\n\nA response is NOT a good page when either is true:\n\n- The final URL redirects to `/login` (fpx follows redirects like a browser,\n  so check the body for the login form, or pass `-H` / inspect with\n  `--json` to see the resolved URL).\n- The body contains **both** `awswaf.com` and `challenge.js`, and is under\n  ~80 KB (the AWS WAF challenge interstitial; a real signed-in page is much\n  larger). `compass-mcp`'s `throwIfSignInPage` uses this exact pair of\n  markers — grep for both before trusting a fetch:\n\n```sh\ngrep -q 'awswaf.com' /tmp/page.html && grep -q 'challenge.js' /tmp/page.html && \\\n  [ \"$(wc -c < /tmp/page.html)\" -lt 80000 ] && echo \"WAF-blocked — refresh the compass.com tab\"\n```\n\n## Exit codes (fetch verbs)\n\n- `0` — success.\n- `2` — bridge unavailable: extension not connected or pairing pending → `fpx pair -p compass`, confirm a compass.com tab is open.\n- `3` — bot wall: the tab hasn't cleared the AWS WAF challenge → refresh a `www.compass.com` tab and retry.\n- `4` — upstream non-2xx from Compass.\n\n## Notes\n\n- **Pagination ceiling.** Compass's SSR search only server-renders its first\n  page (~41 listings); `/page-N/` canonicalizes back to page 1 and returns\n  identical data. To reach more of the market, narrow with price/bed bands\n  (`/<lo>-<hi>-bed/`, `/<lo>-<hi>-price/`) and re-search each band — don't\n  paginate.\n- **Two URL forms per listing.** `_pid/` (`navigationPageLink`) is a stable\n  opaque id — use it for bookmarks/trackers. `_lid/` (content-addressed by\n  `listingIdSHA`) is what you fetch to read the *current* record, but it\n  changes when a property is delisted and relisted.\n- No saved-homes/saved-searches or market-trends endpoint exists server-side\n  (client-rendered GraphQL / not exposed) — `compass-mcp` stubs those tools\n  out too; this skill doesn't cover them.\n- `fpx health -p compass` shows bridge connection state when a call fails.\n- This project is developed and maintained by AI (Claude).\n\nFile v1.1.1:_meta.json\n\n{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"compass-fpx\",\n  \"version\": \"1.1.1\",\n  \"publishedAt\": 1790178299153\n}\n\nFile v1.1.1:references/requests.md\n\n# Compass requests for fpx\n\nAll paths are relative to `https://www.compass.com`. GET requests return HTML\nwith an embedded JSON global — pull it out with `extract-global.mjs` (in this\nsame directory) before piping to `jq`. The one structured endpoint (§4) is a\nplain JSON POST.\n\n```sh\nfpx get 'https://www.compass.com/<path>' -p compass > /tmp/page.html\nnode extract-global.mjs /tmp/page.html <globalName> | jq '...'\n```\n\nEndpoints and field shapes below are transcribed from `compass-mcp`'s\n`src/tools/*.ts` and `src/page-state.ts` — the same live-verified paths and\nextraction targets the MCP tools use.\n\n---\n\n## 1. Search listings\n\n`GET /homes-for-sale/<location-slug>/[<filters>/]`\n\n- `<location-slug>`: lowercase, hyphenated free text (`\"Brooklyn, NY\"` →\n  `brooklyn-ny`, `\"94110\"` → `94110`).\n- Filters are path segments, in this order: `<bedsMin>-<bedsMax>-bed/`,\n  `<priceMin>-<priceMax>-price/`, a home-type slug (`type-house`,\n  `type-condo`, `type-townhouse`, `type-multi-family`, `type-land`,\n  `type-rental`). Omit any filter you don't need.\n- **Pagination ceiling**: Compass server-renders only ~41 listings into this\n  page (`num`); `/page-N/` canonicalizes back to page 1 with identical data.\n  To see more of the market, narrow the price/bed bands and re-search each\n  band — there is no further page to fetch.\n\n```sh\nfpx get 'https://www.compass.com/homes-for-sale/brooklyn-ny/2-3-bed/500000-1500000-price/type-condo/' -p compass > /tmp/search.html\nnode extract-global.mjs /tmp/search.html uc > /tmp/search.json\n```\n\nExtraction path: `.sharedReactAppProps.initialResults.lolResults`\n(`{ totalItems, data: [ { listing: {...} } ] }`).\n\n```sh\njq -r '.sharedReactAppProps.initialResults.lolResults |\n  \"total=\\(.totalItems)\",\n  (.data[].listing |\n    \"\\(.listingIdSHA)\\t\\(.subtitles[0] // \"\")\\t\\(.title // \"\")\\t\\(.subStats // [] | map(select(.title==\"beds\")) | .[0].subtitle // \"\")bd\")' \\\n  /tmp/search.json\n```\n\nPer-listing fields worth pulling: `listingIdSHA`, `pageLink` (the `_lid/`\ndetail URL, relative), `navigationPageLink` (the stable `_pid/` URL when\npresent), `title` (formatted price string), `subtitles` (`[street,\nneighborhood]`), `status`, `location.{latitude,longitude}`,\n`media[].{originalUrl,thumbnailUrl,category}` (`category:0` = photo),\n`subStats[]` (`{title:\"beds\"|\"baths\"|\"sqft\", subtitle:\"<n>\"}`),\n`clusterSummary.{bedrooms,bathrooms,priceRange:[min,max],formattedLotSize,savedListing}`.\n\n**Comparable rentals** reuse this exact path shape with the rental filter:\n`/homes-for-sale/<locality-slug>/type-rental/` — same extraction.\n\n**Free-text SSR search** (`/homes-for-sale/?q=<query>`) exists but is\nWAF-walled in production (reliably 403s) — don't rely on it; use §4 for\naddress resolution instead.\n\n---\n\n## 2. Property / listing detail\n\n`GET /homedetails/<slug>/<listingIdSHA>_lid/`\n\nIf you only have the `listingIdSHA` (no slug), fetch\n`GET /listing/<listingIdSHA>/view` instead — it 302-redirects to the\ncanonical slugged homedetails URL, and fpx follows the redirect like a\nbrowser.\n\n```sh\nfpx get 'https://www.compass.com/homedetails/162-04-12th-Rd-Queens-NY-11357/2109718971930079225_lid/' -p compass > /tmp/detail.html\n# or, sha-only:\nfpx get 'https://www.compass.com/listing/2109718971930079225/view' -p compass > /tmp/detail.html\n\nnode extract-global.mjs /tmp/detail.html __INITIAL_DATA__ > /tmp/detail.json\n```\n\nExtraction path: `.props.listingRelation.listing`.\n\n```sh\njq '.props.listingRelation.listing |\n  {address: .location.prettyAddress, city: .location.city, state: .location.state, zip: .location.zipCode,\n   price: .price.lastKnown, price_formatted: .price.formatted, per_sqft: .price.perSquareFoot,\n   beds: .size.bedrooms, baths: .size.totalBathrooms, sqft: .size.squareFeet,\n   lot_sqft: .size.lotSizeInSquareFeet, status: .localizedStatus, mls_status: .mlsStatus,\n   photos: [.media[]?.originalUrl], schools: .detailedInfo.schools,\n   agent: .agents[0]}' /tmp/detail.json\n```\n\nOther useful top-level fields: `compassPropertyId`, `pageLink` (`_lid/` URL),\n`navigationPageLink` (stable `_pid/` URL), `description`,\n`detailedInfo.{amenities,taxAnnualAmount,associationFee:{amount,frequency},\narchitecturalStyle,garageSpaces,totalParkingSpaces}`,\n`events[]`/`history[]` (see §3), `mlsAlternateAddresses[]`,\n`agents[].{id,fullName,companyName,profileUrl}`.\n\n**Sentinel values**: `detailedInfo.taxAnnualAmount` under `10` is a\nnot-yet-assessed placeholder on new construction, not a real tax bill.\n\n---\n\n## 3. Price history\n\nSame fetch + extraction as §2 (`__INITIAL_DATA__.props.listingRelation.listing`).\nRead `events[]` (this listing's own MLS events) and `history[]` (events from\nprior listings of the same property) — each entry:\n`{ price, status, localizedStatus, timestamp (unix ms), pageLink, source:{externalSourceName} }`.\n\n```sh\njq '.props.listingRelation.listing | {events, history}' /tmp/detail.json\n# Earliest \"Listed\" event (days-on-market anchor):\njq -r '.props.listingRelation.listing.events // [] |\n  map(select(.status==1 or (.localizedStatus|test(\"^listed$\";\"i\")))) |\n  sort_by(.timestamp) | .[0] // empty' /tmp/detail.json\n```\n\n---\n\n## 4. Address typeahead (resolve an address → listing id) — WAF-immune, try FIRST\n\n`POST /api/v3/omnisuggest/autocomplete`\n\nBody: `{\"q\": \"<street + city + state>\", \"sources\": [0]}` — `sources: [0]`\nrestricts results to the Addresses source.\n\n```sh\ncat > /tmp/omni.json <<'JSON'\n{\"q\": \"158 Raven Blvd Lake Lure NC\", \"sources\": [0]}\nJSON\nfpx post-json 'https://www.compass.com/api/v3/omnisuggest/autocomplete' @/tmp/omni.json -p compass \\\n  | jq '.categories[] | select(.name==1) | .items[] | {id, text, subText, redirectUrl}'\n```\n\nResponse shape: `{ categories: [ { name, label, items: [ { text, subText,\nredirectUrl, source, id, ucGeoId } ] } ], success, rankerVersion }`. Category\n`name === 1` is \"Addresses\". `id` **is** the listing's `listingIdSHA` — it\nmatches the `_lid/` segment on the public homedetails page, so it feeds\nstraight into §2's sha-only fetch (`/listing/<id>/view`).\n\n**Verify the match before trusting it** — the query terms should appear as\nwhole tokens in `text`/`subText` (case + street-suffix folded, e.g.\n\"Ln\"/\"Lane\"); Compass's suggest can return an unrelated top hit when the\nlocal market has no real match. `compass-mcp`'s rung order is: try this\ntypeahead first, and only fall back to the (WAF-walled) free-text/slug SSR\nsearch in §1 if it returns nothing verified.\n\n---\n\n## 5. Agent's listings\n\n`GET /agents/<slug>/` (e.g. `/agents/paige-mcguirk/`)\n\n```sh\nfpx get 'https://www.compass.com/agents/paige-mcguirk/' -p compass > /tmp/agent.html\nnode extract-global.mjs /tmp/agent.html __AGENT_PROFILE__ > /tmp/agent.json\n```\n\nExtraction paths (same `listing` shape as §2's `listingRelation.listing`):\n\n- Active listings: `.data.agentProfileProps.activeListingsProps.initialSales[]`\n- Closed deals: `.data.agentProfileProps.closedDealsProps.initialSales[].listing`\n  (each entry wraps the listing under a `.listing` key, alongside `aiHexID`).\n\n```sh\njq '.data.agentProfileProps.activeListingsProps.initialSales[] |\n  {sha: .listingIdSHA, address: .location.prettyAddress, price: .price.formatted, status: .localizedStatus}' \\\n  /tmp/agent.json\n\njq '.data.agentProfileProps.closedDealsProps.initialSales[].listing |\n  {sha: .listingIdSHA, address: .location.prettyAddress, close_price: .price.lastPropertyClosePrice}' \\\n  /tmp/agent.json\n```\n\nThis page is large (~1.4 MB rendered); the `extract-global.mjs` walk handles\nthat fine, but expect the fetch itself to take a few seconds.\n\n---\n\n## Not covered (no server-reachable endpoint)\n\n- **Saved homes / saved searches** — `/overview/favorites` is fully\n  client-rendered via auth-scoped GraphQL the bridge can't intercept.\n  `compass-mcp` stubs these tools out too; there's no fpx recipe for them.\n- **Market trends / stats** — Compass doesn't expose region-level market\n  data in any SSR blob (unlike some other portals' dedicated endpoint).\n\nFile v1.1.1:skill-card.md\n\n## Description:\n\nQuery compass.com from a shell with the fpx CLI to search listings, get property and agent details, inspect price history, and resolve street addresses through one-shot fetches over a signed-in browser tab.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and real-estate data operators use this skill to query Compass listing, property, agent, price-history, and address data from shell workflows when they want Compass data without running the compass-mcp server.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: The skill routes Compass requests through a user's browser session with fetchproxy.\n\nMitigation: Use a dedicated browser profile or Compass tab for separation, and remove or unpair the fetchproxy profile when it is no longer needed.\n\nRisk: Compass pages can return login or AWS WAF challenge content instead of usable listing data.\n\nMitigation: Check for login redirects or AWS WAF challenge markers before trusting fetched pages, and refresh the Compass tab before retrying.\n\n## Reference(s):\n\n- [Compass requests for fpx](references/requests.md)\n- [extract-global.mjs](references/extract-global.mjs)\n\n## Skill Output:\n\n**Output Type(s):** [shell commands, code, configuration, guidance]\n\n**Output Format:** [Markdown guidance with shell commands, jq filters, and a Node.js helper script]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Requires @fetchproxy/cli, the fetchproxy browser extension, and an open Compass browser tab for live requests.]\n\n## Skill Version(s):\n\n1.1.1 (source: server release evidence)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.1.0: 5 files, 9608 bytes\n\nFiles: references/extract-global.mjs (2851b), references/requests.md (7967b), skill-card.md (2190b), SKILL.md (5308b), _meta.json (130b)\n\nFile v1.1.0:SKILL.md\n\n---\nname: compass-fpx\ndescription: >-\n  Query compass.com (US real-estate portal) from a shell with the fpx CLI\n  (@fetchproxy/cli) instead of running the compass-mcp server — search\n  listings, get property/agent detail, price history, and resolve street\n  addresses through one-shot fetches over a signed-in browser tab. Use when\n  you want Compass data without the MCP, in a script, or on a machine where\n  the MCP isn't installed.\n---\n\n# Compass via fpx (no MCP)\n\nCompass has no public JSON data-API for search/listing/agent data — it's a\nfully server-rendered React app that embeds each page's data as JSON inside\nan inline `<script>` global (`global.uc`, `window.__INITIAL_DATA__`,\n`window.__AGENT_PROFILE__`). compass.com also runs **AWS","readmeExcerpt":"Skill: compass-fpx Owner: chrischall Summary: Query compass.com (US real-estate portal) from a shell with the fpx CLI (@fetchproxy/cli) instead of running the compass-mcp server — search listings, get property/agent detail, price history, and resolve street addresses through one-shot fetches over a signed-in browser tab. Use when you want Compass data without the MCP, in a script, or on a machine where the MCP isn't ","codeSnippets":[],"executableExamples":[{"language":"sh","snippet":"npm install -g @fetchproxy/cli              # provides `fpx`\nfpx profile add compass --domain compass.com\nfpx pair -p compass                          # prints a pair code → approve in the ContextMint Bridge extension"},{"language":"sh","snippet":"fpx get 'https://www.compass.com/homes-for-sale/manhattan-ny/' -p compass > /tmp/page.html\nnode references/extract-global.mjs /tmp/page.html uc | jq '.sharedReactAppProps.initialResults.lolResults.data | length'"},{"language":"sh","snippet":"grep -q 'awswaf.com' /tmp/page.html && grep -q 'challenge.js' /tmp/page.html && \\\n  [ \"$(wc -c < /tmp/page.html)\" -lt 80000 ] && echo \"WAF-blocked — refresh the compass.com tab\""},{"language":"sh","snippet":"fpx get 'https://www.compass.com/<path>' -p compass > /tmp/page.html\nnode extract-global.mjs /tmp/page.html <globalName> | jq '...'"},{"language":"sh","snippet":"fpx get 'https://www.compass.com/homes-for-sale/brooklyn-ny/2-3-bed/500000-1500000-price/type-condo/' -p compass > /tmp/search.html\nnode extract-global.mjs /tmp/search.html uc > /tmp/search.json"},{"language":"sh","snippet":"jq -r '.sharedReactAppProps.initialResults.lolResults |\n  \"total=\\(.totalItems)\",\n  (.data[].listing |\n    \"\\(.listingIdSHA)\\t\\(.subtitles[0] // \"\")\\t\\(.title // \"\")\\t\\(.subStats // [] | map(select(.title==\"beds\")) | .[0].subtitle // \"\")bd\")' \\\n  /tmp/search.json"}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: compass-fpx\ndescription: >-\n  Query compass.com (US real-estate portal) from a shell with the fpx CLI\n  (@fetchproxy/cli) instead of running the compass-mcp server — search\n  listings, get property/agent detail, price history, and resolve street\n  addresses through one-shot fetches over a signed-in browser tab. Use when\n  you want Compass data without the MCP, in a script, or on a machine where\n  the MCP isn't installed.\n---\n\n# Compass via fpx (no MCP)\n\nCompass has no public JSON data-API for search/listing/agent data — it's a\nfully server-rendered React app that embeds each page's data as JSON inside\nan inline `<script>` global (`global.uc`, `window.__INITIAL_DATA__`,\n`window.__AGENT_PROFILE__`). compass.com also runs **AWS WAF**, which 403s\nplain `curl`/Node requests on some paths regardless of headers. `fpx` routes\nevery request through the user's own signed-in browser tab (the ContextMint\nBridge extension), which already carries a cleared WAF session, so the page\nrenders normally — you then pull the JSON out of the HTML yourself.\n\nThis is **Bucket 1 (full-fetchproxy)**: every call, not just an auth\nbootstrap, rides the bridge. Compass validates at the session level per\nrequest, same as the `compass-mcp` server itself (no server-side path\nexists). No Compass login is required beyond having a signed-in tab open —\nsearch/listing/agent data and the address-typeahead endpoint are all public.\n\n## One-time setup\n\n```sh\nnpm install -g @fetchproxy/cli              # provides `fpx`\nfpx profile add compass --domain compass.com\nfpx pair -p compass                          # prints a pair code → approve in the ContextMint Bridge extension\n```\n\nRequirements: the **ContextMint Bridge** browser extension installed\n([releases](https://github.com/nullnet-app/contextmint-bridge/releases); it is the\nfetchproxy extension renamed, from the same maintainer, with public source and a\n`.sha256` beside each release zip), an open\n`www.compass.com` tab, and its Chrome **Site access** allowing `compass.com`.\nPairing persists — after the first approval every later `fpx` call reuses it.\n\n## Core call pattern\n\nTwo request shapes:\n\n1. **SSR pages** (search, homedetails, agent profile) — `fpx get` the path,\n   then pull the inline JSON global out of the HTML.\n2. **The one structured endpoint** (address typeahead) — `fpx post-json`.\n\n```sh\nfpx get 'https://www.compass.com/homes-for-sale/manhattan-ny/' -p compass > /tmp/page.html\nnode references/extract-global.mjs /tmp/page.html uc | jq '.sharedReactAppProps.initialResults.lolResults.data | length'\n```\n\n`references/extract-global.mjs` is a small, dependency-free re-implementation\nof the balanced-brace walk `compass-mcp`'s own `src/page-state.ts` uses\n(`extractGlobalAssign`) — Compass writes each global as a JSON *literal*\n(`global.uc = {...};`), so once the object is sliced out, `JSON.parse`/`jq`\nworks normally. Regexing for it without brace-balancing breaks the moment a\nnested value contains `}`.\n\nFull endpoint list, "},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn700jq4sjtf2anb0rk3ft4p7n856872\",\n  \"slug\": \"compass-fpx\",\n  \"version\": \"1.1.9\",\n  \"publishedAt\": 1791588379319\n}"},{"path":"references/requests.md","content":"# Compass requests for fpx\n\nAll paths are relative to `https://www.compass.com`. GET requests return HTML\nwith an embedded JSON global — pull it out with `extract-global.mjs` (in this\nsame directory) before piping to `jq`. The one structured endpoint (§4) is a\nplain JSON POST.\n\n```sh\nfpx get 'https://www.compass.com/<path>' -p compass > /tmp/page.html\nnode extract-global.mjs /tmp/page.html <globalName> | jq '...'\n```\n\nEndpoints and field shapes below are transcribed from `compass-mcp`'s\n`src/tools/*.ts` and `src/page-state.ts` — the same live-verified paths and\nextraction targets the MCP tools use.\n\n---\n\n## 1. Search listings\n\n`GET /homes-for-sale/<location-slug>/[<filters>/]`\n\n- `<location-slug>`: lowercase, hyphenated free text (`\"Brooklyn, NY\"` →\n  `brooklyn-ny`, `\"94110\"` → `94110`).\n- Filters are path segments, in this order: `<bedsMin>-<bedsMax>-bed/`,\n  `<priceMin>-<priceMax>-price/`, a home-type slug (`type-house`,\n  `type-condo`, `type-townhouse`, `type-multi-family`, `type-land`,\n  `type-rental`). Omit any filter you don't need.\n- **Pagination ceiling**: Compass server-renders only ~41 listings into this\n  page (`num`); `/page-N/` canonicalizes back to page 1 with identical data.\n  To see more of the market, narrow the price/bed bands and re-search each\n  band — there is no further page to fetch.\n\n```sh\nfpx get 'https://www.compass.com/homes-for-sale/brooklyn-ny/2-3-bed/500000-1500000-price/type-condo/' -p compass > /tmp/search.html\nnode extract-global.mjs /tmp/search.html uc > /tmp/search.json\n```\n\nExtraction path: `.sharedReactAppProps.initialResults.lolResults`\n(`{ totalItems, data: [ { listing: {...} } ] }`).\n\n```sh\njq -r '.sharedReactAppProps.initialResults.lolResults |\n  \"total=\\(.totalItems)\",\n  (.data[].listing |\n    \"\\(.listingIdSHA)\\t\\(.subtitles[0] // \"\")\\t\\(.title // \"\")\\t\\(.subStats // [] | map(select(.title==\"beds\")) | .[0].subtitle // \"\")bd\")' \\\n  /tmp/search.json\n```\n\nPer-listing fields worth pulling: `listingIdSHA`, `pageLink` (the `_lid/`\ndetail URL, relative), `navigationPageLink` (the stable `_pid/` URL when\npresent), `title` (formatted price string), `subtitles` (`[street,\nneighborhood]`), `status`, `location.{latitude,longitude}`,\n`media[].{originalUrl,thumbnailUrl,category}` (`category:0` = photo),\n`subStats[]` (`{title:\"beds\"|\"baths\"|\"sqft\", subtitle:\"<n>\"}`),\n`clusterSummary.{bedrooms,bathrooms,priceRange:[min,max],formattedLotSize,savedListing}`.\n\n**Comparable rentals** reuse this exact path shape with the rental filter:\n`/homes-for-sale/<locality-slug>/type-rental/` — same extraction.\n\n**Free-text SSR search** (`/homes-for-sale/?q=<query>`) exists but is\nWAF-walled in production (reliably 403s) — don't rely on it; use §4 for\naddress resolution instead.\n\n---\n\n## 2. Property / listing detail\n\n`GET /homedetails/<slug>/<listingIdSHA>_lid/`\n\nIf you only have the `listingIdSHA` (no slug), fetch\n`GET /listing/<listingIdSHA>/view` instead — it 302-redirects to the\ncanonical slugged homedetails URL, and fpx follows the"},{"path":"skill-card.md","content":"## Description:\n\nHelps agents query Compass real-estate listings, property details, price histories, and agent profiles through the fpx CLI and a paired browser tab.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[chrischall](https://clawhub.ai/user/chrischall)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and agents use this skill to search US real-estate listings, inspect property and agent details, check price history, and resolve addresses from Compass without running an MCP server.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Installing a global CLI and browser extension adds software with access to browser-mediated requests.\n\nMitigation: Install only if comfortable with both components; use a dedicated browser profile or Compass session and limit extension site access to compass.com.\n\nRisk: The browser bridge pairing persists and can expose browsing-session data to later requests.\n\nMitigation: Remove the pairing when no longer needed and keep the browser session limited to the intended Compass use.\n\n## Reference(s):\n\n- [Compass fpx request recipes](references/requests.md)\n- [Compass fpx release](https://clawhub.ai/chrischall/skills/compass-fpx)\n- [ContextMint Bridge releases](https://github.com/nullnet-app/contextmint-bridge/releases)\n\n## Skill Output:\n\n**Output Type(s):** [Text, JSON, Shell commands, Guidance]\n\n**Output Format:** [Text or JSON with shell command examples]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Listing results depend on the available Compass pages and the paired browser session.]\n\n## Skill Version(s):\n\n1.1.9 (source: ClawHub release metadata)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment."}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":"Query compass.com (US real-estate portal) from a shell with the fpx CLI (@fetchproxy/cli) instead of running the compass-mcp server — search listings, get property/agent detail, price history, and resolve street addresses through one-shot fetches over a signed-in browser tab. Use when you want Compass data without the MCP, in a script, or on a machine where the MCP isn't installed. Skill: compass-fpx Owner: chrischall Summary: Query compass.com (US real-estate portal) from a shell with the fpx CLI (@fetchproxy/cli) instead of running the compass-mcp server — search listings, get property/agent detail, price history, and resolve street addresses through one-shot fetches over a signed-in browser tab. Use when you want Compass data without the MCP, in a script, or on a machine where the MCP isn't","editorialQuality":{"score":100,"threshold":65,"status":"ready","wordCount":1546,"uniquenessScore":45,"reasons":[]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-10T13:39:32.277Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-10-10T13:39:32.277Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-10T15:53:06.506Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}