Add a stdio MCP server for searching and reading safe engineering source files from the wiki file asset index. The implementation uses registry-backed ACL checks, engineering-file whitelisting, asset:// resource URIs, read-only SQLite access, CLI entrypoints, optional MCP packaging, and unittest coverage for search, metadata, resource reads, blocked sources, bot ACLs, and size limits. Validation: python3 -m unittest discover -v; python3 -m py_compile obsidian_wiki/config.py obsidian_wiki/file_assets.py obsidian_wiki/mcp_assets.py obsidian_wiki/mcp_server.py obsidian_wiki/cli.py tests/test_mcp_assets.py; real BJD vault search/read smoke tests.
59 lines
1.8 KiB
Python
59 lines
1.8 KiB
Python
"""Configuration resolution shared by CLI and MCP entrypoints."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
from obsidian_wiki.file_assets import clean_config_value
|
|
|
|
GLOBAL_CONFIG = Path.home() / ".obsidian-wiki" / "config"
|
|
|
|
|
|
def parse_config_file(path: Path) -> dict[str, str]:
|
|
values: dict[str, str] = {}
|
|
if not path.is_file():
|
|
return values
|
|
for raw in path.read_text(encoding="utf-8", errors="ignore").splitlines():
|
|
line = raw.strip()
|
|
if not line or line.startswith("#") or "=" not in line:
|
|
continue
|
|
key, value = line.split("=", 1)
|
|
values[key.strip()] = clean_config_value(value)
|
|
return values
|
|
|
|
|
|
def find_config(start: str | Path | None = None) -> Path | None:
|
|
"""Resolve config using the llm-wiki protocol."""
|
|
current = Path(start or Path.cwd()).expanduser().resolve()
|
|
if current.is_file():
|
|
current = current.parent
|
|
home = Path.home().resolve()
|
|
|
|
while True:
|
|
candidate = current / ".env"
|
|
if candidate.is_file() and parse_config_file(candidate).get("OBSIDIAN_VAULT_PATH"):
|
|
return candidate
|
|
if current == home or current.parent == current:
|
|
break
|
|
current = current.parent
|
|
|
|
if GLOBAL_CONFIG.is_file():
|
|
return GLOBAL_CONFIG
|
|
return None
|
|
|
|
|
|
def load_config(start: str | Path | None = None) -> dict[str, str]:
|
|
path = find_config(start)
|
|
return parse_config_file(path) if path else {}
|
|
|
|
|
|
def resolve_vault_path(cli_vault: str | None = None, *, start: str | Path | None = None) -> str:
|
|
if cli_vault:
|
|
return str(Path(clean_config_value(cli_vault)).expanduser())
|
|
config = load_config(start)
|
|
return config.get("OBSIDIAN_VAULT_PATH", "")
|
|
|
|
|
|
def config_value(key: str, default: str = "", *, start: str | Path | None = None) -> str:
|
|
return load_config(start).get(key, default)
|