Skip to content

docs(roadmap): add #461 — config suggest_field uses pure edit-distance; { "mcp": { "servers": ... } } (VS Code MCP convention) is told "Did you mean 'env'?"#3074

Open
Yeachan-Heo wants to merge 1 commit into
mainfrom
docs/roadmap-461-config-suggest-field-mcp-suggests-env
Open

docs(roadmap): add #461 — config suggest_field uses pure edit-distance; { "mcp": { "servers": ... } } (VS Code MCP convention) is told "Did you mean 'env'?"#3074
Yeachan-Heo wants to merge 1 commit into
mainfrom
docs/roadmap-461-config-suggest-field-mcp-suggests-env

Conversation

@Yeachan-Heo
Copy link
Copy Markdown
Contributor

ROADMAP pinpoint #461.claw.json validator suggests env when user writes the VS Code-style mcp.servers nested form

Dogfooded across two consecutive Clawhip pinpoint nudges (11:30 message 1508069585461706783 triggered the MCP investigation; 12:00 message 1508077133459751073 triggered finishing it). Root cause traced between the two.

The bug in one line

$ echo '{"mcp": {"servers": {}}}' > .claw/settings.json && claw mcp list --output-format json | jq -r '.config_load_error'
/tmp/proj/.claw/settings.json: unknown key "mcp" (line 1). Did you mean "env"?

The user wrote the VS Code MCP convention ({ "mcp": { "servers": { ... } } } — used by VS Code's settings.json, by hermes_cli/config.py:478, by many MCP docs). claw rejects it and suggests env — a completely unrelated config block. User follows the suggestion → no MCP servers configured → silent functionality loss with actively wrong remediation.

Suggestion-quality matrix

Input Edit distance to mcpServers Edit distance to env Actual suggestion Correct?
mcp (VS Code nested form) 7 3 env opposite intent
mcpServer (singular camelCase) 1 7 mcpServers
mcpserver (singular lowercase) 2 6 mcpServers
mcpServrs (typo) 1 8 mcpServers
MCPServers (case variant) 3 8 mcpServers
mcp_servers (snake_case) 2 7 mcpServers
servers (just servers) 3 5 mcpServers

Root cause (traced)

rust/crates/runtime/src/config_validate.rs:396-407:

fn suggest_field(input: &str, candidates: &[&str]) -> Option<String> {
    let input_lower = input.to_ascii_lowercase();
    candidates
        .iter()
        .filter_map(|candidate| {
            let distance = simple_edit_distance(&input_lower, &candidate.to_ascii_lowercase());
            (distance <= 3).then_some((distance, *candidate))
        })
        .min_by_key(|(distance, _)| *distance)
        .map(|(_, name)| name.to_string())
}

Pure Levenshtein, threshold-3 filter, no prefix awareness, no semantic awareness. For mcp (len 3):

  • distance to mcpServers = 7 (insertions of Servers) — filtered out by threshold
  • distance to env = 3 (full substitution) — at threshold, wins
  • everything else > 3 — filtered out

mcp is a 3-character prefix of mcpServers and shares 100% of its characters with the intended key. Edit-distance has no way to express that — every extra character in the longer candidate is a +1 insertion penalty.

Why distinct from existing items

  • 前排合影 #110 covers ConfigLoader::discover ancestor-walk under-discovery (config files invisible from subdirs).
  • Agent开卷考试启动 #28 covers MissingCredentials error-copy improvements (adjacent-provider env-var hints).
  • 合影 #108 covers CLI subcommand typo fallthrough (no levenshtein for subcommands).

None address the config-key validator's suggestion-ranking algorithm itself producing actively wrong suggestions for prefix-substring inputs.

Why it matters

  1. VS Code MCP convention is widespread. VS Code's settings.json uses "mcp": { "servers": { ... } }. Users coming from VS Code naturally write the nested form.
  2. Hermes CLI (external/hermes-agent/hermes_cli/config.py:478) uses mcp nested. Any user copying from hermes config hits this.
  3. env is the absolute worst possible suggestion — completely different concept (environment variables), zero overlap with MCP server registration.
  4. Silent functionality loss: mcp list reports configured_servers: 0 with no signal that MCP was the user's actual intent.
  5. Worse than no suggestion — a misleading hint costs more user-time than "unknown key" with no hint at all.
  6. General bug class: any short input that is a strict prefix of a long canonical key (where the long key edit-distance exceeds 3) falls through to whatever happens to be within edit-distance-3.

Required fix shape

(a) Add prefix-match as higher-priority than edit-distance. Before computing edit-distance, check if input is a case-insensitive prefix of any candidate. If yes, return that candidate. Makes mcpmcpServers (prefix wins) instead of mcpenv (edit-distance wins).

(b) Add nested-key awareness for known scoped patterns. When unknown key is the parent of a known-nested form (mcp.servers would be valid under VS Code convention), emit specific diagnostic: "unknown key 'mcp' — claw uses flat camelCase 'mcpServers' instead of VS Code-style 'mcp.servers' nested block. Rewrite as: { \"mcpServers\": { ... } }".

(c) Add a claw doctor check mcp_config_form_drift that detects top-level mcp blocks and warns to migrate.

(d) Update README/--help to document the canonical mcpServers key and call out that VS Code-style mcp.servers is not accepted.

(e) Regression coverage: suggest_field("mcp", FIELD_SPECS) asserts Some("mcpServers") not Some("env"). Full matrix as parameterized tests.

Acceptance check

cd /tmp/test && mkdir -p .claw && \
  echo '{"mcp":{"servers":{"a":{"command":"x"}}}}' > .claw/settings.json && \
  claw mcp list --output-format json | jq -r '.config_load_error' | \
  grep -E '"mcpServers"|VS Code|migrate'

Should match (currently outputs Did you mean "env"?).


[repo owner's gaebal-gajae (clawdbot) 🦞]

…e; '"mcp": { "servers": ... }' (VS Code MCP convention) suggests 'env' instead of 'mcpServers'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants