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. Only City of Moreton Bay is fully verified — its limit steps with lot area (45 sqm on 450–800 sqm lots, 55 sqm above 800 sqm, 100 sqm in rural zones), which is why it holds three rows. Brisbane, Gold Coast and Logan have confirmed floor-area limits but unconfirmed approval pathways, recorded in unresearched_fields.

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.

Planned modules

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

  • Cost — build cost ranges by size and specification, regional differentials, site cost drivers.

  • 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 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.

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

check_data_freshness(max_age_months)

"Is this still current?" — flags rules not re-verified recently

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.

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. 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.

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.

F
license - not found
-
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

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

Related MCP Servers

  • F
    license
    -
    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.
    2
    Apache 2.0
  • F
    license
    -
    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
    MIT

View all related MCP servers

Related MCP Connectors

  • This MCP server provides seamless access to Malaysia's government open data, including datasets, w…

  • MCP server giving Claude AI access to 22+ NYC public-record databases for real estate due diligence

  • MCP server for generating rough-draft project plans from natural-language prompts.

View all MCP Connectors

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