Skip to content

CLI Configuration

MD-Models includes a CLI (mdmodels) for running your model as an API or MCP server from one TOML configuration file. This is useful when you want a repeatable deployment setup, keep infrastructure settings in version control, or switch between local development and production without changing Python code.

Use the CLI when you want to:

  • start a REST API quickly from an existing markdown model,
  • expose the same data model as MCP tools for AI clients,
  • manage SQL schema migrations from the same config file,
  • keep model path, database settings, and integration settings in one shared config,
  • run the same setup consistently across local machines, CI, and servers.
  • point model.repo at a GitHub repository so the markdown schema is loaded from raw.githubusercontent.com while SQL and integration settings stay in your TOML.

In short: if your workflow is “configure once, run anywhere”, the CLI is the most direct path.

Another advantage is alignment across teams: data engineers, backend developers, and AI application developers can all use the same project config and run mode-specific commands without maintaining different setup scripts.

  1. Initialize database tables

    Terminal window
    mdmodels init --config config.toml

    By default this creates SQL tables from your model. Use --create-tables false if you only want to validate config and connection setup.

  2. Create a migration revision (Alembic)

    Terminal window
    mdmodels migrate revision --config config.toml --message "describe schema change"

    This autogenerates a migration by comparing the current database schema with metadata rebuilt from your markdown model + config.toml.

  3. Apply migrations

    Terminal window
    mdmodels migrate upgrade --config config.toml head

    Use mdmodels migrate check --config config.toml in CI to fail when model/config schema changes are missing migrations.

  4. Run REST API

    Terminal window
    mdmodels rest --config config.toml

    Optional flags include --host, --port, --name, --graphql, and --env.

  5. Run MCP Server

    Terminal window
    mdmodels mcp --config config.toml --transport stdio

    Supported transports are stdio, sse, and streamable-http. For non-stdio transports, you can also set --host and --port.

  6. Install MCP in Claude Desktop

    Terminal window
    mdmodels install claude-desktop --config config.toml --name mdmodels --project .

    This writes an MCP server entry to Claude Desktop config and runs it via uv --project <project> run mdmodels mcp --config <config>.

1) Initialize storage, then build and test your API locally

Section titled “1) Initialize storage, then build and test your API locally”

This is the fastest way to move from markdown schema to a working HTTP API.

Start by creating tables:

Terminal window
mdmodels init --config config.toml

Then run the API:

Terminal window
mdmodels rest --config config.toml --host 127.0.0.1 --port 8800

Then validate:

  • Open http://127.0.0.1:8800/docs and test generated endpoints.
  • Confirm your expected models appear as routes.
  • Verify create/list/retrieve behavior for at least one model.

During iteration, update your markdown model and rerun the command to verify endpoint shape and validation behavior.

For production workflows, prefer migrations (mdmodels migrate ...) over repeatedly calling init on existing databases.

If you also need GraphQL in the same app for frontend teams or analytical clients:

Terminal window
mdmodels rest --config config.toml --graphql

This keeps REST and GraphQL on one runtime so both consumers share the same data and schema evolution.

Use this when your primary consumer is an assistant or agent.

Run the MCP server in stdio mode for desktop clients:

Terminal window
mdmodels mcp --config config.toml --transport stdio

For Claude Desktop, register it once:

Terminal window
mdmodels install claude-desktop --config config.toml --name mdmodels --project .

Typical flow:

  • Configure a small set of MCP tools under [mcp.tools.<ModelName>].
  • Enable allow_create only where write access is appropriate.
  • Keep descriptive description text so tool intent is clear to the assistant.

This workflow is especially effective for internal copilots that need structured access to experiments, inventory, metadata, or other model-backed records.

Use this when MCP clients connect over a network instead of local stdio.

For networked deployments, use sse or streamable-http:

Terminal window
mdmodels mcp --config config.toml --transport streamable-http --host 0.0.0.0 --port 7000

Recommended rollout pattern:

  • Start locally with stdio to verify tool behavior.
  • Move to streamable-http for shared environments.
  • Add environment-based secrets via --env and infrastructure-level access controls.

For most modern deployments, streamable-http is a good default because it integrates cleanly with standard HTTP infrastructure.

Use this when your markdown model or SQL table config changes over time and you need reproducible schema history.

Create a migration:

Terminal window
mdmodels migrate revision --config config.toml --message "add experiment status"

Apply migrations:

Terminal window
mdmodels migrate upgrade --config config.toml head

Validate migration drift (recommended in CI):

Terminal window
mdmodels migrate check --config config.toml

Downgrade if needed:

Terminal window
mdmodels migrate downgrade --config config.toml <revision>

Migration metadata is generated from your existing model and sql sections. No separate migration config section is required.

Use mdmodels rest when your consumers are web/mobile/backend clients and you want HTTP endpoints with OpenAPI docs. Add --graphql when your consumers need flexible field selection and nested queries in one request.

Use mdmodels mcp when your consumers are AI agents or assistant clients that should interact with your model through tools. This is usually the best choice for assistant workflows in Claude Desktop, Cursor, or custom MCP clients.

It is common to run both in the same project:

  • REST/GraphQL for application integration,
  • MCP for assistant automation and internal productivity workflows.

The root config object contains these sections:

  • model: source markdown model path.
  • sql: database connection and per-table SQL behavior.
  • rest: endpoint exposure per model.
  • mcp: MCP tool exposure per model.

The migration commands also use this same model + sql configuration to generate Alembic metadata.

This separation keeps concerns clear: the sql section defines storage and table behavior, while rest and mcp define how that data is exposed to external consumers.

Minimal example:

[model]
path = "specifications/model.md"
[sql]
type = "postgres"
host = "localhost"
port = 5432
database = "mydb"
username = "postgres"
password = "postgres"
  • model.path (Path, required): path to your markdown model file.
  • model.repo (string, optional): GitHub repository as owner/repo. When set, the model is fetched from GitHub’s raw content URLs (raw.githubusercontent.com) instead of the local filesystem.
  • model.branch (string, optional): branch to read from. Do not set model.tag in the same file when using a branch.
  • model.tag (string, optional): tag to read from. Mutually exclusive with model.branch. If neither branch nor tag is set but repo is set, the default ref is main.

When only model.path is used (no model.repo), relative model.path values are resolved relative to the config file location, which keeps projects portable across environments.

When model.repo is set, model.path must be the path to the markdown file inside the repository (for example specifications/model.md). It is not resolved against the config file directory on disk; it is passed through as the file path in the repo when building the raw GitHub URL. Use forward-slash style paths as in the repo tree.

Pin a published schema in CI or on a server without copying the markdown into your deploy artifact:

[model]
repo = "my-org/my-specs"
branch = "main"
path = "models/experiment.md"
[sql]
type = "sqlite"
database = "app.db"
# ... rest of config (same as local usage)

To pin an immutable release, use tag instead of branch:

[model]
repo = "my-org/my-specs"
tag = "v1.0.0"
path = "models/experiment.md"

All CLI commands that load config (init, rest, mcp, and install via the same config) will use the remote model when repo is present. You still keep sql, rest, and mcp sections locally—only what the markdown schema defines is pulled from GitHub.

Requirements: The repository (or the chosen branch/tag) must be public, or GitHub must allow unauthenticated raw access for your use case. Private repos typically need a token or a vendored copy of the schema; the CLI fetches via HTTPS without git clone. The model file must be valid MD-Models markdown, as with a local file.

Global SQL fields:

  • sql.type (postgres | pgvector | sqlite, required)
  • sql.database (string, required)
  • sql.host (string, optional)
  • sql.port (int, optional)
  • sql.username (string, optional)
  • sql.password (string, optional)

For local development, sqlite is often the fastest way to get started. For production APIs and vector search workloads, postgres or pgvector are typically preferred.

Table-level settings are defined under [sql.tables.<TableName>]:

  • primary_key (string, optional)
  • indexed_columns (string[], default [])
  • deduplicate_on (string[], default [])
  • conflict_policy (error | upsert | ignore, default error)
  • mutability_policy (mutable | append_only, default mutable)

These table options are useful for lifecycle control. For example, use append_only for event-like records, or upsert with deduplicate_on for idempotent ingest pipelines.

Embedding settings can be added under [sql.tables.<TableName>.embedding]:

  • column (string, required when embedding is used)
  • model (string, required)
  • provider (openai | huggingface | fastembed, required)
  • dimension (int, optional)

Provider-specific subsections:

  • [sql.tables.<TableName>.embedding.openai]

  • api_key_env (string, default OPENAI_API_KEY)

  • base_url (string, optional)

  • [sql.tables.<TableName>.embedding.huggingface]

  • device (string, optional)

  • batch_size (int, default 32)

  • normalize_embeddings (bool, default true)

  • trust_remote_code (bool, default false)

  • [sql.tables.<TableName>.embedding.fastembed]

  • cache_dir (string, optional)

  • batch_size (int, optional)

Configure allowed operations per model in [rest.endpoints]:

[rest.endpoints]
Experiment = ["create", "list", "retrieve", "update", "delete", "search", "vectorsearch"]

If a model is omitted in rest.endpoints, no REST endpoints are generated for that model.

This makes endpoint exposure explicit and predictable: you only expose models you list.

Allowed values are:

  • create
  • list
  • retrieve
  • update
  • delete
  • search
  • vectorsearch

Configure MCP tool behavior per model in [mcp.tools.<ModelName>]:

[mcp.tools.Experiment]
description = "Upsert an experiment entry."
allow_create = true

Fields:

  • description (string, optional)
  • allow_create (bool, default false) - enables Upsert_<ModelName> for that model.

This lets you expose only the MCP tools you want. A common pattern is enabling allow_create only for selected models and keeping others query-only.

Use --env to load environment variables from a file before startup:

Terminal window
mdmodels rest --config config.toml --env .env
Terminal window
mdmodels mcp --config config.toml --env .env

This is especially useful for credentials and provider keys. Keeping sensitive values in environment variables instead of TOML files makes deployments safer and easier to rotate.

  • mdmodels init creates tables by default (--create-tables true).
  • For stricter production workflows, prefer mdmodels migrate revision/upgrade/check and use mdmodels init --create-tables false when you only want startup checks.
  • Use explicit --host and --port values in infrastructure environments to avoid accidental defaults for rest and non-stdio MCP transports.
  • If the model file is not found, check that model.path is correct relative to config.toml.
  • If database connection fails, verify sql.type and connection values (host, port, database, credentials).
  • If MCP tools are missing, confirm the model names under [mcp.tools.<ModelName>] match your generated model names.
  • If vector search is not available, check table embedding settings under [sql.tables.<TableName>.embedding].
  • If migration commands fail, verify alembic.ini and migrations/env.py exist in the project root and that your --config path points to the expected TOML file.
[model]
path = "specifications/strenda.md"
[sql]
type = "pgvector"
host = "localhost"
port = 5432
database = "postgres"
[sql.tables.StrendaBiocatalysis]
embedding.column = "description"
embedding.provider = "openai"
embedding.model = "text-embedding-3-small"
[rest.endpoints]
StrendaBiocatalysis = ["create", "list", "retrieve", "search", "vectorsearch"]
[mcp.tools.StrendaBiocatalysis]
description = "Upsert a Strenda-biocatalysis project"
allow_create = true