Reported issues for mcp-airlock
Pod holds 9 of 9 GitHub reports that passed its relevance review. This can include external user reports, maintainer-confirmed bugs, and concrete feature gaps. Treat them as evidence to inspect, not a count of distinct defects.
Back to mcp-airlock.
Most discussed
OTLP span exporter behind an env var
Spans are produced already (execute_tool <tool> with gen_ai.* attributes, principal, verdict) but the only exporter wired in is the console one writing to --otel-file. Anyone running a collector has to patch the source.
What to do
src/mcp_airlock/__main__.py, setup_otel(). Add an OTLP HTTP exporter when OTEL_EXPORTER_OTLP_ENDPOINT is set, keeping the file exporter as it is so both can be on at once. Use BatchSpanProcessor for OTLP, not SimpleSpanProcessor. The dependency…
Read the thread · 2026-09-15 · open · 1 comment
count_arg counts a JSON-encoded list as one object
Policy.count_objects takes len() of the count_arg argument when it is a list, tuple, set or dict, and 1 for anything else. A list sent as a JSON string (ids: "[1,2,3,4,5,6]") counts as one object, but the SDK upstream decodes the string into a list before it validates it. blast_radius.max_per_call and the per-principal window are bypassed that way. Found while reviewing #12, same cause as the not_in bypass fixed there: the policy looks at the raw string, the upstream at the decoded…
Read the thread · 2026-10-01 · closed · 0 comments
requestState does not carry the approval mode
The token binds principal, tool, argument hash, environment and upstream, but not the approval mode. Replicas that share AIRLOCK_SECRET and the store but run different modes accept each other's tokens: a requestState issued by a replica in oob mode is accepted in-band by a replica in inband mode. Reproduced with two instances sharing the secret and a store: the oob one answers pending, the inband one runs the call (tier.L2.confirmed, one real delete). The postgres e2e stack runs…
Read the thread · 2026-10-01 · closed · 0 comments
Warn about risky configuration at startup
Several configurations start fine and are weaker than they look. Log a warning for each at startup:
- no identity configured (no JWT secret, no JWKS URL, no trusted header): every call gets 401
AIRLOCK_JWKS_URLwithoutAIRLOCK_JWT_AUDIENCEAIRLOCK_JWT_SECRETshorter than 32 bytesAIRLOCK_TRUST_PRINCIPAL_HEADER=1together with JWT settings: a request with noAuthorizationheader is trusted on the header aloneAIRLOCK_STORE_DSNset withoutAIRLOCK_SECRET: replicas sign with…
Read the thread · 2026-10-01 · closed · 0 comments
Size limits, shutdown hook, blocking call on /approve
- there is no limit on the request body or on the upstream response;
self.http.postreads the whole answer into memory before the output cap runs Airlockhas no shutdown hook: the httpx client, the audit sinks and the Postgres audit connection are never closedapprove_submitcallsidentity.resolvedirectly; withAIRLOCK_JWKS_URLthat is a blocking fetch on the event loop (handlealready usesasyncio.to_thread)
Do: AIRLOCK_MAX_REQUEST_BYTES (default 1 MiB, 413 above it) and…
Read the thread · 2026-10-01 · closed · 0 comments
Audit detail field is not redacted
audit._row redacts args only. detail carries upstream error text (catalog.unavailable, upstream unreachable: ..., postprocess_error) and can contain credentials, for example a URL with a token in the query string.
Run string values in detail through scrub and dict values through redact, for both sinks.
Test: a deny with a bearer token in the error text reaches the JSONL file and the Postgres table as [REDACTED].
Read the thread · 2026-10-01 · closed · 0 comments
Approve link can be bypassed by answering the prompt in-band
With AIRLOCK_APPROVAL_WEBHOOK set, a caller that holds requestState can repeat the call with inputResponses: {"airlock-confirm": {"action": "accept", "content": {"confirm": true}}}. The call executes and the approve link is never opened. Reproduced on main with an L2 delete_service call: one real delete, no /approve request.
The README says the agent cannot approve its own call. With a webhook configured that is not true.
Add AIRLOCK_APPROVAL_MODE:
oob: only the approve link…
Read the thread · 2026-10-01 · closed · 0 comments
Approval webhook URL is written to the log when delivery fails
approvals.notify logs str(e) of the httpx error. For a failed POST that string contains the full URL, so a Telegram bot token (.../bot<token>/sendMessage) or a Slack webhook path ends up in the log. Reproduced on main with a 401 from a mock transport.
Log the exception class and the HTTP status only. No URL, no response body.
Test: a transport that answers 401 for https://api.telegram.org/bot123:SECRET/sendMessage. SECRET must not appear in caplog.text and notify must still…
Read the thread · 2026-10-01 · closed · 0 comments
Most recent
Helm chart for running the proxy in a cluster
The usual place for this proxy is next to an MCP server in Kubernetes, but the only deployment artifacts are a Dockerfile and a docker run line. A small chart would remove most of the work for anyone trying it in a cluster.
What to do
A charts/mcp-airlock/ chart, deliberately plain: Deployment, Service, ConfigMap for the policy file, and that is close to it. Notes on what matters:
- The policy goes in a ConfigMap mounted at
/data/policy.yaml;--policyalready points there in the…
Read the thread · 2026-09-15 · open · 0 comments
The remaining reports are on the project's issue tracker.