Search destinations
Discover destinations by name or traveler intent, returning stable codes for subsequent searches.
Availability
search_destinations is available through GoVisit page WebMCP and the stateless HTTP JSON endpoint POST https://govisit.ai/api/tools/v1/search_destinations. 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:
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_destinations');
if (!tool) throw new Error('search_destinations is not registered on this page.');
const result = await context.executeTool(tool, { query: 'Prague', mode: 'name', 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_destinations \
-H 'Content-Type: application/json' \
--data '{"query":"Prague","mode":"name","language":"en","limit":5}'
JavaScript
const response = await fetch('https://govisit.ai/api/tools/v1/search_destinations', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({"query": "Prague", "mode": "name", "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_destinations'
payload = {'query': 'Prague', 'mode': 'name', '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.
Continue with the selected destination
Let the traveler choose a destination when the search returns multiple candidates. Pass that result's stable code as destination to search places; do not silently choose the first match for them.
For example, a matching item can provide the cz_prague code shown in the place-search request below. This schema-shaped response example shows the full required envelope; optional fields such as release metadata and warnings may also be present:
{
"schemaVersion": "2",
"locale": "en",
"completeness": "search-index-release",
"data": {
"scope": { "kind": "all", "code": "all" },
"count": 1,
"items": [
{
"code": "cz_prague",
"entityType": "destination",
"name": "Prague",
"url": "https://govisit.ai/en/destinations/cz/prague/",
"match": "name",
"score": null
}
]
}
}
In application code, carry the selected code forward:
const selectedDestination = destinationResult.data.items.find(
(item) => item.code === travelerSelection.destinationCode,
);
if (!selectedDestination) throw new Error('The selected destination is not in this result.');
const placeSearch = {
query: 'quiet cafes with outdoor seating',
mode: 'intent',
destination: selectedDestination.code,
language: 'en',
limit: 5,
};
Input and output contract
Input schema
{
"type": "object",
"properties": {
"query": {
"type": "string",
"minLength": 2,
"maxLength": 120,
"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."
},
"within": {
"type": "string",
"maxLength": 64,
"pattern": "^[A-Za-z0-9][A-Za-z0-9_]*$",
"not": {
"pattern": "^[aA][lL][lL]$"
},
"description": "Canonical destination code to explore within; all is reserved for an explicit unrestricted place search, never a containing-destination filter."
},
"types": {
"type": "array",
"minItems": 1,
"maxItems": 5,
"uniqueItems": true,
"items": {
"enum": [
"country",
"region",
"district",
"city",
"highlight"
]
},
"description": "Match any of these published classifications; not a size/tier filter."
}
},
"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
}
}
}
}
]
}Output schema
{
"type": "object",
"properties": {
"schemaVersion": {
"type": "string",
"const": "2"
},
"locale": {
"type": "string",
"enum": [
"en",
"cs"
]
},
"completeness": {
"type": "string",
"enum": [
"search-index-release",
"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",
"destination"
]
},
"code": {
"type": "string"
}
},
"required": [
"kind",
"code"
],
"additionalProperties": false
},
"items": {
"type": "array",
"maxItems": 20,
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"type": {
"type": [
"string",
"null"
],
"enum": [
"country",
"region",
"district",
"city",
"highlight",
null
]
},
"region": {
"type": [
"string",
"null"
],
"description": "The region it lies in; null for a country or a region."
},
"country": {
"type": [
"string",
"null"
],
"description": "The country it lies in; null for a country."
},
"places": {
"type": "integer",
"minimum": 0,
"description": "Indexed places in the destination."
},
"match": {
"type": "string",
"enum": [
"name",
"intent"
]
},
"score": {
"type": [
"number",
"null"
],
"description": "Engine-specific ranking signal; compare only within the same match method, not a suitability probability."
},
"url": {
"type": "string",
"description": "The destination's canonical GoVisit page."
},
"entityType": {
"type": "string",
"const": "destination"
},
"code": {
"type": "string",
"description": "Stable code accepted by search_places.destination."
},
"classifications": {
"type": "array",
"items": {
"type": "string"
},
"description": "Published classifications; highlight is not a geographic size or tier."
},
"containmentMethod": {
"enum": [
"identity",
"division-coverage",
"parent-chain"
],
"description": "Canonical relation used to establish within scope; parent-chain is disclosed fallback when coverage is absent."
}
},
"required": [
"code",
"entityType",
"name",
"url",
"match",
"score"
]
}
},
"count": {
"type": "integer",
"minimum": 0
}
},
"required": [
"scope",
"items",
"count"
],
"additionalProperties": false
}
},
"required": [
"schemaVersion",
"locale",
"completeness",
"data"
]
}HTTP input schema
{
"type": "object",
"properties": {
"query": {
"type": "string",
"minLength": 2,
"maxLength": 120,
"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."
},
"within": {
"type": "string",
"maxLength": 64,
"pattern": "^[A-Za-z0-9][A-Za-z0-9_]*$",
"not": {
"pattern": "^[aA][lL][lL]$"
},
"description": "Canonical destination code to explore within; all is reserved for an explicit unrestricted place search, never a containing-destination filter."
},
"types": {
"type": "array",
"minItems": 1,
"maxItems": 5,
"uniqueItems": true,
"items": {
"enum": [
"country",
"region",
"district",
"city",
"highlight"
]
},
"description": "Match any of these published classifications; not a size/tier filter."
}
},
"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
}
}
}
}
]
}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 a judgment that a destination fits a trip. Geographic filters require a capable index release; unknown containment is never assumed. Scores are ranking signals, not suitability probabilities.
For the full current contract and linked context, see the WebMCP manifest and the WebMCP overview.