diff options
| -rwxr-xr-x | link/claude.sh | 43 |
1 files changed, 42 insertions, 1 deletions
diff --git a/link/claude.sh b/link/claude.sh index b7b3f9e..56f3724 100755 --- a/link/claude.sh +++ b/link/claude.sh @@ -31,6 +31,21 @@ link "$config_dir/claude/CLAUDE.md" "$HOME/.claude/CLAUDE.md" # # Run by link.sh after other layers' link.sh, so their files are in place on a # first link. +# +# Two things a spec may carry beyond what Claude Code takes, both resolved here: +# +# "${szecret:NAME}" anywhere in a string value, replaced with `szecret get +# NAME` as the server is registered with Claude Code. For a +# remote server's Authorization header: Claude Code's own +# headersHelper is not run in a workspace without persisted +# trust, which includes $HOME, so a token fetched there +# would simply be missing. The secret ends up in +# ~/.claude.json, which Claude Code keeps mode 600. A +# secret that cannot be read skips that server, loudly. +# +# "desktop" a whole spec to give Claude Desktop instead of this one, +# e.g. a local stdio server where Claude Code gets a remote +# one. Claude Code never sees it. mcp_dir="$xdg_config_dir/claude/mcp-servers.d" mcp_manifest="${XDG_STATE_HOME:-$HOME/.local/state}/dotfiles/claude-mcp-servers" claude_desktop_dir="$HOME/Library/Application Support/Claude" @@ -105,9 +120,34 @@ then then echo "Will register MCP server (user scope): $name" else + if ! code_spec="$(python3 -c 'import json, re, subprocess, sys +spec = json.loads(sys.argv[1]) +spec.pop("desktop", None) + +def secret(match): + name = match.group(1) + try: + return subprocess.run(["szecret", "get", name], check=True, capture_output=True, text=True).stdout.strip() + except (OSError, subprocess.CalledProcessError): + sys.exit("szecret get %s failed" % name) + +def expand(value): + if isinstance(value, str): + return re.sub(r"\$\{szecret:([A-Za-z0-9._-]+)\}", secret, value) + if isinstance(value, dict): + return {k: expand(v) for k, v in value.items()} + if isinstance(value, list): + return [expand(v) for v in value] + return value + +print(json.dumps(expand(spec)))' "$spec" 2>&1)" + then + red "MCP server (user scope) not registered: $name: $code_spec" + continue + fi green "MCP server (user scope): $name" claude mcp remove --scope user "$name" >/dev/null 2>&1 || true - claude mcp add-json --scope user "$name" "$spec" >/dev/null + claude mcp add-json --scope user "$name" "$code_spec" >/dev/null fi done <<< "$mcp_wanted" fi @@ -149,6 +189,7 @@ for line in os.environ["MCP_WANTED"].splitlines(): continue name, spec = line.split("\t", 1) spec = json.loads(spec) + spec = spec.get("desktop", spec) command = spec.get("command") if command: if not os.path.isabs(command): |
