Skip to main content
Glama

Find places

find_places
Read-onlyIdempotent

Search for named places around a location or inside a boundary, filtered by category, brand, and operating status to get relevant results.

Instructions

Find named places, either near a point or inside an area's boundary.

Three mutually exclusive modes:
- Point + radius: pass lat and lon (radius_m defaults to 1000m), or the
  same center as `where` — a {"lat", "lon"} dict, a GERS id, or a
  free-text place name ("Alamo Square, SF"), resolved here, so a named
  search is one hop: no geocode()/resolve_place() call first. lat/lon
  or where, not both, and neither with division_id/area. An id/name
  `where` adds a compact "resolved": {"name", "id", "lat", "lon",
  "matched_by"} (absent for lat/lon or a {lat,lon} where); an ambiguous
  name returns {"error": "ambiguous_place", "candidates": [...]} rather
  than picking one. Results are nearest-first around that center.
- Division polygon: pass division_id (a GERS division id, e.g. one from
  an admin-hierarchy chain) instead of lat/lon. Results are every matching place whose
  point falls inside that division's true boundary polygon — no radius
  to guess, and no circle clipping a coastline or straddling a border.
  Results are ordered by name (there's no reference point to rank
  distance from).
- Area by name: pass area ("Palo Alto") to get the division-polygon mode
  without first resolving the id yourself. The name is resolved with the
  same ranking geocode/resolve_place use; the resolved division is
  echoed back as "area" on the response so it's clear which one was
  searched. A name matching several equally-ranked divisions returns
  {"error": "ambiguous_area", "candidates": [...]} listing their
  division_ids rather than silently picking one, and an unresolvable
  name returns {"error": "not_found"} rather than an empty result that
  would read as "this place has no coffee shops".

category matches Overture's taxonomy (e.g. 'coffee_shop', 'restaurant',
'grocery'); name is a substring match on the place name — both compose
with either mode. Results include operating_status ("in business" /
"permanently closed" / null when unknown) — a business-lifecycle
signal, NOT opening hours; this data has no open-now information —
and a compact trust_note calibrated from confidence and that status.

min_confidence (0.0-1.0) keeps only rows whose confidence score is at
least that value; out-of-range values return a bad_request error.
operating_status filters to a single status (see the schema's enum for
accepted relabeled/raw values); "permanently closed"/"closed" also
match Overture's separate "closed_permanently" raw value, since both
relabel the same way. Unrecognized values return a bad_request error.

brand is a substring match on the place's brand name (e.g. 'Starbucks').
Brand data is sparse — most independent businesses have no brand at all,
so brand=X narrows results down to that chain only; the absence of a
result does NOT mean "not a Starbucks", it may just mean brand isn't
populated for that place. has_website/has_phone filter on whether a
place has any website/phone entries at all (presence, not content) —
each result row carries brand (string or null) and has_website/has_phone
(booleans) so an agent can see why a place matched, but the full
websites/phones arrays are only returned by place_details.

Every filter above composes with either mode, and each is a silent
no-op (not an error) if the column it needs (confidence /
operating_status / brand / websites / phones) is absent from the active
dataset — see degraded_fields() on the response.

name has two fallback tiers (point + radius mode only, #373) for when
the literal substring search finds nothing: an alternate-spelling match
("Munich" -> a place named "München") then a typo-tolerant fuzzy match
("Startbucks" -> "Starbucks"). A row found either way carries
"matched_by": "alt_name" | "fuzzy" (absent on an ordinary match), and a
top-level "note" names the spelling actually matched.

Returns {"results": [...]}, plus truncated/omitted_count if the answer
didn't fit the token budget. Returns a structured {"error": "bad_request",
...} if no mode's inputs are given (or more than one is), {"error":
"not_found", ...} if division_id/area doesn't match any known division, or
a structured {"error": ...} if the upstream dataset is unavailable or
missing columns this tool depends on. If a category filter was given and
it matched nothing (in either mode), a non-fatal "note" field hints that
the category slug may be wrong and points at search_categories.

A truncated answer — either token-budget-trimmed or because more
matching rows exist beyond limit — also carries "cursor"; pass it back
unchanged, with every other argument identical, to fetch the next page.
cursor is only ever issued for a literal or ambiguity-free match: an
answer built from #373's alt-name/fuzzy name fallback never carries one
(those pools are small; ROADMAP §4.4). A cursor for a different query,
or one that's malformed, returns {"error": "bad_cursor", ...} naming
the mismatch; a cursor issued against an older Overture release is
honored anyway, against the current release, with a one-line "note"
that rows may have shifted.

detail picks how much of each row comes back (ROADMAP §4.5, roadmap
feature 5): "compact" (the DEFAULT) is {id, name, category, lat, lon,
distance_m, trust} — trust is a tier string ("strong"/"ok"/"weak"/
"unknown") derived from the same confidence/operating-status signals
as "full"'s trust_note, so the two can never disagree; the payload
also carries one "trust_legend" line explaining the tiers. compact
keeps lat/lon (unlike "ids") so a composed call feeding these rows
into a map-rendering tool still has coordinates under the default
tier. "ids" is just {id,
distance_m} — deliberately coordinate-free, the cheapest shape, for
chaining straight into a batch id lookup or place_details. "full" is
every field, unchanged from before this param existed, including
trust_note's prose. division-polygon rows have no distance_m at any
tier (no reference point to measure from) — the key is omitted, never
null; they still carry lat/lon at every tier but "ids". detail is
presentation only: it does not affect which rows match or their
order, is NOT part of a cursor's query identity, and a cursor issued
under one detail continues correctly under a different one.
Projection happens before the token budget is applied, so a smaller
detail tier fits more rows per answer — that's the point of a tier
smaller than "full". Unrecognized values return a bad_request error
naming the accepted ones.

categories (mutually exclusive with category — passing both is a
bad_request) runs a checklist of up to 5 slugs in ONE scan instead of
category's one slug, each matched with identical substring/prefix
semantics. group_by_category=False (the default) merges every
category's matches into one nearest-first list, same shape and cursor
pagination as a single category (categories, sorted, is part of the
cursor's query identity). group_by_category=True instead buckets the
answer as {"results": {category: [rows...]}}, up to `limit` rows PER
category — a category with zero matches is simply absent from the
dict. Grouped answers carry no cursor at all (each category is already
limit-bounded from a single scan; page a specific one further by
re-running with category=<that slug> instead). More than 5 slugs, or
categories together with category, both return a bad_request error;
group_by_category=True together with a non-null cursor is also a
bad_request (there is nothing to continue). The category-miss "note"
(see above) also covers categories: it fires when the scan matched
none of the requested slugs at all.

within = {"minutes", "mode"?, "of"?} (roadmap §4.2) keeps only results
truly reachable from `of` within `minutes` by street-graph `mode` —
the real graph, not a radius guess; radius_m is ignored when set. `of`
(a LocationRef) defaults to the search center — lat/lon or whatever
`where` resolved to; required in division_id/area mode. An id/name `of`
adds a "resolved" echo, unless `where` already claimed that key, in
which case `of`'s match is named in the note. The answer gets a short
reachability note. A cold graph returns
{"error": "needs_confirm", ...} — retry with confirm=true once the
user agrees to wait (5-25s).

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
latNo
lonNo
areaNo
nameNo
brandNo
limitNo
whereNo
cursorNoContinuation cursor from a previous truncated answer; valid for the same query on the same data release.
detailNoRow detail tier for find_places rows: 'ids' (id + distance_m only), 'compact' (id/name/category/lat/lon/distance_m/trust), or 'full' (every field, incl. trust_note prose). Default: compact.
withinNo
confirmNo
categoryNo
radius_mNo
has_phoneNo
categoriesNo
division_idNo
has_websiteNo
min_confidenceNo
operating_statusNoBusiness-lifecycle status filter (relabeled or raw Overture value, case-insensitive). Default: no filter.
group_by_categoryNo

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Install Server

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Even though annotations already mark the tool read-only and idempotent, the description adds substantial behavior: ambiguity errors instead of silent selection, name-fallback tiers with matched_by, cursor identity rules, detail being presentation-only, brand sparsity caveats, and the needs_confirm cold-graph flow. Nothing contradicts the readOnlyHint, idempotentHint, or destructiveHint annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is long, but justified by 20 parameters and three modes, and its labeled mode/filter/pagination/detail structure makes it navigable. It is not perfectly concise—roadmap references such as '#373', 'ROADMAP §4.4', and 'roadmap feature 5' are incidental noise for an agent—so it falls just short of a 5.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool this complex, the description covers input modes, output shapes, error contracts, per-mode ordering, pagination, projection tiers, degraded columns, and data-caveat behavior. The output schema exists, but the description adds the error responses, cursor semantics, and per-mode behavioral rules an agent needs to call the tool correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is only 15%, and the description compensates thoroughly. It explains defaults like radius_m=1000, the where forms (lat/lon dict, GERS id, free text), operating_status relabeling, categories/group_by_category semantics, within/confirm, cursor, and detail tiers. Even limit receives indirect explanation through pagination and grouped-row behavior.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening sentence identifies a specific operation—finding named places—and the two spatial contexts (near a point or inside an area boundary). It also distinguishes itself from siblings by noting that named search is one hop and needs no prior geocode()/resolve_place() call, and later points to search_categories and place_details where relevant.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The three mutually exclusive modes are clearly specified with their exact parameter combinations and the errors for mixing them. It gives routing guidance: skip geocode/resolve_place for named searches, use search_categories when a category slug matches nothing, and use place_details when full website/phone arrays are needed.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/chuofringer/placeroot'

If you have feedback or need assistance with the MCP directory API, please join our Discord server