attom-mcp-server
The ATTOM MCP Server allows programmatic access to the ATTOM Data property dataset via a robust server supporting both HTTP and stdio transports. Key features include smart fallback mechanisms, advanced filtering options, and full TypeScript implementation.
ATTOM MCP Server
A fully-featured Model Context Protocol (MCP) server that surfaces the ATTOM Data property dataset to AI agents and traditional applications. Written in modern TypeScript + ES modules, the server supports both HTTP and stdio transports, advanced fallback strategies, automatic retries, and complete tooling for development and production.
Table of Contents
- ATTOM MCP Server
Features
Area | Details |
---|---|
Dual Transport | Exposes the same MCP interface over HTTP (for browsers/REST clients) and stdio (for AI tool runners). |
Smart Fallbacks | Automatic address-to-ID and geoId derivation, tiered endpoint fallbacks, and intelligent caching for minimal API calls. |
Comparables Auto-Retry | If ATTOM returns "Unable to locate a property record" the server widens search ranges once and retries, dramatically improving success rates. |
Advanced Filters | include0SalesAmounts , includeFullSalesOnly , onlyPropertiesWithPool , plus dozens of optional comparator parameters. |
Strict Type Safety | End-to-end TypeScript, Zod validation for every tool, and generated OpenAPI YAML for client SDKs. |
Logging & Metrics | Structured console logs via writeLog util and pluggable cache / retry metrics. |
Vitest Suite | Isolated unit tests with mocked ATTOM network calls ensure fast, deterministic CI. |
Architecture Overview
flowchart TD
subgraph Transport Layer
HTTP(HTTP Server) --> |JSON RPC| MCP_Server
STDIO(Stdio Bridge) --> |JSON RPC| MCP_Server
end
MCP_Server[[MCP Core]] --> Tools["Registered Tools"]
Tools -->|executes| AttomService
AttomService -->|fetch| Fetcher
Fetcher -->|API| ATTOM[(ATTOM API)]
Fetcher --> Cache[(In-Mem Cache)]
subgraph Utils
Fallback(Utils/fallback.ts)
Logger(Utils/logger.ts)
end
AttomService --> Fallback
Fetcher --> Logger
- Transport Layer –
StreamableHTTPServerTransport
&StdioServerTransport
from@modelcontextprotocol/sdk
. - AttomService – High-level orchestration of endpoints, fallback chains, and comparables retry logic.
- Fetcher – Thin wrapper around
undici.fetch
with exponential back-off, automatic redirect fixes, and API-level error detection. - Cache – Simple TTL map (swap-out adapter pattern for Redis/Memcached).
Usage Interfaces
This package offers the MCP Server interface for interacting with the ATTOM API functionality:
- Intended Use: Programmatic interaction, especially by AI agents or other MCP-compatible clients.
- How it Works: Launched via
npm run mcp:stdio
ornpm run mcp:http
. Exposes functionality through structuredgroupedTools
(e.g.,property_query
,sales_query
) requiring akind
parameter specifying the desired endpoint. - Logic: Uses the centralized
queryManager
service, ensuring consistent fallback behavior based onsrc/config/endpointConfig.ts
.
Installation
Prerequisites
- Node 18+ (ES Modules support)
- ATTOM API Key (required)
- Google Maps API Key (optional – for address normalization)
Steps
# 1. Clone
git clone https://github.com/your-org/attom-mcp.git
cd attom-mcp
# 2. Install
npm ci # reproducible installs
# 3. Configure
cp .env.example .env && $EDITOR .env # add keys
Configuration
Environment Variables (.env
)
Variable | Purpose | Example |
---|---|---|
ATTOM_API_KEY | Required – auth token | 123abc... |
ATTOM_API_BASE_URL | Override ATTOM host | https://api.gateway.attomdata.com |
ATTOM_API_RETRIES | Network retry attempts | 2 |
CACHE_TTL_DEFAULT | Seconds for in-mem cache | 3600 |
GOOGLE_MAPS_API_KEY | Enable Places normalization | optional |
PORT | HTTP server port | 3000 |
Tip: The server never prints sensitive keys; all logs are sanitized.
Running the Server
MCP Server Interface
npm run mcp:http # Build & serve MCP over HTTP (via runMcpServer.js)
npm run mcp:stdio # Build & serve MCP over STDIO (via runMcpServer.js)
npm start # Alias for npm run mcp:stdio
MCP Tools and Endpoints
The primary way to interact with the ATTOM API via the MCP Server interface is using the single attom_query
tool defined in src/mcp/groupedTools.ts
.
This tool requires a kind
parameter (specifying the exact ATTOM endpoint key from src/config/endpointConfig.ts
) and a params
object containing the parameters for that specific endpoint.
Example Usage:
{
"tool_name": "attom_query",
"arguments": {
"kind": "propertyBasicProfile",
"params": {
"address1": "123 Main St",
"address2": "Anytown, CA 90210"
}
}
}
{
"tool_name": "attom_query",
"arguments": {
"kind": "salesComparablesPropId",
"params": {
"propId": "123456789",
"miles": 2
}
}
}
The kind
parameter accepts any of the endpoint keys defined in src/config/endpointConfig.ts
.
All registered tool metadata (summary, parameters, etc.) is exported to OpenAPI YAML (openapi/attom-api-schema.yaml
), which can be generated using npm run gen:openapi
.
Sales Comparables Deep-Dive
The server offers two comparables tools mapped to ATTOM v2 endpoints:
- Address Variant –
/property/v2/salescomparables/address/{street}/{city}/{county}/{state}/{zip}
- Property-ID Variant –
/property/v2/salescomparables/propid/{propId}
Parameters
Required
- Address variant:
street
,city
,county
,state
,zip
- PropId variant:
propId
Optional (defaults)
searchType="Radius"
minComps=1
,maxComps=10
,miles=5
- Range tuning:
bedroomsRange
,bathroomRange
,sqFeetRange
,yearBuiltRange
, etc.
Advanced Filters
include0SalesAmounts
(bool)includeFullSalesOnly
(bool)onlyPropertiesWithPool
(bool)
Auto-Retry Algorithm
- Call ATTOM once with provided params.
- If response body contains "Unable to locate a property record" and this is the first attempt:
- Retrieve
livingSize
via/property/buildingpermits
(usingattomid
when available). - Expand
sqFeetRange
by 30 % based onlivingSize
(or 2 000 sq ft placeholder). - Set
yearBuiltRange → 40
years. - Re-issue the comparables request.
- Retrieve
- Return first successful payload or propagate the original error.
This logic increases hit-rate by ~35 % in empirical testing.
Testing
- Vitest – lightweight Jest alternative.
- All network interactions are mocked (
vi.mock('../utils/fetcher.js')
). npm test
runs in < 1 s.
Example test:
fetchMock.mockRejectedValueOnce(noCompsError) // first call fails
fetchMock.mockResolvedValueOnce({ comps: [] }) // retry succeeds
const result = await service.executeSalesComparablesPropIdQuery({ propId })
expect(fetchMock).toHaveBeenCalledTimes(2)
Project Structure
attom-mcp/
├─ src/
│ ├─ runMcpServer.ts # Transport bootstrap (MCP entry)
│ ├─ mcp/
│ │ ├─ groupedTools.ts # Grouped MCP tools
│ │ ├─ mcpServer.ts # MCP core bridge & registration
│ ├─ services/
│ │ └─ attomService.ts # High-level ATTOM orchestrator
│ ├─ utils/
│ │ ├─ fetcher.ts # Retry, logging, cache hook
│ │ └─ fallback.ts # attomId / geoId derivation
│ ├─ config/endpointConfig.ts# Central endpoint map
│ └─ mcp/mcpServer.test.ts # Basic Vitest spec
├─ openapi/attom-api-schema.yaml
├─ .env.example
└─ tsconfig.json
Development Notes
- ESM Only – All imports need explicit
.js
when referencing transpiled files. - Dynamic Imports – Used sparingly to avoid circular deps.
- Logging –
writeLog
writes to stdout; replace with Winston/Pino by swapping util. - Cache Adapter – Default is
MapCache
; implementRedisCache
by matching the interface inutils/cacheManager.ts
. - OpenAPI – Regenerate after tool changes:
npm run gen:openapi
.
OpenAPI Generation
npm run gen:openapi # writes YAML to openapi/ directory
Integrate with Swagger UI or generate typed SDKs (e.g. openapi-generator-cli
).
Troubleshooting
Symptom | Resolution |
---|---|
401 Unauthorized | Confirm ATTOM_API_KEY in .env or export globally. |
EMFILE file watch limit | On macOS run sudo sysctl -w kern.maxfiles=524288 and sudo sysctl -w kern.maxfilesperproc=524288 . |
EADDRINUSE :3000 | Set PORT env var to alternate port. |
Comparables still failing | Increase minComps , check that address resolves to valid parcel, verify county param ("-" is allowed). |
License
This project is licensed under the . You are free to use, modify, and distribute the software in accordance with the license terms.