Skip to content
IFC-RS
Viewer Русский
Project Wiki en

MCP — technology preview

MCP is not part of the stable Core 0.3 contract. ifc-mcp contains transport-independent semantic read-only queries; ifc-mcp-server is a local JSON-RPC/MCP stdio adapter. Neither contains an LLM client or credentials. MCP transport is stdio; an optional loopback HTTP listener serves read-only Viewer sessions.

Available server tools

Semantic Stages 1–2 use ifc-mcp-server.dto.v3:

  • open_model — open an IFC file, identify its pinned official schema, and build reusable indexes once;
  • close_model — idempotently release source bytes, indexes, and diagnostics;
  • model_info — IFC schema, revision, byte/entity counts, bounded spatial and unit-assignment summaries, and diagnostic counts;
  • model_diagnostics — a bounded diagnostics page filtered by severity/code;
  • search_entities — search by IFC type/subtypes, name, GlobalId, and spatial container;
  • get_spatial_tree — a page of spatial nodes preserving parent relationships;
  • get_elements_in_container — direct or recursive container contents;
  • get_entity — a semantic entity card;
  • get_entity_raw — the separate original STEP record.

The previous get_properties and get_relations tools remain until later stages, together with the IDS tools. model_summary and find_entities are no longer advertised; the previous raw get_entity behavior moved to get_entity_raw.

Diagnostics are bounded while they are collected: completed passes retain exact counts, while exhausted bounded validation work marks the summary incomplete; materialized details never exceed the server limit. The diagnostics_truncated marker survives severity/code filters. Name is resolved from EXPRESS metadata, and GlobalId is restricted to IfcRoot descendants. Before allocating an owned string, semantic projections enforce the official 22-byte IfcGloballyUniqueId bound; an oversized malformed value remains available in raw STEP. casefold_exact/contains use full Unicode case folding. get_entity decodes only Name, Description, and PredefinedType under its query budget/deadline. Unknown IFC types and malformed Name values return typed errors. Raw STEP source ranges and encoded lossy/JSON size are checked before copying the full string; non-UTF-8 bytes remain available through a lossy UTF-8 projection with replacement characters. model_info summary lists stop after one probe item beyond the limit and share the request entity budget/deadline; a schema without IfcFacility skips that model scan. Scanner DiagnosticsLimit always marks the diagnostic total as a lower bound and preserves the truncation marker. close_model checks its response budget before releasing the model. Successful close responses and open_model/close_model errors are explicit root JSON Schema variants; lifecycle errors carry stable diagnostic codes without a fabricated model revision. Full validation without query indexes builds one compact type lookup instead of repeating a linear entity scan for every IFC reference. Stage 1 retains a bounded Express ID/type/GlobalId/reference/Pset-Qto profile so the preserved get_properties and get_relations tools remain correct. max_index_bytes bounds indexes; the aggregate registry budget also charges source capacity, scanner/type metadata, the complete EXPRESS AST, model caches, diagnostics, and the session allocation. If any mandatory index or the complete retained session does not fit, open fails rather than silently dropping semantics; rollback and close release the full charge. A malformed IFC type is bounded before ownership and projected as <invalid_ifc_type> while raw STEP stays inspectable. Model-bound string prechecks use the DTO v3 error envelope. max_response_entities caps the combined model_info references and requested search page.

Request contract

open_model accepts schema + path + context; close_model accepts schema + model_id + context. Other model-bound calls accept:

{
  "schema": "ifc-mcp-server.dto.v3",
  "model_ref": {
    "model_id": 1,
    "expected_revision": 0
  },
  "arguments": {},
  "context": {
    "request_id": "request-1",
    "budget": {
      "max_entities_scanned": 100000,
      "max_values_decoded": 100000,
      "max_relation_expansions": 100000,
      "max_response_bytes": 1048576,
      "soft_timeout_ms": 5000
    }
  }
}

Search returns at most 1,000 items per page. Its cursor is bound to the tool, model, revision, and complete normalized request. Spatial container, storey_id, and space_id filters are supported. Material, classification, and property filters fail closed until Stage 3 instead of being silently ignored.

Defaults remain 512 MiB per model, 8 open models, Express ID up to 10,000,000, 4 KiB per string argument, 10,000 retained diagnostics/IDs, and a 1 MiB input frame. Each response is also constrained by the request budget.

Run

cargo build --release -p ifc-mcp-server
./target/release/ifc-mcp-server

Client configuration:

{
  "mcpServers": {
    "ifccore": {
      "command": "/absolute/path/target/release/ifc-mcp-server",
      "args": []
    }
  }
}

The process can read any file allowed by its OS account. Apply OS-level permissions and sandboxing. See the preview specification.

Semantic Stage 3

Adds get_entity_relations, get_materials, get_quantities, and v3 forms of get_property/get_properties. Legacy forms and the IDS family are preserved. Values retain IFC datatype, units, independent origin/ownership, and exact association paths; same-name instance/type values are listed separately. Search supports material/classification/property predicates before pagination, with cumulative budgets and filter_coverage (material → classification → properties). get_quantities never substitutes geometry for Qto.

The preview supports scalar attributes, IfcPropertySingleValue, complex containers, and simple Qto. Other property/aggregate kinds, unknown relation kinds, and unsupported units fail with typed errors. Request contracts, limitations, and acceptance tests: MCP technology preview. Viewer, IFC writing, and deployment are unchanged.

Stage 6: semantic adapters

The first sequential slice adds get_entity_location, get_related_entities, find_by_property, find_by_material, material_summary, quantity_coverage, and summarize_entities over the shared Stage 1–4 engines. All use model/revision- aware v3 DTOs and remain read-only. These adapters do not establish completion of geometry, temporary Viewer sessions, or the Stage 5 preview release. See the canonical plan for current limitations and acceptance evidence.

Stage 6: geometry queries

get_bounding_box, get_geometry_metrics and query_elements_in_box use ifc-geometry, never Qto fallback. computed_geometry is explicit; ifc_model_world includes placements but is not a georeferenced CRS. Length units come from project UnitsInContext; missing/ambiguous/unsupported units fail. The preview caps are 50,000 records, 4 MiB source range, 1,000 products, 200,000 triangles, 64 MiB geometry and 128 expanded mesh objects. Callers may only lower them. Full-model preflight conservatively reserves source bytes × product count against decode/relation work; large models can be rejected even for one selected product. Synchronous extraction is not cooperatively cancellable; soft deadlines are checked around it, with engine hard limits. Box queries intersect AABBs, not exact solids. Metrics are tessellated values; unverified topology yields an explicit unavailable reason, and multiple meshes are not reported as a union volume. Existing internal workspace dependency ifc-geometry is reused; no new external package or stable facade API change. Acceptance uses three official schemas and a real stdio two-box fixture. Local Viewer sessions are implemented as a separate opt-in capability below.

Stage 6: local Viewer sessions — implemented

create_viewer_session, update_viewer_selection, and close_viewer_session use the v3 model/revision envelope. The backend is disabled by default (viewer_disabled); only the host can enable it with --enable-local-viewer. MCP remains stdio. A separate read-only HTTP listener binds to 127.0.0.1 on an ephemeral port. Browser and MCP server must run on the same machine; a remote VM loopback URL is not a public link. No CDN, hosted upload, source IFC, filesystem path, or write operation is exposed.

Create accepts either entity_ids (at most 16) or the shared filter, plus selection, ttl_seconds (1–900, default 300), and geometry limits. Larger selections and unsupported geometry fail rather than truncate. It returns viewer_session_id, url, model_id, model_revision, ttl_seconds. Update takes the handle and selection; close takes the handle. Revision is checked before semantic argument decoding.

The URL carries a random 256-bit bearer capability in its fragment; the browser removes it from the address bar and uses Authorization. Host/Origin/Fetch-Site checks, CSP, no-store and no-referrer protect the browser boundary. Any local client possessing the token can read that scene: this is not OS-user identity or multi-tenant sharing. The owning actor boundary is the stdio server instance.

Limits: 4 sessions, 8 MiB serialized scene, 16 MiB total JSON payloads. Typed meshes and bounded temporary serialization/HTTP copies are additional; JSON quotas are not total RSS limits. No session IFC copies or temp files are created. Timer expiry works without requests. Close and close_model revoke future requests and release retained payload; an in-flight response may finish. A lifecycle lock prevents create-vs-close races. Browser expiry/revocation/ network failure clears canvas and GPU buffers.

Acceptance uses security/lifecycle tests, real stdio, and scripts/test-mcp-viewer-session.mjs against the actual Rust server and IFC: 2 meshes/24 triangles, selection 2 → 3, orbit, close, independent TTL, no JS errors. Server-only getrandom 0.3.4 (MIT OR Apache-2.0, already in Cargo.lock) supplies portable OS cryptographic entropy absent from std. It runs only at session creation, not in transport-free MCP or the stable WASM facade. Size/build impact is not quantified. Hosted service, write tools and Stage 5 preview release are not claimed complete by Stage 6.