Labsco
jcastilloa logo

context-distill

β˜… 1

from jcastilloa

context-distill is an MCP server that compresses noisy command output into precise, actionable summaries for LLM workflows. Use distill_batch for large logs and distill_watch for cycle-to-cycle deltas. Built with Go, Cobra, Viper, and DI for reliable local and provider-backed distillation.

πŸ”₯πŸ”₯πŸ”₯πŸ”₯βœ“ VerifiedFreeAdvanced setup

context-distill

A Go tool that distills command output and retrieves code context before it reaches a paid LLM. Available as a Skill (recommended), a standalone CLI, and an MCP server. Inspired by the distill CLI and built with hexagonal architecture, dependency injection, and TDD.

Overview

context-distill exposes four operations accessible in three ways:

ModeBest forHow it works
Skill ⭐ (recommended)Any agent that can read markdown and run shell commandsThe agent reads a SKILL.md file from its own skills directory and learns when and how to invoke the CLI. Zero config on the agent side.
CLILocal scripts, CI pipelines, shell-capable agentsDirect subcommands: context-distill distill_batch, context-distill distill_mcp_output, context-distill distill_watch, context-distill search_code.
MCPAgents/clients with native MCP support (Claude Desktop, Cursor, Codex…)Runs as an MCP server over stdio transport.
OperationPurpose
distill_batchCompresses full command output to answer a single, explicit question.
distill_mcp_outputDistills raw MCP payloads, or calls an MCP tool and distills its result in one step.
distill_watchCompares two consecutive snapshots and returns only the relevant delta.
search_codeLocates relevant repository code and returns compact matches for the next reasoning step.

All three modes share the same underlying use cases, validation rules, and output behavior. Only the invocation method differs.

It also provides:

  • LLM provider configuration via YAML and environment variables.
  • An interactive terminal UI for first-time setup (--config-ui).
  • Support for Ollama and any OpenAI-compatible provider.
SkillCLIMCP
Agent config requiredNone β€” just drop SKILL.md in the agent's skills directoryAgent must know how to run shell commandsRegister server in client config
Works across agentsβœ… Any agent that reads markdownβœ… Any agent that runs shell⚠️ Only MCP-compatible clients
Setup complexityCopy one file per agentInstall binaryInstall binary + register transport
PortabilityWorks in any repoWorks in any shellTied to MCP client config

Skill mode works because modern coding agents (Codex, Claude Code, Cursor, Aider, OpenCode…) already know how to read project documentation and execute shell commands. A SKILL.md file teaches the agent when to distill or retrieve code context and how to call the CLI β€” no protocol integration needed.

Features

  • Triple interface β€” Skill file for zero-config agent adoption + CLI for direct shell use + MCP tools for protocol-native clients.
  • Four core operations β€” distill_batch, distill_mcp_output, distill_watch, and search_code.
  • Hexagonal architecture β€” distill/domain, distill/application, platform/*.
  • Dependency injection via sarulabs/di.
  • Config management with viper + .env.
  • Provider-specific validation at config time.
  • Interactive setup UI (--config-ui).
  • Unit, integration, and optional live tests.

Makefile Targets

make help
TargetDescription
make buildBuild the binary to ./bin/context-distill
make installInstall binary to ~/.local/bin/context-distill
make cleanRemove ./bin

CLI Commands Reference

The CLI commands provide the same capabilities as the MCP tools (distillation + code retrieval) but are invoked directly from the shell. Use them in local scripts, CI pipelines, or with agent runtimes that execute shell commands instead of MCP tools.

Input methods

# 1. Pipe (preferred)
echo "data" | context-distill distill_batch --question "..."

# 2. Explicit flag
context-distill distill_batch --question "..." --input "data"

# 3. Explicit stdin marker
echo "data" | context-distill distill_batch --question "..." --input -

distill_batch

Distills one raw output payload using an explicit question contract.

go test ./... 2>&1 | context-distill distill_batch --question "Did all tests pass? Return only PASS or FAIL."

Flags:

FlagRequiredDescription
--questionyesExact question to answer from the command output.
--inputnoRaw command output to distill. If omitted, reads from stdin.

distill_mcp_output

Distills MCP results in either of these modes:

  1. You already have a raw MCP payload and pass it with --output.
  2. You want context-distill to call an MCP tool first and then distill the result.
context-distill distill_mcp_output \
  --tool-name "list_items" \
  --question "Return only item names, one per line." \
  --output '<mcp payload>'
context-distill distill_mcp_output \
  --server-command "/absolute/path/to/mcp-server" \
  --server-arg "--transport" \
  --server-arg "stdio" \
  --tool-name "get_status" \
  --tool-arguments '{"scope":"production"}' \
  --question "Return only status and version as JSON."

Flags:

FlagRequiredDescription
--questionyesExact question to answer from the MCP result.
--tool-nameno*Helpful context when using --output; required when invoking a server.
--outputno*Raw MCP payload to distill directly.
--server-commandno*MCP server binary to invoke when --output is omitted.
--server-argnoMCP server argument. Repeat for multiple values.
--tool-argumentsnoJSON object passed to the target MCP tool.

* You must provide either --output or a server invocation (--server-command + --tool-name).

distill_watch

Distills only the relevant delta between two snapshots.

context-distill distill_watch \
  --question "Return only newly failing services, one per line." \
  --previous-cycle "$(cat /tmp/health.prev)" \
  --current-cycle "$(cat /tmp/health.curr)"

Flags:

FlagRequiredDescription
--questionyesExact question to answer from cycle changes.
--previous-cycleyesPrevious watch cycle output snapshot.
--current-cycleyesCurrent watch cycle output snapshot.

search_code

Searches repository code locally and distills compact matches according to --question.

context-distill search_code \
  --query "distill_watch" \
  --mode symbol \
  --question "Return definitions first, then usages, as file:line."

Important:

  • CLI syntax uses flags. Use --query, --mode, --question, --max-results, --context-lines.
  • Do not use shell arguments like search_code mode=text query="..." max_results=5 (that format is not valid CLI syntax).
  • For --mode path, treat --query as a path fragment (for example, .go), and use --scope for glob filters.

Flags:

FlagRequiredDescription
--queryyesSearch query for text, regex, symbol name, or path fragment.
--modeyesSearch mode: text, regex, symbol, or path.
--questionyesOutput contract for final compact result.
--scopenoOptional glob filters (repeat flag or comma-separated).
--max-resultsnoHard limit for returned candidates (default 20).
--context-linesnoContext lines around each match (default 2).

CLI Notes

  • CLI commands and MCP tools share the same underlying use cases and validation rules.
  • Invalid/missing inputs return a non-zero exit code.
  • Output is written to standard output exactly as produced by the selected use case.

MCP Server Mode

Running the server

context-distill --transport stdio
# or without building:
go run ./cmd/server --transport stdio

Version

go run ./cmd/server version

Server Flags

FlagDescriptionDefault
--transportMCP transport mode (stdio)service.transport
--config-uiOpen setup UI and exitfalse

MCP Client Registration

After building or installing the binary, register it in your MCP client to use the tool interface.

JSON-based clients (Claude Desktop, Cursor, etc.)

{
  "mcpServers": {
    "context-distill": {
      "command": "/absolute/path/to/context-distill",
      "args": ["--transport", "stdio"]
    }
  }
}

Add to ~/.codex/config.toml:

[mcp_servers.context-distill]
command = "/absolute/path/to/context-distill"
args = ["--transport", "stdio"]
startup_timeout_sec = 20.0

If you used make install:

[mcp_servers.context-distill]
command = "/home/<your-user>/.local/bin/context-distill"
args = ["--transport", "stdio"]
startup_timeout_sec = 20.0

Codex β€” CLI registration

codex mcp add context-distill -- /absolute/path/to/context-distill --transport stdio

Verify:

codex mcp list
codex mcp get context-distill

Restart your Codex session so it picks up the new server.

OpenCode β€” interactive CLI

opencode mcp add

Follow the prompts:

  1. Location β†’ Current project or Global.
  2. Name β†’ context-distill.
  3. Type β†’ local.
  4. Command β†’ /absolute/path/to/context-distill --transport stdio.

Verify:

opencode mcp list

If the server is not connected yet, restart your OpenCode session.

OpenCode β€” manual config (opencode.json)

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "context-distill": {
      "type": "local",
      "command": ["/absolute/path/to/context-distill", "--transport", "stdio"],
      "enabled": true
    }
  }
}

Registration notes

  • Always use an absolute binary path.
  • Always use stdio transport.
  • If the server does not appear, run codex mcp list --json to inspect the resolved config.

MCP Tools Reference

The MCP tools expose the same capabilities as the CLI commands (distillation + code retrieval), but are consumed by MCP-compatible clients over the stdio transport.

distill_batch

ParameterTypeRequiredDescription
questionstringyesWhat to extract from the input. Must include an output contract.
inputstringyesRaw command output to distill.

Returns a short, focused answer to question.

distill_watch

ParameterTypeRequiredDescription
questionstringyesWhat delta to report. Must include an output contract.
previous_cyclestringyesSnapshot from the previous cycle.
current_cyclestringyesSnapshot from the current cycle.

Returns a short summary of relevant changes, or a no-change message when nothing meaningful differs.

search_code

ParameterTypeRequiredDescription
querystringyesSearch query for text, regex, symbol, or path.
modestringyesSearch mode: text, regex, symbol, path.
questionstringyesOutput contract for distilled response.
scopearray[string]noOptional glob scope filters.
max_resultsnumbernoHard match limit (default 20).
context_linesnumbernoContext lines per match (default 2).

Returns compact output controlled by question, after local repository retrieval.

Notes:

  • This section documents MCP tool payload fields (snake_case), not shell flags.
  • CLI equivalents are --query, --mode, --question, --scope, --max-results, --context-lines.

Provider Matrix

ProviderTransportAPI Key RequiredDefault Base URL
ollamanative ollamaNohttp://127.0.0.1:11434
openaiopenai-compatibleYeshttps://api.openai.com/v1
openrouteropenai-compatibleYeshttps://openrouter.ai/api/v1
openai-compatibleopenai-compatibleNo (backend-dependent)β€”
lmstudioopenai-compatibleNohttp://127.0.0.1:1234/v1
janopenai-compatibleYeshttp://127.0.0.1:1337/v1
localaiopenai-compatibleNohttp://127.0.0.1:8080/v1
vllmopenai-compatibleNohttp://127.0.0.1:8000/v1
sglangopenai-compatibleNoβ€”
llama.cppopenai-compatibleNoβ€”
mlx-lmopenai-compatibleNoβ€”
docker-model-runneropenai-compatibleNohttp://127.0.0.1:12434/engines/v1

Writing Good Questions

The quality of distillation/search output depends on the question contract β€” whether invoked via Skill, CLI, or MCP. Be explicit about what you want and in what format.

Bad questions

  • "What happened?"
  • "Summarize this"

Good questions

  • "Did tests pass? Return only PASS or FAIL. If FAIL, list failing test names, one per line."
  • "List only changed file paths, one per line."
  • "Return valid JSON only with keys: severity, file, message."

distill_batch examples

SourceQuestion
go test ./..."Did tests pass? Return only PASS or FAIL."
git diff"List changed files and one short reason per file. Max 10 lines."
CI logs"Return only blocking errors with file and line if available."

distill_watch examples

SnapshotsQuestion
Test watcher output at Tβˆ’1 / T"What changed in failure count? Return one short sentence."
Deployment status at Tβˆ’1 / T"Return only newly failing services, one per line."

search_code examples

ModeQuestion
text"Return only file:line, one per line."
symbol"Return likely definitions first, then usages, as file:line."
path"Return matching file paths only, one per line."

AGENTS.md Templates

If you prefer to embed instructions directly in your project's AGENTS.md, use the standalone files instead of copying big blocks out of this README:

Suggested policy one-liner

Drop this into your project docs for a quick reference:

Default policy: use `context-distill` (via Skill, CLI, or MCP) to distill command output and run `search_code` for repository localization before sending data to an LLM, unless raw output is explicitly required.

Project Structure

context-distill/
β”œβ”€β”€ cmd/
β”‚   └── server/
β”‚       β”œβ”€β”€ main.go
β”‚       β”œβ”€β”€ bootstrap.go
β”‚       └── openai_distill_config.go
β”œβ”€β”€ distill/
β”‚   β”œβ”€β”€ application/
β”‚   β”‚   └── distillation/
β”‚   └── domain/
β”œβ”€β”€ mcp/
β”‚   β”œβ”€β”€ application/
β”‚   └── domain/
β”œβ”€β”€ platform/
β”‚   β”œβ”€β”€ config/
β”‚   β”œβ”€β”€ configui/
β”‚   β”œβ”€β”€ di/
β”‚   β”œβ”€β”€ mcp/
β”‚   β”‚   β”œβ”€β”€ commands/
β”‚   β”‚   β”œβ”€β”€ server/
β”‚   β”‚   └── tools/
β”‚   β”œβ”€β”€ ollama/
β”‚   └── openai/
β”œβ”€β”€ shared/
β”‚   β”œβ”€β”€ ai/domain/
β”‚   └── config/domain/
β”œβ”€β”€ SKILL.md
β”œβ”€β”€ config.sample.yaml
β”œβ”€β”€ config.yaml
β”œβ”€β”€ Makefile
└── AGENTS.md

Architecture

Dependency rule:

platform  β†’  shared + distill/application + distill/domain
distill/application  β†’  distill/domain
cmd  β†’  platform + shared

Constraint: shared and distill/domain must never import platform.

Development

Tests and static checks

go test ./...
go vet ./...

Suggested local workflow

  1. go test ./...
  2. context-distill --config-ui
  3. Verify CLI works:
    echo "hello world" | context-distill distill_batch --question "Return the input verbatim."
    context-distill search_code --query "provider_name" --mode text --question "Return only file:line, one per line."
  4. (Optional) Start MCP server: context-distill --transport stdio
  5. Validate behavior from your MCP client or via CLI commands.

Optional live test (real provider)

DISTILL_LIVE_TEST=1 OPENAI_BASE_URL=https://openrouter.ai/api/v1 \
go test -tags=live ./platform/di -run TestLiveDistillBatchWithOpenAICompatibleProvider -v

Security

  • Never commit real API keys to public repositories.
  • Prefer environment-based secrets in shared or CI environments.