streeteasy-mcp
This server provides an MCP interface to the StreetEasy GraphQL API, enabling LLM agents to search and retrieve NYC rental listings.
search_rentals: Search active NYC rentals filtered by area (name or numeric code), price range, bedrooms, bathrooms, amenities (e.g.WASHER_DRYER,DOORMAN,GYM), no-fee, and pet-friendly options. Supports sorting and pagination (up to 100/page). Returns compact summaries with listing IDs, photos, and URLs.get_rental_details: Retrieve full details for a single listing by ID, including description, amenities, pricing history, building info, nearby transit/schools, and media (photos, floor plans, YouTube/Vimeo videos, 3D tours).list_areas: Look up StreetEasy area names and numeric codes, with optional substring filtering (e.g.'brooklyn','harlem').list_amenities: List all valid amenity enum tokens accepted bysearch_rentals.
Notable limitations: Contact info and tour scheduling are not supported (users follow the listing URL directly). Cloud/datacenter deployments may require a residential proxy to bypass StreetEasy's bot-detection.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@streeteasy-mcpSearch for 2-bedroom apartments in Brooklyn under $3000"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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.
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 upstream
search_rentals / get_rental_details calls 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 active NYC rentals by area, price, beds, baths, amenities, pets. Returns compact listings + |
| Full detail for one listing id: description, amenities, pricing history, building info, nearby transit/schools, and resolved media — |
| Look up StreetEasy area names ↔ numeric codes (optionally filtered by a search term). |
| 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.
Related MCP server: Apartment Hunter MCP Server
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:stdioRun as an HTTP server
npm install
npm run build
npm start # listens on $PORT (default 3000), POST /mcpTest it with the MCP SDK client (see test-client.mjs):
MCP_URL=http://localhost:3000/mcp node test-client.mjsConfiguration
Env var | Purpose |
| Port to listen on. Railway sets this automatically. |
| Public origin the server is reachable at (the OAuth issuer), e.g. |
| Set to |
| Optional. HTTP/HTTPS proxy for all upstream StreetEasy calls, e.g. |
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/mcpThe client discovers the OAuth endpoints and registers itself automatically
(Dynamic Client Registration) — no client_id / token to configure.
Available Tools
4 toolsget_rental_detailsGet StreetEasy rental detailsB
Fetch full details for a single rental listing by its id (the id field from search_rentals results). Returns description, full amenities/features, pricing history, building info, nearby transit & schools, and media: media.photoUrls and media.floorPlanUrls (CDN image URLs), media.videoLinks (YouTube/Vimeo watch URLs + thumbnails), and media.tour3dUrl (3D walkthrough) when available.
| Name | Required | Description | Default |
|---|---|---|---|
| listingId | Yes | The StreetEasy rental listing id, e.g. '5072403'. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries full burden. It describes the return value but omits behavioral traits such as read-only nature, authentication requirements, rate limits, or error cases. For a read endpoint, this is a significant gap.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single dense sentence that lists many return fields. While not verbose, it could be restructured (e.g., bullet points) for improved readability. It is not wasteful but could be more scannable.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given a single parameter and no output schema, the description provides a fairly comprehensive list of what is returned. It covers major sections of the response, though details like field types or nested structures are not specified. Overall, it is sufficient for an agent to understand the tool's purpose and output.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% with a single parameter described. The description adds marginal value by clarifying the id comes from search_rentals results and providing an example, but the schema already explains the parameter adequately.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool fetches full details for a single rental listing by id, and enumerates the returned data (description, amenities, pricing history, building info, transit, schools, media). It distinguishes itself from siblings like search_rentals (broad search) and list_amenities/list_areas (categorical listings).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description instructs to use the `id` from search_rentals results, implying a prerequisite. However, it does not explicitly state when not to use this tool or mention alternatives beyond the sibling context.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_amenitiesList StreetEasy amenitiesA
List the valid amenity enum tokens accepted by search_rentals (e.g. WASHER_DRYER, DOORMAN, GYM, PRIVATE_OUTDOOR_SPACE).
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It does not mention side effects, permissions, or stability of the list. However, as a read-only list operation, the description is adequate but not rich.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Single sentence, direct, and to the point. No unnecessary words, and examples are provided for clarity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a parameter-less tool with no output schema, the description sufficiently covers what is returned and its purpose. It could explicitly state the return format, but examples make it clear.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
There are no parameters, so schema coverage is 100%. The description adds value by explaining that the output is meant for use with search_rentals, justifying the tool's existence.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Description clearly states it lists valid amenity tokens for search_rentals, providing examples. This distinguishes it from sibling tools like search_rentals (which consumes the tokens) and other list tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies the tool should be used before constructing search_rentals queries with amenity filters. It does not explicitly state when not to use it or mention alternatives, but the purpose is narrow and clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_areasList StreetEasy areasA
List StreetEasy area names and their numeric codes for use in search_rentals. Optionally pass a search term to filter (e.g. 'brooklyn', 'harlem', 'williams'). Names are matched case/spacing-insensitively.
| Name | Required | Description | Default |
|---|---|---|---|
| search | No | Optional substring to filter area names (case-insensitive). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Discloses matching behavior (case/spacing-insensitivity) and that it returns names and codes. With no annotations, this adequately conveys the read-only, non-destructive nature of the tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, front-loaded with purpose. No redundant words, every sentence serves a clear function.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple list tool with one optional parameter and no output schema, the description fully covers what the agent needs: purpose, parameter usage, return content, and integration with sibling tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema covers 100% of parameters, but description adds value by explaining how the search parameter works (case/spacing-insensitive matching, examples). Adds meaning beyond schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Explicitly states 'List StreetEasy area names and their numeric codes' with a specific verb and resource. Clearly distinguishes from sibling tools like search_rentals by focusing on area codes for use in that tool.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
States the tool is 'for use in search_rentals' and describes optional filtering with examples. Does not explicitly exclude alternatives but clearly implies the context of retrieving area codes for rental searches.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_rentalsSearch StreetEasy rentalsA
Search active NYC rental listings on StreetEasy with filters. Areas accept names (e.g. 'MANHATTAN', 'Williamsburg', 'upper east side') or numeric area codes — use list_areas to discover them. Amenities are uppercase enum tokens — use list_amenities. Returns a compact list of listings plus a totalCount; paginate with page/perPage. Each listing has an id you can pass to get_rental_details, plus leadPhotoUrl and photoUrls (ready-to-view image URLs) and a url to the listing page.
| Name | Required | Description | Default |
|---|---|---|---|
| areas | No | Neighborhoods/boroughs to search. Names or numeric codes. E.g. ['MANHATTAN'] or ['WILLIAMSBURG','LONG_ISLAND_CITY']. | |
| minPrice | No | Minimum monthly rent in USD. | |
| maxPrice | No | Maximum monthly rent in USD. | |
| minBedrooms | No | Minimum bedrooms (0 = studio). | |
| maxBedrooms | No | Maximum bedrooms. | |
| minBathrooms | No | Minimum bathrooms. | |
| maxBathrooms | No | Maximum bathrooms. | |
| amenities | No | Required amenities (uppercase enum tokens, e.g. WASHER_DRYER, DOORMAN, GYM). See list_amenities. | |
| petsAllowed | No | Only listings that allow pets. | |
| noFeeOnly | No | Only no-broker-fee listings (applied to the returned page). | |
| sortBy | No | Sort attribute. Default RECOMMENDED. | |
| sortDirection | No | Sort direction. Default DESCENDING. | |
| perPage | No | Results per page (default 20, max 100). | |
| page | No | Page number (1-based). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations provided, so description carries full burden. It discloses the return structure (compact list, totalCount, pagination, image URLs, url) and the read-only nature implicitly. Lacks explicit statements about auth or rate limits, but sufficient for a search tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Single paragraph with no fluff. Every sentence adds necessary information: purpose, area/amenity inputs, pagination, output fields. Front-loaded with main purpose.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given 14 parameters with full schema coverage and no output schema, the description adequately covers the tool's functionality: filters, valid inputs via sibling tools, pagination, and key output fields. Complete for a search tool context.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, but the description adds value by clarifying areas accept names or numeric codes, amenities are uppercase enum tokens, and directs to sibling tools for valid values. Also explains pagination parameters and output fields.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states it searches active NYC rental listings with filters. It distinguishes itself from siblings (get_rental_details, list_amenities, list_areas) by specifying the search and filter functionality.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides guidance on using list_areas and list_amenities to discover valid inputs, and explains pagination. However, it does not explicitly state when not to use this tool or provide exclusion criteria.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
4 tool updates
v0.1.0- First observed
get_rental_details - First observed
list_amenities - First observed
list_areas - First observed
search_rentals
TDQS
Scored across 4 tools
Each tool has a distinct purpose: search_rentals for querying listings, get_rental_details for full info on a specific listing, and list_amenities/list_areas for retrieving valid filter values. No overlap.
All tool names follow a consistent verb_noun pattern in snake_case (get_rental_details, list_amenities, list_areas, search_rentals), making them predictable and uniform.
With 4 tools, the server is well-scoped for its purpose of browsing NYC rental listings. It covers searching, detail retrieval, and necessary enum lookups without bloat or insufficiency.
The tool surface provides a complete workflow: discover valid filters (amenities/areas), search listings with those filters, and get full details for any listing. No obvious gaps for a read-only rental search API.
Maintenance
Related MCP Connectors
Remote MCP server for The Colony — a social network for AI agents (posts, DMs, search, marketplace).
An MCP server that provides an API to LLMs to manage their JumpCloud resources.
MCP server giving Claude AI access to 22+ NYC public-record databases for real estate due diligence
Related MCP Servers
AlicenseNot gradedqualityCmaintenanceMCP server for the Rentalot API. Manage rental properties, contacts, showings, conversations, and more from any AI assistant.151MIT- AlicenseAqualityBmaintenanceAn MCP server for rental apartment aggregation, enabling geographic polygon search, semantic vector search, and LLM-powered analysis. Integrates with AI agents like Claude Desktop to manage apartment hunting workflows.152MIT
- FlicenseNot gradedqualityBmaintenanceRemote MCP server for SUpost, the Stanford student marketplace. Lets AI agents search active listings, fetch listing details, and read verified market statistics.-
- FlicenseAqualityBmaintenanceMCP server that wraps the Vast.ai REST API to enable LLM agents to search GPU marketplace, rent machines, manage instance lifecycle, run commands, and inspect billing.201-