I spend most of my day with an AI coding agent open: Claude Code, Codex, Cursor, depending on the repo. They are good. They are also forgetful, a bit reckless, and very trusting.
- Forgetful. Every session starts blank. I explain the architecture again. The agent proposes the fix I rejected last week.
- Reckless. It renames a function, fixes the three callers grep found, and misses the fourth one, a caller of a caller.
- Trusting. It reads a GitHub issue that a stranger wrote, and that issue can contain instructions aimed at the agent, hidden where I would never see them.
I ended up writing one tool for each problem. They're separate projects, but they were built to sit side by side in the same session, and together they cover most of what bothered me. Each one is a single Rust binary, runs locally, needs no account or API key, and hooks into the agent through MCP plus a short rule and a few client hooks.
Here is each one, then how they work together.
๐ง palace: memory that survives the session
palace is local-first memory for coding agents. It stores what happened verbatim (not an LLM summary that drops the detail), recalls it when it's relevant, and cites where every answer came from.
You: why did we switch the queue to SQLite last month?
Agent: [palace_search โ wing my_api, room decisions, filed 2026-09-03]
You dropped Redis because the deploy target had no persistent volume.
The benchmark you ran is in the same drawer.
How it's organised
Memory is laid out like a building, which keeps search focused:
- Wings are projects or topics. Rooms are areas inside them (backend, decisions, testing). Drawers hold one chunk of verbatim text plus its embedding.
- A knowledge graph stores time-stamped facts (
my_api โ uses_queue โ SQLite, valid from 2026-09-03), so a changed decision is invalidated, not silently overwritten. - Each agent keeps a diary, which is how tomorrow's session picks up where today's stopped, even when tomorrow you're using a different agent.
Search is hybrid: BM25 keyword scoring fused with cosine similarity over embeddings from all-MiniLM-L6-v2, which runs on your CPU through ONNX Runtime. There's an optional local reranker. Everything lives in one palace.db SQLite file.
The part that matters: you don't have to ask
A memory tool the agent forgets to call is just storage. palace installs user-level hooks so recall happens on its own:
- Session start injects recent diary entries for this project and its top drawers.
- Prompt submit searches with your actual question before the agent answers.
- Post tool use surfaces related memory while the agent greps and reads files.
- Stop asks the agent to write a diary entry if it used memory but saved nothing.
All hooks fail open: an empty palace just means a normal session. It works with 11 clients today, including Claude Code, Codex, Cursor, Gemini CLI, VS Code, Zed and Claude Desktop.
palace install # wire up every agent on this machine
palace init ~/my-project --yes --auto-mine # detect rooms and mine the repo
palace search "how did we decide on the database schema"
๐ฅ impact: know what a change will break before you make it
Before you rename a function, change a type or drop a field, you need to know what depends on it. grep finds the name but misses callers of callers. An agent that reads the code to find out is guessing, and it reads a lot of code to do it.
impact answers "if I change this, what else is affected?" in one call, the same way every time. Point it at a file, a symbol or a git diff:
impact index . # later runs skip unchanged files
impact query src/payments/service.rs # what depends on this file?
impact change "rename PaymentStatus::Failed" # what breaks if I rename this?
git diff | impact diff # what does my uncommitted work reach?
- ๐ด Direct callers, ๐ indirect callers all the way up, ๐ต API routes, events and database tables the affected code touches, and ๐ข tests that reach it.
- Deterministic. It parses with tree-sitter, with no model in the loop. Re-indexing an unchanged project takes about 0.1s, and a query takes tens of milliseconds.
- Errs toward over-reporting. When a call could mean several same-named symbols, it lists all of them with a confidence label (exact, probable, heuristic) rather than guessing and missing one.
- 8 languages: Rust, TypeScript/JavaScript (React and React Native too), Python, Go, Kotlin, Swift and C++.
For agents, impact install registers the MCP server and a rule with four triggers: index at session start, check blast radius before the first edit and before every commit, re-check after editing, and put the impact_diff result in the PR description so the reviewer sees what the agent saw. In Claude Code a PreToolUse hook backs the rule up, because an agent can lose track of a rule halfway through a long session, but a hook fires every time.
It's honest about its limits: a call made through a registry, or a function passed as a value, leaves no edge. A short report is not proof that nothing uses a symbol.
๐ก๏ธ mcp-warden: clean untrusted text before the agent reads it
Coding agents read text that strangers wrote: issues, pull requests, web pages, Slack messages, Sentry events, curl output. Any of it can carry prompt injection, often hidden in invisible Unicode, HTML comments, tracking images or bidi tricks.
mcp-warden sits between those sources and the agent:
- ๐งน Strips what a person wouldn't see: invisible characters, HTML comments, off-host images.
- ๐ Withholds fields that look like injection. The agent gets a notice with a quarantine id and is told to tell you. You review the payload in your own terminal (
mcp-warden show ID). The agent can't unlock the quarantine itself. - ๐ฅ Treats repos the agent cloned as outside content, while leaving your own project files, builds and tests alone.
- ๐ฆ In proxy mode it can also gate sensitive MCP calls once untrusted content has entered the session, and pin tool definitions so it warns you when a server quietly changes them.
It has two modes, and you can use both:
| ๐ช Hook mode | ๐ Proxy mode | |
|---|---|---|
| Set up with | mcp-warden install |
mcp-warden wrap config.json |
| Covers | Every tool: MCP, WebFetch, WebSearch, Bash, reads in cloned repos | One stdio MCP server per wrap |
| Best for | Day-to-day protection with zero config | Fine-grained policy on specific servers |
brew install ancientice/mcp-warden/mcp-warden
mcp-warden install # Claude Code, Codex, Cursor, Gemini CLI, Copilot, Kiro, Cline and more
The README says it plainly, and so will I: prompt injection is ordinary text, and a filter can always be reworded around. warden is one layer of defense, not a sandbox. Keep least-privilege tokens and a sandbox as well.
๐ Why they work better together
Each tool is useful alone. The reason I run all three is that they cover different moments in the same session and reinforce each other:
- Session start. palace hands the agent yesterday's diary and this project's decisions. It doesn't have to rediscover the conventions by grepping.
- The agent reads a bug report. warden strips the hidden comment and withholds the field that tries to give orders. The agent still sees the title and tells you something was flagged.
- The agent is about to edit. impact lists every caller, route and test the change reaches. The agent treats it as a checklist instead of fixing the callers it happened to find.
-
The agent opens a PR.
impact_diffgoes in the description, so the reviewer sees the blast radius without running anything. - Session end. palace's stop hook makes sure the fix, the reason behind it and any new decision get written down, so the next session starts from there.
Some details I like about running them as a set:
-
Same shape everywhere. One binary,
<tool> installto wire up your agents,doctor(palace and impact) to check the wiring, anduninstallremoves only what that tool added. -
Your data stays on your machine. No telemetry, no hosted service, no API keys. Your code, your memory and your quarantine stay on disk. The only network calls are palace's one-time embedding model download and impact's daily release check (
IMPACT_NO_UPDATE_CHECK=1turns it off). - Deterministic where it counts. impact and warden have no model in the loop, so their output is the same every run. palace is the only one using embeddings, and those run locally too.
- They stay out of each other's way. warden checks outside content (web tools, gh/curl, MCP servers that point at public sources), so by default it leaves palace's and impact's local output alone. Your own project's memory and call graph aren't the threat model.
โก Try all three in two minutes
macOS / Linux (Homebrew)
brew tap AncientiCe/palace && brew install palace
brew install ancientice/impact/impact
brew install ancientice/mcp-warden/mcp-warden
palace install && impact install && mcp-warden install
No Homebrew? Each repo also has a curl ... | sh install script.
Windows (PowerShell)
irm https://raw.githubusercontent.com/AncientiCe/palace-rs/main/scripts/install.ps1 | iex
irm https://raw.githubusercontent.com/AncientiCe/impact-rs/master/scripts/install.ps1 | iex
irm https://raw.githubusercontent.com/AncientiCe/mcp-warden/main/scripts/install.ps1 | iex
palace install; impact install; mcp-warden install
The Windows install scripts download the release binary for your platform and verify its SHA-256 checksum. Restart your agent app so it reloads its MCP config, then ask it something like "What do we know about how this project handles migrations?" and watch it answer from memory.
All three are open source (MIT; mcp-warden is MIT or Apache-2.0). Issues, blind-spot reports and PRs are welcome. If impact misses a caller or warden lets something through, I want to hear about it.
๐ Links
- palace (memory): https://github.com/AncientiCe/palace-rs
- impact (blast radius): https://github.com/AncientiCe/impact-rs
- mcp-warden (prompt-injection guard): https://github.com/AncientiCe/mcp-warden
- palace in the official MCP registry: registry.modelcontextprotocol.io (
io.github.ancientice/palace-rs) - Model Context Protocol: modelcontextprotocol.io


Top comments (0)