openclaw - ✅(Solved) Fix [Feature]: New option to pass caller context to MCP [1 pull requests, 1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#75116Fetched 2026-05-01 05:37:59
View on GitHub
Comments
1
Participants
2
Timeline
15
Reactions
2
Timeline (top)
mentioned ×6subscribed ×6commented ×1cross-referenced ×1

Add an opt-in, per-server injectCallerContext flag on mcp.servers.<name> so OpenClaw can forward its caller-identity HTTP headers (x-openclaw-agent-id, x-openclaw-account-id, x-openclaw-message-channel, x-session-key) onto a remote MCP server when bundle MCP is enabled. The flag is off by default, only takes effect on servers that declare a url, and never overwrites user-supplied headers. OPENCLAW_MCP_* placeholder env is populated whenever loopback auth or any merged MCP server opts in.

Root Cause

Add an opt-in, per-server injectCallerContext flag on mcp.servers.<name> so OpenClaw can forward its caller-identity HTTP headers (x-openclaw-agent-id, x-openclaw-account-id, x-openclaw-message-channel, x-session-key) onto a remote MCP server when bundle MCP is enabled. The flag is off by default, only takes effect on servers that declare a url, and never overwrites user-supplied headers. OPENCLAW_MCP_* placeholder env is populated whenever loopback auth or any merged MCP server opts in.

Fix Action

Fix / Workaround

  • Default off. No change for existing configs / third-party MCP endpoints.
  • Remote-only. Applied only to servers with a url; stdio servers ignore the flag.
  • Non-destructive. Caller headers are merged into headers only when the name is not already present — user-supplied headers always win.
  • Layer-aware. Works whether the opt-in lives in user config, plugin .mcp.json, or any bundle MCP merge layer (driven by mergedBundleMcpLayerWantsCallerContextInjection).
  • Clean emission. The injectCallerContext flag is stripped from the emitted MCP config so it never leaks to the bundled CLI.
  • Placeholder env. OPENCLAW_MCP_* placeholders are populated when loopback auth is enabled or any merged server requests caller injection, so the gateway's header substitution is always available when needed.
  • No global flag, no deprecations. Per-server only, to keep caller identity from silently leaking to third-party endpoints.

PR fix notes

PR #75118: feat(mcp): opt-in injectCallerContext for bundle MCP remote servers

Description (problem / solution / changelog)

Summary

  • Problem: When OpenClaw bundles MCP servers into agent CLIs, internal/sibling remote MCP servers had no first-class way to learn the OpenClaw caller (agent / account / message channel / session).
  • Why it matters: The loopback MCP already has this. It allows MCP servers to be agent aware.
  • What changed: New per-server, opt-in mcp.servers.<name>.injectCallerContext: true. When set on a server with a url, bundle MCP merges the OpenClaw caller headers (x-openclaw-agent-id, x-openclaw-account-id, x-openclaw-message-channel, x-session-key) onto that server's headers, without overwriting user-supplied ones. OPENCLAW_MCP_* placeholder env is now populated when either loopback auth or any merged MCP server opts in.
  • What did NOT change (scope boundary): No global mcp.injectCallerContext flag. No deprecations / migrations. Stdio MCPs and servers without the flag are untouched. Existing user headers always win. Default behavior is unchanged for every existing config.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

  • Closes #75116
  • Related #
  • This PR fixes a bug or regression

Root Cause (if applicable)

N/A — this is a feature addition, not a regression fix.

  • Root cause: N/A
  • Missing detection / guardrail: N/A
  • Contributing context (if known): N/A

Regression Test Plan (if applicable)

N/A — feature addition. New unit tests added to lock in the contract:

  • Coverage level:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file: src/agents/cli-runner/bundle-mcp-caller-context.test.ts, src/agents/bundle-mcp-config.test.ts, plus updates to bundle-mcp.{test,codex.test,gemini.test,user-config.test}.ts.
  • Scenario the test should lock in: caller headers are injected only on url servers with injectCallerContext: true, never overwrite user-set headers, ignored on stdio / when flag is omitted, and OPENCLAW_MCP_* placeholder env is populated when any merged MCP layer opts in.
  • Why this is the smallest reliable guardrail: pure config-merging logic with no I/O, fully covered at unit level.
  • Existing test that already covers this: none — the property is new.
  • If no new test is added, why not: N/A.

User-visible / Behavior Changes

  • New optional config property mcp.servers.<name>.injectCallerContext: boolean (default false).
  • When set to true on a server that has url, bundle MCP appends x-openclaw-agent-id, x-openclaw-account-id, x-openclaw-message-channel, x-session-key to that server's outbound headers (only when not already present).
  • OPENCLAW_MCP_* placeholder env is now also populated when any merged MCP server opts in to caller-context injection (previously only when loopback auth was enabled).
  • No change for any existing config that doesn't set the new flag.

Diagram (if applicable)

Before:
agent CLI -> bundled MCP config -> remote MCP server
            (no caller identity)

After (only when injectCallerContext: true on a `url` server):
agent CLI -> bundled MCP config (caller headers merged in,
            without overwriting user-set headers)
         -> remote MCP server (sees x-openclaw-agent-id,
            x-openclaw-account-id, x-openclaw-message-channel,
            x-session-key)

Security Impact (required)

  • New permissions/capabilities? No — opt-in per server, no implicit elevation.
  • Secrets/tokens handling changed? No — the same caller-identity headers the gateway already produces; no new secrets.
  • New/changed network calls? No — same MCP request, only added HTTP headers when explicitly enabled.
  • Command/tool execution surface changed? No.
  • Data access scope changed? No — caller identity is only forwarded to MCP servers the operator has explicitly opted in.
  • Risk + mitigation: Risk is silently leaking caller identity to a third-party MCP. Mitigated by being opt-in per server, no global flag, applied only to servers declaring a url, and never overwriting headers the operator already configured.

Repro + Verification

Environment

  • OS: Windows 11 (developer setup)
  • Runtime/container: Node 22 (OpenClaw monorepo, openclawsource)
  • Model/provider: N/A (logic is config-merge only)
  • Integration/channel: bundle MCP for Claude Code / Codex / Gemini CLIs
  • Relevant config (redacted):
{
  "mcp": {
    "servers": {
      "my-internal-mcp": {
        "url": "https://mcp.example.com",
        "injectCallerContext": true
      }
    }
  }
}

Steps

  1. Add a mcp.servers.<name> entry with url and injectCallerContext: true to the OpenClaw config.
  2. Run any bundle MCP-enabled CLI (e.g. Claude Code) through OpenClaw.
  3. Inspect the generated MCP config / outbound MCP request headers.

Expected

  • The bundled MCP config for <name> contains x-openclaw-agent-id, x-openclaw-account-id, x-openclaw-message-channel, x-session-key on headers (placeholder values that the gateway substitutes), without removing or overwriting any pre-existing operator headers.
  • Servers without the flag, or stdio servers, are untouched.

Actual

  • Matches expected. Verified by bundle-mcp-caller-context.test.ts and the updated bundle-mcp tests.

Evidence

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)
✓ applyBundleMcpCallerContext > injects only on remote servers that set injectCallerContext true
✓ applyBundleMcpCallerContext > does not inject when injectCallerContext is false or omitted on a url server
✓ applyBundleMcpCallerContext > adds caller headers without overwriting existing names
✓ mergedBundleMcpLayerWantsCallerContextInjection > is true when OpenClaw mcp.servers opts in
✓ mergedBundleMcpLayerWantsCallerContextInjection > is false when no server sets injectCallerContext true

Test Files  2 passed (2)
     Tests  5 passed (5)

npm run config:docs:check is also clean against the regenerated docs/.generated/config-baseline.sha256.

Human Verification (required)

  • Verified scenarios:
    • injectCallerContext: true on a url server → caller headers appear in emitted config.
    • Operator-supplied headers["x-openclaw-agent-id"] survives unchanged when the flag is also set.
    • injectCallerContext: false / omitted → emitted config unchanged.
    • Stdio servers (no url) → flag ignored.
    • OPENCLAW_MCP_* placeholder env is populated when only a plugin/bundle layer opts in (covered by mergedBundleMcpLayerWantsCallerContextInjection).
    • npm run config:docs:check and the targeted vitest shard pass on Windows.
  • Edge cases checked: no servers configured; only stdio servers; opt-in only at cfg.mcp.servers vs only in a merged bundle layer; flag stripped from emitted config.
  • What I did not verify: full end-to-end run against a real remote MCP server (validated via unit tests over the merged config object), and non-Windows hosts (logic is platform-agnostic).

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? Yes — new optional property, defaults off.
  • Config/env changes? No required changes. New opt-in mcp.servers.<name>.injectCallerContext.
  • Migration needed? No.
  • Upgrade steps: none. Existing configs continue to work unchanged.

Risks and Mitigations

  • Risk: Operator unintentionally enables injectCallerContext on a third-party MCP endpoint and leaks OpenClaw caller identity to it.
    • Mitigation: Opt-in per server (no global flag), only applied when the server has a url, documented behavior, and operator-supplied headers are never overwritten.
  • Risk: Future change to caller-header names diverges from what bundle MCP injects.
    • Mitigation: Header names are centralized in bundle-mcp-caller-context.ts and asserted by bundle-mcp-caller-context.test.ts; any change has a single chokepoint.

Changed files

  • CHANGELOG.md (modified, +5/-0)
  • docs/.generated/config-baseline.sha256 (modified, +2/-2)
  • src/agents/bundle-mcp-config.test.ts (modified, +57/-47)
  • src/agents/bundle-mcp-config.ts (modified, +28/-0)
  • src/agents/cli-runner/bundle-mcp-caller-context.test.ts (added, +155/-0)
  • src/agents/cli-runner/bundle-mcp-caller-context.ts (added, +65/-0)
  • src/agents/cli-runner/bundle-mcp.codex.test.ts (modified, +32/-0)
  • src/agents/cli-runner/bundle-mcp.gemini.test.ts (modified, +43/-0)
  • src/agents/cli-runner/bundle-mcp.test-support.ts (modified, +4/-1)
  • src/agents/cli-runner/bundle-mcp.test.ts (modified, +36/-3)
  • src/agents/cli-runner/bundle-mcp.ts (modified, +20/-1)
  • src/agents/cli-runner/bundle-mcp.user-config.test.ts (modified, +161/-1)
  • src/agents/cli-runner/prepare.ts (modified, +21/-12)
  • src/config/schema.base.generated.ts (modified, +11/-0)
  • src/config/types.mcp.ts (modified, +14/-0)
  • src/config/zod-schema.ts (modified, +2/-0)
RAW_BUFFERClick to expand / collapse

Summary

Add an opt-in, per-server injectCallerContext flag on mcp.servers.<name> so OpenClaw can forward its caller-identity HTTP headers (x-openclaw-agent-id, x-openclaw-account-id, x-openclaw-message-channel, x-session-key) onto a remote MCP server when bundle MCP is enabled. The flag is off by default, only takes effect on servers that declare a url, and never overwrites user-supplied headers. OPENCLAW_MCP_* placeholder env is populated whenever loopback auth or any merged MCP server opts in.

Problem to solve

When OpenClaw bundles MCP servers into agent CLIs (Claude Code, Codex, Gemini, …), a remote MCP server hosted alongside OpenClaw has no clean way to learn who the upstream caller is (which agent, account, message channel, session). Today operators either:

Hardcode mcp.servers.<name>.headers with the exact x-openclaw-* names, duplicating what the gateway already knows and drifting whenever those names change, or Forward nothing, which forces remote MCP servers to fall back to coarse, deployment-wide credentials and lose per-caller attribution. There is no first-class, opt-in way to say "this MCP endpoint is trusted with the caller context, please inject it."

Proposed solution

Per-server opt-in on McpServerConfig:

  "mcp": {
    "servers": {
      "my-internal-mcp": {
        "url": "https://mcp.example.com",
        "injectCallerContext": true
      }
    }
  }
}

Behavior:

  • Default off. No change for existing configs / third-party MCP endpoints.
  • Remote-only. Applied only to servers with a url; stdio servers ignore the flag.
  • Non-destructive. Caller headers are merged into headers only when the name is not already present — user-supplied headers always win.
  • Layer-aware. Works whether the opt-in lives in user config, plugin .mcp.json, or any bundle MCP merge layer (driven by mergedBundleMcpLayerWantsCallerContextInjection).
  • Clean emission. The injectCallerContext flag is stripped from the emitted MCP config so it never leaks to the bundled CLI.
  • Placeholder env. OPENCLAW_MCP_* placeholders are populated when loopback auth is enabled or any merged server requests caller injection, so the gateway's header substitution is always available when needed.
  • No global flag, no deprecations. Per-server only, to keep caller identity from silently leaking to third-party endpoints.

Alternatives considered

No response

Impact

  • Affected users/systems/channels: Operators of remote (HTTP) MCP servers hosted alongside OpenClaw, across all bundle MCP CLIs (Claude Code, Codex, Gemini, …). Stdio MCPs and third-party endpoints unaffected.
  • Severity: Blocks the clean integration path for per-caller authz/auditing on internal MCP servers; fully opt-in, so no risk to existing setups.
  • Frequency: Always, on every bundle MCP run that targets such a remote MCP server.
  • Consequence: Operators must hand-maintain x-openclaw-* headers per server, or lose per-caller attribution → coarser auth, weaker audit trails, ongoing manual config drift.

Evidence/examples

No response

Additional information

No response

extent analysis

TL;DR

Enable the injectCallerContext flag on a per-server basis in the mcp.servers configuration to forward caller-identity HTTP headers to remote MCP servers.

Guidance

  • To fix the issue, add the injectCallerContext flag to the mcp.servers configuration for the specific server that needs to receive caller-identity headers.
  • Set injectCallerContext to true for the desired server, as shown in the proposed solution example.
  • Ensure the server has a url specified, as the flag only applies to remote servers.
  • Verify that the OPENCLAW_MCP_* placeholder environment variables are populated when loopback auth or any merged MCP server opts in.

Example

{
  "mcp": {
    "servers": {
      "my-internal-mcp": {
        "url": "https://mcp.example.com",
        "injectCallerContext": true
      }
    }
  }
}

Notes

The injectCallerContext flag is opt-in and only applies to servers with a url specified, ensuring that caller identity is not silently leaked to third-party endpoints.

Recommendation

Apply the workaround by adding the injectCallerContext flag to the mcp.servers configuration for the affected servers, as this provides a clean and opt-in way to forward caller-identity headers to remote MCP servers.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - ✅(Solved) Fix [Feature]: New option to pass caller context to MCP [1 pull requests, 1 comments, 2 participants]