fix: make remote MCP service start reliably

Use the Python module entrypoint in the user service, prepare the MCP optional runtime before restart, and serve the configured vault path instead of the repo root.

Verify binary asset resources use an octet-stream MIME type and cover service vault resolution in tests.
This commit is contained in:
Codex
2026-07-05 12:05:12 +00:00
parent d543831b5e
commit acff3c14db
5 changed files with 90 additions and 9 deletions
+29 -2
View File
@@ -35,13 +35,15 @@ class McpServiceConfigTests(unittest.TestCase):
def test_render_service_unit_uses_http_transport_without_embedding_token(self):
unit = manage_mcp_service.render_service_unit(
project_root=Path("/opt/obsidian-wiki"),
vault_path=Path("/data/bjd-vault"),
env_file=Path("/opt/obsidian-wiki/.runtime/obsidian-wiki-mcp.env"),
uv_path=Path("/home/nac/.local/bin/uv"),
)
self.assertIn("WorkingDirectory=/opt/obsidian-wiki", unit)
self.assertIn("EnvironmentFile=/opt/obsidian-wiki/.runtime/obsidian-wiki-mcp.env", unit)
self.assertIn("/home/nac/.local/bin/uv run --no-sync obsidian-wiki-mcp", unit)
self.assertIn("/home/nac/.local/bin/uv run --no-sync python -m obsidian_wiki.mcp_server", unit)
self.assertIn("--vault /data/bjd-vault", unit)
self.assertIn("--transport streamable-http", unit)
self.assertIn("--host 0.0.0.0", unit)
self.assertIn("--port 8765", unit)
@@ -61,6 +63,25 @@ class McpServiceConfigTests(unittest.TestCase):
self.assertEqual(stat.S_IMODE(os.stat(env_path).st_mode), 0o600)
self.assertEqual(stat.S_IMODE(os.stat(env_path.parent).st_mode), 0o700)
def test_resolve_vault_for_service_uses_explicit_or_configured_vault(self):
with tempfile.TemporaryDirectory() as tmp:
project_root = Path(tmp) / "project"
explicit_vault = Path(tmp) / "explicit-vault"
configured_vault = Path(tmp) / "configured-vault"
project_root.mkdir()
explicit_vault.mkdir()
configured_vault.mkdir()
self.assertEqual(
manage_mcp_service.resolve_vault_for_service(project_root, vault_path=explicit_vault),
explicit_vault.resolve(),
)
with patch.object(manage_mcp_service, "resolve_vault_path", return_value=str(configured_vault)):
self.assertEqual(
manage_mcp_service.resolve_vault_for_service(project_root),
configured_vault.resolve(),
)
def test_render_remote_config_prints_client_configuration(self):
output = manage_mcp_service.render_remote_config(
hosts=["192.168.1.10"],
@@ -133,17 +154,21 @@ class McpServiceConfigTests(unittest.TestCase):
patch.object(manage_mcp_service, "generate_token") as generate_token,
patch.object(manage_mcp_service, "detect_remote_hosts", return_value=["10.0.0.2"]),
patch.object(manage_mcp_service, "find_uv_path", return_value=Path("/home/nac/.local/bin/uv")),
patch.object(manage_mcp_service, "resolve_vault_for_service", return_value=Path("/data/bjd-vault")),
patch.object(manage_mcp_service, "prepare_runtime") as prepare_runtime,
patch.object(manage_mcp_service, "run_systemctl") as run_systemctl,
):
output = manage_mcp_service.start_service(project_root=project_root)
unit_path = config_home / "systemd" / "user" / manage_mcp_service.SERVICE_NAME
unit_content = unit_path.read_text(encoding="utf-8")
self.assertIn("/home/nac/.local/bin/uv run --no-sync obsidian-wiki-mcp", unit_content)
self.assertIn("/home/nac/.local/bin/uv run --no-sync python -m obsidian_wiki.mcp_server", unit_content)
self.assertIn("--vault /data/bjd-vault", unit_content)
self.assertIn("EnvironmentFile=", unit_content)
self.assertIn("http://10.0.0.2:8765/mcp", output)
self.assertIn("Bearer saved-token", output)
generate_token.assert_not_called()
prepare_runtime.assert_called_once_with(project_root, uv_path=Path("/home/nac/.local/bin/uv"))
run_systemctl.assert_has_calls(
[
call(["daemon-reload"]),
@@ -162,6 +187,7 @@ class McpServiceConfigTests(unittest.TestCase):
patch.object(manage_mcp_service, "generate_token", return_value="created-token"),
patch.object(manage_mcp_service, "detect_remote_hosts", return_value=["10.0.0.4"]),
patch.object(manage_mcp_service, "find_uv_path", return_value=Path("/home/nac/.local/bin/uv")),
patch.object(manage_mcp_service, "prepare_runtime") as prepare_runtime,
patch.object(manage_mcp_service, "run_systemctl") as run_systemctl,
):
output = manage_mcp_service.start_service(project_root=project_root, host="0.0.0.0", port=9876, path="/wiki-mcp")
@@ -174,6 +200,7 @@ class McpServiceConfigTests(unittest.TestCase):
self.assertIn("http://10.0.0.4:9876/wiki-mcp", output)
self.assertIn("Authorization: Bearer created-token", output)
self.assertIn('"type": "streamable-http"', output)
prepare_runtime.assert_called_once_with(project_root, uv_path=Path("/home/nac/.local/bin/uv"))
run_systemctl.assert_has_calls(
[
call(["daemon-reload"]),