@@ -3,10 +3,10 @@
|
||||
import json
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
import os
|
||||
from mcp.server.transport_security import TransportSecuritySettings
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
|
||||
from .analysis import analyze_search
|
||||
from .eiendom_no import (
|
||||
build_unit_vector,
|
||||
decode_unit_vector,
|
||||
@@ -20,22 +20,37 @@ from .formatting import (
|
||||
render_diff,
|
||||
render_shortlist,
|
||||
render_similar_units,
|
||||
render_unit_images,
|
||||
)
|
||||
from .service import (
|
||||
analyze_ad,
|
||||
analyze_ad_against_comps,
|
||||
analyze_search,
|
||||
compare_ads,
|
||||
find_similar_to_liked,
|
||||
get_new_ads_since_last_run,
|
||||
get_or_fetch_ad,
|
||||
get_or_fetch_eiendom_unit,
|
||||
get_shortlist,
|
||||
get_unit_images,
|
||||
save_feedback,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
mcp = FastMCP("finn_eiendom_mcp")
|
||||
|
||||
def _build_transport_security() -> TransportSecuritySettings:
|
||||
allowed = os.getenv("MCP_ALLOWED_HOSTS", "")
|
||||
if allowed:
|
||||
hosts = [h.strip() for h in allowed.split(",")]
|
||||
return TransportSecuritySettings(
|
||||
enable_dns_rebinding_protection=True,
|
||||
allowed_hosts=["127.0.0.1:*", "localhost:*", "[::1]:*"] + hosts,
|
||||
)
|
||||
return TransportSecuritySettings(enable_dns_rebinding_protection=False)
|
||||
|
||||
|
||||
mcp = FastMCP("finn_eiendom_mcp", transport_security=_build_transport_security())
|
||||
|
||||
|
||||
@mcp.tool(
|
||||
@@ -56,7 +71,7 @@ async def finn_analyze_search(
|
||||
result = await analyze_search(
|
||||
search_url,
|
||||
max_pages=max_pages,
|
||||
fetch_details=include_details,
|
||||
include_details=include_details,
|
||||
detail_limit=detail_limit,
|
||||
include_eiendom_no=include_eiendom_no,
|
||||
)
|
||||
@@ -125,6 +140,22 @@ async def finn_get_eiendom_unit(unit_code: str, force_refresh: bool = False) ->
|
||||
return json.dumps({"error": True, "message": str(e)})
|
||||
|
||||
|
||||
@mcp.tool(
|
||||
description=(
|
||||
"Fetch and analyze unit images for visual assessment of a property. "
|
||||
"Returns property photos with metadata for evaluating views, condition, and layout."
|
||||
)
|
||||
)
|
||||
async def finn_analyze_unit_images(unit_code: str, force_refresh: bool = False) -> str:
|
||||
"""Fetch and return unit images for visual analysis."""
|
||||
try:
|
||||
result = await get_unit_images(unit_code, force_refresh=force_refresh)
|
||||
return render_unit_images(result, "markdown")
|
||||
except Exception as e:
|
||||
logger.error(f"Error fetching unit images for {unit_code}: {e}")
|
||||
return json.dumps({"error": True, "message": str(e)})
|
||||
|
||||
|
||||
@mcp.tool(
|
||||
description="Fetch comparable recently-sold or for-sale units from Eiendom.no using a "
|
||||
"base64-encoded unit vector. Returns list of similar units with sale prices."
|
||||
@@ -296,7 +327,7 @@ async def finn_get_new_ads_since_last_run(search_url: str) -> str:
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""Run the FastMCP stdio server."""
|
||||
"""Run the FastMCP server over stdio (standard MCP transport)."""
|
||||
mcp.run(transport="stdio")
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user