Skip to content

feat(mcp): OMNIVOICE_MCP_ALLOWED_HOSTS — configurable host allowlist for MCP transport security (#1249) - #1250

Merged
debpalash merged 4 commits into
debpalash:mainfrom
paoloantinori:feat/mcp-allowed-hosts
Jul 26, 2026
Merged

feat(mcp): OMNIVOICE_MCP_ALLOWED_HOSTS — configurable host allowlist for MCP transport security (#1249)#1250
debpalash merged 4 commits into
debpalash:mainfrom
paoloantinori:feat/mcp-allowed-hosts

Conversation

@paoloantinori

@paoloantinori paoloantinori commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Closes #1249.

What

Agents in Docker containers (or on other machines) connect via host.containers.internal, which the MCP SDK rejects with 421 (DNS-rebinding guard). Add OMNIVOICE_MCP_ALLOWED_HOSTS (comma-separated host patterns) that extends both allowed_hosts AND allowed_origins — so agents and browser-based MCP clients behind a proxy both work.

Default empty → zero behavior change. Same opt-in pattern as OMNIVOICE_TRUSTED_NETWORKS.

Review

/simplify (4 angles): reuse/simplification/efficiency clean. Altitude: bare except: pass should log (applied — operator's opted-in setting shouldn't fail silently).
/code-review high (correctness + conventions): found Origin-header gap (applied — now extends origins too), fix-quality test gap (applied — test added), docs-sync (applied — docs/mcp.md updated), bare except (applied).

Files

  • backend/mcp_server.py — read env var, extend allowed_hosts + allowed_origins, log on failure
  • tests/test_mcp_mount.py — test the env var extends both lists
  • docs/mcp.md — document the env var for Docker/LAN agents
  • CHANGELOG.md — ### Added entry

Adds the opt-in OMNIVOICE_MCP_ALLOWED_HOSTS environment variable to extend the MCP SDK transport-security allowed_hosts and corresponding allowed_origins for both http:// and https:// from a comma-separated list of host patterns; default remains empty to preserve the current localhost-only behavior. This enables agents in Docker/LAN/proxied setups to reach the /mcp endpoint using non-local Host headers while keeping operators in control via allowlisting. A human should review the host-pattern parsing and the warning-only failure path to ensure malformed input or SDK exceptions can’t leave transport security misconfigured.

… allowlist (debpalash#1249)

Agents running in Docker containers (or on other machines) connect via a
hostname like host.containers.internal, which the MCP SDK's DNS-rebinding
guard rejects with 421. Add OMNIVOICE_MCP_ALLOWED_HOSTS (comma-separated
host patterns) that extends both allowed_hosts and allowed_origins in
create_mcp_server(). Default empty → no behavior change.

Test: assert the env var extends the allowlist + origins. Docs: mcp.md
notes the env var for Docker/LAN agents.
@greptile-apps

greptile-apps Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Adds OMNIVOICE_MCP_ALLOWED_HOSTS, an opt-in comma-separated env var that extends the MCP SDK's DNS-rebinding allowlist (allowed_hosts and allowed_origins for both schemes) so agents running in Docker containers or on a LAN can reach the /mcp endpoint. Default is empty, preserving the existing localhost-only behavior.

  • backend/mcp_server.py: parses the env var after FastMCP construction, extends both allowed_hosts and the corresponding http:///https:// origin variants; exceptions are caught and logged as a warning so a bad SDK version doesn't crash startup.
  • tests/test_mcp_mount.py: new test verifies the env var populates both lists with the expected entries.
  • docs/mcp.md + CHANGELOG.md: document the var with a security caveat (trusted LAN / TLS only).

Important Files Changed

Filename Overview
backend/mcp_server.py Reads OMNIVOICE_MCP_ALLOWED_HOSTS, extends allowed_hosts and allowed_origins (both schemes) on the MCP transport security object; failure is logged and non-fatal.
tests/test_mcp_mount.py Adds test verifying the env var extends both allowed_hosts and allowed_origins with correct http/https variants.
docs/mcp.md Documents OMNIVOICE_MCP_ALLOWED_HOSTS with example patterns and correct security guidance.
CHANGELOG.md Adds changelog entry under ### Added for the new env var with usage example and issue reference.

Reviews (4): Last reviewed commit: "docs(mcp): add security note for remote ..." | Re-trigger Greptile

Comment thread backend/mcp_server.py Outdated
@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 1fd4724e-5df1-4efc-b467-23b3054f0c2d

📥 Commits

Reviewing files that changed from the base of the PR and between 03ed31a and 38c103f.

📒 Files selected for processing (1)
  • docs/mcp.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/mcp.md

📝 Walkthrough

Walkthrough

Adds opt-in MCP host allowlist configuration through OMNIVOICE_MCP_ALLOWED_HOSTS, updates transport security allowlists, and documents and tests access from non-local environments.

Changes

MCP host allowlist

Layer / File(s) Summary
Apply configurable MCP allowlists
backend/mcp_server.py, tests/test_mcp_mount.py
create_mcp_server() extends allowed hosts and HTTP origins from the environment variable, with test coverage for both settings.
Document and announce configuration
docs/mcp.md, CHANGELOG.md
Documents the comma-separated host format and records the new opt-in configuration in the unreleased changelog.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 8 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description skips the required template sections like Summary, Changes, Type, Testing, and Checklist. Rewrite the body to follow the repository template and add Summary, Changes, Type, Testing, and Checklist, plus any applicable release notes.
✅ Passed checks (8 passed)
Check name Status Explanation
Title check ✅ Passed The title is conventional-commit style, scoped, and includes the issue reference.
Linked Issues check ✅ Passed The changes implement the requested opt-in allowlist extension for allowed_hosts and add supporting tests and docs for #1249.
Out of Scope Changes check ✅ Passed The modified files stay within the feature scope: code, test, docs, and changelog updates for the new MCP allowlist setting.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Cross-Platform Default Parity ✅ Passed OMNIVOICE_MCP_ALLOWED_HOSTS is only applied when non-empty, so default behavior stays unchanged across macOS, Windows, and Linux.
I18n Completeness (21 Locales) ✅ Passed No frontend files are changed in the PR diff, so there are no new/changed t('...') keys or hardcoded UI strings to validate against the 21 locale files.
Local-First Guarantee ✅ Passed Only local MCP allowlist parsing was added; no required cloud calls, telemetry, accounts, or API keys were introduced.
Backward Compatibility ✅ Passed PASS: Changes are runtime-only MCP allowlist/docs/tests; no omnivoice_data or model-store code changes, no DB schema change, and no alembic migration needed.

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/mcp_server.py`:
- Around line 125-129: Extend the allowed origins configured by the MCP server
alongside the existing HTTP entries to also include HTTPS origins for each host.
In tests/test_mcp_mount.py lines 108-118, update the matching assertion to
require the corresponding https://...:* origin as well as the existing HTTP
origin.

In `@docs/mcp.md`:
- Around line 33-37: Update the remote MCP connection guidance near
OMNIVOICE_MCP_ALLOWED_HOSTS to state that plain HTTP is only acceptable on a
fully trusted LAN; require Tailscale, WireGuard, or TLS for connections beyond
that boundary, and explicitly warn users not to expose the backend publicly.
- Around line 33-37: Update the documentation around OMNIVOICE_MCP_ALLOWED_HOSTS
to describe the server’s Host header: specify that it should contain the
hostname and port used to reach the /mcp endpoint, rather than the address the
agent connects from. Preserve the comma-separated host-pattern examples.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 0c82dce6-fc81-4e2f-b8b9-06930fba3e84

📥 Commits

Reviewing files that changed from the base of the PR and between b722ca7 and 2ba943c.

📒 Files selected for processing (4)
  • CHANGELOG.md
  • backend/mcp_server.py
  • docs/mcp.md
  • tests/test_mcp_mount.py

Comment thread backend/mcp_server.py Outdated
Comment thread docs/mcp.md
Comment on lines +33 to +37
**Agents in Docker or on another machine:** the MCP SDK rejects non-localhost
Host headers by default (DNS-rebinding guard). Set
`OMNIVOICE_MCP_ALLOWED_HOSTS` to a comma-separated list of host patterns the
agent connects from (e.g. `host.containers.internal:*,192.168.1.50:*`):

Copy link
Copy Markdown
Contributor

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

Document the secure transport boundary.

This newly documented remote path can expose voice operations over plain HTTP unless users are told to stay on a trusted LAN or use Tailscale/WireGuard/TLS, and not to expose the backend publicly. As per path instructions, remote connections beyond a fully trusted LAN must use Tailscale/WireGuard or TLS and must not be exposed publicly.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/mcp.md` around lines 33 - 37, Update the remote MCP connection guidance
near OMNIVOICE_MCP_ALLOWED_HOSTS to state that plain HTTP is only acceptable on
a fully trusted LAN; require Tailscale, WireGuard, or TLS for connections beyond
that boundary, and explicitly warn users not to expose the backend publicly.

Source: Path instructions


🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== files =="
git ls-files | rg '(^docs/mcp\.md$|^docs/|mcp)' || true

echo "== docs/mcp.md relevant section =="
if [ -f docs/mcp.md ]; then
  nl -ba docs/mcp.md | sed -n '1,90p'
fi

echo "== OMNIVOICE_MCP_ALLOWED_HOSTS usages =="
rg -n "OMNIVOICE_MCP_ALLOWED_HOSTS|MCP_ALLOWED_HOSTS|Host|host-header|transport_security|allowed_hosts" .

Repository: debpalash/OmniVoice-Studio

Length of output: 4046


🌐 Web query:

mcp python SDK transport_security.py ALLOWED_HOSTS Host header DNS rebinding guard

💡 Result:

The transport_security.py module in the Model Context Protocol (MCP) Python SDK provides critical security middleware designed to protect against DNS rebinding attacks [1][2]. When enabled, this middleware validates the Host header of incoming HTTP requests to ensure they match a trusted allowlist [1][3]. Key details regarding DNS rebinding protection and ALLOWED_HOSTS: - Mechanism: The TransportSecurityMiddleware intercepts incoming HTTP requests [2]. If enable_dns_rebinding_protection is set to True (which is the default behavior when running on localhost in modern SDK versions), the middleware checks the Host header against the allowed_hosts configuration [1][3][4]. - Default Behavior: For servers created via FastMCP() or standard HTTP transport helpers, DNS rebinding protection is automatically enabled when the host is 127.0.0.1, localhost, or ::1 [4][5]. If a request's Host header does not match the expected value (e.g., when behind a reverse proxy using a custom domain), the server will reject the request with an HTTP 421 ("Misdirected Request") status code [6][7]. - Configuration: When deploying behind a reverse proxy (such as Nginx or Cloudflare), you must explicitly configure TransportSecuritySettings to include your public hostname [6][8]. - Example: python from mcp.server.transport_security import TransportSecuritySettings transport_security = TransportSecuritySettings( allowed_hosts=["mcp.example.com", "mcp.example.com:443"], ) - Note: allowed_hosts supports exact string matches and wildcard port patterns (e.g., hostname:*) [6][3]. - Error Handling: Recent updates ensure that rejected requests return a structured JSON body indicating the failure, such as {"error": "host_not_allowed", "received_host": "...", "configure": "TransportSecuritySettings.allowed_hosts"}, to assist in debugging configuration issues [8]. - Disabling Protection: While it is possible to set enable_dns_rebinding_protection to False, this is generally discouraged for production deployments unless your infrastructure (e.g., a reverse proxy) already guarantees host-header validation [6].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== docs/mcp.md relevant section =="
awk '{printf "%5d\t%s\n", NR, $0}' docs/mcp.md | sed -n '1,90p'

echo "== OMNIVOICE_MCP_ALLOWED_HOSTS usages =="
rg -n "OMNIVOICE_MCP_ALLOWED_HOSTS|MCP_ALLOWED_HOSTS|allowed_hosts|Host|transport_security" .

echo "== backend mcp server relevant sections =="
for f in backend/mcp_server.py backend/mcp_shim/__main__.py backend/services/mcp_bindings.py tests/test_mcp_mount.py tests/test_mcp_bindings.py; do
  if [ -f "$f" ]; then
    echo "--- $f"
    rg -n -C 4 "OMNIVOICE_MCP_ALLOWED_HOSTS|MCP_ALLOWED_HOSTS|TransportSecuritySettings|server\.run|http_transport|allowed_hosts|Host" "$f" || true
  fi
done

Repository: debpalash/OmniVoice-Studio

Length of output: 26391


Describe OMNIVOICE_MCP_ALLOWED_HOSTS as the server’s Host header.

The MCP transport-security allowlist validates requests for that Host value, not the client address, so “connects from” can lead to wrong configuration. Use wording like “hostname and port used to reach the /mcp endpoint” at docs/mcp.md:35-36.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/mcp.md` around lines 33 - 37, Update the documentation around
OMNIVOICE_MCP_ALLOWED_HOSTS to describe the server’s Host header: specify that
it should contain the hostname and port used to reach the /mcp endpoint, rather
than the address the agent connects from. Preserve the comma-separated
host-pattern examples.

@paoloantinori

Copy link
Copy Markdown
Contributor Author

All three bot review comments are addressed in the latest commits — they were posted on 2ba943c before the fixes landed. Summary:

Comment Finding Status
Greptile P1 (mcp_server.py:129) Only http:// origins — HTTPS proxy clients rejected ✅ Fixed in 03ed31a8 — now generates both http:// and https:// origins
CodeRabbit Major (mcp_server.py:129) Same — allow and test HTTPS origins ✅ Same fix; test now asserts https://host.containers.internal:*
CodeRabbit Major (docs/mcp.md:40) No security note for remote connections ✅ Fixed in 38c103f4 — added "keep on a trusted LAN or behind TLS, don't expose publicly"

@debpalash
debpalash merged commit 67d1f6a into debpalash:main Jul 26, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] OMNIVOICE_MCP_ALLOWED_HOSTS — configurable host allowlist for MCP transport security

2 participants