agent-dir 0.5.0 → 1.0.0
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/README.md +40 -421
- package/SECURITY.md +39 -0
- package/assets/agent-dir-banner.svg +38 -0
- package/dist/bin/agent-dir.js +0 -7
- package/dist/src/auth.js +3 -1
- package/dist/src/config.js +0 -2
- package/dist/src/logging.d.ts +0 -1
- package/dist/src/logging.js +6 -69
- package/dist/src/mcp.js +189 -651
- package/dist/src/server.d.ts +1 -1
- package/dist/src/server.js +35 -141
- package/dist/src/telemetry/privacy.js +2 -0
- package/dist/src/telemetry/types.d.ts +2 -0
- package/dist/src/tools/code.d.ts +0 -12
- package/dist/src/tools/code.js +9 -29
- package/dist/src/tools/diagnostics.js +3 -1
- package/dist/src/tools/files.d.ts +2 -21
- package/dist/src/tools/files.js +85 -70
- package/dist/src/tools/git.d.ts +0 -7
- package/dist/src/tools/git.js +0 -64
- package/dist/src/tools/metadata.d.ts +0 -1
- package/dist/src/tools/metadata.js +0 -22
- package/dist/src/tools/skills.d.ts +1 -0
- package/dist/src/tools/skills.js +65 -0
- package/dist/src/types.d.ts +0 -2
- package/docs/CONFIGURATION.md +154 -0
- package/docs/MCP.md +106 -0
- package/docs/RELEASING.md +213 -0
- package/docs/telemetry.md +109 -0
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -4,477 +4,96 @@
|
|
|
4
4
|
|
|
5
5
|
</div>
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
# agent-dir
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
**Expose a trusted local project to AI agents through a controlled, authenticated MCP endpoint.**
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
Agent Dir is a cross-platform TypeScript CLI for bounded project-file access, targeted edits, and explicitly allowlisted development commands. Optional tunnels let an agent connect remotely while the local server retains authentication and filesystem boundaries.
|
|
12
|
+
|
|
13
|
+
> **Security:** this is a remote-control development tool. Expose only directories you trust, keep authentication and tunnel tokens private, and allow only commands you need.
|
|
12
14
|
|
|
13
15
|
## Requirements
|
|
14
16
|
|
|
15
|
-
- Node.js 24
|
|
17
|
+
- Node.js 24 or newer
|
|
16
18
|
- npm
|
|
17
|
-
- Wormhole CLI or `cloudflared`
|
|
18
|
-
|
|
19
|
-
Node.js 24 is the supported LTS baseline. The project currently tracks TypeScript 7 and Biome 2.5.
|
|
19
|
+
- Wormhole CLI or `cloudflared` if remote access is required
|
|
20
20
|
|
|
21
21
|
## Quick start
|
|
22
22
|
|
|
23
|
+
Install globally:
|
|
24
|
+
|
|
23
25
|
```bash
|
|
24
26
|
npm install -g agent-dir
|
|
25
27
|
agent-dir .
|
|
26
28
|
```
|
|
27
29
|
|
|
28
|
-
Or
|
|
30
|
+
Or use it without a global installation:
|
|
29
31
|
|
|
30
32
|
```bash
|
|
31
33
|
npx agent-dir .
|
|
32
34
|
```
|
|
33
35
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
```bash
|
|
37
|
-
agent-dir . --tunnel wormhole --subdomain my-project-x7k4m2
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
For a stable hostname on your own Cloudflare-managed domain, configure a Cloudflare Tunnel route to the Agent Dir port. Cloudflare Tunnel is the default for profiles that have a `tunnels.cloudflared` entry. Use `-c` to select it explicitly or `-w` to override it with Wormhole:
|
|
41
|
-
|
|
42
|
-
```bash
|
|
43
|
-
agent-dir scriptr # profile default (Cloudflare when configured)
|
|
44
|
-
agent-dir scriptr -c # explicitly use Cloudflare Tunnel
|
|
45
|
-
agent-dir scriptr -w # explicitly use Wormhole
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
For a one-off run, the equivalent long option remains available:
|
|
36
|
+
The server starts with a generated Bearer token. Configure an MCP client to connect to the displayed `/mcp` endpoint and send:
|
|
49
37
|
|
|
50
|
-
```
|
|
51
|
-
|
|
38
|
+
```http
|
|
39
|
+
Authorization: Bearer <token>
|
|
52
40
|
```
|
|
53
41
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
### Wormhole domains
|
|
57
|
-
|
|
58
|
-
When using a custom Wormhole subdomain, choose a distinctive name such as `my-project-x7k4m2`. Avoid generic names such as `project`, `test`, `dev`, or `scriptr` to reduce collisions and make the public endpoint less predictable.
|
|
59
|
-
|
|
60
|
-
The Wormhole URL is only the transport endpoint. `agent-dir` authentication is enforced by the local HTTP server; Wormhole does not provide the Bearer-token authentication described below.
|
|
61
|
-
|
|
62
|
-
> **Wormhole subdomain limit:** Wormhole limits how many subdomains a user can have registered/active at once. If you see an error such as `Subdomain limit reached (max 3 per user)`, the tunnel is being rejected by Wormhole, not by `agent-dir`. Release an existing Wormhole subdomain or use `--random` to let Wormhole choose a temporary random URL.
|
|
63
|
-
>
|
|
64
|
-
> A subdomain can remain registered while another `wormhole` process is still running, so stop unused Wormhole tunnels before creating another one. If a configured subdomain is unavailable, `agent-dir` will report the Wormhole registration failure instead of silently starting without a tunnel.
|
|
65
|
-
|
|
66
|
-
## First-run setup
|
|
67
|
-
|
|
68
|
-
On the first interactive run, when no `agent-dir` config exists, the CLI walks you through creating a saved profile. After the profile name, directory, and port, it asks whether to configure Cloudflare Tunnel first; if enabled, it collects the public hostname and Cloudflare connector token. The Cloudflare connector token is entered without being echoed to the terminal. It then asks whether to configure Wormhole as well and optionally collects a custom subdomain. You can enable either provider, both, or neither. Cloudflare is the default when both are configured. The generated Agent Dir MCP bearer token is saved separately from the Cloudflare connector token.
|
|
42
|
+
Use the Authorization header rather than putting tokens in URLs. Authentication cannot be disabled.
|
|
69
43
|
|
|
70
|
-
|
|
44
|
+
## Remote access
|
|
71
45
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
```bash
|
|
75
|
-
agent-dir setup
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
This is useful for creating another profile or replacing an existing profile. The wizard saves the profile and exits without starting a tunnel.
|
|
79
|
-
|
|
80
|
-
You can skip setup completely when you only want a temporary random Wormhole URL:
|
|
46
|
+
For a temporary Wormhole endpoint:
|
|
81
47
|
|
|
82
48
|
```bash
|
|
83
49
|
agent-dir . --random --tunnel wormhole
|
|
84
50
|
```
|
|
85
51
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
Non-interactive invocations and invocations with explicit launch options do not start the setup wizard.
|
|
89
|
-
|
|
90
|
-
## Profiles
|
|
91
|
-
|
|
92
|
-
Profiles save settings you use repeatedly: project directory, tunnel, Wormhole subdomain or Cloudflare hostname, port, npm scripts, allowed non-npm commands, blocked command prefixes, and whether dedicated Git MCP tools are enabled. Dedicated Git tools are enabled automatically when `git` is in the allowed command list, or explicitly with `--git`; use `--no-git` to disable them for a run.
|
|
93
|
-
|
|
94
|
-
```bash
|
|
95
|
-
agent-dir config add my-project \
|
|
96
|
-
--directory ~/src/my-project \
|
|
97
|
-
--tunnel wormhole \
|
|
98
|
-
--subdomain my-project-x7k4m2 \
|
|
99
|
-
--npm typecheck,lint,format,test,build \
|
|
100
|
-
--command grep,find,rg,git \
|
|
101
|
-
--blacklist "git commit,git push --force"
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
Profiles can store both tunnel configurations at once. The Cloudflare connector token is separate from the Agent Dir MCP bearer token (`--token`); store it under `tunnels.cloudflared.token` or pass `--cloudflared-token` when creating the profile. Config files are stored at `~/.config/agent-dir/config.json` with owner-only permissions.
|
|
105
|
-
|
|
106
|
-
```json
|
|
107
|
-
{
|
|
108
|
-
"directory": "~/src/my-project",
|
|
109
|
-
"port": 3002,
|
|
110
|
-
"tunnel": "cloudflared",
|
|
111
|
-
"defaultTunnel": "cloudflared",
|
|
112
|
-
"tunnels": {
|
|
113
|
-
"wormhole": { "subdomain": "my-project-x7k4m2" },
|
|
114
|
-
"cloudflared": {
|
|
115
|
-
"hostname": "mcp.example.com",
|
|
116
|
-
"token": "CLOUDFLARE_TUNNEL_TOKEN"
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
When creating a profile from the CLI, pass `--hostname` and optionally `--cloudflared-token`. **Avoid passing secrets as command-line arguments where possible**: they may be exposed in shell history or process listings. Prefer the interactive setup prompt for the connector token. To configure both providers, include both `--subdomain` and `--hostname`, and set the default using `--tunnel cloudflared` (or `-c`). `agent-dir config show` redacts configured tunnel tokens.
|
|
123
|
-
|
|
124
|
-
For Cloudflare, save the hostname instead of a Wormhole subdomain:
|
|
125
|
-
|
|
126
|
-
```bash
|
|
127
|
-
agent-dir config add scriptr-mcp \
|
|
128
|
-
--directory ~/src/my-project \
|
|
129
|
-
--tunnel cloudflared \
|
|
130
|
-
--hostname mcp.example.com \
|
|
131
|
-
--npm typecheck,lint,test
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
Then start the saved profile:
|
|
135
|
-
|
|
136
|
-
```bash
|
|
137
|
-
agent-dir scriptr-mcp
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
Manage profiles with:
|
|
141
|
-
|
|
142
|
-
```bash
|
|
143
|
-
agent-dir config list
|
|
144
|
-
agent-dir config show my-project
|
|
145
|
-
agent-dir config delete my-project
|
|
146
|
-
agent-dir config delete --all
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
Deletion asks for confirmation by default. Use `--yes` for automation:
|
|
150
|
-
|
|
151
|
-
```bash
|
|
152
|
-
agent-dir config delete my-project --yes
|
|
153
|
-
agent-dir config delete --all --yes
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
The older `config remove <name>` command remains an alias for `config delete <name>`.
|
|
157
|
-
|
|
158
|
-
Retrieve or rotate a profile's authentication token with:
|
|
159
|
-
|
|
160
|
-
```bash
|
|
161
|
-
agent-dir config token my-project
|
|
162
|
-
agent-dir config token my-project --rotate
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
The token command prints the secret intentionally; avoid sharing or committing its output.
|
|
166
|
-
|
|
167
|
-
Profiles are stored per-user at `~/.config/agent-dir/config.json`, not in the project repository. The config directory and file are written with user-only permissions. Command-line options can override saved values for one run.
|
|
168
|
-
|
|
169
|
-
## Telemetry
|
|
170
|
-
|
|
171
|
-
Telemetry is **disabled by default** and is local-only. When enabled, events are stored under `~/.config/agent-dir/telemetry/<profile>/session-<id>.jsonl` with user-only permissions. Each profile and process session gets a separate append-only JSONL file; restarting Agent Dir starts a new session file, and historical events are not truncated. Tool events also include rough byte-based token-size estimates, hashed request/input/output fingerprints, repeated-output signals, cumulative output size, and hashed per-file size metadata. These estimates do not reveal Claude's actual token billing or hidden context. File contents, source code, and raw paths are not stored in the added file metrics. No telemetry is sent to a remote service. The previous shared `telemetry.jsonl` file is migrated into the new layout on the next telemetry read/reset and preserved as a legacy archive.
|
|
172
|
-
|
|
173
|
-
Enable a privacy level with:
|
|
52
|
+
For a configured Wormhole subdomain:
|
|
174
53
|
|
|
175
54
|
```bash
|
|
176
|
-
agent-dir
|
|
177
|
-
agent-dir telemetry enable basic
|
|
178
|
-
agent-dir telemetry enable detailed
|
|
179
|
-
agent-dir telemetry enable diagnostic
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
Levels add bounded operational context:
|
|
183
|
-
|
|
184
|
-
- **anonymous** — MCP methods, tool/command families, success, duration, sizes, result counts, truncation/pagination, and safe error categories.
|
|
185
|
-
- **basic** — anonymous data plus bounded Agent Dir/Node/platform and MCP client version information.
|
|
186
|
-
- **detailed** — basic data plus coarse project classification such as language, package manager, Git/CodeGraph availability, and project size.
|
|
187
|
-
|
|
188
|
-
Standard telemetry levels never record file contents, command arguments, authentication tokens, environment variables, or project paths. The opt-in diagnostic level additionally records local troubleshooting metadata such as project paths, hostname, username, process identifiers, Node version, memory usage, and process uptime; it remains local and is never uploaded by Agent Dir. Session telemetry separates wall-clock session lifetime from active MCP request time and the gaps between requests; those gaps may include agent reasoning, network delay, or other idle time and are not presented as agent thinking time. Persistence failures do not affect MCP requests.
|
|
189
|
-
|
|
190
|
-
Manage telemetry with:
|
|
191
|
-
|
|
192
|
-
```bash
|
|
193
|
-
agent-dir telemetry status
|
|
194
|
-
agent-dir telemetry schema
|
|
195
|
-
agent-dir telemetry show [profile] [session-id] [--follow]
|
|
196
|
-
agent-dir telemetry summary [profile]
|
|
197
|
-
agent-dir telemetry disable
|
|
198
|
-
agent-dir telemetry reset <profile>
|
|
199
|
-
agent-dir telemetry reset --all
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
Use `agent-dir telemetry show scriptr` or `agent-dir telemetry summary scriptr` to focus on one profile. `agent-dir telemetry show scriptr <session-id>` filters to a single session. `agent-dir telemetry reset scriptr` removes only that profile’s telemetry; `agent-dir telemetry reset --all` clears all telemetry and resets the configured level. For the event model, metric definitions, privacy levels, and guidance for interpreting raw and aggregated telemetry, see [Telemetry interpretation](docs/telemetry.md).
|
|
203
|
-
|
|
204
|
-
## MCP transport and compatibility
|
|
205
|
-
|
|
206
|
-
The HTTP MCP endpoint is `POST /mcp`. The implementation supports two protocol compatibility paths:
|
|
207
|
-
|
|
208
|
-
### Modern stateless MCP
|
|
209
|
-
|
|
210
|
-
The native protocol path uses MCP `2026-07-28`. Requests carry the protocol version and client capabilities in `params._meta`. The server does not require an initialize handshake or `Mcp-Session-Id` for this path.
|
|
211
|
-
|
|
212
|
-
Standard Streamable HTTP requests do **not** need the implementation-specific `MCP-Protocol-Version`, `Mcp-Method`, or `Mcp-Name` headers. If a client sends those optional headers, `agent-dir` validates them against the JSON-RPC request and metadata instead of requiring them.
|
|
213
|
-
|
|
214
|
-
The modern implementation includes:
|
|
215
|
-
|
|
216
|
-
- `server/discover`
|
|
217
|
-
- cursor pagination for list-style methods
|
|
218
|
-
- `subscriptions/listen` for tool, prompt, and resource change events
|
|
219
|
-
- resource subscriptions and filesystem change notifications
|
|
220
|
-
- cache metadata
|
|
221
|
-
- structured tool results with `resultType`
|
|
222
|
-
- `outputSchema` for tools
|
|
223
|
-
- the stable `io.modelcontextprotocol/skills` extension
|
|
224
|
-
|
|
225
|
-
### Legacy MCP compatibility
|
|
226
|
-
|
|
227
|
-
Clients using the MCP `2025-11-25` initialize-based lifecycle are also supported. A legacy client can:
|
|
228
|
-
|
|
229
|
-
1. send `initialize` with `params.protocolVersion: "2025-11-25"`, `capabilities`, and `clientInfo`;
|
|
230
|
-
2. negotiate `2025-11-25`;
|
|
231
|
-
3. send subsequent requests with `MCP-Protocol-Version: 2025-11-25` without the modern `params._meta` object.
|
|
232
|
-
|
|
233
|
-
This compatibility path is intentionally narrow. It does not weaken the modern protocol validation, and conflicting protocol headers are rejected.
|
|
234
|
-
|
|
235
|
-
The compatibility layer exists for clients such as MCP integrations that still perform the standard `initialize` handshake instead of using the native stateless lifecycle.
|
|
236
|
-
|
|
237
|
-
### Authentication and protocol troubleshooting
|
|
238
|
-
|
|
239
|
-
Authentication is independent of protocol negotiation. A valid Bearer token is still required before MCP handling:
|
|
240
|
-
|
|
241
|
-
```http
|
|
242
|
-
Authorization: Bearer <token>
|
|
243
|
-
```
|
|
244
|
-
|
|
245
|
-
For clients that cannot send an Authorization header, the server also accepts:
|
|
246
|
-
|
|
247
|
-
```text
|
|
248
|
-
https://your-subdomain.wormhole.bar/mcp?token=<token>
|
|
55
|
+
agent-dir . --tunnel wormhole --subdomain my-project-x7k4m2
|
|
249
56
|
```
|
|
250
57
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
The server log distinguishes protocol/header validation from MCP handler errors. This makes transient client interoperability failures diagnosable without exposing request payloads or secrets.
|
|
254
|
-
|
|
255
|
-
## MCP tools
|
|
256
|
-
|
|
257
|
-
| Tool | Purpose |
|
|
258
|
-
|---|---|
|
|
259
|
-
| `list_files` | Recursive project discovery |
|
|
260
|
-
| `list_dirs` | Direct directory discovery for one or more directories |
|
|
261
|
-
| `read_range` | Read a bounded line range from one UTF-8 file |
|
|
262
|
-
| `read_files` | Read one or more UTF-8 files in one call |
|
|
263
|
-
| `write_files` | Create or replace one or more files in one call |
|
|
264
|
-
| `patch_files` | Apply targeted text replacements to one or more files |
|
|
265
|
-
| `delete_files` | Delete one or more files in one call |
|
|
266
|
-
| `search_files` / `find_files` | Search file contents or find paths by glob |
|
|
267
|
-
| `search_code` | Search source-like files |
|
|
268
|
-
| `find_symbol` / `find_definition` | Locate likely symbol definitions |
|
|
269
|
-
| `find_references` | Locate symbol references |
|
|
270
|
-
| `find_imports` / `find_exports` | Inspect source dependencies and exports |
|
|
271
|
-
| `git_status` / `git_diff` / `git_log` | Git inspection |
|
|
272
|
-
| `git_stage` / `git_unstage` | Stage or unstage paths |
|
|
273
|
-
| `git_commit` | Commit staged changes |
|
|
274
|
-
| `git_restore` | Restore paths, discarding unstaged changes |
|
|
275
|
-
| `git_push` | Push the current branch to a remote |
|
|
276
|
-
| `project_overview` | Project structure, languages, package managers, and Git state |
|
|
277
|
-
| `package_info` / `file_info` | Project and filesystem metadata |
|
|
278
|
-
| `diagnostics` | Project-independent diagnostics; does not run tests, lint, or typecheck |
|
|
279
|
-
| `run_npm_batch` | Run one or more explicitly allowlisted npm scripts sequentially |
|
|
280
|
-
| `run_command_batch` | Run one or more explicitly allowlisted executables sequentially without a shell |
|
|
281
|
-
| `codegraph_explore` | Optional CodeGraph structural code intelligence when the project is indexed |
|
|
282
|
-
|
|
283
|
-
Batch operations execute sequentially and stop on the first failed command. Tool calls return modern `structuredContent` alongside a serialized text representation, and tools that return structured data advertise an `outputSchema`. List-style protocol methods use opaque cursors when more than 50 entries are available.
|
|
284
|
-
|
|
285
|
-
Modern `subscriptions/listen` replaces the legacy GET/SSE notification model. Clients can subscribe to tool, prompt, resource-list, and resource-specific change events; filesystem mutations publish resource-change events to active subscribers.
|
|
286
|
-
|
|
287
|
-
### Optional CodeGraph integration
|
|
288
|
-
|
|
289
|
-
Agent Dir can optionally bridge the CodeGraph MCP server into the same Agent Dir MCP endpoint. CodeGraph remains an independent tool and dependency; Agent Dir only exposes its `codegraph_explore` capability when the current project has a readable `.codegraph/codegraph.db` index and the `codegraph` executable is available on `PATH`. Agent Dir does not start CodeGraph until `codegraph_explore` is actually called.
|
|
290
|
-
|
|
291
|
-
Install CodeGraph separately if you want this capability:
|
|
58
|
+
For Cloudflare Tunnel, configure its DNS and ingress route first, then launch with a hostname:
|
|
292
59
|
|
|
293
60
|
```bash
|
|
294
|
-
|
|
295
|
-
codegraph init
|
|
296
|
-
```
|
|
297
|
-
|
|
298
|
-
CodeGraph exposes `codegraph_explore` as its primary/default MCP tool. Agent Dir forwards its request and structured result without reimplementing graph analysis, and launches CodeGraph with the Agent Dir project root fixed as `--path`; callers cannot select another project through the forwarded tool. Agent Dir intentionally exposes only `codegraph_explore`, even if a CodeGraph installation enables additional MCP tools.
|
|
299
|
-
|
|
300
|
-
The integration distinguishes these states in `server/discover`:
|
|
301
|
-
|
|
302
|
-
- `not_installed` — CodeGraph is unavailable on `PATH`.
|
|
303
|
-
- `not_indexed` — CodeGraph is installed, but this project has no readable `.codegraph/codegraph.db`.
|
|
304
|
-
- `available` — CodeGraph is installed and this project is indexed.
|
|
305
|
-
- `startup_failed` / `runtime_failed` — CodeGraph could not initialize or later terminated; Agent Dir remains available and reports the failure locally.
|
|
306
|
-
|
|
307
|
-
The CodeGraph child process is reused for subsequent calls and is terminated with Agent Dir shutdown. Agent Dir passes a deliberately limited environment to the child and does not forward arbitrary `projectPath` values, credentials, or unrelated filesystem paths.
|
|
308
|
-
|
|
309
|
-
CodeGraph is optional: without it, the normal Agent Dir tool surface and behavior are unchanged.
|
|
310
|
-
|
|
311
|
-
### Command permissions
|
|
312
|
-
|
|
313
|
-
npm scripts must be explicitly enabled:
|
|
314
|
-
|
|
315
|
-
```text
|
|
316
|
-
--npm typecheck,lint,format,test,build
|
|
317
|
-
```
|
|
318
|
-
|
|
319
|
-
Normal executables use a separate allowlist:
|
|
320
|
-
|
|
321
|
-
```text
|
|
322
|
-
--command grep,find,rg,git
|
|
323
|
-
```
|
|
324
|
-
|
|
325
|
-
Commands are launched with `shell: false`; the MCP client supplies the executable and arguments separately. Dedicated Git MCP tools are separately capability-gated; they are exposed only when Git is enabled for the active profile. If Git is disabled, direct calls to Git tools are rejected even if a client attempts to invoke them by name. An executable that is not in the allowlist is rejected. A configured `blacklistedCommands` entry overrides the allowlist and blocks matching command prefixes, so `git` can be allowed while `git commit` is blocked and `git status` remains available. Entries are whitespace-separated command/argument prefixes, for example `git commit` or `git push --force`.
|
|
326
|
-
|
|
327
|
-
Avoid allowing `sh`, `bash`, `zsh`, `cmd`, `node`, or `python` unless you intentionally want to grant a much broader execution capability.
|
|
328
|
-
|
|
329
|
-
## Agent Skills
|
|
330
|
-
|
|
331
|
-
### Agent-aware instructions and capabilities
|
|
332
|
-
|
|
333
|
-
Agent Dir automatically describes how an AI agent should use the server. MCP initialization and `server/discover` return dynamically generated instructions, and `resources/list` exposes two virtual resources:
|
|
334
|
-
|
|
335
|
-
```text
|
|
336
|
-
agent-dir://instructions
|
|
337
|
-
agent-dir://capabilities
|
|
338
|
-
```
|
|
339
|
-
|
|
340
|
-
The instructions emphasize efficient tool selection: targeted search before reading, bounded ranges before whole-file reads, dedicated tools before generic commands, batched related operations, narrow validation before full checks, and scoped Git inspection before full diffs. The execution policy is generated from the active profile, so changing `allowedScripts`, `commands`, or `blacklistedCommands` automatically changes what the agent is told it can execute.
|
|
341
|
-
|
|
342
|
-
The capabilities resource is machine-readable and includes the running Agent Dir version, registered tool names, execution policy, and preferred/avoid tool-selection patterns. This keeps the MCP server itself as the source of truth; client-specific instruction files do not need to be maintained when Agent Dir changes.
|
|
343
|
-
|
|
344
|
-
The server implements the stable MCP Skills extension (`io.modelcontextprotocol/skills`) over the standard Resources primitive. It discovers project-local `SKILL.md` files under:
|
|
345
|
-
|
|
346
|
-
```text
|
|
347
|
-
skills/
|
|
348
|
-
.agents/skills/
|
|
349
|
-
.claude/skills/
|
|
350
|
-
.github/skills/
|
|
351
|
-
```
|
|
352
|
-
|
|
353
|
-
Skills are exposed through `skills/list`, `skills/get`, `resources/list`, and `resources/read`. Skill entries contain parsed frontmatter plus SHA-256 digests and byte sizes for every served file. Skill manifests enforce the 512-resource and 16 MiB limits. Binary supporting files are returned as MCP blobs. `resources/directory/read` lists direct children of a skill resource directory. The extension advertises `directoryRead: true`.
|
|
354
|
-
|
|
355
|
-
This repository now includes a maintainer-facing compatibility skill at `skills/agent-dir-maintainer/SKILL.md`. It documents the modern and legacy MCP paths, troubleshooting signals, and release/test expectations for agents working on this project.
|
|
356
|
-
|
|
357
|
-
## Authentication
|
|
358
|
-
|
|
359
|
-
By default, `agent-dir` generates a random Bearer token when the server starts. The token protects both the MCP endpoint and the HTTP file API.
|
|
360
|
-
|
|
361
|
-
Clients should authenticate with:
|
|
362
|
-
|
|
363
|
-
```http
|
|
364
|
-
Authorization: Bearer <token>
|
|
365
|
-
```
|
|
366
|
-
|
|
367
|
-
The CLI also supports explicitly configured profile tokens and the `--token` option. There is no `--no-auth` option; authentication cannot be disabled through the CLI. Keep the token secret and do not commit it to a repository or publish it alongside a tunnel URL.
|
|
368
|
-
|
|
369
|
-
For clients that cannot send an `Authorization` header, the HTTP server also accepts the token as a query parameter:
|
|
370
|
-
|
|
371
|
-
```text
|
|
372
|
-
https://your-subdomain.wormhole.bar/mcp?token=<token>
|
|
373
|
-
```
|
|
374
|
-
|
|
375
|
-
Query-string tokens may be exposed in URL logs and should be treated as less secure than the Authorization header.
|
|
376
|
-
|
|
377
|
-
## REST API
|
|
378
|
-
|
|
379
|
-
The MCP server is the primary interface, but the HTTP server also exposes:
|
|
380
|
-
|
|
381
|
-
```text
|
|
382
|
-
GET /__tree
|
|
383
|
-
GET /path/to/file
|
|
384
|
-
PUT /path/to/file
|
|
385
|
-
DELETE /path/to/file
|
|
386
|
-
POST /mcp
|
|
387
|
-
```
|
|
388
|
-
|
|
389
|
-
All HTTP endpoints are authenticated by default.
|
|
390
|
-
|
|
391
|
-
## Releases
|
|
392
|
-
|
|
393
|
-
Releases are automated with [semantic-release](https://github.com/semantic-release/semantic-release) from the `main` branch. Developers should **not** manually edit the version in `package.json` for normal releases.
|
|
394
|
-
|
|
395
|
-
Use [Conventional Commits](https://www.conventionalcommits.org/) for commits that should communicate release impact:
|
|
396
|
-
|
|
397
|
-
| Commit | Release |
|
|
398
|
-
| --- | --- |
|
|
399
|
-
| `fix: handle tunnel reconnect` | Patch (`0.3.0` → `0.3.1`) |
|
|
400
|
-
| `feat: add project snapshots` | Minor (`0.3.0` → `0.4.0`) |
|
|
401
|
-
| `feat!: change authentication protocol` | Major (`0.3.0` → `1.0.0`) |
|
|
402
|
-
| `feat: change API` with a `BREAKING CHANGE:` footer | Major |
|
|
403
|
-
| `docs:`, `test:`, `chore:`, `refactor:`, `ci:`, etc. | No release by default |
|
|
404
|
-
|
|
405
|
-
After a pull request is reviewed and merged into `main`, the release workflow runs the normal validation (`npm run check`, `npm test`, and `npm pack --dry-run`). If validation succeeds, semantic-release determines the next SemVer version from the Conventional Commit history, updates `package.json` and `package-lock.json`, creates the release commit, creates the `vX.Y.Z` Git tag, creates the GitHub Release, and publishes the package to npm.
|
|
406
|
-
|
|
407
|
-
The release commit is marked `[skip ci]`, so it does not start another release cycle. A push to `main` that contains no release-worthy Conventional Commit produces no release.
|
|
408
|
-
|
|
409
|
-
For an intentional breaking release, use the `!` marker on the commit type/scope or add a `BREAKING CHANGE:` footer, for example:
|
|
410
|
-
|
|
411
|
-
```text
|
|
412
|
-
feat!: change authentication protocol
|
|
413
|
-
```
|
|
414
|
-
|
|
415
|
-
or:
|
|
416
|
-
|
|
417
|
-
```text
|
|
418
|
-
feat: change authentication protocol
|
|
419
|
-
|
|
420
|
-
BREAKING CHANGE: clients must use the new authentication protocol
|
|
61
|
+
agent-dir . --tunnel cloudflared --hostname mcp.example.com
|
|
421
62
|
```
|
|
422
63
|
|
|
423
|
-
|
|
64
|
+
See [Configuration and tunnel setup](docs/CONFIGURATION.md) for the setup wizard, saved profiles, authentication tokens, tunnel configuration, and command permissions.
|
|
424
65
|
|
|
425
|
-
##
|
|
66
|
+
## What it provides
|
|
426
67
|
|
|
427
|
-
|
|
68
|
+
- A compact MCP toolset for project context, file discovery, source search, bounded reads, targeted changes, and validation.
|
|
69
|
+
- Authenticated, stateless MCP over `POST /mcp`.
|
|
70
|
+
- Explicit npm-script and executable allowlists; commands run without a shell.
|
|
71
|
+
- Optional CodeGraph structural code intelligence when CodeGraph is installed and the project is indexed.
|
|
72
|
+
- Optional local-only telemetry, disabled by default.
|
|
428
73
|
|
|
429
|
-
|
|
74
|
+
See [MCP interface and security model](docs/MCP.md) for the supported protocol, tools, authentication, command policy, and optional CodeGraph integration.
|
|
430
75
|
|
|
431
|
-
|
|
76
|
+
## Documentation
|
|
432
77
|
|
|
433
|
-
|
|
78
|
+
- [Configuration and tunnel setup](docs/CONFIGURATION.md) — setup wizard, profiles, Cloudflare Tunnel, Wormhole, tokens, and command allowlists.
|
|
79
|
+
- [MCP interface and security model](docs/MCP.md) — supported endpoint, protocol behavior, tools, authentication, command execution, and CodeGraph.
|
|
80
|
+
- [Telemetry interpretation](docs/telemetry.md) — privacy levels, event model, metric definitions, and interpretation guidance.
|
|
81
|
+
- [Release procedure](docs/RELEASING.md) — validation, branch/PR flow, semantic-release, and npm publishing.
|
|
82
|
+
- [Contributing guide](https://github.com/SarfDime/agent-dir/blob/main/.github/CONTRIBUTING.md) — development setup, checks, and pull-request expectations.
|
|
83
|
+
- [Security policy](SECURITY.md) — supported versions and private vulnerability reporting.
|
|
84
|
+
- [Maintainer skill](https://github.com/SarfDime/agent-dir/blob/main/skills/agent-dir-maintainer/SKILL.md) — repository-specific guidance for coding agents.
|
|
434
85
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
```bash
|
|
438
|
-
npm run build
|
|
439
|
-
npm run typecheck
|
|
440
|
-
npm test
|
|
441
|
-
npm run format
|
|
442
|
-
npm run lint
|
|
443
|
-
npm run check
|
|
444
|
-
```
|
|
445
|
-
|
|
446
|
-
`npm run check` runs Biome plus TypeScript. Before publishing:
|
|
86
|
+
## Development checks
|
|
447
87
|
|
|
448
88
|
```bash
|
|
89
|
+
npm ci
|
|
449
90
|
npm run check
|
|
450
91
|
npm test
|
|
451
92
|
npm pack --dry-run
|
|
452
93
|
```
|
|
453
94
|
|
|
454
|
-
Review the `npm pack --dry-run`
|
|
455
|
-
|
|
456
|
-
## Toolchain
|
|
457
|
-
|
|
458
|
-
- **Node.js:** 24 LTS baseline
|
|
459
|
-
- **TypeScript:** 7.x
|
|
460
|
-
- **Biome:** 2.5.x
|
|
461
|
-
- **Module system:** native Node ESM with `NodeNext`
|
|
462
|
-
- **Formatting/linting:** Biome with strict recommended rules and import organization
|
|
463
|
-
|
|
464
|
-
The TypeScript configuration uses strict checking, exact optional properties, unchecked indexed access, isolated modules, explicit Node typings, and consistent module resolution.
|
|
465
|
-
|
|
466
|
-
## Security model
|
|
467
|
-
|
|
468
|
-
File access is rooted at the shared directory and resolves existing paths through their real filesystem targets, preventing symlinks from escaping the exposed root. HTTP request bodies are limited to 10 MiB. npm scripts and external executables use explicit allowlists, and external commands are not passed through a shell.
|
|
469
|
-
|
|
470
|
-
Because file writes, deletion, command execution, and Git write operations can modify a project or remote repository, expose only directories and capabilities you intend an AI agent to control. Git write tools are intentionally explicit: staging, unstaging, committing, restoring, and pushing are separate operations. `git_restore` discards unstaged changes, and `git_push` can modify a remote repository. If you do not want Git mutation, do not use the Git write tools and do not allow `git` through the generic command allowlist.
|
|
471
|
-
|
|
472
|
-
The diagnostics tool is deliberately project-independent. It checks conditions such as invalid JSON, broken symlinks, and unresolved merge-conflict markers instead of assuming a particular test runner, linter, compiler, or package ecosystem. Never share directories containing credentials, SSH keys, private certificates, production secrets, or unrelated personal data.
|
|
473
|
-
|
|
474
|
-
## Credits
|
|
475
|
-
|
|
476
|
-
Remote tunneling is provided by [Wormhole](https://wormhole.bar/) or [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/). `agent-dir` invokes the installed CLI and does not provide either tunneling service itself. Cloudflare support expects an already-configured named tunnel.
|
|
95
|
+
Review the package file list from `npm pack --dry-run` before publishing. Releases are automated from `main`; do not manually bump the package version or publish from a local machine. See the [release procedure](docs/RELEASING.md).
|
|
477
96
|
|
|
478
97
|
## License
|
|
479
98
|
|
|
480
|
-
MIT
|
|
99
|
+
[MIT](LICENSE)
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported versions
|
|
4
|
+
|
|
5
|
+
Security fixes are generally made against the latest published version.
|
|
6
|
+
|
|
7
|
+
If you are running an unreleased checkout, include the commit or tag when reporting a vulnerability.
|
|
8
|
+
|
|
9
|
+
## Reporting a vulnerability
|
|
10
|
+
|
|
11
|
+
**Please do not report security vulnerabilities through public GitHub issues.**
|
|
12
|
+
|
|
13
|
+
Use GitHub's private security advisory flow:
|
|
14
|
+
|
|
15
|
+
https://github.com/SarfDime/agent-dir/security/advisories/new
|
|
16
|
+
|
|
17
|
+
Include:
|
|
18
|
+
|
|
19
|
+
- a clear description of the vulnerability;
|
|
20
|
+
- affected version or commit;
|
|
21
|
+
- reproduction steps or a proof of concept;
|
|
22
|
+
- security impact;
|
|
23
|
+
- any suggested mitigation.
|
|
24
|
+
|
|
25
|
+
Please remove secrets, credentials, personal data, and unrelated sensitive information from the report.
|
|
26
|
+
|
|
27
|
+
## Security model
|
|
28
|
+
|
|
29
|
+
agent-dir is designed to expose a trusted local project to an authenticated AI agent. Important security boundaries include:
|
|
30
|
+
|
|
31
|
+
- file access is rooted at the configured project directory;
|
|
32
|
+
- existing paths are resolved through their real filesystem targets to prevent symlink escapes;
|
|
33
|
+
- MCP and HTTP endpoints require authentication;
|
|
34
|
+
- npm scripts and generic executables are explicitly allowlisted;
|
|
35
|
+
- external commands are launched without a shell;
|
|
36
|
+
- Git commands are available only when the `git` executable is allowlisted, with configured blacklist prefixes taking precedence;
|
|
37
|
+
- request diagnostics avoid logging arbitrary request parameter values.
|
|
38
|
+
|
|
39
|
+
When contributing, preserve these boundaries. A compatibility fix must not become an authentication bypass, path traversal, command-execution expansion, or sensitive-data logging change.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="260" viewBox="0 0 1200 260" role="img" aria-labelledby="title desc">
|
|
2
|
+
<title id="title">AGENT-DIR</title>
|
|
3
|
+
<desc id="desc">Expose a local project to AI agents through authenticated MCP.</desc>
|
|
4
|
+
<defs>
|
|
5
|
+
<linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
6
|
+
<stop offset="0%" stop-color="#111827"/>
|
|
7
|
+
<stop offset="48%" stop-color="#312e81"/>
|
|
8
|
+
<stop offset="100%" stop-color="#0f766e"/>
|
|
9
|
+
</linearGradient>
|
|
10
|
+
<radialGradient id="glow" cx="50%" cy="45%" r="65%">
|
|
11
|
+
<stop offset="0%" stop-color="#a5b4fc" stop-opacity=".28"/>
|
|
12
|
+
<stop offset="100%" stop-color="#a5b4fc" stop-opacity="0"/>
|
|
13
|
+
</radialGradient>
|
|
14
|
+
</defs>
|
|
15
|
+
<rect width="1200" height="260" rx="28" fill="url(#bg)"/>
|
|
16
|
+
<rect width="1200" height="260" rx="28" fill="url(#glow)"/>
|
|
17
|
+
<g fill="none" stroke="#fff" stroke-opacity=".08">
|
|
18
|
+
<path d="M0 210C240 120 420 310 690 175S1040 65 1200 120"/>
|
|
19
|
+
<path d="M0 235C250 145 430 335 700 200S1040 90 1200 145"/>
|
|
20
|
+
</g>
|
|
21
|
+
<text x="600" y="112" text-anchor="middle" fill="#fff" font-family="Inter,Segoe UI,Arial,sans-serif" font-size="58" font-weight="800" letter-spacing="3">AGENT-DIR</text>
|
|
22
|
+
<text x="600" y="158" text-anchor="middle" fill="#e0e7ff" font-family="Inter,Segoe UI,Arial,sans-serif" font-size="23">Expose a local project to AI agents through authenticated MCP.</text>
|
|
23
|
+
<text x="600" y="198" text-anchor="middle" fill="#c7d2fe" font-family="Inter,Segoe UI,Arial,sans-serif" font-size="15">Local files · Code intelligence · Git inspection · Allowlisted commands · Optional Wormhole tunneling</text>
|
|
24
|
+
<g transform="translate(600 222)">
|
|
25
|
+
<g transform="translate(-212 0)">
|
|
26
|
+
<rect width="132" height="28" rx="7" fill="#111827" fill-opacity=".72" stroke="#ffffff" stroke-opacity=".16"/>
|
|
27
|
+
<text x="66" y="19" text-anchor="middle" fill="#fff" font-family="Inter,Segoe UI,Arial,sans-serif" font-size="13" font-weight="700">Node.js 24+</text>
|
|
28
|
+
</g>
|
|
29
|
+
<g transform="translate(-62 0)">
|
|
30
|
+
<rect width="124" height="28" rx="7" fill="#6b4fbb" fill-opacity=".92"/>
|
|
31
|
+
<text x="62" y="19" text-anchor="middle" fill="#fff" font-family="Inter,Segoe UI,Arial,sans-serif" font-size="13" font-weight="700">MCP 2026-07-28</text>
|
|
32
|
+
</g>
|
|
33
|
+
<g transform="translate(78 0)">
|
|
34
|
+
<rect width="94" height="28" rx="7" fill="#d4b000" fill-opacity=".94"/>
|
|
35
|
+
<text x="47" y="19" text-anchor="middle" fill="#111827" font-family="Inter,Segoe UI,Arial,sans-serif" font-size="13" font-weight="700">License MIT</text>
|
|
36
|
+
</g>
|
|
37
|
+
</g>
|
|
38
|
+
</svg>
|
package/dist/bin/agent-dir.js
CHANGED
|
@@ -51,7 +51,6 @@ const { values, positionals } = parseArgs({
|
|
|
51
51
|
command: { type: "string" },
|
|
52
52
|
blacklist: { type: "string" },
|
|
53
53
|
token: { type: "string" },
|
|
54
|
-
git: { type: "boolean" },
|
|
55
54
|
rotate: { type: "boolean" },
|
|
56
55
|
"no-tunnel": { type: "boolean" },
|
|
57
56
|
random: { type: "boolean" },
|
|
@@ -344,8 +343,6 @@ if (command === "config") {
|
|
|
344
343
|
const blacklist = parseList(values.blacklist);
|
|
345
344
|
if (blacklist !== undefined)
|
|
346
345
|
profile.blacklistedCommands = blacklist;
|
|
347
|
-
if (values.git !== undefined)
|
|
348
|
-
profile.git = values.git;
|
|
349
346
|
if (profile.tunnel === "wormhole" && !profile.subdomain && !values.random) {
|
|
350
347
|
fail("Wormhole profiles require --subdomain <name>, or use --random.");
|
|
351
348
|
}
|
|
@@ -411,8 +408,6 @@ if (commands !== undefined)
|
|
|
411
408
|
overrides.commands = commands;
|
|
412
409
|
if (blacklist !== undefined)
|
|
413
410
|
overrides.blacklistedCommands = blacklist;
|
|
414
|
-
if (values.git !== undefined)
|
|
415
|
-
overrides.git = values.git;
|
|
416
411
|
const commandConfig = mergeProfile(profile ?? { directory, port, tunnel }, overrides);
|
|
417
412
|
const telemetryConfigId = profile
|
|
418
413
|
? command
|
|
@@ -427,7 +422,6 @@ const telemetryConfigId = profile
|
|
|
427
422
|
npm: commandConfig.npm?.allowedScripts ?? [],
|
|
428
423
|
commands: commandConfig.commands ?? [],
|
|
429
424
|
blacklistedCommands: commandConfig.blacklistedCommands ?? [],
|
|
430
|
-
git: commandConfig.git ?? commandConfig.commands?.includes("git") ?? false,
|
|
431
425
|
}))
|
|
432
426
|
.digest("hex")
|
|
433
427
|
.slice(0, 12);
|
|
@@ -451,7 +445,6 @@ if (allowedCommands)
|
|
|
451
445
|
if (commandConfig.blacklistedCommands?.length) {
|
|
452
446
|
bannerOptions.blacklistedCommands = commandConfig.blacklistedCommands;
|
|
453
447
|
}
|
|
454
|
-
bannerOptions.gitEnabled = commandConfig.git ?? commandConfig.commands?.includes("git") ?? false;
|
|
455
448
|
printBanner(bannerOptions);
|
|
456
449
|
const telemetrySessionId = randomUUID();
|
|
457
450
|
const server = await startServer({
|
package/dist/src/auth.js
CHANGED
|
@@ -14,7 +14,9 @@ function secureCompare(left, right) {
|
|
|
14
14
|
return timingSafeEqual(leftHash, rightHash);
|
|
15
15
|
}
|
|
16
16
|
export function unauthorized() {
|
|
17
|
-
return new Response(JSON.stringify({
|
|
17
|
+
return new Response(JSON.stringify({
|
|
18
|
+
error: "Unauthorized: provide a valid Authorization: Bearer <token> header.",
|
|
19
|
+
}), {
|
|
18
20
|
status: 401,
|
|
19
21
|
headers: {
|
|
20
22
|
"content-type": "application/json; charset=utf-8",
|