@sfranalytics/mcp 0.6.0
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/LICENSE +24 -0
- package/README.md +147 -0
- package/dist/config.d.ts +28 -0
- package/dist/config.js +101 -0
- package/dist/config.js.map +1 -0
- package/dist/http.d.ts +2 -0
- package/dist/http.js +252 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +41 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.js +213 -0
- package/dist/server.js.map +1 -0
- package/dist/services/httpClient.d.ts +19 -0
- package/dist/services/httpClient.js +73 -0
- package/dist/services/httpClient.js.map +1 -0
- package/dist/services/plr.d.ts +26 -0
- package/dist/services/plr.js +75 -0
- package/dist/services/plr.js.map +1 -0
- package/dist/services/sfr.d.ts +33 -0
- package/dist/services/sfr.js +124 -0
- package/dist/services/sfr.js.map +1 -0
- package/dist/services/snowflake.d.ts +12 -0
- package/dist/services/snowflake.js +71 -0
- package/dist/services/snowflake.js.map +1 -0
- package/dist/tools/dateHelper.d.ts +26 -0
- package/dist/tools/dateHelper.js +86 -0
- package/dist/tools/dateHelper.js.map +1 -0
- package/dist/tools/formatters.d.ts +66 -0
- package/dist/tools/formatters.js +219 -0
- package/dist/tools/formatters.js.map +1 -0
- package/dist/tools/health.d.ts +5 -0
- package/dist/tools/health.js +38 -0
- package/dist/tools/health.js.map +1 -0
- package/dist/tools/nextActions.d.ts +10 -0
- package/dist/tools/nextActions.js +23 -0
- package/dist/tools/nextActions.js.map +1 -0
- package/dist/tools/plr/borrowerContacts.d.ts +3 -0
- package/dist/tools/plr/borrowerContacts.js +73 -0
- package/dist/tools/plr/borrowerContacts.js.map +1 -0
- package/dist/tools/plr/borrowerLoans.d.ts +3 -0
- package/dist/tools/plr/borrowerLoans.js +115 -0
- package/dist/tools/plr/borrowerLoans.js.map +1 -0
- package/dist/tools/plr/borrowerProfile.d.ts +3 -0
- package/dist/tools/plr/borrowerProfile.js +201 -0
- package/dist/tools/plr/borrowerProfile.js.map +1 -0
- package/dist/tools/plr/borrowerRankings.d.ts +3 -0
- package/dist/tools/plr/borrowerRankings.js +123 -0
- package/dist/tools/plr/borrowerRankings.js.map +1 -0
- package/dist/tools/plr/borrowerSearch.d.ts +3 -0
- package/dist/tools/plr/borrowerSearch.js +128 -0
- package/dist/tools/plr/borrowerSearch.js.map +1 -0
- package/dist/tools/plr/churnedBorrowers.d.ts +3 -0
- package/dist/tools/plr/churnedBorrowers.js +86 -0
- package/dist/tools/plr/churnedBorrowers.js.map +1 -0
- package/dist/tools/plr/lenderBorrowers.d.ts +3 -0
- package/dist/tools/plr/lenderBorrowers.js +71 -0
- package/dist/tools/plr/lenderBorrowers.js.map +1 -0
- package/dist/tools/plr/lenderRankings.d.ts +3 -0
- package/dist/tools/plr/lenderRankings.js +74 -0
- package/dist/tools/plr/lenderRankings.js.map +1 -0
- package/dist/tools/plr/loansNearby.d.ts +3 -0
- package/dist/tools/plr/loansNearby.js +113 -0
- package/dist/tools/plr/loansNearby.js.map +1 -0
- package/dist/tools/plr/marketTrends.d.ts +3 -0
- package/dist/tools/plr/marketTrends.js +95 -0
- package/dist/tools/plr/marketTrends.js.map +1 -0
- package/dist/tools/plr/msaRankings.d.ts +3 -0
- package/dist/tools/plr/msaRankings.js +74 -0
- package/dist/tools/plr/msaRankings.js.map +1 -0
- package/dist/tools/plr/negativeRemarks.d.ts +3 -0
- package/dist/tools/plr/negativeRemarks.js +94 -0
- package/dist/tools/plr/negativeRemarks.js.map +1 -0
- package/dist/tools/plr/ownerSearch.d.ts +3 -0
- package/dist/tools/plr/ownerSearch.js +57 -0
- package/dist/tools/plr/ownerSearch.js.map +1 -0
- package/dist/tools/plr/portfolioSummary.d.ts +3 -0
- package/dist/tools/plr/portfolioSummary.js +99 -0
- package/dist/tools/plr/portfolioSummary.js.map +1 -0
- package/dist/tools/plr/topBorrowers.d.ts +3 -0
- package/dist/tools/plr/topBorrowers.js +69 -0
- package/dist/tools/plr/topBorrowers.js.map +1 -0
- package/dist/tools/plr/topLenders.d.ts +3 -0
- package/dist/tools/plr/topLenders.js +75 -0
- package/dist/tools/plr/topLenders.js.map +1 -0
- package/dist/tools/plr/transactionHistory.d.ts +3 -0
- package/dist/tools/plr/transactionHistory.js +74 -0
- package/dist/tools/plr/transactionHistory.js.map +1 -0
- package/dist/tools/prompts.d.ts +7 -0
- package/dist/tools/prompts.js +157 -0
- package/dist/tools/prompts.js.map +1 -0
- package/dist/tools/registerToolSafe.d.ts +29 -0
- package/dist/tools/registerToolSafe.js +36 -0
- package/dist/tools/registerToolSafe.js.map +1 -0
- package/dist/tools/sfr/activityHighlights.d.ts +3 -0
- package/dist/tools/sfr/activityHighlights.js +70 -0
- package/dist/tools/sfr/activityHighlights.js.map +1 -0
- package/dist/tools/sfr/bestBuyers.d.ts +3 -0
- package/dist/tools/sfr/bestBuyers.js +60 -0
- package/dist/tools/sfr/bestBuyers.js.map +1 -0
- package/dist/tools/sfr/buyerGrowth.d.ts +3 -0
- package/dist/tools/sfr/buyerGrowth.js +68 -0
- package/dist/tools/sfr/buyerGrowth.js.map +1 -0
- package/dist/tools/sfr/buyerProfile.d.ts +3 -0
- package/dist/tools/sfr/buyerProfile.js +162 -0
- package/dist/tools/sfr/buyerProfile.js.map +1 -0
- package/dist/tools/sfr/distressSearch.d.ts +3 -0
- package/dist/tools/sfr/distressSearch.js +173 -0
- package/dist/tools/sfr/distressSearch.js.map +1 -0
- package/dist/tools/sfr/flipActivity.d.ts +3 -0
- package/dist/tools/sfr/flipActivity.js +110 -0
- package/dist/tools/sfr/flipActivity.js.map +1 -0
- package/dist/tools/sfr/flipStats.d.ts +3 -0
- package/dist/tools/sfr/flipStats.js +98 -0
- package/dist/tools/sfr/flipStats.js.map +1 -0
- package/dist/tools/sfr/getProperty.d.ts +3 -0
- package/dist/tools/sfr/getProperty.js +142 -0
- package/dist/tools/sfr/getProperty.js.map +1 -0
- package/dist/tools/sfr/institutionalOwners.d.ts +3 -0
- package/dist/tools/sfr/institutionalOwners.js +88 -0
- package/dist/tools/sfr/institutionalOwners.js.map +1 -0
- package/dist/tools/sfr/investorActivity.d.ts +3 -0
- package/dist/tools/sfr/investorActivity.js +130 -0
- package/dist/tools/sfr/investorActivity.js.map +1 -0
- package/dist/tools/sfr/marketHighlights.d.ts +3 -0
- package/dist/tools/sfr/marketHighlights.js +100 -0
- package/dist/tools/sfr/marketHighlights.js.map +1 -0
- package/dist/tools/sfr/msaResolver.d.ts +15 -0
- package/dist/tools/sfr/msaResolver.js +109 -0
- package/dist/tools/sfr/msaResolver.js.map +1 -0
- package/dist/tools/sfr/propertyBatch.d.ts +3 -0
- package/dist/tools/sfr/propertyBatch.js +73 -0
- package/dist/tools/sfr/propertyBatch.js.map +1 -0
- package/dist/tools/sfr/propertyComps.d.ts +3 -0
- package/dist/tools/sfr/propertyComps.js +56 -0
- package/dist/tools/sfr/propertyComps.js.map +1 -0
- package/dist/tools/sfr/propertyTransactions.d.ts +3 -0
- package/dist/tools/sfr/propertyTransactions.js +50 -0
- package/dist/tools/sfr/propertyTransactions.js.map +1 -0
- package/dist/tools/sfr/rentalComparables.d.ts +3 -0
- package/dist/tools/sfr/rentalComparables.js +91 -0
- package/dist/tools/sfr/rentalComparables.js.map +1 -0
- package/dist/tools/sfr/rentalMarketAnalysis.d.ts +3 -0
- package/dist/tools/sfr/rentalMarketAnalysis.js +134 -0
- package/dist/tools/sfr/rentalMarketAnalysis.js.map +1 -0
- package/dist/tools/sfr/rentalStats.d.ts +3 -0
- package/dist/tools/sfr/rentalStats.js +118 -0
- package/dist/tools/sfr/rentalStats.js.map +1 -0
- package/dist/tools/sfr/searchProperties.d.ts +3 -0
- package/dist/tools/sfr/searchProperties.js +157 -0
- package/dist/tools/sfr/searchProperties.js.map +1 -0
- package/dist/tools/sfr/topBuyers.d.ts +3 -0
- package/dist/tools/sfr/topBuyers.js +91 -0
- package/dist/tools/sfr/topBuyers.js.map +1 -0
- package/dist/tools/sfr/zipDetail.d.ts +3 -0
- package/dist/tools/sfr/zipDetail.js +85 -0
- package/dist/tools/sfr/zipDetail.js.map +1 -0
- package/dist/tools/sfr/zipFinder.d.ts +3 -0
- package/dist/tools/sfr/zipFinder.js +79 -0
- package/dist/tools/sfr/zipFinder.js.map +1 -0
- package/dist/tools/slugHelper.d.ts +2 -0
- package/dist/tools/slugHelper.js +5 -0
- package/dist/tools/slugHelper.js.map +1 -0
- package/dist/tools/snowflake/compareMarkets.d.ts +2 -0
- package/dist/tools/snowflake/compareMarkets.js +95 -0
- package/dist/tools/snowflake/compareMarkets.js.map +1 -0
- package/dist/tools/snowflake/hviTrend.d.ts +2 -0
- package/dist/tools/snowflake/hviTrend.js +77 -0
- package/dist/tools/snowflake/hviTrend.js.map +1 -0
- package/dist/tools/snowflake/investorActivity.d.ts +2 -0
- package/dist/tools/snowflake/investorActivity.js +138 -0
- package/dist/tools/snowflake/investorActivity.js.map +1 -0
- package/dist/tools/snowflake/marketSnapshot.d.ts +2 -0
- package/dist/tools/snowflake/marketSnapshot.js +185 -0
- package/dist/tools/snowflake/marketSnapshot.js.map +1 -0
- package/dist/tools/snowflake/marketTrends.d.ts +2 -0
- package/dist/tools/snowflake/marketTrends.js +81 -0
- package/dist/tools/snowflake/marketTrends.js.map +1 -0
- package/dist/tools/snowflake/rankRentalZips.d.ts +2 -0
- package/dist/tools/snowflake/rankRentalZips.js +94 -0
- package/dist/tools/snowflake/rankRentalZips.js.map +1 -0
- package/dist/tools/snowflake/rankZips.d.ts +2 -0
- package/dist/tools/snowflake/rankZips.js +86 -0
- package/dist/tools/snowflake/rankZips.js.map +1 -0
- package/dist/tools/snowflake/rentalMarket.d.ts +2 -0
- package/dist/tools/snowflake/rentalMarket.js +111 -0
- package/dist/tools/snowflake/rentalMarket.js.map +1 -0
- package/dist/tools/snowflake/rentalYield.d.ts +2 -0
- package/dist/tools/snowflake/rentalYield.js +143 -0
- package/dist/tools/snowflake/rentalYield.js.map +1 -0
- package/dist/tools/snowflake/zipProfile.d.ts +2 -0
- package/dist/tools/snowflake/zipProfile.js +110 -0
- package/dist/tools/snowflake/zipProfile.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +5 -0
- package/dist/version.js.map +1 -0
- package/package.json +53 -0
package/dist/server.js
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { VERSION } from "./version.js";
|
|
3
|
+
import { HttpClient } from "./services/httpClient.js";
|
|
4
|
+
import { SfrClient } from "./services/sfr.js";
|
|
5
|
+
import { PlrClient } from "./services/plr.js";
|
|
6
|
+
// Tool registrations — Health
|
|
7
|
+
import { registerHealthTool } from "./tools/health.js";
|
|
8
|
+
// SFR Tools
|
|
9
|
+
import { registerSearchPropertiesTool } from "./tools/sfr/searchProperties.js";
|
|
10
|
+
import { registerGetPropertyTool } from "./tools/sfr/getProperty.js";
|
|
11
|
+
import { registerPropertyTransactionsTool } from "./tools/sfr/propertyTransactions.js";
|
|
12
|
+
import { registerMlsListingsTool } from "./tools/sfr/propertyBatch.js";
|
|
13
|
+
import { registerPropertyCompsTool } from "./tools/sfr/propertyComps.js";
|
|
14
|
+
import { registerRentalStatsTool } from "./tools/sfr/rentalStats.js";
|
|
15
|
+
import { registerRentalComparablesTool } from "./tools/sfr/rentalComparables.js";
|
|
16
|
+
import { registerZipFinderTool } from "./tools/sfr/zipFinder.js";
|
|
17
|
+
import { registerZipDetailTool } from "./tools/sfr/zipDetail.js";
|
|
18
|
+
import { registerTopBuyersTool } from "./tools/sfr/topBuyers.js";
|
|
19
|
+
import { registerBuyerProfileTool } from "./tools/sfr/buyerProfile.js";
|
|
20
|
+
import { registerBuyerGrowthTool } from "./tools/sfr/buyerGrowth.js";
|
|
21
|
+
import { registerBestBuyersTool } from "./tools/sfr/bestBuyers.js";
|
|
22
|
+
import { registerMarketHighlightsTool } from "./tools/sfr/marketHighlights.js";
|
|
23
|
+
import { registerInvestorActivityTool } from "./tools/sfr/investorActivity.js";
|
|
24
|
+
import { registerFlipStatsTool } from "./tools/sfr/flipStats.js";
|
|
25
|
+
import { registerFlipActivityTool } from "./tools/sfr/flipActivity.js";
|
|
26
|
+
import { registerInstitutionalOwnersTool } from "./tools/sfr/institutionalOwners.js";
|
|
27
|
+
import { registerActivityHighlightsTool } from "./tools/sfr/activityHighlights.js";
|
|
28
|
+
import { registerDistressSearchTool } from "./tools/sfr/distressSearch.js";
|
|
29
|
+
import { registerRentalMarketAnalysisTool } from "./tools/sfr/rentalMarketAnalysis.js";
|
|
30
|
+
// PLR Tools
|
|
31
|
+
import { registerBorrowerSearchTool } from "./tools/plr/borrowerSearch.js";
|
|
32
|
+
import { registerBorrowerProfileTool } from "./tools/plr/borrowerProfile.js";
|
|
33
|
+
import { registerBorrowerRankingsTool } from "./tools/plr/borrowerRankings.js";
|
|
34
|
+
import { registerTopBorrowersTool } from "./tools/plr/topBorrowers.js";
|
|
35
|
+
import { registerBorrowerLoansTool } from "./tools/plr/borrowerLoans.js";
|
|
36
|
+
import { registerBorrowerContactsTool } from "./tools/plr/borrowerContacts.js";
|
|
37
|
+
import { registerLenderRankingsTool } from "./tools/plr/lenderRankings.js";
|
|
38
|
+
import { registerTopLendersTool } from "./tools/plr/topLenders.js";
|
|
39
|
+
import { registerLenderBorrowersTool } from "./tools/plr/lenderBorrowers.js";
|
|
40
|
+
import { registerChurnedBorrowersTool } from "./tools/plr/churnedBorrowers.js";
|
|
41
|
+
import { registerMarketTrendsTool } from "./tools/plr/marketTrends.js";
|
|
42
|
+
import { registerMsaRankingsTool } from "./tools/plr/msaRankings.js";
|
|
43
|
+
import { registerLoansNearbyTool } from "./tools/plr/loansNearby.js";
|
|
44
|
+
import { registerTransactionHistoryTool } from "./tools/plr/transactionHistory.js";
|
|
45
|
+
import { registerPortfolioSummaryTool } from "./tools/plr/portfolioSummary.js";
|
|
46
|
+
import { registerNegativeRemarksTool } from "./tools/plr/negativeRemarks.js";
|
|
47
|
+
import { registerOwnerSearchTool } from "./tools/plr/ownerSearch.js";
|
|
48
|
+
import { registerPrompts } from "./tools/prompts.js";
|
|
49
|
+
// Snowflake Tools
|
|
50
|
+
import { registerMarketSnapshotTool } from "./tools/snowflake/marketSnapshot.js";
|
|
51
|
+
import { registerMarketTrendsTool as registerSfMarketTrendsTool } from "./tools/snowflake/marketTrends.js";
|
|
52
|
+
import { registerCompareMarketsTool } from "./tools/snowflake/compareMarkets.js";
|
|
53
|
+
import { registerRankZipsTool } from "./tools/snowflake/rankZips.js";
|
|
54
|
+
import { registerRentalMarketTool } from "./tools/snowflake/rentalMarket.js";
|
|
55
|
+
import { registerRentalYieldTool } from "./tools/snowflake/rentalYield.js";
|
|
56
|
+
import { registerRankRentalZipsTool } from "./tools/snowflake/rankRentalZips.js";
|
|
57
|
+
import { registerZipInvestorActivityTool } from "./tools/snowflake/investorActivity.js";
|
|
58
|
+
import { registerZipProfileTool } from "./tools/snowflake/zipProfile.js";
|
|
59
|
+
import { registerHviTrendTool } from "./tools/snowflake/hviTrend.js";
|
|
60
|
+
import { initSnowflake } from "./services/snowflake.js";
|
|
61
|
+
function buildInstructions(hasSfr, hasPlr, hasSnowflake) {
|
|
62
|
+
const sections = [
|
|
63
|
+
"SFR Analytics MCP — real estate investment data for single-family rental (SFR) markets.",
|
|
64
|
+
"",
|
|
65
|
+
];
|
|
66
|
+
sections.push("DATA SOURCES:");
|
|
67
|
+
if (hasSfr) {
|
|
68
|
+
sections.push(" SFR API (sfr_*) — Property transactions, buyer profiles, rental data, zip analytics, market activity");
|
|
69
|
+
}
|
|
70
|
+
if (hasPlr) {
|
|
71
|
+
sections.push(" Private Lender Radar (plr_*) — Private/hard-money lending: borrowers, lenders, loan trends");
|
|
72
|
+
}
|
|
73
|
+
if (hasSnowflake) {
|
|
74
|
+
sections.push(" Snowflake (sfr_market_*, sfr_rental_*, sfr_zip_*, sfr_hvi_*) — Redfin market data, census demographics, rental yield, investor activity");
|
|
75
|
+
}
|
|
76
|
+
sections.push("", "TOOL GROUPS & WHEN TO USE EACH:", "");
|
|
77
|
+
if (hasSfr) {
|
|
78
|
+
sections.push("Property Research: sfr_search_properties (find transactions), sfr_get_property (single property),", " sfr_property_transactions (history), sfr_property_batch (MLS listings), sfr_property_comps (comparables),", " sfr_distress_search (pre-foreclosure, vacant, zombie foreclosure properties)", "", "Rental Analysis: sfr_rental_stats (market-level stats by location), sfr_rental_comparables (comps near address),", " sfr_rental_market_analysis (YoY rent growth, monthly trends — find where rents are rising/falling)", "", "Zip/Market Screening: sfr_zip_finder (rank zips by yield/rent/value), sfr_zip_detail (deep-dive one zip),", " sfr_institutional_owners (REITs/funds in area)", "", "Buyer Intelligence: sfr_top_buyers (any geography), sfr_market_highlights (MSA-only with filters),", " sfr_buyer_profile (investor portfolio & strategy), sfr_buyer_growth (trends over time),", " sfr_best_buyers (hot deals), sfr_activity_highlights (quick market snapshot)", "", "Market Activity: sfr_investor_activity (MSA transactions with portfolio size),", " sfr_flip_stats (flip market summary), sfr_flip_activity (individual flips)", "");
|
|
79
|
+
}
|
|
80
|
+
if (hasPlr) {
|
|
81
|
+
sections.push("Lending Intelligence: plr_borrower_search (filter by loans/type/state/contacts),", " plr_borrower_rankings (growth metrics), plr_top_borrowers (nationwide leaderboard),", " plr_borrower_profile (full profile), plr_borrower_loans (loan history), plr_borrower_contacts (contact info)", "", "Lender Intelligence: plr_lender_rankings (by loan count, nationwide), plr_top_lenders (by volume, with geo),", " plr_lender_borrowers (lender's clients), plr_churned_borrowers (lender's lost clients)", "", "Market Trends: plr_market_trends (nationwide time series), plr_msa_rankings (geographic rankings),", " plr_portfolio_summary (nationwide totals), plr_loans_nearby (loans near address),", " plr_transaction_history (nationwide transaction feed)", "", "Negative Remarks: plr_negative_remarks (foreclosures, defaults, adverse events by borrower/lender)", "", "Principal Search: plr_owner_search (search by person name, not LLC)", "");
|
|
82
|
+
}
|
|
83
|
+
if (hasSnowflake) {
|
|
84
|
+
sections.push("Snowflake Market Data: sfr_market_snapshot (ZIP/city Redfin snapshot with buyer leverage),", " sfr_market_trends (price/DOM/inventory time series), sfr_compare_markets (side-by-side 2-4 locations),", " sfr_rank_zips (rank ZIPs by price/DOM/growth/inventory in a metro)", "", "Snowflake Rental & Yield: sfr_rental_market_sf (rent stats with percentiles from 5.9M listings),", " sfr_rental_yield (market yield vs acquisition yield, cap rates, 1% rule),", " sfr_rank_rental_zips (rank ZIPs by rent/yield/listings)", "", "Snowflake Census & Investor: sfr_zip_profile (demographics, housing stock, SFH detail),", " sfr_hvi_trend (home value + rental value index over time),", " sfr_zip_investor_activity (cash/corp/flip transactions, current corporate ownership)", "");
|
|
85
|
+
}
|
|
86
|
+
sections.push("COMMON WORKFLOWS:");
|
|
87
|
+
if (hasSfr) {
|
|
88
|
+
sections.push("1. Market screening: sfr_zip_finder -> sfr_zip_detail -> sfr_rental_stats -> sfr_top_buyers", "2. Buyer research: sfr_top_buyers -> sfr_buyer_profile -> sfr_buyer_growth");
|
|
89
|
+
}
|
|
90
|
+
if (hasPlr) {
|
|
91
|
+
sections.push(`${hasSfr ? "3" : "1"}. Lending market: plr_portfolio_summary -> plr_market_trends -> plr_msa_rankings`, `${hasSfr ? "4" : "2"}. Borrower outreach: plr_borrower_search -> plr_borrower_loans -> plr_borrower_contacts`);
|
|
92
|
+
}
|
|
93
|
+
if (hasSfr) {
|
|
94
|
+
sections.push(`${hasPlr ? "5" : "3"}. Property analysis: sfr_search_properties -> sfr_get_property -> sfr_rental_comparables`);
|
|
95
|
+
}
|
|
96
|
+
if (hasSfr && hasPlr) {
|
|
97
|
+
sections.push("6. Lender market share: plr_top_lenders (filter by state/MSA/loanType for competitive analysis)", "7. Borrower distress: plr_churned_borrowers -> plr_negative_remarks (check for defaults) -> plr_borrower_loans (get state) -> sfr_distress_search (owner_name + state)", "8. Investor deep-dive: sfr_top_buyers -> sfr_buyer_profile -> plr_borrower_search (check if buyer borrows privately)", "9. Maturing loans prospecting: plr_borrower_search (nextDueStart/nextDueEnd for maturity window) -> plr_borrower_loans (outstandingOnly=true) -> plr_borrower_contacts", "10. Competitive intelligence: plr_top_lenders (market share) -> plr_lender_borrowers (lender's clients) -> plr_churned_borrowers (lost clients) -> plr_borrower_contacts (outreach)", "11. Market sizing: plr_borrower_search (count borrowers by MSA, minBridgeLoans for tier proxy) -> plr_msa_rankings (YoY growth) -> plr_portfolio_summary (national context)", "12. Borrower deep-dive: plr_borrower_search -> plr_borrower_profile (lender relationships) -> plr_borrower_loans (LTC calculation, deal sizes) -> plr_borrower_contacts (phone/email)");
|
|
98
|
+
}
|
|
99
|
+
sections.push("", "IMPORTANT NOTES:");
|
|
100
|
+
if (hasPlr) {
|
|
101
|
+
sections.push(" plr_market_trends and plr_portfolio_summary are NATIONWIDE only — no geographic filters", " plr_top_borrowers has NO MSA/city filter — use plr_borrower_search for metro-level queries", " plr_owner_search requires state filter for best results — person names are matched within states");
|
|
102
|
+
}
|
|
103
|
+
if (hasSfr) {
|
|
104
|
+
sections.push(" sfr_buyer_profile only works for major institutional investors — use sfr_top_buyers to find names", " Buyer endpoints with search_type param require matching location param (e.g., search_type='state' needs state=)", " sfr_top_buyers search_type='city' requires both city= and state= params", " sfr_rental_market_analysis works best at city level — MSA/state often returns zeros", " sfr_investor_activity is the best tool for cash buyer identification (isCashBuyer field) — sfr_search_properties purchase_type is NOT a cash filter", " sfr_flip_activity supports buyer_name filter (partial match, case-insensitive) — use for iBuyer margin analysis (e.g. buyer_name='OPENDOOR')", " sfr_search_properties with seller_name is the best tool for 'where is [institution] selling?' — broader date range than sfr_investor_activity");
|
|
105
|
+
}
|
|
106
|
+
if (hasSfr && hasPlr) {
|
|
107
|
+
sections.push(" sfr_distress_search requires a location filter — when using owner_name from PLR, include state from plr_borrower_loans", " PLR lender tools: use short 'lender' field names for plr_lender_borrowers/plr_churned_borrowers, not display 'clean_name'", " PLR and SFR APIs use DIFFERENT MSA names for the same metro (e.g. PLR: 'Austin-Round Rock-San Marcos, TX' vs SFR: 'Austin-Round Rock-Georgetown, TX'). Use plr_msa_rankings to discover correct PLR MSA names. SFR tools auto-resolve MSA names for sfr_investor_activity, sfr_market_highlights, sfr_top_buyers, sfr_flip_stats, sfr_flip_activity, and sfr_institutional_owners.");
|
|
108
|
+
}
|
|
109
|
+
sections.push(" Use sfra_health to verify API connectivity");
|
|
110
|
+
return sections.join("\n");
|
|
111
|
+
}
|
|
112
|
+
export function createServer(config) {
|
|
113
|
+
const hasSfr = config.sfr !== null;
|
|
114
|
+
const hasPlr = config.plr !== null;
|
|
115
|
+
const hasSnowflake = config.snowflake !== null;
|
|
116
|
+
let sfrClient = null;
|
|
117
|
+
let plrClient = null;
|
|
118
|
+
if (config.sfr) {
|
|
119
|
+
const sfrHttp = new HttpClient({
|
|
120
|
+
baseUrl: config.sfr.baseUrl,
|
|
121
|
+
defaultHeaders: { [config.sfr.apiTokenHeader]: config.sfr.apiToken },
|
|
122
|
+
timeoutMs: config.http.timeoutMs,
|
|
123
|
+
});
|
|
124
|
+
sfrClient = new SfrClient(sfrHttp);
|
|
125
|
+
}
|
|
126
|
+
if (config.plr) {
|
|
127
|
+
const plrHttp = new HttpClient({
|
|
128
|
+
baseUrl: config.plr.baseUrl,
|
|
129
|
+
defaultHeaders: { [config.plr.apiTokenHeader]: config.plr.apiToken },
|
|
130
|
+
timeoutMs: config.http.timeoutMs,
|
|
131
|
+
});
|
|
132
|
+
plrClient = new PlrClient(plrHttp);
|
|
133
|
+
}
|
|
134
|
+
if (config.snowflake) {
|
|
135
|
+
initSnowflake(config.snowflake);
|
|
136
|
+
}
|
|
137
|
+
const server = new McpServer({ name: "@sfranalytics/mcp", version: VERSION }, {
|
|
138
|
+
capabilities: { logging: {} },
|
|
139
|
+
instructions: buildInstructions(hasSfr, hasPlr, hasSnowflake),
|
|
140
|
+
});
|
|
141
|
+
// Health — always registered, checks only configured APIs
|
|
142
|
+
registerHealthTool(server, sfrClient, plrClient, config);
|
|
143
|
+
// SFR Tools
|
|
144
|
+
if (sfrClient) {
|
|
145
|
+
// Properties (6)
|
|
146
|
+
registerSearchPropertiesTool(server, sfrClient);
|
|
147
|
+
registerGetPropertyTool(server, sfrClient);
|
|
148
|
+
registerPropertyTransactionsTool(server, sfrClient);
|
|
149
|
+
registerMlsListingsTool(server, sfrClient);
|
|
150
|
+
registerPropertyCompsTool(server, sfrClient);
|
|
151
|
+
registerDistressSearchTool(server, sfrClient);
|
|
152
|
+
// Rentals (3)
|
|
153
|
+
registerRentalStatsTool(server, sfrClient);
|
|
154
|
+
registerRentalComparablesTool(server, sfrClient);
|
|
155
|
+
registerRentalMarketAnalysisTool(server, sfrClient);
|
|
156
|
+
// Zip/Geo Analytics (3)
|
|
157
|
+
registerZipFinderTool(server, sfrClient);
|
|
158
|
+
registerZipDetailTool(server, sfrClient);
|
|
159
|
+
registerInstitutionalOwnersTool(server, sfrClient);
|
|
160
|
+
// Buyers & Investors (5)
|
|
161
|
+
registerTopBuyersTool(server, sfrClient);
|
|
162
|
+
registerBuyerProfileTool(server, sfrClient);
|
|
163
|
+
registerBuyerGrowthTool(server, sfrClient);
|
|
164
|
+
registerBestBuyersTool(server, sfrClient);
|
|
165
|
+
registerMarketHighlightsTool(server, sfrClient);
|
|
166
|
+
// Market Activity (4)
|
|
167
|
+
registerInvestorActivityTool(server, sfrClient);
|
|
168
|
+
registerFlipStatsTool(server, sfrClient);
|
|
169
|
+
registerFlipActivityTool(server, sfrClient);
|
|
170
|
+
registerActivityHighlightsTool(server, sfrClient);
|
|
171
|
+
}
|
|
172
|
+
// PLR Tools
|
|
173
|
+
if (plrClient) {
|
|
174
|
+
// Borrowers (6)
|
|
175
|
+
registerBorrowerSearchTool(server, plrClient);
|
|
176
|
+
registerBorrowerProfileTool(server, plrClient);
|
|
177
|
+
registerBorrowerRankingsTool(server, plrClient);
|
|
178
|
+
registerTopBorrowersTool(server, plrClient);
|
|
179
|
+
registerBorrowerLoansTool(server, plrClient);
|
|
180
|
+
registerBorrowerContactsTool(server, plrClient);
|
|
181
|
+
// Lenders (4)
|
|
182
|
+
registerLenderRankingsTool(server, plrClient);
|
|
183
|
+
registerTopLendersTool(server, plrClient);
|
|
184
|
+
registerLenderBorrowersTool(server, plrClient);
|
|
185
|
+
registerChurnedBorrowersTool(server, plrClient);
|
|
186
|
+
// Market & Portfolio (5)
|
|
187
|
+
registerMarketTrendsTool(server, plrClient);
|
|
188
|
+
registerMsaRankingsTool(server, plrClient);
|
|
189
|
+
registerLoansNearbyTool(server, plrClient);
|
|
190
|
+
registerTransactionHistoryTool(server, plrClient);
|
|
191
|
+
registerPortfolioSummaryTool(server, plrClient);
|
|
192
|
+
// Negative Remarks & Owner Search (2)
|
|
193
|
+
registerNegativeRemarksTool(server, plrClient);
|
|
194
|
+
registerOwnerSearchTool(server, plrClient);
|
|
195
|
+
}
|
|
196
|
+
// Snowflake Tools (10)
|
|
197
|
+
if (hasSnowflake) {
|
|
198
|
+
registerMarketSnapshotTool(server);
|
|
199
|
+
registerSfMarketTrendsTool(server);
|
|
200
|
+
registerCompareMarketsTool(server);
|
|
201
|
+
registerRankZipsTool(server);
|
|
202
|
+
registerRentalMarketTool(server);
|
|
203
|
+
registerRentalYieldTool(server);
|
|
204
|
+
registerRankRentalZipsTool(server);
|
|
205
|
+
registerZipInvestorActivityTool(server);
|
|
206
|
+
registerZipProfileTool(server);
|
|
207
|
+
registerHviTrendTool(server);
|
|
208
|
+
}
|
|
209
|
+
// Example prompts — scoped to available APIs
|
|
210
|
+
registerPrompts(server, hasSfr, hasPlr);
|
|
211
|
+
return server;
|
|
212
|
+
}
|
|
213
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare class ApiError extends Error {
|
|
2
|
+
readonly status: number;
|
|
3
|
+
readonly url: string;
|
|
4
|
+
readonly details?: unknown | undefined;
|
|
5
|
+
constructor(message: string, status: number, url: string, details?: unknown | undefined);
|
|
6
|
+
}
|
|
7
|
+
export type HttpClientOptions = {
|
|
8
|
+
baseUrl: string;
|
|
9
|
+
defaultHeaders: Record<string, string>;
|
|
10
|
+
timeoutMs: number;
|
|
11
|
+
};
|
|
12
|
+
export declare class HttpClient {
|
|
13
|
+
private readonly opts;
|
|
14
|
+
constructor(opts: HttpClientOptions);
|
|
15
|
+
request<T>(method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE", path: string, init?: {
|
|
16
|
+
query?: Record<string, string | number | boolean | undefined | unknown>;
|
|
17
|
+
body?: unknown;
|
|
18
|
+
}): Promise<T>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { VERSION } from "../version.js";
|
|
2
|
+
export class ApiError extends Error {
|
|
3
|
+
status;
|
|
4
|
+
url;
|
|
5
|
+
details;
|
|
6
|
+
constructor(message, status, url, details) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.status = status;
|
|
9
|
+
this.url = url;
|
|
10
|
+
this.details = details;
|
|
11
|
+
this.name = "ApiError";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export class HttpClient {
|
|
15
|
+
opts;
|
|
16
|
+
constructor(opts) {
|
|
17
|
+
this.opts = opts;
|
|
18
|
+
}
|
|
19
|
+
async request(method, path, init) {
|
|
20
|
+
const url = new URL(this.opts.baseUrl + path);
|
|
21
|
+
if (init?.query) {
|
|
22
|
+
for (const [k, v] of Object.entries(init.query)) {
|
|
23
|
+
if (v === undefined)
|
|
24
|
+
continue;
|
|
25
|
+
if (Array.isArray(v)) {
|
|
26
|
+
for (const item of v)
|
|
27
|
+
url.searchParams.append(k, String(item));
|
|
28
|
+
}
|
|
29
|
+
else if (typeof v === "object" && v !== null) {
|
|
30
|
+
url.searchParams.set(k, JSON.stringify(v));
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
url.searchParams.set(k, String(v));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const controller = new AbortController();
|
|
38
|
+
const t = setTimeout(() => controller.abort(), this.opts.timeoutMs);
|
|
39
|
+
try {
|
|
40
|
+
const res = await fetch(url, {
|
|
41
|
+
method,
|
|
42
|
+
headers: {
|
|
43
|
+
"User-Agent": `@sfranalytics/mcp/${VERSION}`,
|
|
44
|
+
...this.opts.defaultHeaders,
|
|
45
|
+
...(init?.body ? { "Content-Type": "application/json" } : {}),
|
|
46
|
+
},
|
|
47
|
+
body: init?.body ? JSON.stringify(init.body) : undefined,
|
|
48
|
+
signal: controller.signal,
|
|
49
|
+
});
|
|
50
|
+
const contentType = res.headers.get("content-type") ?? "";
|
|
51
|
+
const isJson = contentType.includes("application/json");
|
|
52
|
+
const payload = isJson
|
|
53
|
+
? await res.json().catch(() => undefined)
|
|
54
|
+
: await res.text().catch(() => "");
|
|
55
|
+
if (!res.ok) {
|
|
56
|
+
throw new ApiError(`HTTP ${res.status} ${method} ${path}`, res.status, url.toString(), payload);
|
|
57
|
+
}
|
|
58
|
+
return payload;
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
if (err instanceof ApiError)
|
|
62
|
+
throw err;
|
|
63
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
64
|
+
throw new ApiError(`Timeout after ${this.opts.timeoutMs}ms`, 408, url.toString());
|
|
65
|
+
}
|
|
66
|
+
throw err;
|
|
67
|
+
}
|
|
68
|
+
finally {
|
|
69
|
+
clearTimeout(t);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=httpClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"httpClient.js","sourceRoot":"","sources":["../../src/services/httpClient.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { HttpClient } from "./httpClient.js";
|
|
2
|
+
export declare class PlrClient {
|
|
3
|
+
private readonly http;
|
|
4
|
+
constructor(http: HttpClient);
|
|
5
|
+
borrowerListBuilder(filters: Record<string, unknown>): Promise<unknown>;
|
|
6
|
+
borrowerProfile(params: Record<string, unknown>): Promise<unknown>;
|
|
7
|
+
borrowerRankings(filters: Record<string, unknown>): Promise<unknown>;
|
|
8
|
+
nationwideLargestBorrowers(filters: Record<string, unknown>): Promise<unknown>;
|
|
9
|
+
borrowerLoans(filters: Record<string, unknown>): Promise<unknown>;
|
|
10
|
+
borrowerContacts(params: Record<string, unknown>): Promise<unknown>;
|
|
11
|
+
lenderRankings(filters: Record<string, unknown>): Promise<unknown>;
|
|
12
|
+
churnedBorrowers(params: Record<string, unknown>): Promise<unknown>;
|
|
13
|
+
topLendersByVolume(filters: Record<string, unknown>): Promise<unknown>;
|
|
14
|
+
lenderBorrowers(filters: Record<string, unknown>): Promise<unknown>;
|
|
15
|
+
marketTrends(endpoint: string, params: Record<string, unknown>): Promise<unknown>;
|
|
16
|
+
loansNearby(params: Record<string, unknown>): Promise<unknown>;
|
|
17
|
+
msaRankings(filters: Record<string, unknown>): Promise<unknown>;
|
|
18
|
+
transactionHistory(filters: Record<string, unknown>): Promise<unknown>;
|
|
19
|
+
portfolioSummary(params: Record<string, unknown>): Promise<unknown>;
|
|
20
|
+
negativeRemarks(filters: Record<string, unknown>): Promise<unknown>;
|
|
21
|
+
ownersList(params: Record<string, unknown>): Promise<unknown>;
|
|
22
|
+
healthCheck(): Promise<{
|
|
23
|
+
ok: boolean;
|
|
24
|
+
latencyMs: number;
|
|
25
|
+
}>;
|
|
26
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
export class PlrClient {
|
|
2
|
+
http;
|
|
3
|
+
constructor(http) {
|
|
4
|
+
this.http = http;
|
|
5
|
+
}
|
|
6
|
+
// --- Borrower Analytics ---
|
|
7
|
+
async borrowerListBuilder(filters) {
|
|
8
|
+
return this.http.request("POST", "/api/v1/analytics/borrower/borrower_list_builder/", { body: filters });
|
|
9
|
+
}
|
|
10
|
+
async borrowerProfile(params) {
|
|
11
|
+
return this.http.request("GET", "/api/v1/analytics/borrower/borrower_profile/", { query: params });
|
|
12
|
+
}
|
|
13
|
+
async borrowerRankings(filters) {
|
|
14
|
+
return this.http.request("POST", "/api/v1/analytics/borrower/borrower_rankings/", { body: filters });
|
|
15
|
+
}
|
|
16
|
+
async nationwideLargestBorrowers(filters) {
|
|
17
|
+
return this.http.request("POST", "/api/v1/analytics/borrower/nationwide_largest_borrowers/", { body: filters });
|
|
18
|
+
}
|
|
19
|
+
async borrowerLoans(filters) {
|
|
20
|
+
return this.http.request("POST", "/api/v1/analytics/borrower/borrower_loans/", { body: filters });
|
|
21
|
+
}
|
|
22
|
+
async borrowerContacts(params) {
|
|
23
|
+
return this.http.request("GET", "/api/v1/analytics/borrower/borrower_contact_table/", { query: params });
|
|
24
|
+
}
|
|
25
|
+
// --- Lender Analytics ---
|
|
26
|
+
async lenderRankings(filters) {
|
|
27
|
+
return this.http.request("POST", "/api/v1/analytics/lender/lender_rankings/", { body: filters });
|
|
28
|
+
}
|
|
29
|
+
async churnedBorrowers(params) {
|
|
30
|
+
return this.http.request("POST", "/api/v1/analytics/lender/churned_borrowers/", { body: params });
|
|
31
|
+
}
|
|
32
|
+
async topLendersByVolume(filters) {
|
|
33
|
+
return this.http.request("POST", "/api/v1/analytics/lender/top_lenders_by_volume/", { body: filters });
|
|
34
|
+
}
|
|
35
|
+
async lenderBorrowers(filters) {
|
|
36
|
+
return this.http.request("POST", "/api/v1/analytics/lender/lender_borrowers/", { body: filters });
|
|
37
|
+
}
|
|
38
|
+
// --- Time Series ---
|
|
39
|
+
async marketTrends(endpoint, params) {
|
|
40
|
+
return this.http.request("GET", `/api/v1/analytics/time-series/${endpoint}/`, { query: params });
|
|
41
|
+
}
|
|
42
|
+
// --- Property Analytics ---
|
|
43
|
+
async loansNearby(params) {
|
|
44
|
+
return this.http.request("GET", "/api/v1/analytics/property/loans_nearby/", { query: params });
|
|
45
|
+
}
|
|
46
|
+
async msaRankings(filters) {
|
|
47
|
+
return this.http.request("POST", "/api/v1/analytics/metro/msa_rankings/", { body: filters });
|
|
48
|
+
}
|
|
49
|
+
async transactionHistory(filters) {
|
|
50
|
+
return this.http.request("POST", "/api/v1/analytics/property/transaction_history/", { body: filters });
|
|
51
|
+
}
|
|
52
|
+
async portfolioSummary(params) {
|
|
53
|
+
return this.http.request("GET", "/api/v1/analytics/portfolio/portfolio_summary/", { query: params });
|
|
54
|
+
}
|
|
55
|
+
// --- Negative Remarks ---
|
|
56
|
+
async negativeRemarks(filters) {
|
|
57
|
+
return this.http.request("POST", "/api/v1/analytics/borrower/negative_remarks/", { body: filters });
|
|
58
|
+
}
|
|
59
|
+
// --- Owner Search ---
|
|
60
|
+
async ownersList(params) {
|
|
61
|
+
return this.http.request("GET", "/api/v1/analytics/borrower/owners_list/", { query: params });
|
|
62
|
+
}
|
|
63
|
+
// --- Health ---
|
|
64
|
+
async healthCheck() {
|
|
65
|
+
const start = Date.now();
|
|
66
|
+
try {
|
|
67
|
+
await this.http.request("GET", "/health-check/");
|
|
68
|
+
return { ok: true, latencyMs: Date.now() - start };
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return { ok: false, latencyMs: Date.now() - start };
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=plr.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plr.js","sourceRoot":"","sources":["../../src/services/plr.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { HttpClient } from "./httpClient.js";
|
|
2
|
+
type QueryParams = Record<string, unknown>;
|
|
3
|
+
export declare class SfrClient {
|
|
4
|
+
private readonly http;
|
|
5
|
+
constructor(http: HttpClient);
|
|
6
|
+
getPropertyByAddress(address: string): Promise<unknown>;
|
|
7
|
+
getPropertyBatch(addresses: string[]): Promise<unknown>;
|
|
8
|
+
getNearbySales(params: QueryParams): Promise<unknown>;
|
|
9
|
+
getPropertyTransactions(address: string): Promise<unknown>;
|
|
10
|
+
getBuyerActivity(params: QueryParams): Promise<unknown>;
|
|
11
|
+
getTopBuyers(params: QueryParams): Promise<unknown>;
|
|
12
|
+
getMarketTopBuyers(params: QueryParams): Promise<unknown>;
|
|
13
|
+
getBuyerProfile(name: string): Promise<unknown>;
|
|
14
|
+
getBuyerGrowth(name: string): Promise<unknown>;
|
|
15
|
+
getBestBuyers(address: string): Promise<unknown>;
|
|
16
|
+
getMarketActivity(params: QueryParams): Promise<unknown>;
|
|
17
|
+
getFlips(params: QueryParams): Promise<unknown>;
|
|
18
|
+
getActivityCount(params: QueryParams): Promise<unknown>;
|
|
19
|
+
getZipFinder(params: QueryParams): Promise<unknown>;
|
|
20
|
+
getZipDetail(zipCode: string): Promise<unknown>;
|
|
21
|
+
getInstitutionalLargestOwners(params: QueryParams): Promise<unknown>;
|
|
22
|
+
getFlipHoldTime(params: QueryParams): Promise<unknown>;
|
|
23
|
+
getFlipGrossMargin(params: QueryParams): Promise<unknown>;
|
|
24
|
+
getPropertySearch(params: QueryParams): Promise<unknown>;
|
|
25
|
+
getRentalStats(params: QueryParams): Promise<unknown>;
|
|
26
|
+
getRentalComparables(params: QueryParams): Promise<unknown>;
|
|
27
|
+
getRentalMarketAnalysis(params: QueryParams): Promise<unknown>;
|
|
28
|
+
healthCheck(): Promise<{
|
|
29
|
+
ok: boolean;
|
|
30
|
+
latencyMs: number;
|
|
31
|
+
}>;
|
|
32
|
+
}
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
export class SfrClient {
|
|
2
|
+
http;
|
|
3
|
+
constructor(http) {
|
|
4
|
+
this.http = http;
|
|
5
|
+
}
|
|
6
|
+
// --- Properties ---
|
|
7
|
+
async getPropertyByAddress(address) {
|
|
8
|
+
return this.http.request("GET", "/properties/by-address", {
|
|
9
|
+
query: { address },
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
async getPropertyBatch(addresses) {
|
|
13
|
+
return this.http.request("GET", "/properties/batch", {
|
|
14
|
+
query: { addresses: addresses.join("|") },
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
async getNearbySales(params) {
|
|
18
|
+
return this.http.request("GET", "/properties/nearby-sales", {
|
|
19
|
+
query: params,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
async getPropertyTransactions(address) {
|
|
23
|
+
return this.http.request("GET", "/properties/transactions", {
|
|
24
|
+
query: { address },
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
// --- Buyers ---
|
|
28
|
+
async getBuyerActivity(params) {
|
|
29
|
+
return this.http.request("GET", "/buyers/activity", {
|
|
30
|
+
query: params,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
async getTopBuyers(params) {
|
|
34
|
+
return this.http.request("GET", "/buyers/activity/top-buyers", {
|
|
35
|
+
query: params,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
async getMarketTopBuyers(params) {
|
|
39
|
+
return this.http.request("GET", "/buyers/market/top-buyers", {
|
|
40
|
+
query: params,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
async getBuyerProfile(name) {
|
|
44
|
+
return this.http.request("GET", `/buyer-profiles/${encodeURIComponent(name)}`);
|
|
45
|
+
}
|
|
46
|
+
async getBuyerGrowth(name) {
|
|
47
|
+
return this.http.request("GET", `/buyer-profiles/${encodeURIComponent(name)}/growth`);
|
|
48
|
+
}
|
|
49
|
+
async getBestBuyers(address) {
|
|
50
|
+
return this.http.request("GET", "/buyers/best-buyers", {
|
|
51
|
+
query: { address },
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
async getMarketActivity(params) {
|
|
55
|
+
return this.http.request("GET", "/buyers/market", {
|
|
56
|
+
query: params,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
async getFlips(params) {
|
|
60
|
+
return this.http.request("GET", "/geo-analytics/flips", {
|
|
61
|
+
query: params,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
async getActivityCount(params) {
|
|
65
|
+
return this.http.request("GET", "/buyers/activity/count", {
|
|
66
|
+
query: params,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
// --- Geo Analytics ---
|
|
70
|
+
async getZipFinder(params) {
|
|
71
|
+
return this.http.request("GET", "/geo-analytics/zip-finder", {
|
|
72
|
+
query: params,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
async getZipDetail(zipCode) {
|
|
76
|
+
return this.http.request("GET", `/geo-analytics/zip-finder/zip/${encodeURIComponent(zipCode)}`);
|
|
77
|
+
}
|
|
78
|
+
async getInstitutionalLargestOwners(params) {
|
|
79
|
+
return this.http.request("GET", "/geo-analytics/institutional-ownership/largest-owners", { query: params });
|
|
80
|
+
}
|
|
81
|
+
async getFlipHoldTime(params) {
|
|
82
|
+
return this.http.request("GET", "/geo-analytics/flips/hold-time", {
|
|
83
|
+
query: params,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
async getFlipGrossMargin(params) {
|
|
87
|
+
return this.http.request("GET", "/geo-analytics/flips/gross-margin", {
|
|
88
|
+
query: params,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
async getPropertySearch(params) {
|
|
92
|
+
return this.http.request("GET", "/properties/search", {
|
|
93
|
+
query: params,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
// --- Rental Analytics ---
|
|
97
|
+
async getRentalStats(params) {
|
|
98
|
+
return this.http.request("GET", "/rental-analytics/stats", {
|
|
99
|
+
query: params,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
async getRentalComparables(params) {
|
|
103
|
+
return this.http.request("GET", "/rental-analytics/comparables", {
|
|
104
|
+
query: params,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
async getRentalMarketAnalysis(params) {
|
|
108
|
+
return this.http.request("GET", "/rental-analytics/market-analysis", {
|
|
109
|
+
query: params,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
// --- Health ---
|
|
113
|
+
async healthCheck() {
|
|
114
|
+
const start = Date.now();
|
|
115
|
+
try {
|
|
116
|
+
await this.http.request("GET", "/health-check");
|
|
117
|
+
return { ok: true, latencyMs: Date.now() - start };
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
return { ok: false, latencyMs: Date.now() - start };
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=sfr.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sfr.js","sourceRoot":"","sources":["../../src/services/sfr.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type SnowflakeConfig = {
|
|
2
|
+
account: string;
|
|
3
|
+
username: string;
|
|
4
|
+
password: string;
|
|
5
|
+
database: string;
|
|
6
|
+
schema: string;
|
|
7
|
+
warehouse: string;
|
|
8
|
+
};
|
|
9
|
+
export declare function initSnowflake(cfg: SnowflakeConfig): void;
|
|
10
|
+
export declare function isSnowflakeConfigured(): boolean;
|
|
11
|
+
export declare function sfQuery<T = Record<string, unknown>>(sql: string, binds?: (string | number)[]): Promise<T[]>;
|
|
12
|
+
export declare function disconnectSnowflake(): Promise<void>;
|