feat(mcp): add OpenCode-backed wiki query
Add a query tool group for the wiki-file-assets MCP server that queues fixed /wiki-query OpenCode jobs, exposes status/result/cancel/readiness tools, and keeps query disabled until explicitly enabled. Document the new service switches and config, keep remote query gated in AGENTS.md, preserve token behavior, and pass proxy environment through the managed service so OpenCode smoke/query jobs can reach the model provider. Validation: python3 -m unittest discover -v; python3 -m py_compile obsidian_wiki/mcp_query.py obsidian_wiki/mcp_server.py scripts/manage_mcp_service.py tests/test_mcp_query.py tests/test_mcp_server_remote.py tests/test_mcp_service.py; git diff --check.
This commit is contained in:
@@ -142,6 +142,9 @@ class RemoteMcpServerTests(unittest.TestCase):
|
||||
with patch.dict(os.environ, {"OBSIDIAN_MCP_ENABLE_INGEST": "1"}, clear=True):
|
||||
self.assertEqual(mcp_server.enabled_tool_names(), {"file-assets", "ingest"})
|
||||
|
||||
with patch.dict(os.environ, {"OBSIDIAN_MCP_ENABLED_TOOLS": "file-assets,query"}, clear=True):
|
||||
self.assertEqual(mcp_server.enabled_tool_names(), {"file-assets", "query"})
|
||||
|
||||
def test_create_http_app_wraps_streamable_http_app_with_auth(self):
|
||||
class FakeServer:
|
||||
def __init__(self):
|
||||
@@ -355,6 +358,73 @@ class RemoteMcpServerTests(unittest.TestCase):
|
||||
self.assertIn("create_ingest_upload_ticket", FakeMCP.tool_names)
|
||||
self.assertNotIn("ingest_file_base64", FakeMCP.tool_names)
|
||||
|
||||
def test_create_server_registers_query_tools_only_when_enabled(self):
|
||||
class FakeMCP:
|
||||
tool_names = []
|
||||
|
||||
def __init__(self, _name, **_kwargs):
|
||||
pass
|
||||
|
||||
def tool(self):
|
||||
def decorator(func):
|
||||
self.tool_names.append(func.__name__)
|
||||
return func
|
||||
|
||||
return decorator
|
||||
|
||||
def resource(self, *_args, **_kwargs):
|
||||
def decorator(func):
|
||||
return func
|
||||
|
||||
return decorator
|
||||
|
||||
def prompt(self, *_args, **_kwargs):
|
||||
def decorator(func):
|
||||
return func
|
||||
|
||||
return decorator
|
||||
|
||||
class FakeQueryManager:
|
||||
def start_query_job(self, query, mode="normal", public_only=False, note=None):
|
||||
return {"job_id": "query-job", "state": "queued"}
|
||||
|
||||
def get_query_job_status(self, job_id):
|
||||
return {"job_id": job_id, "state": "running"}
|
||||
|
||||
def get_query_job_result(self, job_id):
|
||||
return {"job_id": job_id, "state": "succeeded", "answer": "answer"}
|
||||
|
||||
def cancel_query_job(self, job_id):
|
||||
return {"job_id": job_id, "state": "cancelled"}
|
||||
|
||||
def check_opencode_readiness(self, *, run_smoke_test=False):
|
||||
return {"ok": True, "runtime_smoke_test": "skipped"}
|
||||
|
||||
with (
|
||||
tempfile.TemporaryDirectory() as tmp,
|
||||
patch.dict("sys.modules", {"mcp.server.fastmcp": type("Mod", (), {"FastMCP": FakeMCP})()}),
|
||||
):
|
||||
root = Path(tmp)
|
||||
disabled_server = mcp_server.create_server(vault_path=root / "vault")
|
||||
disabled_tool_names = list(FakeMCP.tool_names)
|
||||
FakeMCP.tool_names.clear()
|
||||
enabled_server = mcp_server.create_server(
|
||||
vault_path=root / "vault",
|
||||
enable_query=True,
|
||||
project_root=root / "project",
|
||||
query_runtime_root=root / "runtime",
|
||||
query_manager=FakeQueryManager(),
|
||||
)
|
||||
|
||||
self.assertIsInstance(disabled_server, FakeMCP)
|
||||
self.assertIsInstance(enabled_server, FakeMCP)
|
||||
self.assertNotIn("start_wiki_query", disabled_tool_names)
|
||||
self.assertIn("start_wiki_query", FakeMCP.tool_names)
|
||||
self.assertIn("get_wiki_query_status", FakeMCP.tool_names)
|
||||
self.assertIn("get_wiki_query_result", FakeMCP.tool_names)
|
||||
self.assertIn("cancel_wiki_query", FakeMCP.tool_names)
|
||||
self.assertIn("check_query_opencode_readiness", FakeMCP.tool_names)
|
||||
|
||||
def test_create_ingest_upload_ticket_tool_returns_http_only_token(self):
|
||||
class FakeMCP:
|
||||
tools = {}
|
||||
|
||||
Reference in New Issue
Block a user