#!/usr/bin/env node
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';
import { searchProperties } from './tools/property-search.js';
import { getPropertyDetails, getPropertyComparables, searchPropertiesWithDetails } from './tools/property-details.js';
import { calculateMortgage, calculateAffordability } from './tools/mortgage-calculator.js';
import type { MortgageInput, SearchQuery } from './types/real-estate.js';
import { formatUserError } from './utils/errors.js';
// Create MCP server
const server = new McpServer({
name: 'real-estate-mcp',
version: '0.1.0',
});
// Register property search tool
server.registerTool(
'property.search',
{
title: 'Property Search',
description: 'Search for properties using real Zillow API data with comprehensive filters and location search.',
inputSchema: {
location: z.string().describe('Location string, e.g., "Los Angeles, CA", "90210", or "123 Main St, Boston, MA"'),
minPrice: z.number().optional().describe('Minimum price in USD'),
maxPrice: z.number().optional().describe('Maximum price in USD'),
minBeds: z.number().optional().describe('Minimum number of bedrooms'),
minBaths: z.number().optional().describe('Minimum number of bathrooms'),
minSqft: z.number().optional().describe('Minimum square footage'),
maxSqft: z.number().optional().describe('Maximum square footage'),
propertyType: z.enum(['single_family', 'condo', 'townhouse', 'apartment', 'multi_family', 'land', 'manufactured']).optional().describe('Type of property'),
page: z.number().optional().describe('Page number for pagination (default: 1)'),
},
},
async (params) => {
try {
const query = params as SearchQuery;
const results = await searchProperties(query);
return {
content: [
{
type: 'text',
text: JSON.stringify({
success: true,
location: query.location,
count: results.length,
properties: results,
note: results.length > 0 ? 'Real data from Zillow API' : 'No properties found in this area'
}, null, 2),
},
],
};
} catch (error) {
return {
content: [
{
type: 'text',
text: JSON.stringify({
success: false,
error: formatUserError(error as Error),
suggestion: 'Try adjusting your search criteria or check if your API keys are configured'
}, null, 2),
},
],
};
}
}
);
// Register property details tool
server.registerTool(
'property.details',
{
title: 'Property Details',
description: 'Get comprehensive property information including photos, price history, and detailed features using Zillow API.',
inputSchema: {
propertyId: z.string().describe('Property ID (Zillow ZPID) to get detailed information for'),
},
},
async (params) => {
try {
const { propertyId } = params;
const property = await getPropertyDetails(propertyId as string);
if (!property) {
return {
content: [
{
type: 'text',
text: JSON.stringify({
success: false,
error: 'Property not found',
propertyId
}, null, 2),
},
],
};
}
return {
content: [
{
type: 'text',
text: JSON.stringify({
success: true,
property,
note: 'Detailed property information from Zillow API'
}, null, 2),
},
],
};
} catch (error) {
return {
content: [
{
type: 'text',
text: JSON.stringify({
success: false,
error: formatUserError(error as Error),
suggestion: 'Ensure the property ID is valid and your API keys are configured'
}, null, 2),
},
],
};
}
}
);
// Register property comparables tool
server.registerTool(
'property.comparables',
{
title: 'Property Comparables',
description: 'Get similar properties (comparables/comps) for a given property using Zillow API.',
inputSchema: {
propertyId: z.string().describe('Property ID (Zillow ZPID) to find comparables for'),
},
},
async (params) => {
try {
const { propertyId } = params;
const comparables = await getPropertyComparables(propertyId as string);
return {
content: [
{
type: 'text',
text: JSON.stringify({
success: true,
count: comparables.length,
comparables,
note: comparables.length > 0 ? 'Comparable properties from Zillow API' : 'No comparable properties found'
}, null, 2),
},
],
};
} catch (error) {
return {
content: [
{
type: 'text',
text: JSON.stringify({
success: false,
error: formatUserError(error as Error),
suggestion: 'Ensure the property ID is valid and your API keys are configured'
}, null, 2),
},
],
};
}
}
);
// Register enhanced property search tool
server.registerTool(
'property.searchDetailed',
{
title: 'Enhanced Property Search',
description: 'Search for properties and immediately get detailed information including photos and features. Limited to 5 results for performance.',
inputSchema: {
location: z.string().describe('Location string, e.g., "Los Angeles, CA", "90210", or "123 Main St, Boston, MA"'),
minPrice: z.number().optional().describe('Minimum price in USD'),
maxPrice: z.number().optional().describe('Maximum price in USD'),
minBeds: z.number().optional().describe('Minimum number of bedrooms'),
maxResults: z.number().optional().describe('Maximum number of detailed results (default: 5, max: 10)'),
},
},
async (params) => {
try {
const { location, minPrice, maxPrice, minBeds, maxResults } = params;
const properties = await searchPropertiesWithDetails(location as string, {
minPrice,
maxPrice,
minBeds,
maxResults: Math.min(maxResults || 5, 10) // Cap at 10 to prevent excessive API usage
});
return {
content: [
{
type: 'text',
text: JSON.stringify({
success: true,
location,
count: properties.length,
properties,
note: 'Enhanced property search with detailed information from Zillow API'
}, null, 2),
},
],
};
} catch (error) {
return {
content: [
{
type: 'text',
text: JSON.stringify({
success: false,
error: formatUserError(error as Error),
suggestion: 'Try adjusting your search criteria or check if your API keys are configured'
}, null, 2),
},
],
};
}
}
);
// Register mortgage calculator tool
server.registerTool(
'mortgage.calculate',
{
title: 'Mortgage Calculator',
description: 'Calculate monthly mortgage payment and breakdown.',
inputSchema: {
homePrice: z.number(),
downPayment: z.number(),
annualInterestRate: z.number(),
termYears: z.number(),
propertyTaxAnnual: z.number().optional(),
insuranceAnnual: z.number().optional(),
hoaMonthly: z.number().optional(),
pmiMonthly: z.number().optional(),
},
},
async (params) => {
const res = calculateMortgage(params as MortgageInput);
return {
content: [
{
type: 'text',
text: JSON.stringify(res, null, 2),
},
],
};
}
);
// Register affordability calculator tool
server.registerTool(
'mortgage.affordability',
{
title: 'Affordability Calculator',
description: 'Estimate maximum home price based on a target monthly payment.',
inputSchema: {
monthlyPayment: z.number(),
downPayment: z.number(),
annualInterestRate: z.number(),
termYears: z.number().default(30),
},
},
async (params) => {
const { monthlyPayment, downPayment, annualInterestRate, termYears } = params as any;
const max = calculateAffordability(
monthlyPayment,
downPayment,
annualInterestRate,
termYears || 30
);
return {
content: [
{
type: 'text',
text: JSON.stringify({ maxHomePrice: max }, null, 2),
},
],
};
}
);
// Start server over stdio
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('Real Estate MCP server running on stdio');
}
main().catch((err) => {
console.error('Server failed to start:', err);
process.exit(1);
});