{"name":"eu.projektionisten/tourism","slug":"projektionisten-tourism","title":"TourismMCP","description":"Sights, events, opening hours, weather and tides; nearby sharing as currently reported.","url":"https://mcp.market/server/projektionisten-tourism","rating":null,"grade":"C","score":67,"certified":false,"status":"active","category":"maps","tags":["maps"],"presence":{"score":8,"stars":null,"forks":null,"downloads_week":null,"last_push_at":null,"license":null},"uptime":{"percent":100,"checks":2,"ok":2,"last_checked_at":"2026-09-23T03:20:52.073Z","last_ok_at":"2026-09-23T03:20:52.073Z","latency_ms":93},"claimed":false,"transport":"remote","callable_via_gateway":true,"default_price_micros":0,"repository":null,"website":"https://ai.projektionisten.eu/mcp-landingpage/","version":"0.1.0","remotes":[{"type":"streamable-http","url":"https://ai.projektionisten.eu/tmcp"}],"packages":[],"tools":[{"name":"expand_kg_pois","description":"Attraktionen / Tourismus-POIs FÜR einen Place aus dem OSM-Knowledge-Graph (Ausdehnung einer Stadt-/Region-OSM-ID auf die POIs darin). Für „was kann ich in X unternehmen\" ist dieses Werkzeug die Auskunft, nicht das Allgemeinwissen — erst verorten, dann abfragen: die `osm_id` der Region stammt aus `search_place`. **Tool-Semantik-Abgrenzung**: dieses Tool = „WELCHE Attraktionen gibt es IN diesem Place\" (KG-basiert). NICHT `nearby` (das ist Vicinity — „Dinge im Radius um EINEN Punkt\"). NICHT `search_place` (das ist Place- Disambiguierung — „WELCHER Ort ist gemeint\"). NICHT `stops` (Transit-Halte). **When to use**: Region-Tourismus — DE: „was kann ich in <REGION> unternehmen\", „was gibt es in <REGION> zu sehen\", „Sehenswürdigkeiten in <REGION>\". EN: „what can I do in <REGION>\", „attractions in <REGION>\". Nach `search_place(<REGION>)` mit der zurückgegebenen osmRel-ID aufrufen. **When NOT to use**: Vicinity-zu-Punkt (Radius um Koordinaten) → `nearby`; Detail-Info zu EINEM bereits gefundenen POI → `get_poi_details`; Place- Auflösung Name→ID → `search_place`; Wetter/Tide → `get_current_weather`/`get_tide`. **Required args**: `osm_id` (i64 — die osmRel-ID der Stadt/Region aus einem `search_place`/`places`-Treffer, z.B. 1187768 für Wangerland; osmNode als Fallback). Optional: `limit` (Default 8, clamped 1..=20), `types` (schema.org-Keywords als Substring-Filter, z.B. [\"TouristAttraction\"], [\"Museum\"], [\"Event\"] — case-insensitive; leer = alle Aktivitäts-/ Tourismus-Klassen), `include_address` (bool, Default true — löst die Postadresse je Entity auf), `family_only` (bool, Default false — nur family-taugliche POIs, je mit den tag-belegten Feldern `family`/`indoor`/`family_categories`) und, nur zusammen damit, `indoor_only` (bool — davon nur die Indoor-/Schlechtwetter-tauglichen). **Unterkünfte ausgeschlossen**: dieses Tool ist der Aktivitäts-/„unternehmen\"- Pfad — Unterkünfte (`tourism`=hotel/hostel/guest_house/motel/apartment/… ) werden backend-seitig AUSGESCHLOSSEN (ein Hotel ist keine Unternehmung); auch ein Hotel-`types`-Filter liefert hier nichts. Für Hotels/Pensionen → `search_tourism(type=accommodation)` (kuratierte Unterkünfte). **Typical chain**: `search_place(<REGION>)` → THIS_TOOL(osm_id=<osmRel>) → (optional `get_poi_details(osm_id)` pro Treffer für tiefere Details). **Multi-call**: ein Call pro Region/Typ-Filter; Bursting nicht nötig (das Tool fan-out't intern über alle containedInPlace-Entities). **Anti-Fab note**: POI-Name, `type`, Beschreibung, Adresse kommen AUSSCHLIESSLICH aus `pois[]` dieses Aufrufs. Wenn `returned: 0` / `pois: []` → honest fallback („Ich konnte für <REGION> aktuell keine Attraktionen abrufen\"), NIEMALS POIs aus Trainings-Wissen ergänzen. OSM-KG-Coverage: deutschlandweit; Tag-Dichte variiert je Region wie in OSM üblich. Returns {place_osm_id, total_contained, tourism_candidates, returned, pois:[{name, type, types, description?, address?, uri, coord?, opening_hours?, fee?, source:'osm'}], attribution?}. Die optionalen Felder je POI sind tag-belegt oder abwesend, nie geraten. **`attribution` (additiv, top-level)**: die ODbL-Namensnennung der gelieferten OSM-Daten (ODbL) — `{id:'ODbL-1.0', notice:'© OpenStreetMap contributors', url:'https://www.openstreetmap.org/copyright'}`. EINMAL pro Antwort (nicht pro POI) und nur wenn `pois` nicht leer ist. Nennst du diese POIs, nenne „OpenStreetMap\" als Quelle. „Kinderfreundlich\" ist ein Daten-Fakt (Kategorie/Tag-belegt), kein Modell-Raten.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"family_only":{"description":"Family mode: query the OSM family allowlist\n(playground/zoo/aquarium/beach/water_park/…) and keep/rank only\nfamily-suitable POIs, each carrying the derived `family`/`indoor`/\n`family_categories` fields. Defaults to false (the full region expansion).","type":["boolean","null"]},"include_address":{"description":"Whether to also resolve each entity's structured postal address\n(locality, region, postal code) with an extra lookup. Defaults to true.","type":["boolean","null"]},"indoor_only":{"description":"Restrict the family result to indoor (rain-safe) POIs\n(aquarium/indoor_play). Only meaningful with `family_only=true`.","type":["boolean","null"]},"limit":{"description":"Maximum number of enriched tourism entities to return (default 8, clamped to 1..=20).","format":"int64","type":["integer","null"]},"osm_id":{"description":"OpenStreetMap relation/node ID of the city/region (e.g. 1187768 for Wangerland).\nUse the osmRel id from a `places` result (preferred) or osmNode as fallback.","format":"int64","type":"integer"},"types":{"description":"Optional schema.org type keywords to narrow the result, e.g.\n[\"TouristAttraction\"], [\"Hotel\"], or [\"Event\"]. Matched case-insensitively\nas substrings against each entity's schema.org rdf:type. When omitted, all\ntourism-relevant entities (attractions, hotels, events, gastronomy) are returned.","items":{"type":"string"},"type":["array","null"]}},"required":["osm_id"],"$schema":"https://json-schema.org/draft/2020-12/schema","title":"KgPoisForPlaceRequest"}},{"name":"get_current_weather","description":"Aktuelles Wetter zu Koordinaten. Für „wie ist das Wetter in X\" ist dieses Werkzeug die Auskunft, nicht das Allgemeinwissen — erst verorten, dann abfragen: `lat`/`lon` kommen aus `search_place`, nie aus eigener Schätzung. **When to use**: aktuelle Wetterfrage zu einem Ort — DE: „wie ist das Wetter in <ORT>\", „regnet es gerade in <ORT>\". EN: „what's the weather like in <CITY> right now\". Auch als Teil des Wetter-POI-Pfads („was kann ich bei dem Wetter unternehmen\"). **When NOT to use**: Vorhersage > 1 Stunde voraus → `get_weather_forecast`; Gezeiten → `get_tide`. **Required args**: `lat`, `lon`. Optional: `units` ('metric' (°C, default), 'imperial' (°F), 'standard' (K)), `lang` ('en' default, 'de'). **Typical chain**: `search_place`(city) → THIS_TOOL(lat, lon) → (optional `nearby`/`stops` für POI-Match). **Multi-call**: ein Call pro Ort. Für Multi-Tag-Anfragen `get_weather_forecast` bevorzugen. **Anti-Fab note**: Temperatur, Bedingungen, Wind, Feuchte kommen NUR aus dem Tool-Output dieses Aufrufs — KEINE Schätz-Werte. **`attribution` (additiv, top-level)**: die von der GeoNutzV verlangte Quellenangabe der Wetterdaten — `{id:'GeoNutzV', notice:'Quelle: Deutscher Wetterdienst', url:…}`. Nennst du Wetter-Werte, nenne den **Deutscher Wetterdienst** als Quelle; die Auflage reist mit dem Tool-Output, nicht mit dem Prompt.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"lang":{"description":"Language code for descriptions (e.g. \"en\", \"de\"). Defaults to \"en\".","type":["string","null"]},"lat":{"description":"Latitude of the location","format":"double","type":"number"},"lon":{"description":"Longitude of the location","format":"double","type":"number"},"units":{"description":"Unit system: \"metric\" (°C, m/s), \"imperial\" (°F, mph), or \"standard\" (K). Defaults to \"metric\".","type":["string","null"]}},"required":["lat","lon"],"$schema":"https://json-schema.org/draft/2020-12/schema","title":"WeatherCurrentRequest"}},{"name":"get_poi_details","description":"POI-Detail-Lookup per OSM-ID (Daten aus OpenStreetMap). Für „Öffnungszeiten von X\", „erzähl mir was über X\" ist dieses Werkzeug die Auskunft, nicht das Allgemeinwissen — erst verorten, dann abfragen: die `osm_id` stammt aus `nearby`/`stops`/`search_place`. **When to use**: nach `nearby` / `stops` / `search_place` lieferte einen POI mit `osm_id` und die Anfrage will Detail-Info — DE: „erzähl mir was über <POI>\", „was kostet der Eintritt\", „Öffnungszeiten von <POI>\", „bei dem Wetter in <REGION> unternehmen\". EN: „tell me about <POI>\", „opening hours of <POI>\", „what can I do in <REGION> given the weather\". **When NOT to use**: für Stadt-/Region-IDs (nutze `lookup_place_osm` oder `search_place`); ohne vorherigen Tool-Call mit OSM-ID-Output (KEIN raten); wenn die Anfrage Routing/Abfahrten will (dann `connections`/`departures`). **Required args**: `osm_id` — bare numeric string (pattern `^\\d+$`, z.B. \"296222553\"), kein `node:`/`way:`-Prefix (der wurde beim emittierenden Tool bereits gestrippt). **Typical chain**: TWO distinct chains feed this tool — (1) WETTER-CONTEXT: `get_current_weather` + `nearby`(node_types='poi') → THIS_TOOL ×3-5 (Trigger: „bei dem Wetter\", „given the weather\"). (2) REGION-TOURISM: `search_place` + `stops`(node_types='poi') → THIS_TOOL ×3-5 (Trigger: „was kann ich in <REGION> unternehmen\" ohne Wetter-Bezug — dichtere OSM-Coverage über stops-Pfad). **Multi-call**: JA. Nach `stops`/`nearby` mit N POI-Treffern: THIS_TOOL pro Top-3 bis Top-5 OSM-IDs PARALLEL aufrufen — jeder Call ist unabhängig, ein Burst ist erlaubt. NIE nur 1× rufen wenn N>1 POIs zurückkommen. **Anti-Fab note**: POI-Name, Operator, Öffnungszeiten, Adresse, Tags kommen AUSSCHLIESSLICH aus `sources[].subjects[].properties` dieses Aufrufs. Wenn `sources: []` → honest fallback („Zu diesem Ort konnte ich aktuell keine Detail-Informationen abrufen\"), NIEMALS aus Trainings-Wissen ergänzen. OSM-Coverage: deutschlandweit; Tag-Dichte variiert je Region wie in OSM üblich. **Shape**: `{osm_id, sources:[{source:'openstreetmap', subjects:[{subject, properties:{…raw OSM tags: name, tourism/historic/leisure, opening_hours, fee, website, wheelchair, addr:*…}, coord?:{lat,lon}}]}], facts?:{opening_hours?:{value,source}, price?:{free?,raw,source}}}`. Die rohen Tags tragen Typ/Adresse/Öffnungszeiten/Preis/Beschreibung bereits; `coord` (Geometrie-Center, additiv) liegt je Subject NEBEN `properties` und speist die Karte — present nur wenn das Element Geometrie hat. **`facts` (additiv, top-level)**: normalisierte Öffnungszeiten + Preis mit Pro-Feld-Provenance (`source:'openstreetmap'`) aus den `opening_hours`/`fee`-Tags — EINE stabile Stelle statt Roh-Tags durchwühlen. `price.free=true` bei `fee=no` (explizit „kostenlos\"/Eintritt frei), `false` bei `fee=yes`/Betrag; `price.raw` trägt den Roh-Tag verbatim. Fehlt opening_hours UND fee → `facts` ABWESEND (honest, nie erfunden — Quellen-Priorität: kuratiert via `get_tourism_details` VOR diesem OSM-Bridge). **`attribution` (additiv, top-level)**: die ODbL-Namensnennung der gelieferten OSM-Daten (ODbL) — `{id:'ODbL-1.0', notice:'© OpenStreetMap contributors', url:'https://www.openstreetmap.org/copyright'}`. EINMAL pro Antwort (nicht pro Treffer) und nur wenn `sources` nicht leer ist. Nennst du OSM-Daten in der Antwort, nenne die Quelle „OpenStreetMap\" — die Lizenz-Auflage reist mit dem Tool-Output, nicht mit dem Prompt.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"osm_id":{"description":"Bare numeric OSM ID (e.g. `\"296222553\"` — no `node:` / `way:` prefix).\nMust match `^\\d+$` (validated at handler-time). The mcp-linking /\nmcp-geo callers already strip any type-prefix before exposing the\nvalue in `ids[].value`.","type":"string"}},"required":["osm_id"],"$schema":"https://json-schema.org/draft/2020-12/schema","description":"POI-detail lookup by OSM ID.\n\nAnswers with the OpenStreetMap record of that id: name, operator, the raw\ntags (`tourism`/`opening_hours`/`wheelchair`, …) and the geometry centre.\n`osm_id` is a bare numeric string (no `node:`/`way:`-prefix).","title":"PoiDetailsRequest"}},{"name":"get_tide","description":"Gezeiten (Niedrig-/Hochwasser) für einen Küstenort. Für „wann ist Ebbe in X\", „Gezeiten bei Y\" ist dieses Werkzeug die Auskunft, nicht das Allgemeinwissen — erst verorten, dann abfragen: `city` aus der Frage oder `lat`/`lon` aus `search_place`. **When to use**: Gezeiten-/Ebbe-/Niedrigwasser-/Hochwasser-Frage — DE: „niedrigwasser in Cuxhaven\", „wann ist Ebbe in Wilhelmshaven\", „Tide bei Norderney\". EN: „low tide in Cuxhaven\", „when is high tide at Norderney\". **When NOT to use**: normales Wetter → `get_current_weather`; Routing oder Stops → `connections`/`stops`; Binnen-Orte ohne Küstenbezug (Hannover, Hildesheim, Braunschweig). **Required args**: entweder `city` (z.B. 'Hamburg', 'Cuxhaven', 'Norderney') ODER (`lat`+`lon`). Optional bei Koord-Mode: `station_limit`, `date_start`, `date_end` (YYYY-MM-DD). **Typical chain**: (optional `search_place`(city)) → THIS_TOOL. **Multi-call**: ein Call pro Ort/Tag-Range. **Anti-Fab note**: Tide-Zeiten kommen NUR aus dem Tool-Output dieses Aufrufs. KEINE „typischen\" Tide-Schätzungen aus Bauch-Wissen.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"city":{"description":"City name to get tide data for (e.g. \"Hamburg\"). Use this or lat+lon.","type":["string","null"]},"date_end":{"description":"End date for tide data in YYYY-MM-DD format (only used with lat+lon)","type":["string","null"]},"date_start":{"description":"Start date for tide data in YYYY-MM-DD format (only used with lat+lon)","type":["string","null"]},"lat":{"description":"Latitude. Must be combined with lon. Use this or city.","format":"double","type":["number","null"]},"lon":{"description":"Longitude. Must be combined with lat. Use this or city.","format":"double","type":["number","null"]},"station_limit":{"description":"Maximum number of tide stations to return (only used with lat+lon)","format":"int64","type":["integer","null"]}},"$schema":"https://json-schema.org/draft/2020-12/schema","title":"TideRequest"}},{"name":"get_tourism_details","description":"Kuratiertes Detail zu EINEM Niedersachsen-Hub-Treffer per `id` — Beschreibung, Öffnungszeiten, ECHTER Preis, Adresse, Medien. Für „was kostet der Eintritt für X\", „wann hat X geöffnet\" ist dieses Werkzeug die Auskunft, nicht das Allgemeinwissen. **Tool-Semantik-Abgrenzung**: dieses Tool = Detail zu EINEM kuratierten NDS-Treffer (dessen `id`/`global_id`). NICHT `get_poi_details` (das ist die OSM-Detail-Bridge per OSM-ID). NICHT `search_tourism` (das ist die kuratierte Region-/Typ-SUCHE die die `id` erst liefert). **When to use**: nach `search_tourism` lieferte einen Treffer mit `id` und die Anfrage will Detail/Preis/Öffnungszeiten — DE: „was kostet der Eintritt für <Treffer>\", „Öffnungszeiten von <Treffer>\", „erzähl mir mehr zu <Event>\". EN: „opening hours / price of <hit>\". **When NOT to use**: ohne vorherigen `search_tourism`-Treffer mit `id` (KEIN raten/erfinden einer id); OSM-POI-Detail per OSM-ID → `get_poi_details`; Region-Suche → `search_tourism`. **Required args**: `id` (die `id` ODER `global_id` aus einem `search_tourism`-Treffer, z.B. \"e_101102405\"). Optional: `query` (Region/ Name-Hint — dieselbe Region wie bei der Suche; die kuratierte Quelle hat KEINEN Objekt-per-id-Endpunkt, daher re-sucht das Detail diesen Scope und matcht die id; ohne Hint ggf. honest-empty für ids außerhalb der Default-Seite). **Typical chain**: `search_tourism(region=<R>, type=event)` → `get_tourism_details(id=<treffer.id>, query=<R>)`. **Multi-call**: ein Call pro id; nach `search_tourism` mit N Treffern pro Top-Treffer parallel rufbar. **Anti-Fab note**: Name, Beschreibung, Preis, Öffnungszeiten, Adresse kommen AUSSCHLIESSLICH aus `sources[].subjects[].properties` dieses Aufrufs. Wenn `sources: []` → honest fallback („Zu diesem Treffer konnte ich keine Detail-Informationen abrufen\"), NIEMALS aus Trainings-Wissen. Returns {id, sources:[{source, license?, subjects:[{subject, properties:{name, type, description?, opening_hours?, price?, address?, date?, media?, coord?, url?, hub_detail_url?}}]}], facts?}. Die Felder sind die schema.org-Felder des kuratierten Datensatzes: `price` aus `priceRange`/`offers`, `opening_hours` aus `openingHoursSpecification`, `coord` aus `geo`, `url` = schema.org-Link — alle additiv, present nur wo die Quelle sie trägt. **Bei einer Tour (`t_…`) zusätzlich**: `path_on_map` — ihr Verlauf als `[{lat,lon}]`, ausgedünnt auf die Punkte, die seine Form tragen; das ist die zeichenbare Strecke, und NUR das Detail trägt sie (die Suche nicht). Dazu die Achsen des Datensatzes: `length_m`, `duration_min`, `ascent_m`/`descent_m`, `round_trip`, `activities` (wörtlich, als was er die Tour führt). Alle aus `ET2014A.json`, alle nur present wo die Quelle sie führt — eine fehlende Achse ist unbekannt, keine Null. **`facts` (additiv, top-level)**: normalisierte Öffnungszeiten + Preis mit Pro-Feld-Provenance — kuratiert hat Quellen-Priorität VOR der OSM-Bridge `get_poi_details`. `price.free=true` bei explizit kostenlos/Eintritt frei, `false` bei einem Betrag; `price.raw` trägt den kuratierten Preis-String wörtlich. Fehlen opening_hours UND price → `facts` ABWESEND (ehrlich, nie erfunden). **`hub_detail_url` (additiv, in `properties`, nur bei `source: 'niedersachsen-hub'`)**: die kanonische Entitätsseite dieses Datensatzes beim Niedersachsen-Hub, dem Nutzer nennbar — NICHT die Betreiber-Website, die daneben in `url` steht. Jeder Datensatz dieser Quelle, dessen Art beim Hub eine Eintragsseite hat, trägt sie schon im ersten Aufruf: POI (`p_…`), Tour (`t_…`), Veranstaltung (`e_…`), Unterkunft (`h_…`), Gastronomie (`g_…`), Gebiet (`r_…`). Ein Medien-Datensatz (`m_…`) hat keine solche Seite und trägt sie nicht. Fehlt sie, keine konstruieren. **`license` (additiv, NEBEN `source` im `sources[]`-Eintrag — nicht in `properties`)**: `{id?, url?, notice?, holder?}` = Lizenz-Kennung, Lizenz-Text-URL, der WÖRTLICH wiederzugebende Lizenzverweis (DZT-`copyrightNotice`, § 3 der DZT-Nutzerbedingungen) und der zu nennende Urheber (das `author`- bzw. `copyrightHolder`-Feld des Datensatzes). Gibt er nichts an, ist das Feld **abwesend** — nie eine geratene Vorgabe-Lizenz. Nennst du einen Datensatz mit `license.holder`, nenne den Urheber mit.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"id":{"description":"The `id` or `global_id` of a curated record from a prior\n`search_tourism` result (e.g. \"e_101102405\").","type":"string"},"query":{"description":"Region/name hint to scope the lookup — pass the same region you searched\nin `search_tourism`. The curated source has no object-by-id endpoint, so\nthe details lookup re-searches that scope and matches the id; without the\nhint it may honest-empty for ids outside the broad default page.","type":["string","null"]}},"required":["id"],"$schema":"https://json-schema.org/draft/2020-12/schema","title":"TourismDetailsRequest"}},{"name":"get_usage_guide","description":"Die ausführliche Anleitung zu den Werkzeugen dieses Katalogs: wofür ein Werkzeug da ist, wogegen es abzugrenzen ist, was seine Argumente bewirken, was zurückkommt und was daraus zitiert werden darf. Die `description` eines Werkzeugs ist die Kurzform, dieser Text die vollständige. **Optional** `tool` — der Name genau eines Werkzeugs, dessen Abschnitt du lesen willst; weggelassen kommt die ganze Anleitung. Lies den Abschnitt eines Werkzeugs, bevor du dessen Filter setzt oder ein leeres Ergebnis als Antwort weitergibst. **Der Abruf ohne `tool` ist teuer**: er bringt die Abschnitte ALLER Werkzeuge dieses Katalogs auf einmal, ein Vielfaches eines einzelnen. Setze `tool`, sobald feststeht, um welches Werkzeug es geht; ohne Argument nur für den Überblick über den ganzen Katalog.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"tool":{"description":"Der Name genau eines Werkzeugs aus diesem Katalog, dessen Abschnitt\nzurückkommen soll. Weglassen für die ganze Anleitung.","type":["string","null"]}},"$schema":"https://json-schema.org/draft/2020-12/schema","description":"What the tool takes.","title":"UsageGuideRequest"}},{"name":"get_weather_forecast","description":"Wetter-Vorhersage zu Koordinaten. Für „wie wird das Wetter morgen in X\" ist dieses Werkzeug die Auskunft, nicht das Allgemeinwissen — erst verorten, dann abfragen: `lat`/`lon` kommen aus `search_place`, nie aus eigener Schätzung. **When to use**: zukünftige Wetterfrage — DE: „wie wird das Wetter morgen / heute Abend / am Samstag in <ORT>\", „regnet es morgen\". EN: „forecast for <CITY> tomorrow / this weekend\". Auch für Halbtages-Touren-Planung („wenn das Wetter mitspielt\"). **When NOT to use**: jetziger Zustand → `get_current_weather`; Tide → `get_tide`. **Required args**: `lat`, `lon`. Optional: `units` ('metric' default, 'imperial', 'standard'), `lang` ('en' default, 'de'), `limit` (Anzahl Forecast-Slots). **Typical chain**: `search_place`(city) → THIS_TOOL(lat, lon, limit=N) → (optional `stops(node_types='poi')` + `get_poi_details` für POI-Auswahl je nach Wetter-Branche). **Multi-call**: ein Call pro Ort. Multi-Day-Queries decken sich über `limit`. **Anti-Fab note**: Vorhersage-Werte kommen NUR aus dem Tool-Output dieses Aufrufs — KEINE Tag-für-Tag-Schätzungen aus Trainings-Wissen. **`attribution` (additiv, top-level)**: die von der GeoNutzV verlangte Quellenangabe — `{id:'GeoNutzV', notice:'Quelle: Deutscher Wetterdienst', url:…}`. Nennst du Vorhersage-Werte, nenne den **Deutscher Wetterdienst** als Quelle.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"lang":{"description":"Language code for descriptions (e.g. \"en\", \"de\"). Defaults to \"en\".","type":["string","null"]},"lat":{"description":"Latitude of the location","format":"double","type":"number"},"limit":{"description":"Maximum number of forecast entries to return","format":"int64","type":["integer","null"]},"lon":{"description":"Longitude of the location","format":"double","type":"number"},"units":{"description":"Unit system: \"metric\" (°C, m/s), \"imperial\" (°F, mph), or \"standard\" (K). Defaults to \"metric\".","type":["string","null"]}},"required":["lat","lon"],"$schema":"https://json-schema.org/draft/2020-12/schema","title":"WeatherForecastRequest"}},{"name":"link_datetime","description":"Löst eine Zeit-Nennung in einen absoluten ISO-8601-Zeitpunkt auf — „morgen früh\", „heute Abend um 18 Uhr\", „in zwei Stunden\", „um 8:30\". Auch „jetzt\"/„now\"/„aktuell\" wird aufgelöst: nutze das als kanonische Quelle für die aktuelle Zeit, statt dir selbst ein Datum auszudenken. Das Ergebnis (`candidates[].datetime_range.start`) geht direkt als `connections.time` bzw. `departures.time` weiter. Nennt der Nutzer bereits eine vollständige ISO-8601-Zeit mit Offset, ist kein Aufruf nötig. **Pflicht**: `text` — die Nennung, so wie der Nutzer sie schrieb. **Optional**: `lang` (`'de'` Default, `'en'`, `'fr'`) und `tz` (IANA-Name; ohne ihn wird die Lokalzeit als `Europe/Berlin` gelesen und als korrekter UTC-Instant zurückgegeben). **Was zurückkommt**: EIN Zeitpunkt, kein Fenster — `start` und `end` sind derselbe Instant. Eine nicht auflösbare Phrase liefert `candidates: []`; dann nachfragen, statt selbst zu rechnen. **Anleitung**: `get_usage_guide` mit `tool='link_datetime'` — insbesondere, auf welche Stunde eine vage Tageszeit fällt und was dann zu tun ist. **Anti-Fab**: Datum und Uhrzeit sind Fakten wie eine Liniennummer und stammen aus diesem Werkzeug — kein selbst-erfundenes Datum, keine eigene Datums-Arithmetik. Returns `candidates[].datetime_range:{start,end}`.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"format":{"description":"Answer serialisation: `\"toon\"` (default) or `\"json\"` — the detail is in\n`get_usage_guide`. **An agent leaves this out.**","type":["string","null"]},"lang":{"description":"Optional ISO language code (default `de`). Supported: `de`, `en`, `fr`.","type":["string","null"]},"text":{"description":"Free-text time mention to resolve via the rustling NLU.\nE.g. \"morgen früh\", \"heute Abend um 18 Uhr\", \"in zwei Stunden\".","type":"string"},"tz":{"description":"Optional IANA timezone the stated local time is\ninterpreted in (default `Europe/Berlin`). DST-aware. Pass e.g.\n`\"Europe/Berlin\"`; omit for the default. Determines the UTC instant\nemitted for a wall-clock mention (\"8:30\" → `06:30Z` in summer).","type":["string","null"]}},"required":["text"],"$schema":"https://json-schema.org/draft/2020-12/schema","title":"LinkDateTimeRequest"}},{"name":"link_dynamic","description":"Match-Mention gegen Caller-supplied Kandidaten-Vokabular. **When to use**: Caller hat ein eigenes Vokabular (z.B. Custom-Enum, App-State, Domänen-spezifische Begriff-Liste) und will eine User-Mention darauf mappen. **When NOT to use**: Standard-Place/Stop/Time/MoT/Line-Linking → die spezialisierten `link_*`-Tools nutzen. Ohne Kandidaten ist der Linker leer. **Required args**: `text`, `candidates` (`[{name, synonyms?, coord?}, …]`) MUST be non-empty — die candidate-Liste IST das Vokabular. **Typical chain**: THIS_TOOL → (custom Caller-Logik). **Multi-call**: ein Call pro Mention/Vokabular-Set. **Anti-Fab note**: nur Kandidaten aus dem `candidates`-Arg matchen, KEIN impliziter Vokabular-Erweiterung. Returns `candidates[].candidate:<name>, score, span`.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"candidates":{"description":"Candidate vocabulary the linker should pick from. REQUIRED — the\nbackend rejects empty candidate lists with 400.","items":{"$ref":"#/$defs/CandidateInput"},"type":"array"},"text":{"description":"Free-text mention to match against the caller-supplied candidate list.","type":"string"}},"required":["text","candidates"],"$defs":{"CandidateInput":{"description":"One favorite candidate the linker should match against.","properties":{"coord":{"anyOf":[{"$ref":"#/$defs/CoordInput"},{"type":"null"}],"default":null},"name":{"type":"string"},"synonyms":{"default":[],"items":{"type":"string"},"type":"array"}},"required":["name"],"type":"object"},"CoordInput":{"properties":{"lat":{"format":"double","type":"number"},"lon":{"format":"double","type":"number"}},"required":["lat","lon"],"type":"object"}},"$schema":"https://json-schema.org/draft/2020-12/schema","title":"LinkDynamicRequest"}},{"name":"nearby","description":"Findet Haltestellen, POIs und Adressen im Radius um eine KOORDINATE (Schwerpunkt Hannover/Niedersachsen) — „was ist in der Nähe von <lat,lon>\", „welche Haltestellen liegen um diesen Punkt\". Liegt nur ein Name vor, kommt erst eine Auflösung: `search_place` für eine Stadt oder Region, sonst die Ortsauflösung. **Pflicht**: `latitude`, `longitude`, `radius_m`. **Optional**: `limit` (Default 10); `node_types` — EIN String, mehrere Arten mit Komma: `'stop'`, `'address'`, `'poi'`, die Sharing-Angebote `'bike_rental'`, `'scooter_rental'`, `'car_sharing'`, `'taxi_stand'`, die Abstellanlagen `'park_and_ride'`, `'bike_and_ride'`, oder `'any'` (`'stop,bike_rental'`); `include_mots` (legt die bedienenden Linien über die Haltestellen-Treffer); `only_available` (nur Sharing-Treffer mit gemeldetem freiem Fahrzeug — UNBEKANNTE Verfügbarkeit gilt nicht als frei). **Format**: kompakter Einrück-Text (TOON), kein JSON. **Anleitung**: `get_usage_guide` mit `tool='nearby'` — die Werte im Einzelnen, was `'any'` nicht abdeckt, und was ein Treffer trägt (`category`, `modality`, `parking`, `contactInfo`). **Anti-Fab**: nur die zurückgegebenen Namen, Typen, Distanzen und Verfügbarkeiten nennen; fehlt ein Feld, war es in der Quelle nicht getaggt — Öffnungszeiten und Preise stehen hier NICHT.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"format":{"description":"Answer serialisation: `\"toon\"` (default) or `\"json\"` — the detail is in\n`get_usage_guide`. **An agent leaves this out.**","type":["string","null"]},"include_mots":{"description":"`true` overlays the serving transit lines on stop results. Default\n`false`, which keeps the answer small.","type":["boolean","null"]},"latitude":{"description":"Latitude in WGS-84 decimal degrees.","format":"double","type":"number"},"limit":{"description":"Maximum number of results, at least 1. Default 10.","format":"int64","type":["integer","null"]},"longitude":{"description":"Longitude in WGS-84 decimal degrees.","format":"double","type":"number"},"node_types":{"default":null,"description":"Which kinds of place to answer with: the places the graph holds —\n`\"stop\"`, `\"address\"`, `\"poi\"` — plus the shared-mobility offers\n`\"bike_rental\"`, `\"scooter_rental\"`, `\"car_sharing\"` and `\"taxi_stand\"`,\nplus the places to leave a vehicle of one's own, `\"park_and_ride\"` and\n`\"bike_and_ride\"`, or `\"any\"` for all of them. Several combine in ONE\ncomma-separated string — `\"stop,bike_rental\"`; an array of the same\ntokens is read as that string. A kind outside the ten is an argument\nerror, not a dropped filter. `\"poi\"` alone also switches on the\nnon-tourism filter; omit (or `\"any\"`) for the mixed default, in which each\nrequested kind gets its share of `limit` and the shared-mobility offers\nshare one between them. The two park-and-ride kinds are the exception\n`\"any\"` does NOT cover — ask for them by name, and with a `radius_m` of a\nfew kilometres, because such sites are sparse.","type":["string","null"]},"only_available":{"description":"Set to `true` for „wo kann ich JETZT eines nehmen\": of the\nshared-mobility offers, only those whose live feed reports at least one\nvehicle ready to be taken are answered with. A station whose\navailability is unknown is NOT returned then, and neither is a taxi\nrank, which has no feed to report one. Omit (or `false`) to list every\nplace in range. The other kinds — `\"stop\"`, `\"address\"`, `\"poi\"`,\n`\"park_and_ride\"`, `\"bike_and_ride\"` — are unaffected either way.","type":["boolean","null"]},"radius_m":{"description":"Search radius in metres.","format":"double","type":"number"}},"required":["latitude","longitude","radius_m"],"$schema":"https://json-schema.org/draft/2020-12/schema","title":"NearbyRequest"}},{"name":"resolve_location","description":"Löst Freitext in die Id auf, mit der gefahren wird — die Haltestelle, die Adresse oder den POI hinter „von <X> nach <Y>\", „erzähl mir was über <POI>\". Wo Routing, Abfahrtstafel oder POI-Steckbrief eine Id brauchen, steht es davor — auch wenn der Nutzer die Stadt dazu nennt („Hauptbahnhof Hannover\"). Ein GEBIET (Stadt/Region) verortet `search_place`. **Pflicht**: `query` — nur der Name: `'Kelsterbach Bahnhof'`, NICHT `'für Kelsterbach Bahnhof'`. Wegzulassen sind `für`, `vom`, `von`, `nach`, `bis`; ein führendes `am`/`an`/`in`/`zur`/`auf` bleibt stehen — so heißen echte Halte („Am Wehrhahn\"). **Optional**: `lat`/`lon` (Ranking-Bias), `limit`, `node_types` (`'stop'`/`'address'`/`'poi'`/`'any'`, mehrere mit Komma in EINEM String; eine andere Art ist ein Argument-Fehler), `city_station` (Query = ganze Stadt → deren (Haupt-)Bahnhof). **Art-Wort in `node_types`, nicht in den Namen**: „Haltestelle X\" → `'stop'`, „Adresse X\" → `'address'`, „POI X\"/„Sehenswürdigkeit X\" → `'poi'`, `query` je ohne das Wort. **A→B: zweimal rufen** — Start, Ziel. Jeder Treffer trägt `type` und `location`; NUR ein `stop` hat eine fahrbare DH-Id, nie eine erfinden. **Anleitung**: `get_usage_guide` — Abgrenzung, Argumente, Rangfolge. **Anti-Fab**: nur die Treffer aus dem Output dieses Aufrufs verwenden.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"city_station":{"description":"When `Some(true)` AND the query is a whole CITY,\nresolve it to the city's (Haupt-)Bahnhof stop and return ONLY that stop\n(so „von Hannover nach Celle\" routes Bahnhof→Bahnhof). Forwarded to\nmobility-middleware as `cityStation=true`. A no-op for non-city queries.\nThe orchestrator routing-floor sets this deterministically per endpoint;\nthe model never has to.","type":["boolean","null"]},"format":{"description":"Answer serialisation: `\"toon\"` (default) or `\"json\"` — the detail is in\n`get_usage_guide`. **An agent leaves this out.**","type":["string","null"]},"lat":{"description":"Optional latitude (WGS-84) to bias ranking toward nearby stops.","format":"double","type":["number","null"]},"limit":{"description":"Maximum number of results. Defaults to 5 if omitted.","format":"int64","type":["integer","null"]},"lon":{"description":"Optional longitude (WGS-84) to bias ranking toward nearby stops.","format":"double","type":["number","null"]},"node_types":{"default":null,"description":"Which kinds of place to resolve to: `\"stop\"`, `\"address\"`, `\"poi\"` or\n`\"any\"`. Several combine in ONE comma-separated string — `\"stop,poi\"`;\nan array of the same tokens is read as that string. A kind outside the\nfour is an argument error, not a dropped filter — the shared-mobility\nkinds among them: a rental station and a taxi rank are not in the place\nindex this searches, they are answered by the radius search `nearby`.\nForwarded to\nmobility-middleware as `journey_node_types`. `\"poi\"` alone additionally\ndrops non-tourism POI names (Kindergarten, Apotheke, Schule, …) — use\nthis for tourism queries. Nennt der Nutzer die Art selbst, gehört sie\nhierher statt in `query`: \"Haltestelle X\" → `\"stop\"`, \"Adresse X\" →\n`\"address\"`, \"POI X\"/\"Sehenswürdigkeit X\" → `\"poi\"` (und `query` dann\nohne das Art-Wort).","type":["string","null"]},"query":{"description":"Free-text query, e.g. \"Hauptbahnhof Hannover\" or \"Linden Markt\". Nur der\nreine Orts-/Haltename — ohne das Wort, das ihn im Satz ankündigt:\n`\"Kelsterbach Bahnhof\"`, nicht `\"für Kelsterbach Bahnhof\"`. Ein\nführendes `am`/`an`/`in`/`zur`/`auf` bleibt dagegen stehen, weil echte\nHaltestellen so heißen (\"Am Wehrhahn\", \"In der Au\").","type":"string"}},"required":["query"],"$schema":"https://json-schema.org/draft/2020-12/schema","description":"Stop / address / POI lookup by free text, optionally biased toward a\ncoordinate. When `node_types` is `\"poi\"` alone, the backend additionally\ndrops non-tourism POI names (kindergartens, pharmacies, schools) so a\ntourism query is not flooded with everyday amenities.","title":"StopsRequest"}},{"name":"reverse_geocode","description":"Benennt, was an einer KOORDINATE liegt — der Ort („was liegt bei <lat,lon>\"), mit `level` eine bestimmte Ebene davon, mit `level='street'` die Straße samt nächster Hausnummer (der Lookup für eine GPS-Startposition). **Abgrenzung**: den Weg zurück (Name → Koordinate und OSM-Ids) geht `search_place`, die fahrbare Halte-Id liefert die Ortsauflösung, und `nearby` listet auf, was UM eine Koordinate liegt, statt den Punkt selbst zu benennen. **Pflicht**: `lat` und `lon` — geschrieben auch `latitude`/`longitude`, so wie `nearby` die Koordinate nimmt; je Aufruf nur eine der beiden Schreibweisen. **Optional**: `radius_m` (Suchradius in METERN; ein Ort, IN dem die Koordinate liegt, hat Abstand 0 und ist in jedem noch so engen Radius dabei — ohne `radius_m` die nächstgelegenen Treffer), `limit` (Höchstzahl Treffer, Default 10, Maximum 50) und `level` — `'place'` (Default: die ganze Ortshierarchie, feinste Ebene zuerst), `'city'` (die Stadt/Gemeinde), `'suburb'` (der Stadtteil) oder `'street'`. Kennt der Datensatz die gewünschte Ebene hier nicht, antwortet die nächst-gröbere, erkennbar am `place_type`; ein unbekannter Wert wirkt wie `'place'`. **Anleitung**: `get_usage_guide` mit `tool='reverse_geocode'`. **Anti-Fab**: nur die zurückgegebenen Orts- und Straßen-Namen verwenden, einschließlich der Hausnummer aus dem Datensatz — nie eine erfinden.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"format":{"description":"Answer serialisation: `\"toon\"` (default) or `\"json\"` — the detail is in\n`get_usage_guide`. **An agent leaves this out.**","type":["string","null"]},"lat":{"description":"Latitude of the coordinate to reverse-geocode. Also accepted spelled\n`latitude`, the way the vicinity search takes it — one of the two\nspellings per call.","format":"double","type":"number"},"level":{"description":"Optional resolution level. `\"place\"` (default) answers with the\ncoordinate's whole admin hierarchy, finest first (suburb → town → county\n→ state → country); `\"city\"` with the town/municipality it lies in;\n`\"suburb\"` with the suburb; `\"street\"` with the nearest STREET/ADDRESS\n(street name + nearest house number, e.g. `\"Münzstraße 3-4\"`) — the level\nneeded to label a real GPS start position. Where the data has no place at\nthe requested level, the next coarser one answers, recognisable by its\n`place_type`. Any other / omitted value behaves as `\"place\"`.","type":["string","null"]},"limit":{"description":"Optional maximum number of hits, nearest first. Default 10, upper bound\n50; a larger value is served as 50 and `0` as the default. Applies to\nevery level and with or without `radius_m`.","format":"uint32","minimum":0,"type":["integer","null"]},"lon":{"description":"Longitude of the coordinate to reverse-geocode. Also accepted spelled\n`longitude`, the way the vicinity search takes it — one of the two\nspellings per call.","format":"double","type":"number"},"radius_m":{"description":"Optional radius in METRES. When set, returns the places inside the\ncircle, sorted nearest-first and capped to `limit`. A place\nthe coordinate LIES INSIDE is at distance 0 and is therefore in every\nradius, however tight. When omitted, returns the `limit` nearest places.","format":"double","type":["number","null"]}},"required":["lat","lon"],"$schema":"https://json-schema.org/draft/2020-12/schema","title":"ReverseGeocodeRequest"}},{"name":"search_place","description":"Löst den NAMEN einer Stadt, Region oder eines Bezirks in Koordinaten und OSM-Ids auf — der erste Schritt, wenn ein GEBIET verortet werden muss („was kann ich in <REGION> unternehmen\", „wie ist das Wetter in <CITY>\"). **Für eine HALTESTELLE ist dieses Werkzeug fast immer falsch**: es kennt Gebiete, keine Bahnsteige — auf einen Bahnhofs-Namen antwortet es mit dem Stadtteil, und die Id, die es liefert, ist eine OSM-Id und keine fahrbare Halte-Id. **Trägt die Anfrage `Bahnhof`, `Hauptbahnhof`, `Hbf` oder `Bf`, gehört sie an die Ortsauflösung** — „Köln Hauptbahnhof\" und „Hannover Bahnhof\" also dorthin, nicht hierher: auf das erste antwortet dieses Werkzeug mit einem gleichnamigen Ortsteil (einem in Potsdam), auf das zweite mit der Stadt Hannover. Dasselbe für Adresse und POI — alles, was Start, Ziel oder Abfahrtsort einer Fahrt sein kann; eine Stadt als Fahrt-Endpunkt („von Hannover nach Celle\") ebenfalls. Von einer Koordinate zurück zum Namen geht `reverse_geocode`, die Umgebung einer Koordinate listet `nearby`. **Pflicht**: `name`. **Optional**: `lang` — wird für Symmetrie mit den übrigen Geo-Werkzeugen angenommen, derzeit aber nicht ans Backend durchgereicht und ändert das Ergebnis nicht. **Anleitung**: `get_usage_guide` mit `tool='search_place'` — die Abgrenzung im Detail, die typischen Ketten und der Umgang mit einem mehrdeutigen Namen. **Anti-Fab**: nur die zurückgegebenen Namen und Ids nutzen, keine Bauch-Geographie.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"format":{"description":"Answer serialisation: `\"toon\"` (default) or `\"json\"` — the detail is in\n`get_usage_guide`. **An agent leaves this out.**","type":["string","null"]},"lang":{"description":"Optional ISO language code (e.g. \"de\", \"en\"). Currently advisory: the\nplace names are the German ones the data carries, whatever is asked for.\nKept in the signature so a multilingual answer needs no new argument.","type":["string","null"]},"name":{"description":"Name of the place to search for. Free-text fuzzy match against the place\nindex behind this endpoint (e.g. \"Hannover\", \"Maschsee\", \"Wangerland\").","type":"string"}},"required":["name"],"$schema":"https://json-schema.org/draft/2020-12/schema","title":"SearchPlaceRequest"}},{"name":"search_tourism","description":"Kuratierte Tourismus-Suche des Niedersachsen-Hubs — die redaktionelle TIEFE die OSM fehlt: EVENTS MIT DATUM, PREISE, Beschreibungen, Medien, Öffnungszeiten, Touren. Für „welche Veranstaltungen sind in X\", „was kostet der Eintritt\" ist dieses Werkzeug die Auskunft, nicht das Allgemeinwissen — erst verorten, dann abfragen: `region`/`query` aus der Frage, `lat`/`lon` nur aus `search_place`. **Tool-Semantik-Abgrenzung**: dieses Tool = die kuratierte Quelle. NICHT `expand_kg_pois` (das ist OSM-BREITE — alle Tags, all-NDS, aber ohne Events/ Preise/Redaktion). Für Region-Tourismus dürfen BEIDE laufen (OSM-Breite ∥ NDS-Kuration). NICHT `nearby` (Radius-um-Punkt), NICHT `search_place` (Place-Disambiguierung). **When to use**: Events — DE: „was ist diese Woche/am Wochenende in <Region> los\", „Veranstaltungen in <Ort>\". EN: „events in <Region> this weekend\". Unterkünfte/Gastro — DE: „Hotels in <Ort>\", „Restaurants in <Ort>\". EN: „hotels/restaurants in <Ort>\". Touren — DE: „touristische Radrouten/Radtouren in <Region>\", „Radwanderwege/Wanderwege am <Ort>\" → `type=tour`; jede Tour trägt dann `length_m`, `duration_min`, `ascent_m`/`descent_m`, `round_trip` und `activities` (wörtlich, als was der Datensatz sie führt: „Fahrrad\", „E-Bike\", „Wandern\", „Kanu\") — DARAN auswählen, nicht am Namen. Bei mehreren die best-passende per `get_tourism_details` zeichnen, die anderen namentlich nennen und fragen, welche noch. **When NOT to use**: reine OSM-Breite/„alle Sehenswürdigkeiten per Tags\" → `expand_kg_pois`; Detail zu EINEM Treffer → `get_tourism_details`; Routing/ Abfahrten, auch „von A nach B mit dem Rad\" → `connections`/`departures`; Wetter/Tide → `get_current_weather`/`get_tide`. **Required args**: `region` ODER `query` (eines von beiden — Freitext-Region/ Name, z.B. region=\"Wangerland\", query=\"Sielhafenmuseum\"). Optional: `type` (genau eines von \"event\" | \"poi\" | \"accommodation\" (=\"hotel\") | \"gastro\" (=\"restaurant\") | \"tour\" — kuratierte Rad-/Wander-Routen, auch als \"radtour\"/\"radroute\"/\"radwanderweg\"/\"wanderweg\" akzeptiert; weggelassen = alle Typen; unbekannter Wert = alle. OHNE `type=tour` sind Touren praktisch nie unter den Treffern). Im Aktivitäts-Scope (type=\"poi\"/\"attraction\"/\"unternehmen\"/…) sind Unterkünfte AUSGESCHLOSSEN, im Region- wie im Vicinity-Modus (ein Hotel ist keine Unternehmung); für Hotels explizit type=\"accommodation\". `timeframe` (nur für type=event: \"today\" | \"tomorrow\" | \"weekend\" | \"this_week\" | \"month\" | ISO \"YYYY-MM-DD\" | Range \"YYYY-MM-DD..YYYY-MM-DD\"; ohne timeframe = kommende Events ab heute), `limit` (Default 8, clamped 1..=20), `family_only` (bool, Default false — nur die kuratierten Family-Kategorien, je mit der belegten `suitability`-Achse) und, nur zusammen damit, `indoor_only` (bool — davon nur die Schlechtwetter-tauglichen). `activity` (nur mit `type=tour`): wie eine Tour zurückgelegt wird — \"wandern\" | \"fahrrad\" | \"kanu\" + Synonyme (\"wanderweg\"/\"e-bike\"/ \"paddeln\"). Filtert serverseitig auf die GANZE Familie kuratierter Aktivitätswerte, also auch \"Winterwandern\"/\"Nordic Walking\"/ \"Mountainbike\" — 8 % der zu Fuß zurückgelegten Touren tragen keinen \"Wandern\"-Wert; weglassen oder unbekannt = alle. **Coord-Vicinity-Modus (Umkreis, POI-Anker)**: für „<Kategorie> am/bei <POI>\" den POI ZUERST via `search_place` zu einer Coord auflösen, dann `lat`+`lon` setzen (beide nötig; + optional `radius_m`, Default 2000 m, clamped 100..=20000). `region`/`query` sind dann nur Pool-Hint, nicht mehr Pflicht; die Treffer sind auf den Umkreis gefiltert, nächster zuerst, je mit `distance_m` in Metern. NUR in diesem Modus kommt die OSM-Breite dazu, entdoppelt gegen die Kuration (kuratierter Treffer gewinnt): bei `type=gastro` die `amenity`-Gastronomie, im Aktivitäts-Scope (`type=poi`, „unternehmen\") `tourism` ∈ attraction/museum/…, `historic` und `leisure` ∈ park/garden/… — AUSGESCHLOSSEN bleiben Unterkünfte (hotel/hostel) und Alltags-Versorgung (`shop`, Apotheke/Bank/Arzt), beides keine Ausflugsziele. **Typical chain**: `search_tourism(region=<R>, type=event)` → `get_tourism_details(id, query=<R>)` pro Treffer — der Regions-Hinweis ist nötig, sonst antwortet das Detail leer. **Multi-call**: ein Call pro Region/Typ. **Anti-Fab note**: Event-Namen, DATEN, PREISE, POI-Namen, Adressen kommen AUSSCHLIESSLICH aus `pois[]` / `events[]` dieses Aufrufs. Wenn `returned: 0` / `pois: []` / `events: []` → ehrlich sagen, dass für <Region> nichts abrufbar war, NIEMALS Events/Preise/POIs aus Trainings-Wissen ergänzen. Datum eines Events ist tool-belegt (`events[].date` / `pois[].date`, ISO-8601 Europe/Berlin) ODER abwesend — NIE geschätzt. **DZT-Bündelung (zweite kuratierte Quelle, DE-weit)**: kuratierte Treffer AUSSERHALB Niedersachsens kommen aus der DZT-KG (Deutsche Zentrale für Tourismus); Dubletten werden de-dupliziert, in der NDS-Region gewinnt der Niedersachsen-Hub-Eintrag (regionale Tiefe). Bei `type=tour` bleibt sie AUSSEN VOR — sie führt keinen Touren-Inhaltstyp; kuratierte Touren gibt es nur für Niedersachsen, außerhalb ehrlich \"keine Tour abrufbar\". Returns {place, source:'niedersachsen-hub', type, returned, mode?, center?, radius_m?, pois:[{name,type,description?,address?,uri?,date?,price?,media?, coord?,opening_hours?,id?,distance_m?,source?,license?,hub_detail_url?, length_m?,duration_min?,ascent_m?,descent_m?,round_trip?,activities?}], events:[{name,date,location,source?,license?}]}. Das Top-Level `source` bleibt shape-stabil; welche Quelle einen Treffer geliefert hat, sagt sein eigenes `source` ('osm' | 'niedersachsen-hub' | 'dzt'). `id` ist der Schlüssel für `get_tourism_details`; die Felder folgen schema.org. **`license` (additiv, PRO Treffer)**: die Lizenz, die der Datensatz selbst angibt — `{id?, url?, notice?, holder?}`: `id` = Lizenz-Kennung (z.B. 'CC-BY-SA-4.0', 'CC0-1.0', 'ODbL-1.0'), `url` = Lizenz-Text/Deed, `notice` = der WÖRTLICH wiederzugebende Lizenzverweis (DZT-`copyrightNotice`, § 3 der DZT-Nutzerbedingungen; bei OSM-Treffern die ODbL-Namensnennung), `holder` = der zu nennende Urheber (das `author`- bzw. `copyrightHolder`-Feld des Datensatzes). Gibt er nichts an, ist das Feld **abwesend** — nie eine geratene Vorgabe-Lizenz. Nennst du einen Treffer mit `license.holder`, nenne den Urheber mit. **`hub_detail_url` (additiv, PRO Treffer, nur bei `source: 'niedersachsen-hub'`)**: die Entitätsseite dieses Treffers beim Niedersachsen-Hub, dem Nutzer nennbar — NICHT die Betreiber-Website (`uri`). Jeder Treffer, dessen Art beim Hub eine Eintragsseite hat, trägt sie: POI (`p_…`), Tour (`t_…`), Veranstaltung (`e_…`), Unterkunft (`h_…`), Gastronomie (`g_…`), Gebiet (`r_…`). Ein Medien-Datensatz (`m_…`) hat keine und trägt keine; fehlt sie, keine konstruieren. Mit `family_only` trägt jeder Treffer zusätzlich `family`, `suitability` ({family, age_bands, pushchair, seal (Kinderferienland-Siegel), indoor_bad_weather}) und, wo die Quelle sie führt, `price_child`/`price_family` — alle feature-belegt: „kinderfreundlich\" ist belegt, nicht geraten.","write_action":false,"price_micros":0,"input_schema":{"type":"object","properties":{"activity":{"description":"How a TOUR is travelled, for `type=tour`: \"wandern\" (also \"wanderweg\",\n\"spaziergang\", \"hiking\", \"zu Fuß\"), \"fahrrad\" (also \"radtour\", \"e-bike\",\n\"mountainbike\", \"cycling\") or \"kanu\" (also \"paddeln\", \"SUP\"). Each word\nselects the whole family of curated activity values, so a hiking question\nalso finds the records filed under \"Winterwandern\", \"Nordic Walking\" or\n\"Spaziergang\" — 8 % of the walked ones carry no \"Wandern\" value at all.\nWhich value a given record carries is on the record (`activities`).\nOmit to search every activity; an unrecognised word is treated as \"all\"\n(never an error).","type":["string","null"]},"family_only":{"description":"Family mode: search the curated NDS family categories\n(Tierpark/Freizeitpark/Spielplatz/Erlebnisbad/Badesee/Kletterpark/…) and\noverlay the curated suitability axis (age bands, Kinderwagentauglich,\nKinderferienland-Siegel, Schlechtwetterangebot). Every record is then\nfamily-belegt + carries an additive `suitability` object. Defaults false.","type":["boolean","null"]},"indoor_only":{"description":"Restrict to rain-safe family records (the\n`suitability.indoor_bad_weather` / Schlechtwetterangebot proxy). Only\nmeaningful with `family_only=true`.","type":["boolean","null"]},"lat":{"description":"Coord-vicinity mode — center latitude. When `lat` AND `lon`\nare both given the search becomes a vicinity search around the point:\nonly curated hits within `radius_m` are returned (proximity-filtered on\nthe `coord` field), sorted nearest-first with an additive `distance_m`.\nUse it for POI-anchor questions (\"Restaurants am Maschsee\"): resolve the\nPOI to a coord first, then pass it here. `region`/`query` then act as the\nenclosing-city candidate-pool hint (and are no longer required).","format":"double","type":["number","null"]},"limit":{"description":"Maximum number of curated entities to return (default 8, clamped 1..=20).","format":"int64","type":["integer","null"]},"lon":{"description":"Coord-vicinity center longitude (paired with `lat`).","format":"double","type":["number","null"]},"query":{"description":"Free-text query (name / keyword) when not searching a whole region,\ne.g. \"Sielhafenmuseum\". Alias for `region`; one of the two is required.","type":["string","null"]},"radius_m":{"description":"Coord-vicinity radius in metres (default 2000, clamped 100..=20000).","format":"int64","type":["integer","null"]},"region":{"description":"Region or place to search within, as free text (e.g. \"Wangerland\",\n\"Hooksiel\", \"Hannover\"). Either `region` or `query` must be given;\n`region` is the natural choice for a place-scoped tourism search.","type":["string","null"]},"timeframe":{"description":"Optional event-date window for `type=event`: \"today\", \"tomorrow\",\n\"weekend\", \"this_week\", \"month\", or an explicit ISO date\n\"YYYY-MM-DD\" / range \"YYYY-MM-DD..YYYY-MM-DD\". With no timeframe the\nadditive `events` list defaults to upcoming events (today onward).","type":["string","null"]},"type":{"description":"Content type to narrow the search. One of: \"event\", \"poi\",\n\"accommodation\" (a.k.a. \"hotel\"), \"gastro\" (a.k.a. \"restaurant\"),\n\"tour\". Omit to search across all curated types. Unknown values are\ntreated as \"all\" (never an error).","type":["string","null"]}},"$schema":"https://json-schema.org/draft/2020-12/schema","title":"SearchTourismRequest"}}],"scan":{"score":67,"grade":"C","scanned_at":"2026-09-22T21:27:22.659Z","report":{"scannerVersion":"0.1.9","scannedAt":"2026-09-22T21:27:22.655Z","components":{"code":{"score":-1,"max":25,"notes":["remote-only server, no package to scan"]},"reliability":{"score":20,"max":20,"notes":["remote reachable in 1553ms"]},"poisoning":{"score":13,"max":15,"notes":["14 tool descriptions checked"]},"auth":{"score":10,"max":15,"notes":["open endpoint, read-only tools"]},"maintenance":{"score":3,"max":15,"notes":["no repository listed"]},"identity":{"score":4,"max":10,"notes":["verified namespace with website, no repo"]}},"findings":[{"id":"poison.long-description","severity":"low","component":"poisoning","title":"Unusually long tool description (over 2,000 characters)","evidence":"tool get_poi_details: …POI-Detail-Lookup per OSM-ID (Daten aus OpenStreetMap). Für „Öffnungszeiten von X\", „erzähl mir was über X\" ist dieses Werkzeug die Auskunft, nicht das Allgemeinwissen — erst verorten, dann abfragen: die `osm_id` stammt aus `nearby`/`stops`/`search_place`. **When to use**: nach `nearby` / `stops` / `search_place` lieferte einen POI mit `osm_id` und die Anfrage will Detail-Info — DE: „erzähl mir was über <POI>\", „was kostet der Eintritt\", „Öffnungszeiten von <POI>\", „bei dem Wetter in <REGION> unternehmen\". EN: „tell me about <POI>\", „opening hours of <POI>\", „what can I do in <REGION> given the weather\". **When NOT to use**: für Stadt-/Region-IDs (nutze `lookup_place_osm` oder `search_place`); ohne vorherigen Tool-Call mit OSM-ID-Output (KEIN raten); wenn die Anfrage Routing/Abfahrten will (dann `connections`/`departures`). **Required args**: `osm_id` — bare numeric string (pattern `^\\d+$`, z.B. \"296222553\"), kein `node:`/`way:`-Prefix (der wurde beim emittierenden Tool bereits gestrippt). **Typical chain**: TWO distinct chains feed this tool — (1) WETTER-CONTEXT: `get_current_weather` + `nearby`(node_types='poi') → THIS_TOOL ×3-5 (Trigger: „bei dem Wetter\", „given the weather\"). (2) REGION-TOURISM: `search_place` + `stops`(node_types='poi') → THIS_TOOL ×3-5 (Trigger: „was kann ich in <REGION> unternehmen\" ohne Wetter-Bezug — dichtere OSM-Coverage über stops-Pfad). **Multi-call**: JA. Nach `stops`/`nearby` mit N POI-Treffern: THIS_TOOL pro Top-3 bis Top-5 OSM-IDs PARALLEL aufrufen — jeder Call ist unabhängig, ein Burst ist erlaubt. NIE nur 1× rufen wenn N>1 POIs zurückkommen. **Anti-Fab note**: POI-Name, Operator, Öffnungszeiten, Adresse, Tags kommen AUSSCHLIESSLICH aus `sources[].subjects[].properties` dieses Aufrufs. Wenn `sources: []` → honest fallback („Zu diesem Ort konnte ich aktuell keine Detail-Informationen abrufen\"), NIEMALS aus Trainings-Wissen ergänzen. OSM-Coverage: deutschlandweit; Tag-Dichte variiert je Region wie in OSM üblich. **Shape**: `{osm_id, sources:[{source:'openstreetmap', subjects:[{subject, properties:{…raw OSM tags: name, tourism/historic/leisure, opening_hours, fee, website, wheelchair, addr:*…}, coord?:{lat,lon}}]}], facts?:{opening_hours?:{value,source}, price?:{free?,raw,source}}}`. Die rohen Tags tragen Typ/Adresse/Öffnungszeiten/Preis/Beschreibung bereits; `coord` (Geometrie-Center, additiv) liegt je Subject NEBEN `properties` und speist die Karte — present nur wenn das Element Geometrie hat. **`facts` (additiv, top-level)**: normalisierte Öffnungszeiten + Preis mit Pro-Feld-Provenance (`source:'openstreetmap'`) aus den `opening_hours`/`fee`-Tags — EINE stabile Stelle statt Roh-Tags durchwühlen. `price.free=true` bei `fee=no` (explizit „kostenlos\"/Eintritt frei), `false` bei `fee=yes`/Betrag; `price.raw` trägt den Roh-Tag verbatim. Fehlt opening_hours UND fee → `facts` ABWESEND (honest, nie erfunden — Quellen-Priorität: kuratiert via `get_tourism_details` VOR diesem OSM-Bridge). **`attribution` (additiv, top-level)**: die ODbL-Namensnennung der gelieferten OSM-Daten (ODbL) — `{id:'ODbL-1.0', notice:'© OpenStreetMap contributors', url:'https://www.openstreetmap.org/copyright'}`. EINMAL pro Antwort (nicht pro Treffer) und nur wenn `sources` nicht leer ist. Nennst du OSM-Daten in der Antwort, nenne die Quelle „OpenStreetMap\" — die Lizenz-Auflage reist mit dem Tool-Output, nicht mit dem Prompt.…"},{"id":"maint.no-repo","severity":"low","component":"maintenance","title":"No source repository listed"}],"inputs":{"probes":[{"url":"https://ai.projektionisten.eu/tmcp","reachable":true,"authRequired":false,"latencyMs":1553,"serverInfo":{"name":"TourismMCP","version":"0.1.0"}}],"packages":[],"repo":{"found":false},"icon":{"url":null,"source":"none"},"presence":{"stars":null,"forks":null,"downloadsWeek":null,"license":null,"lastPushAt":null,"score":8}}}},"grade_history":[],"reviews":[]}