feat(mcp): queue local OpenCode ingest jobs

Add optional write-capable MCP ingest tools for chunked uploads, SHA-verified completion, and asynchronous local OpenCode wiki-ingest jobs. The tools remain disabled by default and are gated by OBSIDIAN_MCP_ENABLE_INGEST or mcp_service.sh start --enable-ingest.

Remote clients can upload files into the vault inbox, start a restricted fixed-prompt ingest job, poll job status/result, and cancel queued or running work. The implementation keeps arbitrary prompt and shell execution out of the MCP surface.

Validation: python3 -m unittest discover -v; python3 -m py_compile obsidian_wiki/mcp_ingest.py obsidian_wiki/mcp_server.py scripts/manage_mcp_service.py tests/test_mcp_ingest.py tests/test_mcp_server_remote.py tests/test_mcp_service.py; git diff --check.
This commit is contained in:
Codex
2026-07-05 14:54:29 +00:00
parent b89d141f03
commit b174df642f
9 changed files with 865 additions and 4 deletions
+10 -1
View File
@@ -155,6 +155,7 @@ def write_token_env_file(
host: str = DEFAULT_HOST,
port: int = DEFAULT_PORT,
path: str = DEFAULT_PATH,
enable_ingest: bool = False,
) -> None:
env_path.parent.mkdir(parents=True, exist_ok=True)
env_path.parent.chmod(0o700)
@@ -164,6 +165,7 @@ def write_token_env_file(
env_file.write(f"OBSIDIAN_MCP_HOST={host}\n")
env_file.write(f"OBSIDIAN_MCP_PORT={port}\n")
env_file.write(f"OBSIDIAN_MCP_PATH={path}\n")
env_file.write(f"OBSIDIAN_MCP_ENABLE_INGEST={1 if enable_ingest else 0}\n")
env_path.chmod(0o600)
@@ -316,6 +318,7 @@ def start_service(
port: int = DEFAULT_PORT,
path: str = DEFAULT_PATH,
token_bytes: int = DEFAULT_TOKEN_BYTES,
enable_ingest: bool = False,
) -> str:
paths = resolve_service_paths(project_root)
try:
@@ -324,7 +327,7 @@ def start_service(
if paths.env_path.exists():
raise
token = generate_token(token_bytes)
write_token_env_file(paths.env_path, token, host=host, port=port, path=path)
write_token_env_file(paths.env_path, token, host=host, port=port, path=path, enable_ingest=enable_ingest)
uv_path = find_uv_path()
prepare_runtime(paths.project_root, uv_path=uv_path)
resolved_vault = resolve_vault_for_service(paths.project_root, vault_path=vault_path)
@@ -377,6 +380,11 @@ def main(argv: Sequence[str] | None = None) -> int:
start_parser.add_argument("--port", default=DEFAULT_PORT, type=int)
start_parser.add_argument("--path", default=DEFAULT_PATH)
start_parser.add_argument("--token-bytes", default=DEFAULT_TOKEN_BYTES, type=int)
start_parser.add_argument(
"--enable-ingest",
action="store_true",
help="Expose write-capable upload and local OpenCode ingest MCP tools. Defaults to off.",
)
subparsers.add_parser("stop", help="Stop the user service without deleting service files.")
@@ -398,6 +406,7 @@ def main(argv: Sequence[str] | None = None) -> int:
port=args.port,
path=args.path,
token_bytes=args.token_bytes,
enable_ingest=args.enable_ingest,
),
end="",
)