Skip to content

Commit 3a8a135

Browse files
authored
release: 4.0.4 PATH-based MCP setup (#30)
## Summary Fixes #29. Combines the MCP setup fix, the missing MCP client documentation updates, the dependency bump from PR #32, the `4.0.4` release metadata, and the SEO cleanup that missed the already-merged PR #31. ## Root Cause `setup-mcp` used `std::env::current_exe()` when writing `.claude/settings.json`, and the website MCP client guide still showed `/absolute/path/to/gather-step`. Both could pin MCP setup to a build, worktree, or stale install path instead of the stable command users already have on `PATH`. The initial PR also left two silent-failure paths: malformed existing Claude settings JSON was overwritten instead of rejected, and users got no visible signal when `gather-step` could not be resolved on the MCP client's `PATH`. ## Key Decisions - Use `command: "gather-step"` for generated MCP config and manual setup examples. - Use the public top-level `serve` command in MCP args instead of the hidden `mcp serve` alias. - Probe `PATH` during `setup-mcp`, emit `path_resolution` in JSON output, and warn in human output when the command is not found. - Preserve malformed or structurally invalid existing settings files by returning an error instead of overwriting them. - Add a Codex CLI MCP config example, including the restart note required before `mcp__gather_step` tools appear in a running session. - Fold PR #32 into this branch so the release/version/changelog work lands once. - Carry forward the missed landing SEO cleanup from PR #31 after rebasing this branch on `main`. ## Files Changed | File | Change | | --- | --- | | `crates/gather-step-cli/src/commands/setup_mcp.rs` | Writes `command: "gather-step"` and `args: ["--workspace", ..., "serve"]`, reports PATH resolution, and rejects malformed settings JSON. | | `crates/gather-step-cli/tests/cli_setup_mcp.rs` | Regression coverage for generated MCP settings shape, malformed JSON preservation, entry replacement, and bad `mcpServers` shape. | | `crates/gather-step-cli/tests/cli_commands.rs` | Integration coverage for the PATH command, global/local scope behavior, missing HOME, and JSON `path_resolution`. | | `website/src/content/docs/reference/cli.md` | Documents the generated command behavior. | | `website/src/content/docs/guides/getting-started.md` | Updates the generic sample MCP config to use the `PATH` command. | | `website/src/content/docs/guides/mcp-clients.md` | Updates Claude, Codex, Cursor, and generic MCP examples, fixes the Claude Code user settings path, and adds the macOS GUI PATH note. | | `Cargo.toml`, `Cargo.lock`, `website/package.json` | Bump release/package metadata to `4.0.4`. | | `website/bun.lock`, `website/package.json` | Bump `astro` to `^6.3.1` and `@astrojs/starlight` to `^0.39.1`. | | `website/src/content/docs/changelog.md` and landing components | Add the `v4.0.4` changelog entry and release stamps. | | `website/src/layouts/LandingLayout.astro`, `website/src/styles/landing.css` | Use computed site URLs in JSON-LD, add theme/manifest metadata, preload Google Fonts with `display=swap`, and remove ignored `X-Content-Type-Options` meta. | ## Dependency Notes - Cargo updated the resolvable `wasm-bindgen` stack. - `generic-array` remains at `0.14.7` because `crypto-common v0.1.7` requires `generic-array = "=0.14.7"` exactly; forcing `0.14.9` fails dependency resolution. ## Verification - Confirmed PR #31 was merged from `dadd90f`, before the follow-up SEO cleanup commit. - Rebasing PR #30 on `origin/main` completed cleanly. - `cargo fmt --all` - `cargo check --locked --workspace` - `cargo test --test cli_commands setup_mcp` - `cargo test --test cli_setup_mcp` - `cargo test --workspace` - `cd website && bun install` - `cd website && bun install --frozen-lockfile` - `cd website && bun run build` - Verified generated `dist/index.html` has theme color, manifest, font preload, computed JSON-LD IDs, and no `X-Content-Type-Options` meta. - Verified generated sitemap includes `https://gatherstep.dev/`. - `git diff --check` ## Follow-ups - PR #32 is superseded by this combined PR.
2 parents d911ad6 + ed7107b commit 3a8a135

16 files changed

Lines changed: 319 additions & 111 deletions

File tree

Cargo.lock

Lines changed: 22 additions & 22 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ members = [
1414
]
1515

1616
[workspace.package]
17-
version = "4.0.3"
17+
version = "4.0.4"
1818
authors = ["JJ Adonis"]
1919
edition = "2024"
2020
rust-version = "1.94.1"
@@ -24,15 +24,15 @@ homepage = "https://github.com/thedoublejay/gather-step"
2424
description = "High-performance multi-repo codebase intelligence engine"
2525

2626
[workspace.dependencies]
27-
gather-step = { path = "crates/gather-step-cli", version = "4.0.3" }
28-
gather-step-analysis = { path = "crates/gather-step-analysis", version = "4.0.3" }
29-
gather-step-core = { path = "crates/gather-step-core", version = "4.0.3" }
30-
gather-step-deploy = { path = "crates/gather-step-deploy", version = "4.0.3" }
31-
gather-step-git = { path = "crates/gather-step-git", version = "4.0.3" }
32-
gather-step-mcp = { path = "crates/gather-step-mcp", version = "4.0.3" }
33-
gather-step-output = { path = "crates/gather-step-output", version = "4.0.3" }
34-
gather-step-parser = { path = "crates/gather-step-parser", version = "4.0.3" }
35-
gather-step-storage = { path = "crates/gather-step-storage", version = "4.0.3" }
27+
gather-step = { path = "crates/gather-step-cli", version = "4.0.4" }
28+
gather-step-analysis = { path = "crates/gather-step-analysis", version = "4.0.4" }
29+
gather-step-core = { path = "crates/gather-step-core", version = "4.0.4" }
30+
gather-step-deploy = { path = "crates/gather-step-deploy", version = "4.0.4" }
31+
gather-step-git = { path = "crates/gather-step-git", version = "4.0.4" }
32+
gather-step-mcp = { path = "crates/gather-step-mcp", version = "4.0.4" }
33+
gather-step-output = { path = "crates/gather-step-output", version = "4.0.4" }
34+
gather-step-parser = { path = "crates/gather-step-parser", version = "4.0.4" }
35+
gather-step-storage = { path = "crates/gather-step-storage", version = "4.0.4" }
3636

3737
tree-sitter = "=0.26.8"
3838
tree-sitter-typescript = "0.23.2"

crates/gather-step-cli/src/commands/setup_mcp.rs

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use std::path::{Path, PathBuf};
1+
use std::{
2+
env,
3+
path::{Path, PathBuf},
4+
};
25

36
use anyhow::{Context, Result};
47
use clap::{Args, ValueEnum};
@@ -25,6 +28,16 @@ struct SetupMcpOutput {
2528
event: &'static str,
2629
scope: McpScope,
2730
settings_path: String,
31+
path_resolution: PathResolution,
32+
#[serde(skip_serializing_if = "Option::is_none")]
33+
command_path: Option<String>,
34+
}
35+
36+
#[derive(Debug, Clone, Copy, Serialize)]
37+
#[serde(rename_all = "snake_case")]
38+
enum PathResolution {
39+
Ok,
40+
NotFound,
2841
}
2942

3043
pub fn run(app: &AppContext, args: SetupMcpArgs) -> Result<()> {
@@ -34,22 +47,33 @@ pub fn run(app: &AppContext, args: SetupMcpArgs) -> Result<()> {
3447
.context("cannot resolve HOME")?
3548
.join(".claude/settings.json"),
3649
};
37-
let exe = std::env::current_exe().unwrap_or_else(|_| PathBuf::from("gather-step"));
38-
39-
write_settings(&settings_path, &app.workspace_path, &exe)?;
50+
let command_path = find_command_on_path("gather-step");
51+
let path_resolution = if command_path.is_some() {
52+
PathResolution::Ok
53+
} else {
54+
PathResolution::NotFound
55+
};
56+
write_settings(&settings_path, &app.workspace_path)?;
4057

4158
let payload = SetupMcpOutput {
4259
event: "setup_mcp_completed",
4360
scope: args.scope,
4461
settings_path: settings_path.display().to_string(),
62+
path_resolution,
63+
command_path: command_path.as_ref().map(|path| path.display().to_string()),
4564
};
4665
let output = app.output();
4766
output.emit(&payload)?;
67+
if matches!(path_resolution, PathResolution::NotFound) {
68+
output.line(
69+
"Warning: `gather-step` was not found on PATH. MCP clients may fail to start the server until their PATH includes the installed binary.",
70+
);
71+
}
4872
output.line(format!("Updated {}", payload.settings_path));
4973
Ok(())
5074
}
5175

52-
pub fn write_settings(path: &Path, workspace: &Path, exe: &Path) -> Result<()> {
76+
pub fn write_settings(path: &Path, workspace: &Path) -> Result<()> {
5377
if let Some(parent) = path.parent() {
5478
std::fs::create_dir_all(parent)
5579
.with_context(|| format!("creating {}", parent.display()))?;
@@ -58,19 +82,18 @@ pub fn write_settings(path: &Path, workspace: &Path, exe: &Path) -> Result<()> {
5882
let mut root = if path.exists() {
5983
let body =
6084
std::fs::read_to_string(path).with_context(|| format!("reading {}", path.display()))?;
61-
serde_json::from_str::<Value>(&body).unwrap_or_else(|_| Value::Object(Map::default()))
85+
serde_json::from_str::<Value>(&body)
86+
.with_context(|| format!("parsing {}", path.display()))?
6287
} else {
6388
Value::Object(Map::default())
6489
};
6590

6691
let workspace_str = workspace
6792
.to_str()
6893
.context("workspace path is not valid UTF-8")?;
69-
let exe_str = exe.to_str().context("executable path is not valid UTF-8")?;
70-
7194
let entry = json!({
72-
"command": exe_str,
73-
"args": ["--workspace", workspace_str, "mcp", "serve"],
95+
"command": "gather-step",
96+
"args": ["--workspace", workspace_str, "serve"],
7497
});
7598

7699
let servers = root
@@ -90,5 +113,12 @@ pub fn write_settings(path: &Path, workspace: &Path, exe: &Path) -> Result<()> {
90113
}
91114

92115
fn home_dir() -> Option<PathBuf> {
93-
std::env::var_os("HOME").map(PathBuf::from)
116+
env::var_os("HOME").map(PathBuf::from)
117+
}
118+
119+
fn find_command_on_path(command: &str) -> Option<PathBuf> {
120+
let path = env::var_os("PATH")?;
121+
env::split_paths(&path)
122+
.map(|dir| dir.join(command))
123+
.find(|candidate| candidate.is_file())
94124
}

0 commit comments

Comments
 (0)