Skip to content

[feat] Enable user MCP servers by default (http; stdio stays off-by-design)#4912

Open
mmabrouk wants to merge 1 commit into
big-agentsfrom
feat/mcp-user-servers-default-on
Open

[feat] Enable user MCP servers by default (http; stdio stays off-by-design)#4912
mmabrouk wants to merge 1 commit into
big-agentsfrom
feat/mcp-user-servers-default-on

Conversation

@mmabrouk

Copy link
Copy Markdown
Member

Context

Mahmoud's direction: "MCP should not be off by default." User-declared MCP servers were gated behind AGENTA_AGENT_MCP_SERVERS_ENABLED, which defaulted off. This PR flips the default to on.

The flag gates only user-declared mcpServers. The internal agenta-tools channel was never gated, so it is unaffected. http user MCP is verified working end-to-end behind the runner's https + SSRF guards. stdio user MCP still fails loud for both harnesses, so flipping the default does not expose stdio.

What changed

  • services/oss/src/agent/tools/resolver.py_mcp_enabled() now reads the flag with a "true" default, so an unset flag enables user MCP.
  • Deployment defaults flipped wherever the flag/key has one:
    • 7 docker-compose files (oss/ee x dev / gh / gh.ssl / gh.local): ${AGENTA_AGENT_MCP_SERVERS_ENABLED:-false} -> :-true
    • 4 env.*.example files: # AGENTA_AGENT_MCP_SERVERS_ENABLED=false -> =true
    • Helm: values.yaml enableMcp: false -> true, _helpers.tpl default false -> default true, plus the oss/ee values.*.example.yaml commented examples
    • Railway: oss/scripts/configure.sh :-false -> :-true
  • services/agent/src/engines/sandbox_agent/mcp.ts — docstring updated to "on by default" and now spells out the SSRF residual.
  • services/oss/tests/pytest/unit/agent/tools/test_resolution.py — a new test asserts _mcp_enabled() is True when the flag is unset; the two disabled-path tests now set the flag false explicitly.

A self-hoster disables user MCP by setting AGENTA_AGENT_MCP_SERVERS_ENABLED to a falsy value (or agentRunner.enableMcp: false on Helm).

Scope / risk

  • One service default plus deployment defaults. No wire, schema, or route change. The internal gateway-tool channel is untouched.
  • stdio user MCP is unchanged: it still fails loud (disabled by design), so default-on does not expose it.
  • Residual the reviewer must weigh: the runner's http SSRF guard (validateUserMcpUrl in services/agent/src/engines/sandbox_agent/mcp.ts) is scheme + host only — no DNS-rebinding pinning, no redirect re-validation. With user MCP now on by default, an author-controlled config could reach an internal host via DNS rebinding or an https->internal redirect. Secrets ride headers only to https hosts, so the blast radius is bounded, but it is a real residual for an OSS default-on. Tracked in (feat) Harden user-MCP http SSRF guard (DNS rebinding + redirects) for OSS default-on #4911 (AGE-3843). Decision needed: harden the guard first, or ship default-on with the residual tracked.

How to QA

Prereqs: a deployment with the flag UNSET (so the new default applies), a Claude harness, and a project that holds the MCP server's named secret in the vault.

  1. With AGENTA_AGENT_MCP_SERVERS_ENABLED unset, run a Claude agent whose config declares an https user MCP server (transport: "http", a url, and a named secret).
    • Expected: the run resolves the server, the secret rides as a request header, and the tool is callable. Before this PR the same run failed with MCPDisabledError.
  2. Run a Claude agent whose config declares a stdio user MCP server (a command).
    • Expected: the run fails loud with the user-MCP-unsupported error. Default-on does NOT change this.
  3. Set AGENTA_AGENT_MCP_SERVERS_ENABLED=false and repeat step 1.
    • Expected: MCPDisabledError naming the server. The disable path still works.

Unit test: cd services && uv run --no-sync python -m pytest oss/tests/pytest/unit/agent/tools/test_resolution.py -q -> 6 passed.

Edge cases: empty mcpServers with the flag on -> clean empty list; an http url that fails the SSRF guard (non-https or internal host) -> throws before any credential is attached.

Config reference

Surface Before After
resolver._mcp_enabled() default os.getenv(..., "") (off) os.getenv(..., "true") (on)
docker-compose (x7) ${AGENTA_AGENT_MCP_SERVERS_ENABLED:-false} :-true
env.*.example (x4) # AGENTA_AGENT_MCP_SERVERS_ENABLED=false # ...=true
Helm agentRunner.enableMcp false true
Railway configure.sh :-false :-true

Refs #4911

https://claude.ai/code/session_01GYo3UEfvsZpncagqb28Mbc

…esign)

User MCP servers were gated off by default via AGENTA_AGENT_MCP_SERVERS_ENABLED.
Flip the default to on in the service resolver and across every deployment default
(docker-compose, env examples, Helm chart + examples, Railway). http user MCP runs
behind the runner's https + SSRF guards; stdio user MCP still fails loud (disabled
by design), so flipping the default does not expose stdio.

The runner SSRF guard is scheme+host only; the residual hardening (DNS-rebinding
pinning + redirect re-validation) is tracked separately.

Refs #4911

Claude-Session: https://claude.ai/code/session_01GYo3UEfvsZpncagqb28Mbc
@dosubot dosubot Bot added the size:M This PR changes 30-99 lines, ignoring generated files. label Jun 28, 2026
@vercel

vercel Bot commented Jun 28, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agenta-documentation Ready Ready Preview, Comment Jun 28, 2026 1:04pm

Request Review

@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • MCP server support is now enabled by default across supported deployment setups and examples.
    • Kubernetes, Docker Compose, Railway, and Helm defaults now favor enabling the agent runner’s MCP capabilities.
  • Documentation
    • Updated example and template configuration values to reflect the new default behavior.
  • Bug Fixes
    • Aligned runtime behavior so unset configuration now behaves as enabled, reducing setup friction for new installations.

Walkthrough

AGENTA_AGENT_MCP_SERVERS_ENABLED is flipped from false to true as the default across all hosting configurations (Docker Compose OSS/EE, Kubernetes, Railway). The Python resolver's _mcp_enabled() is updated to treat an unset flag as enabled, the SSRF guard comment is revised, and tests are updated to match.

MCP Default-On Enablement

Layer / File(s) Summary
Resolver logic and default-on behavior
services/oss/src/agent/tools/resolver.py, services/agent/src/engines/sandbox_agent/mcp.ts, services/oss/tests/pytest/unit/agent/tools/test_resolution.py
_mcp_enabled() now defaults AGENTA_AGENT_MCP_SERVERS_ENABLED to "true" when unset; module docs and SSRF guard comment updated; tests add a test_mcp_enabled_by_default_when_unset case and update disabled-state tests to explicitly set the flag to "false".
Hosting config defaults
hosting/docker-compose/oss/..., hosting/docker-compose/ee/..., hosting/kubernetes/..., hosting/railway/oss/scripts/configure.sh
All Docker Compose, Kubernetes Helm, and Railway configurations change the AGENTA_AGENT_MCP_SERVERS_ENABLED default (or enableMcp) from false to true, including example env files.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related issues

Possibly related PRs

  • Agenta-AI/agenta#4871: Modifies the same resolver.py MCP gating logic and related tests that this PR builds on.
  • Agenta-AI/agenta#4899: Touches the same SSRF guard documentation in mcp.ts that this PR updates.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 60.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly states the main change: enabling user MCP servers by default, with stdio noted as still off by design.
Description check ✅ Passed The description matches the PR and summarizes the default-on MCP change, deployment defaults, tests, and residual risk.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/mcp-user-servers-default-on

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@mmabrouk mmabrouk added the needs-review Agent updated; awaiting Mahmoud's review label Jun 28, 2026
@mmabrouk

Copy link
Copy Markdown
Member Author

🤖 The AI agent says:

Changes made

This flips user MCP servers to on by default, per your direction that MCP should not be off by default.

  • The service resolver (services/oss/src/agent/tools/resolver.py) now defaults the gate to on: an unset AGENTA_AGENT_MCP_SERVERS_ENABLED enables user MCP.
  • Every deployment default is flipped to match: 7 docker-compose files, 4 env.*.example files, the Helm chart (values.yaml + _helpers.tpl) with its oss/ee example values, and the Railway configure.sh.
  • Only user-declared mcpServers are gated. The internal agenta-tools channel was never gated, so it is unaffected.
  • http user MCP is verified working end-to-end behind the runner's https + SSRF guards. stdio user MCP still fails loud (disabled by design), so default-on does not expose stdio.
  • Tests: a new test pins the default-on behavior, and the two disabled-path tests now set the flag false explicitly. The services unit suite for this file passes (6 passed).

Decision needed (this is a design call, not a code review)

The runner's http SSRF guard is scheme + host only. It does not pin DNS resolution to the validated host, and it does not re-validate redirects. With user MCP now on by default, an author-controlled config could reach an internal host through DNS rebinding or an https->internal redirect. Secrets ride headers only to https hosts, so the blast radius is bounded, but this is a real residual for an OSS default-on.

Options:

  1. Harden first — land DNS-resolution pinning + manual-redirect re-validation ((feat) Harden user-MCP http SSRF guard (DNS rebinding + redirects) for OSS default-on #4911) before this default-on ships.
  2. Ship default-on now, harden next — merge this and track the residual in (feat) Harden user-MCP http SSRF guard (DNS rebinding + redirects) for OSS default-on #4911.

My recommendation: option 2 if this rides only on big-agents / cloud, where egress is already constrained; option 1 if it can reach an OSS self-host build. Once you pick, I will queue the matching work.

What I need from you: the decision above, harden-first vs ship-with-residual. The code change is a one-line default flip plus the matching deployment defaults; a quick correctness skim is welcome, but the real ask is the security call.

Do not merge. I am leaving this for you / the orchestrator to fold back.

# runner's https + SSRF guards; stdio servers still fail loud (disabled by design). Set
# the flag to a falsy value to turn user MCP servers off entirely.
return (
os.getenv("AGENTA_AGENT_MCP_SERVERS_ENABLED", "true").strip().lower() in TRUTHY

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 The AI agent says: This is the whole behavior change: the default moved from "" (off) to "true" (on). An unset AGENTA_AGENT_MCP_SERVERS_ENABLED now enables user MCP. To turn it back off, set the flag to any falsy value. Everything else in this PR is the matching deployment defaults.

* - reject loopback, link-local (incl. the `169.254.169.254` cloud metadata host), and private
* address literals unless the host is in `AGENTA_AGENT_MCP_HOST_ALLOWLIST` (comma-separated).
*
* RESIDUAL (tracked in #4911): the guard does NOT pin DNS resolution to the validated host and

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 The AI agent says: This is the residual behind the decision. The guard validates scheme + host but does not pin DNS resolution or re-validate redirects, so DNS rebinding or an https->internal redirect can still reach an internal sink. Bounded (secrets ride headers only to https hosts) but real now that the flag defaults on. Hardening is tracked in #4911.

@mmabrouk

Copy link
Copy Markdown
Member Author

🤖 The AI agent says: triggering an automated review.

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown

@mmabrouk Reviewing the changes now.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d9caf658-3bfc-47eb-886c-2b00d46ecbd0

📥 Commits

Reviewing files that changed from the base of the PR and between 1e55134 and f209643.

📒 Files selected for processing (19)
  • hosting/docker-compose/ee/docker-compose.dev.yml
  • hosting/docker-compose/ee/docker-compose.gh.local.yml
  • hosting/docker-compose/ee/docker-compose.gh.yml
  • hosting/docker-compose/ee/env.ee.dev.example
  • hosting/docker-compose/ee/env.ee.gh.example
  • hosting/docker-compose/oss/docker-compose.dev.yml
  • hosting/docker-compose/oss/docker-compose.gh.local.yml
  • hosting/docker-compose/oss/docker-compose.gh.ssl.yml
  • hosting/docker-compose/oss/docker-compose.gh.yml
  • hosting/docker-compose/oss/env.oss.dev.example
  • hosting/docker-compose/oss/env.oss.gh.example
  • hosting/kubernetes/ee/values.ee.example.yaml
  • hosting/kubernetes/helm/templates/_helpers.tpl
  • hosting/kubernetes/helm/values.yaml
  • hosting/kubernetes/oss/values.oss.example.yaml
  • hosting/railway/oss/scripts/configure.sh
  • services/agent/src/engines/sandbox_agent/mcp.ts
  • services/oss/src/agent/tools/resolver.py
  • services/oss/tests/pytest/unit/agent/tools/test_resolution.py

DOCKER_NETWORK_MODE: ${DOCKER_NETWORK_MODE:-bridge}
AGENTA_AGENT_RUNNER_URL: ${AGENTA_AGENT_RUNNER_URL:-http://sandbox-agent:8765}
AGENTA_AGENT_MCP_SERVERS_ENABLED: ${AGENTA_AGENT_MCP_SERVERS_ENABLED:-false}
AGENTA_AGENT_MCP_SERVERS_ENABLED: ${AGENTA_AGENT_MCP_SERVERS_ENABLED:-true}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Don't enable user MCP by default in self-hosted compose until the runner hardening lands.

Line 456 turns this OSS compose stack into opt-out for author-controlled HTTP MCP even though the PR notes the runner guard still lacks DNS pinning and redirect re-validation. In this same topology, services already gets host.docker.internal:host-gateway, so the residual SSRF path becomes reachable on default installs instead of only on explicit opt-in. Keep the self-hosted compose/env default false until the runner fix ships.

Suggested minimal mitigation
-            AGENTA_AGENT_MCP_SERVERS_ENABLED: ${AGENTA_AGENT_MCP_SERVERS_ENABLED:-true}
+            AGENTA_AGENT_MCP_SERVERS_ENABLED: ${AGENTA_AGENT_MCP_SERVERS_ENABLED:-false}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
AGENTA_AGENT_MCP_SERVERS_ENABLED: ${AGENTA_AGENT_MCP_SERVERS_ENABLED:-true}
AGENTA_AGENT_MCP_SERVERS_ENABLED: ${AGENTA_AGENT_MCP_SERVERS_ENABLED:-false}

- SCRIPT_NAME=/services
- AGENTA_AGENT_RUNNER_URL=${AGENTA_AGENT_RUNNER_URL:-http://sandbox-agent:8765}
- AGENTA_AGENT_MCP_SERVERS_ENABLED=${AGENTA_AGENT_MCP_SERVERS_ENABLED:-false}
- AGENTA_AGENT_MCP_SERVERS_ENABLED=${AGENTA_AGENT_MCP_SERVERS_ENABLED:-true}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Keep MCP disabled by default in the OSS compose stack. Defaulting AGENTA_AGENT_MCP_SERVERS_ENABLED to true exposes self-hosted installs to the MCP SSRF surface before the HTTP guard hardening lands.

{{- end }}
- name: AGENTA_AGENT_MCP_SERVERS_ENABLED
value: {{ default false $runner.enableMcp | quote }}
value: {{ default true $runner.enableMcp | quote }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Preserve explicit false for enableMcp

default treats false as empty here, so agentRunner.enableMcp: false still renders "true". Use a nil/hasKey check instead if the chart needs to support disabling MCP.

Comment on lines +25 to +30
# On by default: an unset flag enables user MCP servers. http servers run behind the
# runner's https + SSRF guards; stdio servers still fail loud (disabled by design). Set
# the flag to a falsy value to turn user MCP servers off entirely.
return (
os.getenv("AGENTA_AGENT_MCP_SERVERS_ENABLED", "true").strip().lower() in TRUTHY
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Keep the unset default opt-in until the runner hardening lands.

services/agent/src/engines/sandbox_agent/mcp.ts:44-48 now explicitly documents that DNS rebinding and HTTPS→internal redirects can still bypass the current guard. Making _mcp_enabled() return True when the flag is unset exposes that residual in fresh OSS/self-hosted installs that never opted into user MCP. Please keep the unset default falsy here (or scope default-on to controlled environments only) until that hardening ships.

@linear-code

linear-code Bot commented Jun 28, 2026

Copy link
Copy Markdown

AGE-3843

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Backend feature needs-review Agent updated; awaiting Mahmoud's review size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant