@biznezstack/propertydata-mcp
Version:
MCP server for PropertyData UK property market API - Access 60+ endpoints for property valuations, market analysis, demographics, and area intelligence through Claude Desktop
873 lines • 31.8 kB
JavaScript
#!/usr/bin/env node
/**
* PropertyData MCP Server
*
* A comprehensive MCP server providing access to PropertyData API endpoints
* for UK property market analytics, valuations, and area data.
*
* Usage: npx @yourname/propertydata-mcp
*/
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
import fetch from "node-fetch";
const API_KEY = process.env.PROPERTYDATA_API_KEY;
const BASE_URL = "https://api.propertydata.co.uk";
if (!API_KEY) {
console.error("ERROR: PROPERTYDATA_API_KEY environment variable is required");
console.error("Please set it in your Claude Desktop config");
process.exit(1);
}
// API call helper
async function propertydataCall(endpoint, params) {
const url = new URL(`${BASE_URL}${endpoint}`);
url.searchParams.append("key", API_KEY);
Object.entries(params).forEach(([key, value]) => {
if (value !== null && value !== undefined) {
url.searchParams.append(key, String(value));
}
});
try {
const response = await fetch(url.toString());
if (!response.ok) {
const errorText = await response.text();
return { error: `API Error ${response.status}: ${errorText}` };
}
return await response.json();
}
catch (error) {
return { error: `Request failed: ${error}` };
}
}
// Define all tools
const tools = [
// ADDRESS & PROPERTY LOOKUP
{
name: "address_match_uprn",
description: "Match a UK property address to its Unique Property Reference Number (UPRN)",
inputSchema: {
type: "object",
properties: {
address: { type: "string", description: "Full property address to match UPRN" }
},
required: ["address"]
}
},
{
name: "get_uprn_data",
description: "Get property data using Unique Property Reference Number",
inputSchema: {
type: "object",
properties: {
uprn: { type: "string", description: "Unique Property Reference Number" }
},
required: ["uprn"]
}
},
{
name: "get_uprns",
description: "Get all UPRNs in a postcode area",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode to get UPRNs for" }
},
required: ["postcode"]
}
},
{
name: "get_uprn_title",
description: "Get land registry title information for a UPRN",
inputSchema: {
type: "object",
properties: {
uprn: { type: "string", description: "Unique Property Reference Number" }
},
required: ["uprn"]
}
},
{
name: "get_title_data",
description: "Get land registry title data for a postcode area",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for title data" }
},
required: ["postcode"]
}
},
{
name: "get_title_use_class",
description: "Get planning use class data from land registry titles",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for use class data" }
},
required: ["postcode"]
}
},
// AREA ANALYSIS
{
name: "get_estate_agents",
description: "Find estate agents operating in a specific postcode area",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode to search estate agents" }
},
required: ["postcode"]
}
},
{
name: "analyse_buildings",
description: "Get building analysis data for a postcode area",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode of the area to analyze" }
},
required: ["postcode"]
}
},
{
name: "get_area_type",
description: "Get area type classification (urban, suburban, rural etc.)",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for area type data" }
},
required: ["postcode"]
}
},
{
name: "get_postcode_key_stats",
description: "Get key statistics summary for a postcode area",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for key statistics" }
},
required: ["postcode"]
}
},
{
name: "get_demographics",
description: "Get demographic data for a postcode area",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for demographic data" }
},
required: ["postcode"]
}
},
{
name: "get_population_data",
description: "Get population data for a postcode area",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for population data" }
},
required: ["postcode"]
}
},
{
name: "get_household_income",
description: "Get household income data for a postcode area",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for household income data" }
},
required: ["postcode"]
}
},
{
name: "get_politics_data",
description: "Get political/voting data for a postcode area",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for political data" }
},
required: ["postcode"]
}
},
// PROPERTY PRICES & VALUATIONS
{
name: "get_prices",
description: "Get current property prices for a postcode area",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for property prices" }
},
required: ["postcode"]
}
},
{
name: "get_prices_per_sqf",
description: "Get property prices per square foot for a postcode area",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for price per sqf data" }
},
required: ["postcode"]
}
},
{
name: "get_sold_prices",
description: "Get historical sold prices for a postcode area",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for sold prices data" }
},
required: ["postcode"]
}
},
{
name: "get_sold_prices_per_sqf",
description: "Get historical sold prices per square foot for a postcode area",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for sold prices per sqf data" }
},
required: ["postcode"]
}
},
{
name: "get_valuation_sale",
description: "Get sale valuation estimate for a postcode area or specific address",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for valuation" },
address: { type: "string", description: "Specific address for valuation (optional)" }
},
required: ["postcode"]
}
},
{
name: "get_valuation_rent",
description: "Get rental valuation estimate for a postcode area or specific address",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for rental valuation" },
address: { type: "string", description: "Specific address for valuation (optional)" }
},
required: ["postcode"]
}
},
{
name: "get_valuation_hmo",
description: "Get HMO (House in Multiple Occupation) valuation estimate",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for HMO valuation" },
address: { type: "string", description: "Specific address for valuation (optional)" }
},
required: ["postcode"]
}
},
{
name: "get_valuation_historical",
description: "Get historical valuation data for a postcode area or specific address",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for historical valuations" },
address: { type: "string", description: "Specific address for valuation (optional)" }
},
required: ["postcode"]
}
},
{
name: "get_growth_data",
description: "Get property price growth data for a postcode area",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for growth data" }
},
required: ["postcode"]
}
},
{
name: "get_growth_psf",
description: "Get property price growth per square foot for a postcode area",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for growth per sqf data" }
},
required: ["postcode"]
}
},
// RENTAL MARKET
{
name: "get_rental_demand",
description: "Get property rental demand analytics for a postcode area",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for rental demand data" }
},
required: ["postcode"]
}
},
{
name: "get_rental_demand_rent",
description: "Get rental demand with rent data for a postcode area",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for rental demand and rent data" }
},
required: ["postcode"]
}
},
{
name: "get_rents",
description: "Get rental prices for a postcode area",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for rental prices" }
},
required: ["postcode"]
}
},
{
name: "get_rents_hmo",
description: "Get HMO rental prices for a postcode area",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for HMO rental prices" }
},
required: ["postcode"]
}
},
{
name: "get_yields",
description: "Get rental yield data for a postcode area",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for rental yields" }
},
required: ["postcode"]
}
},
{
name: "get_lha_rate",
description: "Get Local Housing Allowance rates for a postcode area",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for LHA rates" }
},
required: ["postcode"]
}
},
// DEVELOPMENT & INVESTMENT
{
name: "get_sourced_properties",
description: "Get sourced investment properties for a postcode area",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for sourced properties" }
},
required: ["postcode"]
}
},
{
name: "get_sourced_property",
description: "Get sourced property data for a specific UPRN",
inputSchema: {
type: "object",
properties: {
uprn: { type: "string", description: "Unique Property Reference Number" }
},
required: ["uprn"]
}
},
{
name: "development_calculator",
description: "Calculate development metrics for a postcode area",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for development calculation" },
build_cost: { type: "number", description: "Build cost per unit (optional)" },
land_cost: { type: "number", description: "Land acquisition cost (optional)" }
},
required: ["postcode"]
}
},
{
name: "get_development_gdv",
description: "Get Gross Development Value estimates for a postcode area",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for GDV data" }
},
required: ["postcode"]
}
},
{
name: "get_build_cost",
description: "Get building cost estimates for a postcode area",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for build cost data" }
},
required: ["postcode"]
}
},
{
name: "get_rebuild_cost",
description: "Get rebuild cost estimates for insurance purposes",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for rebuild cost data" }
},
required: ["postcode"]
}
},
// PLANNING & REGULATIONS
{
name: "get_planning_applications",
description: "Get planning applications for a postcode area",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for planning applications" }
},
required: ["postcode"]
}
},
{
name: "get_conservation_area",
description: "Check if postcode area is in a conservation area",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode to check conservation area status" }
},
required: ["postcode"]
}
},
{
name: "get_listed_buildings",
description: "Get listed buildings data for a postcode area",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for listed buildings data" }
},
required: ["postcode"]
}
},
{
name: "get_green_belt",
description: "Check if postcode area is in green belt land",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode to check green belt status" }
},
required: ["postcode"]
}
},
{
name: "get_aonb_data",
description: "Check if postcode area is in Area of Outstanding Natural Beauty",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode to check AONB status" }
},
required: ["postcode"]
}
},
{
name: "get_national_park",
description: "Check if postcode area is in a National Park",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode to check National Park status" }
},
required: ["postcode"]
}
},
// MARKET & FINANCIAL DATA
{
name: "get_council_tax",
description: "Get council tax information for a postcode area",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for council tax data" }
},
required: ["postcode"]
}
},
{
name: "mortgage_calculator",
description: "Calculate mortgage payments and affordability",
inputSchema: {
type: "object",
properties: {
loan_amount: { type: "number", description: "Loan amount in GBP" },
deposit: { type: "number", description: "Deposit amount in GBP" },
interest_rate: { type: "number", description: "Annual interest rate as percentage" },
term_years: { type: "integer", description: "Mortgage term in years" }
},
required: ["loan_amount", "deposit", "interest_rate", "term_years"]
}
},
{
name: "get_mortgage_rates",
description: "Get current mortgage rates for a postcode area",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for mortgage rates" }
},
required: ["postcode"]
}
},
{
name: "stamp_duty_calculator",
description: "Calculate stamp duty liability",
inputSchema: {
type: "object",
properties: {
property_value: { type: "number", description: "Property value in GBP" },
first_time_buyer: { type: "boolean", description: "Whether buyer is first-time buyer (optional)" }
},
required: ["property_value"]
}
},
// PROPERTY CHARACTERISTICS
{
name: "get_floor_areas",
description: "Get floor area data for properties in a postcode area",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for floor area data" }
},
required: ["postcode"]
}
},
{
name: "get_freeholds",
description: "Get freehold/leasehold data for a postcode area",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for freehold data" }
},
required: ["postcode"]
}
},
{
name: "get_energy_efficiency",
description: "Get energy efficiency (EPC) data for a postcode area",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for energy efficiency data" }
},
required: ["postcode"]
}
},
// LOCATION & AMENITIES
{
name: "get_flood_risk",
description: "Get flood risk information for a postcode area",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for flood risk data" }
},
required: ["postcode"]
}
},
{
name: "get_crime_data",
description: "Get crime statistics for a postcode area",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for crime data" }
},
required: ["postcode"]
}
},
{
name: "get_schools_data",
description: "Get schools information for a postcode area",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for schools data" }
},
required: ["postcode"]
}
},
{
name: "get_ptal_data",
description: "Get Public Transport Accessibility Level (PTAL) data for a postcode area",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for PTAL data" }
},
required: ["postcode"]
}
},
{
name: "get_restaurants",
description: "Get restaurants and dining data for a postcode area",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for restaurants data" }
},
required: ["postcode"]
}
},
{
name: "get_internet_speed",
description: "Get internet speed data for a postcode area",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for internet speed data" }
},
required: ["postcode"]
}
},
// REGISTERS & DATABASES
{
name: "get_national_hmo_register",
description: "Get HMO license data from national HMO register",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for HMO register data" }
},
required: ["postcode"]
}
},
// DOCUMENTS & RECORDS
{
name: "get_land_registry_documents",
description: "Get Land Registry documents for a postcode area",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for Land Registry documents" }
},
required: ["postcode"]
}
},
{
name: "get_site_plan_documents",
description: "Get site plan documents for a postcode area",
inputSchema: {
type: "object",
properties: {
postcode: { type: "string", description: "UK postcode for site plan documents" }
},
required: ["postcode"]
}
},
// ACCOUNT MANAGEMENT
{
name: "get_account_credits",
description: "Get current API account credits and usage",
inputSchema: {
type: "object",
properties: {}
}
},
{
name: "get_account_documents",
description: "Get account documents and downloads",
inputSchema: {
type: "object",
properties: {}
}
}
];
// Tool handler mapping
const toolHandlers = {
// ADDRESS & PROPERTY LOOKUP
address_match_uprn: (args) => propertydataCall("/address-match-uprn", { address: args.address }),
get_uprn_data: (args) => propertydataCall("/uprn", { uprn: args.uprn }),
get_uprns: (args) => propertydataCall("/uprns", { postcode: args.postcode }),
get_uprn_title: (args) => propertydataCall("/uprn-title", { uprn: args.uprn }),
get_title_data: (args) => propertydataCall("/title", { postcode: args.postcode }),
get_title_use_class: (args) => propertydataCall("/title-use-class", { postcode: args.postcode }),
// AREA ANALYSIS
get_estate_agents: (args) => propertydataCall("/agents", { postcode: args.postcode }),
analyse_buildings: (args) => propertydataCall("/analyse-buildings", { postcode: args.postcode }),
get_area_type: (args) => propertydataCall("/area-type", { postcode: args.postcode }),
get_postcode_key_stats: (args) => propertydataCall("/postcode-key-stats", { postcode: args.postcode }),
get_demographics: (args) => propertydataCall("/demographics", { postcode: args.postcode }),
get_population_data: (args) => propertydataCall("/population", { postcode: args.postcode }),
get_household_income: (args) => propertydataCall("/household-income", { postcode: args.postcode }),
get_politics_data: (args) => propertydataCall("/politics", { postcode: args.postcode }),
// PROPERTY PRICES & VALUATIONS
get_prices: (args) => propertydataCall("/prices", { postcode: args.postcode }),
get_prices_per_sqf: (args) => propertydataCall("/prices-per-sqf", { postcode: args.postcode }),
get_sold_prices: (args) => propertydataCall("/sold-prices", { postcode: args.postcode }),
get_sold_prices_per_sqf: (args) => propertydataCall("/sold-prices-per-sqf", { postcode: args.postcode }),
get_valuation_sale: (args) => propertydataCall("/valuation-sale", {
postcode: args.postcode,
address: args.address
}),
get_valuation_rent: (args) => propertydataCall("/valuation-rent", {
postcode: args.postcode,
address: args.address
}),
get_valuation_hmo: (args) => propertydataCall("/valuation-hmo", {
postcode: args.postcode,
address: args.address
}),
get_valuation_historical: (args) => propertydataCall("/valuation-historical", {
postcode: args.postcode,
address: args.address
}),
get_growth_data: (args) => propertydataCall("/growth", { postcode: args.postcode }),
get_growth_psf: (args) => propertydataCall("/growth-psf", { postcode: args.postcode }),
// RENTAL MARKET
get_rental_demand: (args) => propertydataCall("/demand", { postcode: args.postcode }),
get_rental_demand_rent: (args) => propertydataCall("/demand-rent", { postcode: args.postcode }),
get_rents: (args) => propertydataCall("/rents", { postcode: args.postcode }),
get_rents_hmo: (args) => propertydataCall("/rents-hmo", { postcode: args.postcode }),
get_yields: (args) => propertydataCall("/yields", { postcode: args.postcode }),
get_lha_rate: (args) => propertydataCall("/lha-rate", { postcode: args.postcode }),
// DEVELOPMENT & INVESTMENT
get_sourced_properties: (args) => propertydataCall("/sourced-properties", { postcode: args.postcode }),
get_sourced_property: (args) => propertydataCall("/sourced-property", { uprn: args.uprn }),
development_calculator: (args) => propertydataCall("/development-calculator", {
postcode: args.postcode,
build_cost: args.build_cost,
land_cost: args.land_cost
}),
get_development_gdv: (args) => propertydataCall("/development-gdv", { postcode: args.postcode }),
get_build_cost: (args) => propertydataCall("/build-cost", { postcode: args.postcode }),
get_rebuild_cost: (args) => propertydataCall("/rebuild-cost", { postcode: args.postcode }),
// PLANNING & REGULATIONS
get_planning_applications: (args) => propertydataCall("/planning-applications", { postcode: args.postcode }),
get_conservation_area: (args) => propertydataCall("/conservation-area", { postcode: args.postcode }),
get_listed_buildings: (args) => propertydataCall("/listed-buildings", { postcode: args.postcode }),
get_green_belt: (args) => propertydataCall("/green-belt", { postcode: args.postcode }),
get_aonb_data: (args) => propertydataCall("/aonb", { postcode: args.postcode }),
get_national_park: (args) => propertydataCall("/national-park", { postcode: args.postcode }),
// MARKET & FINANCIAL DATA
get_council_tax: (args) => propertydataCall("/council-tax", { postcode: args.postcode }),
mortgage_calculator: (args) => propertydataCall("/mortgage-calculator", {
loan_amount: args.loan_amount,
deposit: args.deposit,
interest_rate: args.interest_rate,
term_years: args.term_years
}),
get_mortgage_rates: (args) => propertydataCall("/mortgage-rates", { postcode: args.postcode }),
stamp_duty_calculator: (args) => propertydataCall("/stamp-duty-calculator", {
property_value: args.property_value,
first_time_buyer: args.first_time_buyer
}),
// PROPERTY CHARACTERISTICS
get_floor_areas: (args) => propertydataCall("/floor-areas", { postcode: args.postcode }),
get_freeholds: (args) => propertydataCall("/freeholds", { postcode: args.postcode }),
get_energy_efficiency: (args) => propertydataCall("/energy-efficiency", { postcode: args.postcode }),
// LOCATION & AMENITIES
get_flood_risk: (args) => propertydataCall("/flood-risk", { postcode: args.postcode }),
get_crime_data: (args) => propertydataCall("/crime", { postcode: args.postcode }),
get_schools_data: (args) => propertydataCall("/schools", { postcode: args.postcode }),
get_ptal_data: (args) => propertydataCall("/ptal", { postcode: args.postcode }),
get_restaurants: (args) => propertydataCall("/restaurants", { postcode: args.postcode }),
get_internet_speed: (args) => propertydataCall("/internet-speed", { postcode: args.postcode }),
// REGISTERS & DATABASES
get_national_hmo_register: (args) => propertydataCall("/national-hmo-register", { postcode: args.postcode }),
// DOCUMENTS & RECORDS
get_land_registry_documents: (args) => propertydataCall("/land-registry-documents", { postcode: args.postcode }),
get_site_plan_documents: (args) => propertydataCall("/site-plan-documents", { postcode: args.postcode }),
// ACCOUNT MANAGEMENT
get_account_credits: () => propertydataCall("/account/credits", {}),
get_account_documents: () => propertydataCall("/account/documents", {})
};
// Create server
const server = new Server({
name: "propertydata-mcp",
version: "1.0.0",
}, {
capabilities: {
tools: {},
},
});
// List tools handler
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools,
}));
// Call tool handler
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
const handler = toolHandlers[name];
if (!handler) {
throw new Error(`Unknown tool: ${name}`);
}
try {
const result = await handler(args || {});
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
}
catch (error) {
return {
content: [
{
type: "text",
text: `Error: ${error}`,
},
],
isError: true,
};
}
});
// Start server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("PropertyData MCP Server running on stdio");
}
main().catch((error) => {
console.error("Server error:", error);
process.exit(1);
});
//# sourceMappingURL=index.js.map