Skip to main content
Glama

ModuHaus — Australian Modular House Knowledge Base

A PostgreSQL-backed knowledge base covering everything a buyer or builder needs to know about modular housing in Australia: planning regulation, finance, cost, suppliers, contracts and construction. It is built to be consumed by an MCP server and by interactive tools on the ModuHaus website (site feasibility lookup, budget estimator, and so on).

The knowledge base is deliberately built one module at a time. The schema and naming conventions below exist so that later modules drop into the same database without restructuring anything.


Design principles

One database, many modules. Everything lives in modular_house_kb. The name describes the whole knowledge base, not any single feature — there is no granny_flat_db. A single database keeps cross-module queries possible: a feasibility check that combines a planning rule with a cost band and a supplier lead time should be one SQL query, not three round trips.

The table name carries the module. Every table is prefixed with its module, so \dt in psql groups the schema by domain and any reader can tell at a glance what a table belongs to:

regulation_*     planning rules, permit pathways, DA process
finance_*        construction loan structures, lender policies, rates
cost_*           build cost ranges by spec and region
supplier_*       suppliers, product lines, specifications, reputation
contract_*       standard clauses, common dispute points
construction_*   timelines, transport and install, delay causes

Rows are jurisdiction-scoped and source-stamped. Australian modular housing rules differ per state and change often. Every fact-bearing table carries the jurisdiction it applies to plus source and last_verified, so stale content is visible rather than silently wrong. Never store a regulatory number without the instrument it came from.


Related MCP server: au-eli-mcp

Current status

Completed — Regulation module, granny flat subset

regulation_granny_flat_rules holds state-level approval standards for secondary dwellings (granny flats, office pods).

Column

Type

Notes

id

SERIAL PK

state

VARCHAR(10)

NSW, VIC, ...

category

VARCHAR(50)

granny_flat

rule_type

VARCHAR(100)

the specific rule being described

max_floor_area_sqm

NUMERIC

min_lot_size_sqm

NUMERIC

default_permit_required

BOOLEAN

whether approval is needed by default

excluding_conditions

TEXT[]

overlays/hazards that void the pathway

fast_track_pathway

VARCHAR(50)

e.g. CDC

fast_track_days

INTEGER

fallback_pathway

VARCHAR(50)

e.g. DA

fallback_max_days

INTEGER

source

TEXT

the planning instrument

last_verified

DATE

All eight jurisdictions are seeded. The headline numbers:

State

Max area

Min lot

Permit by default

Instrument

NSW

60 sqm

450 sqm

yes (CDC)

SEPP (Housing) 2021

VIC

60 sqm

300 sqm

no

Amendment VC253

QLD

set by each council

WA

70 sqm

no

R-Codes Vol 1

SA

70 sqm

yes

Planning and Design Code

TAS

60 sqm

no

State Planning Provisions

ACT

90 sqm

500 sqm

yes

Territory Plan 2023

NT

75 sqm

no

NT Planning Scheme 2020

Every row was checked against the responsible planning authority's own site on its last_verified date. Where a jurisdiction publishes no single statewide figure the column is NULL rather than a number borrowed from a builder's website — see the NULL semantics gap below before reading those blanks.

Two rows deserve explanation:

  • QLD sets no statewide standard at all. The Planning Act 2016 leaves both size and assessment pathway to each local government's planning scheme, so every numeric column is NULL and data_status is no_rule_at_this_level. The only statewide rule is the 2022 amendment removing occupancy restrictions. Queensland's real numbers live in regulation_lga_granny_flat_rules.

  • default_permit_required measures whether approval is needed at all, not how fast it is. NSW is true because a complying development certificate is still issued; VIC, WA, TAS and NT are false because the requirement is removed outright when the conditions are met.

UNIQUE (state, category, rule_type) is the natural key. Note that default_permit_required is not a measure of speed but of whether approval is needed at all: NSW is true because a complying development certificate is still issued, while VIC is false because the permit requirement is removed outright.

The VIC row was verified against Planning Victoria on 2026-08-11: Amendment VC253, gazetted 14 December 2023, is the correct instrument, and the 300 sqm minimum lot size is confirmed for the residential zones.

Completed — Regulation module, council-level subset

regulation_lga_granny_flat_rules mirrors the state table plus lga_name, planning_scheme and varies_from_state. Queensland depends on it entirely; elsewhere a row should exist only where a council departs from the state position, so the absence of a row means the state rule applies.

Seeded with four Queensland councils, of which two are fully verified:

Council

Status

Notes

City of Moreton Bay

verified

Limit steps with lot area (45 / 55 / 100 sqm), hence three rows

City of Gold Coast

verified

Accepted development at 80 sqm; no minimum lot size in the code

Brisbane City Council

partial

Approval pathway unresolved — see below

Logan City Council

partial

Approval pathway sits in Part 5, served only via the council's enquiry tool

Brisbane is the instructive one. Council material says prescribed accepted development (Table 5.3.4.1) does not extend to secondary dwellings, yet Brisbane defines a dwelling house as including one with a secondary dwelling, and a dwelling house is generally accepted development under the Part 5 zone tables. Those are two different mechanisms and the reachable sources do not settle which governs, so default_permit_required stays not_researched rather than being resolved by inference. Recording the ambiguity is the point: a guess here would be indistinguishable from a fact once it is in the table.

Still to add: Sunshine Coast, Ipswich, Townsville, Cairns, Toowoomba, Redland, and the remaining Queensland councils.

Note that published builder and comparison sites disagree with the councils' own schemes on these numbers — Moreton Bay is widely quoted as 90 sqm when the scheme says 45/55/100. Seed only from the responsible authority's own document.

Completed — Cost module, granny flat subset

Two tables. cost_granny_flat_build_ranges holds build cost by state, size and specification; cost_site_works_items holds the twelve site works that get added to it.

They are split because the market's characteristic distortion is a base price presented as a total — site costs commonly add 25–50%. On the figures currently loaded, the priced site items sum to $53,500–$138,500 if every one applied. That number is the reason the module exists: it cannot be expressed as a footnote on a build price, so it gets its own table and always travels with the price.

cost_basis is NOT NULL. A price whose basis is unstated is worse than no price, because it reads as a total. Western Australia is the clearest case for that column: the same 60 sqm dwelling is quoted at about $76,000 supplied as a module, $105,000–200,000 built on site, and $130,000–250,000 all-in. Those are not competing estimates — they cover different things.

All eight jurisdictions carry a two-bedroom mid-spec range, which is what lets compare_states report money. The comparison ships with a warning attached, because the bases genuinely differ: QLD and WA are quoted all-in and so look more expensive than states quoted before site costs, which they are not.

Size and specification grid

23 ranges spanning studio to three bedrooms and basic to premium:

Bedrooms

basic

mid

premium

0 (studio, 25–35 sqm)

national

1 (35–60 sqm)

ACT, TAS

national, ACT, NSW, SA, TAS

2 (~60 sqm)

WA

all 8 + national

national, ACT, WA

3 (80–100 sqm)

national, QLD

Size is a band, and bedrooms carries the natural key. Sources quote "1 bedroom, 45–60 sqm" — the market segments on bedroom count and a size range follows. The original dwelling_size_sqm forced that into a point value, inventing a precision no source claimed; it is now size_min_sqm / size_max_sqm plus bedrooms.

Two facts a budget estimator must not get wrong, both returned on every call:

  • Cost does not scale with floor area. Kitchen, bathroom and service connections are near-fixed, so per-sqm cost rises as the dwelling shrinks. A studio is not half a two-bedroom at half the size, and a $/sqm × area estimator will be wrong at the small end.

  • A price for an illegal size is worse than no price. A three-bedroom around 90 sqm exceeds the secondary dwelling cap in NSW, VIC and TAS (60 sqm) and in Brisbane (80 sqm). Only the ACT permits 90 sqm outright. The three-bedroom rows are priced because the market prices them, not because they can be approved as granny flats — the tool says so, and points at the regulation module.

A migration bug worth remembering

Changing the natural key from size to bedrooms orphaned every pre-existing row. The first migration backfilled size_min/size_max but not bedrooms, so the old rows fell outside the new key: the seed's upsert stopped matching them and inserted duplicates alongside instead of updating, silently doubling the table.

Two fixes, both in MODULE_MIGRATIONS: the backfill now sets bedrooms too, so a database migrating for the first time never orphans anything; and a repair statement deletes rows that have NULL bedrooms and a keyed equivalent superseding them — a row with genuinely unknown bedrooms and no sibling is left alone. The general lesson: when a natural key changes, the migration has to backfill every column the new key touches, or the upsert silently stops being an upsert.

Two rows are worth reading before trusting the label over the detail:

  • SA covers design fees, slab, construction, fit-out and both consents, but excludes utility connections ($6,000–15,000 separately). None of the three cost_basis values fits exactly; the includes / excludes arrays carry what the label cannot.

  • NT is structurally, not cyclically, more expensive: cyclone-rated construction for 316 km/h wind, mandatory air conditioning and termite barriers, freight, and trades at 110–135% of Sydney rates. Those drivers are recorded as NT-scoped rows in cost_site_works_items with no dollar amounts — confirmed to exist, unresearched in size.

Which exposed a bug worth keeping in mind for any future total: unpriced rows sum to zero, so a naive total silently understates by exactly the items nobody has costed. site_works_total now counts them, names them, and labels the total a floor.

What the second module taught us about the architecture

The multi-module design held, but not unchanged. Three things transferred intact: the <module>_<subject> naming, the three-way NULL semantics, and natural-key upserts. Two things did not:

  • data_status is necessary but not sufficient. It answers "has anyone researched this?" Cost data needs a second, orthogonal axis: a builder's advertised price is fully researched and still the least reliable number available, because the figure is marketing. source_type (builder_advertised / industry_estimate / market_survey) carries that, and costs.py renders it into a sentence a consumer can relay. Regulation never needed it — its source was always the instrument itself.

  • UNIQUE needs NULLS NOT DISTINCT. The regulation keys had no nullable columns. Cost keys do: a national figure has no state, a statewide one has no region. Under Postgres's default handling every such row is distinct, so the upsert would insert duplicates forever instead of updating.

Two new integrity rules came with the module: an inverted range (low > high) is rejected, because it produces a plausible-looking answer with the numbers the wrong way round; and equal bounds are accepted, because a fixed price is legitimate.

Planned modules

  • Finance — construction loan structures, per-lender policy, progress payment schedules, current rates.

  • Supplier — suppliers, product lines, specifications, certifications, reviews.

  • Contract — standard clauses, variation and delay provisions, common disputes.

  • Construction — lead times, transport and craneage, install sequence, typical causes of delay.


Setup

Requires PostgreSQL 14+ and uv.

uv sync

Copy .env.example to .env and set DB_PASSWORD to your PostgreSQL password. .env is gitignored and must never be committed; db.py is the only place credentials are read, and every script imports from it.

uv run setup_db.py          # create the database, tables, migrations, constraints
uv run insert_rules.py      # seed the state-level granny flat rules
uv run insert_lga_rules.py  # seed the council-level rules
uv run insert_cost_ranges.py # seed the cost module
uv run query_rules.py       # read them back

All four are safe to re-run. setup_db.py creates only what is missing and applies migrations that are no-ops once current; the seed scripts update existing rows in place instead of duplicating them.

Tests

uv run pytest                    # everything
uv run pytest -m "not protocol"  # skip the subprocess tests (faster)

96 tests against the live development database. Anything a test writes is marked and purged by the conn fixture, so a failure cannot leave the knowledge base in a state where a later query returns invented data.

File

Guards

test_null_semantics.py

The three-way NULL logic — the safety-critical part

test_constraints.py

Every database integrity rule, and that each rejects the value it exists for

test_seeds.py

Setup and seeds are idempotent; coverage and data invariants hold

test_mcp_tools.py

The tools' guardrails, called directly

test_estimator.py

Double-counting, the legality cross-check, and refusing to invent a missing combination

test_mcp_protocol.py

A real stdio handshake — proves a client can actually reach them

The suite asserts guardrails, not numbers. Costs and rules change as research continues; a test pinned to $180,000 would break on every legitimate update and teach the next person to ignore failures. What must not change is that a not_researched field is never presented as a fact, that a price never arrives without its basis, and that Queensland is never silently dropped from a ranking.

Verified by mutation: collapsing not_researched into not_applicable, making every NULL read as no_rule_at_this_level, and dropping site works from the cost payload are each caught by a failing test. A suite that passes but survives those would be worse than none, because it would look like protection.

MCP server

mcp_server.py exposes the knowledge base over the Model Context Protocol (stdio transport, built on the official mcp SDK):

uv run mcp_server.py

Tool

Answers

get_granny_flat_rules(state, lga?)

"Can I build one in X, how big, and do I need approval?" — returns the state rule plus any council rules

compare_states()

"Which state is easiest?" — cross-jurisdiction comparison

get_granny_flat_costs(state?, bedrooms?, modular_site_costs_only?)

"What does it cost?" — build ranges and the site works added to them, always together

estimate_budget(state, bedrooms, spec_level, site_conditions?, is_modular?)

"What will my project cost?" — composes both modules into one figure

check_data_freshness(max_age_months)

"Is this still current?" — spans both modules

The estimator

estimate_budget is the first tool that composes the two modules rather than reading one. It takes the block's conditions (sloping_site, reactive_clay_soil, services_over_15m, separate_vehicle_access, separate_tenancy, site_not_flat_or_clear) and returns a range with the applicable site works folded in.

Two ways it could be confidently wrong, both guarded:

  • Double-counting. Adding site works to a turnkey_incl_site quote inflates it by tens of thousands; omitting them from turnkey_excl_site or base_module understates it by the same. BASIS_NEEDS_SITE_WORKS decides, and the response says which happened and why. On the current data NSW, QLD and WA are quoted all-in and get nothing added; VIC, SA, TAS, ACT and NT do.

  • Pricing something that cannot be built. Every estimate carries a size_legality verdict checked against the state's cap — within_the_cap, over_the_cap, or cannot_check_at_state_level for Queensland, where the cap lives with the council. A NSW three-bedroom returns a price and the fact that 100 sqm exceeds the 60 sqm cap.

A combination with no data returns found: false and lists what is on record, rather than scaling a neighbouring size or spec into an invented figure. Unrecognised site conditions are reported back, because silently dropping one produces an estimate that looks complete and is not.

Site works carry a machine-readable trigger_condition alongside the prose applies_when, so the estimator selects them from the data rather than from a mapping hardcoded in Python — a new item becomes selectable by being inserted.

The server's contract is that a null is never a fact. Every null comes back with a why_missing entry naming one of the three reasons above, and any field whose value is genuinely unknown is also listed in must_not_state. A model relaying an answer has to work against the payload to invent a figure, rather than merely failing to notice a blank. Day counts always travel with their _basis, and every rule carries source and last_verified.

rules.py holds the read layer and the why_null logic. Both the CLI and the MCP server go through it, so the two can never disagree about what a blank cell means — this is the one piece of logic worth centralising, because the failure it prevents is a fabricated regulatory answer. costs.py is the cost module's equivalent and reuses rules.why_null rather than reimplementing it.

The cost tool deliberately has no way to return a build price on its own. Site works come back on every call, with their total, because quoting a base price without them is precisely how this market misleads people.

To register it with an MCP client, point the client at uv run mcp_server.py with this directory as the working directory. The server reads credentials from .env like every other script.

Querying

uv run query_rules.py          # every state
uv run query_rules.py NSW      # one state
uv run query_rules.py nsw vic  # several

Conventions for adding a module

  1. Name the table <module>_<subject>. Use the module prefixes listed above; add a new prefix only for a genuinely new domain, and document it here. Table and column names are snake_case, table names plural where the row is one of many (..._rules, ..._lenders).

  2. Put units in the column name. max_floor_area_sqm, fallback_max_days, deposit_pct. A bare area or days column will eventually be misread.

  3. Every fact-bearing table gets source and last_verified. Regulations, rates and prices all go stale. A row without provenance cannot be trusted by the MCP server and should not be inserted.

  4. Scope by jurisdiction where it varies. Add state (and lga if the rule operates at council level) rather than storing a national average that is wrong everywhere.

  5. Distinguish NULL from zero and from false. NULL means not applicable or not yet researched; encode "no permit needed" as default_permit_required = false, not as a missing row.

  6. Add the DDL to MODULE_TABLES in setup_db.py so a fresh clone builds the whole schema in one command. Keep the DDL CREATE TABLE IF NOT EXISTS.

  7. One seed script per module subset, named for what it loads (insert_rules.py, later insert_lender_policies.py). Make it idempotent by keying on the table's natural key so re-running never duplicates data.

  8. Import connections from db.py. Do not build a connection string or read os.environ anywhere else.

  9. Add tests with the module, not after it. At minimum: the constraints reject what they exist for, the seed is idempotent, and every NULL the module can produce resolves to the right one of the three reasons. Assert guardrails rather than figures, so the suite survives research.

  10. Give every table a natural-key UNIQUE constraint and register it in MODULE_CONSTRAINTS in setup_db.py. Seed scripts then use ON CONFLICT (...) DO UPDATE, which is atomic and safe to re-run. Enforcing the key in the database rather than in Python matters because the MCP server, bulk imports and manual SQL all write to these tables too, and none of them go through the seed scripts.

Reading a NULL

A blank cell is never self-explanatory, so two columns carry the reason:

meaning

how to read a NULL

column named in unresearched_fields

nobody has looked it up

unknown — never present as fact

data_status = 'no_rule_at_this_level'

the jurisdiction legislates elsewhere

look one level down (QLD → councils)

otherwise, data_status = 'verified'

researched, genuinely absent

not applicable

data_status is one of verified, partial (some fields still in unresearched_fields) or no_rule_at_this_level. The distinction matters most to the MCP server: telling a Queensland user "no permit required" because a column was NULL would be a fabricated answer, and this is what stops it.

Day counts

fast_track_days and fallback_max_days are meaningless without *_days_basis, which is business or calendar. NSW, VIC, WA and TAS count calendar days; SA, QLD and ACT count business days. 35 business days is roughly 49 calendar days, so mixing them silently breaks any duration arithmetic. A CHECK constraint refuses a day count whose basis is missing.

Where a jurisdiction publishes two periods, the longer one is stored and the shorter is noted in source — better to under-promise on a timeline. NT stores no figure at all because the Development Consent Authority decides at monthly meetings rather than within a set period.

Where a figure is assembled from published components rather than quoted whole, source shows the arithmetic so a reader can audit it. South Australia is the example: its 12 and 55 business days are each a sum of a verification period, an assessment period, and a decision-issuing period, plus an extension. Both figures replaced an earlier single value of 25 that had been attached to the wrong pathway — 25 is the performance assessed base, not the deemed-to-satisfy period. Attaching a real number to the wrong pathway is the same class of error as inventing one, and it is harder to spot.

Known schema gaps

Recorded rather than fixed, so the next person does not mistake them for oversights. Each needs a schema change, not just more rows:

  • No zone dimension. Victoria's exemption is written per zone (RGZ, GRZ, NRZ, plus rural zones), and the 300 sqm lot threshold applies to those residential zones specifically. WA, SA, TAS and NT all set minimum lot size and sometimes maximum floor area per zone too, which is why those cells are blank. Moreton Bay is currently modelled with one row per lot-size band, which works but would not survive a council that varies by zone and by band.

  • "Researched, but no published figure" has no encoding. NT's missing decision period is recorded as verified with an explanation in source, because it is neither unresearched nor truly inapplicable. If this case recurs it deserves its own data_status value.

  • One row cannot express a threshold split. Victoria is really two rules: over 300 sqm the permit exemption applies; at or under 300 sqm a permit is always required and Clause 54 / VicSmart applies (Amendment VC282, in operation 8 September 2025). This should become a second row with its own rule_type once the sub-300 sqm pathway is researched.

  • Pending changes are recorded only as prose. Tasmania's SPP amendment 01/2026 would raise 60 sqm to 90 sqm but is not gazetted; that fact currently lives in a sentence inside source. Anything that needs to answer "what is changing soon" needs real columns.

  • Council coverage is partial, and absence is ambiguous. The convention is that a missing council row means the state rule applies — but for a council nobody has researched, it equally means "unknown". The MCP server says so in words on every response; the schema cannot yet distinguish the two.

  • Only negative conditions are modelled. excluding_conditions captures what disqualifies a site. Victoria also imposes positive conditions — no more than one existing dwelling on the lot, only one small second dwelling, and no reticulated natural gas connection — which have nowhere to live yet.

  • One row cannot express a threshold split. Victoria is really two rules: over 300 sqm the permit exemption applies; at or under 300 sqm a permit is always required and Clause 54 / VicSmart applies (Amendment VC282, in operation 8 September 2025). This should become a second row with its own rule_type once the sub-300 sqm pathway is researched.

Available Tools

3 tools
check_data_freshnessA

List rules that have not been re-verified recently.

Planning law changes often - Victoria alone saw five amendments touching
secondary dwellings between 2023 and 2025. Call this before relying on a
rule for a decision, and warn the user about anything flagged stale.

Args:
    max_age_months: Rules older than this many months are reported as stale.
ParametersJSON Schema
NameRequiredDescriptionDefault
max_age_monthsNo

TDQS

A4.2/5.0
Behavior3/5

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

No annotations are present, so the description carries the disclosure burden. It implies read-only operation by listing rules and explains the purpose of staleness, but it does not describe the return format or whether any side effects exist. This is adequate but not fully transparent.

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

Conciseness4/5

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

The description is front-loaded with the core purpose and includes a structured Args section. The additional planning law context is useful but slightly verbose; it earns its place by explaining why freshness matters.

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

Completeness4/5

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

For a simple tool with one optional parameter and no output schema, the description covers purpose, usage, and parameter semantics. It does not describe the return format, but the tool's behavior is straightforward enough that this is not a critical gap.

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

Parameters5/5

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

Despite the context signal indicating 0% schema coverage, the description's Args section explicitly defines max_age_months and its meaning ('Rules older than this many months are reported as stale'). This adds significant value beyond the bare schema.

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

Purpose5/5

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

The description states a specific action ('List rules that have not been re-verified recently') with a clear resource (rules) and condition (staleness). This clearly distinguishes it from sibling tools like get_granny_flat_rules and compare_states.

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

Usage Guidelines4/5

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

Provides explicit guidance on when to call it ('Call this before relying on a rule for a decision') and what to do with results ('warn the user'). It does not name alternatives but clearly indicates the appropriate context.

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

compare_statesA

Compare granny flat rules across all eight states and territories.

Use this for questions like "which state is easiest to build in" or "where can I build the largest granny flat". Comparable figures only - a state whose value is unknown is listed separately rather than omitted, so it is never silently dropped from a ranking.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.2/5.0
Behavior3/5

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

With no annotations provided, the description carries the full burden of behavioral disclosure. It does reveal an important trait: 'a state whose value is unknown is listed separately rather than omitted, so it is never silently dropped from a ranking'. This is valuable context about missing data handling. However, it does not explicitly state that the tool is read-only, nor does it describe the return format (e.g., whether it returns a table or list, ordering, etc.), leaving some transparency gaps.

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

Conciseness5/5

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

The description is highly concise: two sentences with no filler. The first sentence states purpose, and the second provides usage examples plus a critical edge-case behavior. It is front-loaded with the primary action and avoids redundancy.

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

Completeness4/5

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

For a simple parameterless tool, the description is largely complete: it states what is compared, across which scope, and includes usage examples. It also addresses the important unknown-value handling. However, since there is no output schema, the description could have explicitly described the return value structure (e.g., a ranked list or table). It implies output via 'listed separately' but does not fully specify it, leaving a minor completeness gap.

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

Parameters4/5

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

The tool has zero parameters, and the schema description coverage is vacuously 100%. Since there are no parameters to explain, the baseline for parameter semantics is 4. The description correctly does not need to add parameter details; it focuses on the tool's behavior.

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

Purpose5/5

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

The description clearly states the tool's function with a specific verb ('Compare') and resource ('granny flat rules'), and explicitly scopes it to 'all eight states and territories'. This distinguishes it from sibling tools like get_granny_flat_rules, which likely targets individual states, and check_data_freshness.

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

Usage Guidelines4/5

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

The description gives concrete example questions ('which state is easiest to build in', 'where can I build the largest granny flat') that illustrate when to use this tool. It clearly implies it's for cross-state comparisons, but it does not explicitly mention alternative tools or state when not to use it. This is clear context without exclusions.

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

get_granny_flat_rulesA

Get granny flat / secondary dwelling approval rules for an Australian state.

Returns the state-level rule plus any council-level rules on record. In
Queensland the state sets no standard at all, so the council rules are the
only real figures - the state row exists to say so.

Args:
    state: State or territory code, e.g. NSW, VIC, QLD, WA, SA, TAS, ACT, NT.
    lga: Optional council name filter, e.g. "Moreton Bay". Partial matches work.
ParametersJSON Schema
NameRequiredDescriptionDefault
lgaNo
stateYes

TDQS

A4.3/5.0
Behavior4/5

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

With no annotations, the description carries the full burden. It discloses the Queensland exception (the state row exists only to say no standard) and that lga filters support partial matches. These are meaningful behavioral traits beyond the basic purpose.

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

Conciseness5/5

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

The description is three short paragraphs, front-loaded with the primary purpose, then return behavior, then arguments. Every sentence earns its place with useful information and no redundancy.

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

Completeness4/5

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

No output schema exists, but the description explains return content and filtering behavior, including the Queensland nuance. It lacks error/edge-case details, but for a simple lookup tool with two params, it is fairly complete.

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

Parameters5/5

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

Schema description coverage is 0%, but the Args section fully compensates: state codes are listed with examples, lga is described as optional with partial-match behavior, and concrete examples are given. This adds significant meaning beyond the raw schema.

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

Purpose5/5

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

The description opens with a specific verb and resource: 'Get granny flat / secondary dwelling approval rules for an Australian state.' It clarifies the return includes state-level and council-level rules, clearly distinguishing it from sibling tools about data freshness and state comparison.

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

Usage Guidelines3/5

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

The description implies use when approval rules for a state are needed, and provides optional council filtering, but it does not explicitly contrast with siblings or state when not to use. The context is clear but alternatives are not mentioned.

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

TDQS

A4.4/5.0
Disambiguation5/5

Each tool serves a distinct purpose: checking data freshness, fetching rules for a specific state, and comparing across states. There is no overlap or ambiguity in what they do.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern (check_data_freshness, get_granny_flat_rules, compare_states). The naming style is uniform and predictable.

Tool Count5/5

With 3 tools, the set is well-scoped for a niche domain like Australian granny flat regulations. Each tool is necessary and focused, fitting within the ideal 3-15 range.

Completeness5/5

The domain is fully covered: fetching specific rules, comparing all states, and ensuring data freshness. There are no obvious gaps for a read-only regulatory lookup service.

Maintenance

ActivityMaintained
ResponsivenessSyncing

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    B
    maintenance
    Provides comprehensive Australian planning property reports, including zoning, overlays, land size, and utility information for AI assistants. This high-performance MCP server is built for Cloudflare Workers and enables real-time property data retrieval through an HTTP-based interface.
  • A
    license
    A
    quality
    A
    maintenance
    MCP server for Australia's Federal Register of Legislation. Enables searching and fetching Commonwealth Acts with verifiable citations.
    3
    Apache 2.0
  • F
    license
    Not graded
    quality
    C
    maintenance
    This MCP server integrates South Korea's national law information, building registers from MOLIT, and KOSIS statistics for housing development research. It enables searching laws, retrieving building details, and accessing statistical data through natural language.
  • A
    license
    A
    quality
    B
    maintenance
    MCP server for HUD housing data, enabling Fair Market Rent lookups, Section 8 income limits, and ZIP-to-county crosswalk mapping for affordable housing assessments.
    6
    89
    MIT

Latest Blog Posts

MCP directory API

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

curl -X GET 'https://glama.ai/api/mcp/v1/servers/HMEI83/Moduhaus-MCP'

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