zillow-mcp 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/dist/bundle.js +94 -79
- package/package.json +1 -1
- package/server.json +2 -2
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
},
|
|
8
8
|
"metadata": {
|
|
9
9
|
"description": "MCP server for Zillow — search listings, fetch property details, Zestimate history, saved searches & homes, market reports. Routes through the user's signed-in zillow.com tab via the fetchproxy browser extension to dodge bot detection.",
|
|
10
|
-
"version": "0.2.
|
|
10
|
+
"version": "0.2.2"
|
|
11
11
|
},
|
|
12
12
|
"plugins": [
|
|
13
13
|
{
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"displayName": "Zillow",
|
|
16
16
|
"source": "./",
|
|
17
17
|
"description": "MCP server for Zillow — search listings, get property details, Zestimate history, saved searches & homes, market reports",
|
|
18
|
-
"version": "0.2.
|
|
18
|
+
"version": "0.2.2",
|
|
19
19
|
"author": {
|
|
20
20
|
"name": "Chris Hall"
|
|
21
21
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zillow-mcp",
|
|
3
3
|
"displayName": "Zillow",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.2",
|
|
5
5
|
"description": "Zillow real-estate access for Claude — search listings, get property details, Zestimate history, your saved searches & homes, market reports",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Chris Hall",
|
package/dist/bundle.js
CHANGED
|
@@ -36668,6 +36668,40 @@ function textResult(data) {
|
|
|
36668
36668
|
};
|
|
36669
36669
|
}
|
|
36670
36670
|
|
|
36671
|
+
// src/next-data.ts
|
|
36672
|
+
var ParseError = class extends Error {
|
|
36673
|
+
constructor(message) {
|
|
36674
|
+
super(message);
|
|
36675
|
+
this.name = "ParseError";
|
|
36676
|
+
}
|
|
36677
|
+
};
|
|
36678
|
+
var OPEN_TAG_RE = /<script[^>]*id=["']__NEXT_DATA__["'][^>]*>/i;
|
|
36679
|
+
var CLOSE_TAG = "</script>";
|
|
36680
|
+
function extractNextData(html) {
|
|
36681
|
+
const openMatch = OPEN_TAG_RE.exec(html);
|
|
36682
|
+
if (!openMatch) {
|
|
36683
|
+
throw new ParseError("__NEXT_DATA__ script tag not found in HTML");
|
|
36684
|
+
}
|
|
36685
|
+
const start = openMatch.index + openMatch[0].length;
|
|
36686
|
+
const end = html.indexOf(CLOSE_TAG, start);
|
|
36687
|
+
if (end < 0) {
|
|
36688
|
+
throw new ParseError("__NEXT_DATA__ script tag has no closing </script>");
|
|
36689
|
+
}
|
|
36690
|
+
const json2 = html.slice(start, end).trim();
|
|
36691
|
+
try {
|
|
36692
|
+
return JSON.parse(json2);
|
|
36693
|
+
} catch (err) {
|
|
36694
|
+
throw new ParseError(
|
|
36695
|
+
`Failed to parse __NEXT_DATA__ JSON: ${err.message}`
|
|
36696
|
+
);
|
|
36697
|
+
}
|
|
36698
|
+
}
|
|
36699
|
+
function getPageProps(nextData) {
|
|
36700
|
+
const props = nextData.props;
|
|
36701
|
+
const pageProps = props?.pageProps;
|
|
36702
|
+
return pageProps ?? {};
|
|
36703
|
+
}
|
|
36704
|
+
|
|
36671
36705
|
// src/tools/search.ts
|
|
36672
36706
|
var HOME_TYPE_FILTERS = {
|
|
36673
36707
|
house: "isSingleFamily",
|
|
@@ -36703,7 +36737,7 @@ function formatListing(raw) {
|
|
|
36703
36737
|
url: url2
|
|
36704
36738
|
};
|
|
36705
36739
|
}
|
|
36706
|
-
function
|
|
36740
|
+
function buildSearchQueryState(input) {
|
|
36707
36741
|
const filterState = {};
|
|
36708
36742
|
switch (input.status ?? "for_sale") {
|
|
36709
36743
|
case "for_rent":
|
|
@@ -36745,17 +36779,18 @@ function buildSearchBody(input) {
|
|
|
36745
36779
|
}
|
|
36746
36780
|
}
|
|
36747
36781
|
return {
|
|
36748
|
-
|
|
36749
|
-
|
|
36750
|
-
|
|
36751
|
-
|
|
36752
|
-
isListVisible: true
|
|
36753
|
-
},
|
|
36754
|
-
wants: { cat1: ["listResults"] },
|
|
36755
|
-
requestId: 1,
|
|
36756
|
-
isDebugRequest: false
|
|
36782
|
+
usersSearchTerm: input.location,
|
|
36783
|
+
filterState,
|
|
36784
|
+
isListVisible: true,
|
|
36785
|
+
isMapVisible: false
|
|
36757
36786
|
};
|
|
36758
36787
|
}
|
|
36788
|
+
function buildSearchPath(input) {
|
|
36789
|
+
const sqs = buildSearchQueryState(input);
|
|
36790
|
+
const slug = encodeURIComponent(input.location.trim());
|
|
36791
|
+
const qs = encodeURIComponent(JSON.stringify(sqs));
|
|
36792
|
+
return `/homes/${slug}_rb/?searchQueryState=${qs}`;
|
|
36793
|
+
}
|
|
36759
36794
|
function registerSearchTools(server2, client2) {
|
|
36760
36795
|
server2.registerTool(
|
|
36761
36796
|
"zillow_search_properties",
|
|
@@ -36792,9 +36827,12 @@ function registerSearchTools(server2, client2) {
|
|
|
36792
36827
|
}
|
|
36793
36828
|
},
|
|
36794
36829
|
async (input) => {
|
|
36795
|
-
const
|
|
36796
|
-
const
|
|
36797
|
-
const
|
|
36830
|
+
const path = buildSearchPath(input);
|
|
36831
|
+
const html = await client2.fetchHtml(path);
|
|
36832
|
+
const nextData = extractNextData(html);
|
|
36833
|
+
const pageProps = getPageProps(nextData);
|
|
36834
|
+
const sps = pageProps.searchPageState;
|
|
36835
|
+
const raw = sps?.cat1?.searchResults?.listResults ?? [];
|
|
36798
36836
|
const limit = input.limit ?? 40;
|
|
36799
36837
|
const formatted = raw.map(formatListing).filter((x) => x !== null).slice(0, limit);
|
|
36800
36838
|
return textResult(formatted);
|
|
@@ -36802,40 +36840,6 @@ function registerSearchTools(server2, client2) {
|
|
|
36802
36840
|
);
|
|
36803
36841
|
}
|
|
36804
36842
|
|
|
36805
|
-
// src/next-data.ts
|
|
36806
|
-
var ParseError = class extends Error {
|
|
36807
|
-
constructor(message) {
|
|
36808
|
-
super(message);
|
|
36809
|
-
this.name = "ParseError";
|
|
36810
|
-
}
|
|
36811
|
-
};
|
|
36812
|
-
var OPEN_TAG_RE = /<script[^>]*id=["']__NEXT_DATA__["'][^>]*>/i;
|
|
36813
|
-
var CLOSE_TAG = "</script>";
|
|
36814
|
-
function extractNextData(html) {
|
|
36815
|
-
const openMatch = OPEN_TAG_RE.exec(html);
|
|
36816
|
-
if (!openMatch) {
|
|
36817
|
-
throw new ParseError("__NEXT_DATA__ script tag not found in HTML");
|
|
36818
|
-
}
|
|
36819
|
-
const start = openMatch.index + openMatch[0].length;
|
|
36820
|
-
const end = html.indexOf(CLOSE_TAG, start);
|
|
36821
|
-
if (end < 0) {
|
|
36822
|
-
throw new ParseError("__NEXT_DATA__ script tag has no closing </script>");
|
|
36823
|
-
}
|
|
36824
|
-
const json2 = html.slice(start, end).trim();
|
|
36825
|
-
try {
|
|
36826
|
-
return JSON.parse(json2);
|
|
36827
|
-
} catch (err) {
|
|
36828
|
-
throw new ParseError(
|
|
36829
|
-
`Failed to parse __NEXT_DATA__ JSON: ${err.message}`
|
|
36830
|
-
);
|
|
36831
|
-
}
|
|
36832
|
-
}
|
|
36833
|
-
function getPageProps(nextData) {
|
|
36834
|
-
const props = nextData.props;
|
|
36835
|
-
const pageProps = props?.pageProps;
|
|
36836
|
-
return pageProps ?? {};
|
|
36837
|
-
}
|
|
36838
|
-
|
|
36839
36843
|
// src/url.ts
|
|
36840
36844
|
function urlToPath(input) {
|
|
36841
36845
|
try {
|
|
@@ -37030,6 +37034,17 @@ function findSavedSearches(pageProps) {
|
|
|
37030
37034
|
);
|
|
37031
37035
|
}
|
|
37032
37036
|
function findSavedHomes(pageProps) {
|
|
37037
|
+
const collections = pageProps.collectionsResponse;
|
|
37038
|
+
if (Array.isArray(collections)) {
|
|
37039
|
+
const flat = [];
|
|
37040
|
+
for (const c of collections) {
|
|
37041
|
+
if (Array.isArray(c.homes)) flat.push(...c.homes);
|
|
37042
|
+
else if (Array.isArray(c.properties)) flat.push(...c.properties);
|
|
37043
|
+
else if (Array.isArray(c.items)) flat.push(...c.items);
|
|
37044
|
+
}
|
|
37045
|
+
if (flat.length > 0) return flat;
|
|
37046
|
+
if (collections.length > 0) return [];
|
|
37047
|
+
}
|
|
37033
37048
|
return findArrayByShape(
|
|
37034
37049
|
pageProps,
|
|
37035
37050
|
SAVED_HOME_KEYS,
|
|
@@ -37080,7 +37095,7 @@ function registerSavedTools(server2, client2) {
|
|
|
37080
37095
|
inputSchema: {}
|
|
37081
37096
|
},
|
|
37082
37097
|
async () => {
|
|
37083
|
-
const html = await client2.fetchHtml("/
|
|
37098
|
+
const html = await client2.fetchHtml("/myzillow/SavedSearches");
|
|
37084
37099
|
const nextData = extractNextData(html);
|
|
37085
37100
|
const pageProps = getPageProps(nextData);
|
|
37086
37101
|
const searches = findSavedSearches(pageProps);
|
|
@@ -37091,7 +37106,7 @@ function registerSavedTools(server2, client2) {
|
|
|
37091
37106
|
"zillow_get_saved_homes",
|
|
37092
37107
|
{
|
|
37093
37108
|
title: "Get my saved (favorited) Zillow homes",
|
|
37094
|
-
description: "The signed-in user's saved (favorited) homes on zillow.com. Returns address, price, Zestimate, status, and when each home was saved. Requires the user to be signed in. Read-only; safe to call repeatedly.",
|
|
37109
|
+
description: "The signed-in user's saved (favorited) homes on zillow.com, flattened across all of the user's collections. Returns address, price, Zestimate, status, and when each home was saved. Requires the user to be signed in. Read-only; safe to call repeatedly.",
|
|
37095
37110
|
annotations: {
|
|
37096
37111
|
title: "Get my saved (favorited) Zillow homes",
|
|
37097
37112
|
readOnlyHint: true,
|
|
@@ -37101,7 +37116,7 @@ function registerSavedTools(server2, client2) {
|
|
|
37101
37116
|
inputSchema: {}
|
|
37102
37117
|
},
|
|
37103
37118
|
async () => {
|
|
37104
|
-
const html = await client2.fetchHtml("/myzillow/favorites
|
|
37119
|
+
const html = await client2.fetchHtml("/myzillow/favorites");
|
|
37105
37120
|
const nextData = extractNextData(html);
|
|
37106
37121
|
const pageProps = getPageProps(nextData);
|
|
37107
37122
|
const homes = findSavedHomes(pageProps);
|
|
@@ -37111,35 +37126,34 @@ function registerSavedTools(server2, client2) {
|
|
|
37111
37126
|
}
|
|
37112
37127
|
|
|
37113
37128
|
// src/tools/market.ts
|
|
37114
|
-
function
|
|
37115
|
-
const candidates = [
|
|
37116
|
-
pageProps.marketInfo,
|
|
37117
|
-
pageProps.marketReport,
|
|
37118
|
-
pageProps.regionInfo,
|
|
37119
|
-
pageProps.componentProps?.marketInfo
|
|
37120
|
-
];
|
|
37129
|
+
function pickRegion(pageProps) {
|
|
37130
|
+
const candidates = [pageProps.zhviRegion, pageProps.requestedRegion];
|
|
37121
37131
|
for (const c of candidates) {
|
|
37122
37132
|
if (c && typeof c === "object") return c;
|
|
37123
37133
|
}
|
|
37124
37134
|
return null;
|
|
37125
37135
|
}
|
|
37126
|
-
function
|
|
37136
|
+
function pickAnalytics(pageProps) {
|
|
37137
|
+
const a = pageProps.odpMarketAnalytics;
|
|
37138
|
+
if (a && typeof a === "object") return a;
|
|
37139
|
+
return null;
|
|
37140
|
+
}
|
|
37141
|
+
function format2(region, analytics) {
|
|
37142
|
+
const yoy = typeof analytics?.zhviLatest?.zhviYoY === "number" ? Math.round(analytics.zhviLatest.zhviYoY * 1e3) / 10 : void 0;
|
|
37127
37143
|
return {
|
|
37128
|
-
|
|
37129
|
-
|
|
37130
|
-
|
|
37131
|
-
|
|
37132
|
-
|
|
37133
|
-
|
|
37134
|
-
median_days_on_market:
|
|
37135
|
-
|
|
37136
|
-
new_listings:
|
|
37137
|
-
|
|
37138
|
-
zhvi:
|
|
37139
|
-
zhvi_yoy_percent:
|
|
37140
|
-
|
|
37141
|
-
buyer_seller_index: raw.buyerSellerIndex,
|
|
37142
|
-
as_of_date: raw.asOfDate
|
|
37144
|
+
region_name: region?.name,
|
|
37145
|
+
region_type: region?.regionTypeName,
|
|
37146
|
+
parent_county: region?.parentCounty?.name,
|
|
37147
|
+
parent_state: region?.parentState?.name,
|
|
37148
|
+
median_sale_price: analytics?.mrktSaleLatest?.medianSalePrice,
|
|
37149
|
+
median_list_price: analytics?.mrktListingLatest?.medianListPrice,
|
|
37150
|
+
median_days_on_market: analytics?.mrktListingLatest?.medianDaysOnMarket,
|
|
37151
|
+
days_to_pending: analytics?.mrktSaleLatest?.daysToPending,
|
|
37152
|
+
new_listings: analytics?.mrktListingLatest?.newListings,
|
|
37153
|
+
for_sale_inventory: analytics?.mrktListingLatest?.forSaleInventory,
|
|
37154
|
+
zhvi: analytics?.zhviLatest?.zhvi,
|
|
37155
|
+
zhvi_yoy_percent: yoy,
|
|
37156
|
+
as_of_date: analytics?.zhviLatest?.asOfDate
|
|
37143
37157
|
};
|
|
37144
37158
|
}
|
|
37145
37159
|
function pathFromInput(args) {
|
|
@@ -37157,7 +37171,7 @@ function registerMarketTools(server2, client2) {
|
|
|
37157
37171
|
"zillow_get_market_report",
|
|
37158
37172
|
{
|
|
37159
37173
|
title: "Get Zillow market report for a region",
|
|
37160
|
-
description: 'Market report for a Zillow region: median sale/list
|
|
37174
|
+
description: 'Market report for a Zillow region: median sale/list prices, days on market, for-sale inventory, new listings, Zillow Home Value Index (ZHVI), and year-over-year ZHVI change. Provide either a `region_path` (e.g. "/home-values/6181/brooklyn-ny/") or a full Zillow home-values URL. Read-only; safe to call repeatedly.',
|
|
37161
37175
|
annotations: {
|
|
37162
37176
|
title: "Get Zillow market report for a region",
|
|
37163
37177
|
readOnlyHint: true,
|
|
@@ -37176,13 +37190,14 @@ function registerMarketTools(server2, client2) {
|
|
|
37176
37190
|
const html = await client2.fetchHtml(path);
|
|
37177
37191
|
const nextData = extractNextData(html);
|
|
37178
37192
|
const pageProps = getPageProps(nextData);
|
|
37179
|
-
const
|
|
37180
|
-
|
|
37193
|
+
const region = pickRegion(pageProps);
|
|
37194
|
+
const analytics = pickAnalytics(pageProps);
|
|
37195
|
+
if (!region && !analytics) {
|
|
37181
37196
|
throw new Error(
|
|
37182
|
-
`Could not locate
|
|
37197
|
+
`Could not locate market data (zhviRegion + odpMarketAnalytics) in __NEXT_DATA__ at ${path}.`
|
|
37183
37198
|
);
|
|
37184
37199
|
}
|
|
37185
|
-
return textResult(format2(
|
|
37200
|
+
return textResult(format2(region, analytics));
|
|
37186
37201
|
}
|
|
37187
37202
|
);
|
|
37188
37203
|
}
|
|
@@ -37266,7 +37281,7 @@ function registerMortgageTools(server2) {
|
|
|
37266
37281
|
}
|
|
37267
37282
|
|
|
37268
37283
|
// src/index.ts
|
|
37269
|
-
var VERSION = "0.2.
|
|
37284
|
+
var VERSION = "0.2.2";
|
|
37270
37285
|
var port = process.env.ZILLOW_WS_PORT ? Number(process.env.ZILLOW_WS_PORT) : void 0;
|
|
37271
37286
|
var transport = new FetchproxyTransport({ port, version: VERSION });
|
|
37272
37287
|
var client = new ZillowClient({ transport });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zillow-mcp",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"mcpName": "io.github.chrischall/zillow-mcp",
|
|
5
5
|
"description": "Zillow MCP server for Claude — developed and maintained by AI (Claude Code)",
|
|
6
6
|
"author": "Claude Code (AI) <https://www.anthropic.com/claude>",
|
package/server.json
CHANGED
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
"url": "https://github.com/chrischall/zillow-mcp",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "0.2.
|
|
9
|
+
"version": "0.2.2",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "zillow-mcp",
|
|
14
|
-
"version": "0.2.
|
|
14
|
+
"version": "0.2.2",
|
|
15
15
|
"transport": {
|
|
16
16
|
"type": "stdio"
|
|
17
17
|
},
|