Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
...}`. New `GraphNotBuiltError` exposed from
`codegraph.mcp_server.server` for callers that want to handle it
explicitly.
- **`workspace_state` MCP tool now distinguishes "not configured" from
"no repos".** When `~/.codegraph/workspace.yml` didn't exist, the
tool returned `{workspace_size: 0, repos: []}` — indistinguishable
from a configured-but-empty workspace. LLMs paraphrased this as
"everything is broken" and cascaded into wrong-answer territory.
Result now carries a `status` field
(`workspace_not_configured` / `workspace_empty` / `ok`) with an
actionable message: "local-graph MCP tools work independently — this
only affects cross-repo tools. Run `codegraph workspace init` …".

## [0.1.1] — 2026-05-24

Expand Down
22 changes: 20 additions & 2 deletions codegraph/mcp_server/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -798,8 +798,26 @@ def _handle_workspace_state(
from codegraph.workspace.config import load_workspace, resolve_workspace_path
from codegraph.workspace.operations import workspace_state

cfg = load_workspace(resolve_workspace_path())
return workspace_state(cfg)
workspace_path = resolve_workspace_path()
if not workspace_path.exists():
return {
"status": "workspace_not_configured",
"workspace_path": str(workspace_path),
"message": (
"polycodegraph: no workspace configured at "
f"{workspace_path}. The local-graph MCP tools (find_symbol, "
"callers, callees, blast_radius, dataflow_trace, …) work "
"independently — this only affects the cross-repo workspace "
"tools. Run `codegraph workspace init` then "
"`codegraph workspace add <path>` to enable them."
),
"workspace_size": 0,
"repos": [],
}
cfg = load_workspace(workspace_path)
result = workspace_state(cfg)
result["status"] = "ok" if cfg.repos else "workspace_empty"
return result


@_register(
Expand Down
44 changes: 44 additions & 0 deletions tests/test_workspace_state_status.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"""Tests for `workspace_state` status field (v0.1.2 #9)."""
from __future__ import annotations

from pathlib import Path

import yaml

from codegraph.mcp_server.server import _handle_workspace_state


def test_workspace_not_configured_when_file_missing(
tmp_path: Path, monkeypatch
) -> None:
monkeypatch.setenv(
"CODEGRAPH_WORKSPACE_FILE", str(tmp_path / "nope" / "workspace.yml")
)
result = _handle_workspace_state(None, {})
assert result["status"] == "workspace_not_configured"
assert "codegraph workspace init" in result["message"]
assert "local-graph MCP tools" in result["message"]
assert result["workspace_size"] == 0
assert result["repos"] == []


def test_workspace_empty_when_file_exists_with_no_repos(
tmp_path: Path, monkeypatch
) -> None:
f = tmp_path / "workspace.yml"
f.write_text(yaml.dump({"repos": []}))
monkeypatch.setenv("CODEGRAPH_WORKSPACE_FILE", str(f))
result = _handle_workspace_state(None, {})
assert result["status"] == "workspace_empty"
assert result["workspace_size"] == 0


def test_workspace_ok_when_repos_present(tmp_path: Path, monkeypatch) -> None:
repo = tmp_path / "myrepo"
repo.mkdir()
f = tmp_path / "workspace.yml"
f.write_text(yaml.dump({"repos": [{"path": str(repo), "name": "myrepo"}]}))
monkeypatch.setenv("CODEGRAPH_WORKSPACE_FILE", str(f))
result = _handle_workspace_state(None, {})
assert result["status"] == "ok"
assert result["workspace_size"] == 1
Loading