Search places
Discover places by name or traveler intent in an explicit geographic scope.
Availability
search_places is available through GoVisit page WebMCP and the stateless HTTP JSON endpoint POST https://govisit.ai/api/tools/v1/search_places. The matching Agent Skill packages the same versioned request contract.
HTTP requests do not inherit browser page context. Supply the traveler-established scope and language supported by this tool. No authentication, cookies, SSE connection, or persistent session are required.
On a GoVisit page in a compatible browser, use the registered WebMCP tool. Pass a destination code returned by search_destinations when the traveler has selected a destination:
const context = document.modelContext;
if (!context) throw new Error('This browser does not expose WebMCP on this page.');
const tool = (await context.getTools()).find((item) => item.name === 'search_places');
if (!tool) throw new Error('search_places is not registered on this page.');
const result = await context.executeTool(tool, {
query: 'Cafe Slavia', mode: 'name', destination: 'cz_prague', language: 'en', limit: 5
});
console.log(result.data.items);
Call over HTTP
curl
curl --fail-with-body --silent --show-error https://govisit.ai/api/tools/v1/search_places \
-H 'Content-Type: application/json' \
--data '{"query":"Cafe Slavia","mode":"name","destination":"cz_prague","language":"en","limit":5}'
JavaScript
const response = await fetch('https://govisit.ai/api/tools/v1/search_places', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({"query": "Cafe Slavia", "mode": "name", "destination": "cz_prague", "language": "en", "limit": 5}),
});
let result;
try {
result = await response.json();
} catch {
throw new Error(`GoVisit returned a non-JSON response (HTTP ${response.status})`);
}
if (!response.ok) {
const retryAfter = response.headers.get('Retry-After');
throw new Error(`GoVisit returned HTTP ${response.status}${retryAfter ? `; retry after ${retryAfter}s` : ''}: ${JSON.stringify(result)}`);
}
if (result.completeness !== 'search-index-release') {
throw new Error(`Search did not complete: ${result.error ?? result.warnings?.join('; ') ?? result.completeness}`);
}
console.log(result.data.items);
Python
import json
from urllib.error import HTTPError
from urllib.request import Request, urlopen
url = 'https://govisit.ai/api/tools/v1/search_places'
payload = {'query': 'Cafe Slavia', 'mode': 'name', 'destination': 'cz_prague', 'language': 'en', 'limit': 5}
request = Request(url, data=json.dumps(payload).encode(), headers={'Content-Type': 'application/json'}, method='POST')
try:
with urlopen(request) as response:
result = json.load(response)
except HTTPError as error:
body = error.read().decode('utf-8', 'replace')
retry_after = error.headers.get('Retry-After')
raise RuntimeError(f'GoVisit returned HTTP {error.code}; Retry-After={retry_after}: {body}') from error
if result.get('completeness') != 'search-index-release':
raise RuntimeError(result.get('error') or result.get('warnings') or result.get('completeness'))
for item in result.get('data', {}).get('items', []):
print(item.get('name'), item.get('url'))
The response is the same result object exposed by WebMCP. Inspect data.items, data.scope, and data.count; preserve any release, completeness, warnings, and truncated fields returned.
Read a returned place
Each item includes a canonical profile url and a bounded set of preview fields. Treat a search match as a candidate, then read the profile before presenting factual details in a planner. This schema-shaped response example shows the required envelope and an item; the place ID and canonical URL are placeholders, and optional release metadata or warnings may also be present:
{
"schemaVersion": "2",
"locale": "en",
"completeness": "search-index-release",
"data": {
"scope": { "kind": "destination", "code": "cz_prague" },
"count": 1,
"items": [
{
"id": "<returned-place-id>",
"entityType": "place",
"name": "<returned place name>",
"url": "<canonical profile URL returned by GoVisit>",
"match": "intent",
"score": null
}
]
}
}
The place overview tool can read additional published facts through WebMCP when a compatible browser is on that place page; it has no stateless HTTP endpoint. An HTTP-only app can show the returned preview fields and link to the canonical profile, but should not assume the full profile is available as JSON. The score is a ranking signal, not a suitability probability.
Input and output contract
Input schema
{
"type": "object",
"properties": {
"query": {
"type": "string",
"minLength": 2,
"maxLength": 200,
"description": "Place or destination name, or the traveler's intent."
},
"mode": {
"type": "string",
"enum": [
"auto",
"name",
"intent"
],
"default": "auto",
"description": "Name lookup, intent retrieval, or name-first auto discovery."
},
"limit": {
"type": "integer",
"minimum": 1,
"maximum": 20,
"default": 10
},
"language": {
"type": "string",
"enum": [
"en",
"cs"
],
"description": "Requested response language; defaults to the page language."
},
"destination": {
"type": "string",
"maxLength": 64,
"pattern": "^[A-Za-z0-9][A-Za-z0-9_]*$",
"description": "Stable destination code or all. Defaults to the current destination, otherwise all."
},
"country": {
"type": "string",
"pattern": "^[A-Za-z]{2}$",
"description": "ISO country scope; intersects destination and proximity where the public geography projection is available."
},
"near": {
"type": "object",
"properties": {
"placeId": {
"type": "string",
"pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
"description": "Reference GERS Place resolved from the same public release."
},
"coordinates": {
"type": "object",
"properties": {
"latitude": {
"type": "number",
"minimum": -90,
"maximum": 90
},
"longitude": {
"type": "number",
"minimum": -180,
"maximum": 180
}
},
"required": [
"latitude",
"longitude"
],
"additionalProperties": false
},
"radiusMeters": {
"type": "number",
"minimum": 1,
"maximum": 100000
}
},
"required": [
"radiusMeters"
],
"additionalProperties": false,
"oneOf": [
{
"required": [
"placeId"
],
"not": {
"required": [
"coordinates"
]
}
},
{
"required": [
"coordinates"
],
"not": {
"required": [
"placeId"
]
}
}
]
}
},
"required": [
"query"
],
"additionalProperties": false,
"allOf": [
{
"if": {
"properties": {
"mode": {
"const": "intent"
}
},
"required": [
"mode"
]
},
"then": {
"properties": {
"query": {
"minLength": 3
}
}
}
},
{
"if": {
"properties": {
"mode": {
"const": "name"
}
},
"required": [
"mode"
]
},
"then": {
"properties": {
"query": {
"maxLength": 120
},
"limit": {
"maximum": 10
}
}
}
}
]
}Output schema
{
"type": "object",
"properties": {
"schemaVersion": {
"type": "string",
"const": "2"
},
"locale": {
"type": "string",
"enum": [
"en",
"cs"
]
},
"completeness": {
"type": "string",
"enum": [
"search-index-release",
"partial-geography",
"invalid-input",
"unavailable"
]
},
"release": {
"type": "string"
},
"error": {
"type": "string"
},
"warnings": {
"type": "array",
"items": {
"type": "string"
}
},
"truncated": {
"type": "boolean"
},
"retryAfter": {
"type": "number"
},
"data": {
"type": "object",
"properties": {
"scope": {
"type": "object",
"properties": {
"kind": {
"type": "string",
"enum": [
"all",
"country",
"destination"
]
},
"code": {
"type": "string"
},
"country": {
"type": "string",
"pattern": "^[A-Z]{2}$"
},
"near": {
"type": "object",
"description": "Resolved proximity constraint; never interpreted as travel time."
}
},
"required": [
"kind",
"code"
],
"additionalProperties": false
},
"items": {
"type": "array",
"maxItems": 20,
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"category": {
"type": [
"string",
"null"
]
},
"locality": {
"type": [
"string",
"null"
]
},
"address": {
"type": [
"string",
"null"
],
"description": "Street address, for example \"Pobřežnà 311/1\"; null when the place has none."
},
"coordinates": {
"type": [
"object",
"null"
],
"description": "The place's point (WGS 84); null when it has none.",
"properties": {
"latitude": {
"type": "number"
},
"longitude": {
"type": "number"
}
},
"required": [
"latitude",
"longitude"
]
},
"url": {
"type": "string",
"description": "The place's canonical GoVisit page."
},
"score": {
"type": [
"number",
"null"
],
"description": "Engine-specific ranking signal; compare only within the same match method, not a suitability probability."
},
"entityType": {
"type": "string",
"const": "place"
},
"id": {
"type": "string",
"format": "uuid",
"description": "Public GERS Place identity."
},
"match": {
"type": "string",
"enum": [
"name",
"intent"
]
},
"profileQuality": {
"type": [
"number",
"null"
],
"description": "Published profile completeness signal; not guest fit."
},
"categoryCode": {
"type": "string",
"description": "Published canonical category, not an amenity or suitability verdict."
},
"destinationCodes": {
"type": [
"array",
"null"
],
"items": {
"type": "string"
},
"description": "Canonical membership; null is unestablished and an empty array is known empty."
},
"operatingStatus": {
"type": [
"string",
"null"
],
"enum": [
"operational",
"temporarily_closed",
"permanently_closed",
null
],
"description": "Published lifecycle status, not open-now or booking availability."
},
"distanceMeters": {
"type": "number",
"minimum": 0,
"description": "Straight-line geographic distance from the explicit proximity reference, not travel time."
}
},
"required": [
"id",
"entityType",
"name",
"url",
"match",
"score"
]
}
},
"count": {
"type": "integer",
"minimum": 0
},
"coverage": {
"type": "object",
"description": "Bounded candidate observation, not exhaustive market coverage.",
"properties": {
"candidatesRetrieved": {
"type": "integer",
"minimum": 0,
"maximum": 200
},
"examined": {
"type": "integer",
"minimum": 0,
"maximum": 200
},
"unestablished": {
"type": "integer",
"minimum": 0,
"maximum": 200
},
"eligible": {
"type": "integer",
"minimum": 0,
"maximum": 200
},
"returned": {
"type": "integer",
"minimum": 0,
"maximum": 20
},
"candidateBudget": {
"const": 200
},
"indexRowsScanned": {
"type": "integer",
"minimum": 0
},
"truncated": {
"type": "boolean"
}
},
"required": [
"candidatesRetrieved",
"examined",
"unestablished",
"eligible",
"returned",
"candidateBudget",
"indexRowsScanned",
"truncated"
],
"additionalProperties": false
}
},
"required": [
"scope",
"items",
"count"
],
"additionalProperties": false
}
},
"required": [
"schemaVersion",
"locale",
"completeness",
"data"
]
}HTTP input schema
{
"type": "object",
"properties": {
"query": {
"type": "string",
"minLength": 2,
"maxLength": 200,
"description": "Place or destination name, or the traveler's intent."
},
"mode": {
"type": "string",
"enum": [
"auto",
"name",
"intent"
],
"default": "auto",
"description": "Name lookup, intent retrieval, or name-first auto discovery."
},
"limit": {
"type": "integer",
"minimum": 1,
"maximum": 20,
"default": 10
},
"language": {
"type": "string",
"enum": [
"en",
"cs"
],
"description": "Requested response language; defaults to the page language."
},
"destination": {
"type": "string",
"maxLength": 64,
"pattern": "^[A-Za-z0-9][A-Za-z0-9_]*$",
"description": "Stable destination code or all. Defaults to the current destination, otherwise all."
},
"country": {
"type": "string",
"pattern": "^[A-Za-z]{2}$",
"description": "ISO country scope; intersects destination and proximity where the public geography projection is available."
},
"near": {
"type": "object",
"properties": {
"placeId": {
"type": "string",
"pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
"description": "Reference GERS Place resolved from the same public release."
},
"coordinates": {
"type": "object",
"properties": {
"latitude": {
"type": "number",
"minimum": -90,
"maximum": 90
},
"longitude": {
"type": "number",
"minimum": -180,
"maximum": 180
}
},
"required": [
"latitude",
"longitude"
],
"additionalProperties": false
},
"radiusMeters": {
"type": "number",
"minimum": 1,
"maximum": 100000
}
},
"required": [
"radiusMeters"
],
"additionalProperties": false,
"oneOf": [
{
"required": [
"placeId"
],
"not": {
"required": [
"coordinates"
]
}
},
{
"required": [
"coordinates"
],
"not": {
"required": [
"placeId"
]
}
}
]
}
},
"required": [
"query"
],
"additionalProperties": false,
"allOf": [
{
"if": {
"properties": {
"mode": {
"const": "intent"
}
},
"required": [
"mode"
]
},
"then": {
"properties": {
"query": {
"minLength": 3
}
}
}
},
{
"if": {
"properties": {
"mode": {
"const": "name"
}
},
"required": [
"mode"
]
},
"then": {
"properties": {
"query": {
"maxLength": 120
},
"limit": {
"maximum": 10
}
}
}
}
]
}The schemas above are rendered server-side from config/webmcp-tools.json, the canonical reviewed tool catalog. The WebMCP manifest also publishes the machine-readable contract; native RegisteredTool entries advertise an output-contract URL because that interface does not provide an inline outputSchema field.
Interpretation limits
Search returns candidates, not verified recommendations. Constraints intersect and never silently widen. Name-mode and geography constraints have release-specific availability; inspect completeness, warnings, counts, and truncation. Distance is straight-line meters, not travel time.
For the full current contract and linked context, see the WebMCP manifest and the WebMCP overview.