diff --git a/scripts/indexnow.mjs b/scripts/indexnow.mjs
index 94dc0fc..bd0c0ae 100644
--- a/scripts/indexnow.mjs
+++ b/scripts/indexnow.mjs
@@ -39,6 +39,10 @@ const URLS = [
`https://${HOST}/guides/claude-code`,
`https://${HOST}/guides/secret-exfiltration`,
`https://${HOST}/guides/ecc`,
+ `https://${HOST}/guides/cursor`,
+ `https://${HOST}/guides/codex`,
+ `https://${HOST}/guides/vscode`,
+ `https://${HOST}/guides/gemini-cli`,
`https://${HOST}/docs`,
`https://${HOST}/docs/design`,
`https://${HOST}/docs/hooks`,
diff --git a/src/data/guides.ts b/src/data/guides.ts
index 3b149b2..9787db2 100644
--- a/src/data/guides.ts
+++ b/src/data/guides.ts
@@ -66,6 +66,42 @@ export const GUIDES: readonly Guide[] = [
blurb:
"ECC checks your setup and makes the agent look before it edits. This checks what each call does and what each result says, and runs next to ECC's hooks without changing them.",
},
+ {
+ slug: 'cursor',
+ h1: 'Screen MCP tool calls in Cursor',
+ title: 'Screen MCP tool calls in Cursor | agent-chaperone',
+ description:
+ "Put Cursor's MCP servers behind agent-chaperone: the mcp.json change, getting the API key to the proxy, checking it works, and what it does not cover.",
+ blurb:
+ 'Cursor keeps servers in JSON that wrap can edit. The change, how the key reaches the proxy, and what Cursor runs outside MCP.',
+ },
+ {
+ slug: 'codex',
+ h1: 'Screen MCP tool calls in Codex',
+ title: 'Screen MCP tool calls in Codex | agent-chaperone',
+ description:
+ "Put Codex's MCP servers behind agent-chaperone: the config.toml change, forwarding the API key past a cleared environment, and timeouts.",
+ blurb:
+ 'Codex keeps servers in TOML and starts them with a cleared environment, so the key has to be forwarded by name. The change, by hand, and the timeouts to set.',
+ },
+ {
+ slug: 'vscode',
+ h1: 'Screen MCP tool calls in VS Code',
+ title: 'Screen MCP tool calls in VS Code | agent-chaperone',
+ description:
+ "Put VS Code agent mode's MCP servers behind agent-chaperone: the mcp.json change, the API key, when wrap can edit the file, and what it misses.",
+ blurb:
+ 'VS Code lists servers under servers rather than mcpServers and allows comments in the file. The change, the key, and where wrap has to give way to a hand edit.',
+ },
+ {
+ slug: 'gemini-cli',
+ h1: 'Screen MCP tool calls in Gemini CLI',
+ title: 'Screen MCP tool calls in Gemini CLI | agent-chaperone',
+ description:
+ "Put Gemini CLI's MCP servers behind agent-chaperone, and get the API key past the filter that strips anything named like a credential.",
+ blurb:
+ "Gemini CLI strips variables named like credentials from a server's environment, the API key included. The change, and the env line that gets the key through.",
+ },
];
export const SITE = 'https://agentchaperone.dev';
diff --git a/src/data/schema.ts b/src/data/schema.ts
index ae41aaa..e5248fc 100644
--- a/src/data/schema.ts
+++ b/src/data/schema.ts
@@ -207,7 +207,7 @@ export function guidesIndexSchema(): unknown {
'@id': GUIDES_ID,
name: 'agent-chaperone guides',
description:
- "Setting up screening for MCP servers, for a client's own shell and file tools, for prompt injection arriving in tool results, for secrets on their way out, and next to ECC's hooks.",
+ "Setting up screening for MCP servers, including in Cursor, Codex, VS Code and Gemini CLI, for a client's own shell and file tools, for prompt injection arriving in tool results, for secrets on their way out, and next to ECC's hooks.",
url: `${SITE}/guides`,
isPartOf: { '@id': SITE_ID },
about: { '@id': APP },
diff --git a/src/pages/guides/codex.astro b/src/pages/guides/codex.astro
new file mode 100644
index 0000000..34c6985
--- /dev/null
+++ b/src/pages/guides/codex.astro
@@ -0,0 +1,182 @@
+---
+import Base from '../../layouts/Base.astro';
+import { REPO } from '../../data/benchmark';
+import { findGuide, guideUrl } from '../../data/guides';
+import { guideSchema } from '../../data/schema';
+
+const guide = findGuide('codex');
+---
+
+
+ Codex starts each MCP server in its configuration as a local process and talks to it over
+ stdio. agent-chaperone goes in front of that process. It reads each tool call before the
+ server gets it and each result before the agent does, and nothing about the server changes.
+
+ Two things about Codex differ from the JSON-configured clients, and both matter here: the
+ configuration is TOML, and a server does not see your environment unless you forward it. This
+ covers MCP servers only. Codex's own shell commands and patches do not go through MCP.
+
+ A global install matters more on Codex than elsewhere. Codex gives a server a fixed time to
+ start, and running the proxy through
+ Servers live in And after:
+ The old command moves after
+ Codex starts a server with a cleared environment and passes back only a short list of
+ variables:
+
+ The proxy removes its own keys before it starts the server behind it, so the server never
+ receives
+
+ Screening adds at most one request to the model backend per call and one per result, and{' '}
+ the benchmark method records how long those took. A tool timeout
+ that suited the server before still suits it unless its calls were already close to the limit.
+
+
+ Each line is one call or result, with the decision and, in shadow mode, what enforcing would
+ have done instead. A line ending in
+ It starts in shadow mode, which blocks nothing.{' '}
+ The MCP guide covers what each screen asks, how to read a
+ shadow-mode log, and when to switch to enforce. The exact wording of every question is in{' '}
+ the design document.
+
+ Cursor starts each MCP server you configure as a local process and talks to it over stdio.
+ agent-chaperone goes in front of that process. It reads each tool call before the server gets
+ it and each result before the agent does, and nothing about the server changes.
+
+ This covers MCP servers only. Cursor's own terminal commands and file edits do not go through
+ MCP, so the proxy does not see them.
+
+ A global install means Cursor starts the proxy directly. Running it through
+ Cursor reads servers from
+ A server entry before and after:
+ Everything after
+ The model screens need
+ To keep the key out of your shell profile, put it in a file of
+ The proxy removes its own keys before it starts the server behind it, so the server never
+ receives
+ Without a key the proxy still starts, the deterministic rules still run, and every judgment
+ records that no model was asked. It never refuses to start because a key is missing.
+
+ The server should show as connected under Customize, then MCPs. If it does not, the Output
+ panel's MCP Logs has what the process printed. In the CLI, Then use the server once from the agent and read what was decided:
+ Each line is one call or result, with the decision and, in shadow mode, what enforcing would
+ have done instead.
+ It starts in shadow mode, which blocks nothing.{' '}
+ The MCP guide covers what each screen asks, how to read a
+ shadow-mode log, and when to switch to enforce. The exact wording of every question is in{' '}
+ the design document.
+
+ Gemini CLI starts each MCP server in its settings as a local process and talks to it over
+ stdio. agent-chaperone goes in front of that process. It reads each tool call before the
+ server gets it and each result before the agent does, and nothing about the server changes.
+
+ One thing about Gemini CLI matters more than the rest here: it strips variables that look like
+ secrets from a server's environment, and
+ A global install means Gemini CLI starts the proxy directly. Running it through{' '}
+
+ Servers live under
+ Everything after
+
+ Gemini CLI removes variables from a server's inherited environment when their names suggest a
+ credential, and a name containing
+ A variable set in the server's own
+ The proxy removes its own keys before it starts the server behind it, so the server never
+ receives
+ Gemini CLI only starts a local server in a folder you have trusted, and shows it as
+ disconnected anywhere else. Then use the server once from the agent and read what was decided:
+ Each line is one call or result, with the decision and, in shadow mode, what enforcing would
+ have done instead. A line ending in
+ It starts in shadow mode, which blocks nothing.{' '}
+ The MCP guide covers what each screen asks, how to read a
+ shadow-mode log, and when to switch to enforce. The exact wording of every question is in{' '}
+ the design document.
+ {guide.h1}
+
+ Install it{' '}
+
+ #
+
+
+
+ npm install -g agent-chaperonenpx spends part of it looking the package
+ up.
+
+ Put a server behind it{' '}
+
+ #
+
+
+ ~/.codex/config.toml, one [mcp_servers.<name>]{' '}
+ table each, and in .codex/config.toml inside a project Codex trusts. The CLI and
+ the IDE extension read the same file. agent-chaperone wrap only edits JSON, so on
+ Codex the change is made by hand. A server before:
+
+ {`[mcp_servers.filesystem]
+command = "npx"
+args = ["-y", "@modelcontextprotocol/server-filesystem", "/Users/me/project"]`}
+ {`[mcp_servers.filesystem]
+command = "agent-chaperone"
+args = ["--server", "filesystem", "--",
+ "npx", "-y", "@modelcontextprotocol/server-filesystem", "/Users/me/project"]
+env_vars = ["TYPESAFE_API_KEY"]
+startup_timeout_sec = 30`}--, unchanged. --server names the
+ section of the policy file that applies, and using the table's own name keeps the two easy to
+ match. Keep any env or cwd the entry already had: they still reach
+ the server.
+
+ Give it the API key{' '}
+
+ #
+
+
+ HOME, PATH, USER, the locale and a few
+ others. A TYPESAFE_API_KEY exported in your shell is not on that list, so without
+ the env_vars line above the proxy never sees it and every judgment records that
+ no model was asked.
+ env_vars forwards the variable by name from the environment Codex was started in,
+ so the key is never written into the file. If Codex was started some other way and the key
+ does not arrive, env sets it directly, at the cost of the key sitting in{' '}
+ config.toml in plain text:
+
+ {`env = { TYPESAFE_API_KEY = "..." }`}TYPESAFE_API_KEY either way.
+
+ Timeouts{' '}
+
+ #
+
+
+ startup_timeout_sec is how long Codex waits for a server to start, and{' '}
+ tool_timeout_sec how long it waits for one call. Codex's documentation and its
+ source disagree on the defaults, 10 and 60 seconds against 30 and 300, so set the startup one
+ yourself rather than depend on either.
+
+ Check that it is working{' '}
+
+ #
+
+
+ /mcp inside a Codex session, or codex mcp list from the shell, shows
+ whether the server started. Then use the server once from the agent and read what was decided:
+
+ agent-chaperone log[not screened] was decided without a model,
+ which means the key did not arrive.
+
+ What this does not cover{' '}
+
+ #
+
+
+
+
+
+ Next{' '}
+
+ #
+
+
+ {guide.h1}
+
+ Install it{' '}
+
+ #
+
+
+
+ npm install -g agent-chaperonenpx{' '}
+ works too, but adds a package lookup every time a server starts.
+
+ Put a server behind it{' '}
+
+ #
+
+
+ ~/.cursor/mcp.json for every project and from{' '}
+ .cursor/mcp.json inside one project, under mcpServers. The editor
+ and the Cursor CLI read the same files.
+ agent-chaperone wrap makes the edit. It prints what it would change and writes
+ nothing until you add --write, and it keeps the original beside the file.
+
+ agent-chaperone wrap ~/.cursor/mcp.json
+agent-chaperone wrap ~/.cursor/mcp.json --write
+ {`{
+ "mcpServers": {
+ "filesystem": {
+ "command": "npx",
+ "args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/me/project"]
+ }
+ }
+}`}
+ {`{
+ "mcpServers": {
+ "filesystem": {
+ "command": "agent-chaperone",
+ "args": ["--server", "filesystem", "--",
+ "npx", "-y", "@modelcontextprotocol/server-filesystem", "/Users/me/project"]
+ }
+ }
+}`}-- is the server that would have run anyway.{' '}
+ --server names the section of the policy file that applies, after the name you
+ gave the server in Cursor. --unwrap takes it all back out.
+
+ Give it the API key{' '}
+
+ #
+
+
+ TYPESAFE_API_KEY. A server started by Cursor gets the
+ environment Cursor itself was started with, so a key exported in your shell profile reaches
+ the proxy once Cursor has been restarted. Cursor's help says the same of any variable a server
+ needs.
+ NAME=value lines
+ and point the entry at it with envFile, which Cursor supports for local servers:
+
+ {`"filesystem": {
+ "command": "agent-chaperone",
+ "args": ["--server", "filesystem", "--", "npx", "-y", "@modelcontextprotocol/server-filesystem", "/Users/me/project"],
+ "envFile": "/Users/me/.config/agent-chaperone/env"
+}`}TYPESAFE_API_KEY either way.
+
+ Check that it is working{' '}
+
+ #
+
+
+ agent mcp list{' '}
+ shows the same status.
+
+ agent-chaperone logagent-chaperone log --follow prints them as they happen. A
+ line ending in [not screened] was decided without a model, which means the key
+ did not arrive; the proxy also says so in MCP Logs when it starts.
+
+ What this does not cover{' '}
+
+ #
+
+
+
+
+ wrap.
+ {' '}
+ Wrap a server Cursor reaches by URL by hand instead, as{' '}
+ the MCP guide shows, and pass any token it needs
+ with --header-env. Headers written in the Cursor entry stay with Cursor and
+ never reach the proxy.
+
+ Next{' '}
+
+ #
+
+
+ {guide.h1}
+ TYPESAFE_API_KEY looks like one. This
+ covers MCP servers only. Gemini CLI's own shell commands and file edits do not go through MCP.
+
+ Install it{' '}
+
+ #
+
+
+
+ npm install -g agent-chaperonenpx works too, but adds a package lookup every time a server starts.
+
+ Put a server behind it{' '}
+
+ #
+
+
+ mcpServers in ~/.gemini/settings.json, and in{' '}
+ .gemini/settings.json inside a project. A server before and after:
+
+ {`{
+ "mcpServers": {
+ "filesystem": {
+ "command": "npx",
+ "args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/me/project"]
+ }
+ }
+}`}
+ {`{
+ "mcpServers": {
+ "filesystem": {
+ "command": "agent-chaperone",
+ "args": ["--server", "filesystem", "--",
+ "npx", "-y", "@modelcontextprotocol/server-filesystem", "/Users/me/project"],
+ "env": { "TYPESAFE_API_KEY": "$TYPESAFE_API_KEY" }
+ }
+ }
+}`}-- is the server that would have run anyway.{' '}
+ --server names the section of the policy file that applies. The env{' '}
+ line is not optional on Gemini CLI, and the next section says why.
+ agent-chaperone wrap ~/.gemini/settings.json can make the command change, but not
+ the env line, which you add afterwards. It reads the file as strict JSON, and
+ Gemini CLI allows comments in its settings, so a file with comments is refused with an error
+ and left untouched. It also wraps every server in the file, so use it on a file whose servers
+ all run a local command.
+
+ Give it the API key{' '}
+
+ #
+
+
+ KEY is one of the patterns. So a{' '}
+ TYPESAFE_API_KEY exported in your shell never reaches the proxy on its own, and
+ every judgment would record that no model was asked.
+ env is not removed. Writing it as{' '}
+ "$TYPESAFE_API_KEY" makes Gemini CLI fill in the value from its own environment
+ when it reads the settings, so the key itself is never written into the file. It has to be
+ exported in the shell Gemini CLI is started from; a missing variable becomes an empty string.
+ TYPESAFE_API_KEY, even though Gemini CLI handed it over.
+
+ Check that it is working{' '}
+
+ #
+
+
+ /mcp inside a session, or{' '}
+ gemini mcp list from the shell, shows each server's state.
+
+ agent-chaperone log[not screened] was decided without a model,
+ which on Gemini CLI almost always means the env line is missing.
+
+ What this does not cover{' '}
+
+ #
+
+
+
+
+ --header-env: headers written in the Gemini CLI entry never reach the
+ proxy.
+
+ Next{' '}
+
+ #
+
+
+
agent-chaperone wrap ~/.claude.json
agent-chaperone wrap ~/.claude.json --write
+ + Each client keeps its configuration somewhere different and hands a server a different + environment, which decides whether the API key reaches the proxy at all. The setup for{' '} + Cursor, Codex,{' '} + VS Code and Gemini CLI has a + page each. +
Screening needs an API key for the model backend. Without one the proxy still starts and the
deterministic rules still run, and every judgment records that no model was asked. It never
diff --git a/src/pages/guides/vscode.astro b/src/pages/guides/vscode.astro
new file mode 100644
index 0000000..bfc0f8e
--- /dev/null
+++ b/src/pages/guides/vscode.astro
@@ -0,0 +1,191 @@
+---
+import Base from '../../layouts/Base.astro';
+import { REPO } from '../../data/benchmark';
+import { findGuide, guideUrl } from '../../data/guides';
+import { guideSchema } from '../../data/schema';
+
+const guide = findGuide('vscode');
+---
+
+
+ VS Code starts each MCP server you configure for agent mode as a local process and talks to it
+ over stdio. agent-chaperone goes in front of that process. It reads each tool call before the
+ server gets it and each result before the agent does, and nothing about the server changes.
+
+ This covers MCP servers only. The terminal commands and file edits the agent runs through VS
+ Code's own tools do not go through MCP, so the proxy does not see them.
+
+ A global install means VS Code starts the proxy directly. Running it through
+ VS Code reads servers from
+ Everything after
+ A new server can also be added from the command line, straight into your user profile:
+ The model screens need
+ To keep the key out of your shell profile, point the entry at a file of{' '}
+
+ The proxy removes its own keys before it starts the server behind it, so the server never
+ receives
+ Without a key the proxy still starts, the deterministic rules still run, and every judgment
+ records that no model was asked. It never refuses to start because a key is missing.
+
+ A server in Then use the server once from agent mode and read what was decided:
+ Each line is one call or result, with the decision and, in shadow mode, what enforcing would
+ have done instead. A line ending in
+ It starts in shadow mode, which blocks nothing.{' '}
+ The MCP guide covers what each screen asks, how to read a
+ shadow-mode log, and when to switch to enforce. The exact wording of every question is in{' '}
+ the design document.
+ {guide.h1}
+
+ Install it{' '}
+
+ #
+
+
+
+ npm install -g agent-chaperonenpx{' '}
+ works too, but adds a package lookup every time a server starts.
+
+ Put a server behind it{' '}
+
+ #
+
+
+ .vscode/mcp.json in a workspace and from the{' '}
+ mcp.json in your user profile, which MCP: Open User Configuration opens.
+ Both list servers under servers, not mcpServers. A server before and
+ after:
+
+ {`{
+ "servers": {
+ "filesystem": {
+ "type": "stdio",
+ "command": "npx",
+ "args": ["-y", "@modelcontextprotocol/server-filesystem", "\${workspaceFolder}"]
+ }
+ }
+}`}
+ {`{
+ "servers": {
+ "filesystem": {
+ "type": "stdio",
+ "command": "agent-chaperone",
+ "args": ["--server", "filesystem", "--",
+ "npx", "-y", "@modelcontextprotocol/server-filesystem", "\${workspaceFolder}"]
+ }
+ }
+}`}-- is the server that would have run anyway, and VS Code still
+ fills in variables like {'${workspaceFolder}'} before it starts anything.{' '}
+ --server names the section of the policy file that applies.
+ agent-chaperone wrap can make the edit, with two limits on VS Code. It reads the
+ file as strict JSON, and VS Code allows comments in mcp.json, so a file with
+ comments is refused with an error and left untouched. And it wraps every server in the file,
+ so use it on a file whose servers all run a local command. Otherwise edit the entry by hand as
+ above.
+
+ agent-chaperone wrap .vscode/mcp.json
+agent-chaperone wrap .vscode/mcp.json --write
+ {`code --add-mcp '{"name":"filesystem","command":"agent-chaperone","args":["--server","filesystem","--","npx","-y","@modelcontextprotocol/server-filesystem","/Users/me/project"]}'`}
+ Give it the API key{' '}
+
+ #
+
+
+ TYPESAFE_API_KEY. A server started by VS Code gets VS
+ Code's own environment, and on macOS VS Code reads your shell profile when it starts,
+ including when it is opened from the Dock. A key exported in .zshrc or{' '}
+ .bashrc reaches the proxy once VS Code has been restarted. If VS Code shows an
+ error about resolving your shell environment, starting it with code . from a
+ terminal avoids the problem.
+ NAME=value lines with envFile:
+
+ {`"filesystem": {
+ "type": "stdio",
+ "command": "agent-chaperone",
+ "args": ["--server", "filesystem", "--", "npx", "-y", "@modelcontextprotocol/server-filesystem", "\${workspaceFolder}"],
+ "envFile": "/Users/me/.config/agent-chaperone/env"
+}`}TYPESAFE_API_KEY either way.
+
+ Check that it is working{' '}
+
+ #
+
+
+ .vscode/mcp.json only starts in a trusted workspace. Run{' '}
+ MCP: List Servers, pick the server, and Show Output has what the process
+ printed, including a line from the proxy if it started without a key.
+
+ agent-chaperone log[not screened] was decided without a model,
+ which means the key did not arrive.
+
+ What this does not cover{' '}
+
+ #
+
+
+
+
+ type set to{' '}
+ stdio, since the entry now runs a command. Pass any token it needs with{' '}
+ --header-env: headers written in the VS Code entry never reach the proxy.
+
+ Next{' '}
+
+ #
+
+
+