Skip to main content
Glama
shubhamattri

mcp-gateway

by shubhamattri

Home Intelligence Platform

A set of small FastAPI services, plus an MCP server, that answer one question about a home in Bangalore: does this location pass the checks that matter before you buy?

The checks are commute to a job hub at peak traffic, drive time to a hospital that can handle a cardiac emergency, the number of schools nearby, and market risk signals (builder distress flags, water supply stage, infrastructure stage of a growth corridor). Each check is a hard gate. A location that fails any one gate is rejected, instead of a good score on one axis averaging away a bad score on another.

The city data in services/*/data/ is a small snapshot built from public sources. It is there so the stack runs and the tests pass from a clean clone. See Data for what is in it and what is not.

Quickstart

Requirements: Python 3.12+, uv, Docker (for the full stack).

make install        # uv sync --all-packages
make check          # ruff + mypy --strict + pytest
make compose-up     # Traefik + 4 services + Redis + 2 Postgres + one-shot loaders

With the stack up, everything goes through the gateway on port 8080:

curl -s localhost:8080/dec/areas | head
curl -s -X POST localhost:8080/dec/decision \
  -H 'content-type: application/json' \
  -d '{"address": {"query": "Koramangala"}, "budget_cr": 1.5}'
curl -s localhost:8080/re/corridors
curl -s localhost:8080/hosp/hospitals/quality
curl -s -X POST localhost:8080/sch/schools/shortlist \
  -H 'content-type: application/json' -d '{"area": "HSR"}'

To use it from an MCP client (a desktop assistant or an agent framework), point the client at this command, which talks to the running stack:

PLATFORM_URL=http://localhost:8080 uv run --package mcp-gateway python -m mcp_gateway.server

No API key is needed. Without TOMTOM_API_KEY the services still answer, but drive times come back as null and the commute and hospital gates fail closed, so every verdict is reject. Set the key (see .env.example) to get traffic-aware drive times.

Related MCP server: Cal MCP Server

Architecture

flowchart LR
    agent["LLM agent<br/>(MCP client)"] -->|MCP stdio| mcp["mcp-gateway<br/>FastMCP, 4 read-only tools"]
    user["curl / UI"] -->|REST| gw["Traefik<br/>/dec /re /hosp /sch"]
    mcp -->|REST| gw
    gw --> dec["decision-svc<br/>orchestrator"]
    gw --> re["real-estate-svc"]
    gw --> hosp["hospitals-svc"]
    gw --> sch["schools-svc"]
    dec -->|"POST /score/gates"| re
    dec -->|"POST /reachability"| hosp
    dec -->|"GET /schools/near"| sch
    re --> pgre[("Postgres<br/>real_estate_db")]
    re --> redis[("Redis job queue")]
    worker["real-estate worker"] --> redis
    sch --> pgsch[("Postgres<br/>schools_db")]
    hosp --> files[("JSON snapshot")]
    re -. drive times .-> tomtom["TomTom API<br/>(optional)"]
    hosp -. drive times .-> tomtom

A decision request flows like this:

  1. decision-svc resolves the address (explicit coordinates, or TomTom search with a Nominatim fallback).

  2. It calls the three domain services concurrently with asyncio.gather.

  3. real-estate-svc returns peak-traffic commute minutes to the job hub. hospitals-svc picks the three nearest quality hospitals by straight-line distance, then asks TomTom for real drive times to those three only, which caps paid routing calls at three per request. schools-svc returns schools within a radius.

  4. decision-svc applies the gates and returns buy or reject with the list of failed gates and a short rationale.

Layout

Path

What it is

services/decision

Orchestrator. Owns no data; composes the other three over HTTP.

services/real-estate

RERA project search, commute gate, growth-corridor heuristic, async jobs, valuation and plot-plus-build cost models.

services/hospitals

Hospital quality table, capability scoring (cardiac vs general emergency), golden-hour reachability.

services/schools

School catalogue, radius search, area shortlist, fuzzy name search (pg_trgm in Postgres).

mcp-gateway

MCP server: score_address, growth_corridors, hospital_quality, school_shortlist.

libs/platform_common

Haversine, geocoders, TomTom router, retrying HTTP client with a circuit breaker, settings, JSON logging.

libs/service_kit

FastAPI app factory: /health, /ready, Prometheus /metrics, error-to-HTTP mapping.

libs/contracts

Pydantic v2 request and response models shared by every service.

libs/jobs

Job queue (in-memory and Redis adapters), worker loop, POST /jobs/{task} then poll GET /jobs/{id}.

libs/persistence

Async SQLAlchemy engine, session factory and declarative base.

deploy/compose

Full local stack.

deploy/helm/bhi

Helm chart: Deployment, Service and HPA per service, Postgres StatefulSet per database, Redis, Ingress, ServiceMonitors.

Design decisions

Why separate services. The four domains have different data sources, refresh cadences and storage needs: project registry data suits Postgres with trigram search, hospital capability data is a small curated table, and scraping or geocoding work is slow and rate-limited so it runs through a job queue. Splitting them keeps each one small, lets the slow parts scale or fail on their own, and forces the orchestrator to work only from API responses, which the integration test checks. The honest trade-off: at this data size a single process would be simpler to run. The split is there to show the service boundaries, contracts and deployment shape, not because load demands it.

Why an MCP gateway. Agents get four workflow-level tools instead of a dozen REST endpoints. score_address answers the whole question in one call and returns short Markdown, which is cheaper in tokens than raw JSON. All tools carry readOnlyHint=True. The gateway reaches the platform only through the same public HTTP API as any other client, so it has no direct data access and no write path.

Hard gates, not weighted averages. A cheap price should not cancel out a 70-minute commute. Every gate is pass/fail, and an unknown value (no routing key, no data) fails the gate rather than passing silently.

Unknown is not the same as no. The hospital capability scorer reports missing capabilities in an unknowns list and scores them as zero, instead of assuming a hospital has a cath lab because it is large.

Repository pattern. Real-estate and schools have a file adapter (used by tests and local runs) and a Postgres adapter selected by DATABASE_URL. The Postgres adapters are tested against in-memory SQLite through aiosqlite, so no database is needed for CI.

Resilience in one place. Every outbound call goes through ResilientClient: exponential-backoff retries via tenacity and a circuit breaker that opens after repeated failures.

Deploying with Helm

helm lint deploy/helm/bhi
helm template bhi deploy/helm/bhi | less
helm install bhi deploy/helm/bhi -n bhi --create-namespace

Build and push one image per service first (docker/Dockerfile, with PKG and APP_MODULE build args) and set image.repository in values.yaml. Set metrics.serviceMonitor=false if the Prometheus Operator is not installed. The chart's Postgres credentials are placeholders for local clusters; use a Secret in anything real. More detail in deploy/README.md.

Testing

make check

This runs ruff check, mypy --strict across all packages, and pytest. On the commit that first published this repo, the suite reported 87 passed. The tests make no network calls and need no API keys, Docker, Redis or Postgres:

  • unit tests for scoring logic (gates, corridor heuristic, capability scoring, valuation, plot-plus-build cost) and the job queue;

  • service tests through FastAPI's TestClient against the bundled snapshot;

  • Postgres repository adapters run against aiosqlite;

  • an integration test that runs the orchestrator against the three real service apps wired through ASGI, with no Docker;

  • data-integrity tests that fail if a hospital's coordinates drift more than 3 km from its known location, or if it borrowed coordinates from a different kind of facility (an eye hospital matched by name once moved a cardiac centre 20 km).

CI (.github/workflows/ci.yml) runs the same checks plus ruff format --check, docker compose config validation, and a build of all four service images. It uses no secrets.

Data

File

Source

Notes

schools/data/bengaluru_schools.json

OpenStreetMap and public board school lists

Name, board, area, coordinates. tier is a rough estimate, not a quality rating.

real-estate/data/base_projects.json

Karnataka RERA public registry

A sample of about 350 registrations, up to 12 per area, out of roughly 8,800. Two rows marked fictional exist to exercise the distress flag.

real-estate/data/geocoded_projects.json

TomTom search on project names

Approximate; nearest curated locality.

real-estate/data/builders_distress.json

Fictional

Format example only. Contains no real company.

real-estate/data/emerging_corridors.json, leasing_water.json, appreciation.json

Public news, BWSSB and office-leasing reports

Each entry lists its source URLs. Prices are 2026 snapshots.

hospitals/data/*.json

Public hospital information

Capabilities, accreditation and bed counts for 15 major hospitals; derived quality scores.

The scrapers that produced the full datasets are not part of this repository.

Limitations

  • budget_cr is validated and passed through, but no price gate uses it yet. GateResult.within_budget is always null.

  • Without a TomTom key there are no drive times, so every verdict is reject. Drive times also depend on TomTom's traffic model for the requested departure time.

  • The growth-corridor score is a hand-built heuristic (infrastructure stage x leasing momentum x water stage). It is not a trained model and has no measured error rate.

  • Hospital quality scores are a heuristic over a small, curated set of hospitals. They are not medical advice.

  • The data is a point-in-time sample, not a live feed, and covers Bangalore only.

  • The Alembic migration covers only the initial projects table; the loaders create the current schema directly.

  • No authentication or rate limiting on the APIs. The MCP gateway runs over stdio and is not part of the compose stack.

License

MIT. See LICENSE.

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    Property intelligence API for US real estate. Returns 16+ data points for any US address: noise levels, wetlands, slope, natural light, powerline proximity, crime rates, property facing direction, zoning, public record, radon risk, natural hazards (earthquake, flood, wildfire), neighborhood demographics, falling tree risk, RF/cell tower exposure, and nearby school.
    -
  • A
    license
    A
    quality
    B
    maintenance
    Enables AI assistants to answer mortgage-related queries by providing tools for lender search, loan limit lookup, down-payment assistance programs, and more, with data sourced from real wholesale lenders and broker-curated intel.
    8
    4 npm
    MIT
  • -
    license
    Not graded
    quality
    C
    maintenance
    Provides real-time London area intelligence including postcode data, crime, schools, transport, and investment metrics, enabling AI assistants to answer property-related queries.
    -
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables structured real estate workflows including property search, agent/client management, market intelligence, mortgage calculations, valuation, investment analysis, and document ingestion, with offline-first capabilities and optional live data integrations.
    AGPL 3.0