{
  "generatedAt": "2026-05-03T09:30:00Z",
  "version": "0.1.0",
  "tools": [
    { "name": "model_info",          "category": "Discovery",  "scope": "read",   "description": "Schema, entity counts, units, georeferencing — the at-a-glance summary of a loaded IFC.", "inputSchema": { "type": "object", "properties": { "model_id": { "type": "string" } } } },
    { "name": "model_list",          "category": "Discovery",  "scope": "read",   "description": "List every model loaded in the current MCP session.", "inputSchema": { "type": "object" } },
    { "name": "model_load",          "category": "Discovery",  "scope": "mutate", "description": "Load an additional .ifc from disk into the federated session.", "inputSchema": { "type": "object", "required": ["file_path"], "properties": { "file_path": { "type": "string" }, "model_id": { "type": "string" } } } },
    { "name": "model_unload",        "category": "Discovery",  "scope": "mutate", "description": "Drop a model from the registry; frees memory.", "inputSchema": { "type": "object", "required": ["model_id"], "properties": { "model_id": { "type": "string" } } } },
    { "name": "schema_describe",     "category": "Discovery",  "scope": "read",   "description": "Attributes, parent class, and inheritance for any IfcType — useful before mutating.", "inputSchema": { "type": "object", "required": ["type"], "properties": { "type": { "type": "string" }, "include_inherited": { "type": "boolean" } } } },

    { "name": "query_entities",      "category": "Query",      "scope": "read",   "description": "Type + property filters with pagination. Returns expressId, GlobalId, name, type for each match.", "inputSchema": { "type": "object", "properties": { "type": { "type": "string" }, "limit": { "type": "integer" } } } },
    { "name": "count_entities",      "category": "Query",      "scope": "read",   "description": "Group counts by type, storey, or material — histogram form, not the full set.", "inputSchema": { "type": "object", "properties": { "group_by": { "type": "string", "enum": ["type", "storey", "material"] } } } },
    { "name": "get_entity",          "category": "Query",      "scope": "read",   "description": "Full attributes + property sets for one entity by GlobalId or expressId.", "inputSchema": { "type": "object", "properties": { "global_id": { "type": "string" }, "express_id": { "type": "integer" } } } },
    { "name": "get_entities_bulk",   "category": "Query",      "scope": "read",   "description": "Batched get_entity for up to 200 ids at once.", "inputSchema": { "type": "object", "required": ["global_ids"], "properties": { "global_ids": { "type": "array", "items": { "type": "string" } } } } },
    { "name": "spatial_hierarchy",   "category": "Query",      "scope": "read",   "description": "Project → site → building → storey → space tree, with element counts at each node.", "inputSchema": { "type": "object" } },
    { "name": "containment_chain",   "category": "Query",      "scope": "read",   "description": "Walk up the spatial chain for one entity (storey + parent + grandparent…).", "inputSchema": { "type": "object", "required": ["global_id"] } },
    { "name": "relationships",       "category": "Query",      "scope": "read",   "description": "Voids, fills, groups, connections — every IfcRel touching this entity.", "inputSchema": { "type": "object", "required": ["global_id"] } },
    { "name": "properties_unique",   "category": "Query",      "scope": "read",   "description": "Unique values + counts for one property across a type set (perfect for filter UIs).", "inputSchema": { "type": "object", "required": ["type", "pset", "property"] } },
    { "name": "materials_list",      "category": "Query",      "scope": "read",   "description": "Distinct materials across the model with usage counts.", "inputSchema": { "type": "object" } },
    { "name": "classifications_list","category": "Query",      "scope": "read",   "description": "Distinct classification references (system + identification) and how often each is used.", "inputSchema": { "type": "object" } },
    { "name": "georeferencing",      "category": "Query",      "scope": "read",   "description": "MapConversion, projected CRS, project north, true north — the geo handshake.", "inputSchema": { "type": "object" } },
    { "name": "units",               "category": "Query",      "scope": "read",   "description": "Length unit scale + the unit assignments declared in the file.", "inputSchema": { "type": "object" } },

    { "name": "geometry_bbox",       "category": "Geometry",   "scope": "read",   "description": "Per-entity axis-aligned bounding box (read from quantity sets when available).", "inputSchema": { "type": "object", "required": ["global_id"] } },
    { "name": "geometry_volume",     "category": "Geometry",   "scope": "read",   "description": "Net/gross volume in m³ for a single entity or a type aggregate.", "inputSchema": { "type": "object", "required": ["global_id"] } },
    { "name": "geometry_area",       "category": "Geometry",   "scope": "read",   "description": "Surface area for an entity (front/side/footprint depending on what the IFC carries).", "inputSchema": { "type": "object", "required": ["global_id"] } },

    { "name": "clash_check",         "category": "Clash",      "scope": "read",   "description": "Find clashes in a single model — the DEFAULT for any 'find/run clashes' request. Omit BOTH a and b to check every element vs every other (all clashes in the model). Give a TYPE selector for a to self-clash within a group, or both a and b for a pairwise check (a=\"IfcDuct*\", b=\"IfcWall*\"). Meshes the model in-browser; returns a summary plus the worst clashes by penetration depth.", "inputSchema": { "type": "object", "properties": { "a": { "type": "string", "description": "Type selector for set A. Defaults to \"*\" (all elements). e.g. \"IfcDuct*|IfcPipe*\", \"!IfcSpace\"." }, "b": { "type": "string", "description": "Type selector for set B. OMIT for a self-clash within A (every element vs every other in the group)." }, "mode": { "type": "string", "enum": ["hard", "clearance"] }, "tolerance": { "type": "number", "description": "Touching band (m). Defaults to the engine tolerance." }, "clearance": { "type": "number", "description": "Required gap (m) for mode=\"clearance\"." }, "model_id": { "type": "string", "description": "Optional; defaults to the only loaded model." } } } },
    { "name": "clash_matrix",        "category": "Clash",      "scope": "read",   "description": "Run the standard discipline clash matrix (MEP×STR, HVAC×ARCH, ...) — INTER-discipline pairs ONLY. Returns NOTHING on a single-discipline or architectural model (no cross-discipline pairs exist to test). For a general 'find all clashes' request use clash_check instead; use this only when the user explicitly asks for the discipline matrix. Returns per-rule and per-severity breakdowns plus a sample of the worst clashes.", "inputSchema": { "type": "object", "properties": { "mode": { "type": "string", "enum": ["hard", "clearance"] }, "clearance": { "type": "number", "description": "Required gap (m) applied to every matrix rule when mode=\"clearance\". Without it a clearance matrix reports nothing." }, "model_id": { "type": "string", "description": "Optional; defaults to the only loaded model." } } } },
    { "name": "clash_bcf_export",    "category": "Clash",      "scope": "export", "description": "Turn the last clash run (clash_check / clash_matrix) into a rich .bcfzip: one BCF topic per clash group, each with a framed 3D viewpoint, the clashing elements as components, and severity/status/distance metadata. Runs a default all-vs-all clash_check first if you haven't clashed yet. Use THIS for 'create/export BCF from clashes' — not bcf_topic_create. (Snapshots are omitted: the inline viewer can't render frames headlessly; BCF viewpoints are valid without an image.)", "inputSchema": { "type": "object", "properties": { "group_by": { "type": "string", "enum": ["cluster", "rule", "typePair", "element"], "description": "How clashes collapse into topics. Default \"cluster\" (spatially-near clashes merge into one topic)." }, "cluster_epsilon": { "type": "number", "description": "Cluster radius (m) for group_by=\"cluster\". Default 1.5." }, "status": { "type": "string", "description": "BCF topic status, e.g. \"Open\"." }, "max_topics": { "type": "integer", "description": "Cap the number of topics. Default 1000." }, "file_path": { "type": "string", "description": "Output .bcfzip name." } } } },

    { "name": "model_audit",         "category": "Validation", "scope": "read",   "description": "Out-of-the-box health score + a list of issues (missing GlobalIds, broken refs, orphan entities).", "inputSchema": { "type": "object" } },
    { "name": "ids_validate",        "category": "Validation", "scope": "read",   "description": "Run a buildingSMART IDS spec against the loaded model. Per-spec pass/fail with offending entities.", "inputSchema": { "type": "object", "required": ["ids_path"], "properties": { "ids_path": { "type": "string" } } } },
    { "name": "ids_explain",         "category": "Validation", "scope": "read",   "description": "Parse + summarize an IDS file in plain language — what each spec asks for, in what order.", "inputSchema": { "type": "object", "required": ["ids_path"] } },

    { "name": "entity_set_property", "category": "Mutation",   "scope": "mutate", "description": "Queue a Pset.property write on one entity. Persist later via export_ifc / model_save.", "inputSchema": { "type": "object", "required": ["pset", "name"] } },
    { "name": "entity_delete_property", "category": "Mutation","scope": "mutate", "description": "Queue a property removal from a Pset. Reversible via mutation_undo.", "inputSchema": { "type": "object", "required": ["pset", "name"] } },
    { "name": "entity_set_attribute","category": "Mutation",   "scope": "mutate", "description": "Set Name, Description, ObjectType, or Tag on an entity.", "inputSchema": { "type": "object", "required": ["attribute", "value"] } },
    { "name": "entity_create",       "category": "Mutation",   "scope": "mutate", "description": "Create a new IFC entity with raw STEP attributes and get back its expressId.", "inputSchema": { "type": "object", "required": ["type"] } },
    { "name": "entity_delete",       "category": "Mutation",   "scope": "mutate", "description": "Delete an entity by expressId/GlobalId. Caller is responsible for cascading rels.", "inputSchema": { "type": "object" } },
    { "name": "mutation_batch",      "category": "Mutation",   "scope": "mutate", "description": "Apply N mutation ops in order, returning per-step results.", "inputSchema": { "type": "object", "required": ["operations"] } },
    { "name": "mutation_diff",       "category": "Mutation",   "scope": "read",   "description": "Inspect every queued mutation vs the original parsed state.", "inputSchema": { "type": "object" } },
    { "name": "mutation_undo",       "category": "Mutation",   "scope": "mutate", "description": "Pop the last N pending mutations off the queue.", "inputSchema": { "type": "object", "properties": { "n": { "type": "integer" } } } },
    { "name": "model_save",          "category": "Mutation",   "scope": "mutate", "description": "Write the current model (with pending mutations) back to .ifc.", "inputSchema": { "type": "object", "required": ["file_path"] } },

    { "name": "bcf_topic_list",      "category": "BCF",        "scope": "read",   "description": "List BCF topics in this session, optionally filtered by status.", "inputSchema": { "type": "object" } },
    { "name": "bcf_topic_create",    "category": "BCF",        "scope": "mutate", "description": "Create a topic with title/description/priority and get the GUID for follow-ups.", "inputSchema": { "type": "object", "required": ["title"] } },
    { "name": "bcf_topic_update",    "category": "BCF",        "scope": "mutate", "description": "Update topic fields or append a comment.", "inputSchema": { "type": "object", "required": ["guid"] } },
    { "name": "bcf_topic_close",     "category": "BCF",        "scope": "mutate", "description": "Mark a topic resolved (status=Closed).", "inputSchema": { "type": "object", "required": ["guid"] } },
    { "name": "bcf_viewpoint_create","category": "BCF",        "scope": "mutate", "description": "Attach a selection-based viewpoint (or full viewer state) to a topic.", "inputSchema": { "type": "object", "required": ["guid"] } },
    { "name": "bcf_export",          "category": "BCF",        "scope": "export", "description": "Export the in-memory BCF project as a .bcfzip file.", "inputSchema": { "type": "object", "required": ["file_path"] } },

    { "name": "bsdd_search",         "category": "bSDD",       "scope": "read",   "description": "Full-text search the buildingSMART Data Dictionary for classes by keyword.", "inputSchema": { "type": "object", "required": ["query"] } },
    { "name": "bsdd_class",          "category": "bSDD",       "scope": "read",   "description": "Full bSDD class info for an IFC entity name (definition, parent, properties).", "inputSchema": { "type": "object", "required": ["ifc_type"] } },
    { "name": "bsdd_property_sets",  "category": "bSDD",       "scope": "read",   "description": "Pset_* groups for an IFC type (Pset_WallCommon for IfcWall, etc.).", "inputSchema": { "type": "object", "required": ["ifc_type"] } },
    { "name": "bsdd_match",          "category": "bSDD",       "scope": "read",   "description": "Suggest matching bSDD classes for an entity in the loaded model.", "inputSchema": { "type": "object" } },

    { "name": "model_diff",          "category": "Diff",       "scope": "read",   "description": "Compare two loaded models. Reports added/removed entities by GlobalId and per-type count deltas.", "inputSchema": { "type": "object", "required": ["a", "b"] } },
    { "name": "quantity_diff",       "category": "Diff",       "scope": "read",   "description": "Per-entity-type quantity comparison between two models, optionally grouped by storey.", "inputSchema": { "type": "object", "required": ["a", "b"] } },

    { "name": "export_ifc",          "category": "Export",     "scope": "export", "description": "Write the model (with pending mutations) to .ifc/.ifczip on disk.", "inputSchema": { "type": "object", "required": ["file_path"] } },
    { "name": "export_csv",          "category": "Export",     "scope": "export", "description": "Tabular property/quantity export. Columns may be Pset_X.Property paths.", "inputSchema": { "type": "object" } },
    { "name": "export_json",         "category": "Export",     "scope": "export", "description": "Structured JSON dump of attributes/properties/quantities for a type set.", "inputSchema": { "type": "object" } },
    { "name": "export_glb",          "category": "Export",     "scope": "export", "description": "(v0.2) Geometry export to glTF binary — needs the WASM mesh pipeline.", "inputSchema": { "type": "object" } },
    { "name": "export_ifcx",         "category": "Export",     "scope": "export", "description": "(v0.2) Export to the new IFCx interchange format.", "inputSchema": { "type": "object" } },
    { "name": "export_pdf_report",   "category": "Export",     "scope": "export", "description": "(v0.5) Branded PDF audit report with charts.", "inputSchema": { "type": "object" } },

    { "name": "viewer_ask",          "category": "Viewer",     "scope": "read",   "description": "Suggest wording the agent can use to ask the user for permission to open the viewer.", "inputSchema": { "type": "object" } },
    { "name": "viewer_open",         "category": "Viewer",     "scope": "read",   "description": "Boot the in-process WebGL viewer and return its URL for the user to open.", "inputSchema": { "type": "object" } },
    { "name": "viewer_close",        "category": "Viewer",     "scope": "read",   "description": "Stop the viewer + clear its selection state.", "inputSchema": { "type": "object" } },
    { "name": "viewer_status",       "category": "Viewer",     "scope": "read",   "description": "Report whether the viewer is open, on what port, and the current selection.", "inputSchema": { "type": "object" } },
    { "name": "viewer_colorize",     "category": "Viewer",     "scope": "read",   "description": "Paint a set of entities with a color (hex / rgb / named).", "inputSchema": { "type": "object", "required": ["color"] } },
    { "name": "viewer_isolate",      "category": "Viewer",     "scope": "read",   "description": "Hide everything except the picked set.", "inputSchema": { "type": "object" } },
    { "name": "viewer_hide",         "category": "Viewer",     "scope": "read",   "description": "Hide the picked set; everything else stays.", "inputSchema": { "type": "object" } },
    { "name": "viewer_show",         "category": "Viewer",     "scope": "read",   "description": "Show the picked set (un-hide).", "inputSchema": { "type": "object" } },
    { "name": "viewer_reset",        "category": "Viewer",     "scope": "read",   "description": "Reset visibility + colors to the model defaults.", "inputSchema": { "type": "object" } },
    { "name": "viewer_fly_to",       "category": "Viewer",     "scope": "read",   "description": "Frame the camera on a set of entities or a bbox.", "inputSchema": { "type": "object" } },
    { "name": "viewer_set_section",  "category": "Viewer",     "scope": "read",   "description": "Apply an axis-aligned section plane.", "inputSchema": { "type": "object", "required": ["axis", "position"] } },
    { "name": "viewer_clear_section","category": "Viewer",     "scope": "read",   "description": "Remove the active section plane.", "inputSchema": { "type": "object" } },
    { "name": "viewer_color_by_storey","category": "Viewer",   "scope": "read",   "description": "Apply a per-storey overlay (built-in viewer preset).", "inputSchema": { "type": "object" } },
    { "name": "viewer_color_by_property","category": "Viewer", "scope": "read",   "description": "Color a type set by property value, returns a legend the agent can describe.", "inputSchema": { "type": "object", "required": ["type", "pset", "property"] } },
    { "name": "viewer_get_selection","category": "Viewer",     "scope": "read",   "description": "Return the current selection — type, expressId, GlobalId, name, attributes, materials.", "inputSchema": { "type": "object" } },
    { "name": "viewer_describe_selection","category": "Viewer","scope": "read",   "description": "Kitchen-sink: every section (attributes, properties, quantities, classifications, materials) for the picked entity.", "inputSchema": { "type": "object" } },
    { "name": "viewer_wait_for_selection","category": "Viewer","scope": "read",   "description": "Block until the user picks something in the viewer (or timeout).", "inputSchema": { "type": "object" } }
  ]
}
