iflow-mcp_sap156-zillow-mcp-server 1.0.0__tar.gz → 1.0.2__tar.gz
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.
- {iflow_mcp_sap156_zillow_mcp_server-1.0.0 → iflow_mcp_sap156_zillow_mcp_server-1.0.2}/PKG-INFO +1 -1
- {iflow_mcp_sap156_zillow_mcp_server-1.0.0 → iflow_mcp_sap156_zillow_mcp_server-1.0.2}/iflow_mcp_sap156_zillow_mcp_server.egg-info/PKG-INFO +1 -1
- {iflow_mcp_sap156_zillow_mcp_server-1.0.0 → iflow_mcp_sap156_zillow_mcp_server-1.0.2}/iflow_mcp_sap156_zillow_mcp_server.egg-info/SOURCES.txt +1 -0
- iflow_mcp_sap156_zillow_mcp_server-1.0.2/iflow_mcp_sap156_zillow_mcp_server.egg-info/top_level.txt +1 -0
- {iflow_mcp_sap156_zillow_mcp_server-1.0.0 → iflow_mcp_sap156_zillow_mcp_server-1.0.2}/pyproject.toml +4 -5
- iflow_mcp_sap156_zillow_mcp_server-1.0.2/zillow_mcp_server.py +844 -0
- iflow_mcp_sap156_zillow_mcp_server-1.0.0/iflow_mcp_sap156_zillow_mcp_server.egg-info/top_level.txt +0 -1
- {iflow_mcp_sap156_zillow_mcp_server-1.0.0 → iflow_mcp_sap156_zillow_mcp_server-1.0.2}/README.md +0 -0
- {iflow_mcp_sap156_zillow_mcp_server-1.0.0 → iflow_mcp_sap156_zillow_mcp_server-1.0.2}/iflow_mcp_sap156_zillow_mcp_server.egg-info/dependency_links.txt +0 -0
- {iflow_mcp_sap156_zillow_mcp_server-1.0.0 → iflow_mcp_sap156_zillow_mcp_server-1.0.2}/iflow_mcp_sap156_zillow_mcp_server.egg-info/entry_points.txt +0 -0
- {iflow_mcp_sap156_zillow_mcp_server-1.0.0 → iflow_mcp_sap156_zillow_mcp_server-1.0.2}/iflow_mcp_sap156_zillow_mcp_server.egg-info/requires.txt +0 -0
- {iflow_mcp_sap156_zillow_mcp_server-1.0.0 → iflow_mcp_sap156_zillow_mcp_server-1.0.2}/setup.cfg +0 -0
iflow_mcp_sap156_zillow_mcp_server-1.0.2/iflow_mcp_sap156_zillow_mcp_server.egg-info/top_level.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
zillow_mcp_server
|
{iflow_mcp_sap156_zillow_mcp_server-1.0.0 → iflow_mcp_sap156_zillow_mcp_server-1.0.2}/pyproject.toml
RENAMED
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "iflow-mcp_sap156-zillow-mcp-server"
|
|
7
|
-
version = "1.0.
|
|
7
|
+
version = "1.0.2"
|
|
8
8
|
description = "Zillow MCP Server for real estate data access via the Model Context Protocol"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {text = "MIT"}
|
|
@@ -48,9 +48,8 @@ Homepage = "https://github.com/sap156/zillow-mcp-server"
|
|
|
48
48
|
Repository = "https://github.com/sap156/zillow-mcp-server"
|
|
49
49
|
Issues = "https://github.com/sap156/zillow-mcp-server/issues"
|
|
50
50
|
|
|
51
|
-
[tool.setuptools
|
|
52
|
-
|
|
53
|
-
include = ["*"]
|
|
51
|
+
[tool.setuptools]
|
|
52
|
+
py-modules = ["zillow_mcp_server"]
|
|
54
53
|
|
|
55
54
|
[tool.setuptools.package-data]
|
|
56
|
-
"*" = ["README.md", "LICENSE"]
|
|
55
|
+
"*" = ["README.md", "LICENSE"]
|
|
@@ -0,0 +1,844 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Zillow MCP Server
|
|
4
|
+
|
|
5
|
+
This server provides access to Zillow real estate data through the Model Context Protocol (MCP).
|
|
6
|
+
It connects to Zillow's Bridge API to retrieve property information, market trends, and more.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
import json
|
|
11
|
+
import logging
|
|
12
|
+
import time
|
|
13
|
+
import backoff
|
|
14
|
+
from typing import Dict, List, Optional, Any, Union
|
|
15
|
+
from datetime import datetime
|
|
16
|
+
import requests
|
|
17
|
+
from requests.adapters import HTTPAdapter
|
|
18
|
+
from urllib3.util.retry import Retry
|
|
19
|
+
from fastmcp import FastMCP, Context
|
|
20
|
+
from dotenv import load_dotenv
|
|
21
|
+
|
|
22
|
+
# Configure logging
|
|
23
|
+
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
|
24
|
+
logger = logging.getLogger("zillow-mcp")
|
|
25
|
+
|
|
26
|
+
# Load environment variables
|
|
27
|
+
load_dotenv()
|
|
28
|
+
|
|
29
|
+
# Initialize the MCP server
|
|
30
|
+
mcp = FastMCP("Zillow-Data-Server")
|
|
31
|
+
|
|
32
|
+
# Get API key from environment variables
|
|
33
|
+
ZILLOW_API_KEY = os.getenv("ZILLOW_API_KEY")
|
|
34
|
+
ZILLOW_API_BASE_URL = "https://api.bridgeinteractive.com/v1"
|
|
35
|
+
|
|
36
|
+
# Set up a session with retries and timeouts
|
|
37
|
+
session = requests.Session()
|
|
38
|
+
retry_strategy = Retry(
|
|
39
|
+
total=3,
|
|
40
|
+
backoff_factor=0.5,
|
|
41
|
+
status_forcelist=[429, 500, 502, 503, 504],
|
|
42
|
+
allowed_methods=["GET", "POST"]
|
|
43
|
+
)
|
|
44
|
+
adapter = HTTPAdapter(max_retries=retry_strategy)
|
|
45
|
+
session.mount("http://", adapter)
|
|
46
|
+
session.mount("https://", adapter)
|
|
47
|
+
|
|
48
|
+
# Helper function to make API requests to Zillow Bridge API
|
|
49
|
+
@backoff.on_exception(backoff.expo, (requests.exceptions.RequestException, ValueError), max_tries=5)
|
|
50
|
+
def zillow_api_request(endpoint: str, params: Dict = None, method: str = "GET") -> Dict:
|
|
51
|
+
"""Make a request to the Zillow Bridge API with retries and error handling."""
|
|
52
|
+
if not ZILLOW_API_KEY:
|
|
53
|
+
raise ValueError("Zillow API key not found. Please set the ZILLOW_API_KEY environment variable.")
|
|
54
|
+
|
|
55
|
+
headers = {
|
|
56
|
+
"Authorization": f"Bearer {ZILLOW_API_KEY}",
|
|
57
|
+
"Content-Type": "application/json",
|
|
58
|
+
"Accept": "application/json",
|
|
59
|
+
"User-Agent": "Zillow-MCP-Server/1.0"
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
url = f"{ZILLOW_API_BASE_URL}/{endpoint}"
|
|
63
|
+
|
|
64
|
+
try:
|
|
65
|
+
logger.info(f"Making {method} request to {endpoint}")
|
|
66
|
+
|
|
67
|
+
if method.upper() == "GET":
|
|
68
|
+
response = session.get(url, headers=headers, params=params, timeout=30)
|
|
69
|
+
elif method.upper() == "POST":
|
|
70
|
+
response = session.post(url, headers=headers, json=params, timeout=30)
|
|
71
|
+
else:
|
|
72
|
+
raise ValueError(f"Unsupported HTTP method: {method}")
|
|
73
|
+
|
|
74
|
+
# Log response status
|
|
75
|
+
logger.info(f"Response status code: {response.status_code}")
|
|
76
|
+
|
|
77
|
+
# Handle rate limiting
|
|
78
|
+
if response.status_code == 429:
|
|
79
|
+
retry_after = int(response.headers.get('Retry-After', 60))
|
|
80
|
+
logger.warning(f"Rate limited. Retrying after {retry_after} seconds.")
|
|
81
|
+
time.sleep(retry_after)
|
|
82
|
+
return zillow_api_request(endpoint, params, method)
|
|
83
|
+
|
|
84
|
+
response.raise_for_status()
|
|
85
|
+
|
|
86
|
+
# Parse response
|
|
87
|
+
response_data = response.json()
|
|
88
|
+
|
|
89
|
+
# Basic validation
|
|
90
|
+
if not response_data:
|
|
91
|
+
raise ValueError("Empty response from Zillow API")
|
|
92
|
+
|
|
93
|
+
# Log success
|
|
94
|
+
logger.info(f"Successfully received data from {endpoint}")
|
|
95
|
+
|
|
96
|
+
return response_data
|
|
97
|
+
except requests.exceptions.Timeout:
|
|
98
|
+
logger.error(f"Request to {endpoint} timed out")
|
|
99
|
+
raise ValueError("Zillow API request timed out. Please try again later.")
|
|
100
|
+
except requests.exceptions.HTTPError as e:
|
|
101
|
+
logger.error(f"HTTP error: {e}")
|
|
102
|
+
error_msg = f"Zillow API HTTP error: {e}"
|
|
103
|
+
# Try to get more error details from response
|
|
104
|
+
try:
|
|
105
|
+
error_data = response.json()
|
|
106
|
+
if 'error' in error_data:
|
|
107
|
+
error_msg += f" - {error_data['error']}"
|
|
108
|
+
except:
|
|
109
|
+
pass
|
|
110
|
+
raise ValueError(error_msg)
|
|
111
|
+
except requests.exceptions.RequestException as e:
|
|
112
|
+
logger.error(f"API request failed: {e}")
|
|
113
|
+
raise ValueError(f"Zillow API request failed: {str(e)}")
|
|
114
|
+
|
|
115
|
+
# Define MCP tools for Zillow data access
|
|
116
|
+
|
|
117
|
+
@mcp.tool()
|
|
118
|
+
def search_properties(
|
|
119
|
+
location: str,
|
|
120
|
+
type: str = "forSale",
|
|
121
|
+
min_price: Optional[int] = None,
|
|
122
|
+
max_price: Optional[int] = None,
|
|
123
|
+
beds_min: Optional[int] = None,
|
|
124
|
+
beds_max: Optional[int] = None,
|
|
125
|
+
baths_min: Optional[float] = None,
|
|
126
|
+
baths_max: Optional[float] = None,
|
|
127
|
+
home_types: Optional[List[str]] = None,
|
|
128
|
+
ctx: Context = None
|
|
129
|
+
) -> Dict:
|
|
130
|
+
"""
|
|
131
|
+
Search for properties on Zillow based on criteria.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
location: Address, city, ZIP code, or neighborhood
|
|
135
|
+
type: Property listing type - "forSale", "forRent", or "sold"
|
|
136
|
+
min_price: Minimum price in dollars
|
|
137
|
+
max_price: Maximum price in dollars
|
|
138
|
+
beds_min: Minimum number of bedrooms
|
|
139
|
+
beds_max: Maximum number of bedrooms
|
|
140
|
+
baths_min: Minimum number of bathrooms
|
|
141
|
+
baths_max: Maximum number of bathrooms
|
|
142
|
+
home_types: List of home types (e.g., ["house", "condo", "apartment"])
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
Dictionary with property listings matching the criteria
|
|
146
|
+
"""
|
|
147
|
+
if ctx:
|
|
148
|
+
ctx.info(f"Searching for {type} properties in {location}")
|
|
149
|
+
|
|
150
|
+
params = {
|
|
151
|
+
"location": location,
|
|
152
|
+
"type": type,
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
# Add optional parameters if provided
|
|
156
|
+
if min_price is not None:
|
|
157
|
+
params["price_min"] = min_price
|
|
158
|
+
if max_price is not None:
|
|
159
|
+
params["price_max"] = max_price
|
|
160
|
+
if beds_min is not None:
|
|
161
|
+
params["beds_min"] = beds_min
|
|
162
|
+
if beds_max is not None:
|
|
163
|
+
params["beds_max"] = beds_max
|
|
164
|
+
if baths_min is not None:
|
|
165
|
+
params["baths_min"] = baths_min
|
|
166
|
+
if baths_max is not None:
|
|
167
|
+
params["baths_max"] = baths_max
|
|
168
|
+
if home_types is not None:
|
|
169
|
+
params["home_types"] = home_types
|
|
170
|
+
|
|
171
|
+
try:
|
|
172
|
+
# Make the actual API call to Zillow Bridge API
|
|
173
|
+
response = zillow_api_request("properties/search", params)
|
|
174
|
+
|
|
175
|
+
# Process the response from the API
|
|
176
|
+
if not response or 'properties' not in response:
|
|
177
|
+
raise ValueError("No properties found or invalid API response")
|
|
178
|
+
|
|
179
|
+
properties = response.get('properties', [])
|
|
180
|
+
|
|
181
|
+
# Apply any additional filtering if needed
|
|
182
|
+
if min_price is not None:
|
|
183
|
+
properties = [p for p in properties if p.get('price', 0) >= min_price]
|
|
184
|
+
if max_price is not None:
|
|
185
|
+
properties = [p for p in properties if p.get('price', 0) <= max_price]
|
|
186
|
+
if beds_min is not None:
|
|
187
|
+
properties = [p for p in properties if p.get('bedrooms', 0) >= beds_min]
|
|
188
|
+
if beds_max is not None:
|
|
189
|
+
properties = [p for p in properties if p.get('bedrooms', 0) <= beds_max]
|
|
190
|
+
if baths_min is not None:
|
|
191
|
+
properties = [p for p in properties if p.get('bathrooms', 0) >= baths_min]
|
|
192
|
+
if baths_max is not None:
|
|
193
|
+
properties = [p for p in properties if p.get('bathrooms', 0) <= baths_max]
|
|
194
|
+
if home_types is not None:
|
|
195
|
+
properties = [p for p in properties if p.get('home_type', '').lower() in [t.lower() for t in home_types]]
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
"success": True,
|
|
199
|
+
"count": len(properties),
|
|
200
|
+
"properties": properties,
|
|
201
|
+
"searchCriteria": params,
|
|
202
|
+
"metadata": {
|
|
203
|
+
"timestamp": datetime.now().isoformat(),
|
|
204
|
+
"source": "Zillow Data Server"
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
except Exception as e:
|
|
208
|
+
logger.error(f"Property search failed: {e}")
|
|
209
|
+
return {
|
|
210
|
+
"success": False,
|
|
211
|
+
"error": str(e),
|
|
212
|
+
"searchCriteria": params
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
@mcp.tool()
|
|
216
|
+
def get_property_details(property_id: str = None, address: str = None) -> Dict:
|
|
217
|
+
"""
|
|
218
|
+
Get detailed information about a property by ID or address.
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
property_id: Zillow property ID (zpid)
|
|
222
|
+
address: Full property address
|
|
223
|
+
|
|
224
|
+
Returns:
|
|
225
|
+
Detailed property information
|
|
226
|
+
"""
|
|
227
|
+
if not property_id and not address:
|
|
228
|
+
raise ValueError("Either property_id or address must be provided")
|
|
229
|
+
|
|
230
|
+
params = {}
|
|
231
|
+
if property_id:
|
|
232
|
+
params["zpid"] = property_id
|
|
233
|
+
else:
|
|
234
|
+
params["address"] = address
|
|
235
|
+
|
|
236
|
+
try:
|
|
237
|
+
# Make the actual API call to Zillow Bridge API
|
|
238
|
+
response = zillow_api_request("properties/details", params)
|
|
239
|
+
|
|
240
|
+
# Process the response from the API
|
|
241
|
+
if not response or 'property' not in response:
|
|
242
|
+
raise ValueError("Property not found or invalid API response")
|
|
243
|
+
|
|
244
|
+
property_data = response.get('property', {})
|
|
245
|
+
|
|
246
|
+
return {
|
|
247
|
+
"success": True,
|
|
248
|
+
"property": property_data,
|
|
249
|
+
"metadata": {
|
|
250
|
+
"timestamp": datetime.now().isoformat(),
|
|
251
|
+
"source": "Zillow Data Server"
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
except Exception as e:
|
|
255
|
+
logger.error(f"Property details lookup failed: {e}")
|
|
256
|
+
return {
|
|
257
|
+
"success": False,
|
|
258
|
+
"error": str(e),
|
|
259
|
+
"searchCriteria": params
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
@mcp.tool()
|
|
263
|
+
def get_zestimate(property_id: str = None, address: str = None) -> Dict:
|
|
264
|
+
"""
|
|
265
|
+
Get Zillow's estimated value (Zestimate) for a property.
|
|
266
|
+
|
|
267
|
+
Args:
|
|
268
|
+
property_id: Zillow property ID (zpid)
|
|
269
|
+
address: Full property address
|
|
270
|
+
|
|
271
|
+
Returns:
|
|
272
|
+
Zestimate information including current value and historical data
|
|
273
|
+
"""
|
|
274
|
+
if not property_id and not address:
|
|
275
|
+
raise ValueError("Either property_id or address must be provided")
|
|
276
|
+
|
|
277
|
+
params = {}
|
|
278
|
+
if property_id:
|
|
279
|
+
params["zpid"] = property_id
|
|
280
|
+
else:
|
|
281
|
+
params["address"] = address
|
|
282
|
+
|
|
283
|
+
try:
|
|
284
|
+
# Make the actual API call to Zillow Bridge API
|
|
285
|
+
response = zillow_api_request("zestimates", params)
|
|
286
|
+
|
|
287
|
+
# Process the response from the API
|
|
288
|
+
if not response or 'zestimate' not in response:
|
|
289
|
+
raise ValueError("Zestimate not found or invalid API response")
|
|
290
|
+
|
|
291
|
+
zestimate_data = response.get('zestimate', {})
|
|
292
|
+
|
|
293
|
+
return {
|
|
294
|
+
"success": True,
|
|
295
|
+
"zestimate": zestimate_data,
|
|
296
|
+
"metadata": {
|
|
297
|
+
"timestamp": datetime.now().isoformat(),
|
|
298
|
+
"source": "Zillow Data Server"
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
except Exception as e:
|
|
302
|
+
logger.error(f"Zestimate lookup failed: {e}")
|
|
303
|
+
return {
|
|
304
|
+
"success": False,
|
|
305
|
+
"error": str(e),
|
|
306
|
+
"searchCriteria": params
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
@mcp.tool()
|
|
310
|
+
def get_market_trends(
|
|
311
|
+
location: str,
|
|
312
|
+
metrics: List[str] = ["median_list_price", "median_sale_price", "median_days_on_market"],
|
|
313
|
+
time_period: str = "1year" # Options: "1month", "3months", "6months", "1year", "5years", "10years", "all"
|
|
314
|
+
) -> Dict:
|
|
315
|
+
"""
|
|
316
|
+
Get real estate market trends for a specific location.
|
|
317
|
+
|
|
318
|
+
Args:
|
|
319
|
+
location: City, ZIP code, or neighborhood
|
|
320
|
+
metrics: List of metrics to retrieve
|
|
321
|
+
time_period: Time period for historical data
|
|
322
|
+
|
|
323
|
+
Returns:
|
|
324
|
+
Market trend data for the specified location and metrics
|
|
325
|
+
"""
|
|
326
|
+
params = {
|
|
327
|
+
"location": location,
|
|
328
|
+
"metrics": metrics,
|
|
329
|
+
"time_period": time_period
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
try:
|
|
333
|
+
# Make the actual API call to Zillow Bridge API
|
|
334
|
+
api_params = {
|
|
335
|
+
"location": location,
|
|
336
|
+
"metrics": metrics,
|
|
337
|
+
"time_period": time_period
|
|
338
|
+
}
|
|
339
|
+
response = zillow_api_request("market/trends", api_params)
|
|
340
|
+
|
|
341
|
+
# Process the response from the API
|
|
342
|
+
if not response or 'trends' not in response:
|
|
343
|
+
raise ValueError("Market trends not found or invalid API response")
|
|
344
|
+
|
|
345
|
+
trend_data = response.get('trends', {})
|
|
346
|
+
|
|
347
|
+
return {
|
|
348
|
+
"success": True,
|
|
349
|
+
"location": location,
|
|
350
|
+
"trends": trend_data,
|
|
351
|
+
"time_period": time_period,
|
|
352
|
+
"metadata": {
|
|
353
|
+
"timestamp": datetime.now().isoformat(),
|
|
354
|
+
"source": "Zillow Data Server"
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
except Exception as e:
|
|
358
|
+
logger.error(f"Market trends lookup failed: {e}")
|
|
359
|
+
return {
|
|
360
|
+
"success": False,
|
|
361
|
+
"error": str(e),
|
|
362
|
+
"searchCriteria": params
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
@mcp.tool()
|
|
366
|
+
def calculate_mortgage(
|
|
367
|
+
home_price: int,
|
|
368
|
+
down_payment: int = None,
|
|
369
|
+
down_payment_percent: float = None,
|
|
370
|
+
loan_term: int = 30, # Years
|
|
371
|
+
interest_rate: float = 6.5, # Percentage
|
|
372
|
+
annual_property_tax: int = None,
|
|
373
|
+
annual_homeowners_insurance: int = None,
|
|
374
|
+
monthly_hoa: int = 0,
|
|
375
|
+
include_pmi: bool = True
|
|
376
|
+
) -> Dict:
|
|
377
|
+
"""
|
|
378
|
+
Calculate mortgage payments and related costs.
|
|
379
|
+
|
|
380
|
+
Args:
|
|
381
|
+
home_price: Price of the home in dollars
|
|
382
|
+
down_payment: Down payment amount in dollars
|
|
383
|
+
down_payment_percent: Down payment as a percentage of home price
|
|
384
|
+
loan_term: Loan term in years
|
|
385
|
+
interest_rate: Annual interest rate as a percentage
|
|
386
|
+
annual_property_tax: Annual property tax in dollars
|
|
387
|
+
annual_homeowners_insurance: Annual homeowners insurance in dollars
|
|
388
|
+
monthly_hoa: Monthly HOA fees in dollars
|
|
389
|
+
include_pmi: Whether to include PMI for down payments less than 20%
|
|
390
|
+
|
|
391
|
+
Returns:
|
|
392
|
+
Dictionary with mortgage payment details
|
|
393
|
+
"""
|
|
394
|
+
# Calculate down payment if not provided
|
|
395
|
+
if down_payment is None and down_payment_percent is None:
|
|
396
|
+
down_payment_percent = 20.0
|
|
397
|
+
|
|
398
|
+
if down_payment is None:
|
|
399
|
+
down_payment = int(home_price * (down_payment_percent / 100))
|
|
400
|
+
else:
|
|
401
|
+
down_payment_percent = (down_payment / home_price) * 100
|
|
402
|
+
|
|
403
|
+
# Calculate loan amount
|
|
404
|
+
loan_amount = home_price - down_payment
|
|
405
|
+
|
|
406
|
+
# Calculate monthly interest rate
|
|
407
|
+
monthly_interest_rate = (interest_rate / 100) / 12
|
|
408
|
+
|
|
409
|
+
# Calculate total number of payments
|
|
410
|
+
total_payments = loan_term * 12
|
|
411
|
+
|
|
412
|
+
# Calculate principal and interest payment
|
|
413
|
+
if monthly_interest_rate > 0:
|
|
414
|
+
monthly_principal_interest = loan_amount * (monthly_interest_rate * (1 + monthly_interest_rate) ** total_payments) / ((1 + monthly_interest_rate) ** total_payments - 1)
|
|
415
|
+
else:
|
|
416
|
+
monthly_principal_interest = loan_amount / total_payments
|
|
417
|
+
|
|
418
|
+
# Calculate PMI (Private Mortgage Insurance)
|
|
419
|
+
monthly_pmi = 0
|
|
420
|
+
if include_pmi and down_payment_percent < 20:
|
|
421
|
+
# Typical PMI is around 0.5% to 1% of the loan amount annually
|
|
422
|
+
monthly_pmi = (loan_amount * 0.007) / 12
|
|
423
|
+
|
|
424
|
+
# Calculate property tax payment if provided
|
|
425
|
+
monthly_property_tax = 0
|
|
426
|
+
if annual_property_tax is not None:
|
|
427
|
+
monthly_property_tax = annual_property_tax / 12
|
|
428
|
+
else:
|
|
429
|
+
# Estimate property tax if not provided (varies by location, using 1.1% as average)
|
|
430
|
+
annual_property_tax = int(home_price * 0.011)
|
|
431
|
+
monthly_property_tax = annual_property_tax / 12
|
|
432
|
+
|
|
433
|
+
# Calculate homeowners insurance payment if provided
|
|
434
|
+
monthly_homeowners_insurance = 0
|
|
435
|
+
if annual_homeowners_insurance is not None:
|
|
436
|
+
monthly_homeowners_insurance = annual_homeowners_insurance / 12
|
|
437
|
+
else:
|
|
438
|
+
# Estimate homeowners insurance if not provided
|
|
439
|
+
annual_homeowners_insurance = int(home_price * 0.0035)
|
|
440
|
+
monthly_homeowners_insurance = annual_homeowners_insurance / 12
|
|
441
|
+
|
|
442
|
+
# Calculate total monthly payment
|
|
443
|
+
monthly_payment = monthly_principal_interest + monthly_pmi + monthly_property_tax + monthly_homeowners_insurance + monthly_hoa
|
|
444
|
+
|
|
445
|
+
# Calculate total cost over the life of the loan
|
|
446
|
+
total_cost = (monthly_payment * total_payments) + down_payment
|
|
447
|
+
|
|
448
|
+
return {
|
|
449
|
+
"success": True,
|
|
450
|
+
"mortgage_details": {
|
|
451
|
+
"home_price": home_price,
|
|
452
|
+
"down_payment": down_payment,
|
|
453
|
+
"down_payment_percent": round(down_payment_percent, 2),
|
|
454
|
+
"loan_amount": loan_amount,
|
|
455
|
+
"loan_term_years": loan_term,
|
|
456
|
+
"interest_rate": interest_rate,
|
|
457
|
+
"monthly_payment": round(monthly_payment, 2),
|
|
458
|
+
"monthly_principal_interest": round(monthly_principal_interest, 2),
|
|
459
|
+
"monthly_property_tax": round(monthly_property_tax, 2),
|
|
460
|
+
"monthly_homeowners_insurance": round(monthly_homeowners_insurance, 2),
|
|
461
|
+
"monthly_hoa": monthly_hoa,
|
|
462
|
+
"monthly_pmi": round(monthly_pmi, 2),
|
|
463
|
+
"total_interest_paid": round((monthly_principal_interest * total_payments) - loan_amount, 2),
|
|
464
|
+
"total_payments": round(monthly_payment * total_payments, 2),
|
|
465
|
+
"total_cost": round(total_cost, 2)
|
|
466
|
+
},
|
|
467
|
+
"metadata": {
|
|
468
|
+
"timestamp": datetime.now().isoformat(),
|
|
469
|
+
"source": "Zillow Mortgage Calculator"
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
@mcp.tool()
|
|
474
|
+
def get_server_tools() -> Dict:
|
|
475
|
+
"""
|
|
476
|
+
Get a list of all available tools on this server.
|
|
477
|
+
|
|
478
|
+
Returns:
|
|
479
|
+
Dictionary with information about all available tools
|
|
480
|
+
"""
|
|
481
|
+
tools = [
|
|
482
|
+
{
|
|
483
|
+
"name": "search_properties",
|
|
484
|
+
"description": "Search for properties on Zillow based on criteria",
|
|
485
|
+
"parameters": {
|
|
486
|
+
"location": "Address, city, ZIP code, or neighborhood",
|
|
487
|
+
"type": "Property listing type - 'forSale', 'forRent', or 'sold'",
|
|
488
|
+
"min_price": "Minimum price in dollars",
|
|
489
|
+
"max_price": "Maximum price in dollars",
|
|
490
|
+
"beds_min": "Minimum number of bedrooms",
|
|
491
|
+
"beds_max": "Maximum number of bedrooms",
|
|
492
|
+
"baths_min": "Minimum number of bathrooms",
|
|
493
|
+
"baths_max": "Maximum number of bathrooms",
|
|
494
|
+
"home_types": "List of home types (e.g., ['house', 'condo', 'apartment'])"
|
|
495
|
+
}
|
|
496
|
+
},
|
|
497
|
+
{
|
|
498
|
+
"name": "get_property_details",
|
|
499
|
+
"description": "Get detailed information about a property by ID or address",
|
|
500
|
+
"parameters": {
|
|
501
|
+
"property_id": "Zillow property ID (zpid)",
|
|
502
|
+
"address": "Full property address"
|
|
503
|
+
}
|
|
504
|
+
},
|
|
505
|
+
{
|
|
506
|
+
"name": "get_zestimate",
|
|
507
|
+
"description": "Get Zillow's estimated value (Zestimate) for a property",
|
|
508
|
+
"parameters": {
|
|
509
|
+
"property_id": "Zillow property ID (zpid)",
|
|
510
|
+
"address": "Full property address"
|
|
511
|
+
}
|
|
512
|
+
},
|
|
513
|
+
{
|
|
514
|
+
"name": "get_market_trends",
|
|
515
|
+
"description": "Get real estate market trends for a specific location",
|
|
516
|
+
"parameters": {
|
|
517
|
+
"location": "City, ZIP code, or neighborhood",
|
|
518
|
+
"metrics": "List of metrics to retrieve",
|
|
519
|
+
"time_period": "Time period for historical data"
|
|
520
|
+
}
|
|
521
|
+
},
|
|
522
|
+
{
|
|
523
|
+
"name": "calculate_mortgage",
|
|
524
|
+
"description": "Calculate mortgage payments and related costs",
|
|
525
|
+
"parameters": {
|
|
526
|
+
"home_price": "Price of the home in dollars",
|
|
527
|
+
"down_payment": "Down payment amount in dollars",
|
|
528
|
+
"down_payment_percent": "Down payment as a percentage of home price",
|
|
529
|
+
"loan_term": "Loan term in years",
|
|
530
|
+
"interest_rate": "Annual interest rate as a percentage",
|
|
531
|
+
"annual_property_tax": "Annual property tax in dollars",
|
|
532
|
+
"annual_homeowners_insurance": "Annual homeowners insurance in dollars",
|
|
533
|
+
"monthly_hoa": "Monthly HOA fees in dollars",
|
|
534
|
+
"include_pmi": "Whether to include PMI for down payments less than 20%"
|
|
535
|
+
}
|
|
536
|
+
},
|
|
537
|
+
{
|
|
538
|
+
"name": "check_health",
|
|
539
|
+
"description": "Check the health and status of the Zillow API connection",
|
|
540
|
+
"parameters": {}
|
|
541
|
+
},
|
|
542
|
+
{
|
|
543
|
+
"name": "get_server_tools",
|
|
544
|
+
"description": "Get a list of all available tools on this server",
|
|
545
|
+
"parameters": {}
|
|
546
|
+
}
|
|
547
|
+
]
|
|
548
|
+
|
|
549
|
+
resources = [
|
|
550
|
+
{
|
|
551
|
+
"name": "zillow://property/{property_id}",
|
|
552
|
+
"description": "Get property information as a formatted text resource",
|
|
553
|
+
"parameters": {
|
|
554
|
+
"property_id": "Zillow property ID (zpid)"
|
|
555
|
+
}
|
|
556
|
+
},
|
|
557
|
+
{
|
|
558
|
+
"name": "zillow://market-trends/{location}",
|
|
559
|
+
"description": "Get market trends information as a formatted text resource",
|
|
560
|
+
"parameters": {
|
|
561
|
+
"location": "City, ZIP code, or neighborhood"
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
]
|
|
565
|
+
|
|
566
|
+
@mcp.tool()
|
|
567
|
+
def check_health() -> Dict:
|
|
568
|
+
"""
|
|
569
|
+
Check the health and status of the Zillow API connection.
|
|
570
|
+
|
|
571
|
+
Returns:
|
|
572
|
+
Dictionary with health status information
|
|
573
|
+
"""
|
|
574
|
+
start_time = datetime.now()
|
|
575
|
+
status = {
|
|
576
|
+
"success": False,
|
|
577
|
+
"api_available": False,
|
|
578
|
+
"response_time_ms": 0,
|
|
579
|
+
"timestamp": start_time.isoformat(),
|
|
580
|
+
"version": "1.0.0"
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
try:
|
|
584
|
+
# Perform a lightweight API call to check connection
|
|
585
|
+
response = zillow_api_request("health", method="GET")
|
|
586
|
+
|
|
587
|
+
# Calculate response time
|
|
588
|
+
end_time = datetime.now()
|
|
589
|
+
response_time = (end_time - start_time).total_seconds() * 1000 # ms
|
|
590
|
+
|
|
591
|
+
# Update status
|
|
592
|
+
status.update({
|
|
593
|
+
"success": True,
|
|
594
|
+
"api_available": True,
|
|
595
|
+
"response_time_ms": round(response_time, 2),
|
|
596
|
+
"zillow_api_status": response.get("status", "OK"),
|
|
597
|
+
"api_version": response.get("version", "unknown")
|
|
598
|
+
})
|
|
599
|
+
except Exception as e:
|
|
600
|
+
# API call failed
|
|
601
|
+
end_time = datetime.now()
|
|
602
|
+
response_time = (end_time - start_time).total_seconds() * 1000 # ms
|
|
603
|
+
|
|
604
|
+
# Update status with error info
|
|
605
|
+
status.update({
|
|
606
|
+
"success": False,
|
|
607
|
+
"api_available": False,
|
|
608
|
+
"response_time_ms": round(response_time, 2),
|
|
609
|
+
"error": str(e)
|
|
610
|
+
})
|
|
611
|
+
|
|
612
|
+
return status
|
|
613
|
+
|
|
614
|
+
@mcp.resource("zillow://property/{property_id}")
|
|
615
|
+
def get_property_resource(property_id: str) -> str:
|
|
616
|
+
"""
|
|
617
|
+
Get property information as a resource.
|
|
618
|
+
|
|
619
|
+
Args:
|
|
620
|
+
property_id: Zillow property ID (zpid)
|
|
621
|
+
|
|
622
|
+
Returns:
|
|
623
|
+
Property information as a formatted string
|
|
624
|
+
"""
|
|
625
|
+
try:
|
|
626
|
+
# Get property details directly from API
|
|
627
|
+
params = {"zpid": property_id} if property_id else {"address": property_id}
|
|
628
|
+
response = zillow_api_request("properties/details", params)
|
|
629
|
+
|
|
630
|
+
if not response or not response.get('success', False):
|
|
631
|
+
error = response.get('error', 'Unknown error')
|
|
632
|
+
return f"Error retrieving property information: {error}"
|
|
633
|
+
|
|
634
|
+
property_info = response.get('property', {})
|
|
635
|
+
|
|
636
|
+
# Format property information as a string - handle potentially missing fields
|
|
637
|
+
info = [f"# Property Details for {property_info.get('address', 'Unknown Address')}"]
|
|
638
|
+
|
|
639
|
+
# Add basic property details with safety checks
|
|
640
|
+
if 'price' in property_info:
|
|
641
|
+
info.append(f"- **Price**: ${property_info['price']:,}")
|
|
642
|
+
if 'zestimate' in property_info:
|
|
643
|
+
info.append(f"- **Zestimate**: ${property_info['zestimate']:,}")
|
|
644
|
+
if 'bedrooms' in property_info:
|
|
645
|
+
info.append(f"- **Bedrooms**: {property_info['bedrooms']}")
|
|
646
|
+
if 'bathrooms' in property_info:
|
|
647
|
+
info.append(f"- **Bathrooms**: {property_info['bathrooms']}")
|
|
648
|
+
if 'sqft' in property_info:
|
|
649
|
+
info.append(f"- **Square Feet**: {property_info['sqft']:,}")
|
|
650
|
+
if 'year_built' in property_info:
|
|
651
|
+
info.append(f"- **Year Built**: {property_info['year_built']}")
|
|
652
|
+
if 'lot_size' in property_info:
|
|
653
|
+
info.append(f"- **Lot Size**: {property_info['lot_size']} acres")
|
|
654
|
+
if 'home_type' in property_info:
|
|
655
|
+
info.append(f"- **Home Type**: {property_info['home_type']}")
|
|
656
|
+
if 'last_sold_date' in property_info and 'last_sold_price' in property_info:
|
|
657
|
+
info.append(f"- **Last Sold**: {property_info['last_sold_date']} for ${property_info['last_sold_price']:,}")
|
|
658
|
+
|
|
659
|
+
# Add features if available
|
|
660
|
+
if 'features' in property_info and property_info['features']:
|
|
661
|
+
info.extend(["", "## Features"])
|
|
662
|
+
features_list = property_info.get('features', [])
|
|
663
|
+
if features_list:
|
|
664
|
+
info.append("- " + "\n- ".join(features_list))
|
|
665
|
+
|
|
666
|
+
# Add schools if available
|
|
667
|
+
if 'schools' in property_info and property_info['schools']:
|
|
668
|
+
info.extend(["", "## Schools"])
|
|
669
|
+
for school in property_info.get('schools', []):
|
|
670
|
+
name = school.get('name', 'Unknown School')
|
|
671
|
+
level = school.get('level', 'Unknown Level')
|
|
672
|
+
rating = school.get('rating', 'N/A')
|
|
673
|
+
distance = school.get('distance', 'Unknown')
|
|
674
|
+
info.append(f"- **{name}** ({level}): Rating {rating}/10, {distance} miles away")
|
|
675
|
+
|
|
676
|
+
# Add neighborhood info if available
|
|
677
|
+
neighborhood_info = []
|
|
678
|
+
if 'neighborhood' in property_info:
|
|
679
|
+
neighborhood_info.append(f"- **Neighborhood**: {property_info['neighborhood']}")
|
|
680
|
+
if 'walk_score' in property_info:
|
|
681
|
+
neighborhood_info.append(f"- **Walk Score**: {property_info['walk_score']}/100")
|
|
682
|
+
if 'transit_score' in property_info:
|
|
683
|
+
neighborhood_info.append(f"- **Transit Score**: {property_info['transit_score']}/100")
|
|
684
|
+
|
|
685
|
+
if neighborhood_info:
|
|
686
|
+
info.extend(["", "## Neighborhood"])
|
|
687
|
+
info.extend(neighborhood_info)
|
|
688
|
+
|
|
689
|
+
# Add Zillow link if available
|
|
690
|
+
if 'url' in property_info:
|
|
691
|
+
info.extend(["", f"View on Zillow: {property_info['url']}"])
|
|
692
|
+
|
|
693
|
+
return "\n".join(info)
|
|
694
|
+
except Exception as e:
|
|
695
|
+
logger.error(f"Property resource lookup failed: {e}")
|
|
696
|
+
return f"Error: {str(e)}"
|
|
697
|
+
|
|
698
|
+
@mcp.resource("zillow://market-trends/{location}")
|
|
699
|
+
def get_market_trends_resource(location: str) -> str:
|
|
700
|
+
"""
|
|
701
|
+
Get market trends information as a resource.
|
|
702
|
+
|
|
703
|
+
Args:
|
|
704
|
+
location: City, ZIP code, or neighborhood
|
|
705
|
+
|
|
706
|
+
Returns:
|
|
707
|
+
Market trends information as a formatted string
|
|
708
|
+
"""
|
|
709
|
+
try:
|
|
710
|
+
# Make direct API call to get market trends
|
|
711
|
+
api_params = {
|
|
712
|
+
"location": location,
|
|
713
|
+
"metrics": ["median_list_price", "median_sale_price", "median_days_on_market"],
|
|
714
|
+
"time_period": "1year"
|
|
715
|
+
}
|
|
716
|
+
response = zillow_api_request("market/trends", api_params)
|
|
717
|
+
|
|
718
|
+
if not response or not response.get('success', False):
|
|
719
|
+
error = response.get('error', 'Unknown error')
|
|
720
|
+
return f"Error retrieving market trends: {error}"
|
|
721
|
+
|
|
722
|
+
trends = response.get('trends', {})
|
|
723
|
+
|
|
724
|
+
# Format trends information as a string - handle potentially missing data
|
|
725
|
+
info = [
|
|
726
|
+
f"# Real Estate Market Trends for {location}",
|
|
727
|
+
"",
|
|
728
|
+
"## Current Market Overview"
|
|
729
|
+
]
|
|
730
|
+
|
|
731
|
+
# Add current metrics with safety checks
|
|
732
|
+
if 'median_list_price' in trends and 'current' in trends['median_list_price'] and 'change_1year' in trends['median_list_price']:
|
|
733
|
+
info.append(f"- **Median Listing Price**: ${trends['median_list_price']['current']:,} ({trends['median_list_price']['change_1year']:+.1f}% year-over-year)")
|
|
734
|
+
|
|
735
|
+
if 'median_sale_price' in trends and 'current' in trends['median_sale_price'] and 'change_1year' in trends['median_sale_price']:
|
|
736
|
+
info.append(f"- **Median Sale Price**: ${trends['median_sale_price']['current']:,} ({trends['median_sale_price']['change_1year']:+.1f}% year-over-year)")
|
|
737
|
+
|
|
738
|
+
if 'median_days_on_market' in trends and 'current' in trends['median_days_on_market'] and 'change_1year' in trends['median_days_on_market']:
|
|
739
|
+
info.append(f"- **Median Days on Market**: {trends['median_days_on_market']['current']} days ({trends['median_days_on_market']['change_1year']:+.0f} days year-over-year)")
|
|
740
|
+
|
|
741
|
+
# Add historical section header if we have historical data
|
|
742
|
+
has_historical = any('historical' in metric_data for metric_data in trends.values() if isinstance(metric_data, dict))
|
|
743
|
+
if has_historical:
|
|
744
|
+
info.append("")
|
|
745
|
+
info.append("## Historical Trends (Last 12 Months)")
|
|
746
|
+
|
|
747
|
+
# Add historical data for each metric
|
|
748
|
+
for metric_name, metric_data in trends.items():
|
|
749
|
+
if not isinstance(metric_data, dict) or 'historical' not in metric_data:
|
|
750
|
+
continue
|
|
751
|
+
|
|
752
|
+
if metric_name == "median_list_price":
|
|
753
|
+
display_name = "Median Listing Price"
|
|
754
|
+
prefix = "$"
|
|
755
|
+
suffix = ""
|
|
756
|
+
elif metric_name == "median_sale_price":
|
|
757
|
+
display_name = "Median Sale Price"
|
|
758
|
+
prefix = "$"
|
|
759
|
+
suffix = ""
|
|
760
|
+
else: # median_days_on_market
|
|
761
|
+
display_name = "Median Days on Market"
|
|
762
|
+
prefix = ""
|
|
763
|
+
suffix = " days"
|
|
764
|
+
|
|
765
|
+
info.append(f"\n### {display_name}")
|
|
766
|
+
for point in metric_data.get("historical", []):
|
|
767
|
+
if 'date' in point and 'value' in point:
|
|
768
|
+
info.append(f"- {point['date']}: {prefix}{point['value']:,}{suffix}")
|
|
769
|
+
|
|
770
|
+
return "\n".join(info)
|
|
771
|
+
except Exception as e:
|
|
772
|
+
logger.error(f"Market trends resource lookup failed: {e}")
|
|
773
|
+
return f"Error: {str(e)}"
|
|
774
|
+
|
|
775
|
+
if __name__ == "__main__":
|
|
776
|
+
import argparse
|
|
777
|
+
|
|
778
|
+
# Set up command line arguments
|
|
779
|
+
parser = argparse.ArgumentParser(description="Zillow MCP Server")
|
|
780
|
+
parser.add_argument("--http", action="store_true", help="Run as HTTP server instead of stdio")
|
|
781
|
+
parser.add_argument("--host", type=str, default="127.0.0.1", help="Host for HTTP server")
|
|
782
|
+
parser.add_argument("--port", type=int, default=8000, help="Port for HTTP server")
|
|
783
|
+
parser.add_argument("--debug", action="store_true", help="Enable debug logging")
|
|
784
|
+
args = parser.parse_args()
|
|
785
|
+
|
|
786
|
+
# Configure logging based on arguments
|
|
787
|
+
if args.debug:
|
|
788
|
+
logging.getLogger().setLevel(logging.DEBUG)
|
|
789
|
+
|
|
790
|
+
# Print some info
|
|
791
|
+
print("Starting Zillow MCP Server...")
|
|
792
|
+
print(f"API Key Present: {'Yes' if ZILLOW_API_KEY else 'No - Please set ZILLOW_API_KEY environment variable'}")
|
|
793
|
+
|
|
794
|
+
# Check API connectivity
|
|
795
|
+
if ZILLOW_API_KEY:
|
|
796
|
+
try:
|
|
797
|
+
# Try a simple API call to verify connectivity
|
|
798
|
+
zillow_api_request("health", method="GET")
|
|
799
|
+
print("✅ Successfully connected to Zillow API")
|
|
800
|
+
except Exception as e:
|
|
801
|
+
print(f"⚠️ Warning: Could not connect to Zillow API: {e}")
|
|
802
|
+
|
|
803
|
+
# Run the server with appropriate transport
|
|
804
|
+
if args.http:
|
|
805
|
+
print(f"Running as HTTP server on {args.host}:{args.port}")
|
|
806
|
+
mcp.run(transport="streamable_http", host=args.host, port=args.port)
|
|
807
|
+
else:
|
|
808
|
+
print("Running as stdio server")
|
|
809
|
+
mcp.run()
|
|
810
|
+
|
|
811
|
+
def main():
|
|
812
|
+
"""Main entry point for the MCP server"""
|
|
813
|
+
import argparse
|
|
814
|
+
|
|
815
|
+
parser = argparse.ArgumentParser(description="Zillow MCP Server")
|
|
816
|
+
parser.add_argument("--http", action="store_true", help="Run as HTTP server instead of stdio")
|
|
817
|
+
parser.add_argument("--host", type=str, default="127.0.0.1", help="Host for HTTP server")
|
|
818
|
+
parser.add_argument("--port", type=int, default=8000, help="Port for HTTP server")
|
|
819
|
+
parser.add_argument("--debug", action="store_true", help="Enable debug logging")
|
|
820
|
+
args = parser.parse_args()
|
|
821
|
+
|
|
822
|
+
# Configure logging based on arguments
|
|
823
|
+
if args.debug:
|
|
824
|
+
logging.getLogger().setLevel(logging.DEBUG)
|
|
825
|
+
|
|
826
|
+
# Print some info
|
|
827
|
+
print("Starting Zillow MCP Server...")
|
|
828
|
+
print(f"API Key Present: {'Yes' if ZILLOW_API_KEY else 'No - Please set ZILLOW_API_KEY environment variable'}")
|
|
829
|
+
|
|
830
|
+
# Check API connectivity
|
|
831
|
+
if ZILLOW_API_KEY:
|
|
832
|
+
try:
|
|
833
|
+
zillow_api_request("health", method="GET")
|
|
834
|
+
print("✅ Successfully connected to Zillow API")
|
|
835
|
+
except Exception as e:
|
|
836
|
+
print(f"⚠️ Warning: Could not connect to Zillow API: {e}")
|
|
837
|
+
|
|
838
|
+
# Run the server
|
|
839
|
+
if args.http:
|
|
840
|
+
print(f"Running as HTTP server on {args.host}:{args.port}")
|
|
841
|
+
mcp.run(transport="streamable_http", host=args.host, port=args.port)
|
|
842
|
+
else:
|
|
843
|
+
print("Running as stdio server")
|
|
844
|
+
mcp.run()
|
iflow_mcp_sap156_zillow_mcp_server-1.0.0/iflow_mcp_sap156_zillow_mcp_server.egg-info/top_level.txt
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
dist
|
{iflow_mcp_sap156_zillow_mcp_server-1.0.0 → iflow_mcp_sap156_zillow_mcp_server-1.0.2}/README.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{iflow_mcp_sap156_zillow_mcp_server-1.0.0 → iflow_mcp_sap156_zillow_mcp_server-1.0.2}/setup.cfg
RENAMED
|
File without changes
|