Skip to content

Commit beecf61

Browse files
committed
fix(mcp): workspace_state distinguishes not-configured from no-repos
When `~/.codegraph/workspace.yml` didn't exist, `workspace_state` returned `{workspace_size: 0, repos: []}` — same shape as a configured but empty workspace. LLMs read this as "everything is broken". Result now carries a `status` field: - `workspace_not_configured` when the YAML doesn't exist - `workspace_empty` when it exists with no repos - `ok` when there's at least one repo The not-configured case includes a message clarifying that the local single-repo MCP tools work independently — only the cross-repo tools are affected. v0.1.2 backlog item #9.
1 parent b6055e1 commit beecf61

3 files changed

Lines changed: 73 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
100100
...}`. New `GraphNotBuiltError` exposed from
101101
`codegraph.mcp_server.server` for callers that want to handle it
102102
explicitly.
103+
- **`workspace_state` MCP tool now distinguishes "not configured" from
104+
"no repos".** When `~/.codegraph/workspace.yml` didn't exist, the
105+
tool returned `{workspace_size: 0, repos: []}` — indistinguishable
106+
from a configured-but-empty workspace. LLMs paraphrased this as
107+
"everything is broken" and cascaded into wrong-answer territory.
108+
Result now carries a `status` field
109+
(`workspace_not_configured` / `workspace_empty` / `ok`) with an
110+
actionable message: "local-graph MCP tools work independently — this
111+
only affects cross-repo tools. Run `codegraph workspace init` …".
103112

104113
## [0.1.1] — 2026-05-24
105114

codegraph/mcp_server/server.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -798,8 +798,26 @@ def _handle_workspace_state(
798798
from codegraph.workspace.config import load_workspace, resolve_workspace_path
799799
from codegraph.workspace.operations import workspace_state
800800

801-
cfg = load_workspace(resolve_workspace_path())
802-
return workspace_state(cfg)
801+
workspace_path = resolve_workspace_path()
802+
if not workspace_path.exists():
803+
return {
804+
"status": "workspace_not_configured",
805+
"workspace_path": str(workspace_path),
806+
"message": (
807+
"polycodegraph: no workspace configured at "
808+
f"{workspace_path}. The local-graph MCP tools (find_symbol, "
809+
"callers, callees, blast_radius, dataflow_trace, …) work "
810+
"independently — this only affects the cross-repo workspace "
811+
"tools. Run `codegraph workspace init` then "
812+
"`codegraph workspace add <path>` to enable them."
813+
),
814+
"workspace_size": 0,
815+
"repos": [],
816+
}
817+
cfg = load_workspace(workspace_path)
818+
result = workspace_state(cfg)
819+
result["status"] = "ok" if cfg.repos else "workspace_empty"
820+
return result
803821

804822

805823
@_register(
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
"""Tests for `workspace_state` status field (v0.1.2 #9)."""
2+
from __future__ import annotations
3+
4+
from pathlib import Path
5+
6+
import yaml
7+
8+
from codegraph.mcp_server.server import _handle_workspace_state
9+
10+
11+
def test_workspace_not_configured_when_file_missing(
12+
tmp_path: Path, monkeypatch
13+
) -> None:
14+
monkeypatch.setenv(
15+
"CODEGRAPH_WORKSPACE_FILE", str(tmp_path / "nope" / "workspace.yml")
16+
)
17+
result = _handle_workspace_state(None, {})
18+
assert result["status"] == "workspace_not_configured"
19+
assert "codegraph workspace init" in result["message"]
20+
assert "local-graph MCP tools" in result["message"]
21+
assert result["workspace_size"] == 0
22+
assert result["repos"] == []
23+
24+
25+
def test_workspace_empty_when_file_exists_with_no_repos(
26+
tmp_path: Path, monkeypatch
27+
) -> None:
28+
f = tmp_path / "workspace.yml"
29+
f.write_text(yaml.dump({"repos": []}))
30+
monkeypatch.setenv("CODEGRAPH_WORKSPACE_FILE", str(f))
31+
result = _handle_workspace_state(None, {})
32+
assert result["status"] == "workspace_empty"
33+
assert result["workspace_size"] == 0
34+
35+
36+
def test_workspace_ok_when_repos_present(tmp_path: Path, monkeypatch) -> None:
37+
repo = tmp_path / "myrepo"
38+
repo.mkdir()
39+
f = tmp_path / "workspace.yml"
40+
f.write_text(yaml.dump({"repos": [{"path": str(repo), "name": "myrepo"}]}))
41+
monkeypatch.setenv("CODEGRAPH_WORKSPACE_FILE", str(f))
42+
result = _handle_workspace_state(None, {})
43+
assert result["status"] == "ok"
44+
assert result["workspace_size"] == 1

0 commit comments

Comments
 (0)