ModuHaus-MCP
This MCP server provides tools to access and analyze Australian granny flat/secondary dwelling regulations and costs across all states and territories.
Look up approval rules: Retrieve state-level planning rules for a given jurisdiction, including maximum floor area, minimum lot size, permit requirements, and approval pathways. Optionally filter by council (LGA) for local overrides.
Compare states: Get a side-by-side comparison of regulations across all eight jurisdictions to easily rank or contrast requirements.
Check data freshness: Identify rules that have not been re-verified within a given number of months, essential given frequent planning law changes.
Estimate costs: Obtain typical build cost ranges and associated site works, and generate a comprehensive budget estimate for a specific granny flat project.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@ModuHaus-MCPCompare granny flat rules in NSW and VIC"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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 causesRows 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 |
| SERIAL PK | |
| VARCHAR(10) |
|
| VARCHAR(50) |
|
| VARCHAR(100) | the specific rule being described |
| NUMERIC | |
| NUMERIC | |
| BOOLEAN | whether approval is needed by default |
| TEXT[] | overlays/hazards that void the pathway |
| VARCHAR(50) | e.g. |
| INTEGER | |
| VARCHAR(50) | e.g. |
| INTEGER | |
| TEXT | the planning instrument |
| 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
NULLanddata_statusisno_rule_at_this_level. The only statewide rule is the 2022 amendment removing occupancy restrictions. Queensland's real numbers live inregulation_lga_granny_flat_rules.default_permit_requiredmeasures whether approval is needed at all, not how fast it is. NSW istruebecause a complying development certificate is still issued; VIC, WA, TAS and NT arefalsebecause 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 × areaestimator 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_basisvalues fits exactly; theincludes/excludesarrays 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_itemswith 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_statusis 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, andcosts.pyrenders it into a sentence a consumer can relay. Regulation never needed it — its source was always the instrument itself.UNIQUEneedsNULLS 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 syncCopy .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 backAll 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 |
| The three-way |
| Every database integrity rule, and that each rejects the value it exists for |
| Setup and seeds are idempotent; coverage and data invariants hold |
| The tools' guardrails, called directly |
| Double-counting, the legality cross-check, and refusing to invent a missing combination |
| 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.pyTool | Answers |
| "Can I build one in X, how big, and do I need approval?" — returns the state rule plus any council rules |
| "Which state is easiest?" — cross-jurisdiction comparison |
| "What does it cost?" — build ranges and the site works added to them, always together |
| "What will my project cost?" — composes both modules into one figure |
| "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_sitequote inflates it by tens of thousands; omitting them fromturnkey_excl_siteorbase_moduleunderstates it by the same.BASIS_NEEDS_SITE_WORKSdecides, 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_legalityverdict checked against the state's cap —within_the_cap,over_the_cap, orcannot_check_at_state_levelfor 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 # severalConventions for adding a module
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 aresnake_case, table names plural where the row is one of many (..._rules,..._lenders).Put units in the column name.
max_floor_area_sqm,fallback_max_days,deposit_pct. A bareareaordayscolumn will eventually be misread.Every fact-bearing table gets
sourceandlast_verified. Regulations, rates and prices all go stale. A row without provenance cannot be trusted by the MCP server and should not be inserted.Scope by jurisdiction where it varies. Add
state(andlgaif the rule operates at council level) rather than storing a national average that is wrong everywhere.Distinguish
NULLfrom zero and from false.NULLmeans not applicable or not yet researched; encode "no permit needed" asdefault_permit_required = false, not as a missing row.Add the DDL to
MODULE_TABLESinsetup_db.pyso a fresh clone builds the whole schema in one command. Keep the DDLCREATE TABLE IF NOT EXISTS.One seed script per module subset, named for what it loads (
insert_rules.py, laterinsert_lender_policies.py). Make it idempotent by keying on the table's natural key so re-running never duplicates data.Import connections from
db.py. Do not build a connection string or reados.environanywhere else.Add tests with the module, not after it. At minimum: the constraints reject what they exist for, the seed is idempotent, and every
NULLthe module can produce resolves to the right one of the three reasons. Assert guardrails rather than figures, so the suite survives research.Give every table a natural-key
UNIQUEconstraint and register it inMODULE_CONSTRAINTSinsetup_db.py. Seed scripts then useON 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 | |
column named in | nobody has looked it up | unknown — never present as fact |
| the jurisdiction legislates elsewhere | look one level down (QLD → councils) |
otherwise, | 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
verifiedwith an explanation insource, because it is neither unresearched nor truly inapplicable. If this case recurs it deserves its owndata_statusvalue.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_typeonce 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_conditionscaptures 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_typeonce the sub-300 sqm pathway is researched.
Available Tools
3 toolscheck_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.
| Name | Required | Description | Default |
|---|---|---|---|
| max_age_months | No |
TDQS
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.
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.
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.
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.
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.
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.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
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.
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.
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.
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.
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.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| lga | No | ||
| state | Yes |
TDQS
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.
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.
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.
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.
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.
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
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.
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.
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.
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
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
Agent-native MCP server over 49M+ US public and government records, privacy-first, always current.
Hosted MCP server for live public-data APIs and Skills for AI agents.
Conformance checker for MCP servers. Free, no key, verdicts recomputable and re-measured daily.
Related MCP Servers
- FlicenseNot gradedqualityBmaintenanceProvides 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.
- AlicenseAqualityAmaintenanceMCP server for Australia's Federal Register of Legislation. Enables searching and fetching Commonwealth Acts with verifiable citations.3Apache 2.0
- FlicenseNot gradedqualityCmaintenanceThis 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.
- AlicenseAqualityBmaintenanceMCP server for HUD housing data, enabling Fair Market Rent lookups, Section 8 income limits, and ZIP-to-county crosswalk mapping for affordable housing assessments.689MIT
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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