Expat Living Italy
Server Details
Sourced data on Italy's ~7,900 comuni for people relocating or retiring there.
- Status
- Healthy
- Uptime
- 100.0% over the last 24 hours
- Last Tested
- Transport
- Streamable HTTP · MCP 2025-11-25
- URL
- ChatGPT
- –
- Claude
- –
TDQS
Scored across 12 tools
Most tools target an unambiguous resource+action pair (get_comune, get_region, get_province, get_nationality, list_articles), and descriptions explicitly delimit them. The main soft spots are search_comuni vs find_comuni (identifier lookup vs filtered search) and get_coverage vs list_datasets (gap detail vs catalog), which are clarified in prose but could still be mixed up by an agent.
A strict verb_noun pattern throughout: check_, find_, get_, list_, search_ followed by a clear noun. No style mixing, camelCase never appears, and the verbs map predictably to retrieval semantics.
Twelve tools for a multi-grain geographic/tax/article domain is well scoped, with no near-duplicate endpoints padding the set. Each tool earns its place at a distinct grain or entity.
Covers lookup, profile, filtered discovery, tax regimes/eligibility, coverage diagnostics, nationality, and editorial content across comune/province/region grains — a full read-only lifecycle. Minor gaps: no list_regions or list_provinces to discover slugs (you must arrive with one), and no dedicated single-regime detail tool beyond list_tax_regimes.
Available Tools
12 toolscheck_tax_eligibilityDoes a comune meet a regime's geographic criteria?ARead-onlyIdempotentInspect
For one regime slug (from list_tax_regimes) and one 6-digit ISTAT code (from search_comuni): qualifies, does_not_qualify or not_assessable, with a one-sentence reason to quote. not_assessable is never a no. This is about the place only; say so, and never present it as whether a person qualifies or as tax advice.
| Name | Required | Description | Default |
|---|---|---|---|
| slug | Yes | A regime slug from /tax/regimes, e.g. "regime-7-percent-pensioners". | |
| comune | Yes | 6-digit ISTAT comune code. Find it with /comuni?q=. |
Output Schema
| Name | Required | Description |
|---|---|---|
| place | Yes | |
| reason | Yes | One sentence on why, safe to quote. |
| regime | Yes | |
| headroom | No | Residents between this comune and the ceiling, when it qualifies. |
| disclaimer | Yes | |
| population | Yes | |
| attribution | Yes | |
| maxPopulation | No | |
| placesNotPeople | Yes | |
| ruleIsIncomplete | Yes | |
| geographicEligibility | Yes | qualifies: the comune meets the regime's verified geographic criteria. does_not_qualify: it doesn't, and the rule has no unverified route that could admit it. not_assessable: no geographic answer can be given (see reason). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover read-only/idempotent/closed-world, and the description adds the semantic traits annotations cannot: the three outcome values, the critical rule that 'not_assessable is never a no', and that a one-sentence reason is returned for quoting. That is exactly the interpretive context needed to avoid misreading a null-ish result.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tight sentences plus a caveat clause, with the outcome enum front-loaded before the interpretive warnings. The clipped phrasing ('for one regime slug ... and one 6-digit ISTAT code') is dense but every clause carries instruction; nothing is padding.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists so return shape needs no explanation, and annotations cover the safety profile. The description supplies what structured fields cannot: input provenance, the three-state semantics, the not_assessable caveat, and the place-only framing — leaving no material ambiguity for a two-parameter read tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and both properties already document format and provenance ('regime slug from /tax/regimes', '6-digit ISTAT code ... /comuni?q='). The description restates the same sourcing hints without adding syntax, constraints, or edge cases beyond the schema, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Names a specific operation (evaluate geographic eligibility) over two precisely identified inputs and enumerates the exact three-valued outcome. It also scopes itself against adjacent concerns ('about the place only'), so an agent can separate it from person-level or advice-oriented questions.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly routes the agent to the two sibling tools that supply each input ('from list_tax_regimes', 'from search_comuni') and states when-not to use the result ('never present it as whether a person qualifies or as tax advice'). Both the positive path and the exclusions are spelled out.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
find_comuniFind comuni by criteriaARead-onlyIdempotentInspect
Which Italian comuni match: region, province, population range, purchase-price and rent ranges (OMI), population change 2019–2025, the eight shortlist filters (January low, days above 30 C, regional hospital-care score, all three care scores at 60, tourist beds per resident, outside seismic zone 1, road time to a hospital with inpatient beds, road time to an airport) and/or the verified comuni of a tax regime. Paged (max 100) and sortable. Healthcare scores are region grain (autonomous-province grain for Bolzano and Trento): every town in the region shares the score. A comune with no price, rent, population-change, climate, healthcare, tourism, seismic or drive-time figure is left out of the filter on it and COUNTED in missingData — never treated as cheap, free or stable; say how many were left out. The taxRegime filter lists only comuni on the regime's verified routes: a comune missing from it is NOT a 'no' (the response says so in taxRegime.note). Rows carry plain numbers; their source and vintage are in figures. Use search_comuni to turn a town name into a code, get_comune for one town's full profile.
| Name | Required | Description | Default |
|---|---|---|---|
| q | No | Only comuni whose name contains this (case- and accent-insensitive). | |
| sort | No | Sort key. A comune without a figure for the sort key always sorts last. | population |
| limit | No | Rows per page, 1–100. Default 25. | |
| order | No | Default: desc for population, populationChange, januaryLow and hospitalCareScore; asc for name, purchasePrice, rent, daysAbove30C, bedsPerResident, seismicClass, hospitalDriveMinutes, emergencyDriveMinutes, deaDriveMinutes, airportDriveMinutes, supermarketDriveMinutes, pharmacyDriveMinutes and stationDriveMinutes. | |
| offset | No | Rows to skip, for the next page. | |
| region | No | A region name or slug, e.g. "Abruzzo" or "valle-d-aosta". | |
| province | No | A province's two-letter code ("AQ") or slug ("l-aquila"). | |
| taxRegime | No | A verified geographic regime slug from /tax/regimes. Lists only comuni on the regime's verified routes; a comune missing from the list is NOT a "no" (the rule may admit comuni by routes not yet encoded). | |
| maxPopulation | No | Residents, inclusive. | |
| minPopulation | No | Residents, inclusive. | |
| minJanuaryLowC | No | [UI key jan] Average January daily minimum (ERA5-Land, 2015 to 2024) at least this many degrees C, whole numbers -10 to 12. Town grain. A comune with no figure is left out and counted in missingData. | |
| maxDaysAbove30C | No | [UI key heat] Average days a year above 30 C (ERA5-Land, 2015 to 2024) at most this many, 0 to 120. Town grain. No figure: left out and counted. | |
| emergencyDeaOnly | No | [UI key edlevel=dea] With maxEmergencyDriveMinutes: count only a DEA (level I or II), not a Pronto Soccorso. A DEA is a larger emergency department with more specialties on site. Ignored without maxEmergencyDriveMinutes. | |
| maxRentEurM2Month | No | Rent in EUR/m2 per month, inclusive. A comune with no rent figure is left out. | |
| minRentEurM2Month | No | Rent in EUR/m2 per month, inclusive. A comune with no rent figure is left out. | |
| maxBedsPerResident | No | [UI key beds] Registered tourist beds per resident (ISTAT, 2024) at most this; one of 0.1, 0.5, 1. Town grain. A comune with no figure is left out and counted; a real zero is a figure. | |
| excludeSeismicZone1 | No | [UI key seis] true: leave out comuni in official seismic zone 1, the highest hazard class (hazard class 4 in the map layer). A legal classification from the Dipartimento della Protezione Civile (31 May 2025), not a forecast. A comune with no single classification is left out and counted. | |
| minHospitalCareScore | No | [UI key hosp] Ministero della Salute NSG 2024 hospital-care score at least this, 50 to 100; 60 is the official sufficiency line. REGION grain: every comune in a region shares its region's score, except Bolzano-province and Trento-province comuni, which carry their own autonomous province's score. No figure: left out and counted. | |
| maxPurchasePriceEurM2 | No | Purchase price in EUR/m2, inclusive. A comune with no price is left out, never treated as cheap. | |
| minPurchasePriceEurM2 | No | Purchase price in EUR/m2, inclusive. A comune with no price is left out, never treated as cheap. | |
| allCareScoresAtLeast60 | No | [UI key nsg60] true: the NSG prevention, community-care and hospital-care scores must all be at least 60. Region grain (autonomous-province grain for Bolzano and Trento). A comune missing any of the three is left out and counted. | |
| maxAirportDriveMinutes | No | [UI key adrive] Minutes by road from the comune's main town to the nearest of the airports where ENAC recorded at least 4,000 passengers in 2025 (it leaves out Taranto-Grottaglie and Brescia-Montichiari, which carried almost none), at most this; one of 60, 90, 120, 180. Town grain, free-flow, approximate; says nothing about flights. No figure: left out and counted. | |
| maxPopulationChangePct | No | Population change 2019 to 2025, in percent, inclusive. | |
| minPopulationChangePct | No | Population change 2019 to 2025, in percent, inclusive. A negative value means decline. | |
| maxHospitalDriveMinutes | No | [UI key hdrive] Minutes by road from the comune's main town to the nearest hospital with inpatient beds (Ministero della Salute 2023 list, OpenStreetMap roads) at most this; one of 15, 30, 45, 60. Town grain, free-flow, approximate. Not necessarily a hospital with an emergency department. A comune with no figure (for example a ferry-only island) is left out and counted. | |
| maxEmergencyDriveMinutes | No | [UI key edrive] Minutes by road from the comune's main town to the nearest emergency department in the Ministero della Salute's emergency network (sites active at 31 December 2025: DEA level II, DEA level I or Pronto Soccorso; OpenStreetMap roads) at most this; one of 15, 30, 45, 60. Town grain, free-flow, approximate; not a service guarantee. Combine with emergencyDeaOnly to count only DEA level I or II. A comune with no figure is left out and counted. |
Output Schema
| Name | Required | Description |
|---|---|---|
| limit | Yes | |
| total | Yes | Comuni matching every filter, before paging. |
| offset | Yes | |
| applied | Yes | The filters and sort as understood, with region and province resolved to their canonical names. |
| figures | Yes | |
| hasMore | Yes | |
| results | Yes | |
| returned | Yes | |
| taxRegime | No | Present only when taxRegime was filtered on. |
| nextOffset | Yes | Pass as offset for the next page; null on the last page. |
| attribution | Yes | |
| missingData | Yes | One entry per figure you filtered on: how many comuni were left out for having no figure. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnly/idempotent annotations, the description discloses paging limits, sortability, region-grain healthcare scoring, missingData counting semantics, taxRegime omission semantics, and that row figures carry source/vintage metadata in `figures`. These are substantial behavioral details that materially affect interpretation of results.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the core purpose and is appropriately sized for a 26-parameter filtering tool. It is dense and packed into one long paragraph, which slightly reduces readability, but most sentences carry important caveats or routing guidance.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With rich annotations, complete schema coverage, and an output schema, the description supplies the remaining critical context: missing-data handling, regional grain caveats, taxRegime limitations, and sibling-tool routing. Nothing essential for correct invocation or interpretation appears to be missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the schema already documents all 26 parameters thoroughly. The description adds useful global semantics beyond the schema: missing figures are excluded and counted rather than treated as favorable, taxRegime omissions are not negatives, and healthcare scores are region-grain with autonomous-province exceptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description immediately states that this tool finds Italian comuni matching specified geographic, demographic, price, climate, healthcare, tourism, seismic, drive-time, and tax-regime criteria. It distinguishes itself from search_comuni, which converts a town name to a code, and get_comune, which returns one town's full profile.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly directs name-to-code lookup to search_comuni and single-town profile requests to get_comune, leaving find_comuni as the criteria-based multi-town filter. It also gives important usage caveats for missing-data and taxRegime results, preventing misinterpretation of omitted rows as negative answers.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_articleRead one articleARead-onlyIdempotentInspect
One article's full text as Markdown, with its places, datasets and a ready-made citation. Quote figures from the dataset pages it cites, which carry their vintage. Content is free to use provided you credit Expat Living Italy and link to the article.
| Name | Required | Description | Default |
|---|---|---|---|
| slug | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| url | Yes | |
| body | Yes | The article as Markdown, blocks flattened. |
| deck | Yes | |
| slug | Yes | |
| title | Yes | |
| author | Yes | |
| places | Yes | |
| status | Yes | |
| topics | Yes | |
| updated | Yes | |
| datasets | Yes | |
| published | Yes | |
| attribution | Yes | |
| markdownUrl | Yes | The whole article as Markdown, with its citation at both ends. |
| supersededBy | No | Slug of the newer article that replaces this one. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover readOnly/idempotent/closed-world, and the description adds real behavioral context beyond them: the return is Markdown, it bundles places, datasets and a citation, figures should be sourced from the cited dataset pages with their vintage, and reuse is permitted only with credit plus a link. That licensing constraint is genuinely useful and not captured by any annotation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, front-loaded with what is returned, then sourcing advice, then licensing. Dense and mostly waste-free, though the licensing sentence is arguably peripheral to tool selection.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return structure need not be restated, and annotations carry the safety profile. The definition is nearly complete for a one-parameter read tool; the only real omission is how an agent obtains a valid slug and what happens on an unknown one.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0% and the description never mentions the sole parameter 'slug' — not its format, not where to obtain it (presumably from list_articles or search). With a required parameter carrying no semantics anywhere, the description does not compensate for the gap.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names the specific resource and the exact payload: one article's full text as Markdown, plus its places, datasets and a citation. 'One article's...' implicitly separates it from the listing sibling, but it never names list_articles or another sibling explicitly, so differentiation is left to inference.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives usage guidance for the content ('quote figures from the dataset pages it cites') and the attribution obligation ('credit Expat Living Italy and link to the article'), but says nothing about when to call this tool versus list_articles, or how to obtain a valid slug. Usage is implied rather than stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_comuneGet one comune's profileARead-onlyIdempotentInspect
One Italian comune's population, foreign residents by citizenship, property purchase/rent price, price-to-rent, verified tax-regime eligibility, its province's rollup, seismicZone (the official zona sismica 1-4: a legal classification, not a forecast and not a verdict on any building; quote it with its label and say so — a comune that straddles zones has no single zone, never round it; the zone has nothing to do with tax eligibility, and is not the 7% regime’s “seismic” routes, which depend on the 2009 and 2016-17 earthquake areas), layers (climate, air quality, crime, healthcare quality, age, population change and every other Italy-map layer) and provinceWellbeing (ISTAT BES indicators, polarity-aware percentiles) — looked up by 6-digit ISTAT code. Each layer's grain and measuredFor say whose number it is: a province or region value is not a measurement of this comune — say so when you quote it. Every field is wrapped in a provenance envelope (source, vintage, extractedOn, license); repeat the vintage when you state the number, and if a value is null, say why rather than treating it as zero.
| Name | Required | Description | Default |
|---|---|---|---|
| istatCode | Yes | 6-digit ISTAT comune code, e.g. 075029. |
Output Schema
| Name | Required | Description |
|---|---|---|
| place | Yes | |
| layers | Yes | Every other map layer's value for this place, at the grain its source publishes. A null value means the source has no figure for this place (see `reason`). |
| articles | No | Published articles about this comune — the reporting behind the numbers. Credit required to reuse. |
| population | Yes | |
| attribution | Yes | |
| seismicZone | Yes | The comune's official seismic zone (zona sismica). A legal classification, not a forecast and not a verdict on any building. value is null, never a default zone, when the Department's table has no row for this comune. |
| provinceRollup | Yes | |
| priceToRentYears | Yes | Years of rent that equal the purchase price. Not a yield, an ROI, or a payback period. |
| provinceWellbeing | Yes | ISTAT BES well-being indicators for this comune's province — province-level, not a comune measurement. |
| purchasePriceEurM2 | Yes | OMI is Agenzia delle Entrate's official range of market values per m2, built from recorded sales and appraisals. Asking prices on listing sites are what sellers hope for, and usually run higher. |
| rentPriceEurM2Month | Yes | |
| taxRegimeEligibility | Yes | Verified regimes only — "placesNotPeople": geographic eligibility, not personal advice. |
| totalForeignResidents | Yes | Sum of foreignResidentsByCitizenship: only the citizenships this site tracks, so a floor, not every foreign resident — Moroccan, Chinese and Indian residents, among others, are not counted. For every foreign resident, see foreignResidentsAllCitizenships. null when the citizenship dataset has no row for this comune. |
| foreignResidentsByCitizenship | Yes | Counts by ISO-2 citizenship code. Citizenship, not birthplace — excludes foreign-born Italian citizens. value is null, never a zero-filled object, when ISTAT's citizenship dataset has no row at all for this comune. |
| foreignResidentsAllCitizenships | Yes | Residents holding any foreign citizenship: ISTAT's own total, not limited to the citizenships tracked in foreignResidentsByCitizenship. Citizenship, not birthplace. value is null, never 0, when ISTAT sends no total for this comune. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/closed-world, yet the description goes well beyond them: provenance envelopes (source, vintage, extractedOn, license), null-vs-zero semantics, and grain/measuredFor telling the agent a province or region value is not a measurement of this comune. It also pre-empts misuse of seismicZone with label, straddling-zone, and tax-eligibility caveats — exactly the 'quote it correctly' context annotations cannot carry.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Nearly all content earns its place, but it is delivered as one enormous run-on sentence with deeply nested parentheticals (the seismicZone parenthesis alone spans multiple clauses), which buries the seismic, layers, and provinceWellbeing fields mid-sentence. Front-loading of the core purpose is good; parseability is poor.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter read with an output schema and full annotation coverage, nothing an agent needs to call or correctly report the result is missing — it even covers citation discipline for vintage and null handling.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the single parameter is fully documented (pattern, example 075029). The description restates '6-digit ISTAT code' but adds no format, validation, or failure-mode detail beyond the schema, which is the baseline 3 case.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Names a specific resource (one Italian comune's profile) and a specific lookup key (6-digit ISTAT code), then enumerates the data domains returned: population, foreign residents, prices, price-to-rent, tax-regime eligibility, province rollup, seismicZone, layers, provinceWellbeing. The singular 'one' implicitly separates it from the batch/search siblings (find_comuni, search_comuni).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The lookup condition is clear ('looked up by 6-digit ISTAT code'), so the agent knows it needs an exact code in hand, which implies find_comuni/search_comuni precede it. What is missing is an explicit statement of that alternative relationship and any exclusion — the agent must infer it from the singular framing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_coverageWhat is missing, by datasetARead-onlyIdempotentInspect
For every dataset: how many places it covers, out of how many at its grain, plus the source's own coverage caveat. Pass dataset (a slug from list_datasets) to list the places that dataset lacks, with the gaps by region. Use it before telling anyone data does not exist for a place, and to explain a null. Missing means no figure, never a zero. Keys a source publishes that match no current place (a superseded province code) are reported and never mapped onto a current place. Drafts are listed as not served, without counts.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Missing places per page when dataset is given, 1–100. Default 50. | |
| offset | No | Missing places to skip, for the next page. | |
| dataset | No | A dataset slug from /datasets. Omit it for the summary of every dataset; pass it to list the places that dataset lacks. |
Output Schema
| Name | Required | Description |
|---|---|---|
| datasets | Yes | |
| attribution | Yes | |
| missingPlaces | No | Present only when `dataset` was given and it has missing places. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnly/idempotent annotations, it discloses non-obvious domain semantics: missing means no figure and never a zero, superseded source codes are reported but never mapped onto a current place, and drafts appear as not served without counts. These are exactly the behaviours an agent could otherwise misreport.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The summary mode is front-loaded, followed by the optional mode and then the rule-based caveats; nearly every sentence carries distinct information. Some sentences are dense and stacked ('Missing means no figure, never a zero. Keys a source publishes that match no current place...'), but nothing is filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, return shape need not be explained; the description covers mode switching, the missing-vs-zero distinction, unmapped codes, and drafts, which is enough to call it correctly. Only the meaning of 'at its grain' is left as unexplained jargon.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the schema documents limit/offset ranges and the dataset slug on its own. The description still adds value by framing `dataset` as a mode switch (omit for the all-dataset summary, pass for the gap listing), which is more than a restatement of the property text.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states the resource precisely: per-dataset coverage counts (places covered out of places at that grain) plus the source's own caveat, and a second mode that lists missing places when `dataset` is passed. It is clearly separable from list_datasets, which is named as the source of the slug, and from the geographic lookup siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit when-to-use guidance: 'Use it before telling anyone data does not exist for a place, and to explain a null.' It also routes the agent to list_datasets for the required slug, so the sequencing with a sibling is spelled out.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_nationalityWhere one nationality lives in ItalyARead-onlyIdempotentInspect
For one tracked citizenship (ISO-2 code such as US, GB, DE): the comuni, provinces and regions where its holders concentrate, with counts and shares. It is CITIZENSHIP, not birthplace — foreign-born Italian citizens are not counted — and it carries small-number caveats: the comune ranking skips places under 1,000 residents by default and flags rows under 30 residents (smallNumber). A comune with no ISTAT row is left out, not counted as zero, so totals are floors. State those caveats when you quote a ranking; they are in the result's caveats. Province and comune lists are different levels of detail — say which.
| Name | Required | Description | Default |
|---|---|---|---|
| cc | Yes | ISO-2 citizenship code of one of the citizenships this site tracks, e.g. "US". | |
| limit | No | How many comuni and provinces to list, 1–50. Default 10. | |
| rankBy | No | share: residents with this citizenship as a fraction of the comune's population. count: absolute numbers. | share |
| minPopulation | No | Smallest comune, in residents, that may appear in the comune ranking. Default 1,000: in a village of sixty, four foreign residents is not a finding. Lowering it lets tiny places top the ranking. |
Output Schema
| Name | Required | Description |
|---|---|---|
| caveats | Yes | Read these before quoting a number; they travel with the data. |
| byRegion | Yes | |
| topComuni | Yes | |
| attribution | Yes | |
| nationality | Yes | |
| topProvinces | Yes | Province grain: a different level of detail from the comuni above. Sums only the comuni ISTAT has a row for. |
| nationalTotal | Yes | Residents with this citizenship in the comuni ISTAT reports on; `coverage` is how many comuni that is. |
| citizenshipNotBirthplace | Yes | Every count here is by CITIZENSHIP, not country of birth: it leaves out foreign-born people who hold Italian citizenship. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Adds substantial behavior beyond the annotations: distinguishes citizenship from birthplace, explains that foreign-born Italian citizens are excluded, discloses small-number suppression (default 1,000-resident floor, `smallNumber` flag under 30), and warns that missing ISTAT rows make totals floors. These are exactly the data-semantics details annotations cannot carry.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the purpose and efficient across three sentences, with no filler. It is dense but appropriately so for the caveats it must convey; the only redundancy is repeating that caveats live in the result's `caveats` field.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given that an output schema exists, the description need not explain return values. It supplies the critical data semantics (citizenship scope, suppression rules, floor totals) and the reporting instruction, so an agent can call and interpret the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, and the schema already documents all four parameters, including the minPopulation default and rationale. The description mostly restates that meaning and adds little per-parameter detail beyond the output-level `smallNumber` flag and the province/comune level distinction.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource: for one ISO-2 citizenship code, returns the comuni, provinces and regions where holders concentrate, with counts and shares. This is clearly distinguishable from sibling tools like get_comune, get_province and find_comuni, which operate on places rather than nationality concentration.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear usage context: it is citizenship not birthplace, and it instructs the agent to state caveats when quoting a ranking. However, it does not explicitly name alternatives or say when to prefer another sibling tool (e.g., get_province for place-level data) over this one.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_provinceGet one province's profileARead-onlyIdempotentInspect
One Italian province (two-letter code such as AQ, or its slug — a comune's provinceCode): population, OMI prices, foreign residents by citizenship (citizenship, not birthplace), ALL ISTAT BES well-being indicators with polarity-aware percentiles among provinces (no composite score), every province- and region-grain map layer, seismicZones (how many of its comuni sit in each official seismic zone — a count of comuni, never an average, since a province has no zone of its own), and how much of the province meets each verified tax regime (places, not people). Sources use different province lists — Sardinia was reorganized in 2026 — so a section a source lacks is null with a reason and listed in missingSections: report it as missing, never substitute another province's figure.
| Name | Required | Description | Default |
|---|---|---|---|
| code | Yes | A province's two-letter code, e.g. "AQ", or its slug, e.g. "l-aquila". Find it in a comune's `provinceCode`. |
Output Schema
| Name | Required | Description |
|---|---|---|
| layers | Yes | Every province- and region-grain map layer for this province, each saying whose number it is. |
| prices | Yes | |
| province | Yes | |
| wellbeing | Yes | |
| population | Yes | Sum of the province's comuni. |
| taxRegimes | Yes | Verified regimes with at least one qualifying comune here: how much of the province meets the geographic criteria. Places, not people; a regime absent here is not a no. |
| attribution | Yes | |
| seismicZones | Yes | How this province's comuni are spread across the official seismic zones: a count per zone, never an average. A province has no zone of its own. |
| missingSections | Yes | Every part of this profile a source has no figure for, with why. Sources use different province lists (Sardinia was reorganized in 2026): a section a source lacks is listed here as missing, never filled in from another province. |
| placesNotPeople | Yes | |
| foreignResidentsByCitizenship | Yes | Citizenship, not birthplace — excludes foreign-born Italian citizens — and only the citizenships this site tracks, so a floor on foreign residents. Summed over the comuni ISTAT has a row for: `coverage` says how many that is, and a comune with no row adds nothing rather than a zero. |
| foreignResidentsAllCitizenships | Yes | Residents holding any foreign citizenship: ISTAT's own total, not limited to the citizenships tracked in foreignResidentsByCitizenship. Citizenship, not birthplace. Summed over the comuni ISTAT has a total for: `coverage` says how many that is, and a comune with no total adds nothing rather than a zero. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint and openWorldHint=false, so the safety profile is settled; the description instead adds genuinely non-obvious behavioural context. It specifies null-with-reason semantics for sections a source lacks, names the `missingSections` field, warns that Sardinia's 2026 reorganization means source province lists diverge, and pins down unit semantics that are easy to get wrong (seismicZones is a count of comuni, never an average; tax regime coverage is places not people; citizenship is not birthplace). That is exactly the kind of caveat structured fields cannot carry.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Almost every clause earns its place — the polarity caveat, the count-not-average rule and the places-not-people distinction all prevent real misinterpretation. However the whole thing is delivered as a single run-on sentence riddled with nested parentheses, so the most important operational rule (missingSections handling) lands at the very end where it is easy to skim past. It is information-dense but poorly structured.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present the description need not document return shapes, yet it usefully pre-empts the interpretation traps an output schema cannot express. For a read-only, single-parameter lookup the coverage is close to sufficient; the residual gap is guidance on when a province call is the right call versus the sibling comune tools.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% for the single required `code` parameter, and the schema already supplies both the pattern and the 'AQ' / 'l-aquila' examples plus the `provinceCode` provenance. The description restates the same two formats without adding syntax, normalisation rules, or case handling beyond what the schema gives. Baseline 3 is appropriate when the schema does all the work.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific resource at a specific grain — one Italian province, identified by two-letter code or slug — and enumerates the exact payload (population, OMI prices, foreign residents by citizenship, ISTAT BES indicators, map layers, seismic zones, tax regimes). That level of specificity separates it cleanly from get_comune, the neighbouring settlement-level profile tool. It only loses a point because the opening is a noun phrase rather than a verb+resource statement, so the action itself is implied by the title rather than the body.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no statement of when to reach for this tool versus get_comune, search_comuni, or find_comuni, and no prerequisite or exclusion is given. The only usage-shaped advice is about handling missing sections after the call ('report it as missing, never substitute another province's figure'), which governs output interpretation, not tool selection. An agent must infer the province-vs-comune routing from the data list alone.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_regionGet one region's profileARead-onlyIdempotentInspect
One Italian region (slug such as abruzzo or friuli-venezia-giulia, or its name): population, foreign residents by citizenship (citizenship, not birthplace), seismicZones (a count of its comuni per official seismic zone, never an average), the region-grain map layers (healthcare scores), its provinces (look each up with get_province) and how much of the region meets each verified tax regime (places, not people). Trentino-Alto Adige/Südtirol is scored by autonomous province, so its regional healthcare value is null with a reason: use get_province for Bolzano and Trento. A section a source lacks is null and listed in missingSections: report it as missing.
| Name | Required | Description | Default |
|---|---|---|---|
| slug | Yes | A region's slug, e.g. "abruzzo" or "friuli-venezia-giulia", or its name. Find it in a comune's `region` or a province's `regionSlug`. |
Output Schema
| Name | Required | Description |
|---|---|---|
| layers | Yes | Every region-grain map layer (healthcare scores) for this region. Trentino-Alto Adige/Südtirol is scored by autonomous province, so its regional value is null with a reason: look up Bolzano and Trento with GET /province/{code}. Province- and comune-grain layers are not rolled up here. |
| region | Yes | |
| population | Yes | Sum of the region's comuni. |
| taxRegimes | Yes | Verified regimes with at least one qualifying comune here. Places, not people; a regime absent here is not a no. |
| attribution | Yes | |
| seismicZones | Yes | How this region's comuni are spread across the official seismic zones: a count per zone, never an average. |
| missingSections | Yes | Every part of this profile a source has no figure for, with why. |
| placesNotPeople | Yes | |
| foreignResidentsByCitizenship | Yes | Citizenship, not birthplace, and only the citizenships this site tracks, so a floor. Summed over the comuni ISTAT has a row for: `coverage` says how many that is, and a comune with no row adds nothing rather than a zero. |
| foreignResidentsAllCitizenships | Yes | ISTAT's own all-citizenship total (citizenship, not birthplace), summed over the comuni that have one; `coverage` says how many. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already mark this as a safe read operation, and the description adds substantial behavior detail: sections may be null and listed in missingSections, seismicZones is a count per zone rather than an average, tax regime coverage is by places rather than people, and the autonomous-province scoring exception explains null regional healthcare values.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the core resource and slug format, then proceeds through output nuances. It is dense and long for one paragraph, but most sentences carry useful information rather than redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the single-parameter schema, output schema, and read-only annotations, the description is complete enough for correct invocation. It explains the special null/missing-section behavior and the Trentino-Alto Adige/Südtirol exception, which are not obvious from structured fields.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents the single slug parameter, its examples, and its pattern. The description repeats slug examples but does not meaningfully extend the parameter semantics beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description identifies a specific tool for retrieving one Italian region's profile and enumerates the resources it returns. It also distinguishes this tool from get_province for province-level data, giving an agent a clear basis for selection.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly routes province lookups to get_province and explains the Trentino-Alto Adige/Südtirol exception, which requires using get_province for Bolzano and Trento. It does not fully specify when not to use this tool versus every other sibling, but the main alternatives are clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_articlesList Expat Living Italy's articlesARead-onlyIdempotentInspect
Dated, sourced reporting on moving to and retiring in Italy, newest first. Filter by topic, by place (a comune's ISTAT code) or by dataset slug. Each entry has its URL, a Markdown URL for the full text, and the places and datasets it cites. Content is free to use provided you credit Expat Living Italy and link to the article.
| Name | Required | Description | Default |
|---|---|---|---|
| place | No | A comune's 6-digit ISTAT code (or a province sigla / region slug). | |
| topic | No | One of: tax, residency-visas, costs-housing, healthcare, places, daily-life, data-method. | |
| dataset | No | A dataset slug from GET /datasets. | |
| include | No | Pass `archived` to include archived articles, flagged by `status`. |
Output Schema
| Name | Required | Description |
|---|---|---|
| articles | Yes | |
| attribution | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already establish readOnly/idempotent/no-open-world, so the safety profile is covered. The description adds real value beyond that: newest-first ordering, the shape of each entry (URL, Markdown URL, cited places/datasets), and licensing/attribution terms that an agent must relay to a user.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three tightly written sentences with zero filler, and the core purpose and ordering are front-loaded before the filtering and licensing details. Every sentence carries information an agent needs.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return-value explanation is not required, yet the description still summarizes entry shape and adds licensing context. It is nearly complete, with the only gap being the unmentioned 'include=archived' filter.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents all four parameters, including the enum and the ISTAT-code format. The description restates the topic/place/dataset filters but adds no syntax or format detail, and it omits the 'include=archived' parameter entirely, leaving the baseline 3.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (List) plus resource (articles) and narrows scope to 'moving to and retiring in Italy' with explicit ordering ('newest first'). This clearly separates it from the get_article sibling, which returns a single article, without needing to open either schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Names the three filtering axes (topic, place, dataset), which implies how to narrow a listing, but never states when to use this versus get_article or the other list_* siblings, and gives no exclusions or prerequisites. Usage is inferable but not spelled out.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_datasetsList every dataset behind expatliving.itARead-onlyIdempotentInspect
The catalog: each dataset's source institution, vintage (the period it describes), geographic grain, license, the expatliving.it page that presents it, and whether it is servable (false = still an unverified draft; never quote its values). Use it to say what data exists and where a reader can see it.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| datasets | Yes | |
| attribution | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover read-only/idempotent/closed-world, and the description adds genuine value beyond that: the crucial semantic that servable=false means an unverified draft whose values must never be quoted. That is a behavioral rule an agent could not infer from annotations. It stops short of describing ordering or size of the catalog.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, front-loaded with the resource identity, then the field inventory, then the usage cue. The enumerated field list is dense but each item earns its place by defining the payload; slight density cost keeps it from 5.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a no-param, read-only inventory with an output schema, the description supplies the one thing structured data can't: the meaning of the servable flag and the prohibition on quoting unverified drafts. Coverage is strong; only catalog size/ordering is unaddressed.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Zero parameters, so the baseline is 4; there is nothing for the description to clarify. The description appropriately spends its words on return-field meaning instead.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with 'The catalog' and enumerates the exact attributes each dataset carries (institution, vintage, grain, license, page, servability), so an agent knows precisely what this tool returns. It's clearly distinguishable from get_article/get_coverage by being a full inventory, though it never names a sibling to contrast with.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
'Use it to say what data exists and where a reader can see it' gives a concrete task-level trigger. It doesn't state when NOT to use it (e.g., when you already need a single dataset's values or coverage detail), so it falls short of 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_tax_regimesList the Italian tax regimes this site encodesARead-onlyIdempotentInspect
Each regime's status (verified or draft), whether it depends on place (geographic) or on the person, its legislation, population ceiling and how many comuni meet its verified criteria. Places, not people: not tax advice.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| regimes | Yes | |
| disclaimer | Yes | |
| attribution | Yes | |
| placesNotPeople | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint and closed-world scope, so the safety profile is covered. The description adds useful data-quality context (regimes can be 'verified or draft', and counts reflect 'verified criteria') plus a non-advice disclaimer. It does not add much beyond the structured fields, so a middle score fits.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single compact sentence enumerating returned attributes, with no filler. It is not front-loaded with a purpose statement, which costs it a point, but nothing is wasted.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists and the annotations cover safety, so the description only needs to orient the agent — which it does by listing the notable fields and the place-vs-person distinction. The main residual gap is routing guidance against the eligibility sibling.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so there is nothing to disambiguate and the baseline of 4 applies. The description correctly introduces no argument concepts.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description implies an enumeration of tax regimes by describing 'Each regime's status... population ceiling and how many comuni meet its verified criteria', and the title supplies the explicit verb+resource. It distinguishes itself from check_tax_eligibility with the 'Places, not people' framing. It never states plainly 'returns the list of regimes', so the purpose is inferred rather than declared.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
'Places, not people: not tax advice' implicitly steers the agent away from person-level eligibility questions (the check_tax_eligibility sibling), but it never names that alternative or states a positive when-to-use condition. Usage is implied, not specified.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_comuniFind a comune by nameARead-onlyIdempotentInspect
Look up Italian comuni by name (case- and accent-insensitive, partial names work) and get each one's 6-digit ISTAT code, province and region — the code get_comune needs. Returns identifiers only, no figures. Several comuni share a name; check the province before choosing.
| Name | Required | Description | Default |
|---|---|---|---|
| q | Yes | A comune name or part of one, accents optional — e.g. "Introdacqua" or "reggio". | |
| limit | No | How many matches to return, 1–25. Default 10. |
Output Schema
| Name | Required | Description |
|---|---|---|
| query | Yes | |
| results | Yes | Best matches first: exact name, then names starting with the query, then names containing it. Several Italian comuni share a name — check the province before picking one. |
| truncated | Yes | True when more comuni matched than `limit` allowed — narrow the query. |
| attribution | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/closed-world, so the safety profile is covered; the description usefully adds that matching is case- and accent-insensitive with partial names, that only identifiers are returned (no figures), and that duplicate names are common. That is real context beyond the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, zero filler, with the core capability and the return payload front-loaded before the caveat. Every clause carries information an agent would otherwise have to guess.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, yet the description still flags that only identifiers come back, pre-empting an agent expecting numeric data. Combined with the duplicate-name warning and the get_comune handoff, nothing needed to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so both parameters are documented in the schema itself. The description's 'case- and accent-insensitive, partial names work' largely restates the schema's 'accents optional' and 'part of one', and it says nothing about the limit parameter. Baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Look up Italian comuni by name') and names exactly what comes back: ISTAT code, province, region. It positions itself relative to get_comune as the producer of that code, though it never addresses how it differs from sibling find_comuni.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
'the code get_comune needs' gives a clear downstream-use condition, and 'Several comuni share a name; check the province before choosing' is actionable selection guidance. It stops short of explicit when-not-to-use / alternative routing (e.g., versus find_comuni).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
10 tool updates
- Changed
check_tax_eligibility2 fields changed- added
Output schema / properties / attribution / properties / licenseAdded value: +{ + "additionalProperties": false, + "description": "License of Expat Living Italy's own work only; upstream data keeps its own license, per dataset.", + "properties": { + "name": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "name", + "url" + ], + "type": "object" +} - changed
Output schema / properties / attribution / requiredPrevious value: -[ - "publisher", - "url", - "citeAs", - "request" -]New value: +[ + "publisher", + "url", + "citeAs", + "request", + "license" +]
- Changed
find_comuni16 fields changed- added
Input schema / properties / emergencyDeaOnlyAdded value: +{ + "anyOf": [ + { + "type": "boolean" + }, + { + "enum": [ + "true", + "false" + ], + "type": "string" + } + ], + "description": "[UI key edlevel=dea] With maxEmergencyDriveMinutes: count only a DEA (level I or II), not a Pronto Soccorso. A DEA is a larger emergency department with more specialties on site. Ignored without maxEmergencyDriveMinutes." +} - added
Input schema / properties / maxEmergencyDriveMinutesAdded value: +{ + "description": "[UI key edrive] Minutes by road from the comune's main town to the nearest emergency department in the Ministero della Salute's emergency network (sites active at 31 December 2025: DEA level II, DEA level I or Pronto Soccorso; OpenStreetMap roads) at most this; one of 15, 30, 45, 60. Town grain, free-flow, approximate; not a service guarantee. Combine with emergencyDeaOnly to count only DEA level I or II. A comune with no figure is left out and counted.", + "type": "number" +} - changed
Input schema / properties / order / descriptionPrevious value: -"Default: desc for population, populationChange, januaryLow and hospitalCareScore; asc for name, purchasePrice, rent, daysAbove30C, bedsPerResident, seismicClass, hospitalDriveMinutes, airportDriveMinutes, supermarketDriveMinutes, pharmacyDriveMinutes and stationDriveMinutes."New value: +"Default: desc for population, populationChange, januaryLow and hospitalCareScore; asc for name, purchasePrice, rent, daysAbove30C, bedsPerResident, seismicClass, hospitalDriveMinutes, emergencyDriveMinutes, deaDriveMinutes, airportDriveMinutes, supermarketDriveMinutes, pharmacyDriveMinutes and stationDriveMinutes." - changed
Input schema / properties / sort / enumPrevious value: -[ - "population", - "name", - "purchasePrice", - "rent", - "populationChange", - "januaryLow", - "daysAbove30C", - "hospitalCareScore", - "bedsPerResident", - "seismicClass", - "hospitalDriveMinutes", - "airportDriveMinutes", - "supermarketDriveMinutes", - "pharmacyDriveMinutes", - "stationDriveMinutes" -]New value: +[ + "population", + "name", + "purchasePrice", + "rent", + "populationChange", + "januaryLow", + "daysAbove30C", + "hospitalCareScore", + "bedsPerResident", + "seismicClass", + "hospitalDriveMinutes", + "emergencyDriveMinutes", + "deaDriveMinutes", + "airportDriveMinutes", + "supermarketDriveMinutes", + "pharmacyDriveMinutes", + "stationDriveMinutes" +] - added
Output schema / properties / attribution / properties / licenseAdded value: +{ + "additionalProperties": false, + "description": "License of Expat Living Italy's own work only; upstream data keeps its own license, per dataset.", + "properties": { + "name": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "name", + "url" + ], + "type": "object" +} - changed
Output schema / properties / attribution / requiredPrevious value: -[ - "publisher", - "url", - "citeAs", - "request" -]New value: +[ + "publisher", + "url", + "citeAs", + "request", + "license" +] - added
Output schema / properties / figures / properties / nearestDeaMinutesAdded value: +{ + "additionalProperties": false, + "properties": { + "coverage": { + "additionalProperties": false, + "description": "How many of Italy's places this dataset covers at all, e.g. 7587 of 7894 comuni.", + "properties": { + "in": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "of": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "in", + "of" + ], + "type": "object" + }, + "extractedOn": { + "description": "ISO date this value was extracted from the source.", + "type": "string" + }, + "license": { + "type": "string" + }, + "method": { + "description": "URL to the methodology page for this dataset.", + "type": "string" + }, + "source": { + "type": "string" + }, + "unit": { + "type": "string" + }, + "vintage": { + "description": "The period the value describes, e.g. \"2025H2\" or \"2025\" — not when it was fetched.", + "type": "string" + } + }, + "required": [ + "unit", + "source", + "vintage", + "extractedOn", + "method", + "license" + ], + "type": "object" +} - changed
Output schema / properties / figures / requiredPrevious value: -[ - "population", - "purchasePriceEurM2", - "rentPriceEurM2Month", - "populationChangePct", - "januaryLowC", - "daysAbove30C", - "hospitalCareScore", - "communityCareScore", - "preventionScore", - "bedsPerResident", - "seismicClass", - "nearestHospitalMinutes", - "nearestEmergencyMinutes", - "nearestAirportMinutes", - "nearestSupermarketMinutes", - "nearestPharmacyMinutes", - "nearestStationMinutes" -]New value: +[ + "population", + "purchasePriceEurM2", + "rentPriceEurM2Month", + "populationChangePct", + "januaryLowC", + "daysAbove30C", + "hospitalCareScore", + "communityCareScore", + "preventionScore", + "bedsPerResident", + "seismicClass", + "nearestHospitalMinutes", + "nearestEmergencyMinutes", + "nearestDeaMinutes", + "nearestAirportMinutes", + "nearestSupermarketMinutes", + "nearestPharmacyMinutes", + "nearestStationMinutes" +] - changed
Output schema / properties / missingData / items / properties / excluded / descriptionPrevious value: -"Comuni that passed every filter not about this figure but have no figure, so could not be tested and are left out. Price, rent and population change are counted against every other filter except their own and can overlap. The eight shortlist filters (januaryLow, daysAbove30C, hospitalCareScore, allCareScores, bedsPerResident, seismicClass, hospitalDrive, airportDrive) are counted in that fixed order, each against the comuni that reached it, exactly as the /data funnel does."New value: +"Comuni that passed every filter not about this figure but have no figure, so could not be tested and are left out. Price, rent and population change are counted against every other filter except their own and can overlap. The nine shortlist filters (januaryLow, daysAbove30C, hospitalCareScore, allCareScores, bedsPerResident, seismicClass, hospitalDrive, emergencyDrive, airportDrive) are counted in that fixed order, each against the comuni that reached it, exactly as the /data funnel does." - changed
Output schema / properties / missingData / items / properties / filter / enumPrevious value: -[ - "purchasePrice", - "rent", - "populationChange", - "januaryLow", - "daysAbove30C", - "hospitalCareScore", - "allCareScores", - "bedsPerResident", - "seismicClass", - "hospitalDrive", - "airportDrive" -]New value: +[ + "purchasePrice", + "rent", + "populationChange", + "januaryLow", + "daysAbove30C", + "hospitalCareScore", + "allCareScores", + "bedsPerResident", + "seismicClass", + "hospitalDrive", + "emergencyDrive", + "airportDrive" +] - changed
Output schema / properties / missingData / maxItemsPrevious value: -11New value: +12 - added
Output schema / properties / results / items / properties / nearestDeaKmAdded value: +{ + "description": "Kilometers by road to nearestDeaName. null: no figure.", + "type": [ + "number", + "null" + ] +} - added
Output schema / properties / results / items / properties / nearestDeaLevelAdded value: +{ + "anyOf": [ + { + "enum": [ + "DEA2", + "DEA1" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "description": "The Ministry's level of nearestDeaName: DEA2 = DEA level II, DEA1 = DEA level I. null: no figure." +} - added
Output schema / properties / results / items / properties / nearestDeaMinutesAdded value: +{ + "anyOf": [ + { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Minutes by road to nearestDeaName, free-flow, approximate. null: no figure (never 0)." +} - added
Output schema / properties / results / items / properties / nearestDeaNameAdded value: +{ + "description": "Nearest hospital the same Ministry list gives as DEA level II or DEA level I (Pronto Soccorso sites left out). Often the same hospital as nearestEmergencyName. null: no figure.", + "type": [ + "string", + "null" + ] +} - changed
Output schema / properties / results / items / requiredPrevious value: -[ - "istatCode", - "name", - "province", - "provinceCode", - "region", - "page", - "population", - "purchasePriceEurM2", - "rentPriceEurM2Month", - "populationChangePct", - "januaryLowC", - "daysAbove30C", - "hospitalCareScore", - "communityCareScore", - "preventionScore", - "nsgGrain", - "bedsPerResident", - "seismicClass", - "seismicZone", - "nearestHospitalName", - "nearestHospitalMinutes", - "nearestHospitalKm", - "nearestEmergencyName", - "nearestEmergencyLevel", - "nearestEmergencyMinutes", - "nearestEmergencyKm", - "nearestAirportName", - "nearestAirportIata", - "nearestAirportMinutes", - "nearestAirportKm", - "nearestSupermarketName", - "nearestSupermarketMinutes", - "nearestSupermarketKm", - "nearestPharmacyName", - "nearestPharmacyMinutes", - "nearestPharmacyKm", - "nearestStationName", - "nearestStationMinutes", - "nearestStationKm" -]New value: +[ + "istatCode", + "name", + "province", + "provinceCode", + "region", + "page", + "population", + "purchasePriceEurM2", + "rentPriceEurM2Month", + "populationChangePct", + "januaryLowC", + "daysAbove30C", + "hospitalCareScore", + "communityCareScore", + "preventionScore", + "nsgGrain", + "bedsPerResident", + "seismicClass", + "seismicZone", + "nearestHospitalName", + "nearestHospitalMinutes", + "nearestHospitalKm", + "nearestEmergencyName", + "nearestEmergencyLevel", + "nearestEmergencyMinutes", + "nearestEmergencyKm", + "nearestDeaName", + "nearestDeaLevel", + "nearestDeaMinutes", + "nearestDeaKm", + "nearestAirportName", + "nearestAirportIata", + "nearestAirportMinutes", + "nearestAirportKm", + "nearestSupermarketName", + "nearestSupermarketMinutes", + "nearestSupermarketKm", + "nearestPharmacyName", + "nearestPharmacyMinutes", + "nearestPharmacyKm", + "nearestStationName", + "nearestStationMinutes", + "nearestStationKm" +]
- Changed
get_comune2 fields changed- added
Output schema / properties / attribution / properties / licenseAdded value: +{ + "additionalProperties": false, + "description": "License of Expat Living Italy's own work only; upstream data keeps its own license, per dataset.", + "properties": { + "name": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "name", + "url" + ], + "type": "object" +} - changed
Output schema / properties / attribution / requiredPrevious value: -[ - "publisher", - "url", - "citeAs", - "request" -]New value: +[ + "publisher", + "url", + "citeAs", + "request", + "license" +]
- Changed
get_coverage2 fields changed- added
Output schema / properties / attribution / properties / licenseAdded value: +{ + "additionalProperties": false, + "description": "License of Expat Living Italy's own work only; upstream data keeps its own license, per dataset.", + "properties": { + "name": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "name", + "url" + ], + "type": "object" +} - changed
Output schema / properties / attribution / requiredPrevious value: -[ - "publisher", - "url", - "citeAs", - "request" -]New value: +[ + "publisher", + "url", + "citeAs", + "request", + "license" +]
- Changed
get_nationality2 fields changed- added
Output schema / properties / attribution / properties / licenseAdded value: +{ + "additionalProperties": false, + "description": "License of Expat Living Italy's own work only; upstream data keeps its own license, per dataset.", + "properties": { + "name": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "name", + "url" + ], + "type": "object" +} - changed
Output schema / properties / attribution / requiredPrevious value: -[ - "publisher", - "url", - "citeAs", - "request" -]New value: +[ + "publisher", + "url", + "citeAs", + "request", + "license" +]
- Changed
get_province2 fields changed- added
Output schema / properties / attribution / properties / licenseAdded value: +{ + "additionalProperties": false, + "description": "License of Expat Living Italy's own work only; upstream data keeps its own license, per dataset.", + "properties": { + "name": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "name", + "url" + ], + "type": "object" +} - changed
Output schema / properties / attribution / requiredPrevious value: -[ - "publisher", - "url", - "citeAs", - "request" -]New value: +[ + "publisher", + "url", + "citeAs", + "request", + "license" +]
- Added
get_region - Changed
list_datasets2 fields changed- added
Output schema / properties / attribution / properties / licenseAdded value: +{ + "additionalProperties": false, + "description": "License of Expat Living Italy's own work only; upstream data keeps its own license, per dataset.", + "properties": { + "name": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "name", + "url" + ], + "type": "object" +} - changed
Output schema / properties / attribution / requiredPrevious value: -[ - "publisher", - "url", - "citeAs", - "request" -]New value: +[ + "publisher", + "url", + "citeAs", + "request", + "license" +]
- Changed
list_tax_regimes2 fields changed- added
Output schema / properties / attribution / properties / licenseAdded value: +{ + "additionalProperties": false, + "description": "License of Expat Living Italy's own work only; upstream data keeps its own license, per dataset.", + "properties": { + "name": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "name", + "url" + ], + "type": "object" +} - changed
Output schema / properties / attribution / requiredPrevious value: -[ - "publisher", - "url", - "citeAs", - "request" -]New value: +[ + "publisher", + "url", + "citeAs", + "request", + "license" +]
- Changed
search_comuni2 fields changed- added
Output schema / properties / attribution / properties / licenseAdded value: +{ + "additionalProperties": false, + "description": "License of Expat Living Italy's own work only; upstream data keeps its own license, per dataset.", + "properties": { + "name": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "name", + "url" + ], + "type": "object" +} - changed
Output schema / properties / attribution / requiredPrevious value: -[ - "publisher", - "url", - "citeAs", - "request" -]New value: +[ + "publisher", + "url", + "citeAs", + "request", + "license" +]
1 tool update
- Changed
find_comuni4 fields changed- added
Output schema / properties / results / items / properties / nearestEmergencyLevelAdded value: +{ + "anyOf": [ + { + "enum": [ + "DEA2", + "DEA1", + "PS" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "description": "The Ministry's emergency level of nearestEmergencyName: DEA2 = DEA level II, DEA1 = DEA level I, PS = Pronto Soccorso (a smaller emergency department than a DEA). null: no figure." +} - changed
Output schema / properties / results / items / properties / nearestEmergencyMinutes / descriptionPrevious value: -"Minutes by road to nearestEmergencyName (OpenStreetMap). null: no figure."New value: +"Minutes by road to nearestEmergencyName (Ministry emergency network; roads © OpenStreetMap contributors). null: no figure." - changed
Output schema / properties / results / items / properties / nearestEmergencyName / descriptionPrevious value: -"Nearest hospital OpenStreetMap volunteers tag as having an emergency department. Less complete and less accurate than the Ministry list: a town can look far only because a nearby one is not tagged. null: no figure."New value: +"Nearest hospital the Ministero della Salute lists in its emergency network (sites active at 31 December 2025) as DEA level II, DEA level I or Pronto Soccorso. A Pronto Soccorso is not the same as a DEA. Not a service guarantee. null: no figure." - changed
Output schema / properties / results / items / requiredPrevious value: -[ - "istatCode", - "name", - "province", - "provinceCode", - "region", - "page", - "population", - "purchasePriceEurM2", - "rentPriceEurM2Month", - "populationChangePct", - "januaryLowC", - "daysAbove30C", - "hospitalCareScore", - "communityCareScore", - "preventionScore", - "nsgGrain", - "bedsPerResident", - "seismicClass", - "seismicZone", - "nearestHospitalName", - "nearestHospitalMinutes", - "nearestHospitalKm", - "nearestEmergencyName", - "nearestEmergencyMinutes", - "nearestEmergencyKm", - "nearestAirportName", - "nearestAirportIata", - "nearestAirportMinutes", - "nearestAirportKm", - "nearestSupermarketName", - "nearestSupermarketMinutes", - "nearestSupermarketKm", - "nearestPharmacyName", - "nearestPharmacyMinutes", - "nearestPharmacyKm", - "nearestStationName", - "nearestStationMinutes", - "nearestStationKm" -]New value: +[ + "istatCode", + "name", + "province", + "provinceCode", + "region", + "page", + "population", + "purchasePriceEurM2", + "rentPriceEurM2Month", + "populationChangePct", + "januaryLowC", + "daysAbove30C", + "hospitalCareScore", + "communityCareScore", + "preventionScore", + "nsgGrain", + "bedsPerResident", + "seismicClass", + "seismicZone", + "nearestHospitalName", + "nearestHospitalMinutes", + "nearestHospitalKm", + "nearestEmergencyName", + "nearestEmergencyLevel", + "nearestEmergencyMinutes", + "nearestEmergencyKm", + "nearestAirportName", + "nearestAirportIata", + "nearestAirportMinutes", + "nearestAirportKm", + "nearestSupermarketName", + "nearestSupermarketMinutes", + "nearestSupermarketKm", + "nearestPharmacyName", + "nearestPharmacyMinutes", + "nearestPharmacyKm", + "nearestStationName", + "nearestStationMinutes", + "nearestStationKm" +]
1 tool update
- Changed
find_comuni16 fields changed- changed
Input schema / properties / order / descriptionPrevious value: -"Default: desc for population, populationChange, januaryLow and hospitalCareScore; asc for name, purchasePrice, rent, daysAbove30C, bedsPerResident, seismicClass, hospitalDriveMinutes and airportDriveMinutes."New value: +"Default: desc for population, populationChange, januaryLow and hospitalCareScore; asc for name, purchasePrice, rent, daysAbove30C, bedsPerResident, seismicClass, hospitalDriveMinutes, airportDriveMinutes, supermarketDriveMinutes, pharmacyDriveMinutes and stationDriveMinutes." - changed
Input schema / properties / sort / enumPrevious value: -[ - "population", - "name", - "purchasePrice", - "rent", - "populationChange", - "januaryLow", - "daysAbove30C", - "hospitalCareScore", - "bedsPerResident", - "seismicClass", - "hospitalDriveMinutes", - "airportDriveMinutes" -]New value: +[ + "population", + "name", + "purchasePrice", + "rent", + "populationChange", + "januaryLow", + "daysAbove30C", + "hospitalCareScore", + "bedsPerResident", + "seismicClass", + "hospitalDriveMinutes", + "airportDriveMinutes", + "supermarketDriveMinutes", + "pharmacyDriveMinutes", + "stationDriveMinutes" +] - added
Output schema / properties / figures / properties / nearestPharmacyMinutesAdded value: +{ + "additionalProperties": false, + "properties": { + "coverage": { + "additionalProperties": false, + "description": "How many of Italy's places this dataset covers at all, e.g. 7587 of 7894 comuni.", + "properties": { + "in": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "of": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "in", + "of" + ], + "type": "object" + }, + "extractedOn": { + "description": "ISO date this value was extracted from the source.", + "type": "string" + }, + "license": { + "type": "string" + }, + "method": { + "description": "URL to the methodology page for this dataset.", + "type": "string" + }, + "source": { + "type": "string" + }, + "unit": { + "type": "string" + }, + "vintage": { + "description": "The period the value describes, e.g. \"2025H2\" or \"2025\" — not when it was fetched.", + "type": "string" + } + }, + "required": [ + "unit", + "source", + "vintage", + "extractedOn", + "method", + "license" + ], + "type": "object" +} - added
Output schema / properties / figures / properties / nearestStationMinutesAdded value: +{ + "additionalProperties": false, + "properties": { + "coverage": { + "additionalProperties": false, + "description": "How many of Italy's places this dataset covers at all, e.g. 7587 of 7894 comuni.", + "properties": { + "in": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "of": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "in", + "of" + ], + "type": "object" + }, + "extractedOn": { + "description": "ISO date this value was extracted from the source.", + "type": "string" + }, + "license": { + "type": "string" + }, + "method": { + "description": "URL to the methodology page for this dataset.", + "type": "string" + }, + "source": { + "type": "string" + }, + "unit": { + "type": "string" + }, + "vintage": { + "description": "The period the value describes, e.g. \"2025H2\" or \"2025\" — not when it was fetched.", + "type": "string" + } + }, + "required": [ + "unit", + "source", + "vintage", + "extractedOn", + "method", + "license" + ], + "type": "object" +} - added
Output schema / properties / figures / properties / nearestSupermarketMinutesAdded value: +{ + "additionalProperties": false, + "properties": { + "coverage": { + "additionalProperties": false, + "description": "How many of Italy's places this dataset covers at all, e.g. 7587 of 7894 comuni.", + "properties": { + "in": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "of": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "in", + "of" + ], + "type": "object" + }, + "extractedOn": { + "description": "ISO date this value was extracted from the source.", + "type": "string" + }, + "license": { + "type": "string" + }, + "method": { + "description": "URL to the methodology page for this dataset.", + "type": "string" + }, + "source": { + "type": "string" + }, + "unit": { + "type": "string" + }, + "vintage": { + "description": "The period the value describes, e.g. \"2025H2\" or \"2025\" — not when it was fetched.", + "type": "string" + } + }, + "required": [ + "unit", + "source", + "vintage", + "extractedOn", + "method", + "license" + ], + "type": "object" +} - changed
Output schema / properties / figures / requiredPrevious value: -[ - "population", - "purchasePriceEurM2", - "rentPriceEurM2Month", - "populationChangePct", - "januaryLowC", - "daysAbove30C", - "hospitalCareScore", - "communityCareScore", - "preventionScore", - "bedsPerResident", - "seismicClass", - "nearestHospitalMinutes", - "nearestEmergencyMinutes", - "nearestAirportMinutes" -]New value: +[ + "population", + "purchasePriceEurM2", + "rentPriceEurM2Month", + "populationChangePct", + "januaryLowC", + "daysAbove30C", + "hospitalCareScore", + "communityCareScore", + "preventionScore", + "bedsPerResident", + "seismicClass", + "nearestHospitalMinutes", + "nearestEmergencyMinutes", + "nearestAirportMinutes", + "nearestSupermarketMinutes", + "nearestPharmacyMinutes", + "nearestStationMinutes" +] - added
Output schema / properties / results / items / properties / nearestPharmacyKmAdded value: +{ + "description": "Kilometers by road to nearestPharmacyName. null: no figure.", + "type": [ + "number", + "null" + ] +} - added
Output schema / properties / results / items / properties / nearestPharmacyMinutesAdded value: +{ + "anyOf": [ + { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Minutes by road from the comune's main town, free-flow, approximate. null: no figure (never 0)." +} - added
Output schema / properties / results / items / properties / nearestPharmacyNameAdded value: +{ + "description": "Nearest pharmacy in the Ministero della Salute's open register (ordinary and branch pharmacies). Says nothing about opening hours or duty. null: no figure.", + "type": [ + "string", + "null" + ] +} - added
Output schema / properties / results / items / properties / nearestStationKmAdded value: +{ + "description": "Kilometers by road to nearestStationName. null: no figure.", + "type": [ + "number", + "null" + ] +} - added
Output schema / properties / results / items / properties / nearestStationMinutesAdded value: +{ + "anyOf": [ + { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Minutes by road from the comune's main town, free-flow, approximate. null: no figure (never 0)." +} - added
Output schema / properties / results / items / properties / nearestStationNameAdded value: +{ + "description": "Nearest railway station with passenger service (OpenStreetMap, volunteer mapping; metro, tram and heritage lines left out). Says nothing about train frequency. null: no figure.", + "type": [ + "string", + "null" + ] +} - added
Output schema / properties / results / items / properties / nearestSupermarketKmAdded value: +{ + "description": "Kilometers by road to nearestSupermarketName. null: no figure.", + "type": [ + "number", + "null" + ] +} - added
Output schema / properties / results / items / properties / nearestSupermarketMinutesAdded value: +{ + "anyOf": [ + { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Minutes by road from the comune's main town, free-flow, approximate. null: no figure (never 0)." +} - added
Output schema / properties / results / items / properties / nearestSupermarketNameAdded value: +{ + "description": "Nearest supermarket (OpenStreetMap shop=supermarket, volunteer mapping: a store missing from the map is missing here; small grocers are not counted). null: no figure.", + "type": [ + "string", + "null" + ] +} - changed
Output schema / properties / results / items / requiredPrevious value: -[ - "istatCode", - "name", - "province", - "provinceCode", - "region", - "page", - "population", - "purchasePriceEurM2", - "rentPriceEurM2Month", - "populationChangePct", - "januaryLowC", - "daysAbove30C", - "hospitalCareScore", - "communityCareScore", - "preventionScore", - "nsgGrain", - "bedsPerResident", - "seismicClass", - "seismicZone", - "nearestHospitalName", - "nearestHospitalMinutes", - "nearestHospitalKm", - "nearestEmergencyName", - "nearestEmergencyMinutes", - "nearestEmergencyKm", - "nearestAirportName", - "nearestAirportIata", - "nearestAirportMinutes", - "nearestAirportKm" -]New value: +[ + "istatCode", + "name", + "province", + "provinceCode", + "region", + "page", + "population", + "purchasePriceEurM2", + "rentPriceEurM2Month", + "populationChangePct", + "januaryLowC", + "daysAbove30C", + "hospitalCareScore", + "communityCareScore", + "preventionScore", + "nsgGrain", + "bedsPerResident", + "seismicClass", + "seismicZone", + "nearestHospitalName", + "nearestHospitalMinutes", + "nearestHospitalKm", + "nearestEmergencyName", + "nearestEmergencyMinutes", + "nearestEmergencyKm", + "nearestAirportName", + "nearestAirportIata", + "nearestAirportMinutes", + "nearestAirportKm", + "nearestSupermarketName", + "nearestSupermarketMinutes", + "nearestSupermarketKm", + "nearestPharmacyName", + "nearestPharmacyMinutes", + "nearestPharmacyKm", + "nearestStationName", + "nearestStationMinutes", + "nearestStationKm" +]
1 tool update
- Changed
find_comuni2 fields changed- changed
Input schema / properties / maxAirportDriveMinutes / descriptionPrevious value: -"[UI key adrive] Minutes by road from the comune's main town to the nearest airport where ENAC recorded passengers in 2025, at most this; one of 60, 90, 120, 180. Town grain, free-flow, approximate; says nothing about flights. No figure: left out and counted."New value: +"[UI key adrive] Minutes by road from the comune's main town to the nearest of the airports where ENAC recorded at least 4,000 passengers in 2025 (it leaves out Taranto-Grottaglie and Brescia-Montichiari, which carried almost none), at most this; one of 60, 90, 120, 180. Town grain, free-flow, approximate; says nothing about flights. No figure: left out and counted." - changed
Output schema / properties / results / items / properties / nearestAirportName / descriptionPrevious value: -"Nearest airport where ENAC recorded passengers in 2025 (positions from OurAirports). Says nothing about flights. null: no figure."New value: +"Nearest of the airports where ENAC recorded at least 4,000 passengers in 2025 (positions from OurAirports; Taranto-Grottaglie and Brescia-Montichiari, which carried almost none, are left out). Says nothing about flights. null: no figure."
1 tool update
- Changed
find_comuni22 fields changed- added
Input schema / properties / maxAirportDriveMinutesAdded value: +{ + "description": "[UI key adrive] Minutes by road from the comune's main town to the nearest airport where ENAC recorded passengers in 2025, at most this; one of 60, 90, 120, 180. Town grain, free-flow, approximate; says nothing about flights. No figure: left out and counted.", + "type": "number" +} - added
Input schema / properties / maxHospitalDriveMinutesAdded value: +{ + "description": "[UI key hdrive] Minutes by road from the comune's main town to the nearest hospital with inpatient beds (Ministero della Salute 2023 list, OpenStreetMap roads) at most this; one of 15, 30, 45, 60. Town grain, free-flow, approximate. Not necessarily a hospital with an emergency department. A comune with no figure (for example a ferry-only island) is left out and counted.", + "type": "number" +} - changed
Input schema / properties / order / descriptionPrevious value: -"Default: desc for population, populationChange, januaryLow and hospitalCareScore; asc for name, purchasePrice, rent, daysAbove30C, bedsPerResident and seismicClass."New value: +"Default: desc for population, populationChange, januaryLow and hospitalCareScore; asc for name, purchasePrice, rent, daysAbove30C, bedsPerResident, seismicClass, hospitalDriveMinutes and airportDriveMinutes." - changed
Input schema / properties / sort / enumPrevious value: -[ - "population", - "name", - "purchasePrice", - "rent", - "populationChange", - "januaryLow", - "daysAbove30C", - "hospitalCareScore", - "bedsPerResident", - "seismicClass" -]New value: +[ + "population", + "name", + "purchasePrice", + "rent", + "populationChange", + "januaryLow", + "daysAbove30C", + "hospitalCareScore", + "bedsPerResident", + "seismicClass", + "hospitalDriveMinutes", + "airportDriveMinutes" +] - added
Output schema / properties / figures / properties / nearestAirportMinutesAdded value: +{ + "additionalProperties": false, + "properties": { + "coverage": { + "additionalProperties": false, + "description": "How many of Italy's places this dataset covers at all, e.g. 7587 of 7894 comuni.", + "properties": { + "in": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "of": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "in", + "of" + ], + "type": "object" + }, + "extractedOn": { + "description": "ISO date this value was extracted from the source.", + "type": "string" + }, + "license": { + "type": "string" + }, + "method": { + "description": "URL to the methodology page for this dataset.", + "type": "string" + }, + "source": { + "type": "string" + }, + "unit": { + "type": "string" + }, + "vintage": { + "description": "The period the value describes, e.g. \"2025H2\" or \"2025\" — not when it was fetched.", + "type": "string" + } + }, + "required": [ + "unit", + "source", + "vintage", + "extractedOn", + "method", + "license" + ], + "type": "object" +} - added
Output schema / properties / figures / properties / nearestEmergencyMinutesAdded value: +{ + "additionalProperties": false, + "properties": { + "coverage": { + "additionalProperties": false, + "description": "How many of Italy's places this dataset covers at all, e.g. 7587 of 7894 comuni.", + "properties": { + "in": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "of": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "in", + "of" + ], + "type": "object" + }, + "extractedOn": { + "description": "ISO date this value was extracted from the source.", + "type": "string" + }, + "license": { + "type": "string" + }, + "method": { + "description": "URL to the methodology page for this dataset.", + "type": "string" + }, + "source": { + "type": "string" + }, + "unit": { + "type": "string" + }, + "vintage": { + "description": "The period the value describes, e.g. \"2025H2\" or \"2025\" — not when it was fetched.", + "type": "string" + } + }, + "required": [ + "unit", + "source", + "vintage", + "extractedOn", + "method", + "license" + ], + "type": "object" +} - added
Output schema / properties / figures / properties / nearestHospitalMinutesAdded value: +{ + "additionalProperties": false, + "properties": { + "coverage": { + "additionalProperties": false, + "description": "How many of Italy's places this dataset covers at all, e.g. 7587 of 7894 comuni.", + "properties": { + "in": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "of": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "in", + "of" + ], + "type": "object" + }, + "extractedOn": { + "description": "ISO date this value was extracted from the source.", + "type": "string" + }, + "license": { + "type": "string" + }, + "method": { + "description": "URL to the methodology page for this dataset.", + "type": "string" + }, + "source": { + "type": "string" + }, + "unit": { + "type": "string" + }, + "vintage": { + "description": "The period the value describes, e.g. \"2025H2\" or \"2025\" — not when it was fetched.", + "type": "string" + } + }, + "required": [ + "unit", + "source", + "vintage", + "extractedOn", + "method", + "license" + ], + "type": "object" +} - changed
Output schema / properties / figures / requiredPrevious value: -[ - "population", - "purchasePriceEurM2", - "rentPriceEurM2Month", - "populationChangePct", - "januaryLowC", - "daysAbove30C", - "hospitalCareScore", - "communityCareScore", - "preventionScore", - "bedsPerResident", - "seismicClass" -]New value: +[ + "population", + "purchasePriceEurM2", + "rentPriceEurM2Month", + "populationChangePct", + "januaryLowC", + "daysAbove30C", + "hospitalCareScore", + "communityCareScore", + "preventionScore", + "bedsPerResident", + "seismicClass", + "nearestHospitalMinutes", + "nearestEmergencyMinutes", + "nearestAirportMinutes" +] - changed
Output schema / properties / missingData / items / properties / excluded / descriptionPrevious value: -"Comuni that passed every filter not about this figure but have no figure, so could not be tested and are left out. Price, rent and population change are counted against every other filter except their own and can overlap. The six shortlist filters (januaryLow, daysAbove30C, hospitalCareScore, allCareScores, bedsPerResident, seismicClass) are counted in that fixed order, each against the comuni that reached it, exactly as the /data funnel does."New value: +"Comuni that passed every filter not about this figure but have no figure, so could not be tested and are left out. Price, rent and population change are counted against every other filter except their own and can overlap. The eight shortlist filters (januaryLow, daysAbove30C, hospitalCareScore, allCareScores, bedsPerResident, seismicClass, hospitalDrive, airportDrive) are counted in that fixed order, each against the comuni that reached it, exactly as the /data funnel does." - changed
Output schema / properties / missingData / items / properties / filter / enumPrevious value: -[ - "purchasePrice", - "rent", - "populationChange", - "januaryLow", - "daysAbove30C", - "hospitalCareScore", - "allCareScores", - "bedsPerResident", - "seismicClass" -]New value: +[ + "purchasePrice", + "rent", + "populationChange", + "januaryLow", + "daysAbove30C", + "hospitalCareScore", + "allCareScores", + "bedsPerResident", + "seismicClass", + "hospitalDrive", + "airportDrive" +] - changed
Output schema / properties / missingData / maxItemsPrevious value: -9New value: +11 - added
Output schema / properties / results / items / properties / nearestAirportIataAdded value: +{ + "description": "IATA code of nearestAirportName.", + "type": [ + "string", + "null" + ] +} - added
Output schema / properties / results / items / properties / nearestAirportKmAdded value: +{ + "description": "Kilometers by road to nearestAirportName. null: no figure.", + "type": [ + "number", + "null" + ] +} - added
Output schema / properties / results / items / properties / nearestAirportMinutesAdded value: +{ + "anyOf": [ + { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Minutes by road from the comune's main town, free-flow, approximate. null: no figure (never 0)." +} - added
Output schema / properties / results / items / properties / nearestAirportNameAdded value: +{ + "description": "Nearest airport where ENAC recorded passengers in 2025 (positions from OurAirports). Says nothing about flights. null: no figure.", + "type": [ + "string", + "null" + ] +} - added
Output schema / properties / results / items / properties / nearestEmergencyKmAdded value: +{ + "description": "Kilometers by road to nearestEmergencyName. null: no figure.", + "type": [ + "number", + "null" + ] +} - added
Output schema / properties / results / items / properties / nearestEmergencyMinutesAdded value: +{ + "anyOf": [ + { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Minutes by road to nearestEmergencyName (OpenStreetMap). null: no figure." +} - added
Output schema / properties / results / items / properties / nearestEmergencyNameAdded value: +{ + "description": "Nearest hospital OpenStreetMap volunteers tag as having an emergency department. Less complete and less accurate than the Ministry list: a town can look far only because a nearby one is not tagged. null: no figure.", + "type": [ + "string", + "null" + ] +} - added
Output schema / properties / results / items / properties / nearestHospitalKmAdded value: +{ + "description": "Kilometers by road to nearestHospitalName. null: no figure.", + "type": [ + "number", + "null" + ] +} - added
Output schema / properties / results / items / properties / nearestHospitalMinutesAdded value: +{ + "anyOf": [ + { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Minutes by road from the comune's main town, free-flow, approximate. Town grain. null: no figure (never 0)." +} - added
Output schema / properties / results / items / properties / nearestHospitalNameAdded value: +{ + "description": "Nearest hospital with inpatient beds (Ministero della Salute 2023 list), by road. Not necessarily one with an emergency department. null: no figure.", + "type": [ + "string", + "null" + ] +} - changed
Output schema / properties / results / items / requiredPrevious value: -[ - "istatCode", - "name", - "province", - "provinceCode", - "region", - "page", - "population", - "purchasePriceEurM2", - "rentPriceEurM2Month", - "populationChangePct", - "januaryLowC", - "daysAbove30C", - "hospitalCareScore", - "communityCareScore", - "preventionScore", - "nsgGrain", - "bedsPerResident", - "seismicClass", - "seismicZone" -]New value: +[ + "istatCode", + "name", + "province", + "provinceCode", + "region", + "page", + "population", + "purchasePriceEurM2", + "rentPriceEurM2Month", + "populationChangePct", + "januaryLowC", + "daysAbove30C", + "hospitalCareScore", + "communityCareScore", + "preventionScore", + "nsgGrain", + "bedsPerResident", + "seismicClass", + "seismicZone", + "nearestHospitalName", + "nearestHospitalMinutes", + "nearestHospitalKm", + "nearestEmergencyName", + "nearestEmergencyMinutes", + "nearestEmergencyKm", + "nearestAirportName", + "nearestAirportIata", + "nearestAirportMinutes", + "nearestAirportKm" +]
1 tool update
- Changed
find_comuni20 fields changed- changed
Input schema / properties / order / descriptionPrevious value: -"Default: desc for population and populationChange, asc for name, purchasePrice and rent."New value: +"Default: desc for population, populationChange, januaryLow and hospitalCareScore; asc for name, purchasePrice, rent, daysAbove30C, bedsPerResident and seismicClass." - changed
Input schema / properties / sort / enumPrevious value: -[ - "population", - "name", - "purchasePrice", - "rent", - "populationChange" -]New value: +[ + "population", + "name", + "purchasePrice", + "rent", + "populationChange", + "januaryLow", + "daysAbove30C", + "hospitalCareScore", + "bedsPerResident", + "seismicClass" +] - added
Output schema / properties / figures / properties / bedsPerResidentAdded value: +{ + "additionalProperties": false, + "properties": { + "coverage": { + "additionalProperties": false, + "description": "How many of Italy's places this dataset covers at all, e.g. 7587 of 7894 comuni.", + "properties": { + "in": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "of": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "in", + "of" + ], + "type": "object" + }, + "extractedOn": { + "description": "ISO date this value was extracted from the source.", + "type": "string" + }, + "license": { + "type": "string" + }, + "method": { + "description": "URL to the methodology page for this dataset.", + "type": "string" + }, + "source": { + "type": "string" + }, + "unit": { + "type": "string" + }, + "vintage": { + "description": "The period the value describes, e.g. \"2025H2\" or \"2025\" — not when it was fetched.", + "type": "string" + } + }, + "required": [ + "unit", + "source", + "vintage", + "extractedOn", + "method", + "license" + ], + "type": "object" +} - added
Output schema / properties / figures / properties / communityCareScoreAdded value: +{ + "additionalProperties": false, + "properties": { + "coverage": { + "additionalProperties": false, + "description": "How many of Italy's places this dataset covers at all, e.g. 7587 of 7894 comuni.", + "properties": { + "in": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "of": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "in", + "of" + ], + "type": "object" + }, + "extractedOn": { + "description": "ISO date this value was extracted from the source.", + "type": "string" + }, + "license": { + "type": "string" + }, + "method": { + "description": "URL to the methodology page for this dataset.", + "type": "string" + }, + "source": { + "type": "string" + }, + "unit": { + "type": "string" + }, + "vintage": { + "description": "The period the value describes, e.g. \"2025H2\" or \"2025\" — not when it was fetched.", + "type": "string" + } + }, + "required": [ + "unit", + "source", + "vintage", + "extractedOn", + "method", + "license" + ], + "type": "object" +} - added
Output schema / properties / figures / properties / daysAbove30CAdded value: +{ + "additionalProperties": false, + "properties": { + "coverage": { + "additionalProperties": false, + "description": "How many of Italy's places this dataset covers at all, e.g. 7587 of 7894 comuni.", + "properties": { + "in": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "of": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "in", + "of" + ], + "type": "object" + }, + "extractedOn": { + "description": "ISO date this value was extracted from the source.", + "type": "string" + }, + "license": { + "type": "string" + }, + "method": { + "description": "URL to the methodology page for this dataset.", + "type": "string" + }, + "source": { + "type": "string" + }, + "unit": { + "type": "string" + }, + "vintage": { + "description": "The period the value describes, e.g. \"2025H2\" or \"2025\" — not when it was fetched.", + "type": "string" + } + }, + "required": [ + "unit", + "source", + "vintage", + "extractedOn", + "method", + "license" + ], + "type": "object" +} - added
Output schema / properties / figures / properties / hospitalCareScoreAdded value: +{ + "additionalProperties": false, + "properties": { + "coverage": { + "additionalProperties": false, + "description": "How many of Italy's places this dataset covers at all, e.g. 7587 of 7894 comuni.", + "properties": { + "in": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "of": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "in", + "of" + ], + "type": "object" + }, + "extractedOn": { + "description": "ISO date this value was extracted from the source.", + "type": "string" + }, + "license": { + "type": "string" + }, + "method": { + "description": "URL to the methodology page for this dataset.", + "type": "string" + }, + "source": { + "type": "string" + }, + "unit": { + "type": "string" + }, + "vintage": { + "description": "The period the value describes, e.g. \"2025H2\" or \"2025\" — not when it was fetched.", + "type": "string" + } + }, + "required": [ + "unit", + "source", + "vintage", + "extractedOn", + "method", + "license" + ], + "type": "object" +} - added
Output schema / properties / figures / properties / januaryLowCAdded value: +{ + "additionalProperties": false, + "properties": { + "coverage": { + "additionalProperties": false, + "description": "How many of Italy's places this dataset covers at all, e.g. 7587 of 7894 comuni.", + "properties": { + "in": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "of": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "in", + "of" + ], + "type": "object" + }, + "extractedOn": { + "description": "ISO date this value was extracted from the source.", + "type": "string" + }, + "license": { + "type": "string" + }, + "method": { + "description": "URL to the methodology page for this dataset.", + "type": "string" + }, + "source": { + "type": "string" + }, + "unit": { + "type": "string" + }, + "vintage": { + "description": "The period the value describes, e.g. \"2025H2\" or \"2025\" — not when it was fetched.", + "type": "string" + } + }, + "required": [ + "unit", + "source", + "vintage", + "extractedOn", + "method", + "license" + ], + "type": "object" +} - added
Output schema / properties / figures / properties / preventionScoreAdded value: +{ + "additionalProperties": false, + "properties": { + "coverage": { + "additionalProperties": false, + "description": "How many of Italy's places this dataset covers at all, e.g. 7587 of 7894 comuni.", + "properties": { + "in": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "of": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "in", + "of" + ], + "type": "object" + }, + "extractedOn": { + "description": "ISO date this value was extracted from the source.", + "type": "string" + }, + "license": { + "type": "string" + }, + "method": { + "description": "URL to the methodology page for this dataset.", + "type": "string" + }, + "source": { + "type": "string" + }, + "unit": { + "type": "string" + }, + "vintage": { + "description": "The period the value describes, e.g. \"2025H2\" or \"2025\" — not when it was fetched.", + "type": "string" + } + }, + "required": [ + "unit", + "source", + "vintage", + "extractedOn", + "method", + "license" + ], + "type": "object" +} - added
Output schema / properties / figures / properties / seismicClassAdded value: +{ + "additionalProperties": false, + "properties": { + "coverage": { + "additionalProperties": false, + "description": "How many of Italy's places this dataset covers at all, e.g. 7587 of 7894 comuni.", + "properties": { + "in": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "of": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "in", + "of" + ], + "type": "object" + }, + "extractedOn": { + "description": "ISO date this value was extracted from the source.", + "type": "string" + }, + "license": { + "type": "string" + }, + "method": { + "description": "URL to the methodology page for this dataset.", + "type": "string" + }, + "source": { + "type": "string" + }, + "unit": { + "type": "string" + }, + "vintage": { + "description": "The period the value describes, e.g. \"2025H2\" or \"2025\" — not when it was fetched.", + "type": "string" + } + }, + "required": [ + "unit", + "source", + "vintage", + "extractedOn", + "method", + "license" + ], + "type": "object" +} - changed
Output schema / properties / figures / requiredPrevious value: -[ - "population", - "purchasePriceEurM2", - "rentPriceEurM2Month", - "populationChangePct" -]New value: +[ + "population", + "purchasePriceEurM2", + "rentPriceEurM2Month", + "populationChangePct", + "januaryLowC", + "daysAbove30C", + "hospitalCareScore", + "communityCareScore", + "preventionScore", + "bedsPerResident", + "seismicClass" +] - added
Output schema / properties / results / items / properties / bedsPerResidentAdded value: +{ + "description": "Registered tourist beds per resident (ISTAT, 2024). Town grain. A real zero is 0; null: no figure.", + "type": [ + "number", + "null" + ] +} - added
Output schema / properties / results / items / properties / communityCareScoreAdded value: +{ + "description": "NSG 2024 community and district care score, 0 to 100. Grain in nsgGrain. null: no figure.", + "type": [ + "number", + "null" + ] +} - added
Output schema / properties / results / items / properties / daysAbove30CAdded value: +{ + "description": "Average days a year above 30 C (ERA5-Land, 2015 to 2024). Town grain. null: no figure.", + "type": [ + "number", + "null" + ] +} - added
Output schema / properties / results / items / properties / hospitalCareScoreAdded value: +{ + "description": "NSG 2024 hospital-care score, 0 to 100; 60 is the official sufficiency line. Grain in nsgGrain. null: no figure.", + "type": [ + "number", + "null" + ] +} - added
Output schema / properties / results / items / properties / januaryLowCAdded value: +{ + "description": "Average January daily minimum, C (ERA5-Land, 2015 to 2024). Town grain, nearest ~9 km grid cell. null: no figure.", + "type": [ + "number", + "null" + ] +} - added
Output schema / properties / results / items / properties / nsgGrainAdded value: +{ + "anyOf": [ + { + "enum": [ + "region", + "autonomous_province" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Whose NSG scores these are: the comune's region, or for Bolzano-province and Trento-province comuni their own autonomous province (scored separately, never averaged). null when the comune has no NSG score." +} - added
Output schema / properties / results / items / properties / preventionScoreAdded value: +{ + "description": "NSG 2024 prevention score, 0 to 100. Grain in nsgGrain. null: no figure.", + "type": [ + "number", + "null" + ] +} - added
Output schema / properties / results / items / properties / seismicClassAdded value: +{ + "anyOf": [ + { + "maximum": 4, + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Hazard class 1 (lowest) to 4 (highest), as on the map layer. Always read it with seismicZone. null: no single classification." +} - added
Output schema / properties / results / items / properties / seismicZoneAdded value: +{ + "anyOf": [ + { + "maximum": 4, + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "The official zona sismica (1 = highest hazard), 5 minus seismicClass. A legal classification from the Dipartimento della Protezione Civile (31 May 2025), not a forecast or a verdict on any building." +} - changed
Output schema / properties / results / items / requiredPrevious value: -[ - "istatCode", - "name", - "province", - "provinceCode", - "region", - "page", - "population", - "purchasePriceEurM2", - "rentPriceEurM2Month", - "populationChangePct" -]New value: +[ + "istatCode", + "name", + "province", + "provinceCode", + "region", + "page", + "population", + "purchasePriceEurM2", + "rentPriceEurM2Month", + "populationChangePct", + "januaryLowC", + "daysAbove30C", + "hospitalCareScore", + "communityCareScore", + "preventionScore", + "nsgGrain", + "bedsPerResident", + "seismicClass", + "seismicZone" +]
3 tool updates
- Changed
find_comuni10 fields changed- added
Input schema / properties / allCareScoresAtLeast60Added value: +{ + "anyOf": [ + { + "type": "boolean" + }, + { + "enum": [ + "true", + "false" + ], + "type": "string" + } + ], + "description": "[UI key nsg60] true: the NSG prevention, community-care and hospital-care scores must all be at least 60. Region grain (autonomous-province grain for Bolzano and Trento). A comune missing any of the three is left out and counted." +} - added
Input schema / properties / excludeSeismicZone1Added value: +{ + "anyOf": [ + { + "type": "boolean" + }, + { + "enum": [ + "true", + "false" + ], + "type": "string" + } + ], + "description": "[UI key seis] true: leave out comuni in official seismic zone 1, the highest hazard class (hazard class 4 in the map layer). A legal classification from the Dipartimento della Protezione Civile (31 May 2025), not a forecast. A comune with no single classification is left out and counted." +} - added
Input schema / properties / maxBedsPerResidentAdded value: +{ + "description": "[UI key beds] Registered tourist beds per resident (ISTAT, 2024) at most this; one of 0.1, 0.5, 1. Town grain. A comune with no figure is left out and counted; a real zero is a figure.", + "type": "number" +} - added
Input schema / properties / maxDaysAbove30CAdded value: +{ + "description": "[UI key heat] Average days a year above 30 C (ERA5-Land, 2015 to 2024) at most this many, 0 to 120. Town grain. No figure: left out and counted.", + "maximum": 120, + "minimum": 0, + "type": "integer" +} - added
Input schema / properties / minHospitalCareScoreAdded value: +{ + "description": "[UI key hosp] Ministero della Salute NSG 2024 hospital-care score at least this, 50 to 100; 60 is the official sufficiency line. REGION grain: every comune in a region shares its region's score, except Bolzano-province and Trento-province comuni, which carry their own autonomous province's score. No figure: left out and counted.", + "maximum": 100, + "minimum": 50, + "type": "integer" +} - added
Input schema / properties / minJanuaryLowCAdded value: +{ + "description": "[UI key jan] Average January daily minimum (ERA5-Land, 2015 to 2024) at least this many degrees C, whole numbers -10 to 12. Town grain. A comune with no figure is left out and counted in missingData.", + "maximum": 12, + "minimum": -10, + "type": "integer" +} - changed
Output schema / properties / applied / additionalProperties / typePrevious value: -[ - "string", - "number" -]New value: +[ + "string", + "number", + "boolean" +] - changed
Output schema / properties / missingData / items / properties / excluded / descriptionPrevious value: -"Comuni that passed every filter not about this figure but have no figure, so could not be tested and are left out. Counted per figure; the counts can overlap."New value: +"Comuni that passed every filter not about this figure but have no figure, so could not be tested and are left out. Price, rent and population change are counted against every other filter except their own and can overlap. The six shortlist filters (januaryLow, daysAbove30C, hospitalCareScore, allCareScores, bedsPerResident, seismicClass) are counted in that fixed order, each against the comuni that reached it, exactly as the /data funnel does." - changed
Output schema / properties / missingData / items / properties / filter / enumPrevious value: -[ - "purchasePrice", - "rent", - "populationChange" -]New value: +[ + "purchasePrice", + "rent", + "populationChange", + "januaryLow", + "daysAbove30C", + "hospitalCareScore", + "allCareScores", + "bedsPerResident", + "seismicClass" +] - changed
Output schema / properties / missingData / maxItemsPrevious value: -3New value: +9
- Changed
get_comune2 fields changed- changed
Output schema / properties / layers / items / properties / grain / descriptionPrevious value: -"The level this value is measured at. Only \"comune\" is a measurement of this comune itself."New value: +"The level this value is measured at. Only \"comune\" is a measurement of this comune itself. \"autonomous_province\" is Bolzano or Trento, which the source scores on their own rather than as one Trentino-Alto Adige region: the figure belongs to that autonomous province, not to the region." - changed
Output schema / properties / layers / items / properties / grain / enumPrevious value: -[ - "comune", - "province", - "region" -]New value: +[ + "comune", + "province", + "autonomous_province", + "region" +]
- Changed
get_province2 fields changed- changed
Output schema / properties / layers / items / properties / grain / descriptionPrevious value: -"The level this value is measured at. Only \"comune\" is a measurement of this comune itself."New value: +"The level this value is measured at. Only \"comune\" is a measurement of this comune itself. \"autonomous_province\" is Bolzano or Trento, which the source scores on their own rather than as one Trentino-Alto Adige region: the figure belongs to that autonomous province, not to the region." - changed
Output schema / properties / layers / items / properties / grain / enumPrevious value: -[ - "comune", - "province", - "region" -]New value: +[ + "comune", + "province", + "autonomous_province", + "region" +]
3 tool updates
- Changed
find_comuni1 field changed- changed
Output schema / properties / results / items / properties / purchasePriceEurM2 / descriptionPrevious value: -"null: no OMI price for this comune. Never a zero."New value: +"null: no OMI price for this comune. Never a zero. OMI is Agenzia delle Entrate's official range of market values per m2, built from recorded sales and appraisals. Asking prices on listing sites are what sellers hope for, and usually run higher."
- Changed
get_comune4 fields changed- added
Output schema / properties / layers / items / properties / officialZoneAdded value: +{ + "description": "Only on the seismic hazard layer: the comune's official zona sismica (1 = highest hazard, 4 = lowest). NOTE this runs the opposite way to `value`, which is a hazard class (5 minus the zone). Omitted for comuni that straddle zones.", + "maximum": 4, + "minimum": 1, + "type": "integer" +} - added
Output schema / properties / layers / items / properties / scaleNoteAdded value: +{ + "description": "Only on layers whose scale direction is easy to misread (the seismic hazard class): says which end is which.", + "type": "string" +} - added
Output schema / properties / provinceRollup / properties / purchasePriceEurM2 / descriptionAdded value: +"OMI is Agenzia delle Entrate's official range of market values per m2, built from recorded sales and appraisals. Asking prices on listing sites are what sellers hope for, and usually run higher." - added
Output schema / properties / purchasePriceEurM2 / descriptionAdded value: +"OMI is Agenzia delle Entrate's official range of market values per m2, built from recorded sales and appraisals. Asking prices on listing sites are what sellers hope for, and usually run higher."
- Changed
get_province3 fields changed- added
Output schema / properties / layers / items / properties / officialZoneAdded value: +{ + "description": "Only on the seismic hazard layer: the comune's official zona sismica (1 = highest hazard, 4 = lowest). NOTE this runs the opposite way to `value`, which is a hazard class (5 minus the zone). Omitted for comuni that straddle zones.", + "maximum": 4, + "minimum": 1, + "type": "integer" +} - added
Output schema / properties / layers / items / properties / scaleNoteAdded value: +{ + "description": "Only on layers whose scale direction is easy to misread (the seismic hazard class): says which end is which.", + "type": "string" +} - added
Output schema / properties / prices / properties / purchasePriceEurM2 / descriptionAdded value: +"OMI is Agenzia delle Entrate's official range of market values per m2, built from recorded sales and appraisals. Asking prices on listing sites are what sellers hope for, and usually run higher."
2 tool updates
- Changed
get_comune2 fields changed- added
Output schema / properties / seismicZoneAdded value: +{ + "additionalProperties": false, + "description": "The comune's official seismic zone (zona sismica). A legal classification, not a forecast and not a verdict on any building. value is null, never a default zone, when the Department's table has no row for this comune.", + "properties": { + "coverage": { + "additionalProperties": false, + "description": "How many of Italy's places this dataset covers at all, e.g. 7587 of 7894 comuni.", + "properties": { + "in": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "of": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "in", + "of" + ], + "type": "object" + }, + "extractedOn": { + "description": "ISO date this value was extracted from the source.", + "type": "string" + }, + "license": { + "type": "string" + }, + "method": { + "description": "URL to the methodology page for this dataset.", + "type": "string" + }, + "reason": { + "description": "Present only when value is null: why this place has no figure.", + "type": "string" + }, + "source": { + "type": "string" + }, + "unit": { + "type": "string" + }, + "value": { + "anyOf": [ + { + "additionalProperties": false, + "properties": { + "hazardClass": { + "anyOf": [ + { + "maximum": 4, + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "The map layer's value: 5 minus the zone, so 4 is the highest hazard (official zone 1). null when split." + }, + "label": { + "description": "The Department of Civil Protection's label as published: \"3\", a regional sub-zone such as \"2B\" or \"3S\", or a combined label such as \"2A-3A-3B\".", + "type": "string" + }, + "officialZone": { + "anyOf": [ + { + "maximum": 4, + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "The national zone, 1 (highest hazard) to 4 (lowest). null when the comune straddles zones." + }, + "reading": { + "description": "What the classification is and is not. A legal classification used for planning and building control — not a forecast, and not an assessment of any building.", + "type": "string" + }, + "zonesInvolved": { + "description": "Every national zone the label names; more than one means the comune straddles zones.", + "items": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "type": "array" + } + }, + "required": [ + "officialZone", + "label", + "hazardClass", + "zonesInvolved", + "reading" + ], + "type": "object" + }, + { + "type": "null" + } + ] + }, + "vintage": { + "description": "The period the value describes, e.g. \"2025H2\" or \"2025\" — not when it was fetched.", + "type": "string" + } + }, + "required": [ + "value", + "unit", + "source", + "vintage", + "extractedOn", + "method", + "license" + ], + "type": "object" +} - changed
Output schema / requiredPrevious value: -[ - "place", - "population", - "foreignResidentsByCitizenship", - "totalForeignResidents", - "foreignResidentsAllCitizenships", - "purchasePriceEurM2", - "rentPriceEurM2Month", - "priceToRentYears", - "taxRegimeEligibility", - "provinceRollup", - "layers", - "provinceWellbeing", - "attribution" -]New value: +[ + "place", + "population", + "foreignResidentsByCitizenship", + "totalForeignResidents", + "foreignResidentsAllCitizenships", + "purchasePriceEurM2", + "rentPriceEurM2Month", + "priceToRentYears", + "taxRegimeEligibility", + "provinceRollup", + "seismicZone", + "layers", + "provinceWellbeing", + "attribution" +]
- Changed
get_province2 fields changed- added
Output schema / properties / seismicZonesAdded value: +{ + "additionalProperties": false, + "description": "How this province's comuni are spread across the official seismic zones: a count per zone, never an average. A province has no zone of its own.", + "properties": { + "coverage": { + "additionalProperties": false, + "description": "How many of Italy's places this dataset covers at all, e.g. 7587 of 7894 comuni.", + "properties": { + "in": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "of": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "in", + "of" + ], + "type": "object" + }, + "extractedOn": { + "description": "ISO date this value was extracted from the source.", + "type": "string" + }, + "license": { + "type": "string" + }, + "method": { + "description": "URL to the methodology page for this dataset.", + "type": "string" + }, + "reason": { + "description": "Present only when value is null: why this place has no figure.", + "type": "string" + }, + "source": { + "type": "string" + }, + "unit": { + "type": "string" + }, + "value": { + "anyOf": [ + { + "additionalProperties": false, + "properties": { + "comuniByOfficialZone": { + "additionalProperties": false, + "description": "Comuni in each official zone (zone1 is the highest hazard). Split comuni are not counted here.", + "properties": { + "zone1": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "zone2": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "zone3": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "zone4": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "zone1", + "zone2", + "zone3", + "zone4" + ], + "type": "object" + }, + "comuniWithoutClassification": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "reading": { + "type": "string" + }, + "straddlingZones": { + "description": "Comuni that straddle zones and so have no single zone; named, not rounded to one.", + "items": { + "additionalProperties": false, + "properties": { + "label": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "name", + "label" + ], + "type": "object" + }, + "type": "array" + }, + "totalComuni": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "comuniByOfficialZone", + "straddlingZones", + "comuniWithoutClassification", + "totalComuni", + "reading" + ], + "type": "object" + }, + { + "type": "null" + } + ] + }, + "vintage": { + "description": "The period the value describes, e.g. \"2025H2\" or \"2025\" — not when it was fetched.", + "type": "string" + } + }, + "required": [ + "value", + "unit", + "source", + "vintage", + "extractedOn", + "method", + "license" + ], + "type": "object" +} - changed
Output schema / requiredPrevious value: -[ - "province", - "population", - "prices", - "foreignResidentsByCitizenship", - "foreignResidentsAllCitizenships", - "wellbeing", - "layers", - "taxRegimes", - "missingSections", - "placesNotPeople", - "attribution" -]New value: +[ + "province", + "population", + "prices", + "foreignResidentsByCitizenship", + "foreignResidentsAllCitizenships", + "wellbeing", + "seismicZones", + "layers", + "taxRegimes", + "missingSections", + "placesNotPeople", + "attribution" +]
3 tool updates
- Changed
get_comune2 fields changed- changed
Output schema / properties / foreignResidentsAllCitizenships / descriptionPrevious value: -"Residents holding any foreign citizenship: ISTAT's own total, not limited to the 25 citizenships tracked in foreignResidentsByCitizenship. Citizenship, not birthplace. value is null, never 0, when ISTAT sends no total for this comune."New value: +"Residents holding any foreign citizenship: ISTAT's own total, not limited to the citizenships tracked in foreignResidentsByCitizenship. Citizenship, not birthplace. value is null, never 0, when ISTAT sends no total for this comune." - changed
Output schema / properties / totalForeignResidents / descriptionPrevious value: -"Sum of foreignResidentsByCitizenship: only the citizenships this site tracks (25), so a floor, not every foreign resident — Romanian, Albanian and Moroccan residents, among others, are not counted. For every foreign resident, see foreignResidentsAllCitizenships. null when the citizenship dataset has no row for this comune."New value: +"Sum of foreignResidentsByCitizenship: only the citizenships this site tracks, so a floor, not every foreign resident — Moroccan, Chinese and Indian residents, among others, are not counted. For every foreign resident, see foreignResidentsAllCitizenships. null when the citizenship dataset has no row for this comune."
- Changed
get_nationality1 field changed- changed
Input schema / properties / cc / descriptionPrevious value: -"ISO-2 citizenship code of one of the 25 citizenships this site tracks, e.g. \"US\"."New value: +"ISO-2 citizenship code of one of the citizenships this site tracks, e.g. \"US\"."
- Changed
get_province2 fields changed- changed
Output schema / properties / foreignResidentsAllCitizenships / descriptionPrevious value: -"Residents holding any foreign citizenship: ISTAT's own total, not limited to the 25 citizenships tracked in foreignResidentsByCitizenship. Citizenship, not birthplace. Summed over the comuni ISTAT has a total for: `coverage` says how many that is, and a comune with no total adds nothing rather than a zero."New value: +"Residents holding any foreign citizenship: ISTAT's own total, not limited to the citizenships tracked in foreignResidentsByCitizenship. Citizenship, not birthplace. Summed over the comuni ISTAT has a total for: `coverage` says how many that is, and a comune with no total adds nothing rather than a zero." - changed
Output schema / properties / foreignResidentsByCitizenship / properties / value / anyOfPrevious value: -[ - { - "items": { - "additionalProperties": false, - "properties": { - "code": { - "description": "ISO-2 citizenship code.", - "type": "string" - }, - "count": { - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "shareOfPopulation": { - "type": "number" - } - }, - "required": [ - "code", - "count", - "shareOfPopulation" - ], - "type": "object" - }, - "maxItems": 40, - "type": "array" - }, - { - "type": "null" - } -]New value: +[ + { + "items": { + "additionalProperties": false, + "properties": { + "code": { + "description": "ISO-2 citizenship code.", + "type": "string" + }, + "count": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "shareOfPopulation": { + "type": "number" + } + }, + "required": [ + "code", + "count", + "shareOfPopulation" + ], + "type": "object" + }, + "maxItems": 100, + "type": "array" + }, + { + "type": "null" + } +]
2 tool updates
- Changed
get_comune3 fields changed- added
Output schema / properties / foreignResidentsAllCitizenshipsAdded value: +{ + "additionalProperties": false, + "description": "Residents holding any foreign citizenship: ISTAT's own total, not limited to the 25 citizenships tracked in foreignResidentsByCitizenship. Citizenship, not birthplace. value is null, never 0, when ISTAT sends no total for this comune.", + "properties": { + "coverage": { + "additionalProperties": false, + "description": "How many of Italy's places this dataset covers at all, e.g. 7587 of 7894 comuni.", + "properties": { + "in": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "of": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "in", + "of" + ], + "type": "object" + }, + "extractedOn": { + "description": "ISO date this value was extracted from the source.", + "type": "string" + }, + "license": { + "type": "string" + }, + "method": { + "description": "URL to the methodology page for this dataset.", + "type": "string" + }, + "reason": { + "description": "Present only when value is null: why this place has no figure.", + "type": "string" + }, + "source": { + "type": "string" + }, + "unit": { + "type": "string" + }, + "value": { + "anyOf": [ + { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "vintage": { + "description": "The period the value describes, e.g. \"2025H2\" or \"2025\" — not when it was fetched.", + "type": "string" + } + }, + "required": [ + "value", + "unit", + "source", + "vintage", + "extractedOn", + "method", + "license" + ], + "type": "object" +} - changed
Output schema / properties / totalForeignResidents / descriptionPrevious value: -"Sum of foreignResidentsByCitizenship: only the citizenships this site tracks (25), so a floor, not every foreign resident — Romanian, Albanian and Moroccan residents, among others, are not counted. null when the citizenship dataset has no row for this comune."New value: +"Sum of foreignResidentsByCitizenship: only the citizenships this site tracks (25), so a floor, not every foreign resident — Romanian, Albanian and Moroccan residents, among others, are not counted. For every foreign resident, see foreignResidentsAllCitizenships. null when the citizenship dataset has no row for this comune." - changed
Output schema / requiredPrevious value: -[ - "place", - "population", - "foreignResidentsByCitizenship", - "totalForeignResidents", - "purchasePriceEurM2", - "rentPriceEurM2Month", - "priceToRentYears", - "taxRegimeEligibility", - "provinceRollup", - "layers", - "provinceWellbeing", - "attribution" -]New value: +[ + "place", + "population", + "foreignResidentsByCitizenship", + "totalForeignResidents", + "foreignResidentsAllCitizenships", + "purchasePriceEurM2", + "rentPriceEurM2Month", + "priceToRentYears", + "taxRegimeEligibility", + "provinceRollup", + "layers", + "provinceWellbeing", + "attribution" +]
- Changed
get_province2 fields changed- added
Output schema / properties / foreignResidentsAllCitizenshipsAdded value: +{ + "additionalProperties": false, + "description": "Residents holding any foreign citizenship: ISTAT's own total, not limited to the 25 citizenships tracked in foreignResidentsByCitizenship. Citizenship, not birthplace. Summed over the comuni ISTAT has a total for: `coverage` says how many that is, and a comune with no total adds nothing rather than a zero.", + "properties": { + "coverage": { + "additionalProperties": false, + "description": "How many of Italy's places this dataset covers at all, e.g. 7587 of 7894 comuni.", + "properties": { + "in": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "of": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "in", + "of" + ], + "type": "object" + }, + "extractedOn": { + "description": "ISO date this value was extracted from the source.", + "type": "string" + }, + "license": { + "type": "string" + }, + "method": { + "description": "URL to the methodology page for this dataset.", + "type": "string" + }, + "reason": { + "description": "Present only when value is null: why this place has no figure.", + "type": "string" + }, + "source": { + "type": "string" + }, + "unit": { + "type": "string" + }, + "value": { + "anyOf": [ + { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "vintage": { + "description": "The period the value describes, e.g. \"2025H2\" or \"2025\" — not when it was fetched.", + "type": "string" + } + }, + "required": [ + "value", + "unit", + "source", + "vintage", + "extractedOn", + "method", + "license" + ], + "type": "object" +} - changed
Output schema / requiredPrevious value: -[ - "province", - "population", - "prices", - "foreignResidentsByCitizenship", - "wellbeing", - "layers", - "taxRegimes", - "missingSections", - "placesNotPeople", - "attribution" -]New value: +[ + "province", + "population", + "prices", + "foreignResidentsByCitizenship", + "foreignResidentsAllCitizenships", + "wellbeing", + "layers", + "taxRegimes", + "missingSections", + "placesNotPeople", + "attribution" +]
8 tool updates
- Changed
check_tax_eligibility1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "attribution": { + "additionalProperties": false, + "properties": { + "citeAs": { + "description": "A ready-made citation line.", + "type": "string" + }, + "page": { + "description": "The expatliving.it page these figures come from — link it when you quote them.", + "type": "string" + }, + "publisher": { + "type": "string" + }, + "request": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "publisher", + "url", + "citeAs", + "request" + ], + "type": "object" + }, + "disclaimer": { + "type": "string" + }, + "geographicEligibility": { + "description": "qualifies: the comune meets the regime's verified geographic criteria. does_not_qualify: it doesn't, and the rule has no unverified route that could admit it. not_assessable: no geographic answer can be given (see reason).", + "enum": [ + "qualifies", + "does_not_qualify", + "not_assessable" + ], + "type": "string" + }, + "headroom": { + "description": "Residents between this comune and the ceiling, when it qualifies.", + "type": "number" + }, + "maxPopulation": { + "type": "number" + }, + "place": { + "additionalProperties": false, + "properties": { + "istatCode": { + "description": "6-digit ISTAT comune code, e.g. 075029.", + "type": "string" + }, + "lat": { + "type": "number" + }, + "lng": { + "type": "number" + }, + "name": { + "type": "string" + }, + "page": { + "description": "This comune's profile page on expatliving.it — link it when you quote any figure below.", + "type": "string" + }, + "province": { + "type": "string" + }, + "provinceCode": { + "type": "string" + }, + "provinceSlug": { + "type": "string" + }, + "region": { + "type": "string" + }, + "regionSlug": { + "type": "string" + }, + "slug": { + "description": "Stable URL slug for /data/comune/{slug} — never changes, even if a comune merges.", + "type": "string" + } + }, + "required": [ + "istatCode", + "slug", + "name", + "province", + "provinceCode", + "provinceSlug", + "region", + "regionSlug", + "lat", + "lng", + "page" + ], + "type": "object" + }, + "placesNotPeople": { + "const": true, + "type": "boolean" + }, + "population": { + "additionalProperties": false, + "properties": { + "coverage": { + "additionalProperties": false, + "description": "How many of Italy's places this dataset covers at all, e.g. 7587 of 7894 comuni.", + "properties": { + "in": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "of": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "in", + "of" + ], + "type": "object" + }, + "extractedOn": { + "description": "ISO date this value was extracted from the source.", + "type": "string" + }, + "license": { + "type": "string" + }, + "method": { + "description": "URL to the methodology page for this dataset.", + "type": "string" + }, + "reason": { + "description": "Present only when value is null: why this place has no figure.", + "type": "string" + }, + "source": { + "type": "string" + }, + "unit": { + "type": "string" + }, + "value": { + "type": [ + "number", + "null" + ] + }, + "vintage": { + "description": "The period the value describes, e.g. \"2025H2\" or \"2025\" — not when it was fetched.", + "type": "string" + } + }, + "required": [ + "value", + "unit", + "source", + "vintage", + "extractedOn", + "method", + "license" + ], + "type": "object" + }, + "reason": { + "description": "One sentence on why, safe to quote.", + "type": "string" + }, + "regime": { + "additionalProperties": false, + "properties": { + "criteria": { + "description": "geographic: some comuni qualify and others don't. person: turns on who you are, not where you live; no per-comune answer exists.", + "enum": [ + "geographic", + "person" + ], + "type": "string" + }, + "label": { + "type": "string" + }, + "page": { + "description": "The regime's page on expatliving.it.", + "type": "string" + }, + "slug": { + "description": "The regime's id, e.g. \"regime-7-percent-pensioners\".", + "type": "string" + }, + "status": { + "description": "draft: encoded but not yet checked against the legislation. A draft regime's eligibility is never served.", + "enum": [ + "verified", + "draft" + ], + "type": "string" + } + }, + "required": [ + "slug", + "label", + "status", + "criteria", + "page" + ], + "type": "object" + }, + "ruleIsIncomplete": { + "type": "boolean" + } + }, + "required": [ + "regime", + "place", + "geographicEligibility", + "reason", + "population", + "ruleIsIncomplete", + "placesNotPeople", + "disclaimer", + "attribution" + ], + "type": "object" +}
- Changed
get_article1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "attribution": { + "additionalProperties": false, + "properties": { + "citeAs": { + "description": "A ready-made citation line, with the article's canonical URL.", + "type": "string" + }, + "creditText": { + "type": "string" + }, + "license": { + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "name", + "url" + ], + "type": "object" + }, + "required": { + "const": true, + "description": "Credit with a link is the condition of free use (CC BY 4.0).", + "type": "boolean" + }, + "requirement": { + "type": "string" + } + }, + "required": [ + "required", + "license", + "creditText", + "citeAs", + "requirement" + ], + "type": "object" + }, + "author": { + "type": "string" + }, + "body": { + "description": "The article as Markdown, blocks flattened.", + "type": "string" + }, + "datasets": { + "items": { + "additionalProperties": false, + "properties": { + "coverage": { + "description": "How much of the place set the data covers, as the article states it.", + "type": "string" + }, + "slug": { + "description": "The dataset's slug in GET /datasets, when the page is one of them.", + "type": "string" + }, + "title": { + "type": "string" + }, + "url": { + "type": "string" + }, + "vintage": { + "type": "string" + } + }, + "required": [ + "title", + "url" + ], + "type": "object" + }, + "type": "array" + }, + "deck": { + "type": "string" + }, + "markdownUrl": { + "description": "The whole article as Markdown, with its citation at both ends.", + "type": "string" + }, + "places": { + "items": { + "additionalProperties": false, + "properties": { + "code": { + "description": "ISTAT code for a comune, sigla for a province, slug for a region.", + "type": "string" + }, + "grain": { + "enum": [ + "comune", + "province", + "region" + ], + "type": "string" + }, + "name": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "grain", + "code", + "name", + "url" + ], + "type": "object" + }, + "type": "array" + }, + "published": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "status": { + "enum": [ + "published", + "archived" + ], + "type": "string" + }, + "supersededBy": { + "description": "Slug of the newer article that replaces this one.", + "type": "string" + }, + "title": { + "type": "string" + }, + "topics": { + "items": { + "type": "string" + }, + "type": "array" + }, + "updated": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "slug", + "title", + "deck", + "author", + "published", + "updated", + "status", + "topics", + "url", + "markdownUrl", + "places", + "datasets", + "body", + "attribution" + ], + "type": "object" +}
- Changed
get_comune1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "articles": { + "description": "Published articles about this comune — the reporting behind the numbers. Credit required to reuse.", + "items": { + "additionalProperties": false, + "properties": { + "markdownUrl": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "title": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "slug", + "title", + "url", + "markdownUrl" + ], + "type": "object" + }, + "type": "array" + }, + "attribution": { + "additionalProperties": false, + "properties": { + "citeAs": { + "description": "A ready-made citation line.", + "type": "string" + }, + "page": { + "description": "The expatliving.it page these figures come from — link it when you quote them.", + "type": "string" + }, + "publisher": { + "type": "string" + }, + "request": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "publisher", + "url", + "citeAs", + "request" + ], + "type": "object" + }, + "foreignResidentsByCitizenship": { + "additionalProperties": false, + "description": "Counts by ISO-2 citizenship code. Citizenship, not birthplace — excludes foreign-born Italian citizens. value is null, never a zero-filled object, when ISTAT's citizenship dataset has no row at all for this comune.", + "properties": { + "coverage": { + "additionalProperties": false, + "description": "How many of Italy's places this dataset covers at all, e.g. 7587 of 7894 comuni.", + "properties": { + "in": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "of": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "in", + "of" + ], + "type": "object" + }, + "extractedOn": { + "description": "ISO date this value was extracted from the source.", + "type": "string" + }, + "license": { + "type": "string" + }, + "method": { + "description": "URL to the methodology page for this dataset.", + "type": "string" + }, + "reason": { + "description": "Present only when value is null: why this place has no figure.", + "type": "string" + }, + "source": { + "type": "string" + }, + "unit": { + "type": "string" + }, + "value": { + "anyOf": [ + { + "additionalProperties": { + "type": "number" + }, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + { + "type": "null" + } + ] + }, + "vintage": { + "description": "The period the value describes, e.g. \"2025H2\" or \"2025\" — not when it was fetched.", + "type": "string" + } + }, + "required": [ + "value", + "unit", + "source", + "vintage", + "extractedOn", + "method", + "license" + ], + "type": "object" + }, + "layers": { + "description": "Every other map layer's value for this place, at the grain its source publishes. A null value means the source has no figure for this place (see `reason`).", + "items": { + "additionalProperties": false, + "properties": { + "coverage": { + "additionalProperties": false, + "description": "How many of Italy's places this dataset covers at all, e.g. 7587 of 7894 comuni.", + "properties": { + "in": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "of": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "in", + "of" + ], + "type": "object" + }, + "download": { + "description": "The whole dataset, as published JSON (every place, with its meta block).", + "type": "string" + }, + "extractedOn": { + "description": "ISO date this value was extracted from the source.", + "type": "string" + }, + "grain": { + "description": "The level this value is measured at. Only \"comune\" is a measurement of this comune itself.", + "enum": [ + "comune", + "province", + "region" + ], + "type": "string" + }, + "label": { + "type": "string" + }, + "license": { + "type": "string" + }, + "measuredFor": { + "description": "The place the value describes, e.g. \"L'Aquila (province)\".", + "type": "string" + }, + "method": { + "description": "URL to the methodology page for this dataset.", + "type": "string" + }, + "note": { + "description": "The source's own coverage caveat, where it has one.", + "type": "string" + }, + "page": { + "description": "The map page for this layer on expatliving.it — link readers here.", + "type": "string" + }, + "reason": { + "description": "Present only when value is null: why this place has no figure.", + "type": "string" + }, + "slug": { + "description": "The dataset slug, as in GET /datasets.", + "type": "string" + }, + "source": { + "type": "string" + }, + "unit": { + "type": "string" + }, + "value": { + "type": [ + "number", + "null" + ] + }, + "vintage": { + "description": "The period the value describes, e.g. \"2025H2\" or \"2025\" — not when it was fetched.", + "type": "string" + } + }, + "required": [ + "value", + "unit", + "source", + "vintage", + "extractedOn", + "method", + "license", + "slug", + "label", + "grain", + "measuredFor", + "page", + "download" + ], + "type": "object" + }, + "maxItems": 100, + "type": "array" + }, + "place": { + "additionalProperties": false, + "properties": { + "istatCode": { + "description": "6-digit ISTAT comune code, e.g. 075029.", + "type": "string" + }, + "lat": { + "type": "number" + }, + "lng": { + "type": "number" + }, + "name": { + "type": "string" + }, + "page": { + "description": "This comune's profile page on expatliving.it — link it when you quote any figure below.", + "type": "string" + }, + "province": { + "type": "string" + }, + "provinceCode": { + "type": "string" + }, + "provinceSlug": { + "type": "string" + }, + "region": { + "type": "string" + }, + "regionSlug": { + "type": "string" + }, + "slug": { + "description": "Stable URL slug for /data/comune/{slug} — never changes, even if a comune merges.", + "type": "string" + } + }, + "required": [ + "istatCode", + "slug", + "name", + "province", + "provinceCode", + "provinceSlug", + "region", + "regionSlug", + "lat", + "lng", + "page" + ], + "type": "object" + }, + "population": { + "additionalProperties": false, + "properties": { + "coverage": { + "additionalProperties": false, + "description": "How many of Italy's places this dataset covers at all, e.g. 7587 of 7894 comuni.", + "properties": { + "in": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "of": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "in", + "of" + ], + "type": "object" + }, + "extractedOn": { + "description": "ISO date this value was extracted from the source.", + "type": "string" + }, + "license": { + "type": "string" + }, + "method": { + "description": "URL to the methodology page for this dataset.", + "type": "string" + }, + "reason": { + "description": "Present only when value is null: why this place has no figure.", + "type": "string" + }, + "source": { + "type": "string" + }, + "unit": { + "type": "string" + }, + "value": { + "anyOf": [ + { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "vintage": { + "description": "The period the value describes, e.g. \"2025H2\" or \"2025\" — not when it was fetched.", + "type": "string" + } + }, + "required": [ + "value", + "unit", + "source", + "vintage", + "extractedOn", + "method", + "license" + ], + "type": "object" + }, + "priceToRentYears": { + "additionalProperties": false, + "description": "Years of rent that equal the purchase price. Not a yield, an ROI, or a payback period.", + "properties": { + "coverage": { + "additionalProperties": false, + "description": "How many of Italy's places this dataset covers at all, e.g. 7587 of 7894 comuni.", + "properties": { + "in": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "of": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "in", + "of" + ], + "type": "object" + }, + "extractedOn": { + "description": "ISO date this value was extracted from the source.", + "type": "string" + }, + "license": { + "type": "string" + }, + "method": { + "description": "URL to the methodology page for this dataset.", + "type": "string" + }, + "reason": { + "description": "Present only when value is null: why this place has no figure.", + "type": "string" + }, + "source": { + "type": "string" + }, + "unit": { + "type": "string" + }, + "value": { + "type": [ + "number", + "null" + ] + }, + "vintage": { + "description": "The period the value describes, e.g. \"2025H2\" or \"2025\" — not when it was fetched.", + "type": "string" + } + }, + "required": [ + "value", + "unit", + "source", + "vintage", + "extractedOn", + "method", + "license" + ], + "type": "object" + }, + "provinceRollup": { + "additionalProperties": false, + "properties": { + "code": { + "type": "string" + }, + "name": { + "type": "string" + }, + "purchasePriceEurM2": { + "additionalProperties": false, + "properties": { + "coverage": { + "additionalProperties": false, + "description": "How many of Italy's places this dataset covers at all, e.g. 7587 of 7894 comuni.", + "properties": { + "in": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "of": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "in", + "of" + ], + "type": "object" + }, + "extractedOn": { + "description": "ISO date this value was extracted from the source.", + "type": "string" + }, + "license": { + "type": "string" + }, + "method": { + "description": "URL to the methodology page for this dataset.", + "type": "string" + }, + "reason": { + "description": "Present only when value is null: why this place has no figure.", + "type": "string" + }, + "source": { + "type": "string" + }, + "unit": { + "type": "string" + }, + "value": { + "type": [ + "number", + "null" + ] + }, + "vintage": { + "description": "The period the value describes, e.g. \"2025H2\" or \"2025\" — not when it was fetched.", + "type": "string" + } + }, + "required": [ + "value", + "unit", + "source", + "vintage", + "extractedOn", + "method", + "license" + ], + "type": "object" + }, + "rentPriceEurM2Month": { + "additionalProperties": false, + "properties": { + "coverage": { + "additionalProperties": false, + "description": "How many of Italy's places this dataset covers at all, e.g. 7587 of 7894 comuni.", + "properties": { + "in": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "of": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "in", + "of" + ], + "type": "object" + }, + "extractedOn": { + "description": "ISO date this value was extracted from the source.", + "type": "string" + }, + "license": { + "type": "string" + }, + "method": { + "description": "URL to the methodology page for this dataset.", + "type": "string" + }, + "reason": { + "description": "Present only when value is null: why this place has no figure.", + "type": "string" + }, + "source": { + "type": "string" + }, + "unit": { + "type": "string" + }, + "value": { + "type": [ + "number", + "null" + ] + }, + "vintage": { + "description": "The period the value describes, e.g. \"2025H2\" or \"2025\" — not when it was fetched.", + "type": "string" + } + }, + "required": [ + "value", + "unit", + "source", + "vintage", + "extractedOn", + "method", + "license" + ], + "type": "object" + }, + "slug": { + "type": "string" + } + }, + "required": [ + "name", + "code", + "slug", + "purchasePriceEurM2", + "rentPriceEurM2Month" + ], + "type": "object" + }, + "provinceWellbeing": { + "additionalProperties": false, + "description": "ISTAT BES well-being indicators for this comune's province — province-level, not a comune measurement.", + "properties": { + "coverage": { + "additionalProperties": false, + "description": "How many of Italy's places this dataset covers at all, e.g. 7587 of 7894 comuni.", + "properties": { + "in": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "of": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "in", + "of" + ], + "type": "object" + }, + "extractedOn": { + "description": "ISO date this value was extracted from the source.", + "type": "string" + }, + "grain": { + "const": "province", + "type": "string" + }, + "license": { + "type": "string" + }, + "measuredFor": { + "type": "string" + }, + "method": { + "description": "URL to the methodology page for this dataset.", + "type": "string" + }, + "page": { + "type": "string" + }, + "reason": { + "description": "Present only when value is null: why this place has no figure.", + "type": "string" + }, + "source": { + "type": "string" + }, + "unit": { + "type": "string" + }, + "value": { + "anyOf": [ + { + "items": { + "additionalProperties": false, + "properties": { + "code": { + "type": "string" + }, + "domain": { + "type": "string" + }, + "label": { + "type": "string" + }, + "percentile": { + "description": "0–100 rank among Italy's provinces, polarity-aware: higher is always better, whichever way the raw value runs.", + "type": "number" + }, + "polarity": { + "description": "\"high\": a higher raw value is better; \"low\": a lower one is.", + "enum": [ + "high", + "low" + ], + "type": "string" + }, + "unit": { + "type": "string" + }, + "value": { + "description": "The raw indicator value.", + "type": "number" + }, + "year": { + "description": "The year this observation describes — indicators update on different cycles.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "code", + "domain", + "label", + "unit", + "polarity", + "value", + "year", + "percentile" + ], + "type": "object" + }, + "maxItems": 80, + "type": "array" + }, + { + "type": "null" + } + ] + }, + "vintage": { + "description": "The period the value describes, e.g. \"2025H2\" or \"2025\" — not when it was fetched.", + "type": "string" + } + }, + "required": [ + "value", + "unit", + "source", + "vintage", + "extractedOn", + "method", + "license", + "grain", + "measuredFor", + "page" + ], + "type": "object" + }, + "purchasePriceEurM2": { + "additionalProperties": false, + "properties": { + "coverage": { + "additionalProperties": false, + "description": "How many of Italy's places this dataset covers at all, e.g. 7587 of 7894 comuni.", + "properties": { + "in": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "of": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "in", + "of" + ], + "type": "object" + }, + "extractedOn": { + "description": "ISO date this value was extracted from the source.", + "type": "string" + }, + "license": { + "type": "string" + }, + "method": { + "description": "URL to the methodology page for this dataset.", + "type": "string" + }, + "reason": { + "description": "Present only when value is null: why this place has no figure.", + "type": "string" + }, + "source": { + "type": "string" + }, + "unit": { + "type": "string" + }, + "value": { + "type": [ + "number", + "null" + ] + }, + "vintage": { + "description": "The period the value describes, e.g. \"2025H2\" or \"2025\" — not when it was fetched.", + "type": "string" + } + }, + "required": [ + "value", + "unit", + "source", + "vintage", + "extractedOn", + "method", + "license" + ], + "type": "object" + }, + "rentPriceEurM2Month": { + "additionalProperties": false, + "properties": { + "coverage": { + "additionalProperties": false, + "description": "How many of Italy's places this dataset covers at all, e.g. 7587 of 7894 comuni.", + "properties": { + "in": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "of": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "in", + "of" + ], + "type": "object" + }, + "extractedOn": { + "description": "ISO date this value was extracted from the source.", + "type": "string" + }, + "license": { + "type": "string" + }, + "method": { + "description": "URL to the methodology page for this dataset.", + "type": "string" + }, + "reason": { + "description": "Present only when value is null: why this place has no figure.", + "type": "string" + }, + "source": { + "type": "string" + }, + "unit": { + "type": "string" + }, + "value": { + "type": [ + "number", + "null" + ] + }, + "vintage": { + "description": "The period the value describes, e.g. \"2025H2\" or \"2025\" — not when it was fetched.", + "type": "string" + } + }, + "required": [ + "value", + "unit", + "source", + "vintage", + "extractedOn", + "method", + "license" + ], + "type": "object" + }, + "taxRegimeEligibility": { + "description": "Verified regimes only — \"placesNotPeople\": geographic eligibility, not personal advice.", + "items": { + "additionalProperties": false, + "properties": { + "headroom": { + "description": "Residents between this comune and the ceiling.", + "type": "number" + }, + "href": { + "description": "Path to this regime's own page on expatliving.it.", + "type": "string" + }, + "label": { + "type": "string" + }, + "maxPopulation": { + "description": "The regime's population ceiling, when it has one.", + "type": "number" + }, + "ruleIsIncomplete": { + "description": "True when the rule admits comuni by routes not yet verified — silence about OTHER regimes proves nothing.", + "type": "boolean" + }, + "slug": { + "type": "string" + } + }, + "required": [ + "slug", + "label", + "href", + "ruleIsIncomplete" + ], + "type": "object" + }, + "type": "array" + }, + "totalForeignResidents": { + "anyOf": [ + { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Sum of foreignResidentsByCitizenship: only the citizenships this site tracks (25), so a floor, not every foreign resident — Romanian, Albanian and Moroccan residents, among others, are not counted. null when the citizenship dataset has no row for this comune." + } + }, + "required": [ + "place", + "population", + "foreignResidentsByCitizenship", + "totalForeignResidents", + "purchasePriceEurM2", + "rentPriceEurM2Month", + "priceToRentYears", + "taxRegimeEligibility", + "provinceRollup", + "layers", + "provinceWellbeing", + "attribution" + ], + "type": "object" +}
- Changed
get_nationality1 field changed- changed
Output schema / properties / topComuni / properties / value / anyOfPrevious value: -[ - { - "items": { - "additionalProperties": false, - "properties": { - "count": { - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "istatCode": { - "type": "string" - }, - "name": { - "type": "string" - }, - "page": { - "type": "string" - }, - "population": { - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "province": { - "type": "string" - }, - "provinceCode": { - "type": "string" - }, - "rank": { - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "region": { - "type": "string" - }, - "shareOfPopulation": { - "description": "A fraction, 0–1.", - "type": "number" - }, - "smallNumber": { - "description": "True when fewer than 50 residents hold this citizenship here: the share rests on a handful of households.", - "type": "boolean" - } - }, - "required": [ - "rank", - "istatCode", - "name", - "province", - "provinceCode", - "region", - "population", - "count", - "shareOfPopulation", - "smallNumber", - "page" - ], - "type": "object" - }, - "maxItems": 50, - "type": "array" - }, - { - "type": "null" - } -]New value: +[ + { + "items": { + "additionalProperties": false, + "properties": { + "count": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "istatCode": { + "type": "string" + }, + "name": { + "type": "string" + }, + "page": { + "type": "string" + }, + "population": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "province": { + "type": "string" + }, + "provinceCode": { + "type": "string" + }, + "rank": { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "region": { + "type": "string" + }, + "shareOfPopulation": { + "description": "A fraction, 0–1.", + "type": "number" + }, + "smallNumber": { + "description": "True when fewer than 30 residents hold this citizenship here: the share rests on a handful of households.", + "type": "boolean" + } + }, + "required": [ + "rank", + "istatCode", + "name", + "province", + "provinceCode", + "region", + "population", + "count", + "shareOfPopulation", + "smallNumber", + "page" + ], + "type": "object" + }, + "maxItems": 50, + "type": "array" + }, + { + "type": "null" + } +]
- Changed
list_articles1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "articles": { + "items": { + "additionalProperties": false, + "properties": { + "author": { + "type": "string" + }, + "datasets": { + "items": { + "additionalProperties": false, + "properties": { + "coverage": { + "description": "How much of the place set the data covers, as the article states it.", + "type": "string" + }, + "slug": { + "description": "The dataset's slug in GET /datasets, when the page is one of them.", + "type": "string" + }, + "title": { + "type": "string" + }, + "url": { + "type": "string" + }, + "vintage": { + "type": "string" + } + }, + "required": [ + "title", + "url" + ], + "type": "object" + }, + "type": "array" + }, + "deck": { + "type": "string" + }, + "markdownUrl": { + "description": "The whole article as Markdown, with its citation at both ends.", + "type": "string" + }, + "places": { + "items": { + "additionalProperties": false, + "properties": { + "code": { + "description": "ISTAT code for a comune, sigla for a province, slug for a region.", + "type": "string" + }, + "grain": { + "enum": [ + "comune", + "province", + "region" + ], + "type": "string" + }, + "name": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "grain", + "code", + "name", + "url" + ], + "type": "object" + }, + "type": "array" + }, + "published": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "status": { + "enum": [ + "published", + "archived" + ], + "type": "string" + }, + "supersededBy": { + "description": "Slug of the newer article that replaces this one.", + "type": "string" + }, + "title": { + "type": "string" + }, + "topics": { + "items": { + "type": "string" + }, + "type": "array" + }, + "updated": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "slug", + "title", + "deck", + "author", + "published", + "updated", + "status", + "topics", + "url", + "markdownUrl", + "places", + "datasets" + ], + "type": "object" + }, + "type": "array" + }, + "attribution": { + "additionalProperties": false, + "properties": { + "citeAs": { + "description": "Per article: \"“<title>” by <author>, Expat Living Italy, <date>. <url>\".", + "type": "string" + }, + "creditText": { + "type": "string" + }, + "license": { + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "name", + "url" + ], + "type": "object" + }, + "required": { + "const": true, + "description": "Credit with a link is the condition of free use (CC BY 4.0).", + "type": "boolean" + }, + "requirement": { + "type": "string" + } + }, + "required": [ + "required", + "license", + "creditText", + "requirement", + "citeAs" + ], + "type": "object" + } + }, + "required": [ + "articles", + "attribution" + ], + "type": "object" +}
- Changed
list_datasets1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "attribution": { + "additionalProperties": false, + "properties": { + "citeAs": { + "description": "A ready-made citation line.", + "type": "string" + }, + "page": { + "description": "The expatliving.it page these figures come from — link it when you quote them.", + "type": "string" + }, + "publisher": { + "type": "string" + }, + "request": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "publisher", + "url", + "citeAs", + "request" + ], + "type": "object" + }, + "datasets": { + "items": { + "additionalProperties": false, + "properties": { + "download": { + "description": "The whole dataset as published JSON: a meta block (provenance, unit, grain) and every place's value.", + "type": "string" + }, + "extractedOn": { + "description": "ISO date this dataset was extracted from the source.", + "type": "string" + }, + "grain": { + "description": "The geographic level the source publishes at. NUTS 2 and NUTS 3 are Eurostat/OECD statistical regions — close to, but not the same as, Italy's regions and provinces. Never present a coarser-grain value as if it were measured for one comune.", + "enum": [ + "comune", + "province", + "region", + "nuts2", + "nuts3", + "national" + ], + "type": "string" + }, + "label": { + "type": "string" + }, + "license": { + "type": "string" + }, + "page": { + "description": "The human-readable page for this dataset on expatliving.it — link readers here.", + "type": "string" + }, + "servable": { + "description": "False while a hand-curated dataset is still \"draft\" (unverified against its legislation). A non-servable dataset returns no values anywhere in this API.", + "type": "boolean" + }, + "slug": { + "type": "string" + }, + "source": { + "description": "The institution to cite, e.g. \"ISTAT\" or \"Eurostat\".", + "type": "string" + }, + "sourceUrl": { + "description": "Where this dataset was fetched from upstream.", + "type": "string" + }, + "vintage": { + "description": "The period the data describes, e.g. \"2025H2\", \"2024\" or \"2015–2024\" — not when it was fetched.", + "type": "string" + } + }, + "required": [ + "slug", + "label", + "source", + "sourceUrl", + "vintage", + "extractedOn", + "license", + "grain", + "servable" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "datasets", + "attribution" + ], + "type": "object" +}
- Changed
list_tax_regimes1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "attribution": { + "additionalProperties": false, + "properties": { + "citeAs": { + "description": "A ready-made citation line.", + "type": "string" + }, + "page": { + "description": "The expatliving.it page these figures come from — link it when you quote them.", + "type": "string" + }, + "publisher": { + "type": "string" + }, + "request": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "publisher", + "url", + "citeAs", + "request" + ], + "type": "object" + }, + "disclaimer": { + "type": "string" + }, + "placesNotPeople": { + "const": true, + "type": "boolean" + }, + "regimes": { + "items": { + "additionalProperties": false, + "properties": { + "criteria": { + "description": "geographic: some comuni qualify and others don't. person: turns on who you are, not where you live; no per-comune answer exists.", + "enum": [ + "geographic", + "person" + ], + "type": "string" + }, + "knownIncompleteness": { + "description": "Ways the encoded rule is narrower than the law. A comune missing from the set is not proof it doesn't qualify while this is non-empty.", + "items": { + "type": "string" + }, + "type": "array" + }, + "label": { + "type": "string" + }, + "maxPopulation": { + "description": "The population ceiling, for a geographic regime that has one.", + "type": "number" + }, + "page": { + "description": "The regime's page on expatliving.it.", + "type": "string" + }, + "qualifyingComuni": { + "description": "How many comuni meet the verified geographic criteria. Only for a verified geographic regime.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "shortLabel": { + "type": "string" + }, + "slug": { + "description": "The regime's id, e.g. \"regime-7-percent-pensioners\".", + "type": "string" + }, + "sourceLegislation": { + "items": { + "additionalProperties": false, + "properties": { + "cite": { + "type": "string" + }, + "title": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "cite", + "title", + "url" + ], + "type": "object" + }, + "type": "array" + }, + "status": { + "description": "draft: encoded but not yet checked against the legislation. A draft regime's eligibility is never served.", + "enum": [ + "verified", + "draft" + ], + "type": "string" + }, + "summary": { + "type": "string" + }, + "validFrom": { + "type": "string" + } + }, + "required": [ + "slug", + "label", + "shortLabel", + "status", + "criteria", + "page", + "summary", + "validFrom", + "sourceLegislation", + "knownIncompleteness" + ], + "type": "object" + }, + "maxItems": 50, + "type": "array" + } + }, + "required": [ + "regimes", + "placesNotPeople", + "disclaimer", + "attribution" + ], + "type": "object" +}
- Changed
search_comuni1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "attribution": { + "additionalProperties": false, + "properties": { + "citeAs": { + "description": "A ready-made citation line.", + "type": "string" + }, + "page": { + "description": "The expatliving.it page these figures come from — link it when you quote them.", + "type": "string" + }, + "publisher": { + "type": "string" + }, + "request": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "publisher", + "url", + "citeAs", + "request" + ], + "type": "object" + }, + "query": { + "type": "string" + }, + "results": { + "description": "Best matches first: exact name, then names starting with the query, then names containing it. Several Italian comuni share a name — check the province before picking one.", + "items": { + "additionalProperties": false, + "properties": { + "istatCode": { + "description": "6-digit ISTAT comune code — pass this to GET /comuni/{istatCode} or get_comune.", + "type": "string" + }, + "name": { + "type": "string" + }, + "page": { + "description": "The comune's profile page on expatliving.it.", + "type": "string" + }, + "province": { + "type": "string" + }, + "provinceCode": { + "type": "string" + }, + "region": { + "type": "string" + } + }, + "required": [ + "istatCode", + "name", + "province", + "provinceCode", + "region", + "page" + ], + "type": "object" + }, + "maxItems": 25, + "type": "array" + }, + "truncated": { + "description": "True when more comuni matched than `limit` allowed — narrow the query.", + "type": "boolean" + } + }, + "required": [ + "query", + "results", + "truncated", + "attribution" + ], + "type": "object" +}
2 tool updates
- Added
get_coverage - Added
get_nationality
2 tool updates
- Added
find_comuni - Added
get_province
2 tool updates
- Added
check_tax_eligibility - Added
list_tax_regimes
2 tool updates
- Added
get_article - Added
list_articles
3 tool updates
- First observed
get_comune - First observed
list_datasets - First observed
search_comuni
Related MCP Connectors
Open data on ~30,000 European towns: monthly climate, costs, amenities, access, hazards, rankings
Start again in Italy: towns that pay you to move, 1-euro houses, remote jobs, plan preview.
Italian cadastre for AI agents: 85M parcels, geocoding, OMI values, risk layers.
Read-only migration data: cities, visa routes, entry rules, costs, safety, climate, with sources.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceEnables querying a comprehensive data lake of Italian civic open data, returning cited facts about Italian municipalities across domains like environment, education, finance, and governance.1-
- AlicenseNot gradedqualityBmaintenanceEnables querying Italian national statistics (ISTAT) data through natural language questions, with tools for accessing demographic, economic, and social indicators.590 npmMIT
- AlicenseNot gradedqualityCmaintenanceEnables read-only lookup and comparison of sourced, dated migration data — 39 countries and 27,000+ cities, visa and residence routes, passport entry rules, cost of living, salaries, crime, air quality and climate. Every fact returned includes its value, unit, geographic grain, observation date, source and grade, with unknown values left explicit rather than estimated.MIT

io.github.stucchi/italyofficial
AlicenseNot gradedqualityDmaintenanceMCP server exposing Italian open data (municipalities, provinces, regions, postal codes, coordinates, geographic data) through simple, developer-friendly tools.1MIT
Glama MCP Gateway
Add one secure layer between your agents and this server.