Skip to content

Commit f7f3db1

Browse files
committed
Add timeout/logging/audit/CLI features for 0.5.0 release
MCP_TIMEOUT wasn't applied to HTTP transport, OAuth callback used a hardcoded port range, audit logs were ephemeral in containers, and there was no structured logging or shell completions. Apply MCP_TIMEOUT to reqwest client builder, make OAuth callback port configurable via MCP_OAUTH_CALLBACK_PORT (single, range, or OS-assigned), add tracing-based structured logging with MCP_LOG_LEVEL/MCP_LOG_FORMAT, add MCP_AUDIT_OUTPUT=stdout/stderr for container log drivers, and introduce mcp config path/edit and mcp completions commands. Fixes #13 Fixes #16 Fixes #76 Fixes #71 Fixes #9 Fixes #8 Fixes #5 Signed-off-by: Avelino <31996+avelino@users.noreply.github.com>
1 parent d2ea82e commit f7f3db1

23 files changed

Lines changed: 972 additions & 134 deletions

Cargo.lock

Lines changed: 105 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ chrondb = "0.2.3-dev.12298e3"
3939
chrono = { version = "0.4", features = ["serde"] }
4040
shell-words = "1"
4141
socket2 = { version = "0.6", features = ["all"] }
42+
tracing = "0.1"
43+
tracing-subscriber = { version = "0.3", features = ["json", "env-filter"] }
4244

4345
[dev-dependencies]
4446
tempfile = "3"

docs/guides/audit-logging.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ Add an `audit` section to `~/.config/mcp/servers.json`:
157157
| Field | Default | Description |
158158
|---|---|---|
159159
| `enabled` | `true` | Enable/disable audit logging |
160+
| `output` | `file` | Output destination: `file` (ChronDB, queryable), `stdout`, `stderr` (JSON lines), or `none` |
160161
| `log_arguments` | `false` | Log tool call arguments (may contain PII) |
161162
| `path` | `~/.config/mcp/audit/data` | ChronDB data directory |
162163
| `index_path` | `~/.config/mcp/audit/index` | ChronDB index directory |
@@ -216,13 +217,44 @@ MCP_AUDIT_ENABLED=false mcp serve --http 0.0.0.0:8080
216217

217218
When disabled, the logger is a no-op and the database is not initialized — zero overhead, no files created, no filesystem writes. This is the default in the Docker image.
218219

220+
## Streaming to stdout/stderr (containers)
221+
222+
In containers, writing audit to ChronDB is often impractical — the filesystem may be read-only, ephemeral, or you want logs flowing to your container log driver (CloudWatch, Datadog, etc.).
223+
224+
Set `output` to `stdout` or `stderr` to emit audit entries as newline-delimited JSON (one JSON object per line):
225+
226+
```bash
227+
MCP_AUDIT_OUTPUT=stdout mcp serve --http 0.0.0.0:8080
228+
```
229+
230+
Or in the config file:
231+
232+
```json
233+
{
234+
"audit": {
235+
"output": "stdout"
236+
}
237+
}
238+
```
239+
240+
Each line is a complete `AuditEntry` JSON object:
241+
242+
```json
243+
{"timestamp":"2026-04-14T12:00:00-03:00","source":"serve:http","method":"tools/call","tool_name":"search_issues","server_name":"sentry","identity":"alice","duration_ms":142,"success":true}
244+
```
245+
246+
When using `stdout` or `stderr` mode, `mcp logs` queries are not available (no database to query). Use your log aggregation pipeline instead.
247+
248+
Set `output` to `none` to disable audit entirely without touching the `enabled` flag.
249+
219250
## Environment variable overrides
220251

221252
All audit settings can be overridden via environment variables, which take priority over the config file. This is useful for container deployments where editing the config JSON is impractical.
222253

223254
| Variable | Overrides | Description |
224255
|---|---|---|
225256
| `MCP_AUDIT_ENABLED` | `audit.enabled` | Set to `false` or `0` to disable |
257+
| `MCP_AUDIT_OUTPUT` | `audit.output` | `file`, `stdout`, `stderr`, or `none` |
226258
| `MCP_AUDIT_PATH` | `audit.path` | ChronDB data directory |
227259
| `MCP_AUDIT_INDEX_PATH` | `audit.index_path` | ChronDB index directory |
228260

@@ -237,4 +269,12 @@ docker run -d \
237269
ghcr.io/avelino/mcp serve --http 0.0.0.0:8080
238270
```
239271

272+
Example: stream audit to container stdout (no volume needed):
273+
274+
```bash
275+
docker run -d \
276+
-e MCP_AUDIT_OUTPUT=stdout \
277+
ghcr.io/avelino/mcp serve --http 0.0.0.0:8080
278+
```
279+
240280
See the full list of variables in the [environment variables reference](../reference/environment-variables.md).

docs/guides/authentication.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ Your browser opens, you authorize the app, and the token is saved. Next time, it
3636
3. Registers as a client using [Dynamic Client Registration](https://datatracker.ietf.org/doc/rfc7591/) if supported
3737
4. Generates a PKCE challenge (S256) for security
3838
5. Opens your browser to the authorization URL
39-
6. Listens on a local port (`localhost:8085-8099`) for the callback
39+
6. Listens on a local port for the callback (default range `localhost:8085-8099`, configurable via `MCP_OAUTH_CALLBACK_PORT`)
4040
7. Exchanges the authorization code for tokens
4141
8. Saves tokens to `~/.config/mcp/auth.json`
4242

docs/howto/docker.md

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,21 @@ docker run -d \
115115

116116
### With audit logging
117117

118-
The default image disables audit logging (`MCP_AUDIT_ENABLED=false`) because `scratch` images have no writable filesystem. To enable it, mount a volume and override:
118+
The default image disables audit logging (`MCP_AUDIT_ENABLED=false`) because `scratch` images have no writable filesystem. You have two options:
119+
120+
**Option A: Stream to stdout (no volume needed)**
121+
122+
```bash
123+
docker run -d \
124+
-e MCP_SERVERS_CONFIG='{"mcpServers":{...}}' \
125+
-e MCP_AUDIT_OUTPUT=stdout \
126+
-p 8080:8080 \
127+
ghcr.io/avelino/mcp serve --http 0.0.0.0:8080 --insecure
128+
```
129+
130+
Audit entries are emitted as JSON lines to stdout, captured by your container log driver (CloudWatch, Datadog, etc.).
131+
132+
**Option B: Persist to a volume**
119133

120134
```bash
121135
docker run -d \
@@ -136,7 +150,10 @@ These variables are especially useful for container deployments. See the full li
136150
|---|---|---|
137151
| `MCP_SERVERS_CONFIG` || Inline JSON config, no file mount needed |
138152
| `MCP_CONFIG_DIR` | `~/.config/mcp` | Override config directory |
153+
| `MCP_LOG_LEVEL` | `info` | Log verbosity: `trace`, `debug`, `info`, `warn`, `error` |
154+
| `MCP_LOG_FORMAT` | `text` | Log format: `text` or `json` (structured, for log drivers) |
139155
| `MCP_AUDIT_ENABLED` | `false` (in Docker image) | Disable audit for read-only fs |
156+
| `MCP_AUDIT_OUTPUT` | `file` | `stdout`/`stderr` for container log drivers, `none` to disable |
140157
| `MCP_AUDIT_PATH` | `~/.config/mcp/db/data` | Override audit data path |
141158
| `MCP_AUDIT_INDEX_PATH` | `~/.config/mcp/db/index` | Override audit index path |
142159
| `MCP_AUTH_PATH` | `~/.config/mcp/auth.json` | Override OAuth token storage |
@@ -194,4 +211,4 @@ docker run --rm ghcr.io/avelino/mcp:0.1.0 --help
194211

195212
- **Stdio servers only work if the runtime is available inside the container.** The default image includes only the `mcp` binary and `ca-certificates`. Servers that require `npx`, `python`, or other runtimes won't work unless you build a custom image. HTTP servers (configured with `url`) work out of the box.
196213
- **OAuth browser flow doesn't work in Docker.** For HTTP servers that need OAuth, run `mcp add <server>` on your host first to complete authentication, then mount the config directory (which includes `auth.json`), or set `MCP_AUTH_PATH` to a mounted volume.
197-
- **Audit logging is disabled by default** in the Docker image because `scratch` images have no writable filesystem. Enable it with `MCP_AUDIT_ENABLED=true` and mount a volume for the data.
214+
- **Audit logging is disabled by default** in the Docker image because `scratch` images have no writable filesystem. Use `MCP_AUDIT_OUTPUT=stdout` to stream to the container log driver, or mount a volume and set `MCP_AUDIT_ENABLED=true`.

docs/howto/kubernetes.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,11 @@ The proxy exposes `GET /health` returning:
124124

125125
By default, audit logging is disabled (`MCP_AUDIT_ENABLED=false`) because the scratch-based image has no writable filesystem.
126126

127-
To enable:
127+
**Option A: Stream to stdout (no PVC needed)**
128+
129+
Set `MCP_AUDIT_OUTPUT=stdout` in the Deployment env. Audit entries are emitted as JSON lines to stdout and captured by your cluster's log pipeline (Fluentd, Loki, CloudWatch, etc.). No persistent storage required.
130+
131+
**Option B: Persist to a PVC**
128132

129133
1. Set `MCP_AUDIT_ENABLED=true` in the Deployment env
130134
2. Mount persistent storage at `/data`:
@@ -207,6 +211,7 @@ When Kubernetes sends `SIGTERM` (during rolling updates or scale-down):
207211
| `MCP_SERVERS_CONFIG` | (from ConfigMap) | Inline JSON config (highest priority) |
208212
| `MCP_PROXY_REQUEST_TIMEOUT` | `120` (app default) | Max seconds per JSON-RPC request |
209213
| `MCP_AUDIT_ENABLED` | `false` | Enable audit logging |
214+
| `MCP_AUDIT_OUTPUT` | `file` | `stdout` for cluster log pipeline, `file` for PVC, `none` to disable |
210215
| `MCP_AUDIT_PATH` | `/data/audit/data` | Audit data directory (app default: `~/.config/mcp/db/data`) |
211216
| `MCP_AUDIT_INDEX_PATH` | `/data/audit/index` | Audit index directory (app default: `~/.config/mcp/db/index`) |
212217
| `MCP_CLASSIFIER_CACHE` | `/tmp/tool-classification.json` | Tool classification cache (app default: `~/.config/mcp/tool-classification.json`) |

docs/reference/cli.md

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ mcp add filesystem
262262
Fails if:
263263
- Server not found in registry
264264
- Server already exists in config
265-
- Name is reserved (`search`, `add`, `remove`, `list`, `help`, `version`)
265+
- Name is reserved (`search`, `add`, `remove`, `list`, `help`, `version`, `serve`, `logs`, `config`, `completions`)
266266

267267
### `mcp add --url <url> <name>`
268268

@@ -311,6 +311,61 @@ Fails if:
311311

312312
> If the server changed type in the registry (stdio ↔ http), `mcp update` warns and drops type-specific fields that no longer apply.
313313
314+
## Config commands
315+
316+
### `mcp config path`
317+
318+
Print the resolved path to the config file (`servers.json`).
319+
320+
```bash
321+
mcp config path
322+
# /Users/you/.config/mcp/servers.json
323+
```
324+
325+
With `--json`, includes metadata:
326+
327+
```json
328+
{
329+
"path": "/Users/you/.config/mcp/servers.json",
330+
"exists": true,
331+
"dir": "/Users/you/.config/mcp"
332+
}
333+
```
334+
335+
### `mcp config edit`
336+
337+
Open the config file in your editor. Uses `$EDITOR`, falls back to `$VISUAL`, then `vi` (or `notepad` on Windows).
338+
339+
```bash
340+
mcp config edit
341+
```
342+
343+
If the config file doesn't exist, it's created with `{}` before opening.
344+
345+
## Shell completions
346+
347+
### `mcp completions <shell>`
348+
349+
Generate shell completion scripts. Supported shells: `bash`, `zsh`, `fish`.
350+
351+
```bash
352+
# Fish (recommended for the current session)
353+
mcp completions fish | source
354+
355+
# Fish (persistent)
356+
mcp completions fish > ~/.config/fish/completions/mcp.fish
357+
358+
# Bash
359+
mcp completions bash >> ~/.bashrc
360+
source ~/.bashrc
361+
362+
# Zsh
363+
mcp completions zsh > "${fpath[1]}/_mcp"
364+
autoload -Uz compinit && compinit
365+
```
366+
367+
Completions cover all subcommands, flags, and nested subcommands (`config path|edit`, `acl classify|check`, `logs` flags, etc.).
368+
314369
## ACL commands
315370

316371
### `mcp acl classify`

0 commit comments

Comments
 (0)