Skip to content

bug(mcp): sandbox read-only header name mismatch — x-posthog-read-only vs x-posthog-readonly #57268

@andrewm4894

Description

@andrewm4894

Bug

The Tasks sandbox MCP client sends the header x-posthog-read-only (with a hyphen between read and only), but the MCP server reads x-posthog-readonly (no second hyphen). The two names never match, so the server's read-only enforcement never applies to sandbox-spawned MCP sessions.

The server has a query-param fallback (?readonly=...) but the sandbox does not set it, so there is no second path that would catch the mismatch.

Code references

Senderproducts/tasks/backend/temporal/process_task/utils.py:363

{\"name\": \"x-posthog-read-only\", \"value\": str(read_only).lower()},

Receiverservices/mcp/src/index.ts:333

const readOnlyRaw = request.headers.get('x-posthog-readonly') || url.searchParams.get('readonly')

The sender line was introduced on 2026-03-09 in 2a68f4dd7750, so the mismatch has been present for ~2 months.

Impact

Two-directional risk:

  1. Today: sandboxes spawned with MCP scopes that include any write capability still see those write tools, regardless of whether the caller intended a read-only session, because the read-only filter on the MCP side is silently bypassed.
  2. If the server is later fixed to also accept x-posthog-read-only: any sandbox-launched workflow that relies on write tools (e.g. an agent harness that needs write-capable MCP tools) can suddenly lose them when the caller passes read_only-style scopes alongside the (now-honored) header.

Suggested fix

Align both ends on a single header name. Recommend standardizing on the server's x-posthog-readonly (no second hyphen) since the MCP server is also consumed by external clients (Claude Desktop, etc.), and changing the wire format on the receiver side has a wider blast radius than fixing one Python sender.

Concretely:

  • Update _build_mcp_config in products/tasks/backend/temporal/process_task/utils.py to emit x-posthog-readonly.
  • Update related tests in products/tasks/backend/temporal/process_task/tests/test_utils.py.
  • Audit any other internal callers (e.g. services/agentsh, eval harnesses) for the same name drift before landing.
  • Add a lightweight integration test that exercises the wire format end-to-end so this kind of mismatch can't silently regress.

Notes

Spotted while reviewing the Signals agent dev branch — the agent uses the standard sandbox MCP plumbing, which is how the issue surfaced, but the bug itself is independent of that work and affects every sandbox MCP consumer.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions