From 12dc4acf92b9e8411fcdd72ad592123418c43da7 Mon Sep 17 00:00:00 2001 From: jessekemp1 <137220111+jessekemp1@users.noreply.github.com> Date: Tue, 9 Jun 2026 08:16:41 -0400 Subject: [PATCH] fix(mcp): let cortex_intelligence accept an explicit project param cortex_intelligence had no project arg, so the bridge fell back to _detect_current_project(), which runs 'git rev-parse --show-toplevel' in the bridge daemon's own cwd (always the cortex repo) and hardcodes a 'cortex' fallback. The IntelligenceQuery model also defaults project to 'cortex'. Net effect: every project-unscoped query was silently scoped to 'cortex', so a pre-flight about another project (e.g. Interac) returned 0.0 confidence. Add an optional project param and forward it to /intelligence/query when set; omit it when empty to preserve existing auto-detect behavior. Verified against the live bridge: no project -> scoped 'cortex'; project=interac -> scoped 'interac'. Co-authored-by: Isaac --- mcp_server.py | 14 +++++++---- tests/test_mcp_research_tools.py | 43 ++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/mcp_server.py b/mcp_server.py index fa1d934..d893aae 100755 --- a/mcp_server.py +++ b/mcp_server.py @@ -100,20 +100,24 @@ def cortex_service_health() -> str: @mcp.tool() -def cortex_intelligence(query: str, query_type: str = "research") -> str: +def cortex_intelligence(query: str, query_type: str = "research", project: str = "") -> str: """Query Cortex intelligence engine with natural language. Args: query: Natural language question about the codebase or projects. query_type: One of 'spec', 'architecture', 'implementation', 'research'. + project: Target project to scope the query to (e.g. 'interac', 'manulife-genie'). + Pass this whenever you know which project the question is about — the bridge + cannot reliably infer it (it falls back to its own working directory, which is + always the cortex repo). Leave empty only for genuinely project-agnostic queries. """ valid_types = {"spec", "architecture", "implementation", "research"} if query_type not in valid_types: query_type = "research" - result = _bridge_post( - "/intelligence/query", - {"request": query, "domain": DOMAIN, "query_type": query_type}, - ) + payload = {"request": query, "domain": DOMAIN, "query_type": query_type} + if project: + payload["project"] = project + result = _bridge_post("/intelligence/query", payload) return json.dumps(result, indent=2) diff --git a/tests/test_mcp_research_tools.py b/tests/test_mcp_research_tools.py index 336dfcf..aa74fa3 100644 --- a/tests/test_mcp_research_tools.py +++ b/tests/test_mcp_research_tools.py @@ -268,3 +268,46 @@ def test_research_digest_always_available(self): registered = set(mcp_instance._tool_manager._tools.keys()) assert "cortex_research_digest" in registered + + +class TestCortexIntelligenceProjectScope: + """cortex_intelligence forwards an explicit project to the bridge. + + Regression test: the tool previously had no `project` param, so the bridge + fell back to its own working directory (always the cortex repo) and silently + mis-scoped every query to project 'cortex'. The tool must now forward an + explicit project when one is given, and omit it (preserving auto-detect) when not. + """ + + @pytest.fixture(autouse=True) + def _skip_if_no_mcp(self): + pytest.importorskip("mcp") + + def _capture_payload(self, monkeypatch, **kwargs): + import cortex.mcp_server as mod + + captured = {} + + def fake_post(path, payload, timeout=5.0): + captured["path"] = path + captured["payload"] = payload + return {"ok": True} + + monkeypatch.setattr(mod, "_bridge_post", fake_post) + # @mcp.tool() wraps the function in a FunctionTool; .fn is the original callable. + fn = getattr(mod.cortex_intelligence, "fn", mod.cortex_intelligence) + fn(**kwargs) + return captured + + def test_project_forwarded_when_provided(self, monkeypatch): + captured = self._capture_payload( + monkeypatch, query="What cloud is Interac on?", project="interac" + ) + assert captured["path"] == "/intelligence/query" + assert captured["payload"].get("project") == "interac" + + def test_project_omitted_when_empty(self, monkeypatch): + captured = self._capture_payload(monkeypatch, query="project-agnostic question") + assert "project" not in captured["payload"], ( + "empty project must be omitted so the bridge default/auto-detect is preserved" + )