{"record":{"id":"d193e2da698172a9","repo":"Hmbown/CodeWhale","slug":"approval-needs-a-reviewed-token-not-a-name-run-mcp-import","errorCode":null,"errorMessage":"Approval needs a reviewed token, not a name. Run /mcp import and copy its approve or decline command","messagePattern":"Approval needs a reviewed token, not a name\\. Run /mcp import and copy its approve or decline command","errorType":"validation","errorClass":"anyhow","httpStatus":null,"severity":"error","filePath":"crates/tui/src/mcp/external_import.rs","lineNumber":729,"sourceCode":"    Ok(ImportReceipt { name: candidate.name, decision, imported: decision == ImportDecision::Approve,\n        enabled: false, revision, consent_recorded,\n        warning: (!consent_recorded).then(|| \"The decision could not be added to import history; the configuration receipt above is authoritative\".into()),\n    })\n}\n\npub fn parse_review_token(token: &str) -> anyhow::Result<(&str, &str, &str)> {\n    let parts: Vec<_> = token.split(':').collect();\n    anyhow::ensure!(\n        parts.len() == 4\n            && parts[0] == \"mcp-import-v1\"\n            && parts[1..3]\n                .iter()\n                .all(|value| value.len() == 64 && value.bytes().all(|b| b.is_ascii_hexdigit()))\n            && (parts[3] == \"mcp-v1-absent\"\n                || parts[3].strip_prefix(\"mcp-v1-\").is_some_and(\n                    |hash| hash.len() == 64 && hash.bytes().all(|b| b.is_ascii_hexdigit())\n                )),\n        \"Approval needs a reviewed token, not a name. Run /mcp import and copy its approve or decline command\"\n    );\n    Ok((parts[1], parts[2], parts[3]))\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use tempfile::tempdir;\n\n    fn write_claude_json(dir: &Path, body: &str) -> PathBuf {\n        let path = dir.join(\".claude.json\");\n        fs::write(&path, body).unwrap();\n        path\n    }\n\n    fn with_import_context(test: impl FnOnce(&ImportContext<'_>)) {\n        let _env = crate::test_support::lock_test_env();\n        let root = tempdir().unwrap();","sourceCodeStart":711,"sourceCodeEnd":747,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/mcp/external_import.rs#L711-L747","documentation":"`parse_review_token` validates a strict token format: id, revision, a 64-hex-char hash, and `mcp-v1-` prefixed hash segment. If the user passed a plain source name or a malformed string instead of a token copied from `/mcp import` output, this error fires before any decision is made.","triggerScenarios":"Running `mcp import apply <name>` with a source name instead of the approve/decline token; copy-pasting only part of the token; quoting/shell-mangling that splits the token.","commonSituations":"Typing the command by hand from memory instead of copying it; older docs/examples showing name-based apply; shell history truncating a long token.","solutions":["Run `/mcp import` and copy the exact approve or decline command it prints.","Paste the whole token as a single shell argument (quote it if it contains special characters).","Verify the token has the expected segments: id, revision, and 64-hex hashes including the `mcp-v1-` prefix."],"exampleFix":"// before\ncodewhale mcp import apply my-source-server\n// after\ncodewhale mcp import apply \"a1b2... 3 rev mcp-v1-64hexhash\"  # token copied from /mcp import","handlingStrategy":"validation","validationCode":"fn looks_like_review_token(s: &str) -> bool {\n    let parts: Vec<&str> = s.split_whitespace().collect();\n    parts.len() == 4\n        && parts[2].len() == 64 && parts[2].bytes().all(|b| b.is_ascii_hexdigit())\n        && (parts[3] == \"mcp-v1-absent\"\n            || parts[3].starts_with(\"mcp-v1-\"))\n}\nif !looks_like_review_token(arg) { eprintln!(\"pass the token copied from /mcp import\"); }","typeGuard":"fn is_review_token(s: &str) -> bool {\n    let p: Vec<&str> = s.split_whitespace().collect();\n    p.len() == 4\n        && p[2].len() == 64 && p[2].bytes().all(|b| b.is_ascii_hexdigit())\n        && (p[3] == \"mcp-v1-absent\"\n            || p[3].strip_prefix(\"mcp-v1-\").is_some_and(|h| h.len() == 64 && h.bytes().all(|b| b.is_ascii_hexdigit())))\n}","tryCatchPattern":"match mcp_import_apply(raw) {\n    Err(e) if e.to_string().contains(\"needs a reviewed token\") => {\n        // show /mcp import output and let the user copy the exact command\n    }\n    other => other?,\n}","preventionTips":["Always copy the approve/decline command verbatim from /mcp import output","Quote tokens in shell invocations","Do not retype long tokens from memory"],"tags":["cli","validation","token"],"backgroundTag":"invalid-argument-format","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T11:17:16.035Z"}