@sfranalytics/mcp 0.6.3 → 0.6.4
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/dist/http.js +69 -3
- package/dist/http.js.map +1 -1
- package/dist/landing.js +17 -4
- package/dist/landing.js.map +1 -1
- package/dist/tools/formatters.js +4 -0
- package/dist/tools/formatters.js.map +1 -1
- package/dist/tools/plr/loansNearby.js +100 -84
- package/dist/tools/plr/loansNearby.js.map +1 -1
- package/dist/tools/plr/transactionHistory.js +67 -50
- package/dist/tools/plr/transactionHistory.js.map +1 -1
- package/dist/tools/sfr/bestBuyers.js +58 -41
- package/dist/tools/sfr/bestBuyers.js.map +1 -1
- package/dist/tools/sfr/buyerProfile.js +6 -3
- package/dist/tools/sfr/buyerProfile.js.map +1 -1
- package/dist/tools/sfr/getProperty.js +147 -124
- package/dist/tools/sfr/getProperty.js.map +1 -1
- package/dist/tools/sfr/propertyBatch.js +3 -1
- package/dist/tools/sfr/propertyBatch.js.map +1 -1
- package/dist/tools/sfr/propertyComps.js +51 -34
- package/dist/tools/sfr/propertyComps.js.map +1 -1
- package/dist/tools/sfr/propertyTransactions.js +48 -31
- package/dist/tools/sfr/propertyTransactions.js.map +1 -1
- package/dist/tools/sfr/rentalComparables.js +83 -66
- package/dist/tools/sfr/rentalComparables.js.map +1 -1
- package/dist/tools/sfr/rentalMarketAnalysis.js +5 -2
- package/dist/tools/sfr/rentalMarketAnalysis.js.map +1 -1
- package/dist/tools/sfr/searchProperties.js +3 -1
- package/dist/tools/sfr/searchProperties.js.map +1 -1
- package/dist/tools/sfr/topBuyers.js +4 -1
- package/dist/tools/sfr/topBuyers.js.map +1 -1
- package/dist/tools/sfr/zipDetail.js +83 -72
- package/dist/tools/sfr/zipDetail.js.map +1 -1
- package/dist/tools/sfr/zipFinder.js +24 -15
- package/dist/tools/sfr/zipFinder.js.map +1 -1
- package/package.json +1 -1
|
@@ -2,8 +2,12 @@ import { z } from "zod";
|
|
|
2
2
|
import { structuredResult, kvSummary } from "../formatters.js";
|
|
3
3
|
import { action } from "../nextActions.js";
|
|
4
4
|
import { registerToolSafe } from "../registerToolSafe.js";
|
|
5
|
+
import { ApiError } from "../../services/httpClient.js";
|
|
5
6
|
const Input = z.object({
|
|
6
|
-
address: z.string().describe("Full property address
|
|
7
|
+
address: z.string().describe("Full property address exactly as recorded (e.g. '123 Main St, Phoenix, AZ 85004'). " +
|
|
8
|
+
"Must match dataset precisely — abbreviations (St/Ave/Blvd), directional prefixes (N/S/E/W), " +
|
|
9
|
+
"and unit numbers matter. If unsure, use sfr_search_properties with the zip code first to find " +
|
|
10
|
+
"the canonical address."),
|
|
7
11
|
});
|
|
8
12
|
export function registerGetPropertyTool(server, sfr) {
|
|
9
13
|
registerToolSafe(server, "sfr_get_property", {
|
|
@@ -14,129 +18,148 @@ export function registerGetPropertyTool(server, sfr) {
|
|
|
14
18
|
"For transaction history, use sfr_property_transactions.",
|
|
15
19
|
inputSchema: Input,
|
|
16
20
|
}, async (args) => {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
[
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
[
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
[
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
[
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
[
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
[
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
21
|
+
try {
|
|
22
|
+
const data = await sfr.getPropertyByAddress(args.address);
|
|
23
|
+
const addr = data?.address;
|
|
24
|
+
const fullAddr = addr
|
|
25
|
+
? `${addr.formatted_street_address}, ${addr.city}, ${addr.state} ${addr.zip_code}`
|
|
26
|
+
: args.address;
|
|
27
|
+
const lines = [
|
|
28
|
+
`## Property Details — ${fullAddr}`,
|
|
29
|
+
"",
|
|
30
|
+
"### Location",
|
|
31
|
+
kvSummary([
|
|
32
|
+
["Address", addr?.formatted_street_address],
|
|
33
|
+
["City", addr?.city],
|
|
34
|
+
["State", addr?.state],
|
|
35
|
+
["Zip", addr?.zip_code],
|
|
36
|
+
["County", data?.county],
|
|
37
|
+
["MSA", data?.msa],
|
|
38
|
+
["Latitude", addr?.latitude],
|
|
39
|
+
["Longitude", addr?.longitude],
|
|
40
|
+
]),
|
|
41
|
+
"",
|
|
42
|
+
"### Structure",
|
|
43
|
+
kvSummary([
|
|
44
|
+
["Beds", data?.structure?.beds_count],
|
|
45
|
+
["Baths", data?.structure?.baths],
|
|
46
|
+
["Sqft", data?.structure?.total_area_sq_ft ?? data?.structure?.living_area_sqft],
|
|
47
|
+
["Lot Size (sqft)", data?.parcel?.area_sq_ft],
|
|
48
|
+
["Year Built", data?.structure?.year_built],
|
|
49
|
+
["Stories", data?.structure?.stories],
|
|
50
|
+
["Property Type", data?.property_type ?? data?.property_class_description],
|
|
51
|
+
["Units", data?.structure?.units_count],
|
|
52
|
+
]),
|
|
53
|
+
"",
|
|
54
|
+
"### Valuation & Assessments",
|
|
55
|
+
kvSummary([
|
|
56
|
+
["Assessed Value", data?.assessments?.assessed_value ? `$${Number(data.assessments.assessed_value).toLocaleString()}` : undefined],
|
|
57
|
+
["Land Value", data?.assessments?.land_value ? `$${Number(data.assessments.land_value).toLocaleString()}` : undefined],
|
|
58
|
+
["Market Value", data?.assessments?.market_value ? `$${Number(data.assessments.market_value).toLocaleString()}` : undefined],
|
|
59
|
+
["AVM", data?.valuation?.value ? `$${Number(data.valuation.value).toLocaleString()}` : undefined],
|
|
60
|
+
["Tax Amount", data?.tax_amount ? `$${Number(data.tax_amount).toLocaleString()}` : undefined],
|
|
61
|
+
]),
|
|
62
|
+
"",
|
|
63
|
+
"### Last Sale",
|
|
64
|
+
kvSummary([
|
|
65
|
+
["Date", data?.last_sale?.date],
|
|
66
|
+
["Price", data?.last_sale?.price ? `$${Number(data.last_sale.price).toLocaleString()}` : undefined],
|
|
67
|
+
["Document Type", data?.last_sale?.document_type],
|
|
68
|
+
["Mortgage Amount", data?.last_sale?.mtg_amount ? `$${Number(data.last_sale.mtg_amount).toLocaleString()}` : undefined],
|
|
69
|
+
["Lender", data?.last_sale?.lender],
|
|
70
|
+
["Months Owned", data?.months_owned],
|
|
71
|
+
]),
|
|
72
|
+
"",
|
|
73
|
+
"### Owner",
|
|
74
|
+
kvSummary([
|
|
75
|
+
["Name", data?.owner?.name],
|
|
76
|
+
["Owner Occupied", data?.owner?.owner_occupied],
|
|
77
|
+
["Corporate", data?.owner?.corporate_owner],
|
|
78
|
+
["Owner Type", data?.owner_type],
|
|
79
|
+
["Purchase Method", data?.purchase_method],
|
|
80
|
+
]),
|
|
81
|
+
"",
|
|
82
|
+
"### Status",
|
|
83
|
+
kvSummary([
|
|
84
|
+
["Listing Status", data?.listing_status],
|
|
85
|
+
["HOA", data?.hoa],
|
|
86
|
+
["Vacant", data?.vacant],
|
|
87
|
+
["Pre-foreclosure", data?.pre_foreclosure?.flag],
|
|
88
|
+
]),
|
|
89
|
+
];
|
|
90
|
+
if (data?.owner?.corporate_owner === true && data?.owner_type === "Individual") {
|
|
91
|
+
lines.push("", "> **Note:** owner_type is reported as \"Individual\" but corporate_owner is true.", "> The owner_type field may be unreliable for entity-owned properties.");
|
|
92
|
+
}
|
|
93
|
+
const actions = [
|
|
94
|
+
action("sfr_rental_comparables", "Estimate rental income", { address: fullAddr }),
|
|
95
|
+
action("sfr_property_comps", "Sales comps for valuation", { address: fullAddr }),
|
|
96
|
+
action("sfr_property_transactions", "Full ownership history", { address: fullAddr }),
|
|
97
|
+
action("plr_loans_nearby", "Private lending activity nearby", { propertyAddress: fullAddr }),
|
|
98
|
+
];
|
|
99
|
+
const structured = {
|
|
100
|
+
address: addr ? {
|
|
101
|
+
formatted_street_address: addr.formatted_street_address,
|
|
102
|
+
city: addr.city,
|
|
103
|
+
state: addr.state,
|
|
104
|
+
zip_code: addr.zip_code,
|
|
105
|
+
latitude: addr.latitude,
|
|
106
|
+
longitude: addr.longitude,
|
|
107
|
+
} : undefined,
|
|
108
|
+
county: data?.county,
|
|
109
|
+
msa: data?.msa,
|
|
110
|
+
structure: data?.structure ? {
|
|
111
|
+
beds_count: data.structure.beds_count,
|
|
112
|
+
baths: data.structure.baths,
|
|
113
|
+
total_area_sq_ft: data.structure.total_area_sq_ft ?? data.structure.living_area_sqft,
|
|
114
|
+
year_built: data.structure.year_built,
|
|
115
|
+
stories: data.structure.stories,
|
|
116
|
+
units_count: data.structure.units_count,
|
|
117
|
+
} : undefined,
|
|
118
|
+
property_type: data?.property_type ?? data?.property_class_description,
|
|
119
|
+
lot_sq_ft: data?.parcel?.area_sq_ft,
|
|
120
|
+
assessments: data?.assessments ? {
|
|
121
|
+
assessed_value: data.assessments.assessed_value,
|
|
122
|
+
land_value: data.assessments.land_value,
|
|
123
|
+
market_value: data.assessments.market_value,
|
|
124
|
+
} : undefined,
|
|
125
|
+
valuation: data?.valuation?.value != null ? { value: data.valuation.value } : undefined,
|
|
126
|
+
tax_amount: data?.tax_amount,
|
|
127
|
+
last_sale: data?.last_sale ? {
|
|
128
|
+
date: data.last_sale.date,
|
|
129
|
+
price: data.last_sale.price,
|
|
130
|
+
document_type: data.last_sale.document_type,
|
|
131
|
+
mtg_amount: data.last_sale.mtg_amount,
|
|
132
|
+
lender: data.last_sale.lender,
|
|
133
|
+
} : undefined,
|
|
134
|
+
months_owned: data?.months_owned,
|
|
135
|
+
owner: data?.owner ? {
|
|
136
|
+
name: data.owner.name,
|
|
137
|
+
owner_occupied: data.owner.owner_occupied,
|
|
138
|
+
corporate_owner: data.owner.corporate_owner,
|
|
139
|
+
} : undefined,
|
|
140
|
+
owner_type: data?.owner_type,
|
|
141
|
+
purchase_method: data?.purchase_method,
|
|
142
|
+
listing_status: data?.listing_status,
|
|
143
|
+
hoa: data?.hoa,
|
|
144
|
+
vacant: data?.vacant,
|
|
145
|
+
pre_foreclosure: data?.pre_foreclosure?.flag,
|
|
146
|
+
};
|
|
147
|
+
if (data?.owner?.corporate_owner === true && data?.owner_type === "Individual") {
|
|
148
|
+
structured.owner_type_warning = "owner_type='Individual' conflicts with corporate_owner=true";
|
|
149
|
+
}
|
|
150
|
+
return structuredResult(lines.join("\n"), structured, actions);
|
|
151
|
+
}
|
|
152
|
+
catch (err) {
|
|
153
|
+
if (err instanceof ApiError && err.status === 404) {
|
|
154
|
+
const zipMatch = args.address.match(/\b(\d{5})\b/);
|
|
155
|
+
const zip = zipMatch?.[1];
|
|
156
|
+
return structuredResult(`## Property Details — ${args.address}\n\n` +
|
|
157
|
+
`_Property not found at this address. The address must exactly match the dataset ` +
|
|
158
|
+
`(including directional prefixes like 'E'/'W', abbreviations like 'St'/'Ave', and unit numbers). ` +
|
|
159
|
+
`Use sfr_search_properties with the zip code to find the canonical address._`, { address: args.address, error: "not_found" }, [action("sfr_search_properties", "Search by zip to find correct address", zip ? { search_type: "zip", zips: zip } : undefined)]);
|
|
160
|
+
}
|
|
161
|
+
throw err;
|
|
162
|
+
}
|
|
140
163
|
});
|
|
141
164
|
}
|
|
142
165
|
//# sourceMappingURL=getProperty.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getProperty.js","sourceRoot":"","sources":["../../../src/tools/sfr/getProperty.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"getProperty.js","sourceRoot":"","sources":["../../../src/tools/sfr/getProperty.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,7 +7,9 @@ const Input = z.object({
|
|
|
7
7
|
.array(z.string())
|
|
8
8
|
.min(1)
|
|
9
9
|
.max(100)
|
|
10
|
-
.describe("Array of full addresses
|
|
10
|
+
.describe("Array of full addresses exactly as recorded (e.g. ['123 Main St, Phoenix, AZ 85004']). " +
|
|
11
|
+
"Each address must match the dataset precisely — abbreviations (St/Ave/Blvd), directional " +
|
|
12
|
+
"prefixes (N/S/E/W), and unit numbers matter. Non-matching addresses return per-item errors."),
|
|
11
13
|
});
|
|
12
14
|
export function registerMlsListingsTool(server, sfr) {
|
|
13
15
|
registerToolSafe(server, "sfr_property_batch", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"propertyBatch.js","sourceRoot":"","sources":["../../../src/tools/sfr/propertyBatch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAC9E,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE1D,MAAM,KAAK,GAAG,CAAC,CAAC,MAAM,CAAC;IACrB,SAAS,EAAE,CAAC;SACT,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;SACjB,GAAG,CAAC,CAAC,CAAC;SACN,GAAG,CAAC,GAAG,CAAC;SACR,QAAQ,
|
|
1
|
+
{"version":3,"file":"propertyBatch.js","sourceRoot":"","sources":["../../../src/tools/sfr/propertyBatch.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,8 +2,12 @@ import { z } from "zod";
|
|
|
2
2
|
import { structuredResult, markdownTable, truncatedJson } from "../formatters.js";
|
|
3
3
|
import { action } from "../nextActions.js";
|
|
4
4
|
import { registerToolSafe } from "../registerToolSafe.js";
|
|
5
|
+
import { ApiError } from "../../services/httpClient.js";
|
|
5
6
|
const Input = z.object({
|
|
6
|
-
address: z.string().describe("Full property address
|
|
7
|
+
address: z.string().describe("Full property address exactly as recorded (e.g. '123 Main St, Phoenix, AZ 85004'). " +
|
|
8
|
+
"Must match dataset precisely — abbreviations (St/Ave/Blvd), directional prefixes (N/S/E/W), " +
|
|
9
|
+
"and unit numbers matter. If unsure, use sfr_search_properties with the zip code first to find " +
|
|
10
|
+
"the canonical address."),
|
|
7
11
|
page: z.coerce.number().default(1).describe("Page number"),
|
|
8
12
|
page_size: z.coerce.number().min(20).default(25).describe("Results per page (min 20)"),
|
|
9
13
|
});
|
|
@@ -16,41 +20,54 @@ export function registerPropertyCompsTool(server, sfr) {
|
|
|
16
20
|
"For RENTAL comparables, use sfr_rental_comparables instead.",
|
|
17
21
|
inputSchema: Input,
|
|
18
22
|
}, async (args) => {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
23
|
+
try {
|
|
24
|
+
const data = await sfr.getNearbySales({
|
|
25
|
+
address: args.address,
|
|
26
|
+
page: args.page,
|
|
27
|
+
page_size: args.page_size,
|
|
28
|
+
});
|
|
29
|
+
const items = data?.data ?? (Array.isArray(data) ? data : []);
|
|
30
|
+
const total = data?.total ?? items.length;
|
|
31
|
+
const lines = [
|
|
32
|
+
`## Nearby Sales — ${args.address}`,
|
|
33
|
+
`**Total:** ${total} comparables`,
|
|
34
|
+
"",
|
|
35
|
+
];
|
|
36
|
+
if (items.length > 0) {
|
|
37
|
+
const fmt$ = (v) => v != null ? `$${Number(v).toLocaleString()}` : "—";
|
|
38
|
+
const rows = items.slice(0, 25).map((c) => [
|
|
39
|
+
c.FULL_STREET_ADDRESS ?? c.address ?? "—",
|
|
40
|
+
c.CITY ?? c.city ?? "—",
|
|
41
|
+
fmt$(c.SALE_AMT ?? c.sales_price),
|
|
42
|
+
c.RECORDING_DATE ?? c.recording_date ?? c.SALE_DATE ?? "—",
|
|
43
|
+
c.SUM_BUILDING_SQFT ?? c.building_sqft ?? "—",
|
|
44
|
+
c.YEAR_BUILT ?? c.year_built ?? "—",
|
|
45
|
+
]);
|
|
46
|
+
lines.push(markdownTable(["Address", "City", "Price", "Date", "Sqft", "Year Built"], rows));
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
lines.push("```json");
|
|
50
|
+
lines.push(truncatedJson(data, 3000));
|
|
51
|
+
lines.push("```");
|
|
52
|
+
}
|
|
53
|
+
const actions = [
|
|
54
|
+
action("sfr_get_property", "Full property details", { address: args.address }),
|
|
55
|
+
action("sfr_rental_comparables", "Rental comps to estimate income", { address: args.address }),
|
|
56
|
+
action("plr_loans_nearby", "Private lending activity nearby", { propertyAddress: args.address }),
|
|
57
|
+
];
|
|
58
|
+
return structuredResult(lines.join("\n"), { items, total, page: args.page }, actions);
|
|
42
59
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
60
|
+
catch (err) {
|
|
61
|
+
if (err instanceof ApiError && err.status === 404) {
|
|
62
|
+
const zipMatch = args.address.match(/\b(\d{5})\b/);
|
|
63
|
+
const zip = zipMatch?.[1];
|
|
64
|
+
return structuredResult(`## Nearby Sales — ${args.address}\n\n` +
|
|
65
|
+
`_Property not found at this address. The address must exactly match the dataset ` +
|
|
66
|
+
`(including directional prefixes like 'E'/'W', abbreviations like 'St'/'Ave', and unit numbers). ` +
|
|
67
|
+
`Use sfr_search_properties with the zip code to find the canonical address._`, { address: args.address, error: "not_found" }, [action("sfr_search_properties", "Search by zip to find correct address", zip ? { search_type: "zip", zips: zip } : undefined)]);
|
|
68
|
+
}
|
|
69
|
+
throw err;
|
|
47
70
|
}
|
|
48
|
-
const actions = [
|
|
49
|
-
action("sfr_get_property", "Full property details", { address: args.address }),
|
|
50
|
-
action("sfr_rental_comparables", "Rental comps to estimate income", { address: args.address }),
|
|
51
|
-
action("plr_loans_nearby", "Private lending activity nearby", { propertyAddress: args.address }),
|
|
52
|
-
];
|
|
53
|
-
return structuredResult(lines.join("\n"), { items, total, page: args.page }, actions);
|
|
54
71
|
});
|
|
55
72
|
}
|
|
56
73
|
//# sourceMappingURL=propertyComps.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"propertyComps.js","sourceRoot":"","sources":["../../../src/tools/sfr/propertyComps.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAClF,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"propertyComps.js","sourceRoot":"","sources":["../../../src/tools/sfr/propertyComps.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,8 +2,12 @@ import { z } from "zod";
|
|
|
2
2
|
import { structuredResult, markdownTable, truncatedJson } from "../formatters.js";
|
|
3
3
|
import { action } from "../nextActions.js";
|
|
4
4
|
import { registerToolSafe } from "../registerToolSafe.js";
|
|
5
|
+
import { ApiError } from "../../services/httpClient.js";
|
|
5
6
|
const Input = z.object({
|
|
6
|
-
address: z.string().describe("Full property address
|
|
7
|
+
address: z.string().describe("Full property address exactly as recorded (e.g. '123 Main St, Phoenix, AZ 85004'). " +
|
|
8
|
+
"Must match dataset precisely — abbreviations (St/Ave/Blvd), directional prefixes (N/S/E/W), " +
|
|
9
|
+
"and unit numbers matter. If unsure, use sfr_search_properties with the zip code first to find " +
|
|
10
|
+
"the canonical address."),
|
|
7
11
|
});
|
|
8
12
|
export function registerPropertyTransactionsTool(server, sfr) {
|
|
9
13
|
registerToolSafe(server, "sfr_property_transactions", {
|
|
@@ -13,38 +17,51 @@ export function registerPropertyTransactionsTool(server, sfr) {
|
|
|
13
17
|
"Use this to understand a property's chain of title, price appreciation, and financing patterns.",
|
|
14
18
|
inputSchema: Input,
|
|
15
19
|
}, async (args) => {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
20
|
+
try {
|
|
21
|
+
const data = await sfr.getPropertyTransactions(args.address);
|
|
22
|
+
// API returns {property_id, address, transactions: [...]}
|
|
23
|
+
const items = data?.transactions ?? data?.data ?? (Array.isArray(data) ? data : []);
|
|
24
|
+
const lines = [
|
|
25
|
+
`## Transaction History — ${args.address}`,
|
|
26
|
+
`**Transactions:** ${items.length}`,
|
|
27
|
+
"",
|
|
28
|
+
];
|
|
29
|
+
if (items.length > 0) {
|
|
30
|
+
const fmt$ = (v) => v != null && Number(v) > 0 ? `$${Number(v).toLocaleString()}` : "—";
|
|
31
|
+
const rows = items.slice(0, 30).map((t) => [
|
|
32
|
+
t.RECORDING_DATE ?? t.recording_date ?? "—",
|
|
33
|
+
t.TRANSACTION_TYPE ?? t.transaction_type ?? "—",
|
|
34
|
+
fmt$(t.SALE_AMT ?? t.sale_amt),
|
|
35
|
+
t.BUYER_BORROWER1_NAME ?? t.buyer_name ?? "—",
|
|
36
|
+
t.SELLER1_NAME ?? t.seller_name ?? "—",
|
|
37
|
+
fmt$(t.FIRST_MTG_AMT ?? t.mortgage_amount),
|
|
38
|
+
t.FIRST_MTG_LENDER_NAME ?? "—",
|
|
39
|
+
]);
|
|
40
|
+
lines.push(markdownTable(["Date", "Type", "Sale Price", "Buyer", "Seller", "Mortgage", "Lender"], rows));
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
lines.push("```json");
|
|
44
|
+
lines.push(truncatedJson(data, 3000));
|
|
45
|
+
lines.push("```");
|
|
46
|
+
}
|
|
47
|
+
const actions = [
|
|
48
|
+
action("sfr_get_property", "Current property details & valuation", { address: args.address }),
|
|
49
|
+
action("sfr_rental_comparables", "Estimate rental income", { address: args.address }),
|
|
50
|
+
action("plr_transaction_history", "Private lending history", { propertyAddress: args.address }),
|
|
51
|
+
];
|
|
52
|
+
return structuredResult(lines.join("\n"), { items, total: items.length, page: 1 }, actions);
|
|
36
53
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
54
|
+
catch (err) {
|
|
55
|
+
if (err instanceof ApiError && err.status === 404) {
|
|
56
|
+
const zipMatch = args.address.match(/\b(\d{5})\b/);
|
|
57
|
+
const zip = zipMatch?.[1];
|
|
58
|
+
return structuredResult(`## Transaction History — ${args.address}\n\n` +
|
|
59
|
+
`_Property not found at this address. The address must exactly match the dataset ` +
|
|
60
|
+
`(including directional prefixes like 'E'/'W', abbreviations like 'St'/'Ave', and unit numbers). ` +
|
|
61
|
+
`Use sfr_search_properties with the zip code to find the canonical address._`, { address: args.address, error: "not_found" }, [action("sfr_search_properties", "Search by zip to find correct address", zip ? { search_type: "zip", zips: zip } : undefined)]);
|
|
62
|
+
}
|
|
63
|
+
throw err;
|
|
41
64
|
}
|
|
42
|
-
const actions = [
|
|
43
|
-
action("sfr_get_property", "Current property details & valuation", { address: args.address }),
|
|
44
|
-
action("sfr_rental_comparables", "Estimate rental income", { address: args.address }),
|
|
45
|
-
action("plr_transaction_history", "Private lending history", { propertyAddress: args.address }),
|
|
46
|
-
];
|
|
47
|
-
return structuredResult(lines.join("\n"), { items, total: items.length, page: 1 }, actions);
|
|
48
65
|
});
|
|
49
66
|
}
|
|
50
67
|
//# sourceMappingURL=propertyTransactions.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"propertyTransactions.js","sourceRoot":"","sources":["../../../src/tools/sfr/propertyTransactions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAClF,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"propertyTransactions.js","sourceRoot":"","sources":["../../../src/tools/sfr/propertyTransactions.ts"],"names":[],"mappings":"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"}
|