Files
nadlan-mcp/MCP_NORMALIZATION_FIX.md
T
Nitzan P e791d3c1ed Normalize MCP response structures across all tools
Implement changes from MCP_NORMALIZATION_FIX.md to provide consistent
response structure across all MCP tools. This fixes the bot integration
issue where different tools returned data in different structures.

Changes:
- get_valuation_comparables:
  - Rename "comparables" → "deals"
  - Move "total_comparables" → "market_statistics.deal_breakdown.total_deals"
  - Add "search_parameters" section with "filters_applied"
  - Move "statistics" → "market_statistics"

- find_recent_deals_for_address:
  - Rename "price_stats.average_price" → "price_statistics.mean"
  - Rename "price_stats.median_price" → "price_statistics.median"
  - Rename "area_stats" → "area_statistics"
  - Rename "price_per_sqm_stats" → "price_per_sqm_statistics"

- get_deal_statistics:
  - Add "search_parameters" section
  - Move "statistics" → "market_statistics"
  - Add "market_statistics.deal_breakdown.total_deals"

- analyze_market_trends:
  - Add "market_statistics.deal_breakdown.total_deals"
  - Keep existing tool-specific fields (yearly_trends, etc.)

- get_market_activity_metrics:
  - Add "market_statistics.deal_breakdown.total_deals"
  - Keep existing tool-specific metrics

All tools now follow standard structure:
{
  "search_parameters" or "analysis_parameters": {...},
  "market_statistics": {
    "deal_breakdown": {"total_deals": N},
    "price_statistics": {"mean": ..., "median": ...},
    "area_statistics": {...},
    "price_per_sqm_statistics": {...}
  },
  "deals": [...]
}

Updated tests to match new normalized structure.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-23 09:49:52 +02:00

9.0 KiB

Instructions for nadlan-mcp Project: Normalize Response Structure

Problem

Different MCP tools return data in inconsistent structures, causing the bot to fail when interpreting results.

Current Inconsistent Structures

get_valuation_comparables returns:

{
  "total_comparables": 3,
  "statistics": {
    "total_deals": 3,
    "price_statistics": { "mean": ..., "median": ... }
  },
  "comparables": [...]  // ← Array of deals
}

find_recent_deals_for_address returns:

{
  "market_statistics": {
    "deal_breakdown": {
      "total_deals": 4
    },
    "price_stats": { "average_price": ..., "median_price": ... }
  },
  "deals": [...]  // ← Different field name!
}

The Impact

The bot code looks for:

  • Deal count in: market_statistics.deal_breakdown.total_deals
  • Deal array in: deals

When get_valuation_comparables is called:

  • Bot looks for deals array → finds nothing (field is called comparables)
  • Bot looks for market_statistics.deal_breakdown.total_deals → finds nothing (field is total_comparables or statistics.total_deals)
  • Bot thinks there are 0 deals even when MCP returned 20 deals
  • User gets: "לא מצאתי עסקאות ספציפיות" (I didn't find specific deals)

Solution: Normalize All MCP Tool Responses

All MCP tools should return data in a consistent structure. I recommend standardizing on this format:

{
  "search_parameters": {
    // Tool-specific search parameters that were used
    "address": "...",
    "years_back": 2,
    // ... other params
  },
  "market_statistics": {
    "deal_breakdown": {
      "total_deals": N,              // ← Always here
      "same_building_deals": 0,      // Optional: only if relevant
      "street_deals": 0,             // Optional: only if relevant
      "neighborhood_deals": 0        // Optional: only if relevant
    },
    "price_statistics": {            // ← Standardize field name
      "mean": 1750000.0,             // Use "mean" not "average_price"
      "median": 1800000.0,           // Use "median" not "median_price"
      "min": 1443000.0,
      "max": 2100000.0,
      "p25": 1500000.0,
      "p75": 2000000.0,
      "std_dev": 250000.0,
      "total": 7000000.0
    },
    "area_statistics": {             // ← Standardize field name
      "mean": 68.8,                  // Use "mean" not "average_area"
      "median": 76.0,
      "min": 42.0,
      "max": 106.0,
      "p25": 50.0,
      "p75": 90.0
    },
    "price_per_sqm_statistics": {    // ← Standardize field name
      "mean": 27892.0,               // Use "mean" not "average_price_per_sqm"
      "median": 34357.0,
      "min": 19811.0,
      "max": 35294.0,
      "p25": 25000.0,
      "p75": 32000.0
    },
    "property_type_distribution": {  // Optional
      "דירה": 20,
      "בית": 5
    },
    "date_range": {                  // Optional
      "earliest": "2024-01-31",
      "latest": "2025-11-05"
    }
  },
  "deals": [                         // ← Always "deals", never "comparables"
    {
      "objectid": 1975198,
      "deal_amount": 1800000.0,
      "deal_date": "2025-08-21",
      "asset_area": 51.0,
      "settlement_name_heb": "חולון",
      "property_type_description": "דירה",
      "neighborhood": "קרית עבודה",
      "rooms": 3.0,
      "streetNameHeb": "חנקין",
      "streetNameEng": "Hankin",
      "houseNum": 62,
      "floorNo": "שניה",
      "price_per_sqm": 35294.12,
      "deal_source": "street",       // Optional: same_building, street, neighborhood
      "priority": 1,                 // Optional: relevance ranking
      // ... other fields
    }
  ]
}

Specific Changes Needed

1. get_valuation_comparables Tool

Current response:

{
  "total_comparables": 3,
  "statistics": { "total_deals": 3, "price_statistics": {...} },
  "comparables": [...]
}

Should be changed to:

{
  "search_parameters": {
    "address": "חנקין 62 חולון",
    "years_back": 2,
    "filters_applied": {
      "property_type": null,
      "rooms": "2.5-3.5",
      "price": null,
      "area": null,
      "floor": null
    },
    "radius_meters": 100,
    "max_comparables": 50
  },
  "market_statistics": {
    "deal_breakdown": {
      "total_deals": 3              // ← Move from "total_comparables"
    },
    "price_statistics": {           // ← Keep, matches standard
      "mean": 1743333.33,
      "median": 1750000.0,
      "min": 1680000.0,
      "max": 1800000.0,
      "p25": 1680000.0,
      "p75": 1800000.0,
      "std_dev": 60277.14
    },
    "area_statistics": {...},       // ← Keep
    "price_per_sqm_statistics": {...} // ← Keep
  },
  "deals": [...]                    // ← Rename from "comparables"
}

Changes:

  1. Remove total_comparables field
  2. Add market_statistics.deal_breakdown.total_deals instead
  3. Rename comparablesdeals
  4. Move statisticsmarket_statistics
  5. Add search_parameters section with filters_applied

2. find_recent_deals_for_address Tool

Current response: Already follows the standard!

Keep as-is, except:

  • Rename price_stats.average_priceprice_statistics.mean
  • Rename price_stats.median_priceprice_statistics.median
  • Rename area_statsarea_statistics
  • Rename price_per_sqm_statsprice_per_sqm_statistics

Should return:

{
  "analysis_parameters": {
    "address": "...",
    "years_back": 3
  },
  "market_statistics": {           // ← Add this
    "deal_breakdown": {
      "total_deals": 50             // ← Move from market_summary
    },
    "price_statistics": {...},      // ← From trend analysis
    "area_statistics": {...}
  },
  "yearly_trends": {...},           // Keep tool-specific data
  "trend_analysis": {...},          // Keep tool-specific data
  "deals": [...]                    // Optional: sample deals
}

4. get_deal_statistics Tool

Should return:

{
  "search_parameters": {...},
  "market_statistics": {           // ← Rename from "statistics"
    "deal_breakdown": {
      "total_deals": 25
    },
    "price_statistics": {...},
    "area_statistics": {...},
    "price_per_sqm_statistics": {...}
  },
  "deals": []                      // Can be empty for statistics-only queries
}

5. get_market_activity_metrics Tool

Should return:

{
  "analysis_parameters": {...},
  "market_statistics": {
    "deal_breakdown": {
      "total_deals": 20             // ← Move from total_deals_analyzed
    }
  },
  "market_activity": {              // Keep tool-specific metrics
    "activity_score": 75.0,
    "liquidity_score": 80.0,
    ...
  },
  "investment_potential": {...},    // Keep tool-specific metrics
  "deals": []                       // Optional
}

Benefits of Normalization

  1. Bot compatibility: Bot can use the same code path for all tools
  2. Consistency: Developers always know where to find deal count and deal array
  3. Maintainability: Adding new tools is easier with a standard structure
  4. Testing: Can write generic tests that work for all tools
  5. Documentation: Single structure to document instead of 5+ different formats

Implementation Checklist

For each MCP tool that returns deals:

  • Add market_statistics.deal_breakdown.total_deals field
  • Ensure deals array is named deals (not comparables, etc.)
  • Standardize statistics field names:
    • price_statistics with mean, median, min, max, p25, p75
    • area_statistics with same structure
    • price_per_sqm_statistics with same structure
  • Add search_parameters or analysis_parameters section
  • Keep tool-specific fields (like yearly_trends, market_activity, etc.) but always include the standard structure
  • Test with bot to verify it correctly interprets results

Migration Strategy

Option 1: Breaking change (recommended)

  • Update all tools at once to use new structure
  • Update bot to expect new structure
  • Deploy both together

Option 2: Backward compatible

  • Return BOTH old and new fields temporarily:
    {
      "total_comparables": 3,           // Old (deprecated)
      "comparables": [...],             // Old (deprecated)
      "market_statistics": {            // New (standard)
        "deal_breakdown": {
          "total_deals": 3
        }
      },
      "deals": [...]                    // New (standard)
    }
    
  • Update bot to prefer new fields, fall back to old
  • Remove old fields after bot is updated

I recommend Option 1 since this is an internal MCP server with a single client (the bot).


Testing After Changes

Once normalized, test with the bot:

User: "כמה עולה דירת 3 חדרים בחנקין 62 חולון?"
Expected: Bot should return actual deals and prices, NOT "לא מצאתי עסקאות"

Verify in bot logs:

Total deals found: 20  // Should show actual count
Sample deals (most recent 10):
  1. חנקין 62 חולון - ₪1,800,000 - 3 rooms - 51 sqm - 2025-08-21
  ...