sitefit

Docs

Your agent connects by signing in to your sitefit account in the browser; there is no key to copy. Everything it does counts toward your account's limits. Searches run for up to 120 s; start one, then poll it.

Add to your agent

MCP server https://sitefit-alpha.vercel.app/api/mcp (Streamable HTTP). Claude Code and Codex: one command, then approve the link it shows; it stores the agent's own key for you. The apps sign in with OAuth: they open a sign-in page, you click Allow, and they renew access themselves.

Claude Code

Run in a terminal and approve the link it shows, on this computer or your phone. Works over SSH too.

curl -fsSL https://sitefit-alpha.vercel.app/connect | sh -s claude

Codex

Run in a terminal and approve the link it shows, on this computer or your phone. Works over SSH too.

curl -fsSL https://sitefit-alpha.vercel.app/connect | sh -s codex

Cursor

Click, confirm in Cursor, and sign in when it asks.

VS Code

Click, confirm in VS Code, and sign in when it asks.

Claude app (web, desktop, phone)

Settings → Connectors → Add custom connector, paste this URL, then Connect. It also works in the phone app.

https://sitefit-alpha.vercel.app/api/mcp

ChatGPT

Settings → Apps → Advanced → Developer mode, then Create: paste this URL and choose OAuth.

https://sitefit-alpha.vercel.app/api/mcp

Any other agent

Paste this to your agent. It shows you a link; you click Approve and it gets its own key.

Connect to sitefit (https://sitefit-alpha.vercel.app): POST https://sitefit-alpha.vercel.app/api/v1/auth/device, show me the link, and once I approve, collect your API key from /api/v1/auth/device/token. API docs: https://sitefit-alpha.vercel.app/docs

Approval link (REST API)

For agents that call the REST API directly. The agent asks for a link, you open it and click Approve, and the agent collects its own API key (shown once; revoke it under API keys). Requests expire after 10 minutes.

curl -s -X POST https://sitefit-alpha.vercel.app/api/v1/auth/device -H "content-type: application/json" -d '{"name": "my agent"}'
# → {"device_code": "...", "user_code": "BCDF-GHJK", "verification_uri_complete": "https://sitefit-alpha.vercel.app/activate?code=BCDF-GHJK", "interval": 5, ...}
# Show the user verification_uri_complete. Then, every 5 s:
curl -s -X POST https://sitefit-alpha.vercel.app/api/v1/auth/device/token -H "content-type: application/json" -d '{"device_code": "..."}'
# → 400 {"error": "authorization_pending"} until approved, then {"api_key": "sf_...", ...}
# Other errors: access_denied, expired_token, invalid_grant. Send the key as Authorization: Bearer sf_...

MCP tools

site_formatHow to describe a site; read first
import_siteBuild a site from the cadastre and OpenStreetMap
coverageCountries covered by import
get_demoExample request
list_presetsPresets
save_siteStore a site GeoJSON
list_sitesYour sites
validate_siteZone, frontage, tree-free space
start_layout_searchStart a search (background)
get_layout_searchPoll with wait_seconds until done
get_layout_geojsonGeometry of one layout
get_layout_drawingSVG of one layout (view modes)
get_layout_scene3D scene of one layout
apply_layoutPhase 2: add a layout to the site

REST API

Send Authorization: Bearer <token>: an API key, or an access token from the MCP sign-in. In the browser, a signed-in session works too.

POST/api/v1/auth/device{name?} → {device_code, user_code, verification_uri_complete, interval}; no sign-in needed
POST/api/v1/auth/device/token{device_code} → {api_key} once the user approved; 400 authorization_pending until then
GET/api/v1/site-formatSite format (Markdown)
GET/api/v1/demoComplete example request with a synthetic site
GET/api/v1/presetsDimension and planning rule presets
GET/api/v1/schemaJSON Schema of search, validate, render and apply requests
GET/api/v1/coverageCountries covered by site import, with data sources and licences
POST/api/v1/sites/import{lat, lon | address, country?, parcels?, existing_buildings?} → {site_id, summary}
POST/api/v1/sites{name, geojson} → {site_id, summary}
GET/api/v1/sitesYour saved sites
GET/api/v1/sites/{id}One site with its GeoJSON
GET/api/v1/sites/{id}/svgSite drawing
GET/api/v1/sites/{id}/scene3D scene of the site (terrain, buildings, trees)
POST/api/v1/validate{site_id | site, program?, rules?} → zone, frontage, largest tree-free rectangle
POST/api/v1/searches{site_id | site, program, dimensions, rules, trees, options} → 202 {job_id}
GET/api/v1/searches/{job}?wait=30Status and ranked layouts (detail=full for geometry)
GET/api/v1/searches/{job}/layouts/{rank}/svg?mode=Layout drawing; mode current, zone, entry, layout, paths, trees or full
GET/api/v1/searches/{job}/layouts/{rank}/scene3D scene of the layout
GET/api/v1/searches/{job}/layouts/{rank}/geojsonLayout geometry in the site's CRS
POST/api/v1/searches/{job}/layouts/{rank}/applyAdd the layout to its site as a planned building → new site_id
curl -s $HOST/api/v1/demo -H "Authorization: Bearer $KEY" > demo.json
curl -s $HOST/api/v1/searches -H "Authorization: Bearer $KEY" -H "content-type: application/json" -d @demo.json
curl -s "$HOST/api/v1/searches/$JOB?wait=50" -H "Authorization: Bearer $KEY"

Advanced: API keys

For agents that cannot sign in through a browser, such as scripts and servers. Create a key under API keys and send it as a header.

claude mcp add --scope user --transport http sitefit https://sitefit-alpha.vercel.app/api/mcp --header "Authorization: Bearer $KEY"

Codex reads the key from the environment at run time; searches can wait up to 55 s, so allow a longer tool timeout:

export SITEFIT_API_KEY="$KEY"
codex mcp add sitefit --url https://sitefit-alpha.vercel.app/api/mcp --bearer-token-env-var SITEFIT_API_KEY

# ~/.codex/config.toml equivalent
[mcp_servers.sitefit]
url = "https://sitefit-alpha.vercel.app/api/mcp"
bearer_token_env_var = "SITEFIT_API_KEY"
tool_timeout_sec = 90

# Cursor and other clients: { "url": "https://sitefit-alpha.vercel.app/api/mcp", "headers": { "Authorization": "Bearer <key>" } }

Site format

# Site format

The easiest way to get a site is import: give a point inside the plot (or an address) in a covered country
(Croatia, Netherlands, France, Poland, Spain, Czechia, New York City, New South Wales) and sitefit builds the site from the
official cadastre plus OpenStreetMap roads, junctions, buildings and trees. Check the trees afterwards: mapped trees are
often incomplete. You can also write the GeoJSON yourself:

A site is a GeoJSON FeatureCollection. Every feature has `properties.kind`:

| kind | geometry | meaning and properties |
|---|---|---|
| parcel | Polygon | Land you build on. `id` (e.g. cadastral number). All parcels together form the plot. |
| road | Polygon | Public road land next to the plot (carriageway, pavements, verges). Required: the plot boundary touching it is the frontage where entries can be made. |
| carriageway | Polygon | Optional: kerb-to-kerb driving surface inside road land. Entries are drawn from it to the plot. |
| tree | Point | `id` or `label`, `protection_radius` (m, default 2), `must_keep` (true: never affected). |
| building | Polygon | Existing building. `keep` true (default) keeps it with distance rules; false means it will be removed. |
| planned_building | Polygon | A building planned in an earlier phase (distance rules apply). |
| keep_clear | Polygon | No building or paving. |
| reserved | Polygon | Land reserved for something else (e.g. another phase's drive). |
| junction | Point | Road junction; entries report their distance to the nearest one. |
| region | Polygon | Optional search area (`id`), selected with `options.region_feature`. |
| context_parcel | Polygon | Neighbouring parcel, drawn for context only. |

Coordinates: a projected CRS in metres (`"crs": {"type": "name", "properties": {"name": "EPSG:3765"}}`), plain local metres
(`"crs": "local"`), or longitude/latitude (EPSG:4326 or no crs), which is projected to UTM internally and returned in the
input CRS.

Program (one building per search): `name`, `footprint_m2`, `floors`, `parking_spaces` (incl. accessible),
`accessible_spaces`, `loading` (a delivery vehicle stands at the storage door), `turning_pocket`, `aspect_ratios`,
`parcels` (building only on these), `paving_parcels` (paving and entry only on these).

Dimensions: preset `standard` (5.5 m aisle and drive, 2.5 x 5 m stalls) or `tight` (5 m aisle, 3.5 m one-lane drive,
2.4 x 4.8 m stalls, delivery van stops in the aisle), or explicit values.

Trees: a tree is affected when its protection circle overlaps the building, aisle, drive or road approach, or when its
trunk is closer than `island_trunk_distance` (default 1 m) to a stall, turning pocket or footpath. A tree further than
that from every stall stays in a green island between stalls.

Each layout also includes vehicle paths (a car and a delivery van from both lanes, swept area, extra paving the drive
mouth needs, trunks hit), proposed new trees, and a 3D scene (terrain, buildings, paving, trees).

For a second building (phase 2), apply the chosen layout of the first building to the site (it becomes a planned
building with reserved paving) and search again on the new site.