streeteasy-mcp
A remote MCP server that wraps the StreetEasy GraphQL API to enable LLM agents to search and parse NYC rental listings.
README
streeteasy-mcp
A remote MCP server that wraps the StreetEasy GraphQL API so an LLM agent can search and parse NYC rental listings.
It vendors the streeteasy-api
client (v0.4.0) and exposes it over either stdio (local) or Streamable
HTTP (remote) transport, so it can be connected to by Claude or any MCP client.
[!IMPORTANT] StreetEasy blocks datacenter/cloud IPs. Its API sits behind PerimeterX bot-detection that
403s cloud/datacenter IPs (AWS, GCP, Railway, etc.). The HTTP build deploys fine and the MCP layer works, but the upstreamsearch_rentals/get_rental_detailscalls fail from a datacenter unless you do one of:
- Run the stdio server locally from a normal home connection — see Run as a local MCP server, or
- Route upstream calls through a residential proxy by setting
STREETEASY_PROXY— this is what lets the hosted HTTP build (Railway, etc.) reach the API. See Proxy / bot-detection.
Tools
| Tool | Description |
|---|---|
search_rentals |
Search active NYC rentals by area, price, beds, baths, amenities, pets. Returns compact listings + totalCount, paginated. Each listing includes leadPhotoUrl / photoUrls and a listing url. |
get_rental_details |
Full detail for one listing id: description, amenities, pricing history, building info, nearby transit/schools, and resolved media — media.photoUrls, media.floorPlanUrls, media.videoLinks (YouTube/Vimeo), media.tour3dUrl. |
list_areas |
Look up StreetEasy area names ↔ numeric codes (optionally filtered by a search term). |
list_amenities |
List the valid amenity enum tokens. |
search_rentals accepts area names ("MANHATTAN", "Williamsburg",
"upper east side") or numeric codes, and validates amenity tokens against the
known set.
Media
Photos resolve to Zillow's CDN (photos.zillowstatic.com/fp/{key}-se_large_800_400.jpg),
videos to their provider watch URL (YouTube/Vimeo) plus a thumbnail, and 3D
tours to a direct tour3dUrl. All are public — no auth required.
Not included: contact info & inquiries
Listing agent contact details and "request a tour" inquiries are not exposed.
They live behind StreetEasy's contact flow, which is protected by PerimeterX
bot-detection (a "Press & Hold" human check). Automating it would mean evading
bot-detection, so it's intentionally left out — the right pattern is to surface
the listing url and let a human submit the tour request in their browser.
Endpoints
POST /mcp— the MCP Streamable HTTP endpoint (stateless). Requires a bearer token unlessMCP_DISABLE_AUTHis set — see Authentication.GET /andGET /health— health checks.- OAuth:
/.well-known/oauth-authorization-server,/.well-known/oauth-protected-resource,/register,/authorize,/token,/revoke.
Run as a local MCP server (recommended)
Runs over stdio from your machine's residential IP — the configuration that actually reaches StreetEasy.
npm install
npm run build
# register with Claude Code (uses the stdio entry point):
claude mcp add streeteasy -- node "$(pwd)/dist/stdio.js"
Then ask Claude to search rentals. To run the stdio server by hand:
npm run start:stdio
Run as an HTTP server
npm install
npm run build
npm start # listens on $PORT (default 3000), POST /mcp
Test it with the MCP SDK client (see test-client.mjs):
MCP_URL=http://localhost:3000/mcp node test-client.mjs
Configuration
| Env var | Purpose |
|---|---|
PORT |
Port to listen on. Railway sets this automatically. |
PUBLIC_BASE_URL |
Public origin the server is reachable at (the OAuth issuer), e.g. https://streeteasy-mcp-production.up.railway.app. Defaults to https://$RAILWAY_PUBLIC_DOMAIN on Railway, else http://localhost:$PORT. |
MCP_DISABLE_AUTH |
Set to 1/true to disable OAuth and leave /mcp open (handy for local testing with the bundled test-client.mjs). |
STREETEASY_PROXY |
Optional. HTTP/HTTPS proxy for all upstream StreetEasy calls, e.g. http://user:pass@host:port. Required for cloud/datacenter deploys — use a residential proxy. HTTPS_PROXY / ALL_PROXY are also honored. |
Authentication (OAuth 2.1 + Dynamic Client Registration)
The HTTP transport requires OAuth by default — MCP clients (Claude, etc.) run the standard authorization flow automatically, so you usually don't configure anything. The server is a self-contained OAuth 2.1 authorization server:
- Advertises metadata at
/.well-known/oauth-authorization-serverand/.well-known/oauth-protected-resource. - Supports Dynamic Client Registration (RFC 7591) at
/register, so clients self-register with no manualclient_id/client_secret. /authorize(PKCE S256 required) →/token(authorization-code + refresh), with/revokefor revocation.- Unauthenticated
POST /mcpreturns401with aWWW-Authenticateheader pointing at the protected-resource metadata, which kicks off discovery + DCR.
Because the tools expose only public listing data, there's no per-user login:
authorization is auto-approved and the issued bearer token simply gates /mcp.
Tokens are held in memory (single replica); a restart just makes clients
transparently re-register. Set MCP_DISABLE_AUTH=1 to turn the whole layer off.
Proxy / bot-detection
StreetEasy 403s datacenter IPs, so any cloud host (Railway included) must send
upstream requests through a residential proxy. Set STREETEASY_PROXY to a
proxy URL (credentials may be embedded, e.g.
http://user:pass@host:port) and all StreetEasy GraphQL traffic is tunnelled
through it. On startup the server logs the proxy in use with credentials
redacted (Outbound proxy: http://***:***@host:port).
Rotating residential proxies hand out a fresh exit IP per connection, and a
clean IP isn't guaranteed every time, so the client automatically retries a
403 bot-challenge (up to 3 times when a proxy is set) to land on a good IP.
A local stdio server on a residential connection doesn't need a proxy.
Deploy on Railway
This repo ships a Dockerfile. With the Railway CLI:
railway login
railway init --name streeteasy-mcp
# Cloud hosts are datacenter IPs — set a residential proxy so calls aren't 403'd:
railway variables --set "STREETEASY_PROXY=http://user:pass@host:port"
railway up
railway domain # generate a public URL
# Set the OAuth issuer to your public URL (or rely on RAILWAY_PUBLIC_DOMAIN):
railway variables --set "PUBLIC_BASE_URL=https://<your-app>.up.railway.app"
Connect from Claude Code
claude mcp add --transport http streeteasy https://<your-app>.up.railway.app/mcp
The client discovers the OAuth endpoints and registers itself automatically
(Dynamic Client Registration) — no client_id / token to configure.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。