Configuration¶
The config module loads configuration from environment variables and provides a typed dataclass for all settings.
Quick Start¶
to_vault_settings maps a loaded configuration onto a VaultSettings, and to_vault_instances resolves the constructed collaborators (embedding provider, summarizer, git strategy). Together they feed Vault construction:
import os
from markdown_vault_mcp.config import ProjectConfig
from markdown_vault_mcp.config_sections._assembly import (
to_vault_instances,
to_vault_settings,
)
from markdown_vault_mcp.vault import Vault
os.environ["MARKDOWN_VAULT_MCP_SOURCE_DIR"] = "/path/to/vault"
config = ProjectConfig.from_env()
instances = to_vault_instances(config)
settings = to_vault_settings(config, instances=instances)
vault = Vault(
source_dir=config.source_dir,
settings=settings,
embedding_provider=instances.embedding_provider,
summarizer=instances.summarizer,
git_strategy=instances.git_strategy,
on_write=instances.on_write,
)
Migrating from 4.x¶
to_vault_kwargs has been removed from both markdown_vault_mcp.config and markdown_vault_mcp.config_sections._assembly. Replace Vault(**to_vault_kwargs(config)) with the construction shown above. Resolve instances once and pass it to to_vault_settings so the provider is loaded once and its context limit determines the chunk size.
For overrides formerly applied to the keyword dictionary, use dataclasses.replace on the settings before constructing the vault:
from dataclasses import replace
from pathlib import Path
settings = replace(settings, index_path=Path("/path/to/other-index.db"))
Keep collaborator overrides on the corresponding Vault keyword arguments.
API Reference¶
ProjectConfig(server=ServerConfig(), server_name=_default_server_name(), source_dir=DEFAULT_SOURCE_DIR, read_only=False, write_protect_existing=True, report_unresolved_links=False, disable_apps_ui=False, index_path=None, state_path=None, embeddings_path=None, indexed_fields=None, required_fields=None, exclude=None, title_field='title', searchable_fields=None, templates_folder='_templates', prompts_folder=None, conventions_file='_conventions.md', okf_mode='auto', okf_write=False, okf_verify='elicit', attachment_extensions=None, max_attachment_size_mb=1.0, max_note_read_bytes=262144, default_search_mode='auto', chunks_per_file=2, snippet_words=200, length_downweight_alpha=0.25, max_chunk_words=400, max_chunk_chars=None, chunk_overlap_words=40, folder_weights=None, fts_weights=None, embedding_provider=None, ollama_host='http://localhost:11434', openai_api_key=None, voyage_api_key=None, voyage_model='voyage-4', ollama_model='nomic-embed-text', ollama_cpu_only=False, openai_base_url='https://api.openai.com/v1', openai_embedding_model='text-embedding-3-small', fastembed_model='BAAI/bge-small-en-v1.5', fastembed_cache_dir=None, embed_context=False, embed_timeout_s=30.0, embedding_batch_size=4, git_repo_url=None, git_token=None, git_username='x-access-token', git_pull_interval_s=600, git_push_delay_s=30.0, git_commit_name='markdown-vault-mcp', git_commit_email='noreply@markdown-vault-mcp', git_commit_name_claim=None, git_commit_email_claim=None, git_lfs=True, file_watcher=True, file_watcher_debounce_s=2.0, file_watcher_root_floor=True, boot_reindex=True, github_webhook_secret=None, gitlab_webhook_signing_token=None, gitlab_webhook_secret_token=None, summarize_provider=None, summarize_openai_api_key=None, summarize_openai_base_url=None, summarize_openai_model='gpt-5-mini', summarize_max_tokens=8192, summarize_max_notes=50, summarize_max_input_chars=200000, summarize_timeout=120.0, transfer=TransferConfig(), jobs=JobsConfig())
dataclass
¶
Domain config for Markdown Vault MCP. Compose — don't inherit.
git
property
¶
The git section assembled from the flat git_* fields.
A property rather than a composed field so the config-surface
generator documents the flat fields' metadata. Construction runs
GitConfig.__post_init__ validation.
indexing
property
¶
The indexing section assembled from the flat index/frontmatter fields.
embeddings
property
¶
The embeddings section assembled from the flat embedding fields.
search
property
¶
The search section assembled from the flat ranking/chunking fields.
summarize
property
¶
The summarize section assembled from the flat summarize_* fields.
sync
property
¶
The sync section assembled from the flat watcher/webhook fields.
content
property
¶
The content section assembled from the flat attachment/folder fields.
A relative prompts_folder is resolved against source_dir here,
so direct construction and from_env behave identically.
__post_init__()
¶
Validate composed domain fields. Raise ConfigurationError when invalid.
ConfigurationError is what serve turns into its one-line
ERROR: configuration error: ... exit; any other exception,
ValueError included, escapes as a full traceback. Name the
variable and the problem in the message.
This hook checks values — bounds, formats, cross-field rules. A
variable that must be set belongs on env(..., required=True) in
CONFIG-FROM-ENV instead: that refuses at startup and marks the var
required in the generated docs. Never refuse an empty required field
here — tests and programmatic callers construct ProjectConfig(...)
directly, without the environment.
Runs on EVERY construction path — from_env and a direct
ProjectConfig(field=...) alike. That is what makes this the right
home for a field invariant: env_float / env_int bounds check
only the env-sourced value, never the default, so a direct
construction slips past them. They also cannot express an exclusive
bound (their minimum / maximum are inclusive, so "must be > 0"
lets 0 through) or a cross-field rule (A requires B,
mutually-exclusive pairs). All three belong here.
The dataclass is frozen=True: read fields freely, but plain
assignment raises. To normalise rather than merely check, use
object.__setattr__(self, "name", value).
from_env()
classmethod
¶
Load :class:ProjectConfig from MARKDOWN_VAULT_MCP_* env vars.