feat(mcp): init/analyze/diff lifecycle tools behind MCP (#171) - #172
Merged
Merged
Conversation
Project::init was bound to the process cwd, which the MCP server cannot change. init_at(root, dirs, name) anchors the project (and relative analysis paths) at an explicit root; init() now delegates with cwd.
MCP may read user-specified source paths, but every write must stay inside the permitted project root. confine_to_root lexically normalizes the candidate and rejects escapes such as store.path="../../x".
…loop 3) McpState now keeps the Project and a swappable Arc<GraphStore> snapshot, so lifecycle tools can rebuild the graph without a server restart. Startup no longer exits when no codeweb.toml is found; queries report status=uninitialized with a pointer to codeweb_init.
Creates codeweb.toml + .codeweb/ under the served directory via Project::init_at. Deliberately does not analyze; reports already_initialized when a project is present.
Runs Project::analyze on spawn_blocking (CPU-bound, must not block the runtime), swaps the resulting store into the query snapshot, and reports the in-memory node/edge counts. Enforces the write guard on store.path.
Reports added/modified/deleted files relative to the project root since the last analyze, so a caller can tell whether graph queries are stale.
…loop 7) The analyze gate now runs store.path through confine_to_root before Project::analyze, so a tampered codeweb.toml cannot write the store outside the directory the MCP server was started for.
Updates the README (en + zh) and DeveloperGuide tool tables, describes the mutable state model and write confinement, and refreshes the server instructions so a client knows to init/analyze instead of asking the user to restart the server. Note: tests/mcp_test.rs test_mcp_tools_list now expects 11 tools instead of 8. That expectation update is required by this feature; the assertion stays an exact tool-set match rather than a loose subset check.
Locks the contract that codeweb_analyze on an already-analyzed, unchanged project reports is_up_to_date with the real in-memory node/edge counts rather than the short-circuit's zeros, and that the graph stays queryable.
c2j
added a commit
that referenced
this pull request
Sep 20, 2026
Squashing #172 with `git add -A` picked up three files that were untracked local artifacts before this work started: .github/copilot-instructions.md .github/hooks/workmux-status/hooks.json .sisyphus/plans/fix-analyze-stale-store-version.md They are unrelated to the MCP lifecycle feature. This removes them from the index only (git rm --cached), so the files stay on disk and return to their previous untracked state.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #171.
问题
codeweb mcp只暴露 8 个只读查询工具,冷启动链路是断的:src/mcp/server.rs第一行Project::find(...)?:目录里没有codeweb.toml时进程直接退出,客户端拿不到任何 JSON-RPC 响应。McpState只持有加载一次的Arc<GraphStore>,不可变。status: "empty",把操作推回给用户并要求重启进程。analyze内存图也不会刷新。变更
新增三个生命周期工具(范围经确认,不自动 analyze):
codeweb_initcodeweb.toml+.codeweb/,不触发分析codeweb_analyzecodeweb_diff配套改造:
Project::init_at(root, dirs, name):init原本绑定进程 cwd,MCP 无法使用;init现在委托给它。McpState改为Arc<Inner>:permitted_root+Mutex<Option<Project>>+RwLock<GraphSnapshot>。查询取Arc<GraphStore>快照后立即释放锁,长查询不阻塞 analyze。codeweb_analyze在spawn_blocking中运行(CPU 密集同步函数,不能阻塞 runtime)。status: "uninitialized"并引导codeweb_init。confine_to_root:读取允许任意目录(analysis.paths),写入必须落在服务目录内;store.path逃逸(如../outside.bincode)时 analyze 直接返回错误。未引入--allow-write开关。TDD 循环(每个都有先失败后通过的测试)
init_at锚定显式 rootproject::tests::init_at_creates_config_under_given_root_not_cwd/init_at_rejects_existing_project_rootmcp::tools::tests::confine_*(4)test_mcp_uninitialized_project_stays_alivetest_mcp_init_creates_project_without_auto_analyze/test_mcp_init_is_idempotent_errortest_mcp_analyze_builds_graph_and_hot_swapstest_mcp_diff_reports_changes_since_last_analyzetest_mcp_analyze_rejects_store_path_escaping_rootis_up_to_date且给出真实 nodes/edges(非短路返回的 0)test_mcp_analyze_refreshes_already_analyzed_project循环 7 的 Red 通过临时禁用守卫验证:无守卫时 analyze 返回
ready且在服务目录外写出outside.bincode;恢复守卫后返回error且不落盘。循环 8 的 Red 通过临时改用
report.nodes/edges验证:此时报告为0 nodes,测试失败。需要说明的人类测试期望更新
tests/mcp_test.rs::test_mcp_tools_list的期望工具集由 8 个变为 11 个。这是本 feature 的必然结果;断言仍保持「精确集合」而非放宽为子集检查。门禁
默认构建剩余 2 个 warning(
node_sub_type_tag、TreeNode::has_more/more_count)为既有 mcp-gated 代码在非 mcp 构建下的 dead_code,与本次改动无关。明确不做
codeweb_analyze)。