Hackerbane
Model Context Protocol
Public research note

MCP Python SDK
Host allowlist and resource paths

The Model Context Protocol Python SDK is the reference library for MCP servers and clients. Two prefix checks in that tree accept values that walk past the prefix. A Host of wild.example:9000.evil satisfies wild.example:*. A resource URL of /api/../admin satisfies a token scoped to /api. The Host patch is PR 3465. Their bot closed it pending assignment on #3463.

0 Critical
0 High
2 Medium
0 Low
0 Info
Document HB-AR-2026.4  /  public source review, HTTP allowlist and resource indicators
What this is A research note and an opinion. The MCP project did not hire Hackerbane for this.
System modelcontextprotocol/python-sdk, the MCP Python SDK
Scope TransportSecurityMiddleware Host/Origin :* matching, and check_resource_allowed path prefix matching
Review commit main @ 08a3bc8
Upstream PR 3465 (closed by their issue-assignment bot) · #3463 · #3464
Date 7 September 2026
Author Barney Chambers, Hackerbane
hackerbane.com hello@hackerbane.com HB-AR-2026.4
Hackerbane · Assessment Report HB-AR-2026.4

Contents

  1. 1Executive summary
  2. 2Scope and method
  3. 3Findings summary
  4. 4MED-01: :* Host/Origin allowlist accepts a suffix that is not a port
  5. 5MED-02: resource indicator prefix treats /api/../admin as under /api
  6. 6Remediation record
  7. 7Disclaimer

1   Executive summary

If you have not used this SDK: it is the Python library for MCP. Servers can sit on Streamable HTTP or SSE. A middleware can check Host and Origin so a page on another site cannot point your browser at the server. The check has a wildcard form, host:*, meaning any port on that host.

On main at commit 08a3bc8 that wildcard is a prefix:

if host.startswith(base_host + ":"):
    return True

wild.example:9000.evil starts with wild.example:. The tests already allow wild.example:9000. They do not reject a suffix after the port. I opened a patch that requires the suffix to be digits. It is PR 3465. This repo only keeps outside PRs open when a maintainer assigns the linked issue. The bot closed 3465. The issue is #3463. The branch is still there.

A second helper, check_resource_allowed, decides whether a requested resource URL sits under a configured one for OAuth resource indicators. It already rejects /api123 against /api. It accepts /api/../admin. That is #3464. I could not open a second PR from this account after the first. The patch is on Oskii/python-sdk branch fix/resource-indicator-dot-segments.

The MCP project did not hire this review. The name on the cover names the project I read.

1.1   The things to fix first

# ID Action Status on 7 Sep 2026
1 MED-01 Require the suffix after a :* allowlist entry to be digits. Patch in PR 3465. Bot-closed. Issue #3463 open.
2 MED-02 Normalise resource indicator paths before the prefix check. Issue #3464 open. Patch on the fork.

2   Scope and method

This is not a full audit of the SDK. I split the tree and read auth, transports, stdio, tools, and path helpers. The two findings I can prove with a unit test on current main are the ones above. I did not treat documented trust boundaries as bugs. I did not review every language SDK in the MCP org.

In scope src/mcp/server/transport_security.py: Host and Origin :* matching. src/mcp/shared/auth_utils.py: check_resource_allowed. Tests in tests/server/test_transport_security.py and tests/shared/test_auth_utils.py.
Out of scope Other MCP language SDKs. Hosted MCP product surfaces. Stdio spawn of a command the caller passed on purpose. FastMCP deploy defaults when auth is left off.
Method Read the code. Compare the existing allowlist and path-boundary tests with the matcher. Add cases that fail on 08a3bc8 and pass with the patch.
Retest uv run pytest tests/server/test_transport_security.py tests/shared/test_auth_utils.py -q

3   Findings summary

ID Severity Title Status
MED-01 Medium :* Host/Origin allowlist accepts a suffix that is not a port PR 3465, bot-closed
MED-02 Medium Resource indicator prefix treats /api/../admin as under /api Issue #3464 open

I called both Medium, not High, because each check is optional or sits behind an operator-configured prefix. MED-01 only fires when DNS rebinding protection is on and the allowlist uses :*. MED-02 only fires when someone relies on check_resource_allowed for hierarchical scope. Both are still the check you think you have.

Hackerbane · Assessment Report MED-01

4   MED-01

MED-01   :* Host/Origin allowlist accepts a suffix that is not a port

Medium CWE-183

src/mcp/server/transport_security.py · _validate_host · _validate_origin

When protection is on, Host must be in allowed_hosts and Origin, if present, must be in allowed_origins. An entry that ends in :* is documented as any port on that base. The implementation accepts any string that starts with base:.

On 08a3bc8, with allowed_hosts=["wild.example:*"], Host wild.example:9000.evil is accepted. Origin http://wild.example:9000.evil is accepted for http://wild.example:*.

The patch requires the suffix after base: to be digits. wild.example:9000 still passes. An empty port does not.

Hackerbane · Assessment Report MED-02 · Remediation

5   MED-02

MED-02   Resource indicator prefix treats /api/../admin as under /api

Medium CWE-22

src/mcp/shared/auth_utils.py · check_resource_allowed

The helper compares scheme, host, and port, then asks whether the requested path starts with the configured path after adding a trailing slash. That is how /api123 is rejected against /api. Dot-segments are left in place, so /api/../admin/ still starts with /api/.

After one percent-decode and posixpath.normpath, /api/../admin is /admin and the prefix fails. /api/foo/../v1 against /api still matches, because it stays under /api.

6   Remediation record

When What
7 Sep 2026 Review commit 08a3bc8. MED-01 and MED-02 confirmed. Tests fail on that commit and pass with the patches.
7 Sep 2026 Opened #3463 and PR 3465 for MED-01. Their contributing rules auto-close outside PRs until a maintainer assigns the issue. 3465 is closed for that reason. The branch was not rewritten.
7 Sep 2026 Opened #3464 for MED-02. A second PR from this account was refused after 3465. The patch is Oskii/python-sdk fix/resource-indicator-dot-segments.

To retest, run the command in section 2 against the two branches, or apply the same helper changes on 08a3bc8.

Hackerbane · Assessment Report 7 · Disclaimer

7   Disclaimer

Read this before you treat anything above as advice.

This file is a public research note. The Model Context Protocol project did not hire Hackerbane. There is no services agreement, statement of work, or client relationship behind it. The MCP name on the cover names the project I read. It is not a partnership badge.

What you have is an opinion about the files and commits named in section 2. It is not a certificate. It is not an endorsement of MCP, of the Python SDK, or of the patches. It is not a claim that the rest of the SDK is safe, or that it is broken.

I am not telling you to merge, deploy, buy, or sell anything. This is not investment advice, legal advice, or a security guarantee. If you ship software, you own that risk. If you run an MCP server, you own that risk.

Hackerbane and I accept no liability for how anyone uses this note, for bugs it does not name, or for loss that follows from relying on it. Findings can be wrong, incomplete, or already stale the day after the review commit. Code added after that commit was not reviewed. A retest covers only the checks it names.

You may share this file. Sharing it does not create a contract and does not make the reader a client.

This note is provided as-is, as available, with all faults. To the maximum extent the law allows, Hackerbane disclaims every warranty, express or implied, including merchantability, fitness for a particular purpose, title, and non-infringement. No promise that this text is accurate, complete, or free of error, or that any error will be fixed.

Nobody may rely on this note as a professional engagement. It is not financial, tax, legal, or regulatory advice. No third party is a beneficiary of it.

Severity words in this note

Critical Someone could steal funds or hurt people with what I looked at.
High The check you think you have does not work in the common case.
Medium A real miss, less common in the output I care about here.
Low Real, but limited, or it needs an unlikely setup.
Informational On the record. Not scored as a bug.

The coloured bars are labels. The word High or Medium is what counts.