fix(plugin): session-start hook reads API token from CLAUDE_PLUGIN_OPTION env, not user_config placeholder #64
Reference in New Issue
Block a user
Delete Branch "dev"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
Phase 1 push-channel test failed on first real install: no standing-context block injected at SessionStart.
Root cause: the SessionStart hook passed the key via
SCRIBE_TOKEN="${user_config.api_token}"inhooks.json, butapi_tokenissensitive: true. Claude Code keeps sensitive userConfig in the keychain and does not interpolate it into hook command strings (only intomcpServersheaders — which is why MCP auth worked). The hook received the literal string${user_config.api_token}, which is non-empty so it passed the guard and was sent as the Bearer token →/api/plugin/contextcorrectly returns 401 →curl -ffails → fail-open → nothing injected.The endpoint itself is healthy: deployed,
login_requiredacceptsBearer fmcp_keys, andbuild_session_contextreturns{"context": ...}.Fix
hooks.json— command is now justbash "${CLAUDE_PLUGIN_ROOT}/hooks/scribe_session_context.sh"(dropped the broken${user_config.*}assignments, which would also shadow the real env var).scribe_session_context.sh— reads the harness-exportedCLAUDE_PLUGIN_OPTION_api_endpoint/api_token/project_idenv vars (SCRIBE_*still override for the settings.json dogfooding path); added a guard that treats any unexpanded${...}literal as unset so the hook fails open cleanly instead of 401-ing.plugin.json— bump0.1.1→0.1.2so installs refresh the cache.Verification
bash -nclean./api/plugin/contextreturns 401 only on missing/invalid keys.Tracked on Scribe task 755 (Scribe-as-plugin, Phase 1).
🤖 Generated with Claude Code
The SessionStart push-channel hook passed the key via SCRIBE_TOKEN="${user_config.api_token}" in hooks.json, but api_token is sensitive:true. Claude Code keeps sensitive userConfig in the keychain and does not interpolate it into hook command strings (only into mcpServers headers), so the hook received the literal placeholder, sent it as the Bearer token, and the context endpoint 401'd -> fail-open -> no context injected. Read the harness-exported CLAUDE_PLUGIN_OPTION_<key> env vars instead (SCRIBE_* still override for the settings.json dogfooding path), and treat any unexpanded ${...} literal as unset so the hook fails open cleanly instead of 401-ing. Bump 0.1.1 -> 0.1.2 so installs refresh the cache. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>