Getting Started and Migration Guide
Use this guide to install Modelable, validate a first workspace, consume its artifacts from another project, or migrate an existing contract format.
Quick Start
Modelable requires Python 3.14 or newer. Install the command-line tool with
uv:
uv tool install modelable
modelable --version
Inside a project, add it as a development dependency instead:
uv add --dev modelable
Create or copy a .mdl workspace, then run:
modelable validate ./models
modelable compile ./models --target json-schema --out ./dist/jsonschema
modelable docs ./models --out ./dist/docs
To build the optional lexical index used by future documentation retrieval, run:
modelable docs-index ./docs --out ./dist/search-index --base-url https://ktjn.github.io/modelable/
This creates deterministic, heading-aware Searchable chunks. It does not call an LLM; retrieval and answer generation are separate features.
The VS Code extension starts modelable lsp. Ensure the command is available
on PATH, or configure the extension's Python path or server command. See
Local chat testing with Ollama for testing
the conversational features without a hosted model.
Generated artifacts are consumer contracts, not the source of truth. Commit
.mdl definitions; regenerate schemas, language bindings, and Markdown in CI
unless a consumer workflow deliberately reviews generated output.
The one exception is registry-ids.lock: although compile writes it, it
must be committed, not regenerated in CI from scratch. It's the durable
record of which small integer id was allocated to each semantic ...
{ registry: true } declaration, and ids are never reassigned or reused —
treat it like a database migration history, not a build artifact.
Before upgrading, read the root changelog for language or artifact compatibility notes.
Status: Approved guidance for adopting Modelable from existing schema and contract formats.
Scope: Practical migration paths from OpenAPI, JSON Schema, Protobuf, SQL DDL, Avro, and existing internal DSLs into
.mdl.
Local Chat Testing with Ollama
Modelable's conversational features (modelable chat, the VS Code @modelable
participant, and the Playground) generate content by calling an LLM provider. No
provider is required to run modelable validate/compile; a provider is only
needed for LLM-assisted edits and natural-language answers. To test locally, point
the LLM configuration at a running Ollama server.
1. Point the Modelable LLM at Ollama
Configure one of the following (they resolve in this order):
- Environment variables:
MODELABLE_LLM_PROVIDER=ollama,MODELABLE_LLM_MODEL=<model>,MODELABLE_LLM_BASE_URL=http://127.0.0.1:11434. - CLI flags:
--provider ollama --model <model> --base-url http://127.0.0.1:11434. - The workspace
ai:block.
Ollama's default local address is http://127.0.0.1:11434. List the installed models
with modelable models before choosing one. If a session starts without a provider,
Modelable prints a notice explaining the limitation and how to configure one.
2. Make VS Code Chat able to run @modelable
@modelable is a native VS Code chat participant. VS Code Chat will not dispatch
any request (including @modelable) unless at least one language model is
available. With none registered, VS Code reports "Language model unavailable" —
this is a VS Code Chat prerequisite, not a Modelable error. (The @modelable
turn itself is generated server-side by the Modelable LSP using the Ollama
configuration above, independent of VS Code's model picker.)
Modern VS Code (1.121+) supports bring-your-own-key local models without a Copilot
login. Add a .vscode/chatLanguageModels.json in your project:
{
"customendpoint": [
{
"name": "Local Ollama",
"vendor": "customendpoint",
"apiType": "chat-completions",
"models": [
{
"id": "llama3.2",
"name": "Llama 3.2 (Ollama)",
"url": "http://127.0.0.1:11434/v1/chat/completions",
"toolCalling": false,
"vision": false,
"maxInputTokens": 8192,
"maxOutputTokens": 4096
}
]
}
]
}
Then set this in VS Code user settings (chat without signing in):
{
"chat.allowAnonymousAccess": true,
"chat.utilityModel": "Default",
"chat.utilitySmallModel": "Default"
}
Pick the Ollama model in Chat's model picker, then use @modelable. Troubleshooting:
- Use
127.0.0.1, notlocalhost, to avoid IPv4/IPv6 binding mismatches. - Keep
toolCallingandvisionfalseunless the model genuinely supports them; a capability mismatch is a common cause of "Language model unavailable" even when the endpoint answerscurl. - Prefer the
customendpoint(OpenAI-compatible) path above; it needs no Copilot login. Alternatively install an extension that registers Ollama as a VS Codevscode.lmprovider (for example the Ollama extension or Continue).
1. Migration Purpose
This guide helps teams introduce Modelable without rewriting every system at once. Migration should start with domain-owned canonical models, then add projections and adapter bindings around existing infrastructure.
The goal is not to mirror every existing table, topic, or API one-for-one. The goal is to identify the canonical domain contracts and make downstream derivation explicit.
2. Migration Principles
- Start with the owning domain, not the consuming system.
- Model canonical entities and events before projections.
- Preserve published contracts as immutable versions.
- Treat incompatible historical changes as new versions.
- Keep adapter-specific details in bindings, not model definitions.
- Preserve or add PII, classification, ownership, and deprecation metadata.
- Validate after each migrated domain before adding cross-domain projections.
3. Source Format Mapping
| Source | Primary Mapping | Notes |
|---|---|---|
| OpenAPI 3.x | Request/response schemas become projections; shared schemas may become entities or value objects | Avoid treating public API shape as canonical unless that API is owned by the domain |
| JSON Schema | Object schemas become entity, event, or value models |
Modelable x-modelable-* extensions preserve version, ownership, classification, PII, keys, and references when present |
| Protobuf | Messages become models; services imply projections or API targets | Preserve field numbers in metadata if needed |
| SQL DDL | Tables become candidate entities; views become candidate projections | Move table/index/storage details to bindings |
| Avro | Records often become event models | Preserve logical types and namespace metadata |
dbt schema.yml / manifest.json |
Models and source tables become draft entities for review | Preserve group/access/ownership metadata manually when dbt metadata is incomplete |
FHIR R4 StructureDefinition |
Direct child elements become draft model fields; repeating elements become arrays; simple extension slices use their nested value[x] type |
Complex FHIR element types may need manual value-model refinement |
| ODCS YAML | Contract schema objects become draft entities | Modelable ODCS customProperties preserve exact type hints, version, PII, owner, classification, keys, and required-field semantics |
| Existing YAML/DSL | Rewrite to .mdl with modelable generate assistance |
Review all generated lineage and governance annotations |
4. Step-by-Step Workflow
Step 1: Inventory
Create a list of source artifacts:
source system
owning team
artifact path
artifact kind
current version
contains PII/restricted data
known consumers
Step 2: Choose Domain Boundaries
Map each artifact to an owning domain. If ownership is unclear, pause migration for that artifact. Modelable requires domain-owned canonical models.
Step 3: Extract Canonical Models
For each domain, create the smallest useful set of canonical models:
domain customer {
owner: "customer-platform"
description: "Customer identity and lifecycle."
entity Customer @ 1 (additive) {
@key customerId: uuid
@pii email?: string
status: enum(active, suspended, deleted)
createdAt: timestamp
}
}
Step 4: Add Projections for Existing Consumers
Map existing views, API responses, topics, or files to projections:
domain billing {
projection BillingCustomer @ 1
from customer.Customer @ 1 as c
{
billingCustomerId <- c.customerId
@pii invoiceEmail <- c.email
isActive = c.status == "active"
}
}
Step 5: Add Bindings Separately
Keep storage and transport details outside canonical models:
binding customer-postgres {
model: customer.Customer @ 1
adapter: postgres
table: "customers"
}
Step 6: Validate and Diff
Run:
modelable validate ./models
modelable lineage billing.BillingCustomer@1 --path ./models
modelable diff customer.Customer@1 customer.Customer@2 --path ./models
5. Format-Specific Guidance
5.1 OpenAPI
- Treat path operations as API surface, not source truth.
- Convert request bodies to
requestprojections. - Convert response bodies to
replyprojections. - Convert shared component schemas to value objects only when they are embedded and not independently owned.
- Preserve operation-level security as projection or binding access policy metadata.
5.2 JSON Schema
- Use
requiredto determine optionality. - Convert
format: uuid,date,date-time, and binary encodings to Modelable scalar types. - Convert
$refto named value objects orref<Domain.Model>depending on ownership. - Add
@classificationmanually when source schemas lack governance metadata.
5.3 Protobuf
- Map packages to candidate domains only when package ownership matches domain ownership.
- Preserve message names as model names where possible.
- Map
repeated Ttoarray<T>. - Map
oneofto an enum discriminator plus optional fields until union types are explicitly supported. - Preserve field numbers in metadata for downstream emitters.
5.4 SQL DDL
- Tables with primary keys usually become entities.
- Append-only audit tables usually become events.
- Lookup tables may become value objects or enums.
- Views should become projections.
- Foreign keys become
ref<Domain.Model>only when they represent domain references, not merely storage joins. - Indexes, partitions, engines, and tablespaces belong in bindings.
5.5 Avro
- Avro records used on topics usually become event models.
- Avro logical types map to Modelable scalar types.
- Union with
nullmaps to optional fields. - Registry subject names should be preserved as metadata or binding configuration, not canonical model names unless they match domain language.
6. AI-Assisted Migration
Use modelable generate to draft .mdl, then review the output:
modelable generate --from ./openapi.yaml --output ./models/customer-api.mdl
modelable generate --from ./dbt/schema.yml --domain customer --output ./models/customer.mdl
modelable generate --from ./dbt/schema.yml --name customers --domain customer --output ./models/customer-source.mdl
modelable generate --from ./fhir/PatientProfile.json --domain clinical --output ./models/patient.mdl
modelable generate --from ./contracts/customer.yml --domain customer --output ./models/customer-contract.mdl
modelable validate ./models/customer-api.mdl
Review checklist:
- Does each model have a clear owning domain?
- Are keys explicit?
- Are versions and change kinds correct?
- Are PII and restricted fields annotated?
- Are API-specific shapes represented as projections rather than canonical models?
- Are adapter details kept in bindings?
7. Common Gaps
| Gap | Resolution |
|---|---|
| No owner for a schema | Do not publish until ownership is assigned |
| No stable key | Use an event or value model, or add a domain-approved identity |
| API response treated as canonical model | Split into source entity and response projection |
| Storage column names in model fields | Move physical names to bindings |
| Missing PII classification | Add annotations before publishing |
| Historical breaking change hidden in one version | Create separate model versions with changeKind: breaking |
8. Acceptance Criteria
- Migrated canonical models have owners, keys where applicable, versions, and change kinds.
- Consumer-specific shapes are represented as projections with explicit lineage.
- Existing adapter details are represented in bindings.
modelable validatesucceeds for the migrated workspace.- Generated JSON Schema and TypeScript artifacts preserve model version metadata.
- Governance annotations are present for PII, restricted, and confidential fields.
9. Dependencies
- Tooling reference for
generate,validate,lineage,diff, LSP, and AI-assisted authoring behavior. - Language reference for ownership, access metadata,
annotations, and
.mdlsyntax. - Architecture for canonical-model and adapter boundaries.