attom-mcp-server

attom-mcp-server

0

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


Features

AreaDetails
Dual TransportExposes the same MCP interface over HTTP (for browsers/REST clients) and stdio (for AI tool runners).
Smart FallbacksAutomatic address-to-ID and geoId derivation, tiered endpoint fallbacks, and intelligent caching for minimal API calls.
Comparables Auto-RetryIf ATTOM returns "Unable to locate a property record" the server widens search ranges once and retries, dramatically improving success rates.
Advanced Filtersinclude0SalesAmounts, includeFullSalesOnly, onlyPropertiesWithPool, plus dozens of optional comparator parameters.
Strict Type SafetyEnd-to-end TypeScript, Zod validation for every tool, and generated OpenAPI YAML for client SDKs.
Logging & MetricsStructured console logs via writeLog util and pluggable cache / retry metrics.
Vitest SuiteIsolated 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 LayerStreamableHTTPServerTransport & 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 or npm run mcp:http. Exposes functionality through structured groupedTools (e.g., property_query, sales_query) requiring a kind parameter specifying the desired endpoint.
  • Logic: Uses the centralized queryManager service, ensuring consistent fallback behavior based on src/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)

VariablePurposeExample
ATTOM_API_KEYRequired – auth token123abc...
ATTOM_API_BASE_URLOverride ATTOM hosthttps://api.gateway.attomdata.com
ATTOM_API_RETRIESNetwork retry attempts2
CACHE_TTL_DEFAULTSeconds for in-mem cache3600
GOOGLE_MAPS_API_KEYEnable Places normalizationoptional
PORTHTTP server port3000

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:

  1. Address Variant/property/v2/salescomparables/address/{street}/{city}/{county}/{state}/{zip}
  2. 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

  1. Call ATTOM once with provided params.
  2. If response body contains "Unable to locate a property record" and this is the first attempt:
    1. Retrieve livingSize via /property/buildingpermits (using attomid when available).
    2. Expand sqFeetRange by 30 % based on livingSize (or 2 000 sq ft placeholder).
    3. Set yearBuiltRange → 40 years.
    4. Re-issue the comparables request.
  3. 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

  1. ESM Only – All imports need explicit .js when referencing transpiled files.
  2. Dynamic Imports – Used sparingly to avoid circular deps.
  3. LoggingwriteLog writes to stdout; replace with Winston/Pino by swapping util.
  4. Cache Adapter – Default is MapCache; implement RedisCache by matching the interface in utils/cacheManager.ts.
  5. 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

SymptomResolution
401 UnauthorizedConfirm ATTOM_API_KEY in .env or export globally.
EMFILE file watch limitOn macOS run sudo sysctl -w kern.maxfiles=524288 and sudo sysctl -w kern.maxfilesperproc=524288.
EADDRINUSE :3000Set PORT env var to alternate port.
Comparables still failingIncrease 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.