Skip to content

RFC: Migrate Python hooks and MCP servers to Rust subcommands #351

@Viscosity4373

Description

@Viscosity4373

Summary

Currently all 6 Claude Code hooks and 2 MCP servers are Python scripts that get deployed to .claude/hooks/ and .claude/mcp/ during crosslink init. This works, but introduces a hard Python dependency with significant complexity. This issue proposes migrating them to crosslink hook <name> and crosslink mcp <name> subcommands in the main Rust binary.

Current pain points

  • ~200 lines of detect_python_prefix heuristics handling uv, poetry, .venv, pipenv, system python — fragile across environments
  • Silent failures: the hook shell wrappers (if [ -f "$HOOK" ]; then ... else exit 0; fi) swallow errors when Python isn't found or configured correctly
  • File drift: deployed .py files can diverge from embedded versions, requiring workflow diff machinery and crosslink init --force to update after upgrades
  • Startup latency: Python interpreter startup (~30-50ms) × 6 hooks adds up on every prompt/edit cycle
  • Zero test coverage on hook logic — separate Python test harness would be needed vs. integrating with existing cargo test

Proposed approach

Hooks → crosslink hook <name>

Replace settings.json entries like:

"command": "HOOK=\"$(git rev-parse --show-toplevel)/.claude/hooks/post-edit-check.py\"; if [ -f \"$HOOK\" ]; then __PYTHON_PREFIX__ \"$HOOK\"; else exit 0; fi"

with:

"command": "crosslink hook post-edit"

Each hook reads JSON from stdin and writes JSON to stdout — same interface, just handled by the Rust binary.

MCP servers → crosslink mcp <name>

Same idea: crosslink mcp safe-fetch and crosslink mcp knowledge as stdio JSON-RPC servers. Needs a Rust MCP implementation (or thin protocol layer).

Trade-offs

Python (status quo) Rust subcommands
Python dependency Required Eliminated
Startup latency ~30-50ms/hook ~1ms/hook
Deployment Copy files + template substitution Just write settings.json
Drift Possible, needs workflow diff Impossible — always matches binary
User customization Edit .py directly Via config/rules (already exists)
Testing Separate harness needed Integrated cargo test
Init complexity Python prefix detection, file copy Minimal
Iteration speed Edit .py, no rebuild Requires cargo build

Migration path

Could be incremental — migrate one hook at a time, starting with the simplest (e.g., heartbeat), while keeping Python fallback for the others. The crosslink init command already knows about all hooks, so it can generate the right settings.json entries based on what's been migrated.

Open questions

  • Is the iteration speed trade-off acceptable? (hooks change less frequently now that they're stable)
  • For MCP servers: use an existing Rust MCP crate, or roll a minimal stdio JSON-RPC layer?
  • Should we keep a "user override" mechanism (e.g., if .claude/hooks/post-edit-check.py exists, prefer it over the built-in)?

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions