Client integration

Install the client you use:

bash
spill install codex
spill install claude
spill install cursor

claude-code is accepted as an alias. These commands configure local CLI clients — not Claude Desktop. All clients share the same ~/.spill/spill.duckdb database. Restart your client after installation and approve any MCP/hook trust prompts.

Configuration ownership

The installer backs up changed files and tracks ownership separately per client. It refuses conflicting entries and only removes entries it added during uninstall:

ClientMCP configHook configResult replacement
Cursor ~/.cursor/mcp.json ~/.cursor/hooks.json postToolUse.updated_mcp_tool_output
Codex ~/.codex/config.toml ~/.codex/hooks.json continue: false, descriptor in stopReason
Claude Code ~/.claude.json ~/.claude/settings.json hookSpecificOutput.updatedMCPToolOutput

Codex

Codex registers [mcp_servers.spill] in ~/.codex/config.toml and appends a matching hook group to ~/.codex/hooks.json. Existing TOML comments and inline hook definitions remain intact.

Codex does not support updatedMCPToolOutput. The adapter uses continue: false with the dataset descriptor in stopReason — the model receives hook feedback in place of the raw result. This requires a Codex version implementing that PostToolUse behavior.

CODEX_HOME overrides the config directory. Paths must be absolute. Install and uninstall using the same directory.

Claude Code

Claude Code registers a user-scope mcpServers.spill in ~/.claude.json and a PostToolUse hook in ~/.claude/settings.json. Project-specific servers, permissions, and other settings remain intact.

The adapter uses hookSpecificOutput.updatedMCPToolOutput — this field works on versions predating the newer all-tool updatedToolOutput. If CLAUDE_CONFIG_DIR is set, the files are <dir>/.claude.json and <dir>/settings.json.

Cursor

Cursor registers the MCP server in ~/.cursor/mcp.json and a PostToolUse hook in ~/.cursor/hooks.json using the documented postToolUse replacement contract: tool_output, tool_use_id, the MCP matcher, and updated_mcp_tool_output.

Manual acceptance check

The automated tests exercise the documented contract but cannot inspect the client's internal context assembly. This check confirms the original result is excluded from model context on your specific build.

  1. Build/install Spill and run the installer for your client.
  2. Register the demo MCP fixture, substituting an absolute path:
    bash
    codex mcp add spill-demo -- python3 /absolute/path/to/spill/scripts/demo_mcp.py
    # or:
    claude mcp add --scope user spill-demo -- python3 /absolute/path/to/spill/scripts/demo_mcp.py
  3. Restart the client. Confirm Spill's query, list, and describe tools are available.
  4. Ask it to call spill-demo.list_issues then count issues by state with Spill SQL.
  5. Inspect the tool output — a compact descriptor with 2,000 rows should replace the full array. Expected counts: OPEN 1,000, CLOSED 1,000.
  6. Confirm with spill list, spill describe <dataset>, and spill sql 'SELECT count(*) FROM <dataset>'.
  7. Call spill-demo.small_result and confirm pass-through.
  8. Run spill uninstall cursor (or your client) and confirm unrelated config is preserved.
  9. Remove the demo server: codex mcp remove spill-demo or claude mcp remove --scope user spill-demo.

Homebrew tap

This repository doubles as a custom Homebrew tap through its Formula/ directory. No separate tap repo is needed:

bash
brew tap spill-ai/spill https://github.com/spill-ai/spill
brew trust --formula spill-ai/spill/spill  # Homebrew 6+ only
brew install spill
spill install cursor

A bare brew install spill without the tap requires a future submission to homebrew/core. The explicit URL is necessary because Homebrew's default shorthand would look for spill-ai/homebrew-spill.

Stable builds

Formula/spill.rb pins a tagged release tarball and SHA256. Homebrew builds with cargo install --locked, including bundled DuckDB. There are no prebuilt bottles yet.

To publish an update:

  1. Run formatting, Clippy, Rust tests, and scripts/smoke.py.
  2. Update Cargo.toml version and commit.
  3. Tag the release: git tag v0.2.0 <sha> && git push origin v0.2.0
  4. Get the tarball SHA: curl -sL https://github.com/spill-ai/spill/archive/refs/tags/v0.2.0.tar.gz | shasum -a 256
  5. Update url, sha256, and version in Formula/spill.rb.
  6. Commit the formula change and push. Users run brew update && brew upgrade spill.

Local verification

bash
brew tap spill-ai/spill https://github.com/spill-ai/spill
brew install --build-from-source spill-ai/spill/spill
brew test spill-ai/spill/spill

The formula test uses Homebrew's temporary test home. It checks installation, spilling a large result, SQL access to saved rows, and uninstall. It does not modify your actual client configuration.

Cursor acceptance demo

This manual check is required to confirm the original result stays out of model context — automated tests exercise the documented contract but cannot inspect Cursor's context assembly.

  1. Build with cargo install --path ., then run spill install cursor.
  2. Add the demo server to ~/.cursor/mcp.json:
    json
    {
      "mcpServers": {
        "spill-demo": {
          "command": "python3",
          "args": ["/absolute/path/to/spill/scripts/demo_mcp.py"]
        }
      }
    }
  3. In Cursor, verify both spill and spill-demo are connected.
  4. Ask: "Use spill-demo list_issues, then count issues by state using Spill SQL."
  5. Inspect tool output — it should contain a dataset descriptor, not 2,000 raw objects. Expected aggregate: OPEN 1,000, CLOSED 1,000.
  6. Run spill list, spill describe <dataset>, and the count query in a terminal.
  7. Ask Cursor to call spill-demo small_result — the single object should pass through unchanged.
  8. Run spill uninstall cursor and confirm unrelated servers and hooks are preserved.
  9. Remove the demo entry from ~/.cursor/mcp.json.

Development commands

bash
cargo fmt --all -- --check
cargo clippy --all-targets -- -D warnings
cargo test
cargo build
python3 scripts/smoke.py target/debug/spill

CI runs on Linux and macOS. The smoke test exercises the real binary, stdio MCP transport, and hook path in a temporary directory without touching your actual client configuration.