Metadata-Version: 2.5
Name: redacted-context-mcp
Version: 0.7.1
Summary: Read-only-by-default redacted local knowledgebase context over MCP for coding agents.
Project-URL: Homepage, https://github.com/zoltan0803/redacted-context-mcp
Project-URL: Repository, https://github.com/zoltan0803/redacted-context-mcp
Project-URL: Issues, https://github.com/zoltan0803/redacted-context-mcp/issues
Project-URL: Changelog, https://github.com/zoltan0803/redacted-context-mcp/blob/main/CHANGELOG.md
Project-URL: Documentation, https://github.com/zoltan0803/redacted-context-mcp/blob/main/SETUP.md
Project-URL: Security, https://github.com/zoltan0803/redacted-context-mcp/blob/main/SECURITY.md
Author: Zoltan Nagy
License-Expression: MIT
License-File: LICENSE
Keywords: claude-code,coding-agents,filesystem,mcp,ollama,pii,privacy,redaction
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Security
Classifier: Topic :: Software Development
Requires-Python: >=3.11
Provides-Extra: dev
Requires-Dist: build>=1.2; extra == 'dev'
Requires-Dist: twine>=5; extra == 'dev'
Description-Content-Type: text/markdown

# Redacted Context MCP

Give coding agents useful local project context without exposing raw client
names, people, email addresses, URLs, phone numbers, secrets, or meaningful
filenames.

`redacted-context-mcp` is a read-only-by-default MCP server and CLI. It lets an
agent search, navigate, and read useful content from a private local folder
while replacing sensitive text and returning opaque file references.

Before redaction:

```text
Client Example Lantern Labs uses production-db.internal.example
Contact avery@example.com about PROJECT-LANTERN-042.
```

Agent-visible result:

```text
Client [ORG_a81f29d4a9c1e672540f68afc10d22c7] uses [DOMAIN_d12c88e1f730065c97d3f82f06d1188c].
Contact [EMAIL_711ae704108cd6e952dcb27f0d6e999a] about [SENSITIVE_45f80ab22bc94e105a93aa830c7d3b9c].
```

The placeholder values above are illustrative. Real values are deterministic
for one local vault salt and will differ.

## Quick Start

Python 3.11 or newer is required. Install the commands with `pipx`, then use a
local Ollama model to draft the project-specific redaction terms:

```sh
pipx install redacted-context-mcp
ollama pull gemma4:e4b
redctx --root ~/private-context discover \
  --model gemma4:e4b \
  --output .agent-context-redactor.toml
```

Review the generated `.agent-context-redactor.toml` because it intentionally
contains the raw names and terms that should be hidden. Then audit the setup
and start the stdio MCP server:

```sh
redctx --root ~/private-context audit
redctx-mcp --root ~/private-context
```

Discovery is explicit: `audit` does not call a model or generate this config.
Without an explicit config, the built-in detectors still cover common emails,
URLs, phone numbers, domains, secrets, and some names, but project-specific
client names and codenames may be missed. To avoid Ollama, create the config
manually using the [Local Redaction Config](#local-redaction-config) example.

The server waits for an MCP client on standard input; press Ctrl-C if you start
it directly in a terminal. For a no-credentials walkthrough using fictional
data, see the [self-contained quick-start demo](examples/quickstart/README.md).

### Claude Code MCP Configuration

Claude Code is one of the clients already supported by this repository. Put
the following in the agent workspace's `.mcp.json`, replacing the root with an
absolute path to the private context folder:

```json
{
  "mcpServers": {
    "redacted_context": {
      "type": "stdio",
      "command": "redctx-mcp",
      "args": [
        "--root",
        "/absolute/path/to/private-context"
      ]
    }
  }
}
```

The same installed `redctx-mcp` command can be used with the Codex and generic
stdio configurations documented below.

## Security Boundary

This project provides practical privacy guardrails, not guaranteed
anonymization, sandboxing, cryptographic isolation, complete DLP, or perfect
prevention of metadata leakage. File sizes, line counts, and timing remain
possible metadata side channels.

The protection can be bypassed if the coding agent can also read the
unredacted source directory through shell commands or other filesystem tools.
For hard enforcement, run the agent as a separate OS user or in a container
that cannot access that directory directly, and expose only the MCP server or a
separate redaction service.

Read [SECURITY.md](SECURITY.md) for the threat model and
[SECURITY_INVARIANTS.md](SECURITY_INVARIANTS.md) for the behavior the test suite
is intended to preserve.

The core workflow is:

```text
agent workspace
  -> redacted MCP tools
    -> private source folder
      -> redacted output with opaque @p_<id> file references
```

## Features

- Dual-era MCP stdio server supporting stateless `2026-07-28` clients and
  legacy initialization-based clients through `2025-11-25`.
- Redacted MCP resources using `redctx://p_<id>` URIs.
- Optional MCP `redctx_submit_doc` tool for controlled writes of generated
  redacted documents back into a configured private-root subdirectory.
- CLI fallback with the same redaction behavior.
- Local-salted opaque stable path ids such as `@p_1a2b3c4d5e6f`.
- Deterministic 128-bit HMAC placeholders such as
  `[PERSON_1a2b3c4d5e6f7890a1b2c3d4e5f60718]`.
- Bounded operation budgets for traversals, reads, bundles, searches, audits,
  benchmarks, discovery samples, MCP resource listing/reads, and controlled
  write rehydration scans.
- Redacted `tree`, `list`, `read`, `search`, `stat`, `bundle`, `audit`, and
  `benchmark` operations.
- CLI-only `rehydrate` command for restoring redacted exports locally from the
  private source root.
- Local ignored redaction config for exact client, person, organization, and
  project terms.
- Optional local-LLM discovery command to draft that config from private files
  without sending content to Claude or a hosted model.
- No runtime Python dependencies.
- Works well with a neutral agent workspace that does not contain raw context
  files.

## Who This Is For

Use this when you want an agent to reason over a private local folder without
handing the model the raw names and identifiers in that folder.

Good fits:

- consulting or client delivery knowledgebases;
- internal project notes, stakeholder notes, and transcripts;
- architecture or governance documentation with private names mixed in;
- private GitHub issues that should be summarized through neutral aliases.

Do not treat this as a formal anonymization or data-loss-prevention system.

## Installation Options

The recommended installation method is `pipx`:

```sh
pipx install redacted-context-mcp
```

Regular `pip` installation is also supported:

```sh
python -m pip install redacted-context-mcp
```

Install from a source checkout only for development or to test an unreleased
version:

```sh
python -m pip install -e .
```

This installs two console commands:

```sh
redctx      # CLI
redctx-mcp  # MCP stdio server
```

For `redctx discover`, install [Ollama](https://ollama.com/) separately and
pull a local model such as `gemma4:e4b`. The core redacted CLI and MCP server do
not require Ollama.

Model tags must match Ollama exactly. Check installed tags with `ollama list`
and pass the full value shown in the `NAME` column to `--model`.

## Recommended Layout

Use two sibling folders under a neutral parent:

```text
/work/
  agent-workdir/    # Claude Code starts here; no raw context files
  source-private/   # private project/context repository
```

The agent starts in `agent-workdir/`. The MCP server reads
`source-private/`, redacts output, and returns only redacted text.

Keep the private folder outside the active agent workspace when possible. If
the agent can still run shell commands against the raw private folder, the MCP
redaction layer is only an instruction-level guardrail, not a hard boundary.

## Claude Code MCP Config

If `redctx-mcp` is installed, put this in `agent-workdir/.mcp.json`:

```json
{
  "mcpServers": {
    "redacted_context": {
      "type": "stdio",
      "command": "redctx-mcp",
      "args": [
        "--root",
        "../source-private"
      ]
    }
  }
}
```

If running directly from a source checkout without installing:

```json
{
  "mcpServers": {
    "redacted_context": {
      "type": "stdio",
      "command": "python3",
      "args": [
        "../redacted-context-mcp/src/redacted_context_mcp/server.py",
        "--root",
        "../source-private"
      ]
    }
  }
}
```

Then start Claude Code from the agent workspace:

```sh
cd /work/agent-workdir
claude
```

If Claude Code was already running, restart it or reconnect MCP servers with
`/mcp`.

For persistent Claude Code guidance, copy `examples/agent-CLAUDE.md` into
`agent-workdir/CLAUDE.md`.

## Codex MCP Config

Codex supports local stdio MCP servers through `config.toml`. Put this in
`~/.codex/config.toml`, or in `agent-workdir/.codex/config.toml` for a trusted
project-scoped setup:

```toml
[mcp_servers.redacted_context]
command = "redctx-mcp"
args = ["--root", "../source-private"]
enabled = true
required = true
```

If running directly from a source checkout without installing:

```toml
[mcp_servers.redacted_context]
command = "python3"
args = [
  "../redacted-context-mcp/src/redacted_context_mcp/server.py",
  "--root",
  "../source-private",
]
enabled = true
required = true
```

For persistent Codex guidance, copy `examples/agent-AGENTS.md` into
`agent-workdir/AGENTS.md`. Codex reads `AGENTS.md` when a session starts, so
restart Codex after adding or changing it.

## Generic MCP Clients

Any MCP client that can launch a stdio server can run:

```sh
redctx-mcp --root /absolute/path/to/source-private
```

Use the client-specific configuration format to pass that command and args.
The server advertises instructions and exposes only redacted `redctx_*` tools.
Modern clients can use the stateless MCP `2026-07-28` flow with per-request
metadata and `server/discover`; legacy clients continue to negotiate through
`initialize`.

## MCP Tools

The server exposes:

- `redctx_tree` — show a redacted file tree with opaque ids
- `redctx_list` — list redacted directory entries
- `redctx_read` — read redacted file contents by path or `@p_<id>`
- `redctx_search` — search redacted text
- `redctx_stat` — inspect redacted metadata
- `redctx_bundle` — concatenate redacted context files
- `redctx_doctor` — show config counts without sensitive terms
- `redctx_audit` — run local containment and redaction checks
- `redctx_refresh_index` — refresh the in-memory opaque path index

Agents should carry `@p_<id>` references between calls rather than using raw
filenames.

The MCP server also exposes redacted text files as resources:

- `resources/list` returns `redctx://p_<id>` resource URIs with redacted titles.
- `resources/read` returns redacted file text for those opaque resource URIs.

### Controlled MCP Writes

By default, the MCP server exposes only read-only tools. To let an agent submit
new redacted documents back into the private source root, start the server with
an explicit write subdirectory:

```sh
redctx-mcp --root ../source-private --enable-writes --write-subdir incoming
```

This adds `redctx_submit_doc`. The tool accepts a relative `target_path`,
redacted `text`, and optional `overwrite`. The server rehydrates known
placeholders locally, rejects unresolved redaction tokens, and writes only under
the configured write subdirectory. Tool responses use redacted paths and opaque
ids; they do not return the raw restored path.

## CLI Fallback

The CLI is useful for smoke tests or clients without MCP:

```sh
redctx --root ../source-private doctor
redctx --root ../source-private tree context --max-depth 2
redctx --root ../source-private search "governance" context --ignore-case --context 2
redctx --root ../source-private read @p_1a2b3c4d5e6f --start-line 1 --end-line 80
redctx --root ../source-private bundle context --glob "*.md" --max-files 10
redctx --root ../source-private audit --format json
redctx --root ../source-private benchmark --format json
```

### Local Rehydration

The `rehydrate` command restores redacted text by scanning the private source
root with the same salt and config, rebuilding the placeholder map, and applying
it to a redacted file or folder. This emits raw private text, so it is CLI-only
and requires an explicit acknowledgement flag.

```sh
redctx --root ../source-private rehydrate ./redacted-output.md --allow-raw-output > raw-output.md
redctx --root ../source-private rehydrate ./redacted-folder \
  --output ./raw-folder \
  --allow-raw-output
```

Rehydration is not cryptographic reversal. A redacted file alone is not enough;
the command needs access to the original private root or equivalent local
source material to rebuild the mapping.

## Local Redaction Config

Create `.agent-context-redactor.toml` in the private source root. This file is
ignored by the example `.gitignore` because it may contain exact sensitive
terms.

```toml
[redaction]
salt = "local-random-string-kept-private"
clients = ["Client Legal Name", "Client Acronym"]
organizations = ["Supplier Name", "Partner Company"]
people = ["Person One", "Person Two"]
terms = ["project codename", "internal programme name"]
allow = ["Azure", "PostgreSQL", "Kubernetes"]
term_files = ["private-redaction-terms.txt"]

[github.repos.context]
owner = "private-org-or-user"
repo = "private-context-repo"
token_env = "GITHUB_TOKEN"
```

The tool also derives likely aliases from the private source folder name and
accepts additional comma- or newline-separated terms through
`REDACTED_CONTEXT_TERMS`.

The optional `salt` controls opaque path ids and deterministic placeholders.
If omitted, `redctx` creates or reuses a random 256-bit vault salt in user-local
state. Empty, malformed, or root-contained salt state fails closed instead of
silently rotating aliases. You can also set `REDACTED_CONTEXT_SALT` in the
environment that starts `redctx` or `redctx-mcp`. `redctx doctor` reports
whether the active salt came from local state, config, or environment.

GitHub repo entries are optional. Use neutral aliases such as `context`; agents
use the alias, while the real `owner/repo` stays in this local config. Private
repos require the named token environment variable in the shell that starts
`redctx` or `redctx-mcp`.

## Redacted GitHub Issues

Configured GitHub issues can be read through the same redaction layer:

```sh
export GITHUB_TOKEN="<github-token>"
redctx --root ../source-private github repos
redctx --root ../source-private github issues context --state open --limit 20
redctx --root ../source-private github issue context 123 --comments
redctx --root ../source-private github search context "policy controls"
```

The MCP server exposes the same flow with:

- `redctx_github_repos`
- `redctx_github_list_issues`
- `redctx_github_read_issue`
- `redctx_github_search_issues`

Outputs redact titles, bodies, labels, and comments, and mark GitHub text as
untrusted external content. Raw author logins and raw GitHub URLs are not
printed; authors are shown as stable per-vault, per-repo opaque ids.

## Discover Terms With A Local LLM

`redctx discover` can draft `.agent-context-redactor.toml` using a local
Ollama model. This is a human setup command, not an MCP tool, because its output
intentionally contains the raw names you want to redact.

Example with a small local model:

```sh
ollama pull gemma4:e4b
redctx --root ../source-private discover context progress archive \
  --model gemma4:e4b \
  --glob "*.md" \
  --output .agent-context-redactor.toml
```

If you switch models, use the exact tag from `ollama list`.

Review the generated file before use. To avoid overwriting an existing config,
the command refuses to write over `--output` unless `--force` is passed.

Discovery output is post-processed with generic cleanup rules. The cleanup
does not include project-specific names; it only:

- omits public/default-allowed terms that the redactor already allows;
- moves other likely tool/package names to `allow`;
- drops obvious filenames, meeting/ticket IDs, country-only values, job titles,
  and generic workflow/process labels;
- strips role notes from full names such as `Alice Example (CIO)`;
- ignores single first names by default because they over-redact.

Use `--raw-discovery` if you want the local model's categories with only basic
dedupe.

Useful options:

```sh
redctx --root ../source-private discover --help
redctx --root ../source-private discover context --format json
redctx --root ../source-private discover context --raw-discovery
redctx --root ../source-private discover context --max-files 20 --max-chars-per-file 8000
redctx --root ../source-private discover context --endpoint http://localhost:11434
```

The command uses Ollama's local `/api/generate` endpoint with streaming disabled
and JSON output requested. No hosted LLM is called by this feature. Non-loopback
plain-http endpoints are refused unless you pass `--allow-remote-endpoint`,
because discovery payloads contain raw private text.

### Automate Incremental Config Updates

Repository hooks can classify exact staged Git blobs without duplicating the
MCP's discovery and merge policy. Supply one JSON object per line:

```json
{"path":"private/meeting.md","text":"raw staged document text","sha256":"optional-source-digest"}
```

Then call the hook-facing CLI:

```sh
redctx --root ../source-private discover-update \
  --input-jsonl /tmp/staged-documents.jsonl \
  --seed-config config/redaction-seed.toml \
  --output-config .agent-context-redactor.toml \
  --model gemma4:e4b
```

`discover-update` sends each complete document to the configured local Ollama
endpoint in a separate request. It rejects model values that are not exact
substrings of that document, monotonically adds sensitive terms, keeps reviewed
seed policy settings authoritative, preserves unrelated TOML tables and
comments, and writes atomically. By default, model output cannot expand the
allow-list.

Documents are never silently truncated. A document over
`--max-chars-per-document`, or an input set over `--max-total-chars`, fails
before the model is called. Set those limits to fit the selected model's actual
context window. `--merge-only` applies a reviewed seed change without reading
documents or calling Ollama.

The equivalent Python composition API is:

```python
from redacted_context_mcp import (
    DiscoveryDocument,
    build_discovery_update,
    discover_documents,
    write_discovery_update,
)
from redacted_context_mcp.discovery import OllamaDiscoveryClient

documents = [DiscoveryDocument(path="private/meeting.md", text=raw_text)]
client = OllamaDiscoveryClient(
    endpoint="http://127.0.0.1:11434",
    model="gemma4:e4b",
    timeout=120,
)
discovery = discover_documents(documents, client=client)
update = build_discovery_update(existing_toml, discovery, seed_text=seed_toml)
write_discovery_update(config_path, update)
```

Both interfaces intentionally handle raw private text and raw discovered names.
Keep them local and outside an agent's accessible workspace. This feature
reduces what a separate coding model receives; it is not encryption, DLP, or a
proof that the local model found every sensitive entity.

## Claude Code Permissions

MCP routing is the main workflow. Claude Code permissions can add guardrails by
denying direct reads/searches into the private source folder and allowing only
the redacted MCP tools. See
[examples/claude-settings.example.json](examples/claude-settings.example.json).

## Security Model

This project is a practical privacy guardrail, not a formal de-identification
system.

It helps because:

- the agent starts in a neutral folder with no raw context files;
- the useful operations are exposed as redacted MCP tools;
- filenames can be navigated through opaque ids;
- raw names, emails, URLs, phones, and configured terms are redacted.

The `rehydrate` command intentionally reverses redacted exports for the local
operator. `redctx_submit_doc` can also rehydrate generated redacted text, but
only when MCP writes are explicitly enabled and only into the configured write
subdirectory. Submitted content is verified to redact consistently on
read-back, and the write subdirectory itself is never used as a rehydration
source. Do not run rehydration workflows from an agent workspace where the
model can read raw output.

Additional guardrails:

- The redaction config (default or explicit `--config`), configured term
  files, `.env*`, `*.key`, `*.pem`, and `*.crt` files are never served through
  redacted tools, even with `--include-private`, with case-folded matching so
  `.ENV` and `server.PEM` variants are refused too.
- Bare long hex strings (the vault-salt shape), salt-keyed assignments, and
  underscore-qualified secrets such as `DB_PASSWORD=...` are redacted by
  default.
- MCP searches enforce an operation deadline, and user-supplied regexes are
  matched in an isolated, killable child process after a fast-fail screen for
  catastrophic-backtracking patterns, so a crafted regex cannot hang the
  server.
- `redctx discover` refuses non-loopback plain-http Ollama endpoints unless
  `--allow-remote-endpoint` acknowledges the exposure.
- Placeholders are deterministic HMACs over the vault salt. Keep the salt in
  the local config or user-local state; `REDACTED_CONTEXT_SALT` can be visible
  in process environments, and anyone holding the salt can verify dictionary
  guesses against placeholders.

It is not a hard security boundary if the agent process runs as the same OS
user that can read the private source folder. For hard enforcement, run the
agent as a separate OS user or container without filesystem access to the
private source folder, and expose only the MCP server or a separate redaction
service.

## Development

See `ARCHITECTURE.md` for the design boundaries and `CONTRIBUTING.md` for local
development and release checks.

```sh
PYTHONPATH=src python3 -m unittest discover -s tests -p 'test_*.py'
python3 -m py_compile src/redacted_context_mcp/core.py src/redacted_context_mcp/server.py
```

## License

MIT.
