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.
When to use the CLI
Section titled “When to use the CLI”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.repoat a GitHub repository so the markdown schema is loaded fromraw.githubusercontent.comwhile 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.
CLI Commands
Section titled “CLI Commands”-
Initialize database tables
Terminal window mdmodels init --config config.tomlBy default this creates SQL tables from your model. Use
--create-tables falseif you only want to validate config and connection setup. -
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. -
Apply migrations
Terminal window mdmodels migrate upgrade --config config.toml headUse
mdmodels migrate check --config config.tomlin CI to fail when model/config schema changes are missing migrations. -
Run REST API
Terminal window mdmodels rest --config config.tomlOptional flags include
--host,--port,--name,--graphql, and--env. -
Run MCP Server
Terminal window mdmodels mcp --config config.toml --transport stdioSupported transports are
stdio,sse, andstreamable-http. For non-stdio transports, you can also set--hostand--port. -
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>.
Typical Workflows
Section titled “Typical Workflows”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:
mdmodels init --config config.tomlThen run the API:
mdmodels rest --config config.toml --host 127.0.0.1 --port 8800Then validate:
- Open
http://127.0.0.1:8800/docsand 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:
mdmodels rest --config config.toml --graphqlThis keeps REST and GraphQL on one runtime so both consumers share the same data and schema evolution.
2) Connect your model to an MCP client
Section titled “2) Connect your model to an MCP client”Use this when your primary consumer is an assistant or agent.
Run the MCP server in stdio mode for desktop clients:
mdmodels mcp --config config.toml --transport stdioFor Claude Desktop, register it once:
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_createonly where write access is appropriate. - Keep descriptive
descriptiontext 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.
3) Deploy over HTTP transport
Section titled “3) Deploy over HTTP transport”Use this when MCP clients connect over a network instead of local stdio.
For networked deployments, use sse or streamable-http:
mdmodels mcp --config config.toml --transport streamable-http --host 0.0.0.0 --port 7000Recommended rollout pattern:
- Start locally with
stdioto verify tool behavior. - Move to
streamable-httpfor shared environments. - Add environment-based secrets via
--envand infrastructure-level access controls.
For most modern deployments, streamable-http is a good default because it integrates cleanly with standard HTTP infrastructure.
4) Govern schema changes with Alembic
Section titled “4) Govern schema changes with Alembic”Use this when your markdown model or SQL table config changes over time and you need reproducible schema history.
Create a migration:
mdmodels migrate revision --config config.toml --message "add experiment status"Apply migrations:
mdmodels migrate upgrade --config config.toml headValidate migration drift (recommended in CI):
mdmodels migrate check --config config.tomlDowngrade if needed:
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.
Choosing the Right Command
Section titled “Choosing the Right Command”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.
Config File Structure
Section titled “Config File Structure”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 = 5432database = "mydb"username = "postgres"password = "postgres"model Section
Section titled “model Section”model.path(Path, required): path to your markdown model file.model.repo(string, optional): GitHub repository asowner/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 setmodel.tagin the same file when using a branch.model.tag(string, optional): tag to read from. Mutually exclusive withmodel.branch. If neitherbranchnortagis set butrepois set, the default ref ismain.
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.
Example: model from GitHub
Section titled “Example: model from GitHub”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.
sql Section
Section titled “sql Section”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, defaulterror)mutability_policy(mutable | append_only, defaultmutable)
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, defaultOPENAI_API_KEY) -
base_url(string, optional) -
[sql.tables.<TableName>.embedding.huggingface] -
device(string, optional) -
batch_size(int, default32) -
normalize_embeddings(bool, defaulttrue) -
trust_remote_code(bool, defaultfalse) -
[sql.tables.<TableName>.embedding.fastembed] -
cache_dir(string, optional) -
batch_size(int, optional)
rest Section
Section titled “rest Section”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:
createlistretrieveupdatedeletesearchvectorsearch
mcp Section
Section titled “mcp Section”Configure MCP tool behavior per model in [mcp.tools.<ModelName>]:
[mcp.tools.Experiment]description = "Upsert an experiment entry."allow_create = trueFields:
description(string, optional)allow_create(bool, defaultfalse) - enablesUpsert_<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.
Environment Variables
Section titled “Environment Variables”Use --env to load environment variables from a file before startup:
mdmodels rest --config config.toml --env .envmdmodels mcp --config config.toml --env .envThis 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.
Operational Notes
Section titled “Operational Notes”mdmodels initcreates tables by default (--create-tables true).- For stricter production workflows, prefer
mdmodels migrate revision/upgrade/checkand usemdmodels init --create-tables falsewhen you only want startup checks. - Use explicit
--hostand--portvalues in infrastructure environments to avoid accidental defaults forrestand non-stdioMCP transports.
Troubleshooting Tips
Section titled “Troubleshooting Tips”- If the model file is not found, check that
model.pathis correct relative toconfig.toml. - If database connection fails, verify
sql.typeand 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.iniandmigrations/env.pyexist in the project root and that your--configpath points to the expected TOML file.
Complete Example
Section titled “Complete Example”[model]path = "specifications/strenda.md"
[sql]type = "pgvector"host = "localhost"port = 5432database = "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