airlok 0.5.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,26 @@
2
2
 
3
3
  All notable changes to airlok. The format follows Keep a Changelog; versions follow SemVer.
4
4
 
5
+ ## [0.6.1] - 2026-09-12
6
+
7
+ ### Fixed
8
+
9
+ - Approving an MCP call with `a` covered the whole server for the rest of the run, so a later call to the same server ran without asking, even when it reached a different place. "All" now covers one tool on one server, and only the places that call named: a call naming anything outside them asks again. Paths are resolved before they are compared, so a different spelling of the same place is still covered and `..` cannot step outside an approval.
10
+ - The confirmation now shows what the server can reach and the resolved place each argument names, marking one outside the current project, and `airlok mcp list` shows the same root. A call that reads outside the repository is visible before it runs rather than after.
11
+ - A tool call or a note printed in the middle of a streamed line split the line in two: a bullet's bold label was rendered as a finished bullet and its text as a separate block, which is what `• Overview:` and its text landing on different lines was. Only complete markdown is flushed now, so a line still arriving keeps its block.
12
+
13
+ ## [0.6.0] - 2026-09-12
14
+
15
+ ### Added
16
+
17
+ - MCP servers. `[[mcp]]` blocks configure servers over stdio or streamable HTTP, and their tools are offered to the model as `<server>__<tool>`, alongside the built-ins. A server starts on the first turn that can use it; one that fails to start is reported and skipped, and the run continues without it. Tools can be limited with `tools = ["name"]`, and a built-in keeps its name if a server ever claims one. The client is the official `rmcp` SDK.
18
+ - `trust` per server decides the gate: `prompt`, the default, shows the server, the tool, and the arguments and asks with the usual `[y]es / [n]o / [a]ll / [q]uit`, where `a` covers that one server for the run; `allow` never asks; `deny` keeps the server's tools from the model. `[safety] confirm_mcp` turns the prompting off wholesale, and `--yes` includes it.
19
+ - `rehydrate` per server decides what an MCP server receives. It is off by default, so secrets found in your files leave as placeholders rather than values, and the confirmation shows exactly what will be sent. The provider API key is refused in arguments either way, as it is for every tool.
20
+ - Tool descriptions and results from a server reach the model inside markers naming the server and saying the text is data, so a server cannot instruct the model or change airlok's own confirmations and deny list.
21
+ - Secrets for a server come from commands: `env_cmd` and `header_cmd` take their values from stdout, the way `api_key_cmd` does, so no token is written in the config.
22
+ - `airlok mcp list` shows each server, whether it answers, and its tools. `airlok mcp call <server> <tool> '<json>'` calls one tool through the same gates. `/mcp` does the listing in a session, and `/mcp <name>` enables a disabled server for that session.
23
+ - Plan mode offers no MCP tools, as it offers no write tools, and starts no servers.
24
+
5
25
  ## [0.5.0] - 2026-09-12
6
26
 
7
27
  ### Added
package/README.md CHANGED
@@ -35,6 +35,7 @@ airlok config init # write a commented config to the
35
35
  airlok config show # print the effective config and the key source
36
36
  airlok context # print the context block sent with the system prompt, after redaction
37
37
  airlok redactions # list what the redactor detects and how each kind is treated
38
+ airlok mcp list # the configured MCP servers, whether they answer, and their tools
38
39
  ```
39
40
 
40
41
  Every `write_file` and `edit_file` call shows a diff with line numbers, three lines of context, +/- gutters, and syntax highlighting, and asks `Apply? [y]es / [n]o / [a]ll / [q]uit`. A diff longer than 40 lines stops at `... N more lines, [v] to view all`; `v` prints the rest and asks again. Every `bash` call that is not on the allow list shows the command and asks the same way. `y` applies this one, `n` sends a rejection back to the model so it can adapt, `a` approves the rest of that kind for the run, and `q` aborts the run with a non-zero exit. Prompts are read from the terminal, not stdin, so piped input still works; without a terminal, pass `--yes` or turn the confirmations off in the config.
@@ -71,6 +72,7 @@ A cancelled turn keeps the text streamed so far in the history, marked as interr
71
72
  |---|---|
72
73
  | `/help` | list the commands |
73
74
  | `/model [<id>]` | show the model, or use `<id>` for the rest of the session; not validated, the provider rejects a bad id on the next turn |
75
+ | `/mcp` | list the MCP servers and their tools; `/mcp <name>` enables a disabled one for this session |
74
76
  | `/plan` | turn plan mode on or off |
75
77
  | `/go` | carry out the plan from plan mode, back in normal mode |
76
78
  | `/provider [<name>]` | show the provider, or switch to `anthropic` or `openai` if a key is available for it; says which key is missing otherwise |
@@ -111,6 +113,49 @@ Long sessions are compacted. Once the last request used more than `compact_at` (
111
113
 
112
114
  Tool results over 50 KiB are cut with a marker telling the model to page with `read_file`'s `offset` and `limit`.
113
115
 
116
+ ## MCP servers
117
+
118
+ airlok can offer the model tools from external [MCP](https://modelcontextprotocol.io) servers. They are configured with `[[mcp]]` blocks, which merge across the user and project files like every other setting:
119
+
120
+ ```toml
121
+ [[mcp]]
122
+ name = "files" # its tools reach the model as files__<tool>
123
+ command = "npx"
124
+ args = ["-y", "@modelcontextprotocol/server-filesystem", "."]
125
+ # env = { NODE_ENV = "production" }
126
+ # env_cmd = { TOKEN = "op read op://vault/item/token" } # stdout is the value
127
+
128
+ [[mcp]]
129
+ name = "docs"
130
+ transport = "http"
131
+ url = "https://example.com/mcp"
132
+ headers = { Accept = "application/json" }
133
+ header_cmd = { Authorization = "printf 'Bearer %s' $(cat ~/.docs-token)" }
134
+ tools = ["search"] # or "all", the default
135
+ trust = "prompt" # "prompt" (default), "allow", "deny"
136
+ rehydrate = false # the default
137
+ timeout_secs = 30 # starting the server, and every call
138
+ enabled = true
139
+ ```
140
+
141
+ `env_cmd` and `header_cmd` take their values from a command's stdout, the way `api_key_cmd` does, so a token never sits in the file. `airlok config show` prints the command, never what it produced.
142
+
143
+ Servers start on the first turn that can use them, not when airlok starts. One that fails to start prints a line and is skipped, and the run continues without it. Tools are named `<server>__<tool>`; a built-in keeps its name if a server ever claims one.
144
+
145
+ `trust` decides the gate. The default, `prompt`, shows the server, the tool, and the arguments, and asks with the same `[y]es / [n]o / [a]ll / [q]uit` prompt as a shell command, where `a` approves that one server for the rest of the run. `allow` never asks. `deny` keeps a server's tools from the model, while `airlok mcp list` still shows them.
146
+
147
+ `rehydrate` decides what the server receives. By default airlok sends placeholders: a secret found in your files leaves as `<<SECRET_1>>` rather than as the value, because an MCP server is a third party in the same way the model is, and the confirmation prompt shows you exactly what will be sent. Set `rehydrate = true` for a server that genuinely needs the value. The provider API key is refused either way, as it is for every tool.
148
+
149
+ A server is untrusted input. Its tool descriptions and its results reach the model inside markers saying they are data from that server, so a description reading "ignore previous instructions" is quoted text and nothing more. Nothing a server sends changes the deny list, the confirmations, or anything else about how airlok behaves.
150
+
151
+ | Command | What it does |
152
+ |---|---|
153
+ | `airlok mcp list` | every configured server, whether it answers, and the tools it offers |
154
+ | `airlok mcp call <server> <tool> '<json>'` | call one tool with the same gates, for debugging |
155
+ | `/mcp` | in a session, the same list; `/mcp <name>` enables a disabled server for this session |
156
+
157
+ Plan mode offers no MCP tools, just as it offers no write tools, and it starts no servers.
158
+
114
159
  ## Context and AIRLOK.md
115
160
 
116
161
  Every run starts with a context block in the system prompt: the working directory, OS and shell, the git branch with `git status --short` and the last five commit subjects, a gitignore-aware file tree (four levels, at most 200 entries), and project instructions. Instructions come from `AIRLOK.md` in the repository root, or `CLAUDE.md` or `AGENTS.md` if there is no `AIRLOK.md`, followed by `~/.config/airlok/AIRLOK.md`. The block goes through the redactor like everything else, and `airlok context` prints exactly what would be sent.
@@ -19,7 +19,7 @@
19
19
  "hasInstallScript": true,
20
20
  "license": "MIT OR Apache-2.0",
21
21
  "name": "airlok",
22
- "version": "0.5.0"
22
+ "version": "0.6.1"
23
23
  },
24
24
  "node_modules/detect-libc": {
25
25
  "engines": {
@@ -48,5 +48,5 @@
48
48
  }
49
49
  },
50
50
  "requires": true,
51
- "version": "0.5.0"
51
+ "version": "0.6.1"
52
52
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "artifactDownloadUrls": [
3
- "https://github.com/airlok-dev/airlok/releases/download/v0.5.0"
3
+ "https://github.com/airlok-dev/airlok/releases/download/v0.6.1"
4
4
  ],
5
5
  "bin": {
6
6
  "airlok": "run-airlok.js"
@@ -60,7 +60,7 @@
60
60
  "zipExt": ".tar.xz"
61
61
  }
62
62
  },
63
- "version": "0.5.0",
63
+ "version": "0.6.1",
64
64
  "volta": {
65
65
  "node": "18.14.1",
66
66
  "npm": "9.5.0"